Merge tag 'pci-v3.13-fixes-1' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 22 Nov 2013 18:53:47 +0000 (10:53 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 22 Nov 2013 18:53:47 +0000 (10:53 -0800)
Pull PCI updates from Bjorn Helgaas:
 "Miscellaneous
   - Remove duplicate disable from pcie_portdrv_remove() (Yinghai Lu)
   - Fix whitespace, capitalization, and spelling errors (Bjorn Helgaas)"

* tag 'pci-v3.13-fixes-1' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci:
  PCI: Remove duplicate pci_disable_device() from pcie_portdrv_remove()
  PCI: Fix whitespace, capitalization, and spelling errors

2816 files changed:
Documentation/ABI/stable/sysfs-driver-ib_srp
Documentation/ABI/stable/sysfs-transport-srp
Documentation/ABI/testing/sysfs-driver-hid-roccat-ryos [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-hid-wiimote
Documentation/DMA-attributes.txt
Documentation/assoc_array.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arc/pmu.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/calxeda/mem-ctrlr.txt
Documentation/devicetree/bindings/crypto/omap-des.txt [new file with mode: 0644]
Documentation/devicetree/bindings/crypto/omap-sham.txt
Documentation/devicetree/bindings/dma/atmel-dma.txt
Documentation/devicetree/bindings/hwmon/lm90.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/i2c-bcm-kona.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/i2c-exynos5.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/i2c-rcar.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/i2c-st.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/trivial-devices.txt
Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt
Documentation/devicetree/bindings/media/st-rc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/as3722.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/s2mps11.txt
Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
Documentation/devicetree/bindings/power/twl-charger.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power_supply/ti,bq24735.txt [new file with mode: 0644]
Documentation/devicetree/bindings/powerpc/fsl/dma.txt
Documentation/devicetree/bindings/pwm/pwm-samsung.txt
Documentation/devicetree/bindings/spi/omap-spi.txt
Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
Documentation/devicetree/bindings/watchdog/dw_wdt.txt [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/men-a021-wdt.txt [moved from Documentation/devicetree/bindings/gpio/men-a021-wdt.txt with 100% similarity]
Documentation/devicetree/bindings/watchdog/moxa,moxart-watchdog.txt [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/rt2880-wdt.txt [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/sirfsoc_wdt.txt [new file with mode: 0644]
Documentation/dmatest.txt
Documentation/filesystems/btrfs.txt
Documentation/hwmon/lm90
Documentation/i2c/busses/i2c-i801
Documentation/input/gamepad.txt
Documentation/kbuild/kconfig.txt
Documentation/kernel-parameters.txt
Documentation/networking/ip-sysctl.txt
Documentation/power/power_supply_class.txt
Documentation/power/runtime_pm.txt
Documentation/pwm.txt
Documentation/security/00-INDEX
Documentation/security/IMA-templates.txt [new file with mode: 0644]
Documentation/security/keys.txt
Documentation/target/tcm_mod_builder.py
Documentation/timers/00-INDEX
Documentation/usb/gadget_configfs.txt
Documentation/virtual/kvm/00-INDEX [new file with mode: 0644]
Documentation/virtual/kvm/api.txt
Documentation/virtual/kvm/cpuid.txt
Documentation/virtual/kvm/devices/vfio.txt [new file with mode: 0644]
Documentation/virtual/kvm/locking.txt
Documentation/vm/00-INDEX
Documentation/vm/split_page_table_lock [new file with mode: 0644]
MAINTAINERS
Makefile
arch/Kconfig
arch/alpha/Kconfig
arch/alpha/include/asm/machvec.h
arch/alpha/include/asm/pal.h
arch/alpha/include/asm/pgalloc.h
arch/alpha/include/asm/rtc.h
arch/alpha/include/asm/string.h
arch/alpha/include/asm/thread_info.h
arch/alpha/include/uapi/asm/pal.h
arch/alpha/kernel/Makefile
arch/alpha/kernel/alpha_ksyms.c
arch/alpha/kernel/irq_alpha.c
arch/alpha/kernel/machvec_impl.h
arch/alpha/kernel/perf_event.c
arch/alpha/kernel/process.c
arch/alpha/kernel/proto.h
arch/alpha/kernel/rtc.c [new file with mode: 0644]
arch/alpha/kernel/setup.c
arch/alpha/kernel/smp.c
arch/alpha/kernel/sys_jensen.c
arch/alpha/kernel/sys_marvel.c
arch/alpha/kernel/time.c
arch/alpha/kernel/traps.c
arch/alpha/lib/csum_partial_copy.c
arch/alpha/lib/ev6-memset.S
arch/alpha/lib/memset.S
arch/arc/Kconfig
arch/arc/boot/dts/abilis_tb100.dtsi
arch/arc/boot/dts/abilis_tb100_dvk.dts
arch/arc/boot/dts/abilis_tb101.dtsi
arch/arc/boot/dts/abilis_tb101_dvk.dts
arch/arc/boot/dts/abilis_tb10x.dtsi
arch/arc/boot/dts/angel4.dts
arch/arc/configs/fpga_noramfs_defconfig [new file with mode: 0644]
arch/arc/include/asm/perf_event.h
arch/arc/include/asm/pgalloc.h
arch/arc/include/asm/thread_info.h
arch/arc/kernel/Makefile
arch/arc/kernel/kprobes.c
arch/arc/kernel/perf_event.c [new file with mode: 0644]
arch/arc/plat-tb10x/Kconfig
arch/arm/Kconfig
arch/arm/boot/dts/am335x-evm.dts
arch/arm/boot/dts/am335x-evmsk.dts
arch/arm/boot/dts/ecx-2000.dts
arch/arm/boot/dts/ecx-common.dtsi
arch/arm/boot/dts/highbank.dts
arch/arm/boot/dts/imx51.dtsi
arch/arm/boot/dts/twl4030.dtsi
arch/arm/common/edma.c
arch/arm/configs/prima2_defconfig
arch/arm/configs/vt8500_v6_v7_defconfig [new file with mode: 0644]
arch/arm/include/asm/dma-mapping.h
arch/arm/include/asm/hardware/iop3xx-adma.h
arch/arm/include/asm/hardware/iop_adma.h
arch/arm/include/asm/io.h
arch/arm/include/asm/kvm_arm.h
arch/arm/include/asm/kvm_asm.h
arch/arm/include/asm/kvm_emulate.h
arch/arm/include/asm/kvm_host.h
arch/arm/include/asm/kvm_mmu.h
arch/arm/include/asm/memory.h
arch/arm/include/asm/pgalloc.h
arch/arm/include/asm/pgtable-3level.h
arch/arm/include/asm/thread_info.h
arch/arm/include/asm/xen/hypervisor.h
arch/arm/include/asm/xen/page-coherent.h [new file with mode: 0644]
arch/arm/include/asm/xen/page.h
arch/arm/include/uapi/asm/kvm.h
arch/arm/kernel/head.S
arch/arm/kernel/traps.c
arch/arm/kvm/Kconfig
arch/arm/kvm/Makefile
arch/arm/kvm/arm.c
arch/arm/kvm/coproc.c
arch/arm/kvm/coproc_a15.c
arch/arm/kvm/coproc_a7.c [new file with mode: 0644]
arch/arm/kvm/emulate.c
arch/arm/kvm/guest.c
arch/arm/kvm/handle_exit.c
arch/arm/kvm/mmio.c
arch/arm/kvm/mmu.c
arch/arm/kvm/psci.c
arch/arm/kvm/reset.c
arch/arm/lib/bitops.h
arch/arm/mach-at91/Makefile
arch/arm/mach-at91/at91sam9260.c
arch/arm/mach-at91/at91sam9261.c
arch/arm/mach-at91/at91sam9263.c
arch/arm/mach-at91/at91sam9g45.c
arch/arm/mach-at91/at91sam9n12.c
arch/arm/mach-at91/at91sam9rl.c
arch/arm/mach-at91/at91sam9x5.c
arch/arm/mach-at91/board-sam9260ek.c
arch/arm/mach-at91/board-sam9263ek.c
arch/arm/mach-at91/generic.h
arch/arm/mach-at91/include/mach/at91sam9n12.h
arch/arm/mach-at91/include/mach/at91sam9x5.h
arch/arm/mach-at91/include/mach/sama5d3.h
arch/arm/mach-at91/sama5d3.c
arch/arm/mach-at91/sysirq_mask.c [new file with mode: 0644]
arch/arm/mach-davinci/board-da830-evm.c
arch/arm/mach-davinci/board-da850-evm.c
arch/arm/mach-davinci/board-dm365-evm.c
arch/arm/mach-davinci/board-dm644x-evm.c
arch/arm/mach-davinci/board-dm646x-evm.c
arch/arm/mach-davinci/board-mityomapl138.c
arch/arm/mach-davinci/board-sffsdr.c
arch/arm/mach-highbank/Kconfig
arch/arm/mach-imx/Makefile
arch/arm/mach-imx/clk-imx6q.c
arch/arm/mach-imx/clk-pllv3.c
arch/arm/mach-imx/common.h
arch/arm/mach-imx/mach-pca100.c
arch/arm/mach-imx/mach-pcm037.c
arch/arm/mach-imx/mach-pcm038.c
arch/arm/mach-imx/mach-pcm043.c
arch/arm/mach-imx/mach-vpr200.c
arch/arm/mach-imx/src.c
arch/arm/mach-imx/system.c
arch/arm/mach-integrator/integrator_cp.c
arch/arm/mach-iop13xx/include/mach/adma.h
arch/arm/mach-kirkwood/lacie_v2-common.c
arch/arm/mach-omap1/board-osk.c
arch/arm/mach-omap2/Makefile
arch/arm/mach-omap2/board-cm-t35.c
arch/arm/mach-omap2/board-h4.c
arch/arm/mach-omap2/board-omap3beagle.c
arch/arm/mach-omap2/board-omap3stalker.c
arch/arm/mach-omap2/cclock3xxx_data.c
arch/arm/mach-omap2/cclock44xx_data.c
arch/arm/mach-omap2/gpmc-smsc911x.c
arch/arm/mach-omap2/omap_device.c
arch/arm/mach-omap2/prm44xx_54xx.h
arch/arm/mach-pxa/cm-x300.c
arch/arm/mach-pxa/colibri-pxa270-income.c
arch/arm/mach-pxa/ezx.c
arch/arm/mach-pxa/hx4700.c
arch/arm/mach-pxa/lpd270.c
arch/arm/mach-pxa/magician.c
arch/arm/mach-pxa/mainstone.c
arch/arm/mach-pxa/mioa701.c
arch/arm/mach-pxa/palm27x.c
arch/arm/mach-pxa/palmtc.c
arch/arm/mach-pxa/palmte2.c
arch/arm/mach-pxa/pcm990-baseboard.c
arch/arm/mach-pxa/raumfeld.c
arch/arm/mach-pxa/stargate2.c
arch/arm/mach-pxa/tavorevb.c
arch/arm/mach-pxa/viper.c
arch/arm/mach-pxa/z2.c
arch/arm/mach-pxa/zylonite.c
arch/arm/mach-s3c24xx/mach-h1940.c
arch/arm/mach-s3c24xx/mach-mini2440.c
arch/arm/mach-s3c24xx/mach-rx1950.c
arch/arm/mach-s3c64xx/mach-crag6410.c
arch/arm/mach-s3c64xx/mach-hmt.c
arch/arm/mach-s3c64xx/mach-smartq.c
arch/arm/mach-s3c64xx/mach-smdk6410.c
arch/arm/mach-s5p64x0/mach-smdk6440.c
arch/arm/mach-s5p64x0/mach-smdk6450.c
arch/arm/mach-s5pc100/mach-smdkc100.c
arch/arm/mach-s5pv210/mach-smdkv210.c
arch/arm/mach-shmobile/board-armadillo800eva.c
arch/arm/mach-sti/Kconfig
arch/arm/mach-tegra/apbio.c
arch/arm/mach-tegra/tegra.c
arch/arm/mm/fault-armv.c
arch/arm/mm/mmu.c
arch/arm/mm/nommu.c
arch/arm/mm/proc-v7.S
arch/arm/plat-samsung/dev-backlight.c
arch/arm/xen/Makefile
arch/arm/xen/mm.c [new file with mode: 0644]
arch/arm/xen/p2m.c [new file with mode: 0644]
arch/arm64/Kconfig
arch/arm64/include/asm/dma-mapping.h
arch/arm64/include/asm/io.h
arch/arm64/include/asm/kvm_arm.h
arch/arm64/include/asm/kvm_emulate.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/include/asm/kvm_mmu.h
arch/arm64/include/asm/pgalloc.h
arch/arm64/include/asm/pgtable-hwdef.h
arch/arm64/include/asm/thread_info.h
arch/arm64/include/asm/xen/page-coherent.h [new file with mode: 0644]
arch/arm64/kvm/Kconfig
arch/arm64/kvm/guest.c
arch/arm64/kvm/handle_exit.c
arch/arm64/xen/Makefile
arch/avr32/boot/u-boot/head.S
arch/avr32/include/asm/kprobes.h
arch/avr32/include/asm/pgalloc.h
arch/avr32/include/asm/thread_info.h
arch/avr32/include/uapi/asm/Kbuild
arch/avr32/include/uapi/asm/auxvec.h
arch/avr32/include/uapi/asm/bitsperlong.h [deleted file]
arch/avr32/include/uapi/asm/byteorder.h
arch/avr32/include/uapi/asm/cachectl.h
arch/avr32/include/uapi/asm/errno.h [deleted file]
arch/avr32/include/uapi/asm/fcntl.h [deleted file]
arch/avr32/include/uapi/asm/ioctl.h [deleted file]
arch/avr32/include/uapi/asm/ioctls.h [deleted file]
arch/avr32/include/uapi/asm/ipcbuf.h [deleted file]
arch/avr32/include/uapi/asm/kvm_para.h [deleted file]
arch/avr32/include/uapi/asm/mman.h [deleted file]
arch/avr32/include/uapi/asm/msgbuf.h
arch/avr32/include/uapi/asm/poll.h [deleted file]
arch/avr32/include/uapi/asm/posix_types.h
arch/avr32/include/uapi/asm/resource.h [deleted file]
arch/avr32/include/uapi/asm/sembuf.h
arch/avr32/include/uapi/asm/setup.h
arch/avr32/include/uapi/asm/shmbuf.h
arch/avr32/include/uapi/asm/sigcontext.h
arch/avr32/include/uapi/asm/siginfo.h [deleted file]
arch/avr32/include/uapi/asm/signal.h
arch/avr32/include/uapi/asm/socket.h
arch/avr32/include/uapi/asm/sockios.h
arch/avr32/include/uapi/asm/stat.h
arch/avr32/include/uapi/asm/statfs.h [deleted file]
arch/avr32/include/uapi/asm/swab.h
arch/avr32/include/uapi/asm/termbits.h
arch/avr32/include/uapi/asm/termios.h
arch/avr32/include/uapi/asm/types.h
arch/avr32/include/uapi/asm/unistd.h
arch/avr32/kernel/entry-avr32b.S
arch/avr32/kernel/head.S
arch/blackfin/Kconfig
arch/blackfin/configs/BF609-EZKIT_defconfig
arch/blackfin/include/asm/hardirq.h
arch/blackfin/include/asm/irq.h
arch/blackfin/include/asm/irq_handler.h
arch/blackfin/include/asm/thread_info.h
arch/blackfin/kernel/bfin_gpio.c
arch/blackfin/mach-bf548/Kconfig
arch/blackfin/mach-bf548/boards/ezkit.c
arch/blackfin/mach-bf548/include/mach/gpio.h
arch/blackfin/mach-bf548/include/mach/irq.h
arch/blackfin/mach-bf609/Kconfig
arch/blackfin/mach-bf609/boards/ezkit.c
arch/blackfin/mach-bf609/include/mach/gpio.h
arch/blackfin/mach-bf609/include/mach/irq.h
arch/blackfin/mach-bf609/include/mach/portmux.h
arch/blackfin/mach-common/ints-priority.c
arch/blackfin/mach-common/pm.c
arch/blackfin/mach-common/smp.c
arch/c6x/Kconfig
arch/c6x/include/asm/thread_info.h
arch/cris/include/asm/hardirq.h
arch/cris/include/asm/pgalloc.h
arch/cris/include/asm/thread_info.h
arch/frv/include/asm/thread_info.h
arch/frv/mm/pgalloc.c
arch/hexagon/Kconfig
arch/hexagon/include/asm/pgalloc.h
arch/hexagon/include/asm/thread_info.h
arch/ia64/Kconfig
arch/ia64/hp/common/sba_iommu.c
arch/ia64/include/asm/kvm_host.h
arch/ia64/include/asm/pci.h
arch/ia64/include/asm/pgalloc.h
arch/ia64/include/asm/thread_info.h
arch/ia64/include/asm/xen/page-coherent.h [new file with mode: 0644]
arch/ia64/kernel/entry.S
arch/ia64/kernel/kprobes.c
arch/ia64/kernel/perfmon.c
arch/ia64/kvm/kvm-ia64.c
arch/ia64/pci/pci.c
arch/ia64/sn/kernel/io_acpi_init.c
arch/m32r/Kconfig
arch/m32r/include/asm/hardirq.h
arch/m32r/include/asm/mmu_context.h
arch/m32r/include/asm/pgalloc.h
arch/m32r/include/asm/thread_info.h
arch/m32r/kernel/entry.S
arch/m68k/Kconfig
arch/m68k/include/asm/hardirq.h
arch/m68k/include/asm/mcf_pgalloc.h
arch/m68k/include/asm/motorola_pgalloc.h
arch/m68k/include/asm/sun3_pgalloc.h
arch/m68k/include/asm/thread_info.h
arch/m68k/kernel/entry.S
arch/m68k/kernel/ints.c
arch/m68k/platform/68000/entry.S
arch/m68k/platform/68360/entry.S
arch/metag/Kconfig
arch/metag/include/asm/pgalloc.h
arch/metag/include/asm/thread_info.h
arch/microblaze/Kconfig
arch/microblaze/include/asm/pgalloc.h
arch/microblaze/include/asm/thread_info.h
arch/mips/Kconfig
arch/mips/configs/db1235_defconfig
arch/mips/include/asm/kvm_host.h
arch/mips/include/asm/octeon/cvmx-pip.h
arch/mips/include/asm/pgalloc.h
arch/mips/include/asm/thread_info.h
arch/mips/kvm/kvm_mips.c
arch/mn10300/Kconfig
arch/mn10300/include/asm/mmu_context.h
arch/mn10300/include/asm/pgalloc.h
arch/mn10300/include/asm/thread_info.h
arch/mn10300/mm/pgtable.c
arch/openrisc/Makefile
arch/openrisc/configs/or1ksim_defconfig
arch/openrisc/include/asm/Kbuild
arch/openrisc/include/asm/pgalloc.h
arch/openrisc/kernel/module.c
arch/openrisc/kernel/setup.c
arch/openrisc/kernel/vmlinux.h
arch/parisc/Kconfig
arch/parisc/include/asm/pgalloc.h
arch/parisc/include/asm/socket.h [new file with mode: 0644]
arch/parisc/include/asm/thread_info.h
arch/parisc/include/asm/uaccess.h
arch/parisc/include/uapi/asm/socket.h
arch/parisc/lib/memcpy.c
arch/parisc/mm/fault.c
arch/powerpc/Kconfig
arch/powerpc/Makefile
arch/powerpc/boot/dts/fsl/b4si-post.dtsi
arch/powerpc/boot/dts/fsl/elo3-dma-0.dtsi [new file with mode: 0644]
arch/powerpc/boot/dts/fsl/elo3-dma-1.dtsi [new file with mode: 0644]
arch/powerpc/boot/dts/fsl/t4240si-post.dtsi
arch/powerpc/configs/pseries_le_defconfig [new file with mode: 0644]
arch/powerpc/include/asm/disassemble.h
arch/powerpc/include/asm/elf.h
arch/powerpc/include/asm/exception-64s.h
arch/powerpc/include/asm/hvcall.h
arch/powerpc/include/asm/kvm_asm.h
arch/powerpc/include/asm/kvm_book3s.h
arch/powerpc/include/asm/kvm_book3s_32.h
arch/powerpc/include/asm/kvm_book3s_64.h
arch/powerpc/include/asm/kvm_book3s_asm.h
arch/powerpc/include/asm/kvm_booke.h
arch/powerpc/include/asm/kvm_host.h
arch/powerpc/include/asm/kvm_ppc.h
arch/powerpc/include/asm/paca.h
arch/powerpc/include/asm/pgalloc-64.h
arch/powerpc/include/asm/plpar_wrappers.h
arch/powerpc/include/asm/processor.h
arch/powerpc/include/asm/pte-book3e.h
arch/powerpc/include/asm/reg.h
arch/powerpc/include/asm/smp.h
arch/powerpc/include/asm/thread_info.h
arch/powerpc/include/uapi/asm/kvm.h
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/eeh.c
arch/powerpc/kernel/eeh_event.c
arch/powerpc/kernel/exceptions-64s.S
arch/powerpc/kernel/idle_power7.S
arch/powerpc/kernel/kprobes.c
arch/powerpc/kernel/process.c
arch/powerpc/kernel/prom.c
arch/powerpc/kernel/signal_32.c
arch/powerpc/kernel/signal_64.c
arch/powerpc/kernel/smp.c
arch/powerpc/kernel/time.c
arch/powerpc/kernel/traps.c
arch/powerpc/kernel/vdso64/sigtramp.S
arch/powerpc/kernel/vio.c
arch/powerpc/kvm/44x.c
arch/powerpc/kvm/44x_emulate.c
arch/powerpc/kvm/44x_tlb.c
arch/powerpc/kvm/Kconfig
arch/powerpc/kvm/Makefile
arch/powerpc/kvm/book3s.c
arch/powerpc/kvm/book3s.h [new file with mode: 0644]
arch/powerpc/kvm/book3s_32_mmu.c
arch/powerpc/kvm/book3s_32_mmu_host.c
arch/powerpc/kvm/book3s_64_mmu.c
arch/powerpc/kvm/book3s_64_mmu_host.c
arch/powerpc/kvm/book3s_64_mmu_hv.c
arch/powerpc/kvm/book3s_64_vio_hv.c
arch/powerpc/kvm/book3s_emulate.c
arch/powerpc/kvm/book3s_exports.c
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/book3s_hv_interrupts.S
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/kvm/book3s_interrupts.S
arch/powerpc/kvm/book3s_mmu_hpte.c
arch/powerpc/kvm/book3s_pr.c
arch/powerpc/kvm/book3s_pr_papr.c
arch/powerpc/kvm/book3s_rmhandlers.S
arch/powerpc/kvm/book3s_rtas.c
arch/powerpc/kvm/book3s_segment.S
arch/powerpc/kvm/book3s_xics.c
arch/powerpc/kvm/booke.c
arch/powerpc/kvm/booke.h
arch/powerpc/kvm/e500.c
arch/powerpc/kvm/e500.h
arch/powerpc/kvm/e500_emulate.c
arch/powerpc/kvm/e500_mmu.c
arch/powerpc/kvm/e500_mmu_host.c
arch/powerpc/kvm/e500mc.c
arch/powerpc/kvm/emulate.c
arch/powerpc/kvm/powerpc.c
arch/powerpc/kvm/trace.h
arch/powerpc/kvm/trace_booke.h [new file with mode: 0644]
arch/powerpc/kvm/trace_pr.h [new file with mode: 0644]
arch/powerpc/mm/gup.c
arch/powerpc/mm/pgtable_32.c
arch/powerpc/mm/pgtable_64.c
arch/powerpc/mm/slice.c
arch/powerpc/platforms/Kconfig.cputype
arch/powerpc/platforms/powermac/low_i2c.c
arch/powerpc/platforms/powernv/rng.c
arch/powerpc/platforms/pseries/eeh_pseries.c
arch/powerpc/platforms/pseries/lpar.c
arch/powerpc/platforms/pseries/nvram.c
arch/powerpc/platforms/pseries/rng.c
arch/powerpc/platforms/pseries/setup.c
arch/powerpc/platforms/pseries/suspend.c
arch/powerpc/platforms/wsp/chroma.c
arch/powerpc/platforms/wsp/h8.c
arch/powerpc/platforms/wsp/ics.c
arch/powerpc/platforms/wsp/opb_pic.c
arch/powerpc/platforms/wsp/psr2.c
arch/powerpc/platforms/wsp/scom_wsp.c
arch/powerpc/platforms/wsp/wsp.c
arch/s390/Kconfig
arch/s390/boot/Makefile
arch/s390/include/asm/ctl_reg.h
arch/s390/include/asm/eadm.h
arch/s390/include/asm/hardirq.h
arch/s390/include/asm/kvm_host.h
arch/s390/include/asm/pci.h
arch/s390/include/asm/sclp.h
arch/s390/include/asm/setup.h
arch/s390/include/asm/thread_info.h
arch/s390/kernel/crash_dump.c
arch/s390/kernel/early.c
arch/s390/kernel/kprobes.c
arch/s390/kernel/setup.c
arch/s390/kvm/diag.c
arch/s390/kvm/gaccess.h
arch/s390/kvm/intercept.c
arch/s390/kvm/interrupt.c
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/kvm-s390.h
arch/s390/kvm/priv.c
arch/s390/mm/pgtable.c
arch/s390/pci/pci.c
arch/s390/pci/pci_clp.c
arch/s390/pci/pci_event.c
arch/score/include/asm/pgalloc.h
arch/score/include/asm/thread_info.h
arch/sh/Kconfig
arch/sh/boards/mach-ecovec24/setup.c
arch/sh/include/asm/mmu_context.h
arch/sh/include/asm/pgalloc.h
arch/sh/include/asm/thread_info.h
arch/sh/kernel/entry-common.S
arch/sparc/Kconfig
arch/sparc/include/asm/hardirq_32.h
arch/sparc/include/asm/hardirq_64.h
arch/sparc/include/asm/mmu_64.h
arch/sparc/include/asm/page_64.h
arch/sparc/include/asm/pgtable_64.h
arch/sparc/include/asm/sparsemem.h
arch/sparc/include/asm/thread_info_32.h
arch/sparc/include/asm/thread_info_64.h
arch/sparc/include/asm/tlbflush_64.h
arch/sparc/include/asm/tsb.h
arch/sparc/kernel/entry.h
arch/sparc/kernel/kgdb_64.c
arch/sparc/kernel/kprobes.c
arch/sparc/kernel/ktlb.S
arch/sparc/kernel/pci.c
arch/sparc/kernel/process_64.c
arch/sparc/kernel/ptrace_64.c
arch/sparc/kernel/rtrap_64.S
arch/sparc/kernel/signal_64.c
arch/sparc/kernel/smp_64.c
arch/sparc/kernel/sun4v_tlb_miss.S
arch/sparc/kernel/sys_sparc_64.c
arch/sparc/kernel/syscalls.S
arch/sparc/kernel/traps_64.c
arch/sparc/kernel/tsb.S
arch/sparc/kernel/unaligned_64.c
arch/sparc/kernel/vmlinux.lds.S
arch/sparc/lib/clear_page.S
arch/sparc/lib/copy_page.S
arch/sparc/mm/fault_64.c
arch/sparc/mm/gup.c
arch/sparc/mm/hugetlbpage.c
arch/sparc/mm/init_64.c
arch/sparc/mm/init_64.h
arch/sparc/mm/srmmu.c
arch/sparc/mm/tlb.c
arch/sparc/mm/tsb.c
arch/sparc/mm/ultra.S
arch/tile/Kconfig
arch/tile/include/asm/hardirq.h
arch/tile/include/asm/thread_info.h
arch/tile/mm/pgtable.c
arch/um/Kconfig.char
arch/um/Kconfig.common
arch/um/Makefile
arch/um/configs/i386_defconfig [new file with mode: 0644]
arch/um/configs/x86_64_defconfig [new file with mode: 0644]
arch/um/defconfig [deleted file]
arch/um/drivers/mconsole_kern.c
arch/um/include/asm/processor-generic.h
arch/um/include/asm/thread_info.h
arch/um/include/shared/as-layout.h
arch/um/include/shared/os.h
arch/um/kernel/mem.c
arch/um/kernel/process.c
arch/um/kernel/sysrq.c
arch/um/kernel/trap.c
arch/um/kernel/um_arch.c
arch/um/os-Linux/signal.c
arch/unicore32/Kconfig
arch/unicore32/include/asm/pgalloc.h
arch/unicore32/include/asm/thread_info.h
arch/unicore32/kernel/puv3-nb0916.c
arch/x86/Kconfig
arch/x86/include/asm/kvm_emulate.h
arch/x86/include/asm/kvm_host.h
arch/x86/include/asm/pci.h
arch/x86/include/asm/pgalloc.h
arch/x86/include/asm/pvclock.h
arch/x86/include/asm/thread_info.h
arch/x86/include/asm/xen/page-coherent.h [new file with mode: 0644]
arch/x86/include/uapi/asm/kvm.h
arch/x86/include/uapi/asm/msr-index.h
arch/x86/kernel/apic/apic.c
arch/x86/kernel/cpu/amd.c
arch/x86/kernel/cpu/intel_cacheinfo.c
arch/x86/kernel/cpu/scattered.c
arch/x86/kernel/early-quirks.c
arch/x86/kernel/ftrace.c
arch/x86/kernel/kvmclock.c
arch/x86/kernel/pvclock.c
arch/x86/kvm/Kconfig
arch/x86/kvm/Makefile
arch/x86/kvm/cpuid.c
arch/x86/kvm/cpuid.h
arch/x86/kvm/emulate.c
arch/x86/kvm/mmu.c
arch/x86/kvm/mmu.h
arch/x86/kvm/mmu_audit.c
arch/x86/kvm/svm.c
arch/x86/kvm/vmx.c
arch/x86/kvm/x86.c
arch/x86/kvm/x86.h
arch/x86/mm/pgtable.c
arch/x86/pci/acpi.c
arch/x86/um/Kconfig
arch/x86/um/asm/processor_32.h
arch/x86/um/asm/processor_64.h
arch/x86/um/sysrq_32.c
arch/x86/um/sysrq_64.c
arch/x86/um/vdso/.gitignore [new file with mode: 0644]
arch/x86/xen/mmu.c
arch/x86/xen/p2m.c
arch/x86/xen/pci-swiotlb-xen.c
arch/x86/xen/setup.c
arch/x86/xen/smp.c
arch/x86/xen/spinlock.c
arch/x86/xen/time.c
arch/xtensa/include/asm/pgalloc.h
arch/xtensa/include/asm/pgtable.h
arch/xtensa/include/asm/thread_info.h
arch/xtensa/mm/mmu.c
block/blk-ioc.c
block/blk-mq-cpu.c
block/blk-mq.c
block/blk-softirq.c
block/blk-sysfs.c
block/partitions/efi.c
crypto/Kconfig
crypto/Makefile
crypto/af_alg.c
crypto/algif_hash.c
crypto/algif_skcipher.c
crypto/asymmetric_keys/Kconfig
crypto/asymmetric_keys/asymmetric_type.c
crypto/asymmetric_keys/public_key.c
crypto/asymmetric_keys/public_key.h
crypto/asymmetric_keys/rsa.c
crypto/asymmetric_keys/x509_cert_parser.c
crypto/asymmetric_keys/x509_parser.h
crypto/asymmetric_keys/x509_public_key.c
crypto/async_tx/async_memcpy.c
crypto/async_tx/async_pq.c
crypto/async_tx/async_raid6_recov.c
crypto/async_tx/async_tx.c
crypto/async_tx/async_xor.c
crypto/async_tx/raid6test.c
crypto/hash_info.c [new file with mode: 0644]
crypto/tcrypt.c
crypto/testmgr.c
drivers/acpi/Kconfig
drivers/acpi/ac.c
drivers/acpi/acpi_lpss.c
drivers/acpi/acpi_platform.c
drivers/acpi/acpica/utobject.c
drivers/acpi/blacklist.c
drivers/acpi/device_pm.c
drivers/acpi/ec.c
drivers/acpi/event.c
drivers/acpi/glue.c
drivers/acpi/pci_root.c
drivers/acpi/scan.c
drivers/acpi/video.c
drivers/ata/libata-acpi.c
drivers/ata/libata-eh.c
drivers/ata/pata_arasan_cf.c
drivers/atm/idt77252.c
drivers/base/dma-contiguous.c
drivers/base/platform.c
drivers/base/power/main.c
drivers/block/amiflop.c
drivers/block/cciss.c
drivers/block/null_blk.c
drivers/block/virtio_blk.c
drivers/char/hw_random/timeriomem-rng.c
drivers/char/hw_random/virtio-rng.c
drivers/char/random.c
drivers/char/tpm/Kconfig
drivers/char/tpm/Makefile
drivers/char/tpm/tpm-interface.c [moved from drivers/char/tpm/tpm.c with 93% similarity]
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm_atmel.c
drivers/char/tpm/tpm_eventlog.c
drivers/char/tpm/tpm_i2c_atmel.c [new file with mode: 0644]
drivers/char/tpm/tpm_i2c_infineon.c
drivers/char/tpm/tpm_i2c_nuvoton.c [new file with mode: 0644]
drivers/char/tpm/tpm_i2c_stm_st33.c
drivers/char/tpm/tpm_ibmvtpm.c
drivers/char/tpm/tpm_ppi.c
drivers/char/tpm/tpm_tis.c
drivers/char/tpm/xen-tpmfront.c
drivers/char/virtio_console.c
drivers/clk/clk-fixed-factor.c
drivers/connector/cn_proc.c
drivers/cpufreq/Kconfig.x86
drivers/cpufreq/cpufreq_conservative.c
drivers/cpufreq/cpufreq_governor.c
drivers/cpufreq/exynos-cpufreq.c
drivers/cpufreq/omap-cpufreq.c
drivers/crypto/tegra-aes.c
drivers/dma/Kconfig
drivers/dma/amba-pl08x.c
drivers/dma/at_hdmac.c
drivers/dma/coh901318.c
drivers/dma/cppi41.c
drivers/dma/dma-jz4740.c
drivers/dma/dmaengine.c
drivers/dma/dmatest.c
drivers/dma/dw/core.c
drivers/dma/edma.c
drivers/dma/ep93xx_dma.c
drivers/dma/fsldma.c
drivers/dma/fsldma.h
drivers/dma/imx-dma.c
drivers/dma/imx-sdma.c
drivers/dma/intel_mid_dma.c
drivers/dma/ioat/dma.c
drivers/dma/ioat/dma.h
drivers/dma/ioat/dma_v2.c
drivers/dma/ioat/dma_v2.h
drivers/dma/ioat/dma_v3.c
drivers/dma/ioat/pci.c
drivers/dma/iop-adma.c
drivers/dma/ipu/ipu_idmac.c
drivers/dma/k3dma.c
drivers/dma/mmp_pdma.c
drivers/dma/mmp_tdma.c
drivers/dma/mv_xor.c
drivers/dma/mv_xor.h
drivers/dma/mxs-dma.c
drivers/dma/omap-dma.c
drivers/dma/pl330.c
drivers/dma/ppc4xx/adma.c
drivers/dma/sa11x0-dma.c
drivers/dma/sh/shdma-base.c
drivers/dma/sh/shdmac.c
drivers/dma/ste_dma40.c
drivers/dma/tegra20-apb-dma.c
drivers/dma/timb_dma.c
drivers/dma/txx9dmac.c
drivers/edac/cell_edac.c
drivers/edac/edac_device.c
drivers/edac/edac_mc.c
drivers/edac/edac_pci.c
drivers/edac/highbank_l2_edac.c
drivers/edac/highbank_mc_edac.c
drivers/edac/mpc85xx_edac.c
drivers/edac/sb_edac.c
drivers/firewire/core-transaction.c
drivers/fmc/Kconfig
drivers/gpio/gpiolib.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/armada/Kconfig [new file with mode: 0644]
drivers/gpu/drm/armada/Makefile [new file with mode: 0644]
drivers/gpu/drm/armada/armada_510.c [new file with mode: 0644]
drivers/gpu/drm/armada/armada_crtc.c [new file with mode: 0644]
drivers/gpu/drm/armada/armada_crtc.h [new file with mode: 0644]
drivers/gpu/drm/armada/armada_debugfs.c [new file with mode: 0644]
drivers/gpu/drm/armada/armada_drm.h [new file with mode: 0644]
drivers/gpu/drm/armada/armada_drv.c [new file with mode: 0644]
drivers/gpu/drm/armada/armada_fb.c [new file with mode: 0644]
drivers/gpu/drm/armada/armada_fb.h [new file with mode: 0644]
drivers/gpu/drm/armada/armada_fbdev.c [new file with mode: 0644]
drivers/gpu/drm/armada/armada_gem.c [new file with mode: 0644]
drivers/gpu/drm/armada/armada_gem.h [new file with mode: 0644]
drivers/gpu/drm/armada/armada_hw.h [new file with mode: 0644]
drivers/gpu/drm/armada/armada_ioctlP.h [new file with mode: 0644]
drivers/gpu/drm/armada/armada_output.c [new file with mode: 0644]
drivers/gpu/drm/armada/armada_output.h [new file with mode: 0644]
drivers/gpu/drm/armada/armada_overlay.c [new file with mode: 0644]
drivers/gpu/drm/armada/armada_slave.c [new file with mode: 0644]
drivers/gpu/drm/armada/armada_slave.h [new file with mode: 0644]
drivers/gpu/drm/ast/Kconfig
drivers/gpu/drm/ast/ast_drv.c
drivers/gpu/drm/ast/ast_drv.h
drivers/gpu/drm/ast/ast_main.c
drivers/gpu/drm/cirrus/Kconfig
drivers/gpu/drm/cirrus/cirrus_drv.c
drivers/gpu/drm/cirrus/cirrus_drv.h
drivers/gpu/drm/cirrus/cirrus_main.c
drivers/gpu/drm/cirrus/cirrus_mode.c
drivers/gpu/drm/drm_context.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_debugfs.c
drivers/gpu/drm/drm_dp_helper.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_edid_load.c
drivers/gpu/drm/drm_encoder_slave.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_flip_work.c
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/drm_global.c
drivers/gpu/drm/drm_info.c
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_irq.c
drivers/gpu/drm/drm_lock.c
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/drm_pci.c
drivers/gpu/drm/drm_platform.c
drivers/gpu/drm/drm_prime.c
drivers/gpu/drm/drm_stub.c
drivers/gpu/drm/drm_sysfs.c
drivers/gpu/drm/drm_usb.c
drivers/gpu/drm/drm_vm.c
drivers/gpu/drm/exynos/Kconfig
drivers/gpu/drm/exynos/exynos_drm_drv.c
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_vidi.c
drivers/gpu/drm/gma500/Kconfig
drivers/gpu/drm/gma500/cdv_device.c
drivers/gpu/drm/gma500/cdv_intel_dp.c
drivers/gpu/drm/gma500/framebuffer.c
drivers/gpu/drm/gma500/gem.c
drivers/gpu/drm/gma500/intel_gmbus.c
drivers/gpu/drm/gma500/oaktrail_crtc.c
drivers/gpu/drm/gma500/oaktrail_device.c
drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
drivers/gpu/drm/gma500/oaktrail_lvds.c
drivers/gpu/drm/gma500/psb_device.c
drivers/gpu/drm/gma500/psb_drv.c
drivers/gpu/drm/gma500/psb_drv.h
drivers/gpu/drm/gma500/psb_intel_display.c
drivers/gpu/drm/gma500/psb_intel_sdvo.c
drivers/gpu/drm/gma500/psb_irq.c
drivers/gpu/drm/i2c/tda998x_drv.c
drivers/gpu/drm/i810/i810_dma.c
drivers/gpu/drm/i915/Kconfig [new file with mode: 0644]
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/dvo.h
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_context.c
drivers/gpu/drm/i915/i915_gem_evict.c
drivers/gpu/drm/i915/i915_gem_execbuffer.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_gem_stolen.c
drivers/gpu/drm/i915/i915_gem_tiling.c
drivers/gpu/drm/i915/i915_gpu_error.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/i915_sysfs.c
drivers/gpu/drm/i915/i915_trace.h
drivers/gpu/drm/i915/intel_acpi.c
drivers/gpu/drm/i915/intel_bios.c
drivers/gpu/drm/i915/intel_bios.h
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_dsi.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_dsi.h [new file with mode: 0644]
drivers/gpu/drm/i915/intel_dsi_cmd.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_dsi_cmd.h [new file with mode: 0644]
drivers/gpu/drm/i915/intel_dsi_pll.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_dvo.c
drivers/gpu/drm/i915/intel_fbdev.c [moved from drivers/gpu/drm/i915/intel_fb.c with 89% similarity]
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_i2c.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_opregion.c
drivers/gpu/drm/i915/intel_overlay.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/i915/intel_ringbuffer.h
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/i915/intel_sideband.c
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/i915/intel_uncore.c
drivers/gpu/drm/mga/mga_dma.c
drivers/gpu/drm/mga/mga_irq.c
drivers/gpu/drm/mgag200/Kconfig
drivers/gpu/drm/mgag200/mgag200_drv.c
drivers/gpu/drm/mgag200/mgag200_drv.h
drivers/gpu/drm/mgag200/mgag200_main.c
drivers/gpu/drm/mgag200/mgag200_mode.c
drivers/gpu/drm/msm/Kconfig
drivers/gpu/drm/msm/Makefile
drivers/gpu/drm/msm/adreno/a2xx.xml.h
drivers/gpu/drm/msm/adreno/a3xx.xml.h
drivers/gpu/drm/msm/adreno/adreno_common.xml.h
drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h
drivers/gpu/drm/msm/dsi/dsi.xml.h
drivers/gpu/drm/msm/dsi/mmss_cc.xml.h
drivers/gpu/drm/msm/dsi/sfpb.xml.h
drivers/gpu/drm/msm/hdmi/hdmi.xml.h
drivers/gpu/drm/msm/hdmi/qfprom.xml.h
drivers/gpu/drm/msm/mdp4/mdp4.xml.h
drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
drivers/gpu/drm/msm/mdp4/mdp4_format.c
drivers/gpu/drm/msm/mdp4/mdp4_kms.c
drivers/gpu/drm/msm/mdp4/mdp4_kms.h
drivers/gpu/drm/msm/mdp4/mdp4_plane.c
drivers/gpu/drm/msm/msm_drv.c
drivers/gpu/drm/msm/msm_drv.h
drivers/gpu/drm/msm/msm_gem.c
drivers/gpu/drm/msm/msm_gem.h
drivers/gpu/drm/msm/msm_gem_prime.c [new file with mode: 0644]
drivers/gpu/drm/msm/msm_gpu.c
drivers/gpu/drm/nouveau/Kconfig
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/core/core/event.c
drivers/gpu/drm/nouveau/core/core/option.c
drivers/gpu/drm/nouveau/core/core/printk.c
drivers/gpu/drm/nouveau/core/engine/device/base.c
drivers/gpu/drm/nouveau/core/engine/device/ctrl.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/device/nv04.c
drivers/gpu/drm/nouveau/core/engine/device/nv10.c
drivers/gpu/drm/nouveau/core/engine/device/nv20.c
drivers/gpu/drm/nouveau/core/engine/device/nv30.c
drivers/gpu/drm/nouveau/core/engine/device/nv40.c
drivers/gpu/drm/nouveau/core/engine/device/nv50.c
drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
drivers/gpu/drm/nouveau/core/engine/device/nve0.c
drivers/gpu/drm/nouveau/core/engine/device/priv.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/dport.c
drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c
drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/mpeg/nv40.c
drivers/gpu/drm/nouveau/core/engine/mpeg/nv44.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/base.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/daemon.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/nv40.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/nv40.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/nv50.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/nv84.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/nva3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/nvc0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/nvc0.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/nve0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/nvf0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/perfmon/priv.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/software/nv04.c
drivers/gpu/drm/nouveau/core/engine/software/nv10.c
drivers/gpu/drm/nouveau/core/engine/software/nv50.c
drivers/gpu/drm/nouveau/core/engine/software/nv50.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
drivers/gpu/drm/nouveau/core/include/core/class.h
drivers/gpu/drm/nouveau/core/include/core/debug.h
drivers/gpu/drm/nouveau/core/include/core/device.h
drivers/gpu/drm/nouveau/core/include/core/event.h
drivers/gpu/drm/nouveau/core/include/core/option.h
drivers/gpu/drm/nouveau/core/include/core/printk.h
drivers/gpu/drm/nouveau/core/include/engine/fifo.h
drivers/gpu/drm/nouveau/core/include/engine/mpeg.h
drivers/gpu/drm/nouveau/core/include/engine/perfmon.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/engine/software.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/boost.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/bios/cstep.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/bios/gpio.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/perf.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/bios/timing.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/bios/vmap.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/bios/volt.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/bus.h
drivers/gpu/drm/nouveau/core/include/subdev/clock.h
drivers/gpu/drm/nouveau/core/include/subdev/fb.h
drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
drivers/gpu/drm/nouveau/core/include/subdev/mc.h
drivers/gpu/drm/nouveau/core/include/subdev/pwr.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/therm.h
drivers/gpu/drm/nouveau/core/include/subdev/volt.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bios/boost.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bios/cstep.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bios/dp.c
drivers/gpu/drm/nouveau/core/subdev/bios/init.c
drivers/gpu/drm/nouveau/core/subdev/bios/perf.c
drivers/gpu/drm/nouveau/core/subdev/bios/pll.c
drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bios/timing.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bios/vmap.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bios/volt.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/hwsq.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/hwsq.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nv04.c
drivers/gpu/drm/nouveau/core/subdev/bus/nv04.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nv31.c
drivers/gpu/drm/nouveau/core/subdev/bus/nv50.c
drivers/gpu/drm/nouveau/core/subdev/bus/nv94.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/clock/base.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c
drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c
drivers/gpu/drm/nouveau/core/subdev/clock/nv50.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/clock/nv84.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c
drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c
drivers/gpu/drm/nouveau/core/subdev/clock/seq.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c
drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c
drivers/gpu/drm/nouveau/core/subdev/fb/base.c
drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv04.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv1a.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv40.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/nv41.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv44.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv46.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv47.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv49.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv4e.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv50.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/nv84.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/nva3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/nvaa.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/nvaf.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/nve0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnvaa.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramseq.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
drivers/gpu/drm/nouveau/core/subdev/mc/base.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv04.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/mc/nv40.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv94.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/mc/nvc3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/mxm/base.c
drivers/gpu/drm/nouveau/core/subdev/pwr/base.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/host.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/idle.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/perf.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/test.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/nv108.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/nva3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/nvc0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/pwr/nvd0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/base.c
drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c
drivers/gpu/drm/nouveau/core/subdev/therm/ic.c
drivers/gpu/drm/nouveau/core/subdev/therm/nv84.c
drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
drivers/gpu/drm/nouveau/core/subdev/therm/temp.c
drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c
drivers/gpu/drm/nouveau/core/subdev/volt/base.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/volt/gpio.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/volt/nv40.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv04/Makefile
drivers/gpu/drm/nouveau/dispnv04/arb.c
drivers/gpu/drm/nouveau/dispnv04/crtc.c
drivers/gpu/drm/nouveau/dispnv04/dfp.c
drivers/gpu/drm/nouveau/dispnv04/disp.c
drivers/gpu/drm/nouveau/dispnv04/disp.h
drivers/gpu/drm/nouveau/dispnv04/hw.c
drivers/gpu/drm/nouveau/dispnv04/overlay.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
drivers/gpu/drm/nouveau/nouveau_abi16.c
drivers/gpu/drm/nouveau/nouveau_acpi.c
drivers/gpu/drm/nouveau/nouveau_agp.c
drivers/gpu/drm/nouveau/nouveau_backlight.c
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nouveau_bo.c
drivers/gpu/drm/nouveau/nouveau_bo.h
drivers/gpu/drm/nouveau/nouveau_chan.c
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_connector.h
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nouveau_display.h
drivers/gpu/drm/nouveau/nouveau_dma.h
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_drm.h
drivers/gpu/drm/nouveau/nouveau_fbcon.c
drivers/gpu/drm/nouveau/nouveau_fence.c
drivers/gpu/drm/nouveau/nouveau_gem.c
drivers/gpu/drm/nouveau/nouveau_gem.h
drivers/gpu/drm/nouveau/nouveau_hwmon.c [moved from drivers/gpu/drm/nouveau/nouveau_pm.c with 57% similarity]
drivers/gpu/drm/nouveau/nouveau_hwmon.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/nouveau_hwsq.h [deleted file]
drivers/gpu/drm/nouveau/nouveau_mem.c [deleted file]
drivers/gpu/drm/nouveau/nouveau_perf.c [deleted file]
drivers/gpu/drm/nouveau/nouveau_pm.h [deleted file]
drivers/gpu/drm/nouveau/nouveau_prime.c
drivers/gpu/drm/nouveau/nouveau_sysfs.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nouveau_sysfs.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/nouveau_volt.c [deleted file]
drivers/gpu/drm/nouveau/nv04_fbcon.c
drivers/gpu/drm/nouveau/nv04_pm.c [deleted file]
drivers/gpu/drm/nouveau/nv40_pm.c [deleted file]
drivers/gpu/drm/nouveau/nv50_pm.c [deleted file]
drivers/gpu/drm/nouveau/nva3_pm.c [deleted file]
drivers/gpu/drm/nouveau/nvc0_pm.c [deleted file]
drivers/gpu/drm/omapdrm/Kconfig
drivers/gpu/drm/omapdrm/omap_drv.c
drivers/gpu/drm/omapdrm/omap_drv.h
drivers/gpu/drm/omapdrm/omap_gem.c
drivers/gpu/drm/omapdrm/omap_irq.c
drivers/gpu/drm/qxl/Kconfig
drivers/gpu/drm/qxl/qxl_display.c
drivers/gpu/drm/qxl/qxl_drv.c
drivers/gpu/drm/qxl/qxl_drv.h
drivers/gpu/drm/qxl/qxl_fb.c
drivers/gpu/drm/qxl/qxl_gem.c
drivers/gpu/drm/qxl/qxl_kms.c
drivers/gpu/drm/qxl/qxl_ttm.c
drivers/gpu/drm/radeon/atombios.h
drivers/gpu/drm/radeon/atombios_crtc.c
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/radeon/atombios_encoders.c
drivers/gpu/drm/radeon/ci_dpm.c
drivers/gpu/drm/radeon/ci_smc.c
drivers/gpu/drm/radeon/cik.c
drivers/gpu/drm/radeon/cik_sdma.c
drivers/gpu/drm/radeon/cikd.h
drivers/gpu/drm/radeon/dce6_afmt.c
drivers/gpu/drm/radeon/evergreen.c
drivers/gpu/drm/radeon/evergreen_hdmi.c
drivers/gpu/drm/radeon/evergreend.h
drivers/gpu/drm/radeon/ni.c
drivers/gpu/drm/radeon/ni_dma.c
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/r600_cs.c
drivers/gpu/drm/radeon/r600_hdmi.c
drivers/gpu/drm/radeon/r600d.h
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_acpi.c
drivers/gpu/drm/radeon/radeon_asic.c
drivers/gpu/drm/radeon/radeon_asic.h
drivers/gpu/drm/radeon/radeon_atpx_handler.c
drivers/gpu/drm/radeon/radeon_bios.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_cs.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_drv.h
drivers/gpu/drm/radeon/radeon_family.h
drivers/gpu/drm/radeon/radeon_fence.c
drivers/gpu/drm/radeon/radeon_gart.c
drivers/gpu/drm/radeon/radeon_gem.c
drivers/gpu/drm/radeon/radeon_ioc32.c
drivers/gpu/drm/radeon/radeon_irq_kms.c
drivers/gpu/drm/radeon/radeon_kms.c
drivers/gpu/drm/radeon/radeon_legacy_crtc.c
drivers/gpu/drm/radeon/radeon_legacy_encoders.c
drivers/gpu/drm/radeon/radeon_mode.h
drivers/gpu/drm/radeon/radeon_pm.c
drivers/gpu/drm/radeon/radeon_trace.h
drivers/gpu/drm/radeon/radeon_ucode.h
drivers/gpu/drm/radeon/radeon_uvd.c
drivers/gpu/drm/radeon/rs600.c
drivers/gpu/drm/radeon/rs690.c
drivers/gpu/drm/radeon/rv515.c
drivers/gpu/drm/radeon/rv6xx_dpm.c
drivers/gpu/drm/radeon/si.c
drivers/gpu/drm/radeon/si_dma.c
drivers/gpu/drm/radeon/si_dpm.c
drivers/gpu/drm/radeon/sid.h
drivers/gpu/drm/rcar-du/Kconfig
drivers/gpu/drm/shmobile/Kconfig
drivers/gpu/drm/shmobile/shmob_drm_crtc.c
drivers/gpu/drm/tegra/Kconfig [moved from drivers/gpu/host1x/drm/Kconfig with 87% similarity]
drivers/gpu/drm/tegra/Makefile [new file with mode: 0644]
drivers/gpu/drm/tegra/bus.c [new file with mode: 0644]
drivers/gpu/drm/tegra/dc.c [moved from drivers/gpu/host1x/drm/dc.c with 93% similarity]
drivers/gpu/drm/tegra/dc.h [moved from drivers/gpu/host1x/drm/dc.h with 98% similarity]
drivers/gpu/drm/tegra/drm.c [new file with mode: 0644]
drivers/gpu/drm/tegra/drm.h [moved from drivers/gpu/host1x/drm/drm.h with 72% similarity]
drivers/gpu/drm/tegra/fb.c [moved from drivers/gpu/host1x/drm/fb.c with 92% similarity]
drivers/gpu/drm/tegra/gem.c [moved from drivers/gpu/host1x/drm/gem.c with 86% similarity]
drivers/gpu/drm/tegra/gem.h [moved from drivers/gpu/host1x/drm/gem.h with 84% similarity]
drivers/gpu/drm/tegra/gr2d.c [new file with mode: 0644]
drivers/gpu/drm/tegra/gr2d.h [new file with mode: 0644]
drivers/gpu/drm/tegra/gr3d.c [new file with mode: 0644]
drivers/gpu/drm/tegra/gr3d.h [new file with mode: 0644]
drivers/gpu/drm/tegra/hdmi.c [moved from drivers/gpu/host1x/drm/hdmi.c with 83% similarity]
drivers/gpu/drm/tegra/hdmi.h [moved from drivers/gpu/host1x/drm/hdmi.h with 72% similarity]
drivers/gpu/drm/tegra/output.c [moved from drivers/gpu/host1x/drm/output.c with 91% similarity]
drivers/gpu/drm/tegra/rgb.c [moved from drivers/gpu/host1x/drm/rgb.c with 96% similarity]
drivers/gpu/drm/tilcdc/Kconfig
drivers/gpu/drm/ttm/Makefile
drivers/gpu/drm/ttm/ttm_bo.c
drivers/gpu/drm/ttm/ttm_bo_util.c
drivers/gpu/drm/ttm/ttm_bo_vm.c
drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
drivers/gpu/drm/udl/Kconfig
drivers/gpu/drm/udl/udl_drv.c
drivers/gpu/drm/udl/udl_drv.h
drivers/gpu/drm/udl/udl_gem.c
drivers/gpu/drm/via/via_mm.c
drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
drivers/gpu/host1x/Kconfig
drivers/gpu/host1x/Makefile
drivers/gpu/host1x/bus.c [new file with mode: 0644]
drivers/gpu/host1x/bus.h [moved from drivers/gpu/host1x/host1x_client.h with 60% similarity]
drivers/gpu/host1x/cdma.c
drivers/gpu/host1x/channel.h
drivers/gpu/host1x/dev.c
drivers/gpu/host1x/dev.h
drivers/gpu/host1x/drm/drm.c [deleted file]
drivers/gpu/host1x/drm/gr2d.c [deleted file]
drivers/gpu/host1x/host1x.h [deleted file]
drivers/gpu/host1x/host1x_bo.h [deleted file]
drivers/gpu/host1x/hw/Makefile [deleted file]
drivers/gpu/host1x/hw/cdma_hw.c
drivers/gpu/host1x/hw/channel_hw.c
drivers/gpu/host1x/hw/debug_hw.c
drivers/gpu/host1x/hw/host1x01.c
drivers/gpu/host1x/hw/host1x02.c [new file with mode: 0644]
drivers/gpu/host1x/hw/host1x02.h [new file with mode: 0644]
drivers/gpu/host1x/hw/hw_host1x01_uclass.h
drivers/gpu/host1x/hw/hw_host1x02_channel.h [new file with mode: 0644]
drivers/gpu/host1x/hw/hw_host1x02_sync.h [new file with mode: 0644]
drivers/gpu/host1x/hw/hw_host1x02_uclass.h [new file with mode: 0644]
drivers/gpu/host1x/hw/intr_hw.c
drivers/gpu/host1x/hw/syncpt_hw.c
drivers/gpu/host1x/job.c
drivers/gpu/host1x/job.h
drivers/gpu/host1x/syncpt.c
drivers/gpu/host1x/syncpt.h
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-apple.c
drivers/hid/hid-axff.c
drivers/hid/hid-core.c
drivers/hid/hid-elo.c
drivers/hid/hid-holtek-mouse.c
drivers/hid/hid-ids.h
drivers/hid/hid-lenovo-tpkbd.c
drivers/hid/hid-lg.c
drivers/hid/hid-lg2ff.c
drivers/hid/hid-lg4ff.c
drivers/hid/hid-logitech-dj.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-roccat-common.c
drivers/hid/hid-roccat-common.h
drivers/hid/hid-roccat-konepure.c
drivers/hid/hid-roccat-konepure.h [deleted file]
drivers/hid/hid-roccat-kovaplus.c
drivers/hid/hid-roccat-ryos.c [new file with mode: 0644]
drivers/hid/hid-roccat-savu.c
drivers/hid/hid-roccat-savu.h
drivers/hid/hid-sensor-hub.c
drivers/hid/hid-sony.c
drivers/hid/hid-wiimote-modules.c
drivers/hid/hid-wiimote.h
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/usbhid/hid-quirks.c
drivers/hwmon/Kconfig
drivers/hwmon/acpi_power_meter.c
drivers/hwmon/jz4740-hwmon.c
drivers/hwmon/lm75.c
drivers/hwmon/lm90.c
drivers/hwmon/nct6775.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-at91.c
drivers/i2c/busses/i2c-bcm-kona.c [new file with mode: 0644]
drivers/i2c/busses/i2c-bcm2835.c
drivers/i2c/busses/i2c-bfin-twi.c
drivers/i2c/busses/i2c-cbus-gpio.c
drivers/i2c/busses/i2c-davinci.c
drivers/i2c/busses/i2c-designware-core.c
drivers/i2c/busses/i2c-designware-platdrv.c
drivers/i2c/busses/i2c-eg20t.c
drivers/i2c/busses/i2c-exynos5.c [new file with mode: 0644]
drivers/i2c/busses/i2c-gpio.c
drivers/i2c/busses/i2c-i801.c
drivers/i2c/busses/i2c-ismt.c
drivers/i2c/busses/i2c-mv64xxx.c
drivers/i2c/busses/i2c-mxs.c
drivers/i2c/busses/i2c-omap.c
drivers/i2c/busses/i2c-pnx.c
drivers/i2c/busses/i2c-rcar.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/busses/i2c-scmi.c
drivers/i2c/busses/i2c-sh_mobile.c
drivers/i2c/busses/i2c-st.c [new file with mode: 0644]
drivers/i2c/busses/i2c-tegra.c
drivers/i2c/busses/i2c-wmt.c
drivers/i2c/busses/i2c-xiic.c
drivers/i2c/i2c-core.c
drivers/i2c/i2c-dev.c
drivers/i2c/i2c-smbus.c
drivers/i2c/muxes/i2c-arb-gpio-challenge.c
drivers/i2c/muxes/i2c-mux-gpio.c
drivers/i2c/muxes/i2c-mux-pinctrl.c
drivers/ide/cs5536.c
drivers/ide/ide-acpi.c
drivers/ide/pmac.c
drivers/idle/intel_idle.c
drivers/iio/adc/ad_sigma_delta.c
drivers/iio/adc/nau7802.c
drivers/iio/industrialio-event.c
drivers/infiniband/Kconfig
drivers/infiniband/core/cm.c
drivers/infiniband/core/cma.c
drivers/infiniband/core/netlink.c
drivers/infiniband/core/sysfs.c
drivers/infiniband/core/ucma.c
drivers/infiniband/core/uverbs.h
drivers/infiniband/core/uverbs_cmd.c
drivers/infiniband/core/uverbs_main.c
drivers/infiniband/core/verbs.c
drivers/infiniband/hw/cxgb4/device.c
drivers/infiniband/hw/ipath/ipath_user_sdma.c
drivers/infiniband/hw/mlx4/cq.c
drivers/infiniband/hw/mlx4/main.c
drivers/infiniband/hw/mlx5/cq.c
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/mlx5/mlx5_ib.h
drivers/infiniband/hw/mlx5/mr.c
drivers/infiniband/hw/mlx5/qp.c
drivers/infiniband/hw/mlx5/srq.c
drivers/infiniband/hw/nes/nes_verbs.c
drivers/infiniband/hw/ocrdma/ocrdma.h
drivers/infiniband/hw/ocrdma/ocrdma_hw.c
drivers/infiniband/hw/ocrdma/ocrdma_main.c
drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
drivers/infiniband/hw/qib/qib_iba7322.c
drivers/infiniband/hw/qib/qib_mad.h
drivers/infiniband/hw/qib/qib_user_sdma.c
drivers/infiniband/hw/qib/qib_verbs.h
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_cm.c
drivers/infiniband/ulp/ipoib/ipoib_ib.c
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/ipoib/ipoib_multicast.c
drivers/infiniband/ulp/ipoib/ipoib_netlink.c
drivers/infiniband/ulp/ipoib/ipoib_vlan.c
drivers/infiniband/ulp/isert/Kconfig
drivers/infiniband/ulp/isert/ib_isert.c
drivers/infiniband/ulp/isert/ib_isert.h
drivers/infiniband/ulp/srp/ib_srp.c
drivers/infiniband/ulp/srp/ib_srp.h
drivers/infiniband/ulp/srpt/ib_srpt.c
drivers/input/Kconfig
drivers/input/evdev.c
drivers/input/input.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/gpio_keys_polled.c
drivers/input/keyboard/lpc32xx-keys.c
drivers/input/keyboard/nspire-keypad.c
drivers/input/keyboard/pxa27x_keypad.c
drivers/input/keyboard/tegra-kbc.c
drivers/input/keyboard/tnetv107x-keypad.c
drivers/input/misc/Kconfig
drivers/input/misc/ad714x-spi.c
drivers/input/misc/cobalt_btns.c
drivers/input/misc/mma8450.c
drivers/input/misc/mpu3050.c
drivers/input/misc/pwm-beeper.c
drivers/input/misc/rb532_button.c
drivers/input/misc/rotary_encoder.c
drivers/input/misc/sirfsoc-onkey.c
drivers/input/misc/uinput.c
drivers/input/mouse/alps.c
drivers/input/mouse/cypress_ps2.c
drivers/input/serio/Kconfig
drivers/input/serio/Makefile
drivers/input/serio/hyperv-keyboard.c [new file with mode: 0644]
drivers/input/serio/i8042-x86ia64io.h
drivers/input/serio/i8042.c
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.h
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ad7877.c
drivers/input/touchscreen/ad7879-spi.c
drivers/input/touchscreen/cyttsp4_core.c
drivers/input/touchscreen/cyttsp4_spi.c
drivers/input/touchscreen/cyttsp_core.c
drivers/input/touchscreen/egalax_ts.c
drivers/input/touchscreen/htcpen.c
drivers/input/touchscreen/st1232.c
drivers/input/touchscreen/ti_am335x_tsc.c
drivers/input/touchscreen/tsc2005.c
drivers/input/touchscreen/usbtouchscreen.c
drivers/input/touchscreen/zforce_ts.c [new file with mode: 0644]
drivers/iommu/Kconfig
drivers/iommu/Makefile
drivers/iommu/arm-smmu.c
drivers/iommu/dmar.c
drivers/iommu/intel-iommu.c
drivers/iommu/intel_irq_remapping.c
drivers/iommu/iommu-traces.c [new file with mode: 0644]
drivers/iommu/iommu.c
drivers/iommu/tegra-gart.c
drivers/iommu/tegra-smmu.c
drivers/isdn/isdnloop/isdnloop.c
drivers/isdn/mISDN/socket.c
drivers/lguest/lguest_device.c
drivers/lguest/x86/core.c
drivers/md/bcache/Kconfig
drivers/md/bcache/alloc.c
drivers/md/bcache/bcache.h
drivers/md/bcache/bset.c
drivers/md/bcache/bset.h
drivers/md/bcache/btree.c
drivers/md/bcache/btree.h
drivers/md/bcache/closure.c
drivers/md/bcache/closure.h
drivers/md/bcache/debug.c
drivers/md/bcache/debug.h
drivers/md/bcache/journal.c
drivers/md/bcache/journal.h
drivers/md/bcache/movinggc.c
drivers/md/bcache/request.c
drivers/md/bcache/request.h
drivers/md/bcache/stats.c
drivers/md/bcache/stats.h
drivers/md/bcache/super.c
drivers/md/bcache/sysfs.c
drivers/md/bcache/trace.c
drivers/md/bcache/util.c
drivers/md/bcache/util.h
drivers/md/bcache/writeback.c
drivers/md/bcache/writeback.h
drivers/md/dm-crypt.c
drivers/md/md.c
drivers/md/raid1.c
drivers/md/raid1.h
drivers/md/raid10.c
drivers/md/raid5.c
drivers/md/raid5.h
drivers/media/common/b2c2/flexcop-sram.c
drivers/media/common/saa7146/saa7146_core.c
drivers/media/common/siano/smscoreapi.c
drivers/media/common/siano/smsdvb-main.c
drivers/media/dvb-core/dvb_demux.c
drivers/media/dvb-frontends/Kconfig
drivers/media/dvb-frontends/Makefile
drivers/media/dvb-frontends/af9013.c
drivers/media/dvb-frontends/af9033.c
drivers/media/dvb-frontends/bcm3510.c
drivers/media/dvb-frontends/cx24110.c
drivers/media/dvb-frontends/cx24117.c [new file with mode: 0644]
drivers/media/dvb-frontends/cx24117.h [new file with mode: 0644]
drivers/media/dvb-frontends/cx24123.c
drivers/media/dvb-frontends/cxd2820r_core.c
drivers/media/dvb-frontends/dib9000.c
drivers/media/dvb-frontends/drxd_hard.c
drivers/media/dvb-frontends/drxk_hard.c
drivers/media/dvb-frontends/itd1000.c
drivers/media/dvb-frontends/mt312.c
drivers/media/dvb-frontends/nxt200x.c
drivers/media/dvb-frontends/rtl2830.c
drivers/media/dvb-frontends/rtl2832.c
drivers/media/dvb-frontends/rtl2832.h
drivers/media/dvb-frontends/s5h1420.c
drivers/media/dvb-frontends/stb0899_drv.c
drivers/media/dvb-frontends/stb6100.c
drivers/media/dvb-frontends/stv0367.c
drivers/media/dvb-frontends/stv090x.c
drivers/media/dvb-frontends/stv6110.c
drivers/media/dvb-frontends/stv6110x.c
drivers/media/dvb-frontends/tda10071.c
drivers/media/dvb-frontends/tda18271c2dd.c
drivers/media/dvb-frontends/tda8083.c
drivers/media/dvb-frontends/ts2020.c
drivers/media/dvb-frontends/ts2020.h
drivers/media/dvb-frontends/zl10039.c
drivers/media/i2c/Kconfig
drivers/media/i2c/Makefile
drivers/media/i2c/adv7183.c
drivers/media/i2c/adv7343.c
drivers/media/i2c/lm3560.c [new file with mode: 0644]
drivers/media/i2c/s5c73m3/s5c73m3-core.c
drivers/media/i2c/soc_camera/imx074.c
drivers/media/i2c/soc_camera/ov9640.c
drivers/media/i2c/ths8200.c
drivers/media/i2c/tvp514x.c
drivers/media/i2c/tvp7002.c
drivers/media/pci/b2c2/flexcop-pci.c
drivers/media/pci/bt8xx/bt878.c
drivers/media/pci/bt8xx/bttv-driver.c
drivers/media/pci/cx18/cx18-driver.c
drivers/media/pci/cx23885/Kconfig
drivers/media/pci/cx23885/cimax2.c
drivers/media/pci/cx23885/cx23885-cards.c
drivers/media/pci/cx23885/cx23885-core.c
drivers/media/pci/cx23885/cx23885-dvb.c
drivers/media/pci/cx23885/cx23885-input.c
drivers/media/pci/cx23885/cx23885-video.c
drivers/media/pci/cx23885/cx23885.h
drivers/media/pci/cx25821/cx25821-cards.c
drivers/media/pci/cx25821/cx25821-medusa-video.c
drivers/media/pci/cx25821/cx25821-medusa-video.h
drivers/media/pci/cx25821/cx25821-video-upstream.c
drivers/media/pci/cx88/cx88-alsa.c
drivers/media/pci/cx88/cx88-mpeg.c
drivers/media/pci/cx88/cx88-video.c
drivers/media/pci/ddbridge/ddbridge-core.c
drivers/media/pci/dm1105/dm1105.c
drivers/media/pci/ivtv/ivtv-driver.c
drivers/media/pci/mantis/mantis_pci.c
drivers/media/pci/meye/meye.c
drivers/media/pci/ngene/ngene-core.c
drivers/media/pci/pluto2/pluto2.c
drivers/media/pci/pt1/pt1.c
drivers/media/pci/saa7134/saa7134-alsa.c
drivers/media/pci/saa7134/saa7134-core.c
drivers/media/pci/saa7164/saa7164-core.c
drivers/media/pci/ttpci/av7110_hw.c
drivers/media/pci/zoran/Kconfig
drivers/media/pci/zoran/zoran_card.c
drivers/media/platform/Kconfig
drivers/media/platform/Makefile
drivers/media/platform/blackfin/bfin_capture.c
drivers/media/platform/coda.c
drivers/media/platform/davinci/vpbe_display.c
drivers/media/platform/davinci/vpfe_capture.c
drivers/media/platform/davinci/vpif_capture.c
drivers/media/platform/exynos-gsc/gsc-core.h
drivers/media/platform/exynos-gsc/gsc-m2m.c
drivers/media/platform/exynos4-is/fimc-isp.c
drivers/media/platform/exynos4-is/media-dev.c
drivers/media/platform/m2m-deinterlace.c
drivers/media/platform/marvell-ccic/mcam-core.c
drivers/media/platform/marvell-ccic/mmp-driver.c
drivers/media/platform/mem2mem_testdev.c
drivers/media/platform/s5p-g2d/g2d.c
drivers/media/platform/s5p-mfc/s5p_mfc.c
drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c
drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
drivers/media/platform/s5p-tv/mixer_grp_layer.c
drivers/media/platform/s5p-tv/mixer_vp_layer.c
drivers/media/platform/soc_camera/rcar_vin.c
drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
drivers/media/platform/soc_camera/soc_camera.c
drivers/media/platform/ti-vpe/Makefile [new file with mode: 0644]
drivers/media/platform/ti-vpe/vpdma.c [new file with mode: 0644]
drivers/media/platform/ti-vpe/vpdma.h [new file with mode: 0644]
drivers/media/platform/ti-vpe/vpdma_priv.h [new file with mode: 0644]
drivers/media/platform/ti-vpe/vpe.c [new file with mode: 0644]
drivers/media/platform/ti-vpe/vpe_regs.h [new file with mode: 0644]
drivers/media/platform/timblogiw.c
drivers/media/radio/radio-keene.c
drivers/media/radio/radio-sf16fmr2.c
drivers/media/radio/radio-shark.c
drivers/media/radio/radio-shark2.c
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/si4713-i2c.c
drivers/media/radio/tef6862.c
drivers/media/radio/wl128x/fmdrv_common.c
drivers/media/rc/Kconfig
drivers/media/rc/Makefile
drivers/media/rc/fintek-cir.h
drivers/media/rc/gpio-ir-recv.c
drivers/media/rc/iguanair.c
drivers/media/rc/ir-rx51.c
drivers/media/rc/keymaps/rc-dib0700-nec.c
drivers/media/rc/keymaps/rc-dib0700-rc5.c
drivers/media/rc/nuvoton-cir.h
drivers/media/rc/st_rc.c [new file with mode: 0644]
drivers/media/rc/winbond-cir.c
drivers/media/tuners/e4000.c
drivers/media/tuners/fc0012.c
drivers/media/tuners/fc0013.c
drivers/media/tuners/fc2580.c
drivers/media/tuners/r820t.c
drivers/media/tuners/tda18212.c
drivers/media/tuners/tda18218.c
drivers/media/tuners/tda9887.c
drivers/media/tuners/tuner-xc2028.c
drivers/media/usb/b2c2/flexcop-usb.c
drivers/media/usb/cpia2/cpia2_usb.c
drivers/media/usb/cx231xx/cx231xx-cards.c
drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
drivers/media/usb/dvb-usb-v2/af9015.c
drivers/media/usb/dvb-usb-v2/af9035.c
drivers/media/usb/dvb-usb-v2/mxl111sf.c
drivers/media/usb/dvb-usb-v2/rtl28xxu.c
drivers/media/usb/dvb-usb-v2/rtl28xxu.h
drivers/media/usb/dvb-usb/az6027.c
drivers/media/usb/dvb-usb/cxusb.c
drivers/media/usb/dvb-usb/dibusb-common.c
drivers/media/usb/dvb-usb/dw2102.c
drivers/media/usb/em28xx/em28xx-camera.c
drivers/media/usb/em28xx/em28xx-cards.c
drivers/media/usb/em28xx/em28xx-dvb.c
drivers/media/usb/em28xx/em28xx-video.c
drivers/media/usb/em28xx/em28xx.h
drivers/media/usb/gspca/conex.c
drivers/media/usb/gspca/cpia1.c
drivers/media/usb/gspca/gspca.c
drivers/media/usb/gspca/gspca.h
drivers/media/usb/gspca/jeilinj.c
drivers/media/usb/gspca/jl2005bcd.c
drivers/media/usb/gspca/m5602/m5602_mt9m111.c
drivers/media/usb/gspca/mars.c
drivers/media/usb/gspca/mr97310a.c
drivers/media/usb/gspca/nw80x.c
drivers/media/usb/gspca/ov519.c
drivers/media/usb/gspca/ov534.c
drivers/media/usb/gspca/ov534_9.c
drivers/media/usb/gspca/pac207.c
drivers/media/usb/gspca/pac7311.c
drivers/media/usb/gspca/se401.c
drivers/media/usb/gspca/sn9c20x.c
drivers/media/usb/gspca/sonixb.c
drivers/media/usb/gspca/sonixj.c
drivers/media/usb/gspca/spca1528.c
drivers/media/usb/gspca/spca500.c
drivers/media/usb/gspca/sq905c.c
drivers/media/usb/gspca/sq930x.c
drivers/media/usb/gspca/stk014.c
drivers/media/usb/gspca/stk1135.c
drivers/media/usb/gspca/stv06xx/stv06xx.c
drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c
drivers/media/usb/gspca/sunplus.c
drivers/media/usb/gspca/topro.c
drivers/media/usb/gspca/tv8532.c
drivers/media/usb/gspca/vicam.c
drivers/media/usb/gspca/w996Xcf.c
drivers/media/usb/gspca/xirlink_cit.c
drivers/media/usb/gspca/zc3xx.c
drivers/media/usb/hdpvr/hdpvr-core.c
drivers/media/usb/pvrusb2/pvrusb2-hdw.c
drivers/media/usb/siano/smsusb.c
drivers/media/usb/tlg2300/pd-main.c
drivers/media/usb/ttusb-dec/ttusb_dec.c
drivers/media/usb/uvc/uvc_ctrl.c
drivers/media/usb/uvc/uvc_video.c
drivers/media/v4l2-core/tuner-core.c
drivers/media/v4l2-core/v4l2-async.c
drivers/media/v4l2-core/v4l2-clk.c
drivers/media/v4l2-core/v4l2-common.c
drivers/media/v4l2-core/v4l2-ctrls.c
drivers/media/v4l2-core/v4l2-mem2mem.c
drivers/media/v4l2-core/videobuf2-core.c
drivers/media/v4l2-core/videobuf2-dma-sg.c
drivers/memstick/core/memstick.c
drivers/memstick/core/ms_block.c
drivers/memstick/core/ms_block.h
drivers/memstick/host/r592.c
drivers/mfd/88pm860x-core.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/aat2870-core.c
drivers/mfd/arizona-core.c
drivers/mfd/arizona-i2c.c
drivers/mfd/arizona-spi.c
drivers/mfd/as3711.c
drivers/mfd/as3722.c [new file with mode: 0644]
drivers/mfd/da9052-i2c.c
drivers/mfd/ezx-pcap.c
drivers/mfd/lpc_ich.c
drivers/mfd/lpc_sch.c
drivers/mfd/max77686.c
drivers/mfd/max77693-irq.c
drivers/mfd/max77693.c
drivers/mfd/max8907.c
drivers/mfd/max8925-i2c.c
drivers/mfd/max8997.c
drivers/mfd/mc13xxx-i2c.c
drivers/mfd/mfd-core.c
drivers/mfd/omap-usb-host.c
drivers/mfd/omap-usb-tll.c
drivers/mfd/palmas.c
drivers/mfd/pm8921-core.c
drivers/mfd/rts5249.c
drivers/mfd/rtsx_pcr.c
drivers/mfd/sec-core.c
drivers/mfd/sm501.c
drivers/mfd/stw481x.c [new file with mode: 0644]
drivers/mfd/tc3589x.c
drivers/mfd/ti-ssp.c
drivers/mfd/ti_am335x_tscadc.c
drivers/mfd/timberdale.c
drivers/mfd/tps6507x.c
drivers/mfd/tps65217.c
drivers/mfd/tps6586x.c
drivers/mfd/tps65910.c
drivers/mfd/twl6040.c
drivers/mfd/ucb1x00-core.c
drivers/mfd/wm5102-tables.c
drivers/mfd/wm5110-tables.c
drivers/mfd/wm8994-core.c
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/carma/carma-fpga.c
drivers/misc/eeprom/at24.c
drivers/misc/ep93xx_pwm.c [deleted file]
drivers/misc/mic/card/mic_virtio.c
drivers/misc/mic/host/mic_boot.c
drivers/misc/ti-st/st_kim.c
drivers/mmc/card/block.c
drivers/mmc/core/bus.c
drivers/mmc/core/core.c
drivers/mmc/core/core.h
drivers/mmc/core/mmc.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/sd.c
drivers/mmc/core/sdio.c
drivers/mmc/core/sdio_bus.c
drivers/mmc/host/atmel-mci.c
drivers/mmc/host/au1xmmc.c
drivers/mmc/host/bfin_sdh.c
drivers/mmc/host/cb710-mmc.c
drivers/mmc/host/davinci_mmc.c
drivers/mmc/host/dw_mmc-exynos.c
drivers/mmc/host/dw_mmc-pltfm.c
drivers/mmc/host/dw_mmc-socfpga.c
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/dw_mmc.h
drivers/mmc/host/jz4740_mmc.c
drivers/mmc/host/mmci.c
drivers/mmc/host/msm_sdcc.c
drivers/mmc/host/mvsdio.c
drivers/mmc/host/mxcmmc.c
drivers/mmc/host/mxs-mmc.c
drivers/mmc/host/omap.c
drivers/mmc/host/omap_hsmmc.c
drivers/mmc/host/pxamci.c
drivers/mmc/host/rtsx_pci_sdmmc.c
drivers/mmc/host/s3cmci.c
drivers/mmc/host/sdhci-bcm-kona.c
drivers/mmc/host/sdhci-bcm2835.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-esdhc.h
drivers/mmc/host/sdhci-of-esdhc.c
drivers/mmc/host/sdhci-pci.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mmc/host/sdricoh_cs.c
drivers/mmc/host/sh_mmcif.c
drivers/mmc/host/tifm_sd.c
drivers/mmc/host/tmio_mmc_pio.c
drivers/mmc/host/via-sdmmc.c
drivers/mmc/host/vub300.c
drivers/mmc/host/wbsd.c
drivers/mmc/host/wmt-sdmmc.c
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/docg4.c
drivers/mtd/nand/fsmc_nand.c
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/r852.c
drivers/mtd/onenand/omap2.c
drivers/net/bonding/bond_sysfs.c
drivers/net/bonding/bonding.h
drivers/net/caif/caif_virtio.c
drivers/net/ethernet/atheros/alx/main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/emulex/benet/be_cmds.c
drivers/net/ethernet/emulex/benet/be_cmds.h
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/mellanox/mlx4/fw.c
drivers/net/ethernet/mellanox/mlx5/core/cmd.c
drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
drivers/net/ethernet/mellanox/mlx5/core/eq.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/mellanox/mlx5/core/mr.c
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/micrel/ks8842.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/xscale/ixp4xx_eth.c
drivers/net/ieee802154/at86rf230.c
drivers/net/ieee802154/mrf24j40.c
drivers/net/irda/ali-ircc.c
drivers/net/irda/nsc-ircc.c
drivers/net/macvtap.c
drivers/net/phy/phy_device.c
drivers/net/phy/vitesse.c
drivers/net/ppp/pppoe.c
drivers/net/team/team.c
drivers/net/tun.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/r8152.c
drivers/net/usb/usbnet.c
drivers/net/virtio_net.c
drivers/net/wireless/ath/ath10k/htc.c
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath5k/dma.c
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h
drivers/net/wireless/ath/ath9k/ar9485_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/dfs_debug.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/carl9170/usb.c
drivers/net/wireless/ath/regd.c
drivers/net/wireless/ath/wcn36xx/debug.c
drivers/net/wireless/ath/wcn36xx/smd.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/brcm80211/brcmfmac/p2p.c
drivers/net/wireless/libertas/debugfs.c
drivers/net/wireless/libertas/if_cs.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/ie.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/uap_txrx.c
drivers/net/wireless/mwifiex/usb.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/prism54/islpci_dev.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800mmio.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00lib.h
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rtlwifi/base.c
drivers/net/wireless/rtlwifi/efuse.c
drivers/net/wireless/rtlwifi/rtl8192cu/mac.c
drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
drivers/net/wireless/rtlwifi/rtl8192de/trx.c
drivers/net/wireless/rtlwifi/rtl8192se/rf.c
drivers/net/wireless/rtlwifi/rtl8192se/trx.c
drivers/net/wireless/rtlwifi/wifi.h
drivers/net/wireless/zd1211rw/zd_usb.c
drivers/net/xen-netback/interface.c
drivers/net/xen-netfront.c
drivers/ntb/ntb_transport.c
drivers/parport/Kconfig
drivers/parport/parport_ip32.c
drivers/pci/hotplug/acpi_pcihp.c
drivers/pci/hotplug/acpiphp.h
drivers/pci/hotplug/pciehp_acpi.c
drivers/pci/hotplug/sgi_hotplug.c
drivers/pci/ioapic.c
drivers/pci/pci-acpi.c
drivers/pci/pci-label.c
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pinctrl/pinctrl-single.c
drivers/platform/x86/apple-gmux.c
drivers/pnp/pnpacpi/core.c
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/ab8500_charger.c
drivers/power/ab8500_fg.c
drivers/power/bq2415x_charger.c
drivers/power/bq24735-charger.c [new file with mode: 0644]
drivers/power/charger-manager.c
drivers/power/isp1704_charger.c
drivers/power/jz4740-battery.c
drivers/power/max17042_battery.c
drivers/power/pm2301_charger.c
drivers/power/tps65090-charger.c
drivers/power/twl4030_charger.c
drivers/pwm/Kconfig
drivers/pwm/Makefile
drivers/pwm/pwm-atmel-tcb.c
drivers/pwm/pwm-ep93xx.c [new file with mode: 0644]
drivers/pwm/pwm-imx.c
drivers/pwm/pwm-lpc32xx.c
drivers/pwm/pwm-mxs.c
drivers/pwm/pwm-samsung.c
drivers/pwm/pwm-tiecap.c
drivers/pwm/pwm-tiehrpwm.c
drivers/pwm/pwm-twl-led.c
drivers/pwm/pwm-twl.c
drivers/regulator/tps65910-regulator.c
drivers/remoteproc/remoteproc_virtio.c
drivers/rtc/Kconfig
drivers/rtc/rtc-at91rm9200.c
drivers/rtc/rtc-hid-sensor-time.c
drivers/s390/block/scm_blk.c
drivers/s390/block/scm_blk_cluster.c
drivers/s390/char/Makefile
drivers/s390/char/fs3270.c
drivers/s390/char/sclp.h
drivers/s390/char/sclp_cmd.c
drivers/s390/char/sclp_early.c [new file with mode: 0644]
drivers/s390/char/sclp_sdias.c
drivers/s390/char/sclp_sdias.h [new file with mode: 0644]
drivers/s390/char/zcore.c
drivers/s390/cio/eadm_sch.c
drivers/s390/cio/scm.c
drivers/s390/kvm/kvm_virtio.c
drivers/s390/kvm/virtio_ccw.c
drivers/s390/scsi/zfcp_dbf.c
drivers/scsi/aacraid/commctrl.c
drivers/scsi/arcmsr/arcmsr_hba.c
drivers/scsi/atp870u.c
drivers/scsi/bfa/bfad.c
drivers/scsi/bnx2i/bnx2i_hwi.c
drivers/scsi/bnx2i/bnx2i_iscsi.c
drivers/scsi/csiostor/csio_init.c
drivers/scsi/dc395x.c
drivers/scsi/fnic/fnic_main.c
drivers/scsi/gdth.c
drivers/scsi/hpsa.c
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/megaraid/megaraid_mbox.c
drivers/scsi/megaraid/megaraid_sas_base.c
drivers/scsi/mvsas/mv_init.c
drivers/scsi/mvsas/mv_sas.c
drivers/scsi/mvumi.c
drivers/scsi/ncr53c8xx.c
drivers/scsi/pm8001/pm8001_init.c
drivers/scsi/pmcraid.c
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/qla2xxx/tcm_qla2xxx.c
drivers/scsi/qla2xxx/tcm_qla2xxx.h
drivers/scsi/qla4xxx/ql4_os.c
drivers/scsi/scsi_transport_srp.c
drivers/scsi/stex.c
drivers/scsi/sym53c8xx_2/sym_glue.h
drivers/scsi/tmscsim.c
drivers/scsi/ufs/ufshcd-pci.c
drivers/scsi/virtio_scsi.c
drivers/scsi/vmw_pvscsi.c
drivers/spi/spi-bcm2835.c
drivers/spi/spi-clps711x.c
drivers/spi/spi-davinci.c
drivers/spi/spi-dw-mid.c
drivers/spi/spi-fsl-espi.c
drivers/spi/spi-fsl-spi.c
drivers/spi/spi-mpc512x-psc.c
drivers/spi/spi-mxs.c
drivers/spi/spi-s3c64xx.c
drivers/spi/spi-sh-msiof.c
drivers/spi/spi-sirf.c
drivers/spi/spi-tegra114.c
drivers/spi/spi-tegra20-sflash.c
drivers/spi/spi-tegra20-slink.c
drivers/spi/spi-xilinx.c
drivers/spi/spi.c
drivers/staging/iio/adc/ad7606.h
drivers/staging/iio/adc/mxs-lradc.c
drivers/staging/imx-drm/Kconfig
drivers/staging/imx-drm/imx-drm-core.c
drivers/staging/media/lirc/TODO
drivers/staging/media/lirc/lirc_bt829.c
drivers/staging/media/lirc/lirc_serial.c
drivers/staging/media/lirc/lirc_zilog.c
drivers/staging/media/msi3101/Kconfig
drivers/staging/media/solo6x10/solo6x10-disp.c
drivers/staging/media/solo6x10/solo6x10-p2m.c
drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c
drivers/staging/media/solo6x10/solo6x10.h
drivers/staging/tidspbridge/core/sync.c
drivers/staging/tidspbridge/include/dspbridge/sync.h
drivers/staging/tidspbridge/rmgr/drv_interface.c
drivers/target/iscsi/iscsi_target.c
drivers/target/iscsi/iscsi_target_auth.c
drivers/target/iscsi/iscsi_target_configfs.c
drivers/target/iscsi/iscsi_target_core.h
drivers/target/iscsi/iscsi_target_device.c
drivers/target/iscsi/iscsi_target_erl0.c
drivers/target/iscsi/iscsi_target_login.c
drivers/target/iscsi/iscsi_target_nego.c
drivers/target/iscsi/iscsi_target_nodeattrib.c
drivers/target/iscsi/iscsi_target_nodeattrib.h
drivers/target/iscsi/iscsi_target_stat.c
drivers/target/iscsi/iscsi_target_tpg.c
drivers/target/iscsi/iscsi_target_tpg.h
drivers/target/iscsi/iscsi_target_util.c
drivers/target/loopback/tcm_loop.c
drivers/target/loopback/tcm_loop.h
drivers/target/sbp/sbp_target.c
drivers/target/target_core_alua.c
drivers/target/target_core_alua.h
drivers/target/target_core_configfs.c
drivers/target/target_core_device.c
drivers/target/target_core_fabric_configfs.c
drivers/target/target_core_file.c
drivers/target/target_core_iblock.c
drivers/target/target_core_internal.h
drivers/target/target_core_pr.c
drivers/target/target_core_rd.c
drivers/target/target_core_sbc.c
drivers/target/target_core_spc.c
drivers/target/target_core_stat.c
drivers/target/target_core_tmr.c
drivers/target/target_core_tpg.c
drivers/target/target_core_transport.c
drivers/target/target_core_ua.h
drivers/target/target_core_xcopy.c
drivers/target/tcm_fc/tcm_fc.h
drivers/target/tcm_fc/tfc_cmd.c
drivers/target/tcm_fc/tfc_conf.c
drivers/target/tcm_fc/tfc_sess.c
drivers/thermal/thermal_core.c
drivers/tty/hvc/hvc_xen.c
drivers/tty/metag_da.c
drivers/tty/serial/sh-sci.c
drivers/usb/c67x00/c67x00-sched.c
drivers/usb/core/Kconfig
drivers/usb/core/hub.c
drivers/usb/core/usb-acpi.c
drivers/usb/gadget/f_fs.c
drivers/usb/gadget/tcm_usb_gadget.c
drivers/usb/serial/mos7720.c
drivers/vhost/scsi.c
drivers/video/Kconfig
drivers/video/backlight/pwm_bl.c
drivers/video/exynos/exynos_mipi_dsi.c
drivers/video/exynos/exynos_mipi_dsi_common.c
drivers/video/omap2/displays-new/encoder-tpd12s015.c
drivers/virtio/virtio_balloon.c
drivers/virtio/virtio_mmio.c
drivers/virtio/virtio_pci.c
drivers/virtio/virtio_ring.c
drivers/w1/masters/w1-gpio.c
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/acquirewdt.c
drivers/watchdog/advantechwdt.c
drivers/watchdog/alim1535_wdt.c
drivers/watchdog/alim7101_wdt.c
drivers/watchdog/ar7_wdt.c
drivers/watchdog/at32ap700x_wdt.c
drivers/watchdog/at91rm9200_wdt.c
drivers/watchdog/ath79_wdt.c
drivers/watchdog/bcm2835_wdt.c
drivers/watchdog/bcm63xx_wdt.c
drivers/watchdog/bfin_wdt.c
drivers/watchdog/cpu5wdt.c
drivers/watchdog/davinci_wdt.c
drivers/watchdog/dw_wdt.c
drivers/watchdog/ep93xx_wdt.c
drivers/watchdog/eurotechwdt.c
drivers/watchdog/gef_wdt.c
drivers/watchdog/geodewdt.c
drivers/watchdog/hpwdt.c
drivers/watchdog/i6300esb.c
drivers/watchdog/iTCO_wdt.c
drivers/watchdog/ib700wdt.c
drivers/watchdog/ibmasr.c
drivers/watchdog/ie6xx_wdt.c
drivers/watchdog/imx2_wdt.c
drivers/watchdog/indydog.c
drivers/watchdog/intel_scu_watchdog.c
drivers/watchdog/iop_wdt.c
drivers/watchdog/it8712f_wdt.c
drivers/watchdog/it87_wdt.c
drivers/watchdog/ixp4xx_wdt.c
drivers/watchdog/jz4740_wdt.c
drivers/watchdog/kempld_wdt.c
drivers/watchdog/ks8695_wdt.c
drivers/watchdog/lantiq_wdt.c
drivers/watchdog/m54xx_wdt.c
drivers/watchdog/machzwd.c
drivers/watchdog/max63xx_wdt.c
drivers/watchdog/mixcomwd.c
drivers/watchdog/moxart_wdt.c [new file with mode: 0644]
drivers/watchdog/mpc8xxx_wdt.c
drivers/watchdog/mtx-1_wdt.c
drivers/watchdog/mv64x60_wdt.c
drivers/watchdog/nuc900_wdt.c
drivers/watchdog/nv_tco.c
drivers/watchdog/of_xilinx_wdt.c
drivers/watchdog/omap_wdt.c
drivers/watchdog/orion_wdt.c
drivers/watchdog/pc87413_wdt.c
drivers/watchdog/pcwd.c
drivers/watchdog/pcwd_pci.c
drivers/watchdog/pcwd_usb.c
drivers/watchdog/pika_wdt.c
drivers/watchdog/pnx4008_wdt.c
drivers/watchdog/pnx833x_wdt.c
drivers/watchdog/rc32434_wdt.c
drivers/watchdog/rdc321x_wdt.c
drivers/watchdog/rt2880_wdt.c [new file with mode: 0644]
drivers/watchdog/s3c2410_wdt.c
drivers/watchdog/sa1100_wdt.c
drivers/watchdog/sb_wdog.c
drivers/watchdog/sbc60xxwdt.c
drivers/watchdog/sbc7240_wdt.c
drivers/watchdog/sbc8360.c
drivers/watchdog/sbc_epx_c3.c
drivers/watchdog/sbc_fitpc2_wdt.c
drivers/watchdog/sc1200wdt.c
drivers/watchdog/sc520_wdt.c
drivers/watchdog/sch311x_wdt.c
drivers/watchdog/scx200_wdt.c
drivers/watchdog/shwdt.c
drivers/watchdog/sirfsoc_wdt.c [new file with mode: 0644]
drivers/watchdog/smsc37b787_wdt.c
drivers/watchdog/softdog.c
drivers/watchdog/sp5100_tco.c
drivers/watchdog/sp805_wdt.c
drivers/watchdog/stmp3xxx_rtc_wdt.c
drivers/watchdog/sunxi_wdt.c
drivers/watchdog/ts72xx_wdt.c
drivers/watchdog/txx9wdt.c
drivers/watchdog/ux500_wdt.c
drivers/watchdog/w83627hf_wdt.c
drivers/watchdog/w83697hf_wdt.c
drivers/watchdog/w83697ug_wdt.c
drivers/watchdog/w83877f_wdt.c
drivers/watchdog/w83977f_wdt.c
drivers/watchdog/wafer5823wdt.c
drivers/watchdog/watchdog_core.c
drivers/watchdog/wdrtas.c
drivers/watchdog/wdt.c
drivers/watchdog/wdt285.c
drivers/watchdog/wdt977.c
drivers/watchdog/wdt_pci.c
drivers/watchdog/wm831x_wdt.c
drivers/watchdog/xen_wdt.c
drivers/xen/Kconfig
drivers/xen/balloon.c
drivers/xen/evtchn.c
drivers/xen/grant-table.c
drivers/xen/pci.c
drivers/xen/platform-pci.c
drivers/xen/swiotlb-xen.c
fs/9p/vfs_dentry.c
fs/aio.c
fs/bio.c
fs/btrfs/Kconfig
fs/btrfs/Makefile
fs/btrfs/acl.c
fs/btrfs/async-thread.c
fs/btrfs/backref.c
fs/btrfs/btrfs_inode.h
fs/btrfs/check-integrity.c
fs/btrfs/compat.h [deleted file]
fs/btrfs/compression.c
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/delayed-inode.c
fs/btrfs/dev-replace.c
fs/btrfs/dir-item.c
fs/btrfs/disk-io.c
fs/btrfs/disk-io.h
fs/btrfs/export.c
fs/btrfs/extent-tree.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/extent_map.h
fs/btrfs/file-item.c
fs/btrfs/file.c
fs/btrfs/free-space-cache.c
fs/btrfs/free-space-cache.h
fs/btrfs/inode-item.c
fs/btrfs/inode-map.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/ordered-data.c
fs/btrfs/ordered-data.h
fs/btrfs/print-tree.c
fs/btrfs/raid56.c
fs/btrfs/relocation.c
fs/btrfs/scrub.c
fs/btrfs/send.c
fs/btrfs/super.c
fs/btrfs/tests/btrfs-tests.c [new file with mode: 0644]
fs/btrfs/tests/btrfs-tests.h
fs/btrfs/tests/extent-buffer-tests.c [new file with mode: 0644]
fs/btrfs/tests/extent-io-tests.c [new file with mode: 0644]
fs/btrfs/tests/inode-tests.c [new file with mode: 0644]
fs/btrfs/transaction.c
fs/btrfs/transaction.h
fs/btrfs/tree-defrag.c
fs/btrfs/tree-log.c
fs/btrfs/uuid-tree.c
fs/btrfs/volumes.c
fs/btrfs/volumes.h
fs/cifs/cifsencrypt.c
fs/cifs/cifsglob.h
fs/cifs/cifspdu.h
fs/cifs/cifssmb.c
fs/cifs/dir.c
fs/cifs/file.c
fs/cifs/inode.c
fs/cifs/ioctl.c
fs/cifs/netmisc.c
fs/cifs/readdir.c
fs/cifs/smb1ops.c
fs/cifs/smb2inode.c
fs/cifs/smb2maperror.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h
fs/cifs/smb2proto.h
fs/configfs/dir.c
fs/coredump.c
fs/dcache.c
fs/dlm/netlink.c
fs/ecryptfs/crypto.c
fs/efivarfs/super.c
fs/exec.c
fs/gfs2/glock.c
fs/gfs2/inode.c
fs/gfs2/lock_dlm.c
fs/gfs2/quota.c
fs/gfs2/rgrp.c
fs/hfsplus/xattr.c
fs/hostfs/hostfs_kern.c
fs/libfs.c
fs/namei.c
fs/nfs/Kconfig
fs/nfs/nfs4state.c
fs/nfs/super.c
fs/nfsd/Kconfig
fs/nfsd/export.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsfh.c
fs/nfsd/vfs.c
fs/ocfs2/dlmglue.c
fs/proc/base.c
fs/proc/consoles.c
fs/proc/generic.c
fs/proc/meminfo.c
fs/proc/namespaces.c
fs/proc/nommu.c
fs/proc/task_mmu.c
fs/proc/task_nommu.c
fs/quota/netlink.c
fs/seq_file.c
fs/squashfs/Kconfig
fs/squashfs/Makefile
fs/squashfs/block.c
fs/squashfs/cache.c
fs/squashfs/decompressor.c
fs/squashfs/decompressor.h
fs/squashfs/decompressor_multi.c [new file with mode: 0644]
fs/squashfs/decompressor_multi_percpu.c [new file with mode: 0644]
fs/squashfs/decompressor_single.c [new file with mode: 0644]
fs/squashfs/file.c
fs/squashfs/file_cache.c [new file with mode: 0644]
fs/squashfs/file_direct.c [new file with mode: 0644]
fs/squashfs/lzo_wrapper.c
fs/squashfs/page_actor.c [new file with mode: 0644]
fs/squashfs/page_actor.h [new file with mode: 0644]
fs/squashfs/squashfs.h
fs/squashfs/squashfs_fs_sb.h
fs/squashfs/super.c
fs/squashfs/xz_wrapper.c
fs/squashfs/zlib_wrapper.c
fs/xfs/xfs_bmap.c
fs/xfs/xfs_mount.c
fs/xfs/xfs_mount.h
fs/xfs/xfs_trans_inode.c
fs/xfs/xfs_trans_resv.c
include/acpi/acpi_bus.h
include/asm-generic/memory_model.h
include/asm-generic/vmlinux.lds.h
include/crypto/hash_info.h [new file with mode: 0644]
include/crypto/public_key.h
include/drm/drmP.h
include/drm/drm_crtc.h
include/drm/drm_crtc_helper.h
include/drm/drm_dp_helper.h
include/drm/drm_pciids.h
include/drm/i915_drm.h
include/drm/i915_pciids.h
include/drm/ttm/ttm_page_alloc.h
include/dt-bindings/mfd/as3722.h [new file with mode: 0644]
include/keys/big_key-type.h [new file with mode: 0644]
include/keys/keyring-type.h
include/keys/system_keyring.h [new file with mode: 0644]
include/linux/acpi.h
include/linux/amba/serial.h
include/linux/assoc_array.h [new file with mode: 0644]
include/linux/assoc_array_priv.h [new file with mode: 0644]
include/linux/audit.h
include/linux/blkdev.h
include/linux/cmdline-parser.h
include/linux/completion.h
include/linux/cpufreq.h
include/linux/devfreq.h
include/linux/device.h
include/linux/dmaengine.h
include/linux/export.h
include/linux/fs.h
include/linux/ftrace.h
include/linux/ftrace_event.h
include/linux/genl_magic_func.h
include/linux/host1x.h [new file with mode: 0644]
include/linux/huge_mm.h
include/linux/hugetlb.h
include/linux/i2c.h
include/linux/if_macvlan.h
include/linux/interrupt.h
include/linux/iommu.h
include/linux/irq.h
include/linux/kernel.h
include/linux/key-type.h
include/linux/key.h
include/linux/kfifo.h
include/linux/kvm_host.h
include/linux/llist.h
include/linux/lockref.h
include/linux/mfd/arizona/registers.h
include/linux/mfd/as3722.h [new file with mode: 0644]
include/linux/mfd/core.h
include/linux/mfd/da9052/da9052.h
include/linux/mfd/max77693-private.h
include/linux/mfd/max77693.h
include/linux/mfd/rtsx_pci.h
include/linux/mfd/si476x-core.h
include/linux/mfd/stw481x.h [new file with mode: 0644]
include/linux/mfd/syscon.h
include/linux/mfd/ti_am335x_tscadc.h
include/linux/mfd/wm8994/core.h
include/linux/mlx5/device.h
include/linux/mlx5/driver.h
include/linux/mm.h
include/linux/mm_types.h
include/linux/mmc/card.h
include/linux/mmc/core.h
include/linux/mmc/dw_mmc.h
include/linux/mmc/host.h
include/linux/module.h
include/linux/net.h
include/linux/netdevice.h
include/linux/nfs4.h
include/linux/pci-acpi.h
include/linux/phy.h
include/linux/platform_data/at24.h [moved from include/linux/i2c/at24.h with 97% similarity]
include/linux/platform_data/edma.h
include/linux/platform_data/mmc-esdhc-imx.h
include/linux/platform_data/zforce_ts.h [new file with mode: 0644]
include/linux/power/bq24735-charger.h [new file with mode: 0644]
include/linux/preempt_mask.h
include/linux/pwm_backlight.h
include/linux/sched.h
include/linux/security.h
include/linux/seq_file.h
include/linux/seqlock.h
include/linux/skbuff.h
include/linux/slab.h
include/linux/slab_def.h
include/linux/slub_def.h
include/linux/smp.h
include/linux/srcu.h
include/linux/swapops.h
include/linux/syscalls.h
include/linux/user_namespace.h
include/linux/virtio.h
include/linux/virtio_config.h
include/linux/virtio_ring.h
include/linux/wait.h
include/media/lm3560.h [new file with mode: 0644]
include/media/soc_camera.h
include/media/v4l2-clk.h
include/media/v4l2-common.h
include/media/v4l2-ctrls.h
include/media/v4l2-fh.h
include/media/v4l2-subdev.h
include/media/videobuf2-core.h
include/media/videobuf2-dma-sg.h
include/net/bluetooth/l2cap.h
include/net/genetlink.h
include/rdma/ib_verbs.h
include/scsi/scsi_transport_srp.h
include/target/target_core_backend.h
include/target/target_core_base.h
include/target/target_core_configfs.h
include/target/target_core_fabric.h
include/trace/events/bcache.h
include/trace/events/btrfs.h
include/trace/events/iommu.h [new file with mode: 0644]
include/trace/events/kvm.h
include/trace/events/random.h
include/trace/events/swiotlb.h [new file with mode: 0644]
include/trace/ftrace.h
include/uapi/drm/armada_drm.h [new file with mode: 0644]
include/uapi/drm/drm.h
include/uapi/drm/drm_mode.h
include/uapi/drm/i915_drm.h
include/uapi/drm/tegra_drm.h
include/uapi/linux/audit.h
include/uapi/linux/bcache.h [new file with mode: 0644]
include/uapi/linux/genetlink.h
include/uapi/linux/hash_info.h [new file with mode: 0644]
include/uapi/linux/keyctl.h
include/uapi/linux/kvm.h
include/uapi/linux/magic.h
include/uapi/linux/pkt_sched.h
include/uapi/linux/raid/md_p.h
include/uapi/linux/v4l2-controls.h
include/uapi/rdma/ib_user_verbs.h
include/xen/interface/physdev.h
include/xen/swiotlb-xen.h
include/xen/xen-ops.h
init/Kconfig
init/main.c
ipc/shm.c
kernel/Kconfig.hz
kernel/Makefile
kernel/audit.c
kernel/audit.h
kernel/auditfilter.c
kernel/auditsc.c
kernel/bounds.c
kernel/cgroup.c
kernel/fork.c
kernel/hung_task.c
kernel/irq/chip.c
kernel/irq/manage.c
kernel/irq/settings.h
kernel/irq/spurious.c
kernel/kexec.c
kernel/modsign_certificate.S [deleted file]
kernel/modsign_pubkey.c [deleted file]
kernel/module-internal.h
kernel/module.c
kernel/module_signing.c
kernel/power/snapshot.c
kernel/power/user.c
kernel/rcu/tiny.c
kernel/rcu/tree.c
kernel/smp.c
kernel/softirq.c
kernel/system_certificates.S [new file with mode: 0644]
kernel/system_keyring.c [new file with mode: 0644]
kernel/taskstats.c
kernel/trace/ftrace.c
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_branch.c
kernel/trace/trace_events.c
kernel/trace/trace_events_filter.c
kernel/trace/trace_export.c
kernel/trace/trace_functions_graph.c
kernel/trace/trace_kprobe.c
kernel/trace/trace_mmiotrace.c
kernel/trace/trace_sched_switch.c
kernel/trace/trace_stat.c
kernel/trace/trace_syscalls.c
kernel/trace/trace_uprobe.c
kernel/up.c
kernel/user.c
kernel/user_namespace.c
lib/Kconfig
lib/Makefile
lib/assoc_array.c [new file with mode: 0644]
lib/kfifo.c
lib/llist.c
lib/lockref.c
lib/mpi/mpiutil.c
lib/percpu_ida.c
lib/random32.c
lib/swiotlb.c
lib/vsprintf.c
mm/Kconfig
mm/filemap.c
mm/huge_memory.c
mm/hugetlb.c
mm/memcontrol.c
mm/memory-failure.c
mm/memory.c
mm/mempolicy.c
mm/migrate.c
mm/mmap.c
mm/oom_kill.c
mm/pgtable-generic.c
mm/rmap.c
mm/slab.c
mm/slub.c
mm/swap.c
net/9p/trans_virtio.c
net/Kconfig
net/appletalk/ddp.c
net/atm/common.c
net/ax25/af_ax25.c
net/bluetooth/af_bluetooth.c
net/bluetooth/hci_sock.c
net/bluetooth/l2cap_core.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c
net/bluetooth/sco.c
net/bluetooth/smp.c
net/bridge/br_if.c
net/bridge/br_vlan.c
net/bridge/netfilter/ebt_ip6.c
net/caif/caif_socket.c
net/can/af_can.c
net/compat.c
net/core/dev.c
net/core/drop_monitor.c
net/core/iovec.c
net/core/skbuff.c
net/hsr/hsr_netlink.c
net/ieee802154/6lowpan.c
net/ieee802154/dgram.c
net/ieee802154/ieee802154.h
net/ieee802154/netlink.c
net/ieee802154/nl-mac.c
net/ieee802154/nl-phy.c
net/ipv4/datagram.c
net/ipv4/fib_trie.c
net/ipv4/ip_tunnel.c
net/ipv4/ip_vti.c
net/ipv4/netfilter/ipt_SYNPROXY.c
net/ipv4/ping.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/tcp.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_metrics.c
net/ipv4/tcp_output.c
net/ipv4/udp.c
net/ipv6/addrconf.c
net/ipv6/af_inet6.c
net/ipv6/ip6_tunnel.c
net/ipv6/ndisc.c
net/ipv6/netfilter/ip6t_SYNPROXY.c
net/ipv6/raw.c
net/ipv6/sit.c
net/ipv6/udp.c
net/ipx/af_ipx.c
net/irda/af_irda.c
net/irda/irnetlink.c
net/iucv/af_iucv.c
net/key/af_key.c
net/l2tp/l2tp_ip.c
net/l2tp/l2tp_netlink.c
net/l2tp/l2tp_ppp.c
net/llc/af_llc.c
net/netfilter/Kconfig
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_seqadj.c
net/netfilter/nf_synproxy_core.c
net/netfilter/nft_compat.c
net/netfilter/xt_set.c
net/netlabel/netlabel_cipso_v4.c
net/netlabel/netlabel_mgmt.c
net/netlabel/netlabel_unlabeled.c
net/netlink/af_netlink.c
net/netlink/genetlink.c
net/netrom/af_netrom.c
net/nfc/llcp_sock.c
net/nfc/netlink.c
net/nfc/rawsock.c
net/openvswitch/datapath.c
net/openvswitch/datapath.h
net/openvswitch/dp_notify.c
net/packet/af_packet.c
net/packet/internal.h
net/phonet/datagram.c
net/phonet/socket.c
net/rds/recv.c
net/rose/af_rose.c
net/rxrpc/ar-recvmsg.c
net/sched/sch_fq.c
net/sctp/associola.c
net/sctp/objcnt.c
net/socket.c
net/sunrpc/auth_gss/gss_krb5_unseal.c
net/sunrpc/auth_gss/gss_krb5_wrap.c
net/sunrpc/auth_gss/gss_rpc_upcall.c
net/sunrpc/auth_gss/gss_rpc_xdr.c
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c
net/sunrpc/svc.c
net/sunrpc/xprtsock.c
net/tipc/link.c
net/tipc/netlink.c
net/tipc/socket.c
net/unix/af_unix.c
net/vmw_vsock/Kconfig
net/vmw_vsock/af_vsock.c
net/vmw_vsock/vmci_transport.c
net/wimax/op-msg.c
net/wimax/op-reset.c
net/wimax/op-rfkill.c
net/wimax/op-state-get.c
net/wimax/stack.c
net/wimax/wimax-internal.h
net/wireless/nl80211.c
net/x25/af_x25.c
samples/kfifo/bytestream-example.c
samples/kfifo/dma-example.c
samples/kfifo/inttype-example.c
scripts/Makefile.modpost
scripts/asn1_compiler.c
scripts/bloat-o-meter
scripts/checkpatch.pl
scripts/coccinelle/api/devm_request_and_ioremap.cocci [deleted file]
scripts/kallsyms.c
scripts/kconfig/expr.h
scripts/kconfig/mconf.c
scripts/kconfig/menu.c
scripts/kconfig/qconf.cc
scripts/kconfig/qconf.h
scripts/kconfig/symbol.c
scripts/kconfig/zconf.l
scripts/kernel-doc
scripts/mod/modpost.c
scripts/mod/sumversion.c
scripts/recordmcount.pl
scripts/show_delta
scripts/tags.sh
security/Makefile
security/apparmor/audit.c
security/apparmor/capability.c
security/apparmor/domain.c
security/apparmor/include/audit.h
security/apparmor/include/capability.h
security/apparmor/include/ipc.h
security/apparmor/ipc.c
security/apparmor/lsm.c
security/capability.c
security/integrity/digsig.c
security/integrity/digsig_asymmetric.c
security/integrity/evm/evm_main.c
security/integrity/evm/evm_posix_acl.c
security/integrity/iint.c
security/integrity/ima/Kconfig
security/integrity/ima/Makefile
security/integrity/ima/ima.h
security/integrity/ima/ima_api.c
security/integrity/ima/ima_appraise.c
security/integrity/ima/ima_crypto.c
security/integrity/ima/ima_fs.c
security/integrity/ima/ima_init.c
security/integrity/ima/ima_main.c
security/integrity/ima/ima_policy.c
security/integrity/ima/ima_queue.c
security/integrity/ima/ima_template.c [new file with mode: 0644]
security/integrity/ima/ima_template_lib.c [new file with mode: 0644]
security/integrity/ima/ima_template_lib.h [new file with mode: 0644]
security/integrity/integrity.h
security/keys/Kconfig
security/keys/Makefile
security/keys/big_key.c [new file with mode: 0644]
security/keys/compat.c
security/keys/gc.c
security/keys/internal.h
security/keys/key.c
security/keys/keyctl.c
security/keys/keyring.c
security/keys/persistent.c [new file with mode: 0644]
security/keys/proc.c
security/keys/process_keys.c
security/keys/request_key.c
security/keys/request_key_auth.c
security/keys/sysctl.c
security/keys/user_defined.c
security/lsm_audit.c
security/security.c
security/selinux/hooks.c
security/selinux/include/objsec.h
security/selinux/include/security.h
security/selinux/include/xfrm.h
security/selinux/netlabel.c
security/selinux/netnode.c
security/selinux/nlmsgtab.c
security/selinux/selinuxfs.c
security/selinux/ss/ebitmap.c
security/selinux/ss/ebitmap.h
security/selinux/ss/mls.c
security/selinux/ss/mls_types.h
security/selinux/ss/policydb.c
security/selinux/ss/services.c
security/selinux/xfrm.c
security/smack/smack.h
security/smack/smack_access.c
security/smack/smack_lsm.c
security/smack/smackfs.c
sound/aoa/fabrics/layout.c
sound/core/compress_offload.c
sound/core/jack.c
sound/core/memalloc.c
sound/drivers/pcsp/pcsp.c
sound/firewire/dice.c
sound/isa/msnd/msnd_pinnacle.c
sound/isa/wavefront/wavefront_synth.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_eld.c
sound/pci/hda/hda_generic.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/ppc/keywest.c
sound/soc/blackfin/bf5xx-i2s.c
sound/soc/codecs/alc5632.c
sound/soc/codecs/cs42l52.h
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8997.c
sound/soc/davinci/davinci-pcm.c
sound/soc/fsl/imx-pcm-fiq.c
sound/soc/fsl/imx-wm8962.c
sound/soc/samsung/ac97.c
sound/sparc/cs4231.c
sound/usb/mixer_quirks.c
sound/usb/stream.c
tools/perf/Documentation/perf-record.txt
tools/perf/builtin-probe.c
tools/perf/builtin-record.c
tools/perf/builtin-top.c
tools/perf/builtin-trace.c
tools/perf/config/Makefile
tools/perf/config/feature-checks/Makefile
tools/perf/config/feature-checks/test-all.c
tools/perf/tests/parse-events.c
tools/perf/ui/browser.c
tools/perf/ui/browsers/hists.c
tools/perf/util/event.c
tools/perf/util/evlist.c
tools/perf/util/evsel.c
tools/perf/util/evsel.h
tools/perf/util/machine.c
tools/perf/util/target.h
tools/perf/util/unwind.c
tools/perf/util/unwind.h
tools/power/x86/turbostat/turbostat.c
tools/virtio/virtio_test.c
tools/virtio/vringh_test.c
virt/kvm/Kconfig
virt/kvm/async_pf.c
virt/kvm/iommu.c
virt/kvm/kvm_main.c
virt/kvm/vfio.c [new file with mode: 0644]

index 5c53d28..b9688de 100644 (file)
@@ -61,6 +61,12 @@ Description: Interface for making ib_srp connect to a new target.
                  interrupt is handled by a different CPU then the comp_vector
                  parameter can be used to spread the SRP completion workload
                  over multiple CPU's.
+               * tl_retry_count, a number in the range 2..7 specifying the
+                 IB RC retry count.
+               * queue_size, the maximum number of commands that the
+                 initiator is allowed to queue per SCSI host. The default
+                 value for this parameter is 62. The lowest supported value
+                 is 2.
 
 What:          /sys/class/infiniband_srp/srp-<hca>-<port_number>/ibdev
 Date:          January 2, 2006
@@ -153,6 +159,13 @@ Contact:   linux-rdma@vger.kernel.org
 Description:   InfiniBand service ID used for establishing communication with
                the SRP target.
 
+What:          /sys/class/scsi_host/host<n>/sgid
+Date:          February 1, 2014
+KernelVersion: 3.13
+Contact:       linux-rdma@vger.kernel.org
+Description:   InfiniBand GID of the source port used for communication with
+               the SRP target.
+
 What:          /sys/class/scsi_host/host<n>/zero_req_lim
 Date:          September 20, 2006
 KernelVersion: 2.6.18
index b36fb0d..ec7af69 100644 (file)
@@ -5,6 +5,24 @@ Contact:       linux-scsi@vger.kernel.org, linux-rdma@vger.kernel.org
 Description:   Instructs an SRP initiator to disconnect from a target and to
                remove all LUNs imported from that target.
 
+What:          /sys/class/srp_remote_ports/port-<h>:<n>/dev_loss_tmo
+Date:          February 1, 2014
+KernelVersion: 3.13
+Contact:       linux-scsi@vger.kernel.org, linux-rdma@vger.kernel.org
+Description:   Number of seconds the SCSI layer will wait after a transport
+               layer error has been observed before removing a target port.
+               Zero means immediate removal. Setting this attribute to "off"
+               will disable the dev_loss timer.
+
+What:          /sys/class/srp_remote_ports/port-<h>:<n>/fast_io_fail_tmo
+Date:          February 1, 2014
+KernelVersion: 3.13
+Contact:       linux-scsi@vger.kernel.org, linux-rdma@vger.kernel.org
+Description:   Number of seconds the SCSI layer will wait after a transport
+               layer error has been observed before failing I/O. Zero means
+               failing I/O immediately. Setting this attribute to "off" will
+               disable the fast_io_fail timer.
+
 What:          /sys/class/srp_remote_ports/port-<h>:<n>/port_id
 Date:          June 27, 2007
 KernelVersion: 2.6.24
@@ -12,8 +30,29 @@ Contact:     linux-scsi@vger.kernel.org
 Description:   16-byte local SRP port identifier in hexadecimal format. An
                example: 4c:49:4e:55:58:20:56:49:4f:00:00:00:00:00:00:00.
 
+What:          /sys/class/srp_remote_ports/port-<h>:<n>/reconnect_delay
+Date:          February 1, 2014
+KernelVersion: 3.13
+Contact:       linux-scsi@vger.kernel.org, linux-rdma@vger.kernel.org
+Description:   Number of seconds the SCSI layer will wait after a reconnect
+               attempt failed before retrying. Setting this attribute to
+               "off" will disable time-based reconnecting.
+
 What:          /sys/class/srp_remote_ports/port-<h>:<n>/roles
 Date:          June 27, 2007
 KernelVersion: 2.6.24
 Contact:       linux-scsi@vger.kernel.org
 Description:   Role of the remote port. Either "SRP Initiator" or "SRP Target".
+
+What:          /sys/class/srp_remote_ports/port-<h>:<n>/state
+Date:          February 1, 2014
+KernelVersion: 3.13
+Contact:       linux-scsi@vger.kernel.org, linux-rdma@vger.kernel.org
+Description:   State of the transport layer used for communication with the
+               remote port. "running" if the transport layer is operational;
+               "blocked" if a transport layer error has been encountered but
+               the fast_io_fail_tmo timer has not yet fired; "fail-fast"
+               after the fast_io_fail_tmo timer has fired and before the
+               "dev_loss_tmo" timer has fired; "lost" after the
+               "dev_loss_tmo" timer has fired and before the port is finally
+               removed.
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-ryos b/Documentation/ABI/testing/sysfs-driver-hid-roccat-ryos
new file mode 100644 (file)
index 0000000..1d6a8cf
--- /dev/null
@@ -0,0 +1,178 @@
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/control
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one select which data from which
+               profile will be read next. The data has to be 3 bytes long.
+               This file is writeonly.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/profile
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   The mouse can store 5 profiles which can be switched by the
+               press of a button. profile holds index of actual profile.
+               This value is persistent, so its value determines the profile
+               that's active when the device is powered on next time.
+               When written, the device activates the set profile immediately.
+               The data has to be 3 bytes long.
+               The device will reject invalid data.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/keys_primary
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the default of all keys for
+               a specific profile. Profile index is included in written data.
+               The data has to be 125 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/keys_function
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the
+               function keys for a specific profile. Profile index is included
+               in written data. The data has to be 95 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/keys_macro
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the macro
+               keys for a specific profile. Profile index is included in
+               written data. The data has to be 35 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/keys_thumbster
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the
+               thumbster keys for a specific profile. Profile index is included
+               in written data. The data has to be 23 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/keys_extra
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the
+               capslock and function keys for a specific profile. Profile index
+               is included in written data. The data has to be 8 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/keys_easyzone
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the
+               easyzone keys for a specific profile. Profile index is included
+               in written data. The data has to be 294 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/key_mask
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one deactivate certain keys like
+               windows and application keys, to prevent accidental presses.
+               Profile index for which this settings occur is included in
+               written data. The data has to be 6 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/light
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the backlight intensity for
+               a specific profile. Profile index is included in written data.
+               This attribute is only valid for the glow and pro variant.
+               The data has to be 16 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/macro
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one store macros with max 480
+               keystrokes for a specific button for a specific profile.
+               Button and profile indexes are included in written data.
+               The data has to be 2002 bytes long.
+               Before reading this file, control has to be written to select
+               which profile and key to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/info
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When read, this file returns general data like firmware version.
+               The data is 8 bytes long.
+               This file is readonly.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/reset
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one reset the device.
+               The data has to be 3 bytes long.
+               This file is writeonly.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/talk
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one trigger easyshift functionality
+               from the host.
+               The data has to be 16 bytes long.
+               This file is writeonly.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/light_control
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one switch between stored and custom
+               light settings.
+               This attribute is only valid for the pro variant.
+               The data has to be 8 bytes long.
+               This file is writeonly.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/stored_lights
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set per-key lighting for different
+               layers.
+               This attribute is only valid for the pro variant.
+               The data has to be 1382 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/custom_lights
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the actual per-key lighting.
+               This attribute is only valid for the pro variant.
+               The data has to be 20 bytes long.
+               This file is writeonly.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/ryos/roccatryos<minor>/light_macro
+Date:          October 2013
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set a light macro that is looped
+               whenever the device gets in dimness mode.
+               This attribute is only valid for the pro variant.
+               The data has to be 2002 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
index ed5dd56..39dfa5c 100644 (file)
@@ -57,3 +57,21 @@ Description: This attribute is only provided if the device was detected as a
                Calibration data is already applied by the kernel to all input
                values but may be used by user-space to perform other
                transformations.
+
+What:          /sys/bus/hid/drivers/wiimote/<dev>/pro_calib
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       David Herrmann <dh.herrmann@gmail.com>
+Description:   This attribute is only provided if the device was detected as a
+               pro-controller. It provides a single line with 4 calibration
+               values for all 4 analog sticks. Format is: "x1:y1 x2:y2". Data
+               is prefixed with a +/-. Each value is a signed 16bit number.
+               Data is encoded as decimal numbers and specifies the offsets of
+               the analog sticks of the pro-controller.
+               Calibration data is already applied by the kernel to all input
+               values but may be used by user-space to perform other
+               transformations.
+               Calibration data is detected by the kernel during device setup.
+               You can write "scan\n" into this file to re-trigger calibration.
+               You can also write data directly in the form "x1:y1 x2:y2" to
+               set the calibration values manually.
index e59480d..cc2450d 100644 (file)
@@ -13,7 +13,7 @@ all pending DMA writes to complete, and thus provides a mechanism to
 strictly order DMA from a device across all intervening busses and
 bridges.  This barrier is not specific to a particular type of
 interconnect, it applies to the system as a whole, and so its
-implementation must account for the idiosyncracies of the system all
+implementation must account for the idiosyncrasies of the system all
 the way from the DMA device to memory.
 
 As an example of a situation where DMA_ATTR_WRITE_BARRIER would be
@@ -60,7 +60,7 @@ such mapping is non-trivial task and consumes very limited resources
 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
+can treat 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.
 
@@ -82,7 +82,7 @@ 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
+same synchronization operation on the CPU cache. CPU cache synchronization
 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
diff --git a/Documentation/assoc_array.txt b/Documentation/assoc_array.txt
new file mode 100644 (file)
index 0000000..f4faec0
--- /dev/null
@@ -0,0 +1,574 @@
+                  ========================================
+                  GENERIC ASSOCIATIVE ARRAY IMPLEMENTATION
+                  ========================================
+
+Contents:
+
+ - Overview.
+
+ - The public API.
+   - Edit script.
+   - Operations table.
+   - Manipulation functions.
+   - Access functions.
+   - Index key form.
+
+ - Internal workings.
+   - Basic internal tree layout.
+   - Shortcuts.
+   - Splitting and collapsing nodes.
+   - Non-recursive iteration.
+   - Simultaneous alteration and iteration.
+
+
+========
+OVERVIEW
+========
+
+This associative array implementation is an object container with the following
+properties:
+
+ (1) Objects are opaque pointers.  The implementation does not care where they
+     point (if anywhere) or what they point to (if anything).
+
+     [!] NOTE: Pointers to objects _must_ be zero in the least significant bit.
+
+ (2) Objects do not need to contain linkage blocks for use by the array.  This
+     permits an object to be located in multiple arrays simultaneously.
+     Rather, the array is made up of metadata blocks that point to objects.
+
+ (3) Objects require index keys to locate them within the array.
+
+ (4) Index keys must be unique.  Inserting an object with the same key as one
+     already in the array will replace the old object.
+
+ (5) Index keys can be of any length and can be of different lengths.
+
+ (6) Index keys should encode the length early on, before any variation due to
+     length is seen.
+
+ (7) Index keys can include a hash to scatter objects throughout the array.
+
+ (8) The array can iterated over.  The objects will not necessarily come out in
+     key order.
+
+ (9) The array can be iterated over whilst it is being modified, provided the
+     RCU readlock is being held by the iterator.  Note, however, under these
+     circumstances, some objects may be seen more than once.  If this is a
+     problem, the iterator should lock against modification.  Objects will not
+     be missed, however, unless deleted.
+
+(10) Objects in the array can be looked up by means of their index key.
+
+(11) Objects can be looked up whilst the array is being modified, provided the
+     RCU readlock is being held by the thread doing the look up.
+
+The implementation uses a tree of 16-pointer nodes internally that are indexed
+on each level by nibbles from the index key in the same manner as in a radix
+tree.  To improve memory efficiency, shortcuts can be emplaced to skip over
+what would otherwise be a series of single-occupancy nodes.  Further, nodes
+pack leaf object pointers into spare space in the node rather than making an
+extra branch until as such time an object needs to be added to a full node.
+
+
+==============
+THE PUBLIC API
+==============
+
+The public API can be found in <linux/assoc_array.h>.  The associative array is
+rooted on the following structure:
+
+       struct assoc_array {
+               ...
+       };
+
+The code is selected by enabling CONFIG_ASSOCIATIVE_ARRAY.
+
+
+EDIT SCRIPT
+-----------
+
+The insertion and deletion functions produce an 'edit script' that can later be
+applied to effect the changes without risking ENOMEM.  This retains the
+preallocated metadata blocks that will be installed in the internal tree and
+keeps track of the metadata blocks that will be removed from the tree when the
+script is applied.
+
+This is also used to keep track of dead blocks and dead objects after the
+script has been applied so that they can be freed later.  The freeing is done
+after an RCU grace period has passed - thus allowing access functions to
+proceed under the RCU read lock.
+
+The script appears as outside of the API as a pointer of the type:
+
+       struct assoc_array_edit;
+
+There are two functions for dealing with the script:
+
+ (1) Apply an edit script.
+
+       void assoc_array_apply_edit(struct assoc_array_edit *edit);
+
+     This will perform the edit functions, interpolating various write barriers
+     to permit accesses under the RCU read lock to continue.  The edit script
+     will then be passed to call_rcu() to free it and any dead stuff it points
+     to.
+
+ (2) Cancel an edit script.
+
+       void assoc_array_cancel_edit(struct assoc_array_edit *edit);
+
+     This frees the edit script and all preallocated memory immediately.  If
+     this was for insertion, the new object is _not_ released by this function,
+     but must rather be released by the caller.
+
+These functions are guaranteed not to fail.
+
+
+OPERATIONS TABLE
+----------------
+
+Various functions take a table of operations:
+
+       struct assoc_array_ops {
+               ...
+       };
+
+This points to a number of methods, all of which need to be provided:
+
+ (1) Get a chunk of index key from caller data:
+
+       unsigned long (*get_key_chunk)(const void *index_key, int level);
+
+     This should return a chunk of caller-supplied index key starting at the
+     *bit* position given by the level argument.  The level argument will be a
+     multiple of ASSOC_ARRAY_KEY_CHUNK_SIZE and the function should return
+     ASSOC_ARRAY_KEY_CHUNK_SIZE bits.  No error is possible.
+
+
+ (2) Get a chunk of an object's index key.
+
+       unsigned long (*get_object_key_chunk)(const void *object, int level);
+
+     As the previous function, but gets its data from an object in the array
+     rather than from a caller-supplied index key.
+
+
+ (3) See if this is the object we're looking for.
+
+       bool (*compare_object)(const void *object, const void *index_key);
+
+     Compare the object against an index key and return true if it matches and
+     false if it doesn't.
+
+
+ (4) Diff the index keys of two objects.
+
+       int (*diff_objects)(const void *a, const void *b);
+
+     Return the bit position at which the index keys of two objects differ or
+     -1 if they are the same.
+
+
+ (5) Free an object.
+
+       void (*free_object)(void *object);
+
+     Free the specified object.  Note that this may be called an RCU grace
+     period after assoc_array_apply_edit() was called, so synchronize_rcu() may
+     be necessary on module unloading.
+
+
+MANIPULATION FUNCTIONS
+----------------------
+
+There are a number of functions for manipulating an associative array:
+
+ (1) Initialise an associative array.
+
+       void assoc_array_init(struct assoc_array *array);
+
+     This initialises the base structure for an associative array.  It can't
+     fail.
+
+
+ (2) Insert/replace an object in an associative array.
+
+       struct assoc_array_edit *
+       assoc_array_insert(struct assoc_array *array,
+                          const struct assoc_array_ops *ops,
+                          const void *index_key,
+                          void *object);
+
+     This inserts the given object into the array.  Note that the least
+     significant bit of the pointer must be zero as it's used to type-mark
+     pointers internally.
+
+     If an object already exists for that key then it will be replaced with the
+     new object and the old one will be freed automatically.
+
+     The index_key argument should hold index key information and is
+     passed to the methods in the ops table when they are called.
+
+     This function makes no alteration to the array itself, but rather returns
+     an edit script that must be applied.  -ENOMEM is returned in the case of
+     an out-of-memory error.
+
+     The caller should lock exclusively against other modifiers of the array.
+
+
+ (3) Delete an object from an associative array.
+
+       struct assoc_array_edit *
+       assoc_array_delete(struct assoc_array *array,
+                          const struct assoc_array_ops *ops,
+                          const void *index_key);
+
+     This deletes an object that matches the specified data from the array.
+
+     The index_key argument should hold index key information and is
+     passed to the methods in the ops table when they are called.
+
+     This function makes no alteration to the array itself, but rather returns
+     an edit script that must be applied.  -ENOMEM is returned in the case of
+     an out-of-memory error.  NULL will be returned if the specified object is
+     not found within the array.
+
+     The caller should lock exclusively against other modifiers of the array.
+
+
+ (4) Delete all objects from an associative array.
+
+       struct assoc_array_edit *
+       assoc_array_clear(struct assoc_array *array,
+                         const struct assoc_array_ops *ops);
+
+     This deletes all the objects from an associative array and leaves it
+     completely empty.
+
+     This function makes no alteration to the array itself, but rather returns
+     an edit script that must be applied.  -ENOMEM is returned in the case of
+     an out-of-memory error.
+
+     The caller should lock exclusively against other modifiers of the array.
+
+
+ (5) Destroy an associative array, deleting all objects.
+
+       void assoc_array_destroy(struct assoc_array *array,
+                                const struct assoc_array_ops *ops);
+
+     This destroys the contents of the associative array and leaves it
+     completely empty.  It is not permitted for another thread to be traversing
+     the array under the RCU read lock at the same time as this function is
+     destroying it as no RCU deferral is performed on memory release -
+     something that would require memory to be allocated.
+
+     The caller should lock exclusively against other modifiers and accessors
+     of the array.
+
+
+ (6) Garbage collect an associative array.
+
+       int assoc_array_gc(struct assoc_array *array,
+                          const struct assoc_array_ops *ops,
+                          bool (*iterator)(void *object, void *iterator_data),
+                          void *iterator_data);
+
+     This iterates over the objects in an associative array and passes each one
+     to iterator().  If iterator() returns true, the object is kept.  If it
+     returns false, the object will be freed.  If the iterator() function
+     returns true, it must perform any appropriate refcount incrementing on the
+     object before returning.
+
+     The internal tree will be packed down if possible as part of the iteration
+     to reduce the number of nodes in it.
+
+     The iterator_data is passed directly to iterator() and is otherwise
+     ignored by the function.
+
+     The function will return 0 if successful and -ENOMEM if there wasn't
+     enough memory.
+
+     It is possible for other threads to iterate over or search the array under
+     the RCU read lock whilst this function is in progress.  The caller should
+     lock exclusively against other modifiers of the array.
+
+
+ACCESS FUNCTIONS
+----------------
+
+There are two functions for accessing an associative array:
+
+ (1) Iterate over all the objects in an associative array.
+
+       int assoc_array_iterate(const struct assoc_array *array,
+                               int (*iterator)(const void *object,
+                                               void *iterator_data),
+                               void *iterator_data);
+
+     This passes each object in the array to the iterator callback function.
+     iterator_data is private data for that function.
+
+     This may be used on an array at the same time as the array is being
+     modified, provided the RCU read lock is held.  Under such circumstances,
+     it is possible for the iteration function to see some objects twice.  If
+     this is a problem, then modification should be locked against.  The
+     iteration algorithm should not, however, miss any objects.
+
+     The function will return 0 if no objects were in the array or else it will
+     return the result of the last iterator function called.  Iteration stops
+     immediately if any call to the iteration function results in a non-zero
+     return.
+
+
+ (2) Find an object in an associative array.
+
+       void *assoc_array_find(const struct assoc_array *array,
+                              const struct assoc_array_ops *ops,
+                              const void *index_key);
+
+     This walks through the array's internal tree directly to the object
+     specified by the index key..
+
+     This may be used on an array at the same time as the array is being
+     modified, provided the RCU read lock is held.
+
+     The function will return the object if found (and set *_type to the object
+     type) or will return NULL if the object was not found.
+
+
+INDEX KEY FORM
+--------------
+
+The index key can be of any form, but since the algorithms aren't told how long
+the key is, it is strongly recommended that the index key includes its length
+very early on before any variation due to the length would have an effect on
+comparisons.
+
+This will cause leaves with different length keys to scatter away from each
+other - and those with the same length keys to cluster together.
+
+It is also recommended that the index key begin with a hash of the rest of the
+key to maximise scattering throughout keyspace.
+
+The better the scattering, the wider and lower the internal tree will be.
+
+Poor scattering isn't too much of a problem as there are shortcuts and nodes
+can contain mixtures of leaves and metadata pointers.
+
+The index key is read in chunks of machine word.  Each chunk is subdivided into
+one nibble (4 bits) per level, so on a 32-bit CPU this is good for 8 levels and
+on a 64-bit CPU, 16 levels.  Unless the scattering is really poor, it is
+unlikely that more than one word of any particular index key will have to be
+used.
+
+
+=================
+INTERNAL WORKINGS
+=================
+
+The associative array data structure has an internal tree.  This tree is
+constructed of two types of metadata blocks: nodes and shortcuts.
+
+A node is an array of slots.  Each slot can contain one of four things:
+
+ (*) A NULL pointer, indicating that the slot is empty.
+
+ (*) A pointer to an object (a leaf).
+
+ (*) A pointer to a node at the next level.
+
+ (*) A pointer to a shortcut.
+
+
+BASIC INTERNAL TREE LAYOUT
+--------------------------
+
+Ignoring shortcuts for the moment, the nodes form a multilevel tree.  The index
+key space is strictly subdivided by the nodes in the tree and nodes occur on
+fixed levels.  For example:
+
+ Level:        0               1               2               3
+       =============== =============== =============== ===============
+                                                       NODE D
+                       NODE B          NODE C  +------>+---+
+               +------>+---+   +------>+---+   |       | 0 |
+       NODE A  |       | 0 |   |       | 0 |   |       +---+
+       +---+   |       +---+   |       +---+   |       :   :
+       | 0 |   |       :   :   |       :   :   |       +---+
+       +---+   |       +---+   |       +---+   |       | f |
+       | 1 |---+       | 3 |---+       | 7 |---+       +---+
+       +---+           +---+           +---+
+       :   :           :   :           | 8 |---+
+       +---+           +---+           +---+   |       NODE E
+       | e |---+       | f |           :   :   +------>+---+
+       +---+   |       +---+           +---+           | 0 |
+       | f |   |                       | f |           +---+
+       +---+   |                       +---+           :   :
+               |       NODE F                          +---+
+               +------>+---+                           | f |
+                       | 0 |           NODE G          +---+
+                       +---+   +------>+---+
+                       :   :   |       | 0 |
+                       +---+   |       +---+
+                       | 6 |---+       :   :
+                       +---+           +---+
+                       :   :           | f |
+                       +---+           +---+
+                       | f |
+                       +---+
+
+In the above example, there are 7 nodes (A-G), each with 16 slots (0-f).
+Assuming no other meta data nodes in the tree, the key space is divided thusly:
+
+       KEY PREFIX      NODE
+       ==========      ====
+       137*            D
+       138*            E
+       13[0-69-f]*     C
+       1[0-24-f]*      B
+       e6*             G
+       e[0-57-f]*      F
+       [02-df]*        A
+
+So, for instance, keys with the following example index keys will be found in
+the appropriate nodes:
+
+       INDEX KEY       PREFIX  NODE
+       =============== ======= ====
+       13694892892489  13      C
+       13795289025897  137     D
+       13889dde88793   138     E
+       138bbb89003093  138     E
+       1394879524789   12      C
+       1458952489      1       B
+       9431809de993ba  -       A
+       b4542910809cd   -       A
+       e5284310def98   e       F
+       e68428974237    e6      G
+       e7fffcbd443     e       F
+       f3842239082     -       A
+
+To save memory, if a node can hold all the leaves in its portion of keyspace,
+then the node will have all those leaves in it and will not have any metadata
+pointers - even if some of those leaves would like to be in the same slot.
+
+A node can contain a heterogeneous mix of leaves and metadata pointers.
+Metadata pointers must be in the slots that match their subdivisions of key
+space.  The leaves can be in any slot not occupied by a metadata pointer.  It
+is guaranteed that none of the leaves in a node will match a slot occupied by a
+metadata pointer.  If the metadata pointer is there, any leaf whose key matches
+the metadata key prefix must be in the subtree that the metadata pointer points
+to.
+
+In the above example list of index keys, node A will contain:
+
+       SLOT    CONTENT         INDEX KEY (PREFIX)
+       ====    =============== ==================
+       1       PTR TO NODE B   1*
+       any     LEAF            9431809de993ba
+       any     LEAF            b4542910809cd
+       e       PTR TO NODE F   e*
+       any     LEAF            f3842239082
+
+and node B:
+
+       3       PTR TO NODE C   13*
+       any     LEAF            1458952489
+
+
+SHORTCUTS
+---------
+
+Shortcuts are metadata records that jump over a piece of keyspace.  A shortcut
+is a replacement for a series of single-occupancy nodes ascending through the
+levels.  Shortcuts exist to save memory and to speed up traversal.
+
+It is possible for the root of the tree to be a shortcut - say, for example,
+the tree contains at least 17 nodes all with key prefix '1111'.  The insertion
+algorithm will insert a shortcut to skip over the '1111' keyspace in a single
+bound and get to the fourth level where these actually become different.
+
+
+SPLITTING AND COLLAPSING NODES
+------------------------------
+
+Each node has a maximum capacity of 16 leaves and metadata pointers.  If the
+insertion algorithm finds that it is trying to insert a 17th object into a
+node, that node will be split such that at least two leaves that have a common
+key segment at that level end up in a separate node rooted on that slot for
+that common key segment.
+
+If the leaves in a full node and the leaf that is being inserted are
+sufficiently similar, then a shortcut will be inserted into the tree.
+
+When the number of objects in the subtree rooted at a node falls to 16 or
+fewer, then the subtree will be collapsed down to a single node - and this will
+ripple towards the root if possible.
+
+
+NON-RECURSIVE ITERATION
+-----------------------
+
+Each node and shortcut contains a back pointer to its parent and the number of
+slot in that parent that points to it.  None-recursive iteration uses these to
+proceed rootwards through the tree, going to the parent node, slot N + 1 to
+make sure progress is made without the need for a stack.
+
+The backpointers, however, make simultaneous alteration and iteration tricky.
+
+
+SIMULTANEOUS ALTERATION AND ITERATION
+-------------------------------------
+
+There are a number of cases to consider:
+
+ (1) Simple insert/replace.  This involves simply replacing a NULL or old
+     matching leaf pointer with the pointer to the new leaf after a barrier.
+     The metadata blocks don't change otherwise.  An old leaf won't be freed
+     until after the RCU grace period.
+
+ (2) Simple delete.  This involves just clearing an old matching leaf.  The
+     metadata blocks don't change otherwise.  The old leaf won't be freed until
+     after the RCU grace period.
+
+ (3) Insertion replacing part of a subtree that we haven't yet entered.  This
+     may involve replacement of part of that subtree - but that won't affect
+     the iteration as we won't have reached the pointer to it yet and the
+     ancestry blocks are not replaced (the layout of those does not change).
+
+ (4) Insertion replacing nodes that we're actively processing.  This isn't a
+     problem as we've passed the anchoring pointer and won't switch onto the
+     new layout until we follow the back pointers - at which point we've
+     already examined the leaves in the replaced node (we iterate over all the
+     leaves in a node before following any of its metadata pointers).
+
+     We might, however, re-see some leaves that have been split out into a new
+     branch that's in a slot further along than we were at.
+
+ (5) Insertion replacing nodes that we're processing a dependent branch of.
+     This won't affect us until we follow the back pointers.  Similar to (4).
+
+ (6) Deletion collapsing a branch under us.  This doesn't affect us because the
+     back pointers will get us back to the parent of the new node before we
+     could see the new node.  The entire collapsed subtree is thrown away
+     unchanged - and will still be rooted on the same slot, so we shouldn't
+     process it a second time as we'll go back to slot + 1.
+
+Note:
+
+ (*) Under some circumstances, we need to simultaneously change the parent
+     pointer and the parent slot pointer on a node (say, for example, we
+     inserted another node before it and moved it up a level).  We cannot do
+     this without locking against a read - so we have to replace that node too.
+
+     However, when we're changing a shortcut into a node this isn't a problem
+     as shortcuts only have one slot and so the parent slot number isn't used
+     when traversing backwards over one.  This means that it's okay to change
+     the slot number first - provided suitable barriers are used to make sure
+     the parent slot number is read after the back pointer.
+
+Obsolete blocks and leaves are freed up after an RCU grace period has passed,
+so as long as anyone doing walking or iteration holds the RCU read lock, the
+old superstructure should not go away on them.
diff --git a/Documentation/devicetree/bindings/arc/pmu.txt b/Documentation/devicetree/bindings/arc/pmu.txt
new file mode 100644 (file)
index 0000000..49d5173
--- /dev/null
@@ -0,0 +1,24 @@
+* ARC Performance Monitor Unit
+
+The ARC 700 can be configured with a pipeline performance monitor for counting
+CPU and cache events like cache misses and hits.
+
+Note that:
+ * ARC 700 refers to a family of ARC processor cores;
+   - There is only one type of PMU available for the whole family;
+   - The PMU may support different sets of events; supported events are probed
+     at boot time, as required by the reference manual.
+
+ * The ARC 700 PMU does not support interrupts; although HW events may be
+   counted, the HW events themselves cannot serve as a trigger for a sample.
+
+Required properties:
+
+- compatible : should contain
+       "snps,arc700-pmu"
+
+Example:
+
+pmu {
+        compatible = "snps,arc700-pmu";
+};
index f770ac0..0496759 100644 (file)
@@ -1,7 +1,9 @@
 Calxeda DDR memory controller
 
 Properties:
-- compatible : Should be "calxeda,hb-ddr-ctrl"
+- compatible : Should be:
+  - "calxeda,hb-ddr-ctrl" for ECX-1000
+  - "calxeda,ecx-2000-ddr-ctrl" for ECX-2000
 - reg : Address and size for DDR controller registers.
 - interrupts : Interrupt for DDR controller.
 
diff --git a/Documentation/devicetree/bindings/crypto/omap-des.txt b/Documentation/devicetree/bindings/crypto/omap-des.txt
new file mode 100644 (file)
index 0000000..e8c63bf
--- /dev/null
@@ -0,0 +1,30 @@
+OMAP SoC DES crypto Module
+
+Required properties:
+
+- compatible : Should contain "ti,omap4-des"
+- ti,hwmods: Name of the hwmod associated with the DES module
+- reg : Offset and length of the register set for the module
+- interrupts : the interrupt-specifier for the DES module
+- clocks : A phandle to the functional clock node of the DES module
+           corresponding to each entry in clock-names
+- clock-names : Name of the functional clock, should be "fck"
+
+Optional properties:
+- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
+       Documentation/devicetree/bindings/dma/dma.txt
+       Each entry corresponds to an entry in dma-names
+- dma-names: DMA request names should include "tx" and "rx" if present
+
+Example:
+       /* DRA7xx SoC */
+       des: des@480a5000 {
+               compatible = "ti,omap4-des";
+               ti,hwmods = "des";
+               reg = <0x480a5000 0xa0>;
+               interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
+               dmas = <&sdma 117>, <&sdma 116>;
+               dma-names = "tx", "rx";
+               clocks = <&l3_iclk_div>;
+               clock-names = "fck";
+       };
index f839acd..ad91155 100644 (file)
@@ -6,7 +6,7 @@ Required properties:
   SHAM versions:
   - "ti,omap2-sham" for OMAP2 & OMAP3.
   - "ti,omap4-sham" for OMAP4 and AM33XX.
-  Note that these two versions are incompatible.
+  - "ti,omap5-sham" for OMAP5, DRA7 and AM43XX.
 - ti,hwmods: Name of the hwmod associated with the SHAM module
 - reg : Offset and length of the register set for the module
 - interrupts : the interrupt-specifier for the SHAM module.
index e1f343c..f69bcf5 100644 (file)
@@ -28,7 +28,7 @@ The three cells in order are:
 dependent:
   - bit 7-0: peripheral identifier for the hardware handshaking interface. The
   identifier can be different for tx and rx.
-  - bit 11-8: FIFO configuration. 0 for half FIFO, 1 for ALAP, 1 for ASAP.
+  - bit 11-8: FIFO configuration. 0 for half FIFO, 1 for ALAP, 2 for ASAP.
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/hwmon/lm90.txt b/Documentation/devicetree/bindings/hwmon/lm90.txt
new file mode 100644 (file)
index 0000000..e863248
--- /dev/null
@@ -0,0 +1,44 @@
+* LM90 series thermometer.
+
+Required node properties:
+- compatible: manufacturer and chip name, one of
+               "adi,adm1032"
+               "adi,adt7461"
+               "adi,adt7461a"
+               "gmt,g781"
+               "national,lm90"
+               "national,lm86"
+               "national,lm89"
+               "national,lm99"
+               "dallas,max6646"
+               "dallas,max6647"
+               "dallas,max6649"
+               "dallas,max6657"
+               "dallas,max6658"
+               "dallas,max6659"
+               "dallas,max6680"
+               "dallas,max6681"
+               "dallas,max6695"
+               "dallas,max6696"
+               "onnn,nct1008"
+               "winbond,w83l771"
+               "nxp,sa56004"
+
+- reg: I2C bus address of the device
+
+- vcc-supply: vcc regulator for the supply voltage.
+
+Optional properties:
+- interrupts: Contains a single interrupt specifier which describes the
+              LM90 "-ALERT" pin output.
+              See interrupt-controller/interrupts.txt for the format.
+
+Example LM90 node:
+
+temp-sensor {
+       compatible = "onnn,nct1008";
+       reg = <0x4c>;
+       vcc-supply = <&palmas_ldo6_reg>;
+       interrupt-parent = <&gpio>;
+       interrupts = <TEGRA_GPIO(O, 4) IRQ_TYPE_LEVEL_LOW>;
+}
diff --git a/Documentation/devicetree/bindings/i2c/i2c-bcm-kona.txt b/Documentation/devicetree/bindings/i2c/i2c-bcm-kona.txt
new file mode 100644 (file)
index 0000000..1b87b74
--- /dev/null
@@ -0,0 +1,35 @@
+Broadcom Kona Family I2C
+=========================
+
+This I2C controller is used in the following Broadcom SoCs:
+
+  BCM11130
+  BCM11140
+  BCM11351
+  BCM28145
+  BCM28155
+
+Required Properties
+-------------------
+- compatible: "brcm,bcm11351-i2c", "brcm,kona-i2c"
+- reg: Physical base address and length of controller registers
+- interrupts: The interrupt number used by the controller
+- clocks: clock specifier for the kona i2c external clock
+- clock-frequency: The I2C bus frequency in Hz
+- #address-cells: Should be <1>
+- #size-cells: Should be <0>
+
+Refer to clocks/clock-bindings.txt for generic clock consumer
+properties.
+
+Example:
+
+i2c@3e016000 {
+       compatible = "brcm,bcm11351-i2c","brcm,kona-i2c";
+       reg = <0x3e016000 0x80>;
+       interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+       clocks = <&bsc1_clk>;
+       clock-frequency = <400000>;
+       #address-cells = <1>;
+       #size-cells = <0>;
+};
diff --git a/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt b/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
new file mode 100644 (file)
index 0000000..056732c
--- /dev/null
@@ -0,0 +1,44 @@
+* Samsung's High Speed I2C controller
+
+The Samsung's High Speed I2C controller is used to interface with I2C devices
+at various speeds ranging from 100khz to 3.4Mhz.
+
+Required properties:
+  - compatible: value should be.
+      -> "samsung,exynos5-hsi2c", for i2c compatible with exynos5 hsi2c.
+  - reg: physical base address of the controller and length of memory mapped
+    region.
+  - interrupts: interrupt number to the cpu.
+  - #address-cells: always 1 (for i2c addresses)
+  - #size-cells: always 0
+
+  - Pinctrl:
+    - pinctrl-0: Pin control group to be used for this controller.
+    - pinctrl-names: Should contain only one value - "default".
+
+Optional properties:
+  - clock-frequency: Desired operating frequency in Hz of the bus.
+    -> If not specified, the bus operates in fast-speed mode at
+       at 100khz.
+    -> If specified, the bus operates in high-speed mode only if the
+       clock-frequency is >= 1Mhz.
+
+Example:
+
+hsi2c@12ca0000 {
+       compatible = "samsung,exynos5-hsi2c";
+       reg = <0x12ca0000 0x100>;
+       interrupts = <56>;
+       clock-frequency = <100000>;
+
+       pinctrl-0 = <&i2c4_bus>;
+       pinctrl-names = "default";
+
+       #address-cells = <1>;
+       #size-cells = <0>;
+
+       s2mps11_pmic@66 {
+               compatible = "samsung,s2mps11-pmic";
+               reg = <0x66>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/i2c/i2c-rcar.txt b/Documentation/devicetree/bindings/i2c/i2c-rcar.txt
new file mode 100644 (file)
index 0000000..897cfcd
--- /dev/null
@@ -0,0 +1,23 @@
+I2C for R-Car platforms
+
+Required properties:
+- compatible: Must be one of
+       "renesas,i2c-rcar"
+       "renesas,i2c-r8a7778"
+       "renesas,i2c-r8a7779"
+       "renesas,i2c-r8a7790"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- interrupts: interrupt specifier.
+
+Optional properties:
+- clock-frequency: desired I2C bus clock frequency in Hz. The absence of this
+  propoerty indicates the default frequency 100 kHz.
+
+Examples :
+
+i2c0: i2c@e6500000 {
+       compatible = "renesas,i2c-rcar-h2";
+       reg = <0 0xe6500000 0 0x428>;
+       interrupts = <0 174 0x4>;
+};
diff --git a/Documentation/devicetree/bindings/i2c/i2c-st.txt b/Documentation/devicetree/bindings/i2c/i2c-st.txt
new file mode 100644 (file)
index 0000000..437e0db
--- /dev/null
@@ -0,0 +1,41 @@
+ST SSC binding, for I2C mode operation
+
+Required properties :
+- compatible : Must be "st,comms-ssc-i2c" or "st,comms-ssc4-i2c"
+- reg : Offset and length of the register set for the device
+- interrupts : the interrupt specifier
+- clock-names: Must contain "ssc".
+- clocks: Must contain an entry for each name in clock-names. See the common
+  clock bindings.
+- A pinctrl state named "default" must be defined to set pins in mode of
+  operation for I2C transfer.
+
+Optional properties :
+- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified,
+  the default 100 kHz frequency will be used. As only Normal and Fast modes
+  are supported, possible values are 100000 and 400000.
+- st,i2c-min-scl-pulse-width-us : The minimum valid SCL pulse width that is
+  allowed through the deglitch circuit. In units of us.
+- st,i2c-min-sda-pulse-width-us : The minimum valid SDA pulse width that is
+  allowed through the deglitch circuit. In units of us.
+- A pinctrl state named "idle" could be defined to set pins in idle state
+  when I2C instance is not performing a transfer.
+- A pinctrl state named "sleep" could be defined to set pins in sleep state
+  when driver enters in suspend.
+
+
+
+Example :
+
+i2c0: i2c@fed40000 {
+       compatible      = "st,comms-ssc4-i2c";
+       reg             = <0xfed40000 0x110>;
+       interrupts      =  <GIC_SPI 187 IRQ_TYPE_LEVEL_HIGH>;
+       clocks          = <&CLK_S_ICN_REG_0>;
+       clock-names     = "ssc";
+       clock-frequency = <400000>;
+       pinctrl-names   = "default";
+       pinctrl-0       = <&pinctrl_i2c0_default>;
+       st,i2c-min-scl-pulse-width-us = <0>;
+       st,i2c-min-sda-pulse-width-us = <5>;
+};
index ad6a738..b1cb341 100644 (file)
@@ -15,6 +15,7 @@ adi,adt7461           +/-1C TDM Extended Temp Range I.C
 adt7461                        +/-1C TDM Extended Temp Range I.C
 at,24c08               i2c serial eeprom  (24cxx)
 atmel,24c02            i2c serial eeprom  (24cxx)
+atmel,at97sc3204t      i2c trusted platform module (TPM)
 catalyst,24c32         i2c serial eeprom
 dallas,ds1307          64 x 8, Serial, I2C Real-Time Clock
 dallas,ds1338          I2C RTC with 56-Byte NV RAM
@@ -35,6 +36,7 @@ fsl,mc13892           MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
 fsl,mma8450            MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
 fsl,mpr121             MPR121: Proximity Capacitive Touch Sensor Controller
 fsl,sgtl5000           SGTL5000: Ultra Low-Power Audio Codec
+gmt,g751               G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
 infineon,slb9635tt     Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz)
 infineon,slb9645tt     Infineon SLB9645 I2C TPM (new protocol, max 400khz)
 maxim,ds1050           5 Bit Programmable, Pulse-Width Modulator
@@ -44,6 +46,7 @@ mc,rv3029c2           Real Time Clock Module with I2C-Bus
 national,lm75          I2C TEMP SENSOR
 national,lm80          Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
 national,lm92          Â±0.33°C Accurate, 12-Bit + Sign Temperature Sensor and Thermal Window Comparator with Two-Wire Interface
+nuvoton,npct501                i2c trusted platform module (TPM)
 nxp,pca9556            Octal SMBus and I2C registered interface
 nxp,pca9557            8-bit I2C-bus and SMBus I/O port with reset
 nxp,pcf8563            Real-time clock/calendar
@@ -61,3 +64,4 @@ taos,tsl2550          Ambient Light Sensor with SMBUS/Two Wire Serial Interface
 ti,tsc2003             I2C Touch-Screen Controller
 ti,tmp102              Low Power Digital Temperature Sensor with SMBUS/Two Wire Serial Interface
 ti,tmp275              Digital Temperature Sensor
+winbond,wpct301                i2c trusted platform module (TPM)
index 491c97b..878549b 100644 (file)
@@ -6,7 +6,7 @@ Required properties:
        ti,wires: Wires refer to application modes i.e. 4/5/8 wire touchscreen
                  support on the platform.
        ti,x-plate-resistance: X plate resistance
-       ti,coordiante-readouts: The sequencer supports a total of 16
+       ti,coordinate-readouts: The sequencer supports a total of 16
                                programmable steps each step is used to
                                read a single coordinate. A single
                                 readout is enough but multiple reads can
diff --git a/Documentation/devicetree/bindings/media/st-rc.txt b/Documentation/devicetree/bindings/media/st-rc.txt
new file mode 100644 (file)
index 0000000..05c432d
--- /dev/null
@@ -0,0 +1,29 @@
+Device-Tree bindings for ST IRB IP
+
+Required properties:
+       - compatible: Should contain "st,comms-irb".
+       - reg: Base physical address of the controller and length of memory
+         mapped region.
+       - interrupts: interrupt-specifier for the sole interrupt generated by
+         the device. The interrupt specifier format depends on the interrupt
+         controller parent.
+       - rx-mode: can be "infrared" or "uhf". This property specifies the L1
+         protocol used for receiving remote control signals. rx-mode should
+         be present iff the rx pins are wired up.
+       - tx-mode: should be "infrared". This property specifies the L1
+         protocol used for transmitting remote control signals. tx-mode should
+         be present iff the tx pins are wired up.
+
+Optional properties:
+       - pinctrl-names, pinctrl-0: the pincontrol settings to configure muxing
+         properly for IRB pins.
+       - clocks : phandle with clock-specifier pair for IRB.
+
+Example node:
+
+       rc: rc@fe518000 {
+               compatible      = "st,comms-irb";
+               reg             = <0xfe518000 0x234>;
+               interrupts      = <0 203 0>;
+               rx-mode         = "infrared";
+       };
diff --git a/Documentation/devicetree/bindings/mfd/as3722.txt b/Documentation/devicetree/bindings/mfd/as3722.txt
new file mode 100644 (file)
index 0000000..fc2191e
--- /dev/null
@@ -0,0 +1,194 @@
+* ams AS3722 Power management IC.
+
+Required properties:
+-------------------
+- compatible: Must be "ams,as3722".
+- reg: I2C device address.
+- interrupt-controller: AS3722 has internal interrupt controller which takes the
+  interrupt request from internal sub-blocks like RTC, regulators, GPIOs as well
+  as external input.
+- #interrupt-cells: Should be set to 2 for IRQ number and flags.
+  The first cell is the IRQ number. IRQ numbers for different interrupt source
+  of AS3722 are defined at dt-bindings/mfd/as3722.h
+  The second cell is the flags, encoded as the trigger masks from binding document
+       interrupts.txt, using dt-bindings/irq.
+
+Optional submodule and their properties:
+=======================================
+
+Pinmux and GPIO:
+===============
+Device has 8 GPIO pins which can be configured as GPIO as well as the special IO
+functions.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+Following are properties which is needed if GPIO and pinmux functionality
+is required:
+    Required properties:
+    -------------------
+       - gpio-controller: Marks the device node as a GPIO controller.
+       - #gpio-cells: Number of GPIO cells. Refer to binding document
+                       gpio/gpio.txt
+
+    Optional properties:
+    --------------------
+       Following properties are require if pin control setting is required
+       at boot.
+       - pinctrl-names: A pinctrl state named "default" be defined, using the
+               bindings in pinctrl/pinctrl-binding.txt.
+       - pinctrl[0...n]: Properties to contain the phandle that refer to
+               different nodes of pin control settings. These nodes represents
+               the pin control setting of state 0 to state n. Each of these
+               nodes contains different subnodes to represents some desired
+               configuration for a list of pins. This configuration can
+               include the mux function to select on those pin(s), and
+               various pin configuration parameters, such as pull-up,
+               open drain.
+
+               Each subnode have following properties:
+               Required properties:
+                   - pins: List of pins. Valid values of pins properties are:
+                               gpio0, gpio1, gpio2, gpio3, gpio4, gpio5,
+                               gpio6, gpio7
+
+               Optional properties:
+                       function, bias-disable, bias-pull-up, bias-pull-down,
+                       bias-high-impedance, drive-open-drain.
+
+                       Valid values for function properties are:
+                               gpio, interrupt-out, gpio-in-interrupt,
+                               vsup-vbat-low-undebounce-out,
+                               vsup-vbat-low-debounce-out,
+                               voltage-in-standby, oc-pg-sd0, oc-pg-sd6,
+                               powergood-out, pwm-in, pwm-out, clk32k-out,
+                               watchdog-in, soft-reset-in
+
+Regulators:
+===========
+Device has multiple DCDC and LDOs. The node "regulators" is require if regulator
+functionality is needed.
+
+Following are properties of regulator subnode.
+
+    Optional properties:
+    -------------------
+       The input supply of regulators are the optional properties on the
+       regulator node. The input supply of these regulators are provided
+       through following properties:
+               vsup-sd2-supply: Input supply for SD2.
+               vsup-sd3-supply: Input supply for SD3.
+               vsup-sd4-supply: Input supply for SD4.
+               vsup-sd5-supply: Input supply for SD5.
+               vin-ldo0-supply: Input supply for LDO0.
+               vin-ldo1-6-supply: Input supply for LDO1 and LDO6.
+               vin-ldo2-5-7-supply: Input supply for LDO2, LDO5 and LDO7.
+               vin-ldo3-4-supply: Input supply for LDO3 and LDO4.
+               vin-ldo9-10-supply: Input supply for LDO9 and LDO10.
+               vin-ldo11-supply: Input supply for LDO11.
+
+    Optional sub nodes for regulators:
+    ---------------------------------
+       The subnodes name is the name of regulator and it must be one of:
+       sd[0-6], ldo[0-7], ldo[9-11]
+
+       Each sub-node should contain the constraints and initialization
+       information for that regulator. See regulator.txt for a description
+       of standard properties for these sub-nodes.
+       Additional optional custom properties  are listed below.
+               ams,ext-control: External control of the rail. The option of
+                       this properties will tell which external input is
+                       controlling this rail. Valid values are 0, 1, 2 ad 3.
+                       0: There is no external control of this rail.
+                       1: Rail is controlled by ENABLE1 input pin.
+                       2: Rail is controlled by ENABLE2 input pin.
+                       3: Rail is controlled by ENABLE3 input pin.
+                       Missing this property on DT will be assume as no
+                       external control. The external control pin macros
+                       are defined @dt-bindings/mfd/as3722.h
+
+               ams,enable-tracking: Enable tracking with SD1, only supported
+                       by LDO3.
+
+Example:
+--------
+#include <dt-bindings/mfd/as3722.h>
+...
+ams3722 {
+       compatible = "ams,as3722";
+       reg = <0x48>;
+
+       interrupt-parent = <&intc>;
+       interrupt-controller;
+       #interrupt-cells = <2>;
+
+       gpio-controller;
+       #gpio-cells = <2>;
+
+       pinctrl-names = "default";
+       pinctrl-0 = <&as3722_default>;
+
+       as3722_default: pinmux {
+                       gpio0 {
+                               pins = "gpio0";
+                               function = "gpio";
+                               bias-pull-down;
+                       };
+
+                       gpio1_2_4_7 {
+                               pins = "gpio1", "gpio2", "gpio4", "gpio7";
+                               function = "gpio";
+                               bias-pull-up;
+                       };
+
+                       gpio5 {
+                               pins = "gpio5";
+                               function = "clk32k_out";
+                       };
+       }
+
+       regulators {
+                       vsup-sd2-supply = <...>;
+                       ...
+
+                       sd0 {
+                               regulator-name = "vdd_cpu";
+                               regulator-min-microvolt = <700000>;
+                               regulator-max-microvolt = <1400000>;
+                               regulator-always-on;
+                               ams,ext-control = <2>;
+                       };
+
+                       sd1 {
+                               regulator-name = "vdd_core";
+                               regulator-min-microvolt = <700000>;
+                               regulator-max-microvolt = <1400000>;
+                               regulator-always-on;
+                               ams,ext-control = <1>;
+                       };
+
+                       sd2 {
+                               regulator-name = "vddio_ddr";
+                               regulator-min-microvolt = <1350000>;
+                               regulator-max-microvolt = <1350000>;
+                               regulator-always-on;
+                       };
+
+                       sd4 {
+                               regulator-name = "avdd-hdmi-pex";
+                               regulator-min-microvolt = <1050000>;
+                               regulator-max-microvolt = <1050000>;
+                               regulator-always-on;
+                       };
+
+                       sd5 {
+                               regulator-name = "vdd-1v8";
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-always-on;
+                       };
+                       ....
+       };
+};
index c9332c6..78a840d 100644 (file)
@@ -1,10 +1,10 @@
 
 * Samsung S2MPS11 Voltage and Current Regulator
 
-The Samsung S2MP211 is a multi-function device which includes voltage and
+The Samsung S2MPS11 is a multi-function device which includes voltage and
 current regulators, RTC, charger controller and other sub-blocks. It is
-interfaced to the host controller using a I2C interface. Each sub-block is
-addressed by the host system using different I2C slave address.
+interfaced to the host controller using an I2C interface. Each sub-block is
+addressed by the host system using different I2C slave addresses.
 
 Required properties:
 - compatible: Should be "samsung,s2mps11-pmic".
@@ -43,7 +43,8 @@ sub-node should be of the format as listed below.
 
  BUCK[2/3/4/6] supports disabling ramp delay on hardware, so explictly
  regulator-ramp-delay = <0> can be used for them to disable ramp delay.
- In absence of regulator-ramp-delay property, default ramp delay will be used.
+ In the absence of the regulator-ramp-delay property, the default ramp
+ delay will be used.
 
 NOTE: Some BUCKs share the ramp rate setting i.e. same ramp value will be set
 for a particular group of BUCKs. So provide same regulator-ramp-delay<value>.
@@ -58,10 +59,10 @@ supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number
 as per the datasheet of s2mps11.
 
        - LDOn
-                 - valid values for n are 1 to 28
+                 - valid values for n are 1 to 38
                  - Example: LDO0, LD01, LDO28
        - BUCKn
-                 - valid values for n are 1 to 9.
+                 - valid values for n are 1 to 10.
                  - Example: BUCK1, BUCK2, BUCK9
 
 Example:
index 1dd6225..9046ba0 100644 (file)
@@ -12,6 +12,11 @@ Required properties:
 Optional properties:
 - fsl,cd-controller : Indicate to use controller internal card detection
 - fsl,wp-controller : Indicate to use controller internal write protection
+- fsl,delay-line : Specify the number of delay cells for override mode.
+  This is used to set the clock delay for DLL(Delay Line) on override mode
+  to select a proper data sampling window in case the clock quality is not good
+  due to signal path is too long on the board. Please refer to eSDHC/uSDHC
+  chapter, DLL (Delay Line) section in RM for details.
 
 Examples:
 
index 066a78b..8f3f133 100644 (file)
@@ -52,6 +52,9 @@ Optional properties:
   is specified and the ciu clock is specified then we'll try to set the ciu
   clock to this at probe time.
 
+* clock-freq-min-max: Minimum and Maximum clock frequency for card output
+  clock(cclk_out). If it's not specified, max is 200MHZ and min is 400KHz by default.
+
 * num-slots: specifies the number of slots supported by the controller.
   The number of physical slots actually used could be equal or less than the
   value specified by num-slots. If this property is not specified, the value
@@ -66,6 +69,10 @@ Optional properties:
 
 * supports-highspeed: Enables support for high speed cards (up to 50MHz)
 
+* caps2-mmc-hs200-1_8v: Supports mmc HS200 SDR 1.8V mode
+
+* caps2-mmc-hs200-1_2v: Supports mmc HS200 SDR 1.2V mode
+
 * broken-cd: as documented in mmc core bindings.
 
 * vmmc-supply: The phandle to the regulator to use for vmmc.  If this is
@@ -93,8 +100,10 @@ board specific portions as listed below.
 
        dwmmc0@12200000 {
                clock-frequency = <400000000>;
+               clock-freq-min-max = <400000 200000000>;
                num-slots = <1>;
                supports-highspeed;
+               caps2-mmc-hs200-1_8v;
                broken-cd;
                fifo-depth = <0x80>;
                card-detect-delay = <200>;
diff --git a/Documentation/devicetree/bindings/power/twl-charger.txt b/Documentation/devicetree/bindings/power/twl-charger.txt
new file mode 100644 (file)
index 0000000..d5c7062
--- /dev/null
@@ -0,0 +1,20 @@
+TWL BCI (Battery Charger Interface)
+
+Required properties:
+- compatible:
+  - "ti,twl4030-bci"
+- interrupts: two interrupt lines from the TWL SIH (secondary
+  interrupt handler) - interrupts 9 and 2.
+
+Optional properties:
+- ti,bb-uvolt: microvolts for charging the backup battery.
+- ti,bb-uamp: microamps for charging the backup battery.
+
+Examples:
+
+bci {
+   compatible = "ti,twl4030-bci";
+   interrupts = <9>, <2>;
+   ti,bb-uvolt = <3200000>;
+   ti,bb-uamp = <150>;
+};
diff --git a/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt b/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt
new file mode 100644 (file)
index 0000000..4f6a550
--- /dev/null
@@ -0,0 +1,32 @@
+TI BQ24735 Charge Controller
+~~~~~~~~~~
+
+Required properties :
+ - compatible : "ti,bq24735"
+
+Optional properties :
+ - interrupts : Specify the interrupt to be used to trigger when the AC
+   adapter is either plugged in or removed.
+ - ti,ac-detect-gpios : This GPIO is optionally used to read the AC adapter
+   presence. This is a Host GPIO that is configured as an input and
+   connected to the bq24735.
+ - ti,charge-current : Used to control and set the charging current. This value
+   must be between 128mA and 8.128A with a 64mA step resolution. The POR value
+   is 0x0000h. This number is in mA (e.g. 8192), see spec for more information
+   about the ChargeCurrent (0x14h) register.
+ - ti,charge-voltage : Used to control and set the charging voltage. This value
+   must be between 1.024V and 19.2V with a 16mV step resolution. The POR value
+   is 0x0000h. This number is in mV (e.g. 19200), see spec for more information
+   about the ChargeVoltage (0x15h) register.
+ - ti,input-current : Used to control and set the charger input current. This
+   value must be between 128mA and 8.064A with a 128mA step resolution. The
+   POR value is 0x1000h. This number is in mA (e.g. 8064), see the spec for
+   more information about the InputCurrent (0x3fh) register.
+
+Example:
+
+       bq24735@9 {
+               compatible = "ti,bq24735";
+               reg = <0x9>;
+               ti,ac-detect-gpios = <&gpio 72 0x1>;
+       }
index 2a4b4bc..7fc1b01 100644 (file)
@@ -1,33 +1,30 @@
-* Freescale 83xx DMA Controller
+* Freescale DMA Controllers
 
-Freescale PowerPC 83xx have on chip general purpose DMA controllers.
+** Freescale Elo DMA Controller
+   This is a little-endian 4-channel DMA controller, used in Freescale mpc83xx
+   series chips such as mpc8315, mpc8349, mpc8379 etc.
 
 Required properties:
 
-- compatible        : compatible list, contains 2 entries, first is
-                "fsl,CHIP-dma", where CHIP is the processor
-                (mpc8349, mpc8360, etc.) and the second is
-                "fsl,elo-dma"
-- reg               : <registers mapping for DMA general status reg>
-- ranges               : Should be defined as specified in 1) to describe the
-                 DMA controller channels.
+- compatible        : must include "fsl,elo-dma"
+- reg               : DMA General Status Register, i.e. DGSR which contains
+                      status for all the 4 DMA channels
+- ranges            : describes the mapping between the address space of the
+                      DMA channels and the address space of the DMA controller
 - cell-index        : controller index.  0 for controller @ 0x8100
-- interrupts        : <interrupt mapping for DMA IRQ>
+- interrupts        : interrupt specifier for DMA IRQ
 - interrupt-parent  : optional, if needed for interrupt mapping
 
-
 - DMA channel nodes:
-        - compatible        : compatible list, contains 2 entries, first is
-                        "fsl,CHIP-dma-channel", where CHIP is the processor
-                        (mpc8349, mpc8350, etc.) and the second is
-                        "fsl,elo-dma-channel". However, see note below.
-        - reg               : <registers mapping for channel>
-        - cell-index        : dma channel index starts at 0.
+        - compatible        : must include "fsl,elo-dma-channel"
+                              However, see note below.
+        - reg               : DMA channel specific registers
+        - cell-index        : DMA channel index starts at 0.
 
 Optional properties:
-        - interrupts        : <interrupt mapping for DMA channel IRQ>
-                         (on 83xx this is expected to be identical to
-                          the interrupts property of the parent node)
+        - interrupts        : interrupt specifier for DMA channel IRQ
+                              (on 83xx this is expected to be identical to
+                              the interrupts property of the parent node)
         - interrupt-parent  : optional, if needed for interrupt mapping
 
 Example:
@@ -70,30 +67,27 @@ Example:
                };
        };
 
-* Freescale 85xx/86xx DMA Controller
-
-Freescale PowerPC 85xx/86xx have on chip general purpose DMA controllers.
+** Freescale EloPlus DMA Controller
+   This is a 4-channel DMA controller with extended addresses and chaining,
+   mainly used in Freescale mpc85xx/86xx, Pxxx and BSC series chips, such as
+   mpc8540, mpc8641 p4080, bsc9131 etc.
 
 Required properties:
 
-- compatible        : compatible list, contains 2 entries, first is
-                "fsl,CHIP-dma", where CHIP is the processor
-                (mpc8540, mpc8540, etc.) and the second is
-                "fsl,eloplus-dma"
-- reg               : <registers mapping for DMA general status reg>
+- compatible        : must include "fsl,eloplus-dma"
+- reg               : DMA General Status Register, i.e. DGSR which contains
+                      status for all the 4 DMA channels
 - cell-index        : controller index.  0 for controller @ 0x21000,
                                          1 for controller @ 0xc000
-- ranges               : Should be defined as specified in 1) to describe the
-                 DMA controller channels.
+- ranges            : describes the mapping between the address space of the
+                      DMA channels and the address space of the DMA controller
 
 - DMA channel nodes:
-        - compatible        : compatible list, contains 2 entries, first is
-                        "fsl,CHIP-dma-channel", where CHIP is the processor
-                        (mpc8540, mpc8560, etc.) and the second is
-                        "fsl,eloplus-dma-channel". However, see note below.
-        - cell-index        : dma channel index starts at 0.
-        - reg               : <registers mapping for channel>
-        - interrupts        : <interrupt mapping for DMA channel IRQ>
+        - compatible        : must include "fsl,eloplus-dma-channel"
+                              However, see note below.
+        - cell-index        : DMA channel index starts at 0.
+        - reg               : DMA channel specific registers
+        - interrupts        : interrupt specifier for DMA channel IRQ
         - interrupt-parent  : optional, if needed for interrupt mapping
 
 Example:
@@ -134,6 +128,76 @@ Example:
                };
        };
 
+** Freescale Elo3 DMA Controller
+   DMA controller which has same function as EloPlus except that Elo3 has 8
+   channels while EloPlus has only 4, it is used in Freescale Txxx and Bxxx
+   series chips, such as t1040, t4240, b4860.
+
+Required properties:
+
+- compatible        : must include "fsl,elo3-dma"
+- reg               : contains two entries for DMA General Status Registers,
+                      i.e. DGSR0 which includes status for channel 1~4, and
+                      DGSR1 for channel 5~8
+- ranges            : describes the mapping between the address space of the
+                      DMA channels and the address space of the DMA controller
+
+- DMA channel nodes:
+        - compatible        : must include "fsl,eloplus-dma-channel"
+        - reg               : DMA channel specific registers
+        - interrupts        : interrupt specifier for DMA channel IRQ
+        - interrupt-parent  : optional, if needed for interrupt mapping
+
+Example:
+dma@100300 {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       compatible = "fsl,elo3-dma";
+       reg = <0x100300 0x4>,
+             <0x100600 0x4>;
+       ranges = <0x0 0x100100 0x500>;
+       dma-channel@0 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x0 0x80>;
+               interrupts = <28 2 0 0>;
+       };
+       dma-channel@80 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x80 0x80>;
+               interrupts = <29 2 0 0>;
+       };
+       dma-channel@100 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x100 0x80>;
+               interrupts = <30 2 0 0>;
+       };
+       dma-channel@180 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x180 0x80>;
+               interrupts = <31 2 0 0>;
+       };
+       dma-channel@300 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x300 0x80>;
+               interrupts = <76 2 0 0>;
+       };
+       dma-channel@380 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x380 0x80>;
+               interrupts = <77 2 0 0>;
+       };
+       dma-channel@400 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x400 0x80>;
+               interrupts = <78 2 0 0>;
+       };
+       dma-channel@480 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x480 0x80>;
+               interrupts = <79 2 0 0>;
+       };
+};
+
 Note on DMA channel compatible properties: The compatible property must say
 "fsl,elo-dma-channel" or "fsl,eloplus-dma-channel" to be used by the Elo DMA
 driver (fsldma).  Any DMA channel used by fsldma cannot be used by another
index d61fccd..5538de9 100644 (file)
@@ -15,7 +15,7 @@ Required properties:
     samsung,s5pc100-pwm - for 32-bit timers present on S5PC100, S5PV210,
                          Exynos4210 rev0 SoCs
     samsung,exynos4210-pwm - for 32-bit timers present on Exynos4210,
-                          Exynos4x12 and Exynos5250 SoCs
+                          Exynos4x12, Exynos5250 and Exynos5420 SoCs
 - reg: base address and size of register area
 - interrupts: list of timer interrupts (one interrupt per timer, starting at
   timer 0)
index 4c85c4c..2ba5f9c 100644 (file)
@@ -2,8 +2,8 @@ OMAP2+ McSPI device
 
 Required properties:
 - compatible :
-  - "ti,omap2-spi" for OMAP2 & OMAP3.
-  - "ti,omap4-spi" for OMAP4+.
+  - "ti,omap2-mcspi" for OMAP2 & OMAP3.
+  - "ti,omap4-mcspi" for OMAP4+.
 - ti,spi-num-cs : Number of chipselect supported  by the instance.
 - ti,hwmods: Name of the hwmod associated to the McSPI
 - ti,pindir-d0-out-d1-in: Select the D0 pin as output and D1 as
index 1e4fc72..764db86 100644 (file)
@@ -10,12 +10,16 @@ Required properties:
       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)
+  - power-supply: regulator for supply voltage
 
 Optional properties:
   - pwm-names: a list of names for the PWM devices specified in the
                "pwms" property (see PWM binding[0])
+  - enable-gpios: contains a single GPIO specifier for the GPIO which enables
+                  and disables the backlight (see GPIO binding[1])
 
 [0]: Documentation/devicetree/bindings/pwm/pwm.txt
+[1]: Documentation/devicetree/bindings/gpio/gpio.txt
 
 Example:
 
@@ -25,4 +29,7 @@ Example:
 
                brightness-levels = <0 4 8 16 32 64 128 255>;
                default-brightness-level = <6>;
+
+               power-supply = <&vdd_bl_reg>;
+               enable-gpios = <&gpio 58 0>;
        };
diff --git a/Documentation/devicetree/bindings/watchdog/dw_wdt.txt b/Documentation/devicetree/bindings/watchdog/dw_wdt.txt
new file mode 100644 (file)
index 0000000..08e16f6
--- /dev/null
@@ -0,0 +1,21 @@
+Synopsys Designware Watchdog Timer
+
+Required Properties:
+
+- compatible   : Should contain "snps,dw-wdt"
+- reg          : Base address and size of the watchdog timer registers.
+- clocks       : phandle + clock-specifier for the clock that drives the
+               watchdog timer.
+
+Optional Properties:
+
+- interrupts   : The interrupt used for the watchdog timeout warning.
+
+Example:
+
+       watchdog0: wd@ffd02000 {
+               compatible = "snps,dw-wdt";
+               reg = <0xffd02000 0x1000>;
+               interrupts = <0 171 4>;
+               clocks = <&per_base_clk>;
+       };
diff --git a/Documentation/devicetree/bindings/watchdog/moxa,moxart-watchdog.txt b/Documentation/devicetree/bindings/watchdog/moxa,moxart-watchdog.txt
new file mode 100644 (file)
index 0000000..1169857
--- /dev/null
@@ -0,0 +1,15 @@
+MOXA ART Watchdog timer
+
+Required properties:
+
+- compatible : Must be "moxa,moxart-watchdog"
+- reg : Should contain registers location and length
+- clocks : Should contain phandle for the clock that drives the counter
+
+Example:
+
+       watchdog: watchdog@98500000 {
+               compatible = "moxa,moxart-watchdog";
+               reg = <0x98500000 0x10>;
+               clocks = <&coreclk>;
+       };
diff --git a/Documentation/devicetree/bindings/watchdog/rt2880-wdt.txt b/Documentation/devicetree/bindings/watchdog/rt2880-wdt.txt
new file mode 100644 (file)
index 0000000..d7bab3d
--- /dev/null
@@ -0,0 +1,19 @@
+Ralink Watchdog Timers
+
+Required properties:
+- compatible: must be "ralink,rt2880-wdt"
+- reg: physical base address of the controller and length of the register range
+
+Optional properties:
+- interrupt-parent: phandle to the INTC device node
+- interrupts: Specify the INTC interrupt number
+
+Example:
+
+       watchdog@120 {
+               compatible = "ralink,rt2880-wdt";
+               reg = <0x120 0x10>;
+
+               interrupt-parent = <&intc>;
+               interrupts = <1>;
+       };
diff --git a/Documentation/devicetree/bindings/watchdog/sirfsoc_wdt.txt b/Documentation/devicetree/bindings/watchdog/sirfsoc_wdt.txt
new file mode 100644 (file)
index 0000000..9cbc76c
--- /dev/null
@@ -0,0 +1,14 @@
+SiRFSoC Timer and Watchdog Timer(WDT) Controller
+
+Required properties:
+- compatible: "sirf,prima2-tick"
+- reg: Address range of tick timer/WDT register set
+- interrupts: interrupt number to the cpu
+
+Example:
+
+timer@b0020000 {
+       compatible = "sirf,prima2-tick";
+       reg = <0xb0020000 0x1000>;
+       interrupts = <0>;
+};
index a2b5663..dd77a81 100644 (file)
@@ -15,39 +15,48 @@ be built as module or inside kernel. Let's consider those cases.
 
        Part 2 - When dmatest is built as a module...
 
-After mounting debugfs and loading the module, the /sys/kernel/debug/dmatest
-folder with nodes will be created. There are two important files located. First
-is the 'run' node that controls run and stop phases of the test, and the second
-one, 'results', is used to get the test case results.
-
-Note that in this case test will not run on load automatically.
-
 Example of usage:
+       % modprobe dmatest channel=dma0chan0 timeout=2000 iterations=1 run=1
+
+...or:
+       % modprobe dmatest
        % echo dma0chan0 > /sys/module/dmatest/parameters/channel
        % echo 2000 > /sys/module/dmatest/parameters/timeout
        % echo 1 > /sys/module/dmatest/parameters/iterations
-       % echo 1 > /sys/kernel/debug/dmatest/run
+       % echo 1 > /sys/module/dmatest/parameters/run
+
+...or on the kernel command line:
+
+       dmatest.channel=dma0chan0 dmatest.timeout=2000 dmatest.iterations=1 dmatest.run=1
 
 Hint: available channel list could be extracted by running the following
 command:
        % ls -1 /sys/class/dma/
 
-After a while you will start to get messages about current status or error like
-in the original code.
+Once started a message like "dmatest: Started 1 threads using dma0chan0" is
+emitted.  After that only test failure messages are reported until the test
+stops.
 
 Note that running a new test will not stop any in progress test.
 
-The following command should return actual state of the test.
-       % cat /sys/kernel/debug/dmatest/run
-
-To wait for test done the user may perform a busy loop that checks the state.
-
-       % while [ $(cat /sys/kernel/debug/dmatest/run) = "Y" ]
-       > do
-       >       echo -n "."
-       >       sleep 1
-       > done
-       > echo
+The following command returns the state of the test.
+       % cat /sys/module/dmatest/parameters/run
+
+To wait for test completion userpace can poll 'run' until it is false, or use
+the wait parameter.  Specifying 'wait=1' when loading the module causes module
+initialization to pause until a test run has completed, while reading
+/sys/module/dmatest/parameters/wait waits for any running test to complete
+before returning.  For example, the following scripts wait for 42 tests
+to complete before exiting.  Note that if 'iterations' is set to 'infinite' then
+waiting is disabled.
+
+Example:
+       % modprobe dmatest run=1 iterations=42 wait=1
+       % modprobe -r dmatest
+...or:
+       % modprobe dmatest run=1 iterations=42
+       % cat /sys/module/dmatest/parameters/wait
+       % modprobe -r dmatest
 
        Part 3 - When built-in in the kernel...
 
@@ -62,21 +71,22 @@ case. You always could check them at run-time by running
 
        Part 4 - Gathering the test results
 
-The module provides a storage for the test results in the memory. The gathered
-data could be used after test is done.
+Test results are printed to the kernel log buffer with the format:
 
-The special file 'results' in the debugfs represents gathered data of the in
-progress test. The messages collected are printed to the kernel log as well.
+"dmatest: result <channel>: <test id>: '<error msg>' with src_off=<val> dst_off=<val> len=<val> (<err code>)"
 
 Example of output:
-       % cat /sys/kernel/debug/dmatest/results
-       dma0chan0-copy0: #1: No errors with src_off=0x7bf dst_off=0x8ad len=0x3fea (0)
+       % dmesg | tail -n 1
+       dmatest: result dma0chan0-copy0: #1: No errors with src_off=0x7bf dst_off=0x8ad len=0x3fea (0)
 
 The message format is unified across the different types of errors. A number in
 the parens represents additional information, e.g. error code, error counter,
-or status.
+or status.  A test thread also emits a summary line at completion listing the
+number of tests executed, number that failed, and a result code.
 
-Comparison between buffers is stored to the dedicated structure.
+Example:
+       % dmesg | tail -n 1
+       dmatest: dma0chan0-copy0: summary 1 test, 0 failures 1000 iops 100000 KB/s (0)
 
-Note that the verify result is now accessible only via file 'results' in the
-debugfs.
+The details of a data miscompare error are also emitted, but do not follow the
+above format.
index 9dae594..5dd282d 100644 (file)
@@ -70,6 +70,12 @@ Unless otherwise specified, all options default to off.
 
        See comments at the top of fs/btrfs/check-integrity.c for more info.
 
+  commit=<seconds>
+       Set the interval of periodic commit, 30 seconds by default. Higher
+       values defer data being synced to permanent storage with obvious
+       consequences when the system crashes. The upper bound is not forced,
+       but a warning is printed if it's more than 300 seconds (5 minutes).
+
   compress
   compress=<type>
   compress-force
@@ -154,7 +160,11 @@ Unless otherwise specified, all options default to off.
        Currently this scans a list of several previous tree roots and tries to 
        use the first readable.
 
- skip_balance
+  rescan_uuid_tree
+       Force check and rebuild procedure of the UUID tree. This should not
+       normally be needed.
+
+  skip_balance
        Skip automatic resume of interrupted balance operation after mount.
        May be resumed with "btrfs balance resume."
 
@@ -234,24 +244,14 @@ available from the git repository at the following location:
 
 These include the following tools:
 
-mkfs.btrfs: create a filesystem
-
-btrfsctl: control program to create snapshots and subvolumes:
+* mkfs.btrfs: create a filesystem
 
-       mount /dev/sda2 /mnt
-       btrfsctl -s new_subvol_name /mnt
-       btrfsctl -s snapshot_of_default /mnt/default
-       btrfsctl -s snapshot_of_new_subvol /mnt/new_subvol_name
-       btrfsctl -s snapshot_of_a_snapshot /mnt/snapshot_of_new_subvol
-       ls /mnt
-       default snapshot_of_a_snapshot snapshot_of_new_subvol
-       new_subvol_name snapshot_of_default
+* btrfs: a single tool to manage the filesystems, refer to the manpage for more details
 
-       Snapshots and subvolumes cannot be deleted right now, but you can
-       rm -rf all the files and directories inside them.
+* 'btrfsck' or 'btrfs check': do a consistency check of the filesystem
 
-btrfsck: do a limited check of the FS extent trees.
+Other tools for specific tasks:
 
-btrfs-debug-tree: print all of the FS metadata in text form.  Example:
+* btrfs-convert: in-place conversion from ext2/3/4 filesystems
 
-       btrfs-debug-tree /dev/sda2 >& big_output_file
+* btrfs-image: dump filesystem metadata for debugging
index b466974..ab81013 100644 (file)
@@ -122,6 +122,12 @@ Supported chips:
     Prefix: 'g781'
     Addresses scanned: I2C 0x4c, 0x4d
     Datasheet: Not publicly available from GMT
+  * Texas Instruments TMP451
+    Prefix: 'tmp451'
+    Addresses scanned: I2C 0x4c
+    Datasheet: Publicly available at TI website
+               http://www.ti.com/litv/pdf/sbos686
+
 
 Author: Jean Delvare <khali@linux-fr.org>
 
index d29dea0..7b0dcdb 100644 (file)
@@ -25,6 +25,7 @@ Supported adapters:
   * Intel Avoton (SOC)
   * Intel Wellsburg (PCH)
   * Intel Coleto Creek (PCH)
+  * Intel Wildcat Point-LP (PCH)
    Datasheets: Publicly available at the Intel website
 
 On Intel Patsburg and later chipsets, both the normal host SMBus controller
index 8002c89..31bb6a4 100644 (file)
@@ -122,12 +122,14 @@ D-Pad:
       BTN_DPAD_*
     Analog buttons are reported as:
       ABS_HAT0X and ABS_HAT0Y
+      (for ABS values negative is left/up, positive is right/down)
 
 Analog-Sticks:
   The left analog-stick is reported as ABS_X, ABS_Y. The right analog stick is
   reported as ABS_RX, ABS_RY. Zero, one or two sticks may be present.
   If analog-sticks provide digital buttons, they are mapped accordingly as
   BTN_THUMBL (first/left) and BTN_THUMBR (second/right).
+    (for ABS values negative is left/up, positive is right/down)
 
 Triggers:
   Trigger buttons can be available as digital or analog buttons or both. User-
@@ -138,6 +140,7 @@ Triggers:
   ABS_HAT2X (right/ZR) and BTN_TL2 or ABS_HAT2Y (left/ZL).
   If only one trigger-button combination is present (upper+lower), they are
   reported as "right" triggers (BTN_TR/ABS_HAT1X).
+    (ABS trigger values start at 0, pressure is reported as positive values)
 
 Menu-Pad:
   Menu buttons are always digital and are mapped according to their location
index 8ef6dbb..bbc99c0 100644 (file)
@@ -20,16 +20,9 @@ symbols have been introduced.
 To see a list of new config symbols when using "make oldconfig", use
 
        cp user/some/old.config .config
-       yes "" | make oldconfig >conf.new
+       make listnewconfig
 
-and the config program will list as (NEW) any new symbols that have
-unknown values.  Of course, the .config file is also updated with
-new (default) values, so you can use:
-
-       grep "(NEW)" conf.new
-
-to see the new config symbols or you can use diffconfig to see the
-differences between the previous and new .config files:
+and the config program will list any new symbols, one per line.
 
        scripts/diffconfig .config.old .config | less
 
index 9ca3e74..50680a5 100644 (file)
@@ -1190,15 +1190,24 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        owned by uid=0.
 
        ima_hash=       [IMA]
-                       Format: { "sha1" | "md5" }
+                       Format: { md5 | sha1 | rmd160 | sha256 | sha384
+                                  | sha512 | ... }
                        default: "sha1"
 
+                       The list of supported hash algorithms is defined
+                       in crypto/hash_info.h.
+
        ima_tcb         [IMA]
                        Load a policy which meets the needs of the Trusted
                        Computing Base.  This means IMA will measure all
                        programs exec'd, files mmap'd for exec, and all files
                        opened for read by uid=0.
 
+       ima_template=   [IMA]
+                       Select one of defined IMA measurements template formats.
+                       Formats: { "ima" | "ima-ng" }
+                       Default: "ima-ng"
+
        init=           [KNL]
                        Format: <full_path>
                        Run specified binary instead of /sbin/init as init
index 8b8a057..3c12d9a 100644 (file)
@@ -577,9 +577,6 @@ tcp_limit_output_bytes - INTEGER
        typical pfifo_fast qdiscs.
        tcp_limit_output_bytes limits the number of bytes on qdisc
        or device to reduce artificial RTT/cwnd and reduce bufferbloat.
-       Note: For GSO/TSO enabled flows, we try to have at least two
-       packets in flight. Reducing tcp_limit_output_bytes might also
-       reduce the size of individual GSO packet (64KB being the max)
        Default: 131072
 
 tcp_challenge_ack_limit - INTEGER
index 3f10b39..89a8816 100644 (file)
@@ -135,11 +135,11 @@ 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_ALERT_MIN - minimum battery temperature alert.
+TEMP_ALERT_MAX - maximum battery temperature alert.
 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.
+TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert.
+TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert.
 
 TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e.
 while battery powers a load)
index 0f54333..b6ce00b 100644 (file)
@@ -547,13 +547,11 @@ helper functions described in Section 4.  In that case, pm_runtime_resume()
 should be used.  Of course, for this purpose the device's runtime PM has to be
 enabled earlier by calling pm_runtime_enable().
 
-If the device bus type's or driver's ->probe() callback runs
-pm_runtime_suspend() or pm_runtime_idle() or their asynchronous counterparts,
-they will fail returning -EAGAIN, because the device's usage counter is
-incremented by the driver core before executing ->probe().  Still, it may be
-desirable to suspend the device as soon as ->probe() has finished, so the driver
-core uses pm_runtime_put_sync() to invoke the subsystem-level idle callback for
-the device at that time.
+It may be desirable to suspend the device once ->probe() has finished.
+Therefore the driver core uses the asyncronous pm_request_idle() to submit a
+request to execute the subsystem-level idle callback for the device at that
+time.  A driver that makes use of the runtime autosuspend feature, may want to
+update the last busy mark before returning from ->probe().
 
 Moreover, the driver core prevents runtime PM callbacks from racing with the bus
 notifier callback in __device_release_driver(), which is necessary, because the
@@ -656,7 +654,7 @@ out the following operations:
     __pm_runtime_disable() with 'false' as the second argument for every device
     right before executing the subsystem-level .suspend_late() callback for it.
 
-  * During system resume it calls pm_runtime_enable() and pm_runtime_put_sync()
+  * During system resume it calls pm_runtime_enable() and pm_runtime_put()
     for every device right after executing the subsystem-level .resume_early()
     callback and right after executing the subsystem-level .resume() callback
     for it, respectively.
index 1039b68..93cb979 100644 (file)
@@ -39,7 +39,7 @@ 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. Managed
 variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist.
 
-After being requested a PWM has to be configured using:
+After being requested, a PWM has to be configured using:
 
 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
 
@@ -94,7 +94,7 @@ 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
+number of PWM devices provided by the chip and the chip-specific
 implementation of the supported PWM operations to the framework.
 
 Locking
index 414235c..45c82fd 100644 (file)
@@ -22,3 +22,5 @@ keys.txt
        - description of the kernel key retention service.
 tomoyo.txt
        - documentation on the TOMOYO Linux Security Module.
+IMA-templates.txt
+       - documentation on the template management mechanism for IMA.
diff --git a/Documentation/security/IMA-templates.txt b/Documentation/security/IMA-templates.txt
new file mode 100644 (file)
index 0000000..a777e5f
--- /dev/null
@@ -0,0 +1,87 @@
+                       IMA Template Management Mechanism
+
+
+==== INTRODUCTION ====
+
+The original 'ima' template is fixed length, containing the filedata hash
+and pathname. The filedata hash is limited to 20 bytes (md5/sha1).
+The pathname is a null terminated string, limited to 255 characters.
+To overcome these limitations and to add additional file metadata, it is
+necessary to extend the current version of IMA by defining additional
+templates. For example, information that could be possibly reported are
+the inode UID/GID or the LSM labels either of the inode and of the process
+that is accessing it.
+
+However, the main problem to introduce this feature is that, each time
+a new template is defined, the functions that generate and display
+the measurements list would include the code for handling a new format
+and, thus, would significantly grow over the time.
+
+The proposed solution solves this problem by separating the template
+management from the remaining IMA code. The core of this solution is the
+definition of two new data structures: a template descriptor, to determine
+which information should be included in the measurement list; a template
+field, to generate and display data of a given type.
+
+Managing templates with these structures is very simple. To support
+a new data type, developers define the field identifier and implement
+two functions, init() and show(), respectively to generate and display
+measurement entries. Defining a new template descriptor requires
+specifying the template format, a string of field identifiers separated
+by the '|' character. While in the current implementation it is possible
+to define new template descriptors only by adding their definition in the
+template specific code (ima_template.c), in a future version it will be
+possible to register a new template on a running kernel by supplying to IMA
+the desired format string. In this version, IMA initializes at boot time
+all defined template descriptors by translating the format into an array
+of template fields structures taken from the set of the supported ones.
+
+After the initialization step, IMA will call ima_alloc_init_template()
+(new function defined within the patches for the new template management
+mechanism) to generate a new measurement entry by using the template
+descriptor chosen through the kernel configuration or through the newly
+introduced 'ima_template=' kernel command line parameter. It is during this
+phase that the advantages of the new architecture are clearly shown:
+the latter function will not contain specific code to handle a given template
+but, instead, it simply calls the init() method of the template fields
+associated to the chosen template descriptor and store the result (pointer
+to allocated data and data length) in the measurement entry structure.
+
+The same mechanism is employed to display measurements entries.
+The functions ima[_ascii]_measurements_show() retrieve, for each entry,
+the template descriptor used to produce that entry and call the show()
+method for each item of the array of template fields structures.
+
+
+
+==== SUPPORTED TEMPLATE FIELDS AND DESCRIPTORS ====
+
+In the following, there is the list of supported template fields
+('<identifier>': description), that can be used to define new template
+descriptors by adding their identifier to the format string
+(support for more data types will be added later):
+
+ - 'd': the digest of the event (i.e. the digest of a measured file),
+        calculated with the SHA1 or MD5 hash algorithm;
+ - 'n': the name of the event (i.e. the file name), with size up to 255 bytes;
+ - 'd-ng': the digest of the event, calculated with an arbitrary hash
+           algorithm (field format: [<hash algo>:]digest, where the digest
+           prefix is shown only if the hash algorithm is not SHA1 or MD5);
+ - 'n-ng': the name of the event, without size limitations.
+
+
+Below, there is the list of defined template descriptors:
+ - "ima": its format is 'd|n';
+ - "ima-ng" (default): its format is 'd-ng|n-ng'.
+
+
+
+==== USE ====
+
+To specify the template descriptor to be used to generate measurement entries,
+currently the following methods are supported:
+
+ - select a template descriptor among those supported in the kernel
+   configuration ('ima-ng' is the default choice);
+ - specify a template descriptor name from the kernel command line through
+   the 'ima_template=' parameter.
index 7b4145d..a4c33f1 100644 (file)
@@ -865,15 +865,14 @@ encountered:
      calling processes has a searchable link to the key from one of its
      keyrings. There are three functions for dealing with these:
 
-       key_ref_t make_key_ref(const struct key *key,
-                              unsigned long possession);
+       key_ref_t make_key_ref(const struct key *key, bool possession);
 
        struct key *key_ref_to_ptr(const key_ref_t key_ref);
 
-       unsigned long is_key_possessed(const key_ref_t key_ref);
+       bool is_key_possessed(const key_ref_t key_ref);
 
      The first function constructs a key reference from a key pointer and
-     possession information (which must be 0 or 1 and not any other value).
+     possession information (which must be true or false).
 
      The second function retrieves the key pointer from a reference and the
      third retrieves the possession flag.
@@ -961,14 +960,17 @@ payload contents" for more information.
     the argument will not be parsed.
 
 
-(*) Extra references can be made to a key by calling the following function:
+(*) Extra references can be made to a key by calling one of the following
+    functions:
 
+       struct key *__key_get(struct key *key);
        struct key *key_get(struct key *key);
 
-    These need to be disposed of by calling key_put() when they've been
-    finished with. The key pointer passed in will be returned. If the pointer
-    is NULL or CONFIG_KEYS is not set then the key will not be dereferenced and
-    no increment will take place.
+    Keys so references will need to be disposed of by calling key_put() when
+    they've been finished with.  The key pointer passed in will be returned.
+
+    In the case of key_get(), if the pointer is NULL or CONFIG_KEYS is not set
+    then the key will not be dereferenced and no increment will take place.
 
 
 (*) A key's serial number can be obtained by calling:
index 54d29c1..230ce71 100755 (executable)
@@ -440,15 +440,15 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name):
        buf += "        /*\n"
        buf += "         * Setup default attribute lists for various fabric->tf_cit_tmpl\n"
        buf += "         */\n"
-       buf += "        TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = " + fabric_mod_name + "_wwn_attrs;\n"
-       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = NULL;\n"
-       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;\n"
-       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;\n"
-       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;\n"
-       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;\n"
-       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;\n"
-       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;\n"
-       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;\n"
+       buf += "        fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = " + fabric_mod_name + "_wwn_attrs;\n"
+       buf += "        fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = NULL;\n"
+       buf += "        fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL;\n"
+       buf += "        fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;\n"
+       buf += "        fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;\n"
+       buf += "        fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL;\n"
+       buf += "        fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;\n"
+       buf += "        fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL;\n"
+       buf += "        fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;\n"
        buf += "        /*\n"
        buf += "         * Register the fabric for use within TCM\n"
        buf += "         */\n"
index a9248da..ef2ccbf 100644 (file)
@@ -8,5 +8,9 @@ hpet_example.c
        - sample hpet timer test program
 hrtimers.txt
        - subsystem for high-resolution kernel timers
+NO_HZ.txt
+       - Summary of the different methods for the scheduler clock-interrupts management.
+timers-howto.txt
+       - how to insert delays in the kernel the right (tm) way.
 timer_stats.txt
        - timer usage statistics
index 8ec2a67..4cf53e4 100644 (file)
@@ -26,7 +26,7 @@ Linux provides a number of functions for gadgets to use.
 Creating a gadget means deciding what configurations there will be
 and which functions each configuration will provide.
 
-Configfs (please see Documentation/filesystems/configfs/*) lends itslef nicely
+Configfs (please see Documentation/filesystems/configfs/*) lends itself nicely
 for the purpose of telling the kernel about the above mentioned decision.
 This document is about how to do it.
 
@@ -99,7 +99,7 @@ directories must be created:
 $ mkdir configs/<name>.<number>
 
 where <name> can be any string which is legal in a filesystem and the
-<numebr> is the configuration's number, e.g.:
+<number> is the configuration's number, e.g.:
 
 $ mkdir configs/c.1
 
@@ -327,7 +327,7 @@ from the buffer to the cs), but it is up to the implementer of the
 two functions to decide what they actually do.
 
 typedef struct configured_structure cs;
-typedef struc specific_attribute sa;
+typedef struct specific_attribute sa;
 
                                        sa
                        +----------------------------------+
diff --git a/Documentation/virtual/kvm/00-INDEX b/Documentation/virtual/kvm/00-INDEX
new file mode 100644 (file)
index 0000000..641ec92
--- /dev/null
@@ -0,0 +1,24 @@
+00-INDEX
+       - this file.
+api.txt
+       - KVM userspace API.
+cpuid.txt
+       - KVM-specific cpuid leaves (x86).
+devices/
+       - KVM_CAP_DEVICE_CTRL userspace API.
+hypercalls.txt
+       - KVM hypercalls.
+locking.txt
+       - notes on KVM locks.
+mmu.txt
+       - the x86 kvm shadow mmu.
+msr.txt
+       - KVM-specific MSRs (x86).
+nested-vmx.txt
+       - notes on nested virtualization for Intel x86 processors.
+ppc-pv.txt
+       - the paravirtualization interface on PowerPC.
+review-checklist.txt
+       - review checklist for KVM patches.
+timekeeping.txt
+       - timekeeping virtualization for x86-based architectures.
index 858aecf..a30035d 100644 (file)
@@ -1122,9 +1122,9 @@ struct kvm_cpuid2 {
        struct kvm_cpuid_entry2 entries[0];
 };
 
-#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX 1
-#define KVM_CPUID_FLAG_STATEFUL_FUNC    2
-#define KVM_CPUID_FLAG_STATE_READ_NEXT  4
+#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX                BIT(0)
+#define KVM_CPUID_FLAG_STATEFUL_FUNC           BIT(1)
+#define KVM_CPUID_FLAG_STATE_READ_NEXT         BIT(2)
 
 struct kvm_cpuid_entry2 {
        __u32 function;
@@ -1810,6 +1810,50 @@ registers, find a list below:
   PPC   | KVM_REG_PPC_TLB3PS   | 32
   PPC   | KVM_REG_PPC_EPTCFG   | 32
   PPC   | KVM_REG_PPC_ICP_STATE | 64
+  PPC   | KVM_REG_PPC_TB_OFFSET        | 64
+  PPC   | KVM_REG_PPC_SPMC1    | 32
+  PPC   | KVM_REG_PPC_SPMC2    | 32
+  PPC   | KVM_REG_PPC_IAMR     | 64
+  PPC   | KVM_REG_PPC_TFHAR    | 64
+  PPC   | KVM_REG_PPC_TFIAR    | 64
+  PPC   | KVM_REG_PPC_TEXASR   | 64
+  PPC   | KVM_REG_PPC_FSCR     | 64
+  PPC   | KVM_REG_PPC_PSPB     | 32
+  PPC   | KVM_REG_PPC_EBBHR    | 64
+  PPC   | KVM_REG_PPC_EBBRR    | 64
+  PPC   | KVM_REG_PPC_BESCR    | 64
+  PPC   | KVM_REG_PPC_TAR      | 64
+  PPC   | KVM_REG_PPC_DPDES    | 64
+  PPC   | KVM_REG_PPC_DAWR     | 64
+  PPC   | KVM_REG_PPC_DAWRX    | 64
+  PPC   | KVM_REG_PPC_CIABR    | 64
+  PPC   | KVM_REG_PPC_IC       | 64
+  PPC   | KVM_REG_PPC_VTB      | 64
+  PPC   | KVM_REG_PPC_CSIGR    | 64
+  PPC   | KVM_REG_PPC_TACR     | 64
+  PPC   | KVM_REG_PPC_TCSCR    | 64
+  PPC   | KVM_REG_PPC_PID      | 64
+  PPC   | KVM_REG_PPC_ACOP     | 64
+  PPC   | KVM_REG_PPC_VRSAVE   | 32
+  PPC   | KVM_REG_PPC_LPCR     | 64
+  PPC   | KVM_REG_PPC_PPR      | 64
+  PPC   | KVM_REG_PPC_ARCH_COMPAT 32
+  PPC   | KVM_REG_PPC_TM_GPR0  | 64
+          ...
+  PPC   | KVM_REG_PPC_TM_GPR31 | 64
+  PPC   | KVM_REG_PPC_TM_VSR0  | 128
+          ...
+  PPC   | KVM_REG_PPC_TM_VSR63 | 128
+  PPC   | KVM_REG_PPC_TM_CR    | 64
+  PPC   | KVM_REG_PPC_TM_LR    | 64
+  PPC   | KVM_REG_PPC_TM_CTR   | 64
+  PPC   | KVM_REG_PPC_TM_FPSCR | 64
+  PPC   | KVM_REG_PPC_TM_AMR   | 64
+  PPC   | KVM_REG_PPC_TM_PPR   | 64
+  PPC   | KVM_REG_PPC_TM_VRSAVE        | 64
+  PPC   | KVM_REG_PPC_TM_VSCR  | 32
+  PPC   | KVM_REG_PPC_TM_DSCR  | 64
+  PPC   | KVM_REG_PPC_TM_TAR   | 64
 
 ARM registers are mapped using the lower 32 bits.  The upper 16 of that
 is the register group type, or coprocessor number:
@@ -2304,7 +2348,31 @@ Possible features:
          Depends on KVM_CAP_ARM_EL1_32BIT (arm64 only).
 
 
-4.83 KVM_GET_REG_LIST
+4.83 KVM_ARM_PREFERRED_TARGET
+
+Capability: basic
+Architectures: arm, arm64
+Type: vm ioctl
+Parameters: struct struct kvm_vcpu_init (out)
+Returns: 0 on success; -1 on error
+Errors:
+  ENODEV:    no preferred target available for the host
+
+This queries KVM for preferred CPU target type which can be emulated
+by KVM on underlying host.
+
+The ioctl returns struct kvm_vcpu_init instance containing information
+about preferred CPU target type and recommended features for it.  The
+kvm_vcpu_init->features bitmap returned will have feature bits set if
+the preferred target recommends setting these features, but this is
+not mandatory.
+
+The information returned by this ioctl can be used to prepare an instance
+of struct kvm_vcpu_init for KVM_ARM_VCPU_INIT ioctl which will result in
+in VCPU matching underlying host.
+
+
+4.84 KVM_GET_REG_LIST
 
 Capability: basic
 Architectures: arm, arm64
@@ -2323,8 +2391,7 @@ struct kvm_reg_list {
 This ioctl returns the guest registers that are supported for the
 KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
 
-
-4.84 KVM_ARM_SET_DEVICE_ADDR
+4.85 KVM_ARM_SET_DEVICE_ADDR
 
 Capability: KVM_CAP_ARM_SET_DEVICE_ADDR
 Architectures: arm, arm64
@@ -2362,7 +2429,7 @@ must be called after calling KVM_CREATE_IRQCHIP, but before calling
 KVM_RUN on any of the VCPUs.  Calling this ioctl twice for any of the
 base addresses will return -EEXIST.
 
-4.85 KVM_PPC_RTAS_DEFINE_TOKEN
+4.86 KVM_PPC_RTAS_DEFINE_TOKEN
 
 Capability: KVM_CAP_PPC_RTAS
 Architectures: ppc
@@ -2661,6 +2728,77 @@ and usually define the validity of a groups of registers. (e.g. one bit
 };
 
 
+4.81 KVM_GET_EMULATED_CPUID
+
+Capability: KVM_CAP_EXT_EMUL_CPUID
+Architectures: x86
+Type: system ioctl
+Parameters: struct kvm_cpuid2 (in/out)
+Returns: 0 on success, -1 on error
+
+struct kvm_cpuid2 {
+       __u32 nent;
+       __u32 flags;
+       struct kvm_cpuid_entry2 entries[0];
+};
+
+The member 'flags' is used for passing flags from userspace.
+
+#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX                BIT(0)
+#define KVM_CPUID_FLAG_STATEFUL_FUNC           BIT(1)
+#define KVM_CPUID_FLAG_STATE_READ_NEXT         BIT(2)
+
+struct kvm_cpuid_entry2 {
+       __u32 function;
+       __u32 index;
+       __u32 flags;
+       __u32 eax;
+       __u32 ebx;
+       __u32 ecx;
+       __u32 edx;
+       __u32 padding[3];
+};
+
+This ioctl returns x86 cpuid features which are emulated by
+kvm.Userspace can use the information returned by this ioctl to query
+which features are emulated by kvm instead of being present natively.
+
+Userspace invokes KVM_GET_EMULATED_CPUID by passing a kvm_cpuid2
+structure with the 'nent' field indicating the number of entries in
+the variable-size array 'entries'. If the number of entries is too low
+to describe the cpu capabilities, an error (E2BIG) is returned. If the
+number is too high, the 'nent' field is adjusted and an error (ENOMEM)
+is returned. If the number is just right, the 'nent' field is adjusted
+to the number of valid entries in the 'entries' array, which is then
+filled.
+
+The entries returned are the set CPUID bits of the respective features
+which kvm emulates, as returned by the CPUID instruction, with unknown
+or unsupported feature bits cleared.
+
+Features like x2apic, for example, may not be present in the host cpu
+but are exposed by kvm in KVM_GET_SUPPORTED_CPUID because they can be
+emulated efficiently and thus not included here.
+
+The fields in each entry are defined as follows:
+
+  function: the eax value used to obtain the entry
+  index: the ecx value used to obtain the entry (for entries that are
+         affected by ecx)
+  flags: an OR of zero or more of the following:
+        KVM_CPUID_FLAG_SIGNIFCANT_INDEX:
+           if the index field is valid
+        KVM_CPUID_FLAG_STATEFUL_FUNC:
+           if cpuid for this function returns different values for successive
+           invocations; there will be several entries with the same function,
+           all with this flag set
+        KVM_CPUID_FLAG_STATE_READ_NEXT:
+           for KVM_CPUID_FLAG_STATEFUL_FUNC entries, set if this entry is
+           the first entry to be read by a cpu
+   eax, ebx, ecx, edx: the values returned by the cpuid instruction for
+         this function/index combination
+
+
 6. Capabilities that can be enabled
 -----------------------------------
 
index 22ff659..3c65feb 100644 (file)
@@ -43,6 +43,13 @@ KVM_FEATURE_CLOCKSOURCE2           ||     3 || kvmclock available at msrs
 KVM_FEATURE_ASYNC_PF               ||     4 || async pf can be enabled by
                                    ||       || writing to msr 0x4b564d02
 ------------------------------------------------------------------------------
+KVM_FEATURE_STEAL_TIME             ||     5 || steal time can be enabled by
+                                   ||       || writing to msr 0x4b564d03.
+------------------------------------------------------------------------------
+KVM_FEATURE_PV_EOI                 ||     6 || paravirtualized end of interrupt
+                                   ||       || handler can be enabled by writing
+                                   ||       || to msr 0x4b564d04.
+------------------------------------------------------------------------------
 KVM_FEATURE_PV_UNHALT              ||     7 || guest checks this feature bit
                                    ||       || before enabling paravirtualized
                                    ||       || spinlock support.
diff --git a/Documentation/virtual/kvm/devices/vfio.txt b/Documentation/virtual/kvm/devices/vfio.txt
new file mode 100644 (file)
index 0000000..ef51740
--- /dev/null
@@ -0,0 +1,22 @@
+VFIO virtual device
+===================
+
+Device types supported:
+  KVM_DEV_TYPE_VFIO
+
+Only one VFIO instance may be created per VM.  The created device
+tracks VFIO groups in use by the VM and features of those groups
+important to the correctness and acceleration of the VM.  As groups
+are enabled and disabled for use by the VM, KVM should be updated
+about their presence.  When registered with KVM, a reference to the
+VFIO-group is held by KVM.
+
+Groups:
+  KVM_DEV_VFIO_GROUP
+
+KVM_DEV_VFIO_GROUP attributes:
+  KVM_DEV_VFIO_GROUP_ADD: Add a VFIO group to VFIO-KVM device tracking
+  KVM_DEV_VFIO_GROUP_DEL: Remove a VFIO group from VFIO-KVM device tracking
+
+For each, kvm_device_attr.addr points to an int32_t file descriptor
+for the VFIO group.
index 41b7ac9..f886941 100644 (file)
@@ -132,10 +132,14 @@ See the comments in spte_has_volatile_bits() and mmu_spte_update().
 ------------
 
 Name:          kvm_lock
-Type:          raw_spinlock
+Type:          spinlock_t
 Arch:          any
 Protects:      - vm_list
-               - hardware virtualization enable/disable
+
+Name:          kvm_count_lock
+Type:          raw_spinlock_t
+Arch:          any
+Protects:      - hardware virtualization enable/disable
 Comment:       'raw' because hardware enabling/disabling must be atomic /wrt
                migration.
 
@@ -151,3 +155,14 @@ Type:              spinlock_t
 Arch:          any
 Protects:      -shadow page/shadow tlb entry
 Comment:       it is a spinlock since it is used in mmu notifier.
+
+Name:          kvm->srcu
+Type:          srcu lock
+Arch:          any
+Protects:      - kvm->memslots
+               - kvm->buses
+Comment:       The srcu read lock must be held while accessing memslots (e.g.
+               when using gfn_to_* functions) and while accessing in-kernel
+               MMIO/PIO address->device structure mapping (kvm->buses).
+               The srcu index can be stored in kvm_vcpu->srcu_idx per vcpu
+               if it is needed by multiple functions.
index 5481c8b..a39d066 100644 (file)
@@ -4,10 +4,12 @@ active_mm.txt
        - An explanation from Linus about tsk->active_mm vs tsk->mm.
 balance
        - various information on memory balancing.
-hugepage-mmap.c
-       - Example app using huge page memory with the mmap system call.
-hugepage-shm.c
-       - Example app using huge page memory with Sys V shared memory system calls.
+cleancache.txt
+       - Intro to cleancache and page-granularity victim cache.
+frontswap.txt
+       - Outline frontswap, part of the transcendent memory frontend.
+highmem.txt
+       - Outline of highmem and common issues.
 hugetlbpage.txt
        - a brief summary of hugetlbpage support in the Linux kernel.
 hwpoison.txt
@@ -16,21 +18,23 @@ ksm.txt
        - how to use the Kernel Samepage Merging feature.
 locking
        - info on how locking and synchronization is done in the Linux vm code.
-map_hugetlb.c
-       - an example program that uses the MAP_HUGETLB mmap flag.
 numa
        - information about NUMA specific code in the Linux vm.
 numa_memory_policy.txt
        - documentation of concepts and APIs of the 2.6 memory policy support.
 overcommit-accounting
        - description of the Linux kernels overcommit handling modes.
-page-types.c
-       - Tool for querying page flags
 page_migration
        - description of page migration in NUMA systems.
 pagemap.txt
        - pagemap, from the userspace perspective
 slub.txt
        - a short users guide for SLUB.
+soft-dirty.txt
+       - short explanation for soft-dirty PTEs
+transhuge.txt
+       - Transparent Hugepage Support, alternative way of using hugepages.
 unevictable-lru.txt
        - Unevictable LRU infrastructure
+zswap.txt
+       - Intro to compressed cache for swap pages
diff --git a/Documentation/vm/split_page_table_lock b/Documentation/vm/split_page_table_lock
new file mode 100644 (file)
index 0000000..6dea4fd
--- /dev/null
@@ -0,0 +1,94 @@
+Split page table lock
+=====================
+
+Originally, mm->page_table_lock spinlock protected all page tables of the
+mm_struct. But this approach leads to poor page fault scalability of
+multi-threaded applications due high contention on the lock. To improve
+scalability, split page table lock was introduced.
+
+With split page table lock we have separate per-table lock to serialize
+access to the table. At the moment we use split lock for PTE and PMD
+tables. Access to higher level tables protected by mm->page_table_lock.
+
+There are helpers to lock/unlock a table and other accessor functions:
+ - pte_offset_map_lock()
+       maps pte and takes PTE table lock, returns pointer to the taken
+       lock;
+ - pte_unmap_unlock()
+       unlocks and unmaps PTE table;
+ - pte_alloc_map_lock()
+       allocates PTE table if needed and take the lock, returns pointer
+       to taken lock or NULL if allocation failed;
+ - pte_lockptr()
+       returns pointer to PTE table lock;
+ - pmd_lock()
+       takes PMD table lock, returns pointer to taken lock;
+ - pmd_lockptr()
+       returns pointer to PMD table lock;
+
+Split page table lock for PTE tables is enabled compile-time if
+CONFIG_SPLIT_PTLOCK_CPUS (usually 4) is less or equal to NR_CPUS.
+If split lock is disabled, all tables guaded by mm->page_table_lock.
+
+Split page table lock for PMD tables is enabled, if it's enabled for PTE
+tables and the architecture supports it (see below).
+
+Hugetlb and split page table lock
+---------------------------------
+
+Hugetlb can support several page sizes. We use split lock only for PMD
+level, but not for PUD.
+
+Hugetlb-specific helpers:
+ - huge_pte_lock()
+       takes pmd split lock for PMD_SIZE page, mm->page_table_lock
+       otherwise;
+ - huge_pte_lockptr()
+       returns pointer to table lock;
+
+Support of split page table lock by an architecture
+---------------------------------------------------
+
+There's no need in special enabling of PTE split page table lock:
+everything required is done by pgtable_page_ctor() and pgtable_page_dtor(),
+which must be called on PTE table allocation / freeing.
+
+Make sure the architecture doesn't use slab allocator for page table
+allocation: slab uses page->slab_cache and page->first_page for its pages.
+These fields share storage with page->ptl.
+
+PMD split lock only makes sense if you have more than two page table
+levels.
+
+PMD split lock enabling requires pgtable_pmd_page_ctor() call on PMD table
+allocation and pgtable_pmd_page_dtor() on freeing.
+
+Allocation usually happens in pmd_alloc_one(), freeing in pmd_free() and
+pmd_free_tlb(), but make sure you cover all PMD table allocation / freeing
+paths: i.e X86_PAE preallocate few PMDs on pgd_alloc().
+
+With everything in place you can set CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK.
+
+NOTE: pgtable_page_ctor() and pgtable_pmd_page_ctor() can fail -- it must
+be handled properly.
+
+page->ptl
+---------
+
+page->ptl is used to access split page table lock, where 'page' is struct
+page of page containing the table. It shares storage with page->private
+(and few other fields in union).
+
+To avoid increasing size of struct page and have best performance, we use a
+trick:
+ - if spinlock_t fits into long, we use page->ptr as spinlock, so we
+   can avoid indirect access and save a cache line.
+ - if size of spinlock_t is bigger then size of long, we use page->ptl as
+   pointer to spinlock_t and allocate it dynamically. This allows to use
+   split lock with enabled DEBUG_SPINLOCK or DEBUG_LOCK_ALLOC, but costs
+   one more cache line for indirect access;
+
+The spinlock_t allocated in pgtable_page_ctor() for PTE table and in
+pgtable_pmd_page_ctor() for PMD table.
+
+Please, never access page->ptl directly -- use appropriate helper.
index f3ef1d1..8285ed4 100644 (file)
@@ -1070,7 +1070,6 @@ S:        Maintained
 ARM/NOMADIK ARCHITECTURE
 M:     Alessandro Rubini <rubini@unipv.it>
 M:     Linus Walleij <linus.walleij@linaro.org>
-M:     STEricsson <STEricsson_nomadik_linux@list.st.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     arch/arm/mach-nomadik/
@@ -1426,7 +1425,7 @@ M:        Wolfram Sang <wsa@the-dreams.de>
 L:     linux-i2c@vger.kernel.org
 S:     Maintained
 F:     drivers/misc/eeprom/at24.c
-F:     include/linux/i2c/at24.h
+F:     include/linux/platform_data/at24.h
 
 ATA OVER ETHERNET (AOE) DRIVER
 M:     "Ed L. Cashin" <ecashin@coraid.com>
@@ -2469,7 +2468,7 @@ S:        Maintained
 F:     drivers/media/dvb-frontends/cxd2820r*
 
 CXGB3 ETHERNET DRIVER (CXGB3)
-M:     Divy Le Ray <divy@chelsio.com>
+M:     Santosh Raspatur <santosh@chelsio.com>
 L:     netdev@vger.kernel.org
 W:     http://www.chelsio.com
 S:     Supported
@@ -2849,7 +2848,9 @@ L:        dri-devel@lists.freedesktop.org
 L:     linux-tegra@vger.kernel.org
 T:     git git://anongit.freedesktop.org/tegra/linux.git
 S:     Supported
+F:     drivers/gpu/drm/tegra/
 F:     drivers/gpu/host1x/
+F:     include/linux/host1x.h
 F:     include/uapi/drm/tegra_drm.h
 F:     Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt
 
@@ -3062,6 +3063,14 @@ W:       bluesmoke.sourceforge.net
 S:     Maintained
 F:     drivers/edac/amd64_edac*
 
+EDAC-CALXEDA
+M:     Doug Thompson <dougthompson@xmission.com>
+M:     Robert Richter <rric@kernel.org>
+L:     linux-edac@vger.kernel.org
+W:     bluesmoke.sourceforge.net
+S:     Maintained
+F:     drivers/edac/highbank*
+
 EDAC-CAVIUM
 M:     Ralf Baechle <ralf@linux-mips.org>
 M:     David Daney <david.daney@cavium.com>
@@ -3143,6 +3152,13 @@ W:       bluesmoke.sourceforge.net
 S:     Maintained
 F:     drivers/edac/i82975x_edac.c
 
+EDAC-MPC85XX
+M:     Johannes Thumshirn <johannes.thumshirn@men.de>
+L:     linux-edac@vger.kernel.org
+W:     bluesmoke.sourceforge.net
+S:     Maintained
+F:     drivers/edac/mpc85xx_edac.[ch]
+
 EDAC-PASEMI
 M:     Egor Martovetsky <egor@pasemi.com>
 L:     linux-edac@vger.kernel.org
@@ -4049,6 +4065,7 @@ F:        arch/x86/include/uapi/asm/hyperv.h
 F:     arch/x86/kernel/cpu/mshyperv.c
 F:     drivers/hid/hid-hyperv.c
 F:     drivers/hv/
+F:     drivers/input/serio/hyperv-keyboard.c
 F:     drivers/net/hyperv/
 F:     drivers/scsi/storvsc_drv.c
 F:     drivers/video/hyperv_fb.c
@@ -4806,9 +4823,10 @@ S:       Maintained
 F:     drivers/staging/ktap/
 
 KCONFIG
-M:     Michal Marek <mmarek@suse.cz>
+M:     "Yann E. MORIN" <yann.morin.1998@free.fr>
 L:     linux-kbuild@vger.kernel.org
-S:     Odd Fixes
+T:     git://gitorious.org/linux-kconfig/linux-kconfig
+S:     Maintained
 F:     Documentation/kbuild/kconfig-language.txt
 F:     scripts/kconfig/
 
@@ -4871,7 +4889,8 @@ KERNEL VIRTUAL MACHINE (KVM)
 M:     Gleb Natapov <gleb@redhat.com>
 M:     Paolo Bonzini <pbonzini@redhat.com>
 L:     kvm@vger.kernel.org
-W:     http://linux-kvm.org
+W:     http://www.linux-kvm.org
+T:     git git://git.kernel.org/pub/scm/virt/kvm/kvm.git
 S:     Supported
 F:     Documentation/*/kvm*.txt
 F:     Documentation/virtual/kvm/
@@ -5211,6 +5230,7 @@ M:        Jean Delvare <khali@linux-fr.org>
 L:     lm-sensors@lm-sensors.org
 S:     Maintained
 F:     Documentation/hwmon/lm90
+F:     Documentation/devicetree/bindings/hwmon/lm90.txt
 F:     drivers/hwmon/lm90.c
 
 LM95234 HARDWARE MONITOR DRIVER
@@ -6780,8 +6800,7 @@ PWM SUBSYSTEM
 M:     Thierry Reding <thierry.reding@gmail.com>
 L:     linux-pwm@vger.kernel.org
 S:     Maintained
-W:     http://gitorious.org/linux-pwm
-T:     git git://gitorious.org/linux-pwm/linux-pwm.git
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git
 F:     Documentation/pwm.txt
 F:     Documentation/devicetree/bindings/pwm/
 F:     include/linux/pwm.h
@@ -7497,9 +7516,10 @@ SELINUX SECURITY MODULE
 M:     Stephen Smalley <sds@tycho.nsa.gov>
 M:     James Morris <james.l.morris@oracle.com>
 M:     Eric Paris <eparis@parisplace.org>
+M:     Paul Moore <paul@paul-moore.com>
 L:     selinux@tycho.nsa.gov (subscribers-only, general discussion)
 W:     http://selinuxproject.org
-T:     git git://git.infradead.org/users/eparis/selinux.git
+T:     git git://git.infradead.org/users/pcmoore/selinux
 S:     Supported
 F:     include/linux/selinux*
 F:     security/selinux/
@@ -8646,6 +8666,7 @@ F:        drivers/media/usb/tm6000/
 TPM DEVICE DRIVER
 M:     Leonidas Da Silva Barbosa <leosilva@linux.vnet.ibm.com>
 M:     Ashley Lai <ashley@ashleylai.com>
+M:     Peter Huewe <peterhuewe@gmx.de>
 M:     Rajiv Andrade <mail@srajiv.net>
 W:     http://tpmdd.sourceforge.net
 M:     Marcel Selhorst <tpmdd@selhorst.net>
@@ -8942,8 +8963,8 @@ USB PEGASUS DRIVER
 M:     Petko Manolov <petkan@nucleusys.com>
 L:     linux-usb@vger.kernel.org
 L:     netdev@vger.kernel.org
-T:     git git://git.code.sf.net/p/pegasus2/git
-W:     http://pegasus2.sourceforge.net/
+T:     git git://github.com/petkan/pegasus.git
+W:     https://github.com/petkan/pegasus
 S:     Maintained
 F:     drivers/net/usb/pegasus.*
 
@@ -8964,8 +8985,8 @@ USB RTL8150 DRIVER
 M:     Petko Manolov <petkan@nucleusys.com>
 L:     linux-usb@vger.kernel.org
 L:     netdev@vger.kernel.org
-T:     git git://git.code.sf.net/p/pegasus2/git
-W:     http://pegasus2.sourceforge.net/
+T:     git git://github.com/petkan/rtl8150.git
+W:     https://github.com/petkan/rtl8150
 S:     Maintained
 F:     drivers/net/usb/rtl8150.c
 
@@ -9504,8 +9525,8 @@ F:        drivers/xen/*swiotlb*
 
 XFS FILESYSTEM
 P:     Silicon Graphics Inc
+M:     Dave Chinner <dchinner@fromorbit.com>
 M:     Ben Myers <bpm@sgi.com>
-M:     Alex Elder <elder@kernel.org>
 M:     xfs@oss.sgi.com
 L:     xfs@oss.sgi.com
 W:     http://oss.sgi.com/projects/xfs
index 606a66c..920ad07 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,9 @@ LC_COLLATE=C
 LC_NUMERIC=C
 export LC_COLLATE LC_NUMERIC
 
+# Avoid interference with shell env settings
+unexport GREP_OPTIONS
+
 # We are using a recursive build, so we need to do a little thinking
 # to get the ordering right.
 #
@@ -659,6 +662,12 @@ KBUILD_CFLAGS      += $(call cc-option,-fno-strict-overflow)
 # conserve stack if available
 KBUILD_CFLAGS   += $(call cc-option,-fconserve-stack)
 
+# disallow errors like 'EXPORT_GPL(foo);' with missing header
+KBUILD_CFLAGS   += $(call cc-option,-Werror=implicit-int)
+
+# require functions to have arguments in prototypes, not empty 'int foo()'
+KBUILD_CFLAGS   += $(call cc-option,-Werror=strict-prototypes)
+
 # use the deterministic mode of AR if available
 KBUILD_ARFLAGS := $(call ar-option,D)
 
index ded747c..f1cf895 100644 (file)
@@ -207,9 +207,6 @@ config HAVE_DMA_ATTRS
 config HAVE_DMA_CONTIGUOUS
        bool
 
-config USE_GENERIC_SMP_HELPERS
-       bool
-
 config GENERIC_SMP_IDLE_THREAD
        bool
 
index 35a300d..d39dc9b 100644 (file)
@@ -1,6 +1,7 @@
 config ALPHA
        bool
        default y
+       select ARCH_MIGHT_HAVE_PC_PARPORT
        select HAVE_AOUT
        select HAVE_IDE
        select HAVE_OPROFILE
@@ -15,8 +16,8 @@ config ALPHA
        select ARCH_WANT_IPC_PARSE_VERSION
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
+       select GENERIC_CLOCKEVENTS
        select GENERIC_SMP_IDLE_THREAD
-       select GENERIC_CMOS_UPDATE
        select GENERIC_STRNCPY_FROM_USER
        select GENERIC_STRNLEN_USER
        select HAVE_MOD_ARCH_SPECIFIC
@@ -487,6 +488,20 @@ config VGA_HOSE
          which always have multiple hoses, and whose consoles support it.
 
 
+config ALPHA_QEMU
+       bool "Run under QEMU emulation"
+       depends on !ALPHA_GENERIC
+       ---help---
+         Assume the presence of special features supported by QEMU PALcode
+         that reduce the overhead of system emulation.
+
+         Generic kernels will auto-detect QEMU.  But when building a
+         system-specific kernel, the assumption is that we want to
+         elimiate as many runtime tests as possible.
+
+         If unsure, say N.
+
+
 config ALPHA_SRM
        bool "Use SRM as bootloader" if ALPHA_CABRIOLET || ALPHA_AVANTI_CH || ALPHA_EB64P || ALPHA_PC164 || ALPHA_TAKARA || ALPHA_EB164 || ALPHA_ALCOR || ALPHA_MIATA || ALPHA_LX164 || ALPHA_SX164 || ALPHA_NAUTILUS || ALPHA_NONAME
        depends on TTY
@@ -522,7 +537,6 @@ config ARCH_MAY_HAVE_PC_FDC
 config SMP
        bool "Symmetric multi-processing support"
        depends on ALPHA_SABLE || ALPHA_LYNX || ALPHA_RAWHIDE || ALPHA_DP264 || ALPHA_WILDFIRE || ALPHA_TITAN || ALPHA_GENERIC || ALPHA_SHARK || ALPHA_MARVEL
-       select USE_GENERIC_SMP_HELPERS
        ---help---
          This enables support for systems with more than one CPU. If you have
          a system with only one CPU, like most personal computers, say N. If
@@ -572,6 +586,30 @@ config NUMA
          Access).  This option is for configuring high-end multiprocessor
          server machines.  If in doubt, say N.
 
+config ALPHA_WTINT
+       bool "Use WTINT" if ALPHA_SRM || ALPHA_GENERIC
+       default y if ALPHA_QEMU
+       default n if ALPHA_EV5 || ALPHA_EV56 || (ALPHA_EV4 && !ALPHA_LCA)
+       default n if !ALPHA_SRM && !ALPHA_GENERIC
+       default y if SMP
+       ---help---
+         The Wait for Interrupt (WTINT) PALcall attempts to place the CPU
+         to sleep until the next interrupt.  This may reduce the power
+         consumed, and the heat produced by the computer.  However, it has
+         the side effect of making the cycle counter unreliable as a timing
+         device across the sleep.
+
+         For emulation under QEMU, definitely say Y here, as we have other
+         mechanisms for measuring time than the cycle counter.
+
+         For EV4 (but not LCA), EV5 and EV56 systems, or for systems running
+         MILO, sleep mode is not supported so you might as well say N here.
+
+         For SMP systems we cannot use the cycle counter for timing anyway,
+         so you might as well say Y here.
+
+         If unsure, say N.
+
 config NODES_SHIFT
        int
        default "7"
@@ -613,9 +651,41 @@ config VERBOSE_MCHECK_ON
 
          Take the default (1) unless you want more control or more info.
 
+choice
+       prompt "Timer interrupt frequency (HZ)?"
+       default HZ_128 if ALPHA_QEMU
+       default HZ_1200 if ALPHA_RAWHIDE
+       default HZ_1024
+       ---help---
+         The frequency at which timer interrupts occur.  A high frequency
+         minimizes latency, whereas a low frequency minimizes overhead of
+         process accounting.  The later effect is especially significant
+         when being run under QEMU.
+
+         Note that some Alpha hardware cannot change the interrupt frequency
+         of the timer.  If unsure, say 1024 (or 1200 for Rawhide).
+
+       config HZ_32
+               bool "32 Hz"
+       config HZ_64
+               bool "64 Hz"
+       config HZ_128
+               bool "128 Hz"
+       config HZ_256
+               bool "256 Hz"
+       config HZ_1024
+               bool "1024 Hz"
+       config HZ_1200
+               bool "1200 Hz"
+endchoice
+
 config HZ
-       int
-       default 1200 if ALPHA_RAWHIDE
+       int 
+       default 32 if HZ_32
+       default 64 if HZ_64
+       default 128 if HZ_128
+       default 256 if HZ_256
+       default 1200 if HZ_1200
        default 1024
 
 source "drivers/pci/Kconfig"
index 72dbf23..75cb364 100644 (file)
@@ -33,6 +33,7 @@ struct alpha_machine_vector
 
        int nr_irqs;
        int rtc_port;
+       int rtc_boot_cpu_only;
        unsigned int max_asn;
        unsigned long max_isa_dma_address;
        unsigned long irq_probe_mask;
@@ -95,9 +96,6 @@ struct alpha_machine_vector
 
        struct _alpha_agp_info *(*agp_info)(void);
 
-       unsigned int (*rtc_get_time)(struct rtc_time *);
-       int (*rtc_set_time)(struct rtc_time *);
-
        const char *vector_name;
 
        /* NUMA information */
@@ -126,13 +124,19 @@ extern struct alpha_machine_vector alpha_mv;
 
 #ifdef CONFIG_ALPHA_GENERIC
 extern int alpha_using_srm;
+extern int alpha_using_qemu;
 #else
-#ifdef CONFIG_ALPHA_SRM
-#define alpha_using_srm 1
-#else
-#define alpha_using_srm 0
-#endif
+# ifdef CONFIG_ALPHA_SRM
+#  define alpha_using_srm 1
+# else
+#  define alpha_using_srm 0
+# endif
+# ifdef CONFIG_ALPHA_QEMU
+#  define alpha_using_qemu 1
+# else
+#  define alpha_using_qemu 0
+# endif
 #endif /* GENERIC */
 
-#endif
+#endif /* __KERNEL__ */
 #endif /* __ALPHA_MACHVEC_H */
index 6fcd2b5..5422a47 100644 (file)
@@ -89,6 +89,7 @@ __CALL_PAL_W1(wrmces, unsigned long);
 __CALL_PAL_RW2(wrperfmon, unsigned long, unsigned long, unsigned long);
 __CALL_PAL_W1(wrusp, unsigned long);
 __CALL_PAL_W1(wrvptptr, unsigned long);
+__CALL_PAL_RW1(wtint, unsigned long, unsigned long);
 
 /*
  * TB routines..
@@ -111,5 +112,75 @@ __CALL_PAL_W1(wrvptptr, unsigned long);
 #define tbiap()                __tbi(-1, /* no second argument */)
 #define tbia()         __tbi(-2, /* no second argument */)
 
+/*
+ * QEMU Cserv routines..
+ */
+
+static inline unsigned long
+qemu_get_walltime(void)
+{
+       register unsigned long v0 __asm__("$0");
+       register unsigned long a0 __asm__("$16") = 3;
+
+       asm("call_pal %2 # cserve get_time"
+           : "=r"(v0), "+r"(a0)
+           : "i"(PAL_cserve)
+           : "$17", "$18", "$19", "$20", "$21");
+
+       return v0;
+}
+
+static inline unsigned long
+qemu_get_alarm(void)
+{
+       register unsigned long v0 __asm__("$0");
+       register unsigned long a0 __asm__("$16") = 4;
+
+       asm("call_pal %2 # cserve get_alarm"
+           : "=r"(v0), "+r"(a0)
+           : "i"(PAL_cserve)
+           : "$17", "$18", "$19", "$20", "$21");
+
+       return v0;
+}
+
+static inline void
+qemu_set_alarm_rel(unsigned long expire)
+{
+       register unsigned long a0 __asm__("$16") = 5;
+       register unsigned long a1 __asm__("$17") = expire;
+
+       asm volatile("call_pal %2 # cserve set_alarm_rel"
+                    : "+r"(a0), "+r"(a1)
+                    : "i"(PAL_cserve)
+                    : "$0", "$18", "$19", "$20", "$21");
+}
+
+static inline void
+qemu_set_alarm_abs(unsigned long expire)
+{
+       register unsigned long a0 __asm__("$16") = 6;
+       register unsigned long a1 __asm__("$17") = expire;
+
+       asm volatile("call_pal %2 # cserve set_alarm_abs"
+                    : "+r"(a0), "+r"(a1)
+                    : "i"(PAL_cserve)
+                    : "$0", "$18", "$19", "$20", "$21");
+}
+
+static inline unsigned long
+qemu_get_vmtime(void)
+{
+       register unsigned long v0 __asm__("$0");
+       register unsigned long a0 __asm__("$16") = 7;
+
+       asm("call_pal %2 # cserve get_time"
+           : "=r"(v0), "+r"(a0)
+           : "i"(PAL_cserve)
+           : "$17", "$18", "$19", "$20", "$21");
+
+       return v0;
+}
+
 #endif /* !__ASSEMBLY__ */
 #endif /* __ALPHA_PAL_H */
index bc2a0da..aab14a0 100644 (file)
@@ -72,7 +72,10 @@ pte_alloc_one(struct mm_struct *mm, unsigned long address)
        if (!pte)
                return NULL;
        page = virt_to_page(pte);
-       pgtable_page_ctor(page);
+       if (!pgtable_page_ctor(page)) {
+               __free_page(page);
+               return NULL;
+       }
        return page;
 }
 
index d70408d..f71c3b0 100644 (file)
@@ -1,12 +1 @@
-#ifndef _ALPHA_RTC_H
-#define _ALPHA_RTC_H
-
-#if defined(CONFIG_ALPHA_MARVEL) && defined(CONFIG_SMP) \
- || defined(CONFIG_ALPHA_GENERIC)
-# define get_rtc_time          alpha_mv.rtc_get_time
-# define set_rtc_time          alpha_mv.rtc_set_time
-#endif
-
 #include <asm-generic/rtc.h>
-
-#endif
index b02b8a2..c2911f5 100644 (file)
@@ -22,15 +22,27 @@ extern void * __memcpy(void *, const void *, size_t);
 
 #define __HAVE_ARCH_MEMSET
 extern void * __constant_c_memset(void *, unsigned long, size_t);
+extern void * ___memset(void *, int, size_t);
 extern void * __memset(void *, int, size_t);
 extern void * memset(void *, int, size_t);
 
-#define memset(s, c, n)                                                            \
-(__builtin_constant_p(c)                                                   \
- ? (__builtin_constant_p(n) && (c) == 0                                            \
-    ? __builtin_memset((s),0,(n))                                          \
-    : __constant_c_memset((s),0x0101010101010101UL*(unsigned char)(c),(n))) \
- : __memset((s),(c),(n)))
+/* For gcc 3.x, we cannot have the inline function named "memset" because
+   the __builtin_memset will attempt to resolve to the inline as well,
+   leading to a "sorry" about unimplemented recursive inlining.  */
+extern inline void *__memset(void *s, int c, size_t n)
+{
+       if (__builtin_constant_p(c)) {
+               if (__builtin_constant_p(n)) {
+                       return __builtin_memset(s, c, n);
+               } else {
+                       unsigned long c8 = (c & 0xff) * 0x0101010101010101UL;
+                       return __constant_c_memset(s, c8, n);
+               }
+       }
+       return ___memset(s, c, n);
+}
+
+#define memset __memset
 
 #define __HAVE_ARCH_STRCPY
 extern char * strcpy(char *,const char *);
index 52cd2a4..453597b 100644 (file)
@@ -58,8 +58,6 @@ register struct thread_info *__current_thread_info __asm__("$8");
 #define THREAD_SIZE_ORDER 1
 #define THREAD_SIZE (2*PAGE_SIZE)
 
-#define PREEMPT_ACTIVE         0x40000000
-
 /*
  * Thread information flags:
  * - these are process state flags and used from assembly
index 3c0ce08..dfc8140 100644 (file)
@@ -46,6 +46,7 @@
 #define PAL_rdusp      58
 #define PAL_whami      60
 #define PAL_retsys     61
+#define PAL_wtint      62
 #define PAL_rti                63
 
 
index 84ec46b..0d54650 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_PCI)     += pci.o pci_iommu.o pci-sysfs.o
 obj-$(CONFIG_SRM_ENV)  += srm_env.o
 obj-$(CONFIG_MODULES)  += module.o
 obj-$(CONFIG_PERF_EVENTS) += perf_event.o
+obj-$(CONFIG_RTC_DRV_ALPHA) += rtc.o
 
 ifdef CONFIG_ALPHA_GENERIC
 
index 89566b3..f4c7ab6 100644 (file)
@@ -40,6 +40,7 @@ EXPORT_SYMBOL(strrchr);
 EXPORT_SYMBOL(memmove);
 EXPORT_SYMBOL(__memcpy);
 EXPORT_SYMBOL(__memset);
+EXPORT_SYMBOL(___memset);
 EXPORT_SYMBOL(__memsetw);
 EXPORT_SYMBOL(__constant_c_memset);
 EXPORT_SYMBOL(copy_page);
index 28e4429..1c8625c 100644 (file)
@@ -66,21 +66,7 @@ do_entInt(unsigned long type, unsigned long vector,
                break;
        case 1:
                old_regs = set_irq_regs(regs);
-#ifdef CONFIG_SMP
-         {
-               long cpu;
-
-               smp_percpu_timer_interrupt(regs);
-               cpu = smp_processor_id();
-               if (cpu != boot_cpuid) {
-                       kstat_incr_irqs_this_cpu(RTC_IRQ, irq_to_desc(RTC_IRQ));
-               } else {
-                       handle_irq(RTC_IRQ);
-               }
-         }
-#else
                handle_irq(RTC_IRQ);
-#endif
                set_irq_regs(old_regs);
                return;
        case 2:
@@ -228,7 +214,7 @@ process_mcheck_info(unsigned long vector, unsigned long la_ptr,
  */
 
 struct irqaction timer_irqaction = {
-       .handler        = timer_interrupt,
+       .handler        = rtc_timer_interrupt,
        .name           = "timer",
 };
 
index 7fa6248..f54bdf6 100644 (file)
 #define CAT1(x,y)  x##y
 #define CAT(x,y)   CAT1(x,y)
 
-#define DO_DEFAULT_RTC \
-       .rtc_port = 0x70, \
-       .rtc_get_time = common_get_rtc_time, \
-       .rtc_set_time = common_set_rtc_time
+#define DO_DEFAULT_RTC                 .rtc_port = 0x70
 
 #define DO_EV4_MMU                                                     \
        .max_asn =                      EV4_MAX_ASN,                    \
index d821b17..c52e7f0 100644 (file)
@@ -83,6 +83,8 @@ struct alpha_pmu_t {
        long pmc_left[3];
         /* Subroutine for allocation of PMCs.  Enforces constraints. */
        int (*check_constraints)(struct perf_event **, unsigned long *, int);
+       /* Subroutine for checking validity of a raw event for this PMU. */
+       int (*raw_event_valid)(u64 config);
 };
 
 /*
@@ -203,6 +205,12 @@ success:
 }
 
 
+static int ev67_raw_event_valid(u64 config)
+{
+       return config >= EV67_CYCLES && config < EV67_LAST_ET;
+};
+
+
 static const struct alpha_pmu_t ev67_pmu = {
        .event_map = ev67_perfmon_event_map,
        .max_events = ARRAY_SIZE(ev67_perfmon_event_map),
@@ -211,7 +219,8 @@ static const struct alpha_pmu_t ev67_pmu = {
        .pmc_count_mask = {EV67_PCTR_0_COUNT_MASK,  EV67_PCTR_1_COUNT_MASK,  0},
        .pmc_max_period = {(1UL<<20) - 1, (1UL<<20) - 1, 0},
        .pmc_left = {16, 4, 0},
-       .check_constraints = ev67_check_constraints
+       .check_constraints = ev67_check_constraints,
+       .raw_event_valid = ev67_raw_event_valid,
 };
 
 
@@ -609,7 +618,9 @@ static int __hw_perf_event_init(struct perf_event *event)
        } else if (attr->type == PERF_TYPE_HW_CACHE) {
                return -EOPNOTSUPP;
        } else if (attr->type == PERF_TYPE_RAW) {
-               ev = attr->config & 0xff;
+               if (!alpha_pmu->raw_event_valid(attr->config))
+                       return -EINVAL;
+               ev = attr->config;
        } else {
                return -EOPNOTSUPP;
        }
index f2360a7..1941a07 100644 (file)
 void (*pm_power_off)(void) = machine_power_off;
 EXPORT_SYMBOL(pm_power_off);
 
+#ifdef CONFIG_ALPHA_WTINT
+/*
+ * Sleep the CPU.
+ * EV6, LCA45 and QEMU know how to power down, skipping N timer interrupts.
+ */
+void arch_cpu_idle(void)
+{
+       wtint(0);
+       local_irq_enable();
+}
+
+void arch_cpu_idle_dead(void)
+{
+       wtint(INT_MAX);
+}
+#endif /* ALPHA_WTINT */
+
 struct halt_info {
        int mode;
        char *restart_cmd;
index d3e52d3..da2d6ec 100644 (file)
@@ -135,17 +135,15 @@ extern void unregister_srm_console(void);
 /* smp.c */
 extern void setup_smp(void);
 extern void handle_ipi(struct pt_regs *);
-extern void smp_percpu_timer_interrupt(struct pt_regs *);
 
 /* bios32.c */
 /* extern void reset_for_srm(void); */
 
 /* time.c */
-extern irqreturn_t timer_interrupt(int irq, void *dev);
+extern irqreturn_t rtc_timer_interrupt(int irq, void *dev);
+extern void init_clockevent(void);
 extern void common_init_rtc(void);
 extern unsigned long est_cycle_freq;
-extern unsigned int common_get_rtc_time(struct rtc_time *time);
-extern int common_set_rtc_time(struct rtc_time *time);
 
 /* smc37c93x.c */
 extern void SMC93x_Init(void);
diff --git a/arch/alpha/kernel/rtc.c b/arch/alpha/kernel/rtc.c
new file mode 100644 (file)
index 0000000..c8d284d
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ *  linux/arch/alpha/kernel/rtc.c
+ *
+ *  Copyright (C) 1991, 1992, 1995, 1999, 2000  Linus Torvalds
+ *
+ * This file contains date handling.
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mc146818rtc.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+
+#include <asm/rtc.h>
+
+#include "proto.h"
+
+
+/*
+ * Support for the RTC device.
+ *
+ * We don't want to use the rtc-cmos driver, because we don't want to support
+ * alarms, as that would be indistinguishable from timer interrupts.
+ *
+ * Further, generic code is really, really tied to a 1900 epoch.  This is
+ * true in __get_rtc_time as well as the users of struct rtc_time e.g.
+ * rtc_tm_to_time.  Thankfully all of the other epochs in use are later
+ * than 1900, and so it's easy to adjust.
+ */
+
+static unsigned long rtc_epoch;
+
+static int __init
+specifiy_epoch(char *str)
+{
+       unsigned long epoch = simple_strtoul(str, NULL, 0);
+       if (epoch < 1900)
+               printk("Ignoring invalid user specified epoch %lu\n", epoch);
+       else
+               rtc_epoch = epoch;
+       return 1;
+}
+__setup("epoch=", specifiy_epoch);
+
+static void __init
+init_rtc_epoch(void)
+{
+       int epoch, year, ctrl;
+
+       if (rtc_epoch != 0) {
+               /* The epoch was specified on the command-line.  */
+               return;
+       }
+
+       /* Detect the epoch in use on this computer.  */
+       ctrl = CMOS_READ(RTC_CONTROL);
+       year = CMOS_READ(RTC_YEAR);
+       if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+               year = bcd2bin(year);
+
+       /* PC-like is standard; used for year >= 70 */
+       epoch = 1900;
+       if (year < 20) {
+               epoch = 2000;
+       } else if (year >= 20 && year < 48) {
+               /* NT epoch */
+               epoch = 1980;
+       } else if (year >= 48 && year < 70) {
+               /* Digital UNIX epoch */
+               epoch = 1952;
+       }
+       rtc_epoch = epoch;
+
+       printk(KERN_INFO "Using epoch %d for rtc year %d\n", epoch, year);
+}
+
+static int
+alpha_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       __get_rtc_time(tm);
+
+       /* Adjust for non-default epochs.  It's easier to depend on the
+          generic __get_rtc_time and adjust the epoch here than create
+          a copy of __get_rtc_time with the edits we need.  */
+       if (rtc_epoch != 1900) {
+               int year = tm->tm_year;
+               /* Undo the century adjustment made in __get_rtc_time.  */
+               if (year >= 100)
+                       year -= 100;
+               year += rtc_epoch - 1900;
+               /* Redo the century adjustment with the epoch in place.  */
+               if (year <= 69)
+                       year += 100;
+               tm->tm_year = year;
+       }
+
+       return rtc_valid_tm(tm);
+}
+
+static int
+alpha_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct rtc_time xtm;
+
+       if (rtc_epoch != 1900) {
+               xtm = *tm;
+               xtm.tm_year -= rtc_epoch - 1900;
+               tm = &xtm;
+       }
+
+       return __set_rtc_time(tm);
+}
+
+static int
+alpha_rtc_set_mmss(struct device *dev, unsigned long nowtime)
+{
+       int retval = 0;
+       int real_seconds, real_minutes, cmos_minutes;
+       unsigned char save_control, save_freq_select;
+
+       /* Note: This code only updates minutes and seconds.  Comments
+          indicate this was to avoid messing with unknown time zones,
+          and with the epoch nonsense described above.  In order for
+          this to work, the existing clock cannot be off by more than
+          15 minutes.
+
+          ??? This choice is may be out of date.  The x86 port does
+          not have problems with timezones, and the epoch processing has
+          now been fixed in alpha_set_rtc_time.
+
+          In either case, one can always force a full rtc update with
+          the userland hwclock program, so surely 15 minute accuracy
+          is no real burden.  */
+
+       /* In order to set the CMOS clock precisely, we have to be called
+          500 ms after the second nowtime has started, because when
+          nowtime is written into the registers of the CMOS clock, it will
+          jump to the next second precisely 500 ms later. Check the Motorola
+          MC146818A or Dallas DS12887 data sheet for details.  */
+
+       /* irq are locally disabled here */
+       spin_lock(&rtc_lock);
+       /* Tell the clock it's being set */
+       save_control = CMOS_READ(RTC_CONTROL);
+       CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
+
+       /* Stop and reset prescaler */
+       save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
+       CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+       cmos_minutes = CMOS_READ(RTC_MINUTES);
+       if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+               cmos_minutes = bcd2bin(cmos_minutes);
+
+       real_seconds = nowtime % 60;
+       real_minutes = nowtime / 60;
+       if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1) {
+               /* correct for half hour time zone */
+               real_minutes += 30;
+       }
+       real_minutes %= 60;
+
+       if (abs(real_minutes - cmos_minutes) < 30) {
+               if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+                       real_seconds = bin2bcd(real_seconds);
+                       real_minutes = bin2bcd(real_minutes);
+               }
+               CMOS_WRITE(real_seconds,RTC_SECONDS);
+               CMOS_WRITE(real_minutes,RTC_MINUTES);
+       } else {
+               printk_once(KERN_NOTICE
+                           "set_rtc_mmss: can't update from %d to %d\n",
+                           cmos_minutes, real_minutes);
+               retval = -1;
+       }
+
+       /* The following flags have to be released exactly in this order,
+        * otherwise the DS12887 (popular MC146818A clone with integrated
+        * battery and quartz) will not reset the oscillator and will not
+        * update precisely 500 ms later. You won't find this mentioned in
+        * the Dallas Semiconductor data sheets, but who believes data
+        * sheets anyway ...                           -- Markus Kuhn
+        */
+       CMOS_WRITE(save_control, RTC_CONTROL);
+       CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+       spin_unlock(&rtc_lock);
+
+       return retval;
+}
+
+static int
+alpha_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case RTC_EPOCH_READ:
+               return put_user(rtc_epoch, (unsigned long __user *)arg);
+       case RTC_EPOCH_SET:
+               if (arg < 1900)
+                       return -EINVAL;
+               rtc_epoch = arg;
+               return 0;
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+static const struct rtc_class_ops alpha_rtc_ops = {
+       .read_time = alpha_rtc_read_time,
+       .set_time = alpha_rtc_set_time,
+       .set_mmss = alpha_rtc_set_mmss,
+       .ioctl = alpha_rtc_ioctl,
+};
+
+/*
+ * Similarly, except do the actual CMOS access on the boot cpu only.
+ * This requires marshalling the data across an interprocessor call.
+ */
+
+#if defined(CONFIG_SMP) && \
+    (defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_MARVEL))
+# define HAVE_REMOTE_RTC 1
+
+union remote_data {
+       struct rtc_time *tm;
+       unsigned long now;
+       long retval;
+};
+
+static void
+do_remote_read(void *data)
+{
+       union remote_data *x = data;
+       x->retval = alpha_rtc_read_time(NULL, x->tm);
+}
+
+static int
+remote_read_time(struct device *dev, struct rtc_time *tm)
+{
+       union remote_data x;
+       if (smp_processor_id() != boot_cpuid) {
+               x.tm = tm;
+               smp_call_function_single(boot_cpuid, do_remote_read, &x, 1);
+               return x.retval;
+       }
+       return alpha_rtc_read_time(NULL, tm);
+}
+
+static void
+do_remote_set(void *data)
+{
+       union remote_data *x = data;
+       x->retval = alpha_rtc_set_time(NULL, x->tm);
+}
+
+static int
+remote_set_time(struct device *dev, struct rtc_time *tm)
+{
+       union remote_data x;
+       if (smp_processor_id() != boot_cpuid) {
+               x.tm = tm;
+               smp_call_function_single(boot_cpuid, do_remote_set, &x, 1);
+               return x.retval;
+       }
+       return alpha_rtc_set_time(NULL, tm);
+}
+
+static void
+do_remote_mmss(void *data)
+{
+       union remote_data *x = data;
+       x->retval = alpha_rtc_set_mmss(NULL, x->now);
+}
+
+static int
+remote_set_mmss(struct device *dev, unsigned long now)
+{
+       union remote_data x;
+       if (smp_processor_id() != boot_cpuid) {
+               x.now = now;
+               smp_call_function_single(boot_cpuid, do_remote_mmss, &x, 1);
+               return x.retval;
+       }
+       return alpha_rtc_set_mmss(NULL, now);
+}
+
+static const struct rtc_class_ops remote_rtc_ops = {
+       .read_time = remote_read_time,
+       .set_time = remote_set_time,
+       .set_mmss = remote_set_mmss,
+       .ioctl = alpha_rtc_ioctl,
+};
+#endif
+
+static int __init
+alpha_rtc_init(void)
+{
+       const struct rtc_class_ops *ops;
+       struct platform_device *pdev;
+       struct rtc_device *rtc;
+       const char *name;
+
+       init_rtc_epoch();
+       name = "rtc-alpha";
+       ops = &alpha_rtc_ops;
+
+#ifdef HAVE_REMOTE_RTC
+       if (alpha_mv.rtc_boot_cpu_only)
+               ops = &remote_rtc_ops;
+#endif
+
+       pdev = platform_device_register_simple(name, -1, NULL, 0);
+       rtc = devm_rtc_device_register(&pdev->dev, name, ops, THIS_MODULE);
+       if (IS_ERR(rtc))
+               return PTR_ERR(rtc);
+
+       platform_set_drvdata(pdev, rtc);
+       return 0;
+}
+device_initcall(alpha_rtc_init);
index 9e3107c..b20af76 100644 (file)
@@ -115,10 +115,17 @@ unsigned long alpha_agpgart_size = DEFAULT_AGP_APER_SIZE;
 
 #ifdef CONFIG_ALPHA_GENERIC
 struct alpha_machine_vector alpha_mv;
+#endif
+
+#ifndef alpha_using_srm
 int alpha_using_srm;
 EXPORT_SYMBOL(alpha_using_srm);
 #endif
 
+#ifndef alpha_using_qemu
+int alpha_using_qemu;
+#endif
+
 static struct alpha_machine_vector *get_sysvec(unsigned long, unsigned long,
                                               unsigned long);
 static struct alpha_machine_vector *get_sysvec_byname(const char *);
@@ -529,11 +536,15 @@ setup_arch(char **cmdline_p)
        atomic_notifier_chain_register(&panic_notifier_list,
                        &alpha_panic_block);
 
-#ifdef CONFIG_ALPHA_GENERIC
+#ifndef alpha_using_srm
        /* Assume that we've booted from SRM if we haven't booted from MILO.
           Detect the later by looking for "MILO" in the system serial nr.  */
        alpha_using_srm = strncmp((const char *)hwrpb->ssn, "MILO", 4) != 0;
 #endif
+#ifndef alpha_using_qemu
+       /* Similarly, look for QEMU.  */
+       alpha_using_qemu = strstr((const char *)hwrpb->ssn, "QEMU") != 0;
+#endif
 
        /* If we are using SRM, we want to allow callbacks
           as early as possible, so do this NOW, and then
@@ -1207,6 +1218,7 @@ show_cpuinfo(struct seq_file *f, void *slot)
        char *systype_name;
        char *sysvariation_name;
        int nr_processors;
+       unsigned long timer_freq;
 
        cpu_index = (unsigned) (cpu->type - 1);
        cpu_name = "Unknown";
@@ -1218,6 +1230,12 @@ show_cpuinfo(struct seq_file *f, void *slot)
 
        nr_processors = get_nr_processors(cpu, hwrpb->nr_processors);
 
+#if CONFIG_HZ == 1024 || CONFIG_HZ == 1200
+       timer_freq = (100UL * hwrpb->intr_freq) / 4096;
+#else
+       timer_freq = 100UL * CONFIG_HZ;
+#endif
+
        seq_printf(f, "cpu\t\t\t: Alpha\n"
                      "cpu model\t\t: %s\n"
                      "cpu variation\t\t: %ld\n"
@@ -1243,8 +1261,7 @@ show_cpuinfo(struct seq_file *f, void *slot)
                       (char*)hwrpb->ssn,
                       est_cycle_freq ? : hwrpb->cycle_freq,
                       est_cycle_freq ? "est." : "",
-                      hwrpb->intr_freq / 4096,
-                      (100 * hwrpb->intr_freq / 4096) % 100,
+                      timer_freq / 100, timer_freq % 100,
                       hwrpb->pagesize,
                       hwrpb->pa_bits,
                       hwrpb->max_asn,
index 9dbbcb3..99ac36d 100644 (file)
@@ -138,9 +138,11 @@ smp_callin(void)
 
        /* Get our local ticker going. */
        smp_setup_percpu_timer(cpuid);
+       init_clockevent();
 
        /* Call platform-specific callin, if specified */
-       if (alpha_mv.smp_callin) alpha_mv.smp_callin();
+       if (alpha_mv.smp_callin)
+               alpha_mv.smp_callin();
 
        /* All kernel threads share the same mm context.  */
        atomic_inc(&init_mm.mm_count);
@@ -498,35 +500,6 @@ smp_cpus_done(unsigned int max_cpus)
               ((bogosum + 2500) / (5000/HZ)) % 100);
 }
 
-\f
-void
-smp_percpu_timer_interrupt(struct pt_regs *regs)
-{
-       struct pt_regs *old_regs;
-       int cpu = smp_processor_id();
-       unsigned long user = user_mode(regs);
-       struct cpuinfo_alpha *data = &cpu_data[cpu];
-
-       old_regs = set_irq_regs(regs);
-
-       /* Record kernel PC.  */
-       profile_tick(CPU_PROFILING);
-
-       if (!--data->prof_counter) {
-               /* We need to make like a normal interrupt -- otherwise
-                  timer interrupts ignore the global interrupt lock,
-                  which would be a Bad Thing.  */
-               irq_enter();
-
-               update_process_times(user);
-
-               data->prof_counter = data->prof_multiplier;
-
-               irq_exit();
-       }
-       set_irq_regs(old_regs);
-}
-
 int
 setup_profiling_timer(unsigned int multiplier)
 {
index 5a0af11..608f2a7 100644 (file)
@@ -224,8 +224,6 @@ struct alpha_machine_vector jensen_mv __initmv = {
        .machine_check          = jensen_machine_check,
        .max_isa_dma_address    = ALPHA_MAX_ISA_DMA_ADDRESS,
        .rtc_port               = 0x170,
-       .rtc_get_time           = common_get_rtc_time,
-       .rtc_set_time           = common_set_rtc_time,
 
        .nr_irqs                = 16,
        .device_interrupt       = jensen_device_interrupt,
index c92e389..f21d61f 100644 (file)
@@ -22,7 +22,6 @@
 #include <asm/hwrpb.h>
 #include <asm/tlbflush.h>
 #include <asm/vga.h>
-#include <asm/rtc.h>
 
 #include "proto.h"
 #include "err_impl.h"
@@ -400,57 +399,6 @@ marvel_init_rtc(void)
        init_rtc_irq();
 }
 
-struct marvel_rtc_time {
-       struct rtc_time *time;
-       int retval;
-};
-
-#ifdef CONFIG_SMP
-static void
-smp_get_rtc_time(void *data)
-{
-       struct marvel_rtc_time *mrt = data;
-       mrt->retval = __get_rtc_time(mrt->time);
-}
-
-static void
-smp_set_rtc_time(void *data)
-{
-       struct marvel_rtc_time *mrt = data;
-       mrt->retval = __set_rtc_time(mrt->time);
-}
-#endif
-
-static unsigned int
-marvel_get_rtc_time(struct rtc_time *time)
-{
-#ifdef CONFIG_SMP
-       struct marvel_rtc_time mrt;
-
-       if (smp_processor_id() != boot_cpuid) {
-               mrt.time = time;
-               smp_call_function_single(boot_cpuid, smp_get_rtc_time, &mrt, 1);
-               return mrt.retval;
-       }
-#endif
-       return __get_rtc_time(time);
-}
-
-static int
-marvel_set_rtc_time(struct rtc_time *time)
-{
-#ifdef CONFIG_SMP
-       struct marvel_rtc_time mrt;
-
-       if (smp_processor_id() != boot_cpuid) {
-               mrt.time = time;
-               smp_call_function_single(boot_cpuid, smp_set_rtc_time, &mrt, 1);
-               return mrt.retval;
-       }
-#endif
-       return __set_rtc_time(time);
-}
-
 static void
 marvel_smp_callin(void)
 {
@@ -492,8 +440,7 @@ struct alpha_machine_vector marvel_ev7_mv __initmv = {
        .vector_name            = "MARVEL/EV7",
        DO_EV7_MMU,
        .rtc_port               = 0x70,
-       .rtc_get_time           = marvel_get_rtc_time,
-       .rtc_set_time           = marvel_set_rtc_time,
+       .rtc_boot_cpu_only      = 1,
        DO_MARVEL_IO,
        .machine_check          = marvel_machine_check,
        .max_isa_dma_address    = ALPHA_MAX_ISA_DMA_ADDRESS,
index ea33950..ee39cee 100644 (file)
@@ -3,13 +3,7 @@
  *
  *  Copyright (C) 1991, 1992, 1995, 1999, 2000  Linus Torvalds
  *
- * This file contains the PC-specific time handling details:
- * reading the RTC at bootup, etc..
- * 1994-07-02    Alan Modra
- *     fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
- * 1995-03-26    Markus Kuhn
- *      fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887
- *      precision CMOS clock update
+ * This file contains the clocksource time handling.
  * 1997-09-10  Updated NTP code according to technical memorandum Jan '96
  *             "A Kernel Model for Precision Timekeeping" by Dave Mills
  * 1997-01-09    Adrian Sun
@@ -21,9 +15,6 @@
  * 1999-04-16  Thorsten Kranzkowski (dl8bcu@gmx.net)
  *     fixed algorithm in do_gettimeofday() for calculating the precise time
  *     from processor cycle counter (now taking lost_ticks into account)
- * 2000-08-13  Jan-Benedict Glaw <jbglaw@lug-owl.de>
- *     Fixed time_init to be aware of epoches != 1900. This prevents
- *     booting up in 2048 for me;) Code is stolen from rtc.c.
  * 2003-06-03  R. Scott Bailey <scott.bailey@eds.com>
  *     Tighten sanity in time_init from 1% (10,000 PPM) to 250 PPM
  */
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/hwrpb.h>
-#include <asm/rtc.h>
 
 #include <linux/mc146818rtc.h>
 #include <linux/time.h>
 #include <linux/timex.h>
 #include <linux/clocksource.h>
+#include <linux/clockchips.h>
 
 #include "proto.h"
 #include "irq_impl.h"
 
-static int set_rtc_mmss(unsigned long);
-
 DEFINE_SPINLOCK(rtc_lock);
 EXPORT_SYMBOL(rtc_lock);
 
-#define TICK_SIZE (tick_nsec / 1000)
-
-/*
- * Shift amount by which scaled_ticks_per_cycle is scaled.  Shifting
- * by 48 gives us 16 bits for HZ while keeping the accuracy good even
- * for large CPU clock rates.
- */
-#define FIX_SHIFT      48
-
-/* lump static variables together for more efficient access: */
-static struct {
-       /* cycle counter last time it got invoked */
-       __u32 last_time;
-       /* ticks/cycle * 2^48 */
-       unsigned long scaled_ticks_per_cycle;
-       /* partial unused tick */
-       unsigned long partial_tick;
-} state;
-
 unsigned long est_cycle_freq;
 
 #ifdef CONFIG_IRQ_WORK
@@ -108,109 +78,156 @@ static inline __u32 rpcc(void)
        return __builtin_alpha_rpcc();
 }
 
-int update_persistent_clock(struct timespec now)
-{
-       return set_rtc_mmss(now.tv_sec);
-}
 
-void read_persistent_clock(struct timespec *ts)
+\f
+/*
+ * The RTC as a clock_event_device primitive.
+ */
+
+static DEFINE_PER_CPU(struct clock_event_device, cpu_ce);
+
+irqreturn_t
+rtc_timer_interrupt(int irq, void *dev)
 {
-       unsigned int year, mon, day, hour, min, sec, epoch;
-
-       sec = CMOS_READ(RTC_SECONDS);
-       min = CMOS_READ(RTC_MINUTES);
-       hour = CMOS_READ(RTC_HOURS);
-       day = CMOS_READ(RTC_DAY_OF_MONTH);
-       mon = CMOS_READ(RTC_MONTH);
-       year = CMOS_READ(RTC_YEAR);
-
-       if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
-               sec = bcd2bin(sec);
-               min = bcd2bin(min);
-               hour = bcd2bin(hour);
-               day = bcd2bin(day);
-               mon = bcd2bin(mon);
-               year = bcd2bin(year);
-       }
+       int cpu = smp_processor_id();
+       struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
 
-       /* PC-like is standard; used for year >= 70 */
-       epoch = 1900;
-       if (year < 20)
-               epoch = 2000;
-       else if (year >= 20 && year < 48)
-               /* NT epoch */
-               epoch = 1980;
-       else if (year >= 48 && year < 70)
-               /* Digital UNIX epoch */
-               epoch = 1952;
+       /* Don't run the hook for UNUSED or SHUTDOWN.  */
+       if (likely(ce->mode == CLOCK_EVT_MODE_PERIODIC))
+               ce->event_handler(ce);
 
-       printk(KERN_INFO "Using epoch = %d\n", epoch);
+       if (test_irq_work_pending()) {
+               clear_irq_work_pending();
+               irq_work_run();
+       }
 
-       if ((year += epoch) < 1970)
-               year += 100;
+       return IRQ_HANDLED;
+}
 
-       ts->tv_sec = mktime(year, mon, day, hour, min, sec);
-       ts->tv_nsec = 0;
+static void
+rtc_ce_set_mode(enum clock_event_mode mode, struct clock_event_device *ce)
+{
+       /* The mode member of CE is updated in generic code.
+          Since we only support periodic events, nothing to do.  */
+}
+
+static int
+rtc_ce_set_next_event(unsigned long evt, struct clock_event_device *ce)
+{
+       /* This hook is for oneshot mode, which we don't support.  */
+       return -EINVAL;
 }
 
+static void __init
+init_rtc_clockevent(void)
+{
+       int cpu = smp_processor_id();
+       struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
+
+       *ce = (struct clock_event_device){
+               .name = "rtc",
+               .features = CLOCK_EVT_FEAT_PERIODIC,
+               .rating = 100,
+               .cpumask = cpumask_of(cpu),
+               .set_mode = rtc_ce_set_mode,
+               .set_next_event = rtc_ce_set_next_event,
+       };
 
+       clockevents_config_and_register(ce, CONFIG_HZ, 0, 0);
+}
 
+\f
 /*
- * timer_interrupt() needs to keep up the real-time clock,
- * as well as call the "xtime_update()" routine every clocktick
+ * The QEMU clock as a clocksource primitive.
  */
-irqreturn_t timer_interrupt(int irq, void *dev)
+
+static cycle_t
+qemu_cs_read(struct clocksource *cs)
 {
-       unsigned long delta;
-       __u32 now;
-       long nticks;
+       return qemu_get_vmtime();
+}
 
-#ifndef CONFIG_SMP
-       /* Not SMP, do kernel PC profiling here.  */
-       profile_tick(CPU_PROFILING);
-#endif
+static struct clocksource qemu_cs = {
+       .name                   = "qemu",
+       .rating                 = 400,
+       .read                   = qemu_cs_read,
+       .mask                   = CLOCKSOURCE_MASK(64),
+       .flags                  = CLOCK_SOURCE_IS_CONTINUOUS,
+       .max_idle_ns            = LONG_MAX
+};
 
-       /*
-        * Calculate how many ticks have passed since the last update,
-        * including any previous partial leftover.  Save any resulting
-        * fraction for the next pass.
-        */
-       now = rpcc();
-       delta = now - state.last_time;
-       state.last_time = now;
-       delta = delta * state.scaled_ticks_per_cycle + state.partial_tick;
-       state.partial_tick = delta & ((1UL << FIX_SHIFT) - 1); 
-       nticks = delta >> FIX_SHIFT;
 
-       if (nticks)
-               xtime_update(nticks);
+/*
+ * The QEMU alarm as a clock_event_device primitive.
+ */
 
-       if (test_irq_work_pending()) {
-               clear_irq_work_pending();
-               irq_work_run();
-       }
+static void
+qemu_ce_set_mode(enum clock_event_mode mode, struct clock_event_device *ce)
+{
+       /* The mode member of CE is updated for us in generic code.
+          Just make sure that the event is disabled.  */
+       qemu_set_alarm_abs(0);
+}
 
-#ifndef CONFIG_SMP
-       while (nticks--)
-               update_process_times(user_mode(get_irq_regs()));
-#endif
+static int
+qemu_ce_set_next_event(unsigned long evt, struct clock_event_device *ce)
+{
+       qemu_set_alarm_rel(evt);
+       return 0;
+}
 
+static irqreturn_t
+qemu_timer_interrupt(int irq, void *dev)
+{
+       int cpu = smp_processor_id();
+       struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
+
+       ce->event_handler(ce);
        return IRQ_HANDLED;
 }
 
+static void __init
+init_qemu_clockevent(void)
+{
+       int cpu = smp_processor_id();
+       struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
+
+       *ce = (struct clock_event_device){
+               .name = "qemu",
+               .features = CLOCK_EVT_FEAT_ONESHOT,
+               .rating = 400,
+               .cpumask = cpumask_of(cpu),
+               .set_mode = qemu_ce_set_mode,
+               .set_next_event = qemu_ce_set_next_event,
+       };
+
+       clockevents_config_and_register(ce, NSEC_PER_SEC, 1000, LONG_MAX);
+}
+
+\f
 void __init
 common_init_rtc(void)
 {
-       unsigned char x;
+       unsigned char x, sel = 0;
 
        /* Reset periodic interrupt frequency.  */
-       x = CMOS_READ(RTC_FREQ_SELECT) & 0x3f;
-        /* Test includes known working values on various platforms
-           where 0x26 is wrong; we refuse to change those. */
-       if (x != 0x26 && x != 0x25 && x != 0x19 && x != 0x06) {
-               printk("Setting RTC_FREQ to 1024 Hz (%x)\n", x);
-               CMOS_WRITE(0x26, RTC_FREQ_SELECT);
+#if CONFIG_HZ == 1024 || CONFIG_HZ == 1200
+       x = CMOS_READ(RTC_FREQ_SELECT) & 0x3f;
+       /* Test includes known working values on various platforms
+          where 0x26 is wrong; we refuse to change those. */
+       if (x != 0x26 && x != 0x25 && x != 0x19 && x != 0x06) {
+               sel = RTC_REF_CLCK_32KHZ + 6;
        }
+#elif CONFIG_HZ == 256 || CONFIG_HZ == 128 || CONFIG_HZ == 64 || CONFIG_HZ == 32
+       sel = RTC_REF_CLCK_32KHZ + __builtin_ffs(32768 / CONFIG_HZ);
+#else
+# error "Unknown HZ from arch/alpha/Kconfig"
+#endif
+       if (sel) {
+               printk(KERN_INFO "Setting RTC_FREQ to %d Hz (%x)\n",
+                      CONFIG_HZ, sel);
+               CMOS_WRITE(sel, RTC_FREQ_SELECT);
+       }
 
        /* Turn on periodic interrupts.  */
        x = CMOS_READ(RTC_CONTROL);
@@ -233,16 +250,37 @@ common_init_rtc(void)
        init_rtc_irq();
 }
 
-unsigned int common_get_rtc_time(struct rtc_time *time)
-{
-       return __get_rtc_time(time);
-}
+\f
+#ifndef CONFIG_ALPHA_WTINT
+/*
+ * The RPCC as a clocksource primitive.
+ *
+ * While we have free-running timecounters running on all CPUs, and we make
+ * a half-hearted attempt in init_rtc_rpcc_info to sync the timecounter
+ * with the wall clock, that initialization isn't kept up-to-date across
+ * different time counters in SMP mode.  Therefore we can only use this
+ * method when there's only one CPU enabled.
+ *
+ * When using the WTINT PALcall, the RPCC may shift to a lower frequency,
+ * or stop altogether, while waiting for the interrupt.  Therefore we cannot
+ * use this method when WTINT is in use.
+ */
 
-int common_set_rtc_time(struct rtc_time *time)
+static cycle_t read_rpcc(struct clocksource *cs)
 {
-       return __set_rtc_time(time);
+       return rpcc();
 }
 
+static struct clocksource clocksource_rpcc = {
+       .name                   = "rpcc",
+       .rating                 = 300,
+       .read                   = read_rpcc,
+       .mask                   = CLOCKSOURCE_MASK(32),
+       .flags                  = CLOCK_SOURCE_IS_CONTINUOUS
+};
+#endif /* ALPHA_WTINT */
+
+\f
 /* Validate a computed cycle counter result against the known bounds for
    the given processor core.  There's too much brokenness in the way of
    timing hardware for any one method to work everywhere.  :-(
@@ -353,33 +391,6 @@ rpcc_after_update_in_progress(void)
        return rpcc();
 }
 
-#ifndef CONFIG_SMP
-/* Until and unless we figure out how to get cpu cycle counters
-   in sync and keep them there, we can't use the rpcc.  */
-static cycle_t read_rpcc(struct clocksource *cs)
-{
-       cycle_t ret = (cycle_t)rpcc();
-       return ret;
-}
-
-static struct clocksource clocksource_rpcc = {
-       .name                   = "rpcc",
-       .rating                 = 300,
-       .read                   = read_rpcc,
-       .mask                   = CLOCKSOURCE_MASK(32),
-       .flags                  = CLOCK_SOURCE_IS_CONTINUOUS
-};
-
-static inline void register_rpcc_clocksource(long cycle_freq)
-{
-       clocksource_register_hz(&clocksource_rpcc, cycle_freq);
-}
-#else /* !CONFIG_SMP */
-static inline void register_rpcc_clocksource(long cycle_freq)
-{
-}
-#endif /* !CONFIG_SMP */
-
 void __init
 time_init(void)
 {
@@ -387,6 +398,15 @@ time_init(void)
        unsigned long cycle_freq, tolerance;
        long diff;
 
+       if (alpha_using_qemu) {
+               clocksource_register_hz(&qemu_cs, NSEC_PER_SEC);
+               init_qemu_clockevent();
+
+               timer_irqaction.handler = qemu_timer_interrupt;
+               init_rtc_irq();
+               return;
+       }
+
        /* Calibrate CPU clock -- attempt #1.  */
        if (!est_cycle_freq)
                est_cycle_freq = validate_cc_value(calibrate_cc_with_pit());
@@ -421,100 +441,25 @@ time_init(void)
                       "and unable to estimate a proper value!\n");
        }
 
-       /* From John Bowman <bowman@math.ualberta.ca>: allow the values
-          to settle, as the Update-In-Progress bit going low isn't good
-          enough on some hardware.  2ms is our guess; we haven't found 
-          bogomips yet, but this is close on a 500Mhz box.  */
-       __delay(1000000);
-
-
-       if (HZ > (1<<16)) {
-               extern void __you_loose (void);
-               __you_loose();
-       }
-
-       register_rpcc_clocksource(cycle_freq);
-
-       state.last_time = cc1;
-       state.scaled_ticks_per_cycle
-               = ((unsigned long) HZ << FIX_SHIFT) / cycle_freq;
-       state.partial_tick = 0L;
+       /* See above for restrictions on using clocksource_rpcc.  */
+#ifndef CONFIG_ALPHA_WTINT
+       if (hwrpb->nr_processors == 1)
+               clocksource_register_hz(&clocksource_rpcc, cycle_freq);
+#endif
 
        /* Startup the timer source. */
        alpha_mv.init_rtc();
+       init_rtc_clockevent();
 }
 
-/*
- * In order to set the CMOS clock precisely, set_rtc_mmss has to be
- * called 500 ms after the second nowtime has started, because when
- * nowtime is written into the registers of the CMOS clock, it will
- * jump to the next second precisely 500 ms later. Check the Motorola
- * MC146818A or Dallas DS12887 data sheet for details.
- *
- * BUG: This routine does not handle hour overflow properly; it just
- *      sets the minutes. Usually you won't notice until after reboot!
- */
-
-
-static int
-set_rtc_mmss(unsigned long nowtime)
+/* Initialize the clock_event_device for secondary cpus.  */
+#ifdef CONFIG_SMP
+void __init
+init_clockevent(void)
 {
-       int retval = 0;
-       int real_seconds, real_minutes, cmos_minutes;
-       unsigned char save_control, save_freq_select;
-
-       /* irq are locally disabled here */
-       spin_lock(&rtc_lock);
-       /* Tell the clock it's being set */
-       save_control = CMOS_READ(RTC_CONTROL);
-       CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
-
-       /* Stop and reset prescaler */
-       save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
-       CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
-
-       cmos_minutes = CMOS_READ(RTC_MINUTES);
-       if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
-               cmos_minutes = bcd2bin(cmos_minutes);
-
-       /*
-        * since we're only adjusting minutes and seconds,
-        * don't interfere with hour overflow. This avoids
-        * messing with unknown time zones but requires your
-        * RTC not to be off by more than 15 minutes
-        */
-       real_seconds = nowtime % 60;
-       real_minutes = nowtime / 60;
-       if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) {
-               /* correct for half hour time zone */
-               real_minutes += 30;
-       }
-       real_minutes %= 60;
-
-       if (abs(real_minutes - cmos_minutes) < 30) {
-               if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
-                       real_seconds = bin2bcd(real_seconds);
-                       real_minutes = bin2bcd(real_minutes);
-               }
-               CMOS_WRITE(real_seconds,RTC_SECONDS);
-               CMOS_WRITE(real_minutes,RTC_MINUTES);
-       } else {
-               printk_once(KERN_NOTICE
-                      "set_rtc_mmss: can't update from %d to %d\n",
-                      cmos_minutes, real_minutes);
-               retval = -1;
-       }
-
-       /* The following flags have to be released exactly in this order,
-        * otherwise the DS12887 (popular MC146818A clone with integrated
-        * battery and quartz) will not reset the oscillator and will not
-        * update precisely 500 ms later. You won't find this mentioned in
-        * the Dallas Semiconductor data sheets, but who believes data
-        * sheets anyway ...                           -- Markus Kuhn
-        */
-       CMOS_WRITE(save_control, RTC_CONTROL);
-       CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
-       spin_unlock(&rtc_lock);
-
-       return retval;
+       if (alpha_using_qemu)
+               init_qemu_clockevent();
+       else
+               init_rtc_clockevent();
 }
+#endif
index bd0665c..9c4c189 100644 (file)
@@ -241,6 +241,21 @@ do_entIF(unsigned long type, struct pt_regs *regs)
                               (const char *)(data[1] | (long)data[2] << 32), 
                               data[0]);
                }
+#ifdef CONFIG_ALPHA_WTINT
+               if (type == 4) {
+                       /* If CALL_PAL WTINT is totally unsupported by the
+                          PALcode, e.g. MILO, "emulate" it by overwriting
+                          the insn.  */
+                       unsigned int *pinsn
+                         = (unsigned int *) regs->pc - 1;
+                       if (*pinsn == PAL_wtint) {
+                               *pinsn = 0x47e01400; /* mov 0,$0 */
+                               imb();
+                               regs->r0 = 0;
+                               return;
+                       }
+               }
+#endif /* ALPHA_WTINT */
                die_if_kernel((type == 1 ? "Kernel Bug" : "Instruction fault"),
                              regs, type, NULL);
        }
index ffb19b7..ff3c107 100644 (file)
@@ -130,7 +130,7 @@ csum_partial_cfu_aligned(const unsigned long __user *src, unsigned long *dst,
                *dst = word | tmp;
                checksum += carry;
        }
-       if (err) *errp = err;
+       if (err && errp) *errp = err;
        return checksum;
 }
 
@@ -185,7 +185,7 @@ csum_partial_cfu_dest_aligned(const unsigned long __user *src,
                *dst = word | tmp;
                checksum += carry;
        }
-       if (err) *errp = err;
+       if (err && errp) *errp = err;
        return checksum;
 }
 
@@ -242,7 +242,7 @@ csum_partial_cfu_src_aligned(const unsigned long __user *src,
        stq_u(partial_dest | second_dest, dst);
 out:
        checksum += carry;
-       if (err) *errp = err;
+       if (err && errp) *errp = err;
        return checksum;
 }
 
@@ -325,7 +325,7 @@ csum_partial_cfu_unaligned(const unsigned long __user * src,
                stq_u(partial_dest | word | second_dest, dst);
                checksum += carry;
        }
-       if (err) *errp = err;
+       if (err && errp) *errp = err;
        return checksum;
 }
 
@@ -339,7 +339,7 @@ csum_partial_copy_from_user(const void __user *src, void *dst, int len,
 
        if (len) {
                if (!access_ok(VERIFY_READ, src, len)) {
-                       *errp = -EFAULT;
+                       if (errp) *errp = -EFAULT;
                        memset(dst, 0, len);
                        return sum;
                }
index d8b94e1..356bb2f 100644 (file)
        .set noat
        .set noreorder
 .text
+       .globl memset
        .globl __memset
+       .globl ___memset
        .globl __memsetw
        .globl __constant_c_memset
-       .globl memset
 
-       .ent __memset
+       .ent ___memset
 .align 5
-__memset:
+___memset:
        .frame $30,0,$26,0
        .prologue 0
 
@@ -227,7 +228,7 @@ end_b:
        nop
        nop
        ret $31,($26),1         # L0 :
-       .end __memset
+       .end ___memset
 
        /*
         * This is the original body of code, prior to replication and
@@ -594,4 +595,5 @@ end_w:
 
        .end __memsetw
 
-memset = __memset
+memset = ___memset
+__memset = ___memset
index 311b8cf..76ccc6d 100644 (file)
 .text
        .globl memset
        .globl __memset
+       .globl ___memset
        .globl __memsetw
        .globl __constant_c_memset
-       .ent __memset
+
+       .ent ___memset
 .align 5
-__memset:
+___memset:
        .frame $30,0,$26,0
        .prologue 0
 
@@ -103,7 +105,7 @@ within_one_quad:
 
 end:
        ret $31,($26),1         /* E1 */
-       .end __memset
+       .end ___memset
 
        .align 5
        .ent __memsetw
@@ -121,4 +123,5 @@ __memsetw:
 
        .end __memsetw
 
-memset = __memset
+memset = ___memset
+__memset = ___memset
index 5ede546..2ee0c9b 100644 (file)
@@ -125,7 +125,6 @@ config ARC_PLAT_NEEDS_CPU_TO_DMA
 config SMP
        bool "Symmetric Multi-Processing (Incomplete)"
        default n
-       select USE_GENERIC_SMP_HELPERS
        help
          This enables support for systems with more than one CPU. If you have
          a system with only one CPU, like most personal computers, say N. If
index d9f8249..3942634 100644 (file)
                iomux: iomux@FF10601c {
                        /* Port 1 */
                        pctl_tsin_s0: pctl-tsin-s0 {   /* Serial TS-in 0 */
-                               pingrp = "mis0_pins";
+                               abilis,function = "mis0";
                        };
                        pctl_tsin_s1: pctl-tsin-s1 {   /* Serial TS-in 1 */
-                               pingrp = "mis1_pins";
+                               abilis,function = "mis1";
                        };
                        pctl_gpio_a: pctl-gpio-a {     /* GPIO bank A */
-                               pingrp = "gpioa_pins";
+                               abilis,function = "gpioa";
                        };
                        pctl_tsin_p1: pctl-tsin-p1 {   /* Parallel TS-in 1 */
-                               pingrp = "mip1_pins";
+                               abilis,function = "mip1";
                        };
                        /* Port 2 */
                        pctl_tsin_s2: pctl-tsin-s2 {   /* Serial TS-in 2 */
-                               pingrp = "mis2_pins";
+                               abilis,function = "mis2";
                        };
                        pctl_tsin_s3: pctl-tsin-s3 {   /* Serial TS-in 3 */
-                               pingrp = "mis3_pins";
+                               abilis,function = "mis3";
                        };
                        pctl_gpio_c: pctl-gpio-c {     /* GPIO bank C */
-                               pingrp = "gpioc_pins";
+                               abilis,function = "gpioc";
                        };
                        pctl_tsin_p3: pctl-tsin-p3 {   /* Parallel TS-in 3 */
-                               pingrp = "mip3_pins";
+                               abilis,function = "mip3";
                        };
                        /* Port 3 */
                        pctl_tsin_s4: pctl-tsin-s4 {   /* Serial TS-in 4 */
-                               pingrp = "mis4_pins";
+                               abilis,function = "mis4";
                        };
                        pctl_tsin_s5: pctl-tsin-s5 {   /* Serial TS-in 5 */
-                               pingrp = "mis5_pins";
+                               abilis,function = "mis5";
                        };
                        pctl_gpio_e: pctl-gpio-e {     /* GPIO bank E */
-                               pingrp = "gpioe_pins";
+                               abilis,function = "gpioe";
                        };
                        pctl_tsin_p5: pctl-tsin-p5 {   /* Parallel TS-in 5 */
-                               pingrp = "mip5_pins";
+                               abilis,function = "mip5";
                        };
                        /* Port 4 */
                        pctl_tsin_s6: pctl-tsin-s6 {   /* Serial TS-in 6 */
-                               pingrp = "mis6_pins";
+                               abilis,function = "mis6";
                        };
                        pctl_tsin_s7: pctl-tsin-s7 {   /* Serial TS-in 7 */
-                               pingrp = "mis7_pins";
+                               abilis,function = "mis7";
                        };
                        pctl_gpio_g: pctl-gpio-g {     /* GPIO bank G */
-                               pingrp = "gpiog_pins";
+                               abilis,function = "gpiog";
                        };
                        pctl_tsin_p7: pctl-tsin-p7 {   /* Parallel TS-in 7 */
-                               pingrp = "mip7_pins";
+                               abilis,function = "mip7";
                        };
                        /* Port 5 */
                        pctl_gpio_j: pctl-gpio-j {     /* GPIO bank J */
-                               pingrp = "gpioj_pins";
+                               abilis,function = "gpioj";
                        };
                        pctl_gpio_k: pctl-gpio-k {     /* GPIO bank K */
-                               pingrp = "gpiok_pins";
+                               abilis,function = "gpiok";
                        };
                        pctl_ciplus: pctl-ciplus {     /* CI+ interface */
-                               pingrp = "ciplus_pins";
+                               abilis,function = "ciplus";
                        };
                        pctl_mcard: pctl-mcard {       /* M-Card interface */
-                               pingrp = "mcard_pins";
+                               abilis,function = "mcard";
                        };
                        /* Port 6 */
                        pctl_tsout_p: pctl-tsout-p {   /* Parallel TS-out */
-                               pingrp = "mop_pins";
+                               abilis,function = "mop";
                        };
                        pctl_tsout_s0: pctl-tsout-s0 { /* Serial TS-out 0 */
-                               pingrp = "mos0_pins";
+                               abilis,function = "mos0";
                        };
                        pctl_tsout_s1: pctl-tsout-s1 { /* Serial TS-out 1 */
-                               pingrp = "mos1_pins";
+                               abilis,function = "mos1";
                        };
                        pctl_tsout_s2: pctl-tsout-s2 { /* Serial TS-out 2 */
-                               pingrp = "mos2_pins";
+                               abilis,function = "mos2";
                        };
                        pctl_tsout_s3: pctl-tsout-s3 { /* Serial TS-out 3 */
-                               pingrp = "mos3_pins";
+                               abilis,function = "mos3";
                        };
                        /* Port 7 */
                        pctl_uart0: pctl-uart0 {       /* UART 0 */
-                               pingrp = "uart0_pins";
+                               abilis,function = "uart0";
                        };
                        pctl_uart1: pctl-uart1 {       /* UART 1 */
-                               pingrp = "uart1_pins";
+                               abilis,function = "uart1";
                        };
                        pctl_gpio_l: pctl-gpio-l {     /* GPIO bank L */
-                               pingrp = "gpiol_pins";
+                               abilis,function = "gpiol";
                        };
                        pctl_gpio_m: pctl-gpio-m {     /* GPIO bank M */
-                               pingrp = "gpiom_pins";
+                               abilis,function = "gpiom";
                        };
                        /* Port 8 */
                        pctl_spi3: pctl-spi3 {
-                               pingrp = "spi3_pins";
+                               abilis,function = "spi3";
                        };
                        /* Port 9 */
                        pctl_spi1: pctl-spi1 {
-                               pingrp = "spi1_pins";
+                               abilis,function = "spi1";
                        };
                        pctl_gpio_n: pctl-gpio-n {
-                               pingrp = "gpion_pins";
+                               abilis,function = "gpion";
                        };
                        /* Unmuxed GPIOs */
                        pctl_gpio_b: pctl-gpio-b {
-                               pingrp = "gpiob_pins";
+                               abilis,function = "gpiob";
                        };
                        pctl_gpio_d: pctl-gpio-d {
-                               pingrp = "gpiod_pins";
+                               abilis,function = "gpiod";
                        };
                        pctl_gpio_f: pctl-gpio-f {
-                               pingrp = "gpiof_pins";
+                               abilis,function = "gpiof";
                        };
                        pctl_gpio_h: pctl-gpio-h {
-                               pingrp = "gpioh_pins";
+                               abilis,function = "gpioh";
                        };
                        pctl_gpio_i: pctl-gpio-i {
-                               pingrp = "gpioi_pins";
+                               abilis,function = "gpioi";
                        };
                };
 
                        interrupts = <27 2>;
                        reg = <0xFF140000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <0>;
-                       gpio-pins = <&pctl_gpio_a>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <3>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioa";
                };
                gpiob: gpio@FF141000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF141000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <3>;
-                       gpio-pins = <&pctl_gpio_b>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <2>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiob";
                };
                gpioc: gpio@FF142000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF142000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <5>;
-                       gpio-pins = <&pctl_gpio_c>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <3>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioc";
                };
                gpiod: gpio@FF143000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF143000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <8>;
-                       gpio-pins = <&pctl_gpio_d>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <2>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiod";
                };
                gpioe: gpio@FF144000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF144000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <10>;
-                       gpio-pins = <&pctl_gpio_e>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <3>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioe";
                };
                gpiof: gpio@FF145000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF145000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <13>;
-                       gpio-pins = <&pctl_gpio_f>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <2>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiof";
                };
                gpiog: gpio@FF146000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF146000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <15>;
-                       gpio-pins = <&pctl_gpio_g>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <3>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiog";
                };
                gpioh: gpio@FF147000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF147000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <18>;
-                       gpio-pins = <&pctl_gpio_h>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <2>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioh";
                };
                gpioi: gpio@FF148000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF148000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <20>;
-                       gpio-pins = <&pctl_gpio_i>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <12>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioi";
                };
                gpioj: gpio@FF149000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF149000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <32>;
-                       gpio-pins = <&pctl_gpio_j>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <32>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioj";
                };
                gpiok: gpio@FF14a000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF14A000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <64>;
-                       gpio-pins = <&pctl_gpio_k>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <22>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiok";
                };
                gpiol: gpio@FF14b000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF14B000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <86>;
-                       gpio-pins = <&pctl_gpio_l>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <4>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiol";
                };
                gpiom: gpio@FF14c000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF14C000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <90>;
-                       gpio-pins = <&pctl_gpio_m>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <4>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiom";
                };
                gpion: gpio@FF14d000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF14D000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <94>;
-                       gpio-pins = <&pctl_gpio_n>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <5>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpion";
                };
        };
 };
index ebc313a..3dd6ed9 100644 (file)
                        compatible = "gpio-leds";
                        power {
                                label = "Power";
-                               gpios = <&gpioi 0>;
+                               gpios = <&gpioi 0 0>;
                                linux,default-trigger = "default-on";
                        };
                        heartbeat {
                                label = "Heartbeat";
-                               gpios = <&gpioi 1>;
+                               gpios = <&gpioi 1 0>;
                                linux,default-trigger = "heartbeat";
                        };
                        led2 {
                                label = "LED2";
-                               gpios = <&gpioi 2>;
+                               gpios = <&gpioi 2 0>;
                                default-state = "off";
                        };
                        led3 {
                                label = "LED3";
-                               gpios = <&gpioi 3>;
+                               gpios = <&gpioi 3 0>;
                                default-state = "off";
                        };
                        led4 {
                                label = "LED4";
-                               gpios = <&gpioi 4>;
+                               gpios = <&gpioi 4 0>;
                                default-state = "off";
                        };
                        led5 {
                                label = "LED5";
-                               gpios = <&gpioi 5>;
+                               gpios = <&gpioi 5 0>;
                                default-state = "off";
                        };
                        led6 {
                                label = "LED6";
-                               gpios = <&gpioi 6>;
+                               gpios = <&gpioi 6 0>;
                                default-state = "off";
                        };
                        led7 {
                                label = "LED7";
-                               gpios = <&gpioi 7>;
+                               gpios = <&gpioi 7 0>;
                                default-state = "off";
                        };
                        led8 {
                                label = "LED8";
-                               gpios = <&gpioi 8>;
+                               gpios = <&gpioi 8 0>;
                                default-state = "off";
                        };
                        led9 {
                                label = "LED9";
-                               gpios = <&gpioi 9>;
+                               gpios = <&gpioi 9 0>;
                                default-state = "off";
                        };
                        led10 {
                                label = "LED10";
-                               gpios = <&gpioi 10>;
+                               gpios = <&gpioi 10 0>;
                                default-state = "off";
                        };
                        led11 {
                                label = "LED11";
-                               gpios = <&gpioi 11>;
+                               gpios = <&gpioi 11 0>;
                                default-state = "off";
                        };
                };
index da8ca79..b046722 100644 (file)
                iomux: iomux@FF10601c {
                        /* Port 1 */
                        pctl_tsin_s0: pctl-tsin-s0 {   /* Serial TS-in 0 */
-                               pingrp = "mis0_pins";
+                               abilis,function = "mis0";
                        };
                        pctl_tsin_s1: pctl-tsin-s1 {   /* Serial TS-in 1 */
-                               pingrp = "mis1_pins";
+                               abilis,function = "mis1";
                        };
                        pctl_gpio_a: pctl-gpio-a {     /* GPIO bank A */
-                               pingrp = "gpioa_pins";
+                               abilis,function = "gpioa";
                        };
                        pctl_tsin_p1: pctl-tsin-p1 {   /* Parallel TS-in 1 */
-                               pingrp = "mip1_pins";
+                               abilis,function = "mip1";
                        };
                        /* Port 2 */
                        pctl_tsin_s2: pctl-tsin-s2 {   /* Serial TS-in 2 */
-                               pingrp = "mis2_pins";
+                               abilis,function = "mis2";
                        };
                        pctl_tsin_s3: pctl-tsin-s3 {   /* Serial TS-in 3 */
-                               pingrp = "mis3_pins";
+                               abilis,function = "mis3";
                        };
                        pctl_gpio_c: pctl-gpio-c {     /* GPIO bank C */
-                               pingrp = "gpioc_pins";
+                               abilis,function = "gpioc";
                        };
                        pctl_tsin_p3: pctl-tsin-p3 {   /* Parallel TS-in 3 */
-                               pingrp = "mip3_pins";
+                               abilis,function = "mip3";
                        };
                        /* Port 3 */
                        pctl_tsin_s4: pctl-tsin-s4 {   /* Serial TS-in 4 */
-                               pingrp = "mis4_pins";
+                               abilis,function = "mis4";
                        };
                        pctl_tsin_s5: pctl-tsin-s5 {   /* Serial TS-in 5 */
-                               pingrp = "mis5_pins";
+                               abilis,function = "mis5";
                        };
                        pctl_gpio_e: pctl-gpio-e {     /* GPIO bank E */
-                               pingrp = "gpioe_pins";
+                               abilis,function = "gpioe";
                        };
                        pctl_tsin_p5: pctl-tsin-p5 {   /* Parallel TS-in 5 */
-                               pingrp = "mip5_pins";
+                               abilis,function = "mip5";
                        };
                        /* Port 4 */
                        pctl_tsin_s6: pctl-tsin-s6 {   /* Serial TS-in 6 */
-                               pingrp = "mis6_pins";
+                               abilis,function = "mis6";
                        };
                        pctl_tsin_s7: pctl-tsin-s7 {   /* Serial TS-in 7 */
-                               pingrp = "mis7_pins";
+                               abilis,function = "mis7";
                        };
                        pctl_gpio_g: pctl-gpio-g {     /* GPIO bank G */
-                               pingrp = "gpiog_pins";
+                               abilis,function = "gpiog";
                        };
                        pctl_tsin_p7: pctl-tsin-p7 {   /* Parallel TS-in 7 */
-                               pingrp = "mip7_pins";
+                               abilis,function = "mip7";
                        };
                        /* Port 5 */
                        pctl_gpio_j: pctl-gpio-j {     /* GPIO bank J */
-                               pingrp = "gpioj_pins";
+                               abilis,function = "gpioj";
                        };
                        pctl_gpio_k: pctl-gpio-k {     /* GPIO bank K */
-                               pingrp = "gpiok_pins";
+                               abilis,function = "gpiok";
                        };
                        pctl_ciplus: pctl-ciplus {     /* CI+ interface */
-                               pingrp = "ciplus_pins";
+                               abilis,function = "ciplus";
                        };
                        pctl_mcard: pctl-mcard {       /* M-Card interface */
-                               pingrp = "mcard_pins";
+                               abilis,function = "mcard";
                        };
                        pctl_stc0: pctl-stc0 {         /* Smart card I/F 0 */
-                               pingrp = "stc0_pins";
+                               abilis,function = "stc0";
                        };
                        pctl_stc1: pctl-stc1 {         /* Smart card I/F 1 */
-                               pingrp = "stc1_pins";
+                               abilis,function = "stc1";
                        };
                        /* Port 6 */
                        pctl_tsout_p: pctl-tsout-p {   /* Parallel TS-out */
-                               pingrp = "mop_pins";
+                               abilis,function = "mop";
                        };
                        pctl_tsout_s0: pctl-tsout-s0 { /* Serial TS-out 0 */
-                               pingrp = "mos0_pins";
+                               abilis,function = "mos0";
                        };
                        pctl_tsout_s1: pctl-tsout-s1 { /* Serial TS-out 1 */
-                               pingrp = "mos1_pins";
+                               abilis,function = "mos1";
                        };
                        pctl_tsout_s2: pctl-tsout-s2 { /* Serial TS-out 2 */
-                               pingrp = "mos2_pins";
+                               abilis,function = "mos2";
                        };
                        pctl_tsout_s3: pctl-tsout-s3 { /* Serial TS-out 3 */
-                               pingrp = "mos3_pins";
+                               abilis,function = "mos3";
                        };
                        /* Port 7 */
                        pctl_uart0: pctl-uart0 {       /* UART 0 */
-                               pingrp = "uart0_pins";
+                               abilis,function = "uart0";
                        };
                        pctl_uart1: pctl-uart1 {       /* UART 1 */
-                               pingrp = "uart1_pins";
+                               abilis,function = "uart1";
                        };
                        pctl_gpio_l: pctl-gpio-l {     /* GPIO bank L */
-                               pingrp = "gpiol_pins";
+                               abilis,function = "gpiol";
                        };
                        pctl_gpio_m: pctl-gpio-m {     /* GPIO bank M */
-                               pingrp = "gpiom_pins";
+                               abilis,function = "gpiom";
                        };
                        /* Port 8 */
                        pctl_spi3: pctl-spi3 {
-                               pingrp = "spi3_pins";
+                               abilis,function = "spi3";
                        };
                        pctl_jtag: pctl-jtag {
-                               pingrp = "jtag_pins";
+                               abilis,function = "jtag";
                        };
                        /* Port 9 */
                        pctl_spi1: pctl-spi1 {
-                               pingrp = "spi1_pins";
+                               abilis,function = "spi1";
                        };
                        pctl_gpio_n: pctl-gpio-n {
-                               pingrp = "gpion_pins";
+                               abilis,function = "gpion";
                        };
                        /* Unmuxed GPIOs */
                        pctl_gpio_b: pctl-gpio-b {
-                               pingrp = "gpiob_pins";
+                               abilis,function = "gpiob";
                        };
                        pctl_gpio_d: pctl-gpio-d {
-                               pingrp = "gpiod_pins";
+                               abilis,function = "gpiod";
                        };
                        pctl_gpio_f: pctl-gpio-f {
-                               pingrp = "gpiof_pins";
+                               abilis,function = "gpiof";
                        };
                        pctl_gpio_h: pctl-gpio-h {
-                               pingrp = "gpioh_pins";
+                               abilis,function = "gpioh";
                        };
                        pctl_gpio_i: pctl-gpio-i {
-                               pingrp = "gpioi_pins";
+                               abilis,function = "gpioi";
                        };
                };
 
                        interrupts = <27 2>;
                        reg = <0xFF140000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <0>;
-                       gpio-pins = <&pctl_gpio_a>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <3>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioa";
                };
                gpiob: gpio@FF141000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF141000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <3>;
-                       gpio-pins = <&pctl_gpio_b>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <2>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiob";
                };
                gpioc: gpio@FF142000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF142000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <5>;
-                       gpio-pins = <&pctl_gpio_c>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <3>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioc";
                };
                gpiod: gpio@FF143000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF143000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <8>;
-                       gpio-pins = <&pctl_gpio_d>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <2>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiod";
                };
                gpioe: gpio@FF144000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF144000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <10>;
-                       gpio-pins = <&pctl_gpio_e>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <3>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioe";
                };
                gpiof: gpio@FF145000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF145000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <13>;
-                       gpio-pins = <&pctl_gpio_f>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <2>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiof";
                };
                gpiog: gpio@FF146000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF146000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <15>;
-                       gpio-pins = <&pctl_gpio_g>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <3>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiog";
                };
                gpioh: gpio@FF147000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF147000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <18>;
-                       gpio-pins = <&pctl_gpio_h>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <2>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioh";
                };
                gpioi: gpio@FF148000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF148000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <20>;
-                       gpio-pins = <&pctl_gpio_i>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <12>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioi";
                };
                gpioj: gpio@FF149000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF149000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <32>;
-                       gpio-pins = <&pctl_gpio_j>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <32>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpioj";
                };
                gpiok: gpio@FF14a000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF14A000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <64>;
-                       gpio-pins = <&pctl_gpio_k>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <22>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiok";
                };
                gpiol: gpio@FF14b000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF14B000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <86>;
-                       gpio-pins = <&pctl_gpio_l>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <4>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiol";
                };
                gpiom: gpio@FF14c000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF14C000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <90>;
-                       gpio-pins = <&pctl_gpio_m>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <4>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpiom";
                };
                gpion: gpio@FF14d000 {
                        compatible = "abilis,tb10x-gpio";
                        interrupts = <27 2>;
                        reg = <0xFF14D000 0x1000>;
                        gpio-controller;
-                       #gpio-cells = <1>;
-                       gpio-base  = <94>;
-                       gpio-pins = <&pctl_gpio_n>;
+                       #gpio-cells = <2>;
+                       abilis,ngpio = <5>;
+                       gpio-ranges = <&iomux 0 0 0>;
+                       gpio-ranges-group-names = "gpion";
                };
        };
 };
index b204657..1cf51c2 100644 (file)
                        compatible = "gpio-leds";
                        power {
                                label = "Power";
-                               gpios = <&gpioi 0>;
+                               gpios = <&gpioi 0 0>;
                                linux,default-trigger = "default-on";
                        };
                        heartbeat {
                                label = "Heartbeat";
-                               gpios = <&gpioi 1>;
+                               gpios = <&gpioi 1 0>;
                                linux,default-trigger = "heartbeat";
                        };
                        led2 {
                                label = "LED2";
-                               gpios = <&gpioi 2>;
+                               gpios = <&gpioi 2 0>;
                                default-state = "off";
                        };
                        led3 {
                                label = "LED3";
-                               gpios = <&gpioi 3>;
+                               gpios = <&gpioi 3 0>;
                                default-state = "off";
                        };
                        led4 {
                                label = "LED4";
-                               gpios = <&gpioi 4>;
+                               gpios = <&gpioi 4 0>;
                                default-state = "off";
                        };
                        led5 {
                                label = "LED5";
-                               gpios = <&gpioi 5>;
+                               gpios = <&gpioi 5 0>;
                                default-state = "off";
                        };
                        led6 {
                                label = "LED6";
-                               gpios = <&gpioi 6>;
+                               gpios = <&gpioi 6 0>;
                                default-state = "off";
                        };
                        led7 {
                                label = "LED7";
-                               gpios = <&gpioi 7>;
+                               gpios = <&gpioi 7 0>;
                                default-state = "off";
                        };
                        led8 {
                                label = "LED8";
-                               gpios = <&gpioi 8>;
+                               gpios = <&gpioi 8 0>;
                                default-state = "off";
                        };
                        led9 {
                                label = "LED9";
-                               gpios = <&gpioi 9>;
+                               gpios = <&gpioi 9 0>;
                                default-state = "off";
                        };
                        led10 {
                                label = "LED10";
-                               gpios = <&gpioi 10>;
+                               gpios = <&gpioi 10 0>;
                                default-state = "off";
                        };
                        led11 {
                                label = "LED11";
-                               gpios = <&gpioi 11>;
+                               gpios = <&gpioi 11 0>;
                                default-state = "off";
                        };
                };
index edf56f4..a098d7c 100644 (file)
@@ -62,9 +62,8 @@
                };
 
                iomux: iomux@FF10601c {
-                       #address-cells = <1>;
-                       #size-cells = <1>;
                        compatible = "abilis,tb10x-iomux";
+                       #gpio-range-cells = <3>;
                        reg = <0xFF10601c 0x4>;
                };
 
index 4fb2d6f..bcf662d 100644 (file)
@@ -67,5 +67,9 @@
                                reg = <1>;
                        };
                };
+
+               arcpmu0: pmu {
+                       compatible = "snps,arc700-pmu";
+               };
        };
 };
diff --git a/arch/arc/configs/fpga_noramfs_defconfig b/arch/arc/configs/fpga_noramfs_defconfig
new file mode 100644 (file)
index 0000000..5276a52
--- /dev/null
@@ -0,0 +1,64 @@
+CONFIG_CROSS_COMPILE="arc-linux-uclibc-"
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_DEFAULT_HOSTNAME="ARCLinux"
+# CONFIG_SWAP is not set
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_COMPAT_BRK is not set
+CONFIG_KPROBES=y
+CONFIG_MODULES=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARC_PLAT_FPGA_LEGACY=y
+CONFIG_ARC_BOARD_ML509=y
+# CONFIG_ARC_HAS_RTSC is not set
+CONFIG_ARC_BUILTIN_DTB_NAME="angel4"
+CONFIG_PREEMPT=y
+# CONFIG_COMPACTION is not set
+# CONFIG_CROSS_MEMORY_ATTACH is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_UNIX_DIAG=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IPV6 is not set
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+# CONFIG_BLK_DEV is not set
+CONFIG_NETDEVICES=y
+CONFIG_ARC_EMAC=y
+CONFIG_LXT_PHY=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX 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_ARC=y
+CONFIG_SERIAL_ARC_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_HID is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_XZ_DEC=y
index 115ad96..cbf755e 100644 (file)
@@ -1,5 +1,7 @@
 /*
- * Copyright (C) 2011-2012 Synopsys, Inc. (www.synopsys.com)
+ * Linux performance counter support for ARC
+ *
+ * Copyright (C) 2011-2013 Synopsys, Inc. (www.synopsys.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
 #ifndef __ASM_PERF_EVENT_H
 #define __ASM_PERF_EVENT_H
 
+/* real maximum varies per CPU, this is the maximum supported by the driver */
+#define ARC_PMU_MAX_HWEVENTS   64
+
+#define ARC_REG_CC_BUILD       0xF6
+#define ARC_REG_CC_INDEX       0x240
+#define ARC_REG_CC_NAME0       0x241
+#define ARC_REG_CC_NAME1       0x242
+
+#define ARC_REG_PCT_BUILD      0xF5
+#define ARC_REG_PCT_COUNTL     0x250
+#define ARC_REG_PCT_COUNTH     0x251
+#define ARC_REG_PCT_SNAPL      0x252
+#define ARC_REG_PCT_SNAPH      0x253
+#define ARC_REG_PCT_CONFIG     0x254
+#define ARC_REG_PCT_CONTROL    0x255
+#define ARC_REG_PCT_INDEX      0x256
+
+#define ARC_REG_PCT_CONTROL_CC (1 << 16)       /* clear counts */
+#define ARC_REG_PCT_CONTROL_SN (1 << 17)       /* snapshot */
+
+struct arc_reg_pct_build {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+       unsigned int m:8, c:8, r:6, s:2, v:8;
+#else
+       unsigned int v:8, s:2, r:6, c:8, m:8;
+#endif
+};
+
+struct arc_reg_cc_build {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+       unsigned int c:16, r:8, v:8;
+#else
+       unsigned int v:8, r:8, c:16;
+#endif
+};
+
+#define PERF_COUNT_ARC_DCLM    (PERF_COUNT_HW_MAX + 0)
+#define PERF_COUNT_ARC_DCSM    (PERF_COUNT_HW_MAX + 1)
+#define PERF_COUNT_ARC_ICM     (PERF_COUNT_HW_MAX + 2)
+#define PERF_COUNT_ARC_BPOK    (PERF_COUNT_HW_MAX + 3)
+#define PERF_COUNT_ARC_EDTLB   (PERF_COUNT_HW_MAX + 4)
+#define PERF_COUNT_ARC_EITLB   (PERF_COUNT_HW_MAX + 5)
+#define PERF_COUNT_ARC_HW_MAX  (PERF_COUNT_HW_MAX + 6)
+
+/*
+ * The "generalized" performance events seem to really be a copy
+ * of the available events on x86 processors; the mapping to ARC
+ * events is not always possible 1-to-1. Fortunately, there doesn't
+ * seem to be an exact definition for these events, so we can cheat
+ * a bit where necessary.
+ *
+ * In particular, the following PERF events may behave a bit differently
+ * compared to other architectures:
+ *
+ * PERF_COUNT_HW_CPU_CYCLES
+ *     Cycles not in halted state
+ *
+ * PERF_COUNT_HW_REF_CPU_CYCLES
+ *     Reference cycles not in halted state, same as PERF_COUNT_HW_CPU_CYCLES
+ *     for now as we don't do Dynamic Voltage/Frequency Scaling (yet)
+ *
+ * PERF_COUNT_HW_BUS_CYCLES
+ *     Unclear what this means, Intel uses 0x013c, which according to
+ *     their datasheet means "unhalted reference cycles". It sounds similar
+ *     to PERF_COUNT_HW_REF_CPU_CYCLES, and we use the same counter for it.
+ *
+ * PERF_COUNT_HW_STALLED_CYCLES_BACKEND
+ * PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
+ *     The ARC 700 can either measure stalls per pipeline stage, or all stalls
+ *     combined; for now we assign all stalls to STALLED_CYCLES_BACKEND
+ *     and all pipeline flushes (e.g. caused by mispredicts, etc.) to
+ *     STALLED_CYCLES_FRONTEND.
+ *
+ *     We could start multiple performance counters and combine everything
+ *     afterwards, but that makes it complicated.
+ *
+ *     Note that I$ cache misses aren't counted by either of the two!
+ */
+
+static const char * const arc_pmu_ev_hw_map[] = {
+       [PERF_COUNT_HW_CPU_CYCLES] = "crun",
+       [PERF_COUNT_HW_REF_CPU_CYCLES] = "crun",
+       [PERF_COUNT_HW_BUS_CYCLES] = "crun",
+       [PERF_COUNT_HW_INSTRUCTIONS] = "iall",
+       [PERF_COUNT_HW_BRANCH_MISSES] = "bpfail",
+       [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = "ijmp",
+       [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = "bflush",
+       [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = "bstall",
+       [PERF_COUNT_ARC_DCLM] = "dclm",
+       [PERF_COUNT_ARC_DCSM] = "dcsm",
+       [PERF_COUNT_ARC_ICM] = "icm",
+       [PERF_COUNT_ARC_BPOK] = "bpok",
+       [PERF_COUNT_ARC_EDTLB] = "edtlb",
+       [PERF_COUNT_ARC_EITLB] = "eitlb",
+};
+
+#define C(_x)                  PERF_COUNT_HW_CACHE_##_x
+#define CACHE_OP_UNSUPPORTED   0xffff
+
+static const unsigned arc_pmu_cache_map[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
+       [C(L1D)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = PERF_COUNT_ARC_DCLM,
+               },
+               [C(OP_WRITE)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = PERF_COUNT_ARC_DCSM,
+               },
+               [C(OP_PREFETCH)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+       },
+       [C(L1I)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = PERF_COUNT_ARC_ICM,
+               },
+               [C(OP_WRITE)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+               [C(OP_PREFETCH)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+       },
+       [C(LL)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+               [C(OP_WRITE)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+               [C(OP_PREFETCH)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+       },
+       [C(DTLB)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = PERF_COUNT_ARC_EDTLB,
+               },
+               [C(OP_WRITE)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+               [C(OP_PREFETCH)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+       },
+       [C(ITLB)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = PERF_COUNT_ARC_EITLB,
+               },
+               [C(OP_WRITE)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+               [C(OP_PREFETCH)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+       },
+       [C(BPU)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)] = PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
+                       [C(RESULT_MISS)]        = PERF_COUNT_HW_BRANCH_MISSES,
+               },
+               [C(OP_WRITE)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+               [C(OP_PREFETCH)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+       },
+       [C(NODE)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+               [C(OP_WRITE)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+               [C(OP_PREFETCH)] = {
+                       [C(RESULT_ACCESS)]      = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)]        = CACHE_OP_UNSUPPORTED,
+               },
+       },
+};
+
 #endif /* __ASM_PERF_EVENT_H */
index 36a9f20..81208bf 100644 (file)
@@ -105,11 +105,16 @@ static inline pgtable_t
 pte_alloc_one(struct mm_struct *mm, unsigned long address)
 {
        pgtable_t pte_pg;
+       struct page *page;
 
        pte_pg = __get_free_pages(GFP_KERNEL | __GFP_REPEAT, __get_order_pte());
-       if (pte_pg) {
-               memzero((void *)pte_pg, PTRS_PER_PTE * 4);
-               pgtable_page_ctor(virt_to_page(pte_pg));
+       if (!pte_pg)
+               return 0;
+       memzero((void *)pte_pg, PTRS_PER_PTE * 4);
+       page = virt_to_page(pte_pg);
+       if (!pgtable_page_ctor(page)) {
+               __free_page(page);
+               return 0;
        }
 
        return pte_pg;
index 2d50a4c..45be216 100644 (file)
@@ -80,8 +80,6 @@ static inline __attribute_const__ struct thread_info *current_thread_info(void)
 
 #endif /* !__ASSEMBLY__ */
 
-#define PREEMPT_ACTIVE      0x10000000
-
 /*
  * thread information flags
  * - these are process state flags that various assembly files may need to
index c242ef0..8004b4f 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_KPROBES)                 += kprobes.o
 obj-$(CONFIG_ARC_MISALIGN_ACCESS)      += unaligned.o
 obj-$(CONFIG_KGDB)                     += kgdb.o
 obj-$(CONFIG_ARC_METAWARE_HLINK)       += arc_hostlink.o
+obj-$(CONFIG_PERF_EVENTS)              += perf_event.o
 
 obj-$(CONFIG_ARC_FPU_SAVE_RESTORE)     += fpu.o
 CFLAGS_fpu.o   += -mdpfp
index eb1c2ee..42b0504 100644 (file)
@@ -327,7 +327,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned long trapnr)
                 */
 
                /* We increment the nmissed count for accounting,
-                * we can also use npre/npostfault count for accouting
+                * we can also use npre/npostfault count for accounting
                 * these specific fault cases.
                 */
                kprobes_inc_nmissed_count(cur);
diff --git a/arch/arc/kernel/perf_event.c b/arch/arc/kernel/perf_event.c
new file mode 100644 (file)
index 0000000..e46d81f
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * Linux performance counter support for ARC700 series
+ *
+ * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This code is inspired by the perf support of various other architectures.
+ *
+ * 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/errno.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+#include <asm/arcregs.h>
+
+struct arc_pmu {
+       struct pmu      pmu;
+       int             counter_size;   /* in bits */
+       int             n_counters;
+       unsigned long   used_mask[BITS_TO_LONGS(ARC_PMU_MAX_HWEVENTS)];
+       int             ev_hw_idx[PERF_COUNT_ARC_HW_MAX];
+};
+
+/* read counter #idx; note that counter# != event# on ARC! */
+static uint64_t arc_pmu_read_counter(int idx)
+{
+       uint32_t tmp;
+       uint64_t result;
+
+       /*
+        * ARC supports making 'snapshots' of the counters, so we don't
+        * need to care about counters wrapping to 0 underneath our feet
+        */
+       write_aux_reg(ARC_REG_PCT_INDEX, idx);
+       tmp = read_aux_reg(ARC_REG_PCT_CONTROL);
+       write_aux_reg(ARC_REG_PCT_CONTROL, tmp | ARC_REG_PCT_CONTROL_SN);
+       result = (uint64_t) (read_aux_reg(ARC_REG_PCT_SNAPH)) << 32;
+       result |= read_aux_reg(ARC_REG_PCT_SNAPL);
+
+       return result;
+}
+
+static void arc_perf_event_update(struct perf_event *event,
+                                 struct hw_perf_event *hwc, int idx)
+{
+       struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu);
+       uint64_t prev_raw_count, new_raw_count;
+       int64_t delta;
+
+       do {
+               prev_raw_count = local64_read(&hwc->prev_count);
+               new_raw_count = arc_pmu_read_counter(idx);
+       } while (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+                                new_raw_count) != prev_raw_count);
+
+       delta = (new_raw_count - prev_raw_count) &
+               ((1ULL << arc_pmu->counter_size) - 1ULL);
+
+       local64_add(delta, &event->count);
+       local64_sub(delta, &hwc->period_left);
+}
+
+static void arc_pmu_read(struct perf_event *event)
+{
+       arc_perf_event_update(event, &event->hw, event->hw.idx);
+}
+
+static int arc_pmu_cache_event(u64 config)
+{
+       unsigned int cache_type, cache_op, cache_result;
+       int ret;
+
+       cache_type      = (config >>  0) & 0xff;
+       cache_op        = (config >>  8) & 0xff;
+       cache_result    = (config >> 16) & 0xff;
+       if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
+               return -EINVAL;
+       if (cache_type >= PERF_COUNT_HW_CACHE_OP_MAX)
+               return -EINVAL;
+       if (cache_type >= PERF_COUNT_HW_CACHE_RESULT_MAX)
+               return -EINVAL;
+
+       ret = arc_pmu_cache_map[cache_type][cache_op][cache_result];
+
+       if (ret == CACHE_OP_UNSUPPORTED)
+               return -ENOENT;
+
+       return ret;
+}
+
+/* initializes hw_perf_event structure if event is supported */
+static int arc_pmu_event_init(struct perf_event *event)
+{
+       struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu);
+       struct hw_perf_event *hwc = &event->hw;
+       int ret;
+
+       /* ARC 700 PMU does not support sampling events */
+       if (is_sampling_event(event))
+               return -ENOENT;
+
+       switch (event->attr.type) {
+       case PERF_TYPE_HARDWARE:
+               if (event->attr.config >= PERF_COUNT_HW_MAX)
+                       return -ENOENT;
+               if (arc_pmu->ev_hw_idx[event->attr.config] < 0)
+                       return -ENOENT;
+               hwc->config = arc_pmu->ev_hw_idx[event->attr.config];
+               pr_debug("initializing event %d with cfg %d\n",
+                        (int) event->attr.config, (int) hwc->config);
+               return 0;
+       case PERF_TYPE_HW_CACHE:
+               ret = arc_pmu_cache_event(event->attr.config);
+               if (ret < 0)
+                       return ret;
+               hwc->config = arc_pmu->ev_hw_idx[ret];
+               return 0;
+       default:
+               return -ENOENT;
+       }
+}
+
+/* starts all counters */
+static void arc_pmu_enable(struct pmu *pmu)
+{
+       uint32_t tmp;
+       tmp = read_aux_reg(ARC_REG_PCT_CONTROL);
+       write_aux_reg(ARC_REG_PCT_CONTROL, (tmp & 0xffff0000) | 0x1);
+}
+
+/* stops all counters */
+static void arc_pmu_disable(struct pmu *pmu)
+{
+       uint32_t tmp;
+       tmp = read_aux_reg(ARC_REG_PCT_CONTROL);
+       write_aux_reg(ARC_REG_PCT_CONTROL, (tmp & 0xffff0000) | 0x0);
+}
+
+/*
+ * Assigns hardware counter to hardware condition.
+ * Note that there is no separate start/stop mechanism;
+ * stopping is achieved by assigning the 'never' condition
+ */
+static void arc_pmu_start(struct perf_event *event, int flags)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       int idx = hwc->idx;
+
+       if (WARN_ON_ONCE(idx == -1))
+               return;
+
+       if (flags & PERF_EF_RELOAD)
+               WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE));
+
+       event->hw.state = 0;
+
+       /* enable ARC pmu here */
+       write_aux_reg(ARC_REG_PCT_INDEX, idx);
+       write_aux_reg(ARC_REG_PCT_CONFIG, hwc->config);
+}
+
+static void arc_pmu_stop(struct perf_event *event, int flags)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       int idx = hwc->idx;
+
+       if (!(event->hw.state & PERF_HES_STOPPED)) {
+               /* stop ARC pmu here */
+               write_aux_reg(ARC_REG_PCT_INDEX, idx);
+
+               /* condition code #0 is always "never" */
+               write_aux_reg(ARC_REG_PCT_CONFIG, 0);
+
+               event->hw.state |= PERF_HES_STOPPED;
+       }
+
+       if ((flags & PERF_EF_UPDATE) &&
+           !(event->hw.state & PERF_HES_UPTODATE)) {
+               arc_perf_event_update(event, &event->hw, idx);
+               event->hw.state |= PERF_HES_UPTODATE;
+       }
+}
+
+static void arc_pmu_del(struct perf_event *event, int flags)
+{
+       struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu);
+
+       arc_pmu_stop(event, PERF_EF_UPDATE);
+       __clear_bit(event->hw.idx, arc_pmu->used_mask);
+
+       perf_event_update_userpage(event);
+}
+
+/* allocate hardware counter and optionally start counting */
+static int arc_pmu_add(struct perf_event *event, int flags)
+{
+       struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu);
+       struct hw_perf_event *hwc = &event->hw;
+       int idx = hwc->idx;
+
+       if (__test_and_set_bit(idx, arc_pmu->used_mask)) {
+               idx = find_first_zero_bit(arc_pmu->used_mask,
+                                         arc_pmu->n_counters);
+               if (idx == arc_pmu->n_counters)
+                       return -EAGAIN;
+
+               __set_bit(idx, arc_pmu->used_mask);
+               hwc->idx = idx;
+       }
+
+       write_aux_reg(ARC_REG_PCT_INDEX, idx);
+       write_aux_reg(ARC_REG_PCT_CONFIG, 0);
+       write_aux_reg(ARC_REG_PCT_COUNTL, 0);
+       write_aux_reg(ARC_REG_PCT_COUNTH, 0);
+       local64_set(&hwc->prev_count, 0);
+
+       hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+       if (flags & PERF_EF_START)
+               arc_pmu_start(event, PERF_EF_RELOAD);
+
+       perf_event_update_userpage(event);
+
+       return 0;
+}
+
+static int arc_pmu_device_probe(struct platform_device *pdev)
+{
+       struct arc_pmu *arc_pmu;
+       struct arc_reg_pct_build pct_bcr;
+       struct arc_reg_cc_build cc_bcr;
+       int i, j, ret;
+
+       union cc_name {
+               struct {
+                       uint32_t word0, word1;
+                       char sentinel;
+               } indiv;
+               char str[9];
+       } cc_name;
+
+
+       READ_BCR(ARC_REG_PCT_BUILD, pct_bcr);
+       if (!pct_bcr.v) {
+               pr_err("This core does not have performance counters!\n");
+               return -ENODEV;
+       }
+
+       arc_pmu = devm_kzalloc(&pdev->dev, sizeof(struct arc_pmu),
+                              GFP_KERNEL);
+       if (!arc_pmu)
+               return -ENOMEM;
+
+       arc_pmu->n_counters = pct_bcr.c;
+       BUG_ON(arc_pmu->n_counters > ARC_PMU_MAX_HWEVENTS);
+
+       arc_pmu->counter_size = 32 + (pct_bcr.s << 4);
+       pr_info("ARC PMU found with %d counters of size %d bits\n",
+               arc_pmu->n_counters, arc_pmu->counter_size);
+
+       READ_BCR(ARC_REG_CC_BUILD, cc_bcr);
+
+       if (!cc_bcr.v)
+               pr_err("Strange! Performance counters exist, but no countable conditions?\n");
+
+       pr_info("ARC PMU has %d countable conditions\n", cc_bcr.c);
+
+       cc_name.str[8] = 0;
+       for (i = 0; i < PERF_COUNT_HW_MAX; i++)
+               arc_pmu->ev_hw_idx[i] = -1;
+
+       for (j = 0; j < cc_bcr.c; j++) {
+               write_aux_reg(ARC_REG_CC_INDEX, j);
+               cc_name.indiv.word0 = read_aux_reg(ARC_REG_CC_NAME0);
+               cc_name.indiv.word1 = read_aux_reg(ARC_REG_CC_NAME1);
+               for (i = 0; i < ARRAY_SIZE(arc_pmu_ev_hw_map); i++) {
+                       if (arc_pmu_ev_hw_map[i] &&
+                           !strcmp(arc_pmu_ev_hw_map[i], cc_name.str) &&
+                           strlen(arc_pmu_ev_hw_map[i])) {
+                               pr_debug("mapping %d to idx %d with name %s\n",
+                                        i, j, cc_name.str);
+                               arc_pmu->ev_hw_idx[i] = j;
+                       }
+               }
+       }
+
+       arc_pmu->pmu = (struct pmu) {
+               .pmu_enable     = arc_pmu_enable,
+               .pmu_disable    = arc_pmu_disable,
+               .event_init     = arc_pmu_event_init,
+               .add            = arc_pmu_add,
+               .del            = arc_pmu_del,
+               .start          = arc_pmu_start,
+               .stop           = arc_pmu_stop,
+               .read           = arc_pmu_read,
+       };
+
+       ret = perf_pmu_register(&arc_pmu->pmu, pdev->name, PERF_TYPE_RAW);
+
+       return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id arc_pmu_match[] = {
+       { .compatible = "snps,arc700-pmu" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, arc_pmu_match);
+#endif
+
+static struct platform_driver arc_pmu_driver = {
+       .driver = {
+               .name           = "arc700-pmu",
+               .of_match_table = of_match_ptr(arc_pmu_match),
+       },
+       .probe          = arc_pmu_device_probe,
+};
+
+module_platform_driver(arc_pmu_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mischa Jonker <mjonker@synopsys.com>");
+MODULE_DESCRIPTION("ARC PMU driver");
index 1ab386b..6994c18 100644 (file)
@@ -20,8 +20,10 @@ menuconfig ARC_PLAT_TB10X
        bool "Abilis TB10x"
        select COMMON_CLK
        select PINCTRL
+       select PINCTRL_TB10X
        select PINMUX
        select ARCH_REQUIRE_GPIOLIB
+       select GPIO_TB10X
        select TB10X_IRQC
        help
          Support for platforms based on the TB10x home media gateway SOC by
index 603d661..c1f1a7e 100644 (file)
@@ -5,6 +5,7 @@ config ARM
        select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
        select ARCH_HAVE_CUSTOM_GPIO_H
+       select ARCH_MIGHT_HAVE_PC_PARPORT
        select ARCH_USE_CMPXCHG_LOCKREF
        select ARCH_WANT_IPC_PARSE_VERSION
        select BUILDTIME_EXTABLE_SORT if MMU
@@ -24,7 +25,7 @@ config ARM
        select HARDIRQS_SW_RESEND
        select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL
        select HAVE_ARCH_KGDB
-       select HAVE_ARCH_SECCOMP_FILTER
+       select HAVE_ARCH_SECCOMP_FILTER if (AEABI && !OABI_COMPAT)
        select HAVE_ARCH_TRACEHOOK
        select HAVE_BPF_JIT
        select HAVE_CONTEXT_TRACKING
@@ -1432,7 +1433,6 @@ config SMP
        depends on GENERIC_CLOCKEVENTS
        depends on HAVE_SMP
        depends on MMU || ARM_MPU
-       select USE_GENERIC_SMP_HELPERS
        help
          This enables support for systems with more than one CPU. If you have
          a system with only one CPU, like most personal computers, say N. If
@@ -1496,6 +1496,7 @@ config HAVE_ARM_ARCH_TIMER
        bool "Architected timer support"
        depends on CPU_V7
        select ARM_ARCH_TIMER
+       select GENERIC_CLOCKEVENTS
        help
          This option enables support for the ARM architected timer
 
@@ -1719,7 +1720,6 @@ config AEABI
 config OABI_COMPAT
        bool "Allow old ABI binaries to run with this kernel (EXPERIMENTAL)"
        depends on AEABI && !THUMB2_KERNEL
-       default y
        help
          This option preserves the old syscall interface along with the
          new (ARM EABI) one. It also provides a compatibility layer to
@@ -1727,11 +1727,16 @@ config OABI_COMPAT
          in memory differs between the legacy ABI and the new ARM EABI
          (only for non "thumb" binaries). This option adds a tiny
          overhead to all syscalls and produces a slightly larger kernel.
+
+         The seccomp filter system will not be available when this is
+         selected, since there is no way yet to sensibly distinguish
+         between calling conventions during filtering.
+
          If you know you'll be using only pure EABI user space then you
          can say N here. If this option is not selected and you attempt
          to execute a legacy ABI binary then the result will be
          UNPREDICTABLE (in fact it can be predicted that it won't work
-         at all). If in doubt say Y.
+         at all). If in doubt say N.
 
 config ARCH_HAS_HOLES_MEMORYMODEL
        bool
@@ -1863,6 +1868,12 @@ config CC_STACKPROTECTOR
          neutralized via a kernel panic.
          This feature requires gcc version 4.2 or above.
 
+config SWIOTLB
+       def_bool y
+
+config IOMMU_HELPER
+       def_bool SWIOTLB
+
 config XEN_DOM0
        def_bool y
        depends on XEN
@@ -1873,6 +1884,7 @@ config XEN
        depends on CPU_V7 && !CPU_V6
        depends on !GENERIC_ATOMIC64
        select ARM_PSCI
+       select SWIOTLB_XEN
        help
          Say Y if you want to run Linux in a Virtual Machine on Xen on ARM.
 
index 9874294..7e6c64e 100644 (file)
        tsc {
                ti,wires = <4>;
                ti,x-plate-resistance = <200>;
-               ti,coordiante-readouts = <5>;
+               ti,coordinate-readouts = <5>;
                ti,wire-config = <0x00 0x11 0x22 0x33>;
        };
 
index 03febf8..4718ec4 100644 (file)
                tx-num-evt = <1>;
                rx-num-evt = <1>;
 };
+
+&tscadc {
+       status = "okay";
+       tsc {
+               ti,wires = <4>;
+               ti,x-plate-resistance = <200>;
+               ti,coordinate-readouts = <5>;
+               ti,wire-config = <0x00 0x11 0x22 0x33>;
+       };
+};
index 139b40c..2ccbb57 100644 (file)
                                <1 10 0xf08>;
                };
 
+               memory-controller@fff00000 {
+                       compatible = "calxeda,ecx-2000-ddr-ctrl";
+                       reg = <0xfff00000 0x1000>;
+                       interrupts = <0 91 4>;
+               };
+
                intc: interrupt-controller@fff11000 {
                        compatible = "arm,cortex-a15-gic";
                        #interrupt-cells = <3>;
index bc22557..b90045a 100644 (file)
                        status = "disabled";
                };
 
-               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>;
index 6aad34a..ed14aea 100644 (file)
        soc {
                ranges = <0x00000000 0x00000000 0xffffffff>;
 
+               memory-controller@fff00000 {
+                       compatible = "calxeda,hb-ddr-ctrl";
+                       reg = <0xfff00000 0x1000>;
+                       interrupts = <0 91 4>;
+               };
+
                timer@fff10600 {
                        compatible = "arm,cortex-a9-twd-timer";
                        reg = <0xfff10600 0x20>;
index f4dcff3..4bcdd3a 100644 (file)
 
                        usbphy0: usbphy@0 {
                                compatible = "usb-nop-xceiv";
-                               clocks = <&clks 124>;
+                               clocks = <&clks 75>;
                                clock-names = "main_clk";
                                status = "okay";
                        };
index fb1b2ec..4217096 100644 (file)
                interrupts = <11>;
        };
 
+       charger: bci {
+               compatible = "ti,twl4030-bci";
+               interrupts = <9>, <2>;
+               bci3v1-supply = <&vusb3v1>;
+       };
+
        watchdog {
                compatible = "ti,twl4030-wdt";
        };
index 8e1a024..41bca32 100644 (file)
@@ -404,7 +404,7 @@ static irqreturn_t dma_irq_handler(int irq, void *data)
                                        BIT(slot));
                        if (edma_cc[ctlr]->intr_data[channel].callback)
                                edma_cc[ctlr]->intr_data[channel].callback(
-                                       channel, DMA_COMPLETE,
+                                       channel, EDMA_DMA_COMPLETE,
                                        edma_cc[ctlr]->intr_data[channel].data);
                }
        } while (sh_ipr);
@@ -459,7 +459,7 @@ static irqreturn_t dma_ccerr_handler(int irq, void *data)
                                                                callback) {
                                                edma_cc[ctlr]->intr_data[k].
                                                callback(k,
-                                               DMA_CC_ERROR,
+                                               EDMA_DMA_CC_ERROR,
                                                edma_cc[ctlr]->intr_data
                                                [k].data);
                                        }
index 002a1ce..23591db 100644 (file)
@@ -39,6 +39,7 @@ CONFIG_SPI=y
 CONFIG_SPI_SIRF=y
 CONFIG_SPI_SPIDEV=y
 # CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_MASS_STORAGE=m
 CONFIG_MMC=y
diff --git a/arch/arm/configs/vt8500_v6_v7_defconfig b/arch/arm/configs/vt8500_v6_v7_defconfig
new file mode 100644 (file)
index 0000000..f052017
--- /dev/null
@@ -0,0 +1,90 @@
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_ARCH_MULTI_V6=y
+CONFIG_ARCH_WM8750=y
+CONFIG_ARCH_WM8850=y
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_ARM_ERRATA_775420=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_HIGHPTE=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM_RUNTIME=y
+CONFIG_NET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_EEPROM_93CX6=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_BROADCOM 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_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+CONFIG_VIA_VELOCITY=y
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_PHYLIB=y
+CONFIG_INPUT_MATRIXKMAP=y
+CONFIG_SERIAL_VT8500=y
+CONFIG_SERIAL_VT8500_CONSOLE=y
+CONFIG_I2C=y
+CONFIG_I2C_WMT=y
+CONFIG_PINCTRL_SINGLE=y
+CONFIG_PINCTRL_WM8750=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_POWER_RESET=y
+CONFIG_MFD_SYSCON=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_USB=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_UHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_GPIO_VBUS=y
+CONFIG_USB_ULPI=y
+CONFIG_MMC=y
+CONFIG_MMC_DEBUG=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_VT8500=y
+CONFIG_DMADEVICES=y
+CONFIG_COMMON_CLK_DEBUG=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_PWM=y
+CONFIG_PWM_VT8500=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_GENERIC_PHY=y
+CONFIG_EXT4_FS=y
+CONFIG_TMPFS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_LOCKUP_DETECTOR=y
index 863cd84..e701a4d 100644 (file)
 #include <asm-generic/dma-coherent.h>
 #include <asm/memory.h>
 
+#include <xen/xen.h>
+#include <asm/xen/hypervisor.h>
+
 #define DMA_ERROR_CODE (~0)
 extern struct dma_map_ops arm_dma_ops;
 extern struct dma_map_ops arm_coherent_dma_ops;
 
-static inline struct dma_map_ops *get_dma_ops(struct device *dev)
+static inline struct dma_map_ops *__generic_dma_ops(struct device *dev)
 {
        if (dev && dev->archdata.dma_ops)
                return dev->archdata.dma_ops;
        return &arm_dma_ops;
 }
 
+static inline struct dma_map_ops *get_dma_ops(struct device *dev)
+{
+       if (xen_initial_domain())
+               return xen_dma_ops;
+       else
+               return __generic_dma_ops(dev);
+}
+
 static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops)
 {
        BUG_ON(!dev);
@@ -94,6 +105,39 @@ static inline unsigned long dma_max_pfn(struct device *dev)
 }
 #define dma_max_pfn(dev) dma_max_pfn(dev)
 
+static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
+{
+       unsigned int offset = paddr & ~PAGE_MASK;
+       return pfn_to_dma(dev, __phys_to_pfn(paddr)) + offset;
+}
+
+static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dev_addr)
+{
+       unsigned int offset = dev_addr & ~PAGE_MASK;
+       return __pfn_to_phys(dma_to_pfn(dev, dev_addr)) + offset;
+}
+
+static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+{
+       u64 limit, mask;
+
+       if (!dev->dma_mask)
+               return 0;
+
+       mask = *dev->dma_mask;
+
+       limit = (mask + 1) & ~mask;
+       if (limit && size > limit)
+               return 0;
+
+       if ((addr | (addr + size - 1)) & ~mask)
+               return 0;
+
+       return 1;
+}
+
+static inline void dma_mark_clean(void *addr, size_t size) { }
+
 /*
  * DMA errors are defined by all-bits-set in the DMA address.
  */
index 9b28f12..240b29e 100644 (file)
@@ -393,36 +393,6 @@ static inline int iop_chan_zero_sum_slot_count(size_t len, int src_cnt,
        return slot_cnt;
 }
 
-static inline int iop_desc_is_pq(struct iop_adma_desc_slot *desc)
-{
-       return 0;
-}
-
-static inline u32 iop_desc_get_dest_addr(struct iop_adma_desc_slot *desc,
-                                       struct iop_adma_chan *chan)
-{
-       union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, };
-
-       switch (chan->device->id) {
-       case DMA0_ID:
-       case DMA1_ID:
-               return hw_desc.dma->dest_addr;
-       case AAU_ID:
-               return hw_desc.aau->dest_addr;
-       default:
-               BUG();
-       }
-       return 0;
-}
-
-
-static inline u32 iop_desc_get_qdest_addr(struct iop_adma_desc_slot *desc,
-                                         struct iop_adma_chan *chan)
-{
-       BUG();
-       return 0;
-}
-
 static inline u32 iop_desc_get_byte_count(struct iop_adma_desc_slot *desc,
                                        struct iop_adma_chan *chan)
 {
index 122f86d..250760e 100644 (file)
@@ -82,8 +82,6 @@ struct iop_adma_chan {
  * @slot_cnt: total slots used in an transaction (group of operations)
  * @slots_per_op: number of slots per operation
  * @idx: pool index
- * @unmap_src_cnt: number of xor sources
- * @unmap_len: transaction bytecount
  * @tx_list: list of descriptors that are associated with one operation
  * @async_tx: support for the async_tx api
  * @group_list: list of slots that make up a multi-descriptor transaction
@@ -99,8 +97,6 @@ struct iop_adma_desc_slot {
        u16 slot_cnt;
        u16 slots_per_op;
        u16 idx;
-       u16 unmap_src_cnt;
-       size_t unmap_len;
        struct list_head tx_list;
        struct dma_async_tx_descriptor async_tx;
        union {
index d070741..3c597c2 100644 (file)
 #ifdef __KERNEL__
 
 #include <linux/types.h>
+#include <linux/blk_types.h>
 #include <asm/byteorder.h>
 #include <asm/memory.h>
 #include <asm-generic/pci_iomap.h>
+#include <xen/xen.h>
 
 /*
  * ISA I/O bus memory addresses are 1:1 with the physical address.
@@ -372,6 +374,13 @@ extern void pci_iounmap(struct pci_dev *dev, void __iomem *addr);
 #define BIOVEC_MERGEABLE(vec1, vec2)   \
        ((bvec_to_phys((vec1)) + (vec1)->bv_len) == bvec_to_phys((vec2)))
 
+struct bio_vec;
+extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1,
+                                     const struct bio_vec *vec2);
+#define BIOVEC_PHYS_MERGEABLE(vec1, vec2)                              \
+       (__BIOVEC_PHYS_MERGEABLE(vec1, vec2) &&                         \
+        (!xen_domain() || xen_biovec_phys_mergeable(vec1, vec2)))
+
 #ifdef CONFIG_MMU
 #define ARCH_HAS_VALID_PHYS_ADDR_RANGE
 extern int valid_phys_addr_range(phys_addr_t addr, size_t size);
index 64e9696..1d3153c 100644 (file)
@@ -57,6 +57,7 @@
  * TSC:                Trap SMC
  * TSW:                Trap cache operations by set/way
  * TWI:                Trap WFI
+ * TWE:                Trap WFE
  * TIDCP:      Trap L2CTLR/L2ECTLR
  * BSU_IS:     Upgrade barriers to the inner shareable domain
  * FB:         Force broadcast of all maintainance operations
@@ -67,7 +68,7 @@
  */
 #define HCR_GUEST_MASK (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \
                        HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \
-                       HCR_SWIO | HCR_TIDCP)
+                       HCR_TWE | HCR_SWIO | HCR_TIDCP)
 #define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
 
 /* System Control Register (SCTLR) bits */
 #define TTBCR_IRGN1    (3 << 24)
 #define TTBCR_EPD1     (1 << 23)
 #define TTBCR_A1       (1 << 22)
-#define TTBCR_T1SZ     (3 << 16)
+#define TTBCR_T1SZ     (7 << 16)
 #define TTBCR_SH0      (3 << 12)
 #define TTBCR_ORGN0    (3 << 10)
 #define TTBCR_IRGN0    (3 << 8)
 #define TTBCR_EPD0     (1 << 7)
-#define TTBCR_T0SZ     3
+#define TTBCR_T0SZ     (7 << 0)
 #define HTCR_MASK      (TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0)
 
 /* Hyp System Trap Register */
 #define HSR_EC_DABT    (0x24)
 #define HSR_EC_DABT_HYP        (0x25)
 
+#define HSR_WFI_IS_WFE         (1U << 0)
+
 #define HSR_HVC_IMM_MASK       ((1UL << 16) - 1)
 
 #define HSR_DABT_S1PTW         (1U << 7)
index a2f43dd..661da11 100644 (file)
@@ -39,7 +39,7 @@
 #define c6_IFAR                17      /* Instruction Fault Address Register */
 #define c7_PAR         18      /* Physical Address Register */
 #define c7_PAR_high    19      /* PAR top 32 bits */
-#define c9_L2CTLR      20      /* Cortex A15 L2 Control Register */
+#define c9_L2CTLR      20      /* Cortex A15/A7 L2 Control Register */
 #define c10_PRRR       21      /* Primary Region Remap Register */
 #define c10_NMRR       22      /* Normal Memory Remap Register */
 #define c12_VBAR       23      /* Vector Base Address Register */
index a464e8d..0fa90c9 100644 (file)
@@ -157,4 +157,55 @@ static inline u32 kvm_vcpu_hvc_get_imm(struct kvm_vcpu *vcpu)
        return kvm_vcpu_get_hsr(vcpu) & HSR_HVC_IMM_MASK;
 }
 
+static inline unsigned long kvm_vcpu_get_mpidr(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.cp15[c0_MPIDR];
+}
+
+static inline void kvm_vcpu_set_be(struct kvm_vcpu *vcpu)
+{
+       *vcpu_cpsr(vcpu) |= PSR_E_BIT;
+}
+
+static inline bool kvm_vcpu_is_be(struct kvm_vcpu *vcpu)
+{
+       return !!(*vcpu_cpsr(vcpu) & PSR_E_BIT);
+}
+
+static inline unsigned long vcpu_data_guest_to_host(struct kvm_vcpu *vcpu,
+                                                   unsigned long data,
+                                                   unsigned int len)
+{
+       if (kvm_vcpu_is_be(vcpu)) {
+               switch (len) {
+               case 1:
+                       return data & 0xff;
+               case 2:
+                       return be16_to_cpu(data & 0xffff);
+               default:
+                       return be32_to_cpu(data);
+               }
+       }
+
+       return data;            /* Leave LE untouched */
+}
+
+static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
+                                                   unsigned long data,
+                                                   unsigned int len)
+{
+       if (kvm_vcpu_is_be(vcpu)) {
+               switch (len) {
+               case 1:
+                       return data & 0xff;
+               case 2:
+                       return cpu_to_be16(data & 0xffff);
+               default:
+                       return cpu_to_be32(data);
+               }
+       }
+
+       return data;            /* Leave LE untouched */
+}
+
 #endif /* __ARM_KVM_EMULATE_H__ */
index 7d22517..8a6f6db 100644 (file)
 
 #define KVM_VCPU_MAX_FEATURES 1
 
-/* We don't currently support large pages. */
-#define KVM_HPAGE_GFN_SHIFT(x) 0
-#define KVM_NR_PAGE_SIZES      1
-#define KVM_PAGES_PER_HPAGE(x) (1UL<<31)
-
 #include <kvm/arm_vgic.h>
 
 struct kvm_vcpu;
@@ -154,6 +149,7 @@ struct kvm_vcpu_stat {
 struct kvm_vcpu_init;
 int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
                        const struct kvm_vcpu_init *init);
+int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init);
 unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu);
 int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices);
 struct kvm_one_reg;
index 9b28c41..77de4a4 100644 (file)
@@ -62,6 +62,12 @@ phys_addr_t kvm_get_idmap_vector(void);
 int kvm_mmu_init(void);
 void kvm_clear_hyp_idmap(void);
 
+static inline void kvm_set_pmd(pmd_t *pmd, pmd_t new_pmd)
+{
+       *pmd = new_pmd;
+       flush_pmd_entry(pmd);
+}
+
 static inline void kvm_set_pte(pte_t *pte, pte_t new_pte)
 {
        *pte = new_pte;
@@ -103,9 +109,15 @@ static inline void kvm_set_s2pte_writable(pte_t *pte)
        pte_val(*pte) |= L_PTE_S2_RDWR;
 }
 
+static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
+{
+       pmd_val(*pmd) |= L_PMD_S2_RDWR;
+}
+
 struct kvm;
 
-static inline void coherent_icache_guest_page(struct kvm *kvm, gfn_t gfn)
+static inline void coherent_icache_guest_page(struct kvm *kvm, hva_t hva,
+                                             unsigned long size)
 {
        /*
         * If we are going to insert an instruction page and the icache is
@@ -120,8 +132,7 @@ static inline void coherent_icache_guest_page(struct kvm *kvm, gfn_t gfn)
         * need any kind of flushing (DDI 0406C.b - Page B3-1392).
         */
        if (icache_is_pipt()) {
-               unsigned long hva = gfn_to_hva(kvm, gfn);
-               __cpuc_coherent_user_range(hva, hva + PAGE_SIZE);
+               __cpuc_coherent_user_range(hva, hva + size);
        } else if (!icache_is_vivt_asid_tagged()) {
                /* any kind of VIPT cache */
                __flush_icache_all();
index 4dd2145..9ecccc8 100644 (file)
@@ -226,7 +226,14 @@ static inline phys_addr_t __virt_to_phys(unsigned long x)
 static inline unsigned long __phys_to_virt(phys_addr_t x)
 {
        unsigned long t;
-       __pv_stub(x, t, "sub", __PV_BITS_31_24);
+
+       /*
+        * 'unsigned long' cast discard upper word when
+        * phys_addr_t is 64 bit, and makes sure that inline
+        * assembler expression receives 32 bit argument
+        * in place where 'r' 32 bit operand is expected.
+        */
+       __pv_stub((unsigned long) x, t, "sub", __PV_BITS_31_24);
        return t;
 }
 
index 943504f..78a7793 100644 (file)
@@ -102,12 +102,14 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)
 #else
        pte = alloc_pages(PGALLOC_GFP, 0);
 #endif
-       if (pte) {
-               if (!PageHighMem(pte))
-                       clean_pte_table(page_address(pte));
-               pgtable_page_ctor(pte);
+       if (!pte)
+               return NULL;
+       if (!PageHighMem(pte))
+               clean_pte_table(page_address(pte));
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
        }
-
        return pte;
 }
 
index 39c54cf..4f95039 100644 (file)
 #define L_PTE_S2_RDONLY                 (_AT(pteval_t, 1) << 6)   /* HAP[1]   */
 #define L_PTE_S2_RDWR           (_AT(pteval_t, 3) << 6)   /* HAP[2:1] */
 
+#define L_PMD_S2_RDWR           (_AT(pmdval_t, 3) << 6)   /* HAP[2:1] */
+
 /*
  * Hyp-mode PL2 PTE definitions for LPAE.
  */
index df5e13d..71a06b2 100644 (file)
@@ -141,12 +141,6 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
 #endif
 
 /*
- * We use bit 30 of the preempt_count to indicate that kernel
- * preemption is occurring.  See <asm/hardirq.h>.
- */
-#define PREEMPT_ACTIVE 0x40000000
-
-/*
  * thread information flags:
  *  TIF_SYSCALL_TRACE  - syscall trace active
  *  TIF_SYSCAL_AUDIT   - syscall auditing active
index d7ab99a..1317ee4 100644 (file)
@@ -16,4 +16,6 @@ static inline enum paravirt_lazy_mode paravirt_get_lazy_mode(void)
        return PARAVIRT_LAZY_NONE;
 }
 
+extern struct dma_map_ops *xen_dma_ops;
+
 #endif /* _ASM_ARM_XEN_HYPERVISOR_H */
diff --git a/arch/arm/include/asm/xen/page-coherent.h b/arch/arm/include/asm/xen/page-coherent.h
new file mode 100644 (file)
index 0000000..1109017
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef _ASM_ARM_XEN_PAGE_COHERENT_H
+#define _ASM_ARM_XEN_PAGE_COHERENT_H
+
+#include <asm/page.h>
+#include <linux/dma-attrs.h>
+#include <linux/dma-mapping.h>
+
+static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size,
+               dma_addr_t *dma_handle, gfp_t flags,
+               struct dma_attrs *attrs)
+{
+       return __generic_dma_ops(hwdev)->alloc(hwdev, size, dma_handle, flags, attrs);
+}
+
+static inline void xen_free_coherent_pages(struct device *hwdev, size_t size,
+               void *cpu_addr, dma_addr_t dma_handle,
+               struct dma_attrs *attrs)
+{
+       __generic_dma_ops(hwdev)->free(hwdev, size, cpu_addr, dma_handle, attrs);
+}
+
+static inline void xen_dma_map_page(struct device *hwdev, struct page *page,
+            unsigned long offset, size_t size, enum dma_data_direction dir,
+            struct dma_attrs *attrs)
+{
+       __generic_dma_ops(hwdev)->map_page(hwdev, page, offset, size, dir, attrs);
+}
+
+static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
+               size_t size, enum dma_data_direction dir,
+               struct dma_attrs *attrs)
+{
+       if (__generic_dma_ops(hwdev)->unmap_page)
+               __generic_dma_ops(hwdev)->unmap_page(hwdev, handle, size, dir, attrs);
+}
+
+static inline void xen_dma_sync_single_for_cpu(struct device *hwdev,
+               dma_addr_t handle, size_t size, enum dma_data_direction dir)
+{
+       if (__generic_dma_ops(hwdev)->sync_single_for_cpu)
+               __generic_dma_ops(hwdev)->sync_single_for_cpu(hwdev, handle, size, dir);
+}
+
+static inline void xen_dma_sync_single_for_device(struct device *hwdev,
+               dma_addr_t handle, size_t size, enum dma_data_direction dir)
+{
+       if (__generic_dma_ops(hwdev)->sync_single_for_device)
+               __generic_dma_ops(hwdev)->sync_single_for_device(hwdev, handle, size, dir);
+}
+#endif /* _ASM_ARM_XEN_PAGE_COHERENT_H */
index 359a7b5..75579a9 100644 (file)
@@ -6,12 +6,12 @@
 
 #include <linux/pfn.h>
 #include <linux/types.h>
+#include <linux/dma-mapping.h>
 
+#include <xen/xen.h>
 #include <xen/interface/grant_table.h>
 
-#define pfn_to_mfn(pfn)                        (pfn)
 #define phys_to_machine_mapping_valid(pfn) (1)
-#define mfn_to_pfn(mfn)                        (mfn)
 #define mfn_to_virt(m)                 (__va(mfn_to_pfn(m) << PAGE_SHIFT))
 
 #define pte_mfn            pte_pfn
@@ -32,6 +32,38 @@ typedef struct xpaddr {
 
 #define INVALID_P2M_ENTRY      (~0UL)
 
+unsigned long __pfn_to_mfn(unsigned long pfn);
+unsigned long __mfn_to_pfn(unsigned long mfn);
+extern struct rb_root phys_to_mach;
+
+static inline unsigned long pfn_to_mfn(unsigned long pfn)
+{
+       unsigned long mfn;
+
+       if (phys_to_mach.rb_node != NULL) {
+               mfn = __pfn_to_mfn(pfn);
+               if (mfn != INVALID_P2M_ENTRY)
+                       return mfn;
+       }
+
+       return pfn;
+}
+
+static inline unsigned long mfn_to_pfn(unsigned long mfn)
+{
+       unsigned long pfn;
+
+       if (phys_to_mach.rb_node != NULL) {
+               pfn = __mfn_to_pfn(mfn);
+               if (pfn != INVALID_P2M_ENTRY)
+                       return pfn;
+       }
+
+       return mfn;
+}
+
+#define mfn_to_local_pfn(mfn) mfn_to_pfn(mfn)
+
 static inline xmaddr_t phys_to_machine(xpaddr_t phys)
 {
        unsigned offset = phys.paddr & ~PAGE_MASK;
@@ -76,11 +108,9 @@ static inline int m2p_remove_override(struct page *page, bool clear_pte)
        return 0;
 }
 
-static inline bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn)
-{
-       BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY);
-       return true;
-}
+bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn);
+bool __set_phys_to_machine_multi(unsigned long pfn, unsigned long mfn,
+               unsigned long nr_pages);
 
 static inline bool set_phys_to_machine(unsigned long pfn, unsigned long mfn)
 {
index c1ee007..c498b60 100644 (file)
@@ -63,7 +63,8 @@ struct kvm_regs {
 
 /* Supported Processor Types */
 #define KVM_ARM_TARGET_CORTEX_A15      0
-#define KVM_ARM_NUM_TARGETS            1
+#define KVM_ARM_TARGET_CORTEX_A7       1
+#define KVM_ARM_NUM_TARGETS            2
 
 /* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */
 #define KVM_ARM_DEVICE_TYPE_SHIFT      0
index 7801866..11d59b3 100644 (file)
@@ -508,6 +508,7 @@ __fixup_smp:
        teq     r0, #0x0                @ '0' on actual UP A9 hardware
        beq     __fixup_smp_on_up       @ So its an A9 UP
        ldr     r0, [r0, #4]            @ read SCU Config
+ARM_BE8(rev    r0, r0)                 @ byteswap if big endian
        and     r0, r0, #0x3            @ number of CPUs
        teq     r0, #0x0                @ is 1?
        movne   pc, lr
@@ -644,7 +645,11 @@ ARM_BE8(rev16      ip, ip)
        bcc     1b
        bx      lr
 #else
+#ifdef CONFIG_CPU_ENDIAN_BE8
+       moveq   r0, #0x00004000 @ set bit 22, mov to mvn instruction
+#else
        moveq   r0, #0x400000   @ set bit 22, mov to mvn instruction
+#endif
        b       2f
 1:     ldr     ip, [r7, r3]
 #ifdef CONFIG_CPU_ENDIAN_BE8
@@ -653,7 +658,7 @@ ARM_BE8(rev16       ip, ip)
        tst     ip, #0x000f0000 @ check the rotation field
        orrne   ip, ip, r6, lsl #24 @ mask in offset bits 31-24
        biceq   ip, ip, #0x00004000 @ clear bit 22
-       orreq   ip, ip, r0, lsl #24 @ mask in offset bits 7-0
+       orreq   ip, ip, r0      @ mask in offset bits 7-0
 #else
        bic     ip, ip, #0x000000ff
        tst     ip, #0xf00      @ check the rotation field
index 6125f25..dbf0923 100644 (file)
@@ -856,7 +856,7 @@ static void __init kuser_init(void *vectors)
                memcpy(vectors + 0xfe0, vectors + 0xfe8, 4);
 }
 #else
-static void __init kuser_init(void *vectors)
+static inline void __init kuser_init(void *vectors)
 {
 }
 #endif
index ebf5015..466bd29 100644 (file)
@@ -20,6 +20,7 @@ config KVM
        bool "Kernel-based Virtual Machine (KVM) support"
        select PREEMPT_NOTIFIERS
        select ANON_INODES
+       select HAVE_KVM_CPU_RELAX_INTERCEPT
        select KVM_MMIO
        select KVM_ARM_HOST
        depends on ARM_VIRT_EXT && ARM_LPAE
index d99bee4..789bca9 100644 (file)
@@ -19,6 +19,6 @@ kvm-arm-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o
 
 obj-y += kvm-arm.o init.o interrupts.o
 obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o
-obj-y += coproc.o coproc_a15.o mmio.o psci.o perf.o
+obj-y += coproc.o coproc_a15.o coproc_a7.o mmio.o psci.o perf.o
 obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o
 obj-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o
index aea7ccb..2a700e0 100644 (file)
@@ -152,12 +152,13 @@ int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
        return VM_FAULT_SIGBUS;
 }
 
-void kvm_arch_free_memslot(struct kvm_memory_slot *free,
+void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
                           struct kvm_memory_slot *dont)
 {
 }
 
-int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
+int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
+                           unsigned long npages)
 {
        return 0;
 }
@@ -797,6 +798,19 @@ long kvm_arch_vm_ioctl(struct file *filp,
                        return -EFAULT;
                return kvm_vm_ioctl_set_device_addr(kvm, &dev_addr);
        }
+       case KVM_ARM_PREFERRED_TARGET: {
+               int err;
+               struct kvm_vcpu_init init;
+
+               err = kvm_vcpu_preferred_target(&init);
+               if (err)
+                       return err;
+
+               if (copy_to_user(argp, &init, sizeof(init)))
+                       return -EFAULT;
+
+               return 0;
+       }
        default:
                return -EINVAL;
        }
index db9cf69..78c0885 100644 (file)
@@ -71,6 +71,98 @@ int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run)
        return 1;
 }
 
+static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
+{
+       /*
+        * Compute guest MPIDR. We build a virtual cluster out of the
+        * vcpu_id, but we read the 'U' bit from the underlying
+        * hardware directly.
+        */
+       vcpu->arch.cp15[c0_MPIDR] = ((read_cpuid_mpidr() & MPIDR_SMP_BITMASK) |
+                                    ((vcpu->vcpu_id >> 2) << MPIDR_LEVEL_BITS) |
+                                    (vcpu->vcpu_id & 3));
+}
+
+/* TRM entries A7:4.3.31 A15:4.3.28 - RO WI */
+static bool access_actlr(struct kvm_vcpu *vcpu,
+                        const struct coproc_params *p,
+                        const struct coproc_reg *r)
+{
+       if (p->is_write)
+               return ignore_write(vcpu, p);
+
+       *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c1_ACTLR];
+       return true;
+}
+
+/* TRM entries A7:4.3.56, A15:4.3.60 - R/O. */
+static bool access_cbar(struct kvm_vcpu *vcpu,
+                       const struct coproc_params *p,
+                       const struct coproc_reg *r)
+{
+       if (p->is_write)
+               return write_to_read_only(vcpu, p);
+       return read_zero(vcpu, p);
+}
+
+/* TRM entries A7:4.3.49, A15:4.3.48 - R/O WI */
+static bool access_l2ctlr(struct kvm_vcpu *vcpu,
+                         const struct coproc_params *p,
+                         const struct coproc_reg *r)
+{
+       if (p->is_write)
+               return ignore_write(vcpu, p);
+
+       *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c9_L2CTLR];
+       return true;
+}
+
+static void reset_l2ctlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
+{
+       u32 l2ctlr, ncores;
+
+       asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr));
+       l2ctlr &= ~(3 << 24);
+       ncores = atomic_read(&vcpu->kvm->online_vcpus) - 1;
+       /* How many cores in the current cluster and the next ones */
+       ncores -= (vcpu->vcpu_id & ~3);
+       /* Cap it to the maximum number of cores in a single cluster */
+       ncores = min(ncores, 3U);
+       l2ctlr |= (ncores & 3) << 24;
+
+       vcpu->arch.cp15[c9_L2CTLR] = l2ctlr;
+}
+
+static void reset_actlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
+{
+       u32 actlr;
+
+       /* ACTLR contains SMP bit: make sure you create all cpus first! */
+       asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr));
+       /* Make the SMP bit consistent with the guest configuration */
+       if (atomic_read(&vcpu->kvm->online_vcpus) > 1)
+               actlr |= 1U << 6;
+       else
+               actlr &= ~(1U << 6);
+
+       vcpu->arch.cp15[c1_ACTLR] = actlr;
+}
+
+/*
+ * TRM entries: A7:4.3.50, A15:4.3.49
+ * R/O WI (even if NSACR.NS_L2ERR, a write of 1 is ignored).
+ */
+static bool access_l2ectlr(struct kvm_vcpu *vcpu,
+                          const struct coproc_params *p,
+                          const struct coproc_reg *r)
+{
+       if (p->is_write)
+               return ignore_write(vcpu, p);
+
+       *vcpu_reg(vcpu, p->Rt1) = 0;
+       return true;
+}
+
 /* See note at ARM ARM B1.14.4 */
 static bool access_dcsw(struct kvm_vcpu *vcpu,
                        const struct coproc_params *p,
@@ -153,10 +245,22 @@ static bool pm_fake(struct kvm_vcpu *vcpu,
  *            registers preceding 32-bit ones.
  */
 static const struct coproc_reg cp15_regs[] = {
+       /* MPIDR: we use VMPIDR for guest access. */
+       { CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32,
+                       NULL, reset_mpidr, c0_MPIDR },
+
        /* CSSELR: swapped by interrupt.S. */
        { CRn( 0), CRm( 0), Op1( 2), Op2( 0), is32,
                        NULL, reset_unknown, c0_CSSELR },
 
+       /* ACTLR: trapped by HCR.TAC bit. */
+       { CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32,
+                       access_actlr, reset_actlr, c1_ACTLR },
+
+       /* CPACR: swapped by interrupt.S. */
+       { CRn( 1), CRm( 0), Op1( 0), Op2( 2), is32,
+                       NULL, reset_val, c1_CPACR, 0x00000000 },
+
        /* TTBR0/TTBR1: swapped by interrupt.S. */
        { CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 },
        { CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 },
@@ -195,6 +299,13 @@ static const struct coproc_reg cp15_regs[] = {
        { CRn( 7), CRm(10), Op1( 0), Op2( 2), is32, access_dcsw},
        { CRn( 7), CRm(14), Op1( 0), Op2( 2), is32, access_dcsw},
        /*
+        * L2CTLR access (guest wants to know #CPUs).
+        */
+       { CRn( 9), CRm( 0), Op1( 1), Op2( 2), is32,
+                       access_l2ctlr, reset_l2ctlr, c9_L2CTLR },
+       { CRn( 9), CRm( 0), Op1( 1), Op2( 3), is32, access_l2ectlr},
+
+       /*
         * Dummy performance monitor implementation.
         */
        { CRn( 9), CRm(12), Op1( 0), Op2( 0), is32, access_pmcr},
@@ -234,6 +345,9 @@ static const struct coproc_reg cp15_regs[] = {
        /* CNTKCTL: swapped by interrupt.S. */
        { CRn(14), CRm( 1), Op1( 0), Op2( 0), is32,
                        NULL, reset_val, c14_CNTKCTL, 0x00000000 },
+
+       /* The Configuration Base Address Register. */
+       { CRn(15), CRm( 0), Op1( 4), Op2( 0), is32, access_cbar},
 };
 
 /* Target specific emulation tables */
@@ -241,6 +355,12 @@ static struct kvm_coproc_target_table *target_tables[KVM_ARM_NUM_TARGETS];
 
 void kvm_register_target_coproc_table(struct kvm_coproc_target_table *table)
 {
+       unsigned int i;
+
+       for (i = 1; i < table->num; i++)
+               BUG_ON(cmp_reg(&table->table[i-1],
+                              &table->table[i]) >= 0);
+
        target_tables[table->target] = table;
 }
 
index cf93472..bb0cac1 100644 (file)
  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 #include <linux/kvm_host.h>
-#include <asm/cputype.h>
-#include <asm/kvm_arm.h>
-#include <asm/kvm_host.h>
-#include <asm/kvm_emulate.h>
 #include <asm/kvm_coproc.h>
+#include <asm/kvm_emulate.h>
 #include <linux/init.h>
 
-static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
-{
-       /*
-        * Compute guest MPIDR:
-        * (Even if we present only one VCPU to the guest on an SMP
-        * host we don't set the U bit in the MPIDR, or vice versa, as
-        * revealing the underlying hardware properties is likely to
-        * be the best choice).
-        */
-       vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_LEVEL_MASK)
-               | (vcpu->vcpu_id & MPIDR_LEVEL_MASK);
-}
-
 #include "coproc.h"
 
-/* A15 TRM 4.3.28: RO WI */
-static bool access_actlr(struct kvm_vcpu *vcpu,
-                        const struct coproc_params *p,
-                        const struct coproc_reg *r)
-{
-       if (p->is_write)
-               return ignore_write(vcpu, p);
-
-       *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c1_ACTLR];
-       return true;
-}
-
-/* A15 TRM 4.3.60: R/O. */
-static bool access_cbar(struct kvm_vcpu *vcpu,
-                       const struct coproc_params *p,
-                       const struct coproc_reg *r)
-{
-       if (p->is_write)
-               return write_to_read_only(vcpu, p);
-       return read_zero(vcpu, p);
-}
-
-/* A15 TRM 4.3.48: R/O WI. */
-static bool access_l2ctlr(struct kvm_vcpu *vcpu,
-                         const struct coproc_params *p,
-                         const struct coproc_reg *r)
-{
-       if (p->is_write)
-               return ignore_write(vcpu, p);
-
-       *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c9_L2CTLR];
-       return true;
-}
-
-static void reset_l2ctlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
-{
-       u32 l2ctlr, ncores;
-
-       asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr));
-       l2ctlr &= ~(3 << 24);
-       ncores = atomic_read(&vcpu->kvm->online_vcpus) - 1;
-       l2ctlr |= (ncores & 3) << 24;
-
-       vcpu->arch.cp15[c9_L2CTLR] = l2ctlr;
-}
-
-static void reset_actlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
-{
-       u32 actlr;
-
-       /* ACTLR contains SMP bit: make sure you create all cpus first! */
-       asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr));
-       /* Make the SMP bit consistent with the guest configuration */
-       if (atomic_read(&vcpu->kvm->online_vcpus) > 1)
-               actlr |= 1U << 6;
-       else
-               actlr &= ~(1U << 6);
-
-       vcpu->arch.cp15[c1_ACTLR] = actlr;
-}
-
-/* A15 TRM 4.3.49: R/O WI (even if NSACR.NS_L2ERR, a write of 1 is ignored). */
-static bool access_l2ectlr(struct kvm_vcpu *vcpu,
-                          const struct coproc_params *p,
-                          const struct coproc_reg *r)
-{
-       if (p->is_write)
-               return ignore_write(vcpu, p);
-
-       *vcpu_reg(vcpu, p->Rt1) = 0;
-       return true;
-}
-
 /*
  * A15-specific CP15 registers.
  * CRn denotes the primary register number, but is copied to the CRm in the
@@ -121,29 +32,9 @@ static bool access_l2ectlr(struct kvm_vcpu *vcpu,
  *            registers preceding 32-bit ones.
  */
 static const struct coproc_reg a15_regs[] = {
-       /* MPIDR: we use VMPIDR for guest access. */
-       { CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32,
-                       NULL, reset_mpidr, c0_MPIDR },
-
        /* SCTLR: swapped by interrupt.S. */
        { CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
                        NULL, reset_val, c1_SCTLR, 0x00C50078 },
-       /* ACTLR: trapped by HCR.TAC bit. */
-       { CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32,
-                       access_actlr, reset_actlr, c1_ACTLR },
-       /* CPACR: swapped by interrupt.S. */
-       { CRn( 1), CRm( 0), Op1( 0), Op2( 2), is32,
-                       NULL, reset_val, c1_CPACR, 0x00000000 },
-
-       /*
-        * L2CTLR access (guest wants to know #CPUs).
-        */
-       { CRn( 9), CRm( 0), Op1( 1), Op2( 2), is32,
-                       access_l2ctlr, reset_l2ctlr, c9_L2CTLR },
-       { CRn( 9), CRm( 0), Op1( 1), Op2( 3), is32, access_l2ectlr},
-
-       /* The Configuration Base Address Register. */
-       { CRn(15), CRm( 0), Op1( 4), Op2( 0), is32, access_cbar},
 };
 
 static struct kvm_coproc_target_table a15_target_table = {
@@ -154,12 +45,6 @@ static struct kvm_coproc_target_table a15_target_table = {
 
 static int __init coproc_a15_init(void)
 {
-       unsigned int i;
-
-       for (i = 1; i < ARRAY_SIZE(a15_regs); i++)
-               BUG_ON(cmp_reg(&a15_regs[i-1],
-                              &a15_regs[i]) >= 0);
-
        kvm_register_target_coproc_table(&a15_target_table);
        return 0;
 }
diff --git a/arch/arm/kvm/coproc_a7.c b/arch/arm/kvm/coproc_a7.c
new file mode 100644 (file)
index 0000000..1df7673
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Copyright (C) 2013 - ARM Ltd
+ *
+ * Authors: Rusty Russell <rusty@rustcorp.au>
+ *          Christoffer Dall <c.dall@virtualopensystems.com>
+ *          Jonathan Austin <jonathan.austin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include <linux/kvm_host.h>
+#include <asm/kvm_coproc.h>
+#include <asm/kvm_emulate.h>
+#include <linux/init.h>
+
+#include "coproc.h"
+
+/*
+ * Cortex-A7 specific CP15 registers.
+ * CRn denotes the primary register number, but is copied to the CRm in the
+ * user space API for 64-bit register access in line with the terminology used
+ * in the ARM ARM.
+ * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit
+ *            registers preceding 32-bit ones.
+ */
+static const struct coproc_reg a7_regs[] = {
+       /* SCTLR: swapped by interrupt.S. */
+       { CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
+                       NULL, reset_val, c1_SCTLR, 0x00C50878 },
+};
+
+static struct kvm_coproc_target_table a7_target_table = {
+       .target = KVM_ARM_TARGET_CORTEX_A7,
+       .table = a7_regs,
+       .num = ARRAY_SIZE(a7_regs),
+};
+
+static int __init coproc_a7_init(void)
+{
+       kvm_register_target_coproc_table(&a7_target_table);
+       return 0;
+}
+late_initcall(coproc_a7_init);
index bdede9e..d6c0052 100644 (file)
@@ -354,7 +354,7 @@ static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr)
        *vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset;
 
        if (is_pabt) {
-               /* Set DFAR and DFSR */
+               /* Set IFAR and IFSR */
                vcpu->arch.cp15[c6_IFAR] = addr;
                is_lpae = (vcpu->arch.cp15[c2_TTBCR] >> 31);
                /* Always give debug fault for now - should give guest a clue */
index 152d036..20f8d97 100644 (file)
@@ -190,6 +190,8 @@ int __attribute_const__ kvm_target_cpu(void)
                return -EINVAL;
 
        switch (part_number) {
+       case ARM_CPU_PART_CORTEX_A7:
+               return KVM_ARM_TARGET_CORTEX_A7;
        case ARM_CPU_PART_CORTEX_A15:
                return KVM_ARM_TARGET_CORTEX_A15;
        default:
@@ -202,7 +204,7 @@ int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
 {
        unsigned int i;
 
-       /* We can only do a cortex A15 for now. */
+       /* We can only cope with guest==host and only on A15/A7 (for now). */
        if (init->target != kvm_target_cpu())
                return -EINVAL;
 
@@ -222,6 +224,26 @@ int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
        return kvm_reset_vcpu(vcpu);
 }
 
+int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init)
+{
+       int target = kvm_target_cpu();
+
+       if (target < 0)
+               return -ENODEV;
+
+       memset(init, 0, sizeof(*init));
+
+       /*
+        * For now, we don't return any features.
+        * In future, we might use features to return target
+        * specific features available for the preferred
+        * target type.
+        */
+       init->target = (__u32)target;
+
+       return 0;
+}
+
 int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
 {
        return -EINVAL;
index df4c82d..a920790 100644 (file)
@@ -73,23 +73,29 @@ static int handle_dabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
 }
 
 /**
- * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest
+ * kvm_handle_wfx - handle a WFI or WFE instructions trapped in guests
  * @vcpu:      the vcpu pointer
  * @run:       the kvm_run structure pointer
  *
- * Simply sets the wait_for_interrupts flag on the vcpu structure, which will
- * halt execution of world-switches and schedule other host processes until
- * there is an incoming IRQ or FIQ to the VM.
+ * WFE: Yield the CPU and come back to this vcpu when the scheduler
+ * decides to.
+ * WFI: Simply call kvm_vcpu_block(), which will halt execution of
+ * world-switches and schedule other host processes until there is an
+ * incoming IRQ or FIQ to the VM.
  */
-static int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run)
+static int kvm_handle_wfx(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
        trace_kvm_wfi(*vcpu_pc(vcpu));
-       kvm_vcpu_block(vcpu);
+       if (kvm_vcpu_get_hsr(vcpu) & HSR_WFI_IS_WFE)
+               kvm_vcpu_on_spin(vcpu);
+       else
+               kvm_vcpu_block(vcpu);
+
        return 1;
 }
 
 static exit_handle_fn arm_exit_handlers[] = {
-       [HSR_EC_WFI]            = kvm_handle_wfi,
+       [HSR_EC_WFI]            = kvm_handle_wfx,
        [HSR_EC_CP15_32]        = kvm_handle_cp15_32,
        [HSR_EC_CP15_64]        = kvm_handle_cp15_64,
        [HSR_EC_CP14_MR]        = kvm_handle_cp14_access,
index 0c25d94..4cb5a93 100644 (file)
 
 #include "trace.h"
 
+static void mmio_write_buf(char *buf, unsigned int len, unsigned long data)
+{
+       void *datap = NULL;
+       union {
+               u8      byte;
+               u16     hword;
+               u32     word;
+               u64     dword;
+       } tmp;
+
+       switch (len) {
+       case 1:
+               tmp.byte        = data;
+               datap           = &tmp.byte;
+               break;
+       case 2:
+               tmp.hword       = data;
+               datap           = &tmp.hword;
+               break;
+       case 4:
+               tmp.word        = data;
+               datap           = &tmp.word;
+               break;
+       case 8:
+               tmp.dword       = data;
+               datap           = &tmp.dword;
+               break;
+       }
+
+       memcpy(buf, datap, len);
+}
+
+static unsigned long mmio_read_buf(char *buf, unsigned int len)
+{
+       unsigned long data = 0;
+       union {
+               u16     hword;
+               u32     word;
+               u64     dword;
+       } tmp;
+
+       switch (len) {
+       case 1:
+               data = buf[0];
+               break;
+       case 2:
+               memcpy(&tmp.hword, buf, len);
+               data = tmp.hword;
+               break;
+       case 4:
+               memcpy(&tmp.word, buf, len);
+               data = tmp.word;
+               break;
+       case 8:
+               memcpy(&tmp.dword, buf, len);
+               data = tmp.dword;
+               break;
+       }
+
+       return data;
+}
+
 /**
  * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation
  * @vcpu: The VCPU pointer
  */
 int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-       unsigned long *dest;
+       unsigned long data;
        unsigned int len;
        int mask;
 
        if (!run->mmio.is_write) {
-               dest = vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt);
-               *dest = 0;
-
                len = run->mmio.len;
                if (len > sizeof(unsigned long))
                        return -EINVAL;
 
-               memcpy(dest, run->mmio.data, len);
-
-               trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
-                               *((u64 *)run->mmio.data));
+               data = mmio_read_buf(run->mmio.data, len);
 
                if (vcpu->arch.mmio_decode.sign_extend &&
                    len < sizeof(unsigned long)) {
                        mask = 1U << ((len * 8) - 1);
-                       *dest = (*dest ^ mask) - mask;
+                       data = (data ^ mask) - mask;
                }
+
+               trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
+                              data);
+               data = vcpu_data_host_to_guest(vcpu, data, len);
+               *vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt) = data;
        }
 
        return 0;
@@ -105,6 +166,7 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
                 phys_addr_t fault_ipa)
 {
        struct kvm_exit_mmio mmio;
+       unsigned long data;
        unsigned long rt;
        int ret;
 
@@ -125,13 +187,15 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
        }
 
        rt = vcpu->arch.mmio_decode.rt;
+       data = vcpu_data_guest_to_host(vcpu, *vcpu_reg(vcpu, rt), mmio.len);
+
        trace_kvm_mmio((mmio.is_write) ? KVM_TRACE_MMIO_WRITE :
                                         KVM_TRACE_MMIO_READ_UNSATISFIED,
                        mmio.len, fault_ipa,
-                       (mmio.is_write) ? *vcpu_reg(vcpu, rt) : 0);
+                       (mmio.is_write) ? data : 0);
 
        if (mmio.is_write)
-               memcpy(mmio.data, vcpu_reg(vcpu, rt), mmio.len);
+               mmio_write_buf(mmio.data, mmio.len, data);
 
        if (vgic_handle_mmio(vcpu, run, &mmio))
                return 1;
index b0de86b..5809069 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/mman.h>
 #include <linux/kvm_host.h>
 #include <linux/io.h>
+#include <linux/hugetlb.h>
 #include <trace/events/kvm.h>
 #include <asm/pgalloc.h>
 #include <asm/cacheflush.h>
@@ -41,6 +42,8 @@ static unsigned long hyp_idmap_start;
 static unsigned long hyp_idmap_end;
 static phys_addr_t hyp_idmap_vector;
 
+#define kvm_pmd_huge(_x)       (pmd_huge(_x) || pmd_trans_huge(_x))
+
 static void kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
 {
        /*
@@ -93,19 +96,29 @@ static bool page_empty(void *ptr)
 
 static void clear_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr)
 {
-       pmd_t *pmd_table = pmd_offset(pud, 0);
-       pud_clear(pud);
-       kvm_tlb_flush_vmid_ipa(kvm, addr);
-       pmd_free(NULL, pmd_table);
+       if (pud_huge(*pud)) {
+               pud_clear(pud);
+               kvm_tlb_flush_vmid_ipa(kvm, addr);
+       } else {
+               pmd_t *pmd_table = pmd_offset(pud, 0);
+               pud_clear(pud);
+               kvm_tlb_flush_vmid_ipa(kvm, addr);
+               pmd_free(NULL, pmd_table);
+       }
        put_page(virt_to_page(pud));
 }
 
 static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
 {
-       pte_t *pte_table = pte_offset_kernel(pmd, 0);
-       pmd_clear(pmd);
-       kvm_tlb_flush_vmid_ipa(kvm, addr);
-       pte_free_kernel(NULL, pte_table);
+       if (kvm_pmd_huge(*pmd)) {
+               pmd_clear(pmd);
+               kvm_tlb_flush_vmid_ipa(kvm, addr);
+       } else {
+               pte_t *pte_table = pte_offset_kernel(pmd, 0);
+               pmd_clear(pmd);
+               kvm_tlb_flush_vmid_ipa(kvm, addr);
+               pte_free_kernel(NULL, pte_table);
+       }
        put_page(virt_to_page(pmd));
 }
 
@@ -136,18 +149,32 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
                        continue;
                }
 
+               if (pud_huge(*pud)) {
+                       /*
+                        * If we are dealing with a huge pud, just clear it and
+                        * move on.
+                        */
+                       clear_pud_entry(kvm, pud, addr);
+                       addr = pud_addr_end(addr, end);
+                       continue;
+               }
+
                pmd = pmd_offset(pud, addr);
                if (pmd_none(*pmd)) {
                        addr = pmd_addr_end(addr, end);
                        continue;
                }
 
-               pte = pte_offset_kernel(pmd, addr);
-               clear_pte_entry(kvm, pte, addr);
-               next = addr + PAGE_SIZE;
+               if (!kvm_pmd_huge(*pmd)) {
+                       pte = pte_offset_kernel(pmd, addr);
+                       clear_pte_entry(kvm, pte, addr);
+                       next = addr + PAGE_SIZE;
+               }
 
-               /* If we emptied the pte, walk back up the ladder */
-               if (page_empty(pte)) {
+               /*
+                * If the pmd entry is to be cleared, walk back up the ladder
+                */
+               if (kvm_pmd_huge(*pmd) || page_empty(pte)) {
                        clear_pmd_entry(kvm, pmd, addr);
                        next = pmd_addr_end(addr, end);
                        if (page_empty(pmd) && !page_empty(pud)) {
@@ -307,6 +334,17 @@ out:
        return err;
 }
 
+static phys_addr_t kvm_kaddr_to_phys(void *kaddr)
+{
+       if (!is_vmalloc_addr(kaddr)) {
+               BUG_ON(!virt_addr_valid(kaddr));
+               return __pa(kaddr);
+       } else {
+               return page_to_phys(vmalloc_to_page(kaddr)) +
+                      offset_in_page(kaddr);
+       }
+}
+
 /**
  * create_hyp_mappings - duplicate a kernel virtual address range in Hyp mode
  * @from:      The virtual kernel start address of the range
@@ -318,16 +356,27 @@ out:
  */
 int create_hyp_mappings(void *from, void *to)
 {
-       unsigned long phys_addr = virt_to_phys(from);
+       phys_addr_t phys_addr;
+       unsigned long virt_addr;
        unsigned long start = KERN_TO_HYP((unsigned long)from);
        unsigned long end = KERN_TO_HYP((unsigned long)to);
 
-       /* Check for a valid kernel memory mapping */
-       if (!virt_addr_valid(from) || !virt_addr_valid(to - 1))
-               return -EINVAL;
+       start = start & PAGE_MASK;
+       end = PAGE_ALIGN(end);
 
-       return __create_hyp_mappings(hyp_pgd, start, end,
-                                    __phys_to_pfn(phys_addr), PAGE_HYP);
+       for (virt_addr = start; virt_addr < end; virt_addr += PAGE_SIZE) {
+               int err;
+
+               phys_addr = kvm_kaddr_to_phys(from + virt_addr - start);
+               err = __create_hyp_mappings(hyp_pgd, virt_addr,
+                                           virt_addr + PAGE_SIZE,
+                                           __phys_to_pfn(phys_addr),
+                                           PAGE_HYP);
+               if (err)
+                       return err;
+       }
+
+       return 0;
 }
 
 /**
@@ -420,29 +469,71 @@ void kvm_free_stage2_pgd(struct kvm *kvm)
        kvm->arch.pgd = NULL;
 }
 
-
-static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
-                         phys_addr_t addr, const pte_t *new_pte, bool iomap)
+static pmd_t *stage2_get_pmd(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
+                            phys_addr_t addr)
 {
        pgd_t *pgd;
        pud_t *pud;
        pmd_t *pmd;
-       pte_t *pte, old_pte;
 
-       /* Create 2nd stage page table mapping - Level 1 */
        pgd = kvm->arch.pgd + pgd_index(addr);
        pud = pud_offset(pgd, addr);
        if (pud_none(*pud)) {
                if (!cache)
-                       return 0; /* ignore calls from kvm_set_spte_hva */
+                       return NULL;
                pmd = mmu_memory_cache_alloc(cache);
                pud_populate(NULL, pud, pmd);
                get_page(virt_to_page(pud));
        }
 
-       pmd = pmd_offset(pud, addr);
+       return pmd_offset(pud, addr);
+}
+
+static int stage2_set_pmd_huge(struct kvm *kvm, struct kvm_mmu_memory_cache
+                              *cache, phys_addr_t addr, const pmd_t *new_pmd)
+{
+       pmd_t *pmd, old_pmd;
+
+       pmd = stage2_get_pmd(kvm, cache, addr);
+       VM_BUG_ON(!pmd);
+
+       /*
+        * Mapping in huge pages should only happen through a fault.  If a
+        * page is merged into a transparent huge page, the individual
+        * subpages of that huge page should be unmapped through MMU
+        * notifiers before we get here.
+        *
+        * Merging of CompoundPages is not supported; they should become
+        * splitting first, unmapped, merged, and mapped back in on-demand.
+        */
+       VM_BUG_ON(pmd_present(*pmd) && pmd_pfn(*pmd) != pmd_pfn(*new_pmd));
+
+       old_pmd = *pmd;
+       kvm_set_pmd(pmd, *new_pmd);
+       if (pmd_present(old_pmd))
+               kvm_tlb_flush_vmid_ipa(kvm, addr);
+       else
+               get_page(virt_to_page(pmd));
+       return 0;
+}
+
+static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
+                         phys_addr_t addr, const pte_t *new_pte, bool iomap)
+{
+       pmd_t *pmd;
+       pte_t *pte, old_pte;
+
+       /* Create stage-2 page table mapping - Level 1 */
+       pmd = stage2_get_pmd(kvm, cache, addr);
+       if (!pmd) {
+               /*
+                * Ignore calls from kvm_set_spte_hva for unallocated
+                * address ranges.
+                */
+               return 0;
+       }
 
-       /* Create 2nd stage page table mapping - Level 2 */
+       /* Create stage-2 page mappings - Level 2 */
        if (pmd_none(*pmd)) {
                if (!cache)
                        return 0; /* ignore calls from kvm_set_spte_hva */
@@ -507,16 +598,60 @@ out:
        return ret;
 }
 
+static bool transparent_hugepage_adjust(pfn_t *pfnp, phys_addr_t *ipap)
+{
+       pfn_t pfn = *pfnp;
+       gfn_t gfn = *ipap >> PAGE_SHIFT;
+
+       if (PageTransCompound(pfn_to_page(pfn))) {
+               unsigned long mask;
+               /*
+                * The address we faulted on is backed by a transparent huge
+                * page.  However, because we map the compound huge page and
+                * not the individual tail page, we need to transfer the
+                * refcount to the head page.  We have to be careful that the
+                * THP doesn't start to split while we are adjusting the
+                * refcounts.
+                *
+                * We are sure this doesn't happen, because mmu_notifier_retry
+                * was successful and we are holding the mmu_lock, so if this
+                * THP is trying to split, it will be blocked in the mmu
+                * notifier before touching any of the pages, specifically
+                * before being able to call __split_huge_page_refcount().
+                *
+                * We can therefore safely transfer the refcount from PG_tail
+                * to PG_head and switch the pfn from a tail page to the head
+                * page accordingly.
+                */
+               mask = PTRS_PER_PMD - 1;
+               VM_BUG_ON((gfn & mask) != (pfn & mask));
+               if (pfn & mask) {
+                       *ipap &= PMD_MASK;
+                       kvm_release_pfn_clean(pfn);
+                       pfn &= ~mask;
+                       kvm_get_pfn(pfn);
+                       *pfnp = pfn;
+               }
+
+               return true;
+       }
+
+       return false;
+}
+
 static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
-                         gfn_t gfn, struct kvm_memory_slot *memslot,
+                         struct kvm_memory_slot *memslot,
                          unsigned long fault_status)
 {
-       pte_t new_pte;
-       pfn_t pfn;
        int ret;
-       bool write_fault, writable;
+       bool write_fault, writable, hugetlb = false, force_pte = false;
        unsigned long mmu_seq;
+       gfn_t gfn = fault_ipa >> PAGE_SHIFT;
+       unsigned long hva = gfn_to_hva(vcpu->kvm, gfn);
+       struct kvm *kvm = vcpu->kvm;
        struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
+       struct vm_area_struct *vma;
+       pfn_t pfn;
 
        write_fault = kvm_is_write_fault(kvm_vcpu_get_hsr(vcpu));
        if (fault_status == FSC_PERM && !write_fault) {
@@ -524,6 +659,26 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
                return -EFAULT;
        }
 
+       /* Let's check if we will get back a huge page backed by hugetlbfs */
+       down_read(&current->mm->mmap_sem);
+       vma = find_vma_intersection(current->mm, hva, hva + 1);
+       if (is_vm_hugetlb_page(vma)) {
+               hugetlb = true;
+               gfn = (fault_ipa & PMD_MASK) >> PAGE_SHIFT;
+       } else {
+               /*
+                * Pages belonging to VMAs not aligned to the PMD mapping
+                * granularity cannot be mapped using block descriptors even
+                * if the pages belong to a THP for the process, because the
+                * stage-2 block descriptor will cover more than a single THP
+                * and we loose atomicity for unmapping, updates, and splits
+                * of the THP or other pages in the stage-2 block range.
+                */
+               if (vma->vm_start & ~PMD_MASK)
+                       force_pte = true;
+       }
+       up_read(&current->mm->mmap_sem);
+
        /* We need minimum second+third level pages */
        ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
        if (ret)
@@ -541,26 +696,40 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
         */
        smp_rmb();
 
-       pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
+       pfn = gfn_to_pfn_prot(kvm, gfn, write_fault, &writable);
        if (is_error_pfn(pfn))
                return -EFAULT;
 
-       new_pte = pfn_pte(pfn, PAGE_S2);
-       coherent_icache_guest_page(vcpu->kvm, gfn);
-
-       spin_lock(&vcpu->kvm->mmu_lock);
-       if (mmu_notifier_retry(vcpu->kvm, mmu_seq))
+       spin_lock(&kvm->mmu_lock);
+       if (mmu_notifier_retry(kvm, mmu_seq))
                goto out_unlock;
-       if (writable) {
-               kvm_set_s2pte_writable(&new_pte);
-               kvm_set_pfn_dirty(pfn);
+       if (!hugetlb && !force_pte)
+               hugetlb = transparent_hugepage_adjust(&pfn, &fault_ipa);
+
+       if (hugetlb) {
+               pmd_t new_pmd = pfn_pmd(pfn, PAGE_S2);
+               new_pmd = pmd_mkhuge(new_pmd);
+               if (writable) {
+                       kvm_set_s2pmd_writable(&new_pmd);
+                       kvm_set_pfn_dirty(pfn);
+               }
+               coherent_icache_guest_page(kvm, hva & PMD_MASK, PMD_SIZE);
+               ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd);
+       } else {
+               pte_t new_pte = pfn_pte(pfn, PAGE_S2);
+               if (writable) {
+                       kvm_set_s2pte_writable(&new_pte);
+                       kvm_set_pfn_dirty(pfn);
+               }
+               coherent_icache_guest_page(kvm, hva, PAGE_SIZE);
+               ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte, false);
        }
-       stage2_set_pte(vcpu->kvm, memcache, fault_ipa, &new_pte, false);
+
 
 out_unlock:
-       spin_unlock(&vcpu->kvm->mmu_lock);
+       spin_unlock(&kvm->mmu_lock);
        kvm_release_pfn_clean(pfn);
-       return 0;
+       return ret;
 }
 
 /**
@@ -629,7 +798,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
        memslot = gfn_to_memslot(vcpu->kvm, gfn);
 
-       ret = user_mem_abort(vcpu, fault_ipa, gfn, memslot, fault_status);
+       ret = user_mem_abort(vcpu, fault_ipa, memslot, fault_status);
        if (ret == 0)
                ret = 1;
 out_unlock:
index 86a693a..0881bf1 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/kvm_host.h>
 #include <linux/wait.h>
 
+#include <asm/cputype.h>
 #include <asm/kvm_emulate.h>
 #include <asm/kvm_psci.h>
 
@@ -34,22 +35,30 @@ static void kvm_psci_vcpu_off(struct kvm_vcpu *vcpu)
 static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
 {
        struct kvm *kvm = source_vcpu->kvm;
-       struct kvm_vcpu *vcpu;
+       struct kvm_vcpu *vcpu = NULL, *tmp;
        wait_queue_head_t *wq;
        unsigned long cpu_id;
+       unsigned long mpidr;
        phys_addr_t target_pc;
+       int i;
 
        cpu_id = *vcpu_reg(source_vcpu, 1);
        if (vcpu_mode_is_32bit(source_vcpu))
                cpu_id &= ~((u32) 0);
 
-       if (cpu_id >= atomic_read(&kvm->online_vcpus))
+       kvm_for_each_vcpu(i, tmp, kvm) {
+               mpidr = kvm_vcpu_get_mpidr(tmp);
+               if ((mpidr & MPIDR_HWID_BITMASK) == (cpu_id & MPIDR_HWID_BITMASK)) {
+                       vcpu = tmp;
+                       break;
+               }
+       }
+
+       if (!vcpu)
                return KVM_PSCI_RET_INVAL;
 
        target_pc = *vcpu_reg(source_vcpu, 2);
 
-       vcpu = kvm_get_vcpu(kvm, cpu_id);
-
        wq = kvm_arch_vcpu_wq(vcpu);
        if (!waitqueue_active(wq))
                return KVM_PSCI_RET_INVAL;
@@ -62,6 +71,10 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
                vcpu_set_thumb(vcpu);
        }
 
+       /* Propagate caller endianness */
+       if (kvm_vcpu_is_be(source_vcpu))
+               kvm_vcpu_set_be(vcpu);
+
        *vcpu_pc(vcpu) = target_pc;
        vcpu->arch.pause = false;
        smp_mb();               /* Make sure the above is visible */
index c02ba4a..f558c07 100644 (file)
 #include <kvm/arm_arch_timer.h>
 
 /******************************************************************************
- * Cortex-A15 Reset Values
+ * Cortex-A15 and Cortex-A7 Reset Values
  */
 
-static const int a15_max_cpu_idx = 3;
-
-static struct kvm_regs a15_regs_reset = {
+static struct kvm_regs cortexa_regs_reset = {
        .usr_regs.ARM_cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT,
 };
 
-static const struct kvm_irq_level a15_vtimer_irq = {
+static const struct kvm_irq_level cortexa_vtimer_irq = {
        { .irq = 27 },
        .level = 1,
 };
@@ -62,12 +60,11 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
        const struct kvm_irq_level *cpu_vtimer_irq;
 
        switch (vcpu->arch.target) {
+       case KVM_ARM_TARGET_CORTEX_A7:
        case KVM_ARM_TARGET_CORTEX_A15:
-               if (vcpu->vcpu_id > a15_max_cpu_idx)
-                       return -EINVAL;
-               reset_regs = &a15_regs_reset;
+               reset_regs = &cortexa_regs_reset;
                vcpu->arch.midr = read_cpuid_id();
-               cpu_vtimer_irq = &a15_vtimer_irq;
+               cpu_vtimer_irq = &cortexa_vtimer_irq;
                break;
        default:
                return -ENODEV;
index e0c68d5..52886b8 100644 (file)
@@ -10,7 +10,7 @@ UNWIND(       .fnstart        )
        and     r3, r0, #31             @ Get bit offset
        mov     r0, r0, lsr #5
        add     r1, r1, r0, lsl #2      @ Get word offset
-#if __LINUX_ARM_ARCH__ >= 7
+#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP)
        .arch_extension mp
        ALT_SMP(W(pldw) [r1])
        ALT_UP(W(nop))
index c1b7370..90aab2d 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for the linux kernel.
 #
 
-obj-y          := irq.o gpio.o setup.o
+obj-y          := irq.o gpio.o setup.o sysirq_mask.o
 obj-m          :=
 obj-n          :=
 obj-           :=
index f8629a3..d6a1fa8 100644 (file)
@@ -351,6 +351,8 @@ static void __init at91sam9260_initialize(void)
        arm_pm_idle = at91sam9_idle;
        arm_pm_restart = at91sam9_alt_restart;
 
+       at91_sysirq_mask_rtt(AT91SAM9260_BASE_RTT);
+
        /* Register GPIO subsystem */
        at91_gpio_init(at91sam9260_gpio, 3);
 }
index 1f3867a..23ba1d8 100644 (file)
@@ -293,6 +293,8 @@ static void __init at91sam9261_initialize(void)
        arm_pm_idle = at91sam9_idle;
        arm_pm_restart = at91sam9_alt_restart;
 
+       at91_sysirq_mask_rtt(AT91SAM9261_BASE_RTT);
+
        /* Register GPIO subsystem */
        at91_gpio_init(at91sam9261_gpio, 3);
 }
index 90d455d..7eccb0f 100644 (file)
@@ -330,6 +330,9 @@ static void __init at91sam9263_initialize(void)
        arm_pm_idle = at91sam9_idle;
        arm_pm_restart = at91sam9_alt_restart;
 
+       at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT0);
+       at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT1);
+
        /* Register GPIO subsystem */
        at91_gpio_init(at91sam9263_gpio, 5);
 }
index e9bf0b8..9405aa0 100644 (file)
@@ -379,6 +379,9 @@ static void __init at91sam9g45_initialize(void)
        arm_pm_idle = at91sam9_idle;
        arm_pm_restart = at91sam9g45_restart;
 
+       at91_sysirq_mask_rtc(AT91SAM9G45_BASE_RTC);
+       at91_sysirq_mask_rtt(AT91SAM9G45_BASE_RTT);
+
        /* Register GPIO subsystem */
        at91_gpio_init(at91sam9g45_gpio, 5);
 }
index 2d895a2..388ec3a 100644 (file)
@@ -224,7 +224,13 @@ static void __init at91sam9n12_map_io(void)
        at91_init_sram(0, AT91SAM9N12_SRAM_BASE, AT91SAM9N12_SRAM_SIZE);
 }
 
+static void __init at91sam9n12_initialize(void)
+{
+       at91_sysirq_mask_rtc(AT91SAM9N12_BASE_RTC);
+}
+
 AT91_SOC_START(at91sam9n12)
        .map_io = at91sam9n12_map_io,
        .register_clocks = at91sam9n12_register_clocks,
+       .init = at91sam9n12_initialize,
 AT91_SOC_END
index 88995af..0750ffb 100644 (file)
@@ -296,6 +296,9 @@ static void __init at91sam9rl_initialize(void)
        arm_pm_idle = at91sam9_idle;
        arm_pm_restart = at91sam9_alt_restart;
 
+       at91_sysirq_mask_rtc(AT91SAM9RL_BASE_RTC);
+       at91_sysirq_mask_rtt(AT91SAM9RL_BASE_RTT);
+
        /* Register GPIO subsystem */
        at91_gpio_init(at91sam9rl_gpio, 4);
 }
index 916e5a1..e8a2e07 100644 (file)
@@ -322,6 +322,11 @@ static void __init at91sam9x5_map_io(void)
        at91_init_sram(0, AT91SAM9X5_SRAM_BASE, AT91SAM9X5_SRAM_SIZE);
 }
 
+static void __init at91sam9x5_initialize(void)
+{
+       at91_sysirq_mask_rtc(AT91SAM9X5_BASE_RTC);
+}
+
 /* --------------------------------------------------------------------
  *  Interrupt initialization
  * -------------------------------------------------------------------- */
@@ -329,4 +334,5 @@ static void __init at91sam9x5_map_io(void)
 AT91_SOC_START(at91sam9x5)
        .map_io = at91sam9x5_map_io,
        .register_clocks = at91sam9x5_register_clocks,
+       .init = at91sam9x5_initialize,
 AT91_SOC_END
index 0b153c8..f4f8735 100644 (file)
@@ -28,7 +28,7 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/at73c213.h>
 #include <linux/clk.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/gpio_keys.h>
 #include <linux/input.h>
 
index 8b4942c..2f93191 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/fb.h>
 #include <linux/gpio_keys.h>
 #include <linux/input.h>
index dc6e2f5..26dee3c 100644 (file)
@@ -34,6 +34,8 @@ extern int  __init at91_aic_of_init(struct device_node *node,
                                    struct device_node *parent);
 extern int  __init at91_aic5_of_init(struct device_node *node,
                                    struct device_node *parent);
+extern void __init at91_sysirq_mask_rtc(u32 rtc_base);
+extern void __init at91_sysirq_mask_rtt(u32 rtt_base);
 
 
  /* Timer */
index d374b87..0151bcf 100644 (file)
 #define AT91SAM9N12_BASE_USART3        0xf8028000
 
 /*
+ * System Peripherals
+ */
+#define AT91SAM9N12_BASE_RTC   0xfffffeb0
+
+/*
  * Internal Memory.
  */
 #define AT91SAM9N12_SRAM_BASE  0x00300000      /* Internal SRAM base address */
index c75ee19..2fc76c4 100644 (file)
 #define AT91SAM9X5_BASE_USART2 0xf8024000
 
 /*
+ * System Peripherals
+ */
+#define AT91SAM9X5_BASE_RTC    0xfffffeb0
+
+/*
  * Internal Memory.
  */
 #define AT91SAM9X5_SRAM_BASE   0x00300000      /* Internal SRAM base address */
index 31096a8..25613d8 100644 (file)
 #define SAMA5D3_BASE_USART3    0xf8024000
 
 /*
+ * System Peripherals
+ */
+#define SAMA5D3_BASE_RTC       0xfffffeb0
+
+/*
  * Internal Memory
  */
 #define SAMA5D3_SRAM_BASE      0x00300000      /* Internal SRAM base address */
index 4012797..3ea8642 100644 (file)
@@ -371,7 +371,13 @@ static void __init sama5d3_map_io(void)
        at91_init_sram(0, SAMA5D3_SRAM_BASE, SAMA5D3_SRAM_SIZE);
 }
 
+static void __init sama5d3_initialize(void)
+{
+       at91_sysirq_mask_rtc(SAMA5D3_BASE_RTC);
+}
+
 AT91_SOC_START(sama5d3)
        .map_io = sama5d3_map_io,
        .register_clocks = sama5d3_register_clocks,
+       .init = sama5d3_initialize,
 AT91_SOC_END
diff --git a/arch/arm/mach-at91/sysirq_mask.c b/arch/arm/mach-at91/sysirq_mask.c
new file mode 100644 (file)
index 0000000..2ba694f
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * sysirq_mask.c - System-interrupt masking
+ *
+ * Copyright (C) 2013 Johan Hovold <jhovold@gmail.com>
+ *
+ * Functions to disable system interrupts from backup-powered peripherals.
+ *
+ * The RTC and RTT-peripherals are generally powered by backup power (VDDBU)
+ * and are not reset on wake-up, user, watchdog or software reset. This means
+ * that their interrupts may be enabled during early boot (e.g. after a user
+ * reset).
+ *
+ * As the RTC and RTT share the system-interrupt line with the PIT, an
+ * interrupt occurring before a handler has been installed would lead to the
+ * system interrupt being disabled and prevent the system from booting.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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 <mach/at91_rtt.h>
+
+#include "generic.h"
+
+#define AT91_RTC_IDR   0x24    /* Interrupt Disable Register */
+#define AT91_RTC_IMR   0x28    /* Interrupt Mask Register */
+
+void __init at91_sysirq_mask_rtc(u32 rtc_base)
+{
+       void __iomem *base;
+       u32 mask;
+
+       base = ioremap(rtc_base, 64);
+       if (!base)
+               return;
+
+       mask = readl_relaxed(base + AT91_RTC_IMR);
+       if (mask) {
+               pr_info("AT91: Disabling rtc irq\n");
+               writel_relaxed(mask, base + AT91_RTC_IDR);
+               (void)readl_relaxed(base + AT91_RTC_IMR);       /* flush */
+       }
+
+       iounmap(base);
+}
+
+void __init at91_sysirq_mask_rtt(u32 rtt_base)
+{
+       void __iomem *base;
+       void __iomem *reg;
+       u32 mode;
+
+       base = ioremap(rtt_base, 16);
+       if (!base)
+               return;
+
+       reg = base + AT91_RTT_MR;
+
+       mode = readl_relaxed(reg);
+       if (mode & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)) {
+               pr_info("AT91: Disabling rtt irq\n");
+               mode &= ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
+               writel_relaxed(mode, reg);
+               (void)readl_relaxed(reg);                       /* flush */
+       }
+
+       iounmap(base);
+}
index 40f15f1..d1f45af 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
 #include <linux/i2c/pcf857x.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/spi/spi.h>
index df16cb8..e0af0ec 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/platform_data/pca953x.h>
 #include <linux/input.h>
 #include <linux/input/tps6507x-ts.h>
index f4a6c18..e08a868 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/i2c.h>
 #include <linux/io.h>
 #include <linux/clk.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/leds.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index 9cc32c2..987605b 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/gpio.h>
 #include <linux/i2c.h>
 #include <linux/i2c/pcf857x.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
index 44b2019..13d0801 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/i2c/pcf857x.h>
 
 #include <media/tvp514x.h>
index cd0f587..7aa105b 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/regulator/machine.h>
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/etherdevice.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
index d843601..41c7c96 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
index 08332d8..0aded64 100644 (file)
@@ -7,7 +7,7 @@ config ARCH_HIGHBANK
        select ARCH_SUPPORTS_BIG_ENDIAN
        select ARCH_WANT_OPTIONAL_GPIOLIB
        select ARM_AMBA
-       select ARM_ERRATA_764369
+       select ARM_ERRATA_764369 if SMP
        select ARM_ERRATA_775420
        select ARM_ERRATA_798181 if SMP
        select ARM_GIC
index bbe1f5b..1789e2b 100644 (file)
@@ -102,8 +102,8 @@ obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o
 
 ifeq ($(CONFIG_PM),y)
 obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o
-# i.MX6SL reuses pm-imx6q.c
-obj-$(CONFIG_SOC_IMX6SL) += pm-imx6q.o
+# i.MX6SL reuses i.MX6Q code
+obj-$(CONFIG_SOC_IMX6SL) += pm-imx6q.o headsmp.o
 endif
 
 # i.MX5 based machines
index d756d91..04cfd0f 100644 (file)
@@ -122,13 +122,14 @@ static struct clk_div_table clk_enet_ref_table[] = {
        { .val = 1, .div = 10, },
        { .val = 2, .div = 5, },
        { .val = 3, .div = 4, },
+       { /* sentinel */ }
 };
 
 static struct clk_div_table post_div_table[] = {
        { .val = 2, .div = 1, },
        { .val = 1, .div = 2, },
        { .val = 0, .div = 4, },
-       { }
+       { /* sentinel */ }
 };
 
 static struct clk_div_table video_div_table[] = {
@@ -136,7 +137,7 @@ static struct clk_div_table video_div_table[] = {
        { .val = 1, .div = 2, },
        { .val = 2, .div = 1, },
        { .val = 3, .div = 4, },
-       { }
+       { /* sentinel */ }
 };
 
 static void __init imx6q_clocks_init(struct device_node *ccm_node)
@@ -298,7 +299,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
        clk[asrc_podf]        = imx_clk_divider("asrc_podf",        "asrc_pred",         base + 0x30, 9,  3);
        clk[spdif_pred]       = imx_clk_divider("spdif_pred",       "spdif_sel",         base + 0x30, 25, 3);
        clk[spdif_podf]       = imx_clk_divider("spdif_podf",       "spdif_pred",        base + 0x30, 22, 3);
-       clk[can_root]         = imx_clk_divider("can_root",         "pll3_usb_otg",      base + 0x20, 2,  6);
+       clk[can_root]         = imx_clk_divider("can_root",         "pll3_60m",          base + 0x20, 2,  6);
        clk[ecspi_root]       = imx_clk_divider("ecspi_root",       "pll3_60m",          base + 0x38, 19, 6);
        clk[gpu2d_core_podf]  = imx_clk_divider("gpu2d_core_podf",  "gpu2d_core_sel",    base + 0x18, 23, 3);
        clk[gpu3d_core_podf]  = imx_clk_divider("gpu3d_core_podf",  "gpu3d_core_sel",    base + 0x18, 26, 3);
index f6640b6..6136405 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
+#include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/jiffies.h>
@@ -45,33 +46,49 @@ struct clk_pllv3 {
 
 #define to_clk_pllv3(_hw) container_of(_hw, struct clk_pllv3, hw)
 
+static int clk_pllv3_wait_lock(struct clk_pllv3 *pll)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(10);
+       u32 val = readl_relaxed(pll->base) & BM_PLL_POWER;
+
+       /* No need to wait for lock when pll is not powered up */
+       if ((pll->powerup_set && !val) || (!pll->powerup_set && val))
+               return 0;
+
+       /* Wait for PLL to lock */
+       do {
+               if (readl_relaxed(pll->base) & BM_PLL_LOCK)
+                       break;
+               if (time_after(jiffies, timeout))
+                       break;
+               usleep_range(50, 500);
+       } while (1);
+
+       return readl_relaxed(pll->base) & BM_PLL_LOCK ? 0 : -ETIMEDOUT;
+}
+
 static int clk_pllv3_prepare(struct clk_hw *hw)
 {
        struct clk_pllv3 *pll = to_clk_pllv3(hw);
-       unsigned long timeout;
        u32 val;
+       int ret;
 
        val = readl_relaxed(pll->base);
-       val &= ~BM_PLL_BYPASS;
        if (pll->powerup_set)
                val |= BM_PLL_POWER;
        else
                val &= ~BM_PLL_POWER;
        writel_relaxed(val, pll->base);
 
-       timeout = jiffies + msecs_to_jiffies(10);
-       /* Wait for PLL to lock */
-       do {
-               if (readl_relaxed(pll->base) & BM_PLL_LOCK)
-                       break;
-               if (time_after(jiffies, timeout))
-                       break;
-       } while (1);
+       ret = clk_pllv3_wait_lock(pll);
+       if (ret)
+               return ret;
 
-       if (readl_relaxed(pll->base) & BM_PLL_LOCK)
-               return 0;
-       else
-               return -ETIMEDOUT;
+       val = readl_relaxed(pll->base);
+       val &= ~BM_PLL_BYPASS;
+       writel_relaxed(val, pll->base);
+
+       return 0;
 }
 
 static void clk_pllv3_unprepare(struct clk_hw *hw)
@@ -146,7 +163,7 @@ static int clk_pllv3_set_rate(struct clk_hw *hw, unsigned long rate,
        val |= div;
        writel_relaxed(val, pll->base);
 
-       return 0;
+       return clk_pllv3_wait_lock(pll);
 }
 
 static const struct clk_ops clk_pllv3_ops = {
@@ -202,7 +219,7 @@ static int clk_pllv3_sys_set_rate(struct clk_hw *hw, unsigned long rate,
        val |= div;
        writel_relaxed(val, pll->base);
 
-       return 0;
+       return clk_pllv3_wait_lock(pll);
 }
 
 static const struct clk_ops clk_pllv3_sys_ops = {
@@ -276,7 +293,7 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate,
        writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
        writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
 
-       return 0;
+       return clk_pllv3_wait_lock(pll);
 }
 
 static const struct clk_ops clk_pllv3_av_ops = {
index 7cbe22d..24a7899 100644 (file)
@@ -127,11 +127,6 @@ static inline void imx_smp_prepare(void) {}
 static inline void imx_scu_standby_enable(void) {}
 #endif
 void imx_src_init(void);
-#ifdef CONFIG_HAVE_IMX_SRC
-void imx_src_prepare_restart(void);
-#else
-static inline void imx_src_prepare_restart(void) {}
-#endif
 void imx_gpc_init(void);
 void imx_gpc_pre_suspend(void);
 void imx_gpc_post_resume(void);
index 19bb644..c5f9567 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/dma-mapping.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/eeprom.h>
index 45303bd..639a3df 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/smsc911x.h>
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/delay.h>
 #include <linux/spi/spi.h>
 #include <linux/irq.h>
index e805ac2..592ddbe 100644 (file)
@@ -18,7 +18,7 @@
  */
 
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/io.h>
 #include <linux/mtd/plat-ram.h>
 #include <linux/mtd/physmap.h>
index b726cb1..ac504b6 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/ulpi.h>
 
index 0910761..8825d12 100644 (file)
@@ -29,7 +29,7 @@
 #include <asm/mach/time.h>
 
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/mfd/mc13xxx.h>
 
 #include "common.h"
index 4754373..45f7f4e 100644 (file)
@@ -115,21 +115,6 @@ void imx_set_cpu_arg(int cpu, u32 arg)
        writel_relaxed(arg, src_base + SRC_GPR1 + cpu * 8 + 4);
 }
 
-void imx_src_prepare_restart(void)
-{
-       u32 val;
-
-       /* clear enable bits of secondary cores */
-       spin_lock(&scr_lock);
-       val = readl_relaxed(src_base + SRC_SCR);
-       val &= ~(0x7 << BP_SRC_SCR_CORE1_ENABLE);
-       writel_relaxed(val, src_base + SRC_SCR);
-       spin_unlock(&scr_lock);
-
-       /* clear persistent entry register of primary core */
-       writel_relaxed(0, src_base + SRC_GPR1);
-}
-
 void __init imx_src_init(void)
 {
        struct device_node *np;
index e6edcd3..5e3027d 100644 (file)
@@ -42,9 +42,6 @@ void mxc_restart(enum reboot_mode mode, const char *cmd)
 {
        unsigned int wcr_enable;
 
-       if (cpu_is_imx6q() || cpu_is_imx6dl())
-               imx_src_prepare_restart();
-
        if (wdog_clk)
                clk_enable(wdog_clk);
 
@@ -55,7 +52,14 @@ void mxc_restart(enum reboot_mode mode, const char *cmd)
 
        /* Assert SRS signal */
        __raw_writew(wcr_enable, wdog_base);
-       /* write twice to ensure the request will not get ignored */
+       /*
+        * Due to imx6q errata ERR004346 (WDOG: WDOG SRS bit requires to be
+        * written twice), we add another two writes to ensure there must be at
+        * least two writes happen in the same one 32kHz clock period.  We save
+        * the target check here, since the writes shouldn't be a huge burden
+        * for other platforms.
+        */
+       __raw_writew(wcr_enable, wdog_base);
        __raw_writew(wcr_enable, wdog_base);
 
        /* wait for reset to assert... */
index 1df6e76..4fc0a19 100644 (file)
@@ -198,7 +198,8 @@ static struct mmci_platform_data mmc_data = {
 static void cp_clcd_enable(struct clcd_fb *fb)
 {
        struct fb_var_screeninfo *var = &fb->fb.var;
-       u32 val = CM_CTRL_STATIC1 | CM_CTRL_STATIC2;
+       u32 val = CM_CTRL_STATIC1 | CM_CTRL_STATIC2
+                       | CM_CTRL_LCDEN0 | CM_CTRL_LCDEN1;
 
        if (var->bits_per_pixel <= 8 ||
            (var->bits_per_pixel == 16 && var->green.length == 5))
index 6d3782d..a86fd0e 100644 (file)
@@ -218,20 +218,6 @@ iop_chan_xor_slot_count(size_t len, int src_cnt, int *slots_per_op)
 #define iop_chan_pq_slot_count iop_chan_xor_slot_count
 #define iop_chan_pq_zero_sum_slot_count iop_chan_xor_slot_count
 
-static inline u32 iop_desc_get_dest_addr(struct iop_adma_desc_slot *desc,
-                                       struct iop_adma_chan *chan)
-{
-       struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
-       return hw_desc->dest_addr;
-}
-
-static inline u32 iop_desc_get_qdest_addr(struct iop_adma_desc_slot *desc,
-                                         struct iop_adma_chan *chan)
-{
-       struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
-       return hw_desc->q_dest_addr;
-}
-
 static inline u32 iop_desc_get_byte_count(struct iop_adma_desc_slot *desc,
                                        struct iop_adma_chan *chan)
 {
@@ -350,18 +336,6 @@ iop_desc_init_pq(struct iop_adma_desc_slot *desc, int src_cnt,
        hw_desc->desc_ctrl = u_desc_ctrl.value;
 }
 
-static inline int iop_desc_is_pq(struct iop_adma_desc_slot *desc)
-{
-       struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
-       union {
-               u32 value;
-               struct iop13xx_adma_desc_ctrl field;
-       } u_desc_ctrl;
-
-       u_desc_ctrl.value = hw_desc->desc_ctrl;
-       return u_desc_ctrl.field.pq_xfer_en;
-}
-
 static inline void
 iop_desc_init_pq_zero_sum(struct iop_adma_desc_slot *desc, int src_cnt,
                          unsigned long flags)
index 4894959..8e3e433 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/spi/flash.h>
 #include <linux/spi/spi.h>
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/gpio.h>
 #include <asm/mach/time.h>
 #include <mach/kirkwood.h>
index a7ce692..d68909b 100644 (file)
@@ -300,7 +300,7 @@ static struct omap_lcd_config osk_lcd_config __initdata = {
 #ifdef CONFIG_OMAP_OSK_MISTRAL
 
 #include <linux/input.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
 
index e15ac00..1f25f3e 100644 (file)
@@ -40,7 +40,7 @@ omap-4-5-common                               =  omap4-common.o omap-wakeupgen.o
 obj-$(CONFIG_ARCH_OMAP4)               += $(omap-4-5-common) $(smp-y) sleep44xx.o
 obj-$(CONFIG_SOC_OMAP5)                        += $(omap-4-5-common) $(smp-y) sleep44xx.o
 obj-$(CONFIG_SOC_AM43XX)               += $(omap-4-5-common)
-obj-$(CONFIG_SOC_DRA7XX)               += $(omap-4-5-common) $(smp-y)
+obj-$(CONFIG_SOC_DRA7XX)               += $(omap-4-5-common) $(smp-y) sleep44xx.o
 
 plus_sec := $(call as-instr,.arch_extension sec,+sec)
 AFLAGS_omap-headsmp.o                  :=-Wa,-march=armv7-a$(plus_sec)
index 33d159e..8dd0ec8 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/gpio.h>
 #include <linux/platform_data/gpio-omap.h>
 
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/i2c/twl.h>
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/machine.h>
index 87e41a8..f780834 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/input.h>
 #include <linux/err.h>
 #include <linux/clk.h>
index a516c1b..d6ed819 100644 (file)
@@ -510,7 +510,7 @@ static int __init beagle_opp_init(void)
                mpu_dev = get_cpu_device(0);
                iva_dev = omap_device_get_by_hwmod_name("iva");
 
-               if (IS_ERR(mpu_dev) || IS_ERR(iva_dev)) {
+               if (!mpu_dev || IS_ERR(iva_dev)) {
                        pr_err("%s: Aiee.. no mpu/dsp devices? %p %p\n",
                                __func__, mpu_dev, iva_dev);
                        return -ENODEV;
index ba8342f..119efaf 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/spi/spi.h>
 #include <linux/interrupt.h>
 #include <linux/smsc911x.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/usb/phy.h>
 
 #include <asm/mach-types.h>
index 03a2829..3b05aea 100644 (file)
@@ -381,6 +381,42 @@ static struct clk_hw_omap dpll4_ck_hw = {
 
 DEFINE_STRUCT_CLK(dpll4_ck, dpll3_ck_parent_names, dpll4_ck_ops);
 
+static const struct clk_div_table dpll4_mx_ck_div_table[] = {
+       { .div = 1, .val = 1 },
+       { .div = 2, .val = 2 },
+       { .div = 3, .val = 3 },
+       { .div = 4, .val = 4 },
+       { .div = 5, .val = 5 },
+       { .div = 6, .val = 6 },
+       { .div = 7, .val = 7 },
+       { .div = 8, .val = 8 },
+       { .div = 9, .val = 9 },
+       { .div = 10, .val = 10 },
+       { .div = 11, .val = 11 },
+       { .div = 12, .val = 12 },
+       { .div = 13, .val = 13 },
+       { .div = 14, .val = 14 },
+       { .div = 15, .val = 15 },
+       { .div = 16, .val = 16 },
+       { .div = 17, .val = 17 },
+       { .div = 18, .val = 18 },
+       { .div = 19, .val = 19 },
+       { .div = 20, .val = 20 },
+       { .div = 21, .val = 21 },
+       { .div = 22, .val = 22 },
+       { .div = 23, .val = 23 },
+       { .div = 24, .val = 24 },
+       { .div = 25, .val = 25 },
+       { .div = 26, .val = 26 },
+       { .div = 27, .val = 27 },
+       { .div = 28, .val = 28 },
+       { .div = 29, .val = 29 },
+       { .div = 30, .val = 30 },
+       { .div = 31, .val = 31 },
+       { .div = 32, .val = 32 },
+       { .div = 0 },
+};
+
 DEFINE_CLK_DIVIDER(dpll4_m5_ck, "dpll4_ck", &dpll4_ck, 0x0,
                   OMAP_CM_REGADDR(OMAP3430_CAM_MOD, CM_CLKSEL),
                   OMAP3430_CLKSEL_CAM_SHIFT, OMAP3630_CLKSEL_CAM_WIDTH,
@@ -524,10 +560,10 @@ static const struct clksel_rate clkout2_src_54m_rates[] = {
        { .div = 0 }
 };
 
-DEFINE_CLK_DIVIDER(dpll4_m3_ck, "dpll4_ck", &dpll4_ck, 0x0,
+DEFINE_CLK_DIVIDER_TABLE(dpll4_m3_ck, "dpll4_ck", &dpll4_ck, 0x0,
                   OMAP_CM_REGADDR(OMAP3430_DSS_MOD, CM_CLKSEL),
                   OMAP3430_CLKSEL_TV_SHIFT, OMAP3630_CLKSEL_TV_WIDTH,
-                  CLK_DIVIDER_ONE_BASED, NULL);
+                  0, dpll4_mx_ck_div_table, NULL);
 
 static struct clk dpll4_m3x2_ck;
 
@@ -847,10 +883,10 @@ static struct clk dpll3_m3x2_ck_3630 = {
 
 DEFINE_CLK_FIXED_FACTOR(dpll3_x2_ck, "dpll3_ck", &dpll3_ck, 0x0, 2, 1);
 
-DEFINE_CLK_DIVIDER(dpll4_m4_ck, "dpll4_ck", &dpll4_ck, 0x0,
+DEFINE_CLK_DIVIDER_TABLE(dpll4_m4_ck, "dpll4_ck", &dpll4_ck, 0x0,
                   OMAP_CM_REGADDR(OMAP3430_DSS_MOD, CM_CLKSEL),
                   OMAP3430_CLKSEL_DSS1_SHIFT, OMAP3630_CLKSEL_DSS1_WIDTH,
-                  CLK_DIVIDER_ONE_BASED, NULL);
+                  0, dpll4_mx_ck_div_table, NULL);
 
 static struct clk dpll4_m4x2_ck;
 
@@ -869,7 +905,8 @@ static struct clk_hw_omap dpll4_m4x2_ck_hw = {
        .clkdm_name     = "dpll4_clkdm",
 };
 
-DEFINE_STRUCT_CLK(dpll4_m4x2_ck, dpll4_m4x2_ck_parent_names, dpll4_m5x2_ck_ops);
+DEFINE_STRUCT_CLK_FLAGS(dpll4_m4x2_ck, dpll4_m4x2_ck_parent_names,
+               dpll4_m5x2_ck_ops, CLK_SET_RATE_PARENT);
 
 static struct clk dpll4_m4x2_ck_3630 = {
        .name           = "dpll4_m4x2_ck",
@@ -877,6 +914,7 @@ static struct clk dpll4_m4x2_ck_3630 = {
        .parent_names   = dpll4_m4x2_ck_parent_names,
        .num_parents    = ARRAY_SIZE(dpll4_m4x2_ck_parent_names),
        .ops            = &dpll4_m5x2_ck_3630_ops,
+       .flags          = CLK_SET_RATE_PARENT,
 };
 
 DEFINE_CLK_DIVIDER(dpll4_m6_ck, "dpll4_ck", &dpll4_ck, 0x0,
@@ -968,8 +1006,9 @@ static struct clk_hw_omap dss1_alwon_fck_3430es1_hw = {
        .clkdm_name     = "dss_clkdm",
 };
 
-DEFINE_STRUCT_CLK(dss1_alwon_fck_3430es1, dss1_alwon_fck_3430es1_parent_names,
-                 aes2_ick_ops);
+DEFINE_STRUCT_CLK_FLAGS(dss1_alwon_fck_3430es1,
+               dss1_alwon_fck_3430es1_parent_names, aes2_ick_ops,
+               CLK_SET_RATE_PARENT);
 
 static struct clk dss1_alwon_fck_3430es2;
 
@@ -983,8 +1022,9 @@ static struct clk_hw_omap dss1_alwon_fck_3430es2_hw = {
        .clkdm_name     = "dss_clkdm",
 };
 
-DEFINE_STRUCT_CLK(dss1_alwon_fck_3430es2, dss1_alwon_fck_3430es1_parent_names,
-                 aes2_ick_ops);
+DEFINE_STRUCT_CLK_FLAGS(dss1_alwon_fck_3430es2,
+               dss1_alwon_fck_3430es1_parent_names, aes2_ick_ops,
+               CLK_SET_RATE_PARENT);
 
 static struct clk dss2_alwon_fck;
 
index b237950..ec0dc0b 100644 (file)
@@ -830,7 +830,8 @@ DEFINE_CLK_GATE(dss_tv_clk, "extalt_clkin_ck", &extalt_clkin_ck, 0x0,
                OMAP4430_CM_DSS_DSS_CLKCTRL,
                OMAP4430_OPTFCLKEN_TV_CLK_SHIFT, 0x0, NULL);
 
-DEFINE_CLK_GATE(dss_dss_clk, "dpll_per_m5x2_ck", &dpll_per_m5x2_ck, 0x0,
+DEFINE_CLK_GATE(dss_dss_clk, "dpll_per_m5x2_ck", &dpll_per_m5x2_ck,
+               CLK_SET_RATE_PARENT,
                OMAP4430_CM_DSS_DSS_CLKCTRL, OMAP4430_OPTFCLKEN_DSSCLK_SHIFT,
                0x0, NULL);
 
index ef99011..2757504 100644 (file)
@@ -83,7 +83,7 @@ void __init gpmc_smsc911x_init(struct omap_smsc911x_platform_data *gpmc_cfg)
        pdev = platform_device_register_resndata(NULL, "smsc911x", gpmc_cfg->id,
                 gpmc_smsc911x_resources, ARRAY_SIZE(gpmc_smsc911x_resources),
                 &gpmc_smsc911x_config, sizeof(gpmc_smsc911x_config));
-       if (!pdev) {
+       if (IS_ERR(pdev)) {
                pr_err("Unable to register platform device\n");
                gpio_free(gpmc_cfg->gpio_reset);
                goto free2;
index b69dd9a..53f0735 100644 (file)
@@ -621,6 +621,7 @@ static int _od_suspend_noirq(struct device *dev)
 
        if (!ret && !pm_runtime_status_suspended(dev)) {
                if (pm_generic_runtime_suspend(dev) == 0) {
+                       pm_runtime_set_suspended(dev);
                        omap_device_idle(pdev);
                        od->flags |= OMAP_DEVICE_SUSPENDED;
                }
@@ -634,10 +635,18 @@ static int _od_resume_noirq(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct omap_device *od = to_omap_device(pdev);
 
-       if ((od->flags & OMAP_DEVICE_SUSPENDED) &&
-           !pm_runtime_status_suspended(dev)) {
+       if (od->flags & OMAP_DEVICE_SUSPENDED) {
                od->flags &= ~OMAP_DEVICE_SUSPENDED;
                omap_device_enable(pdev);
+               /*
+                * XXX: we run before core runtime pm has resumed itself. At
+                * this point in time, we just restore the runtime pm state and
+                * considering symmetric operations in resume, we donot expect
+                * to fail. If we failed, something changed in core runtime_pm
+                * framework OR some device driver messed things up, hence, WARN
+                */
+               WARN(pm_runtime_set_active(dev),
+                    "Could not set %s runtime state active\n", dev_name(dev));
                pm_generic_runtime_resume(dev);
        }
 
index a085d9c..7a97606 100644 (file)
@@ -42,7 +42,8 @@ extern u32 omap4_prm_vcvp_read(u8 offset);
 extern void omap4_prm_vcvp_write(u32 val, u8 offset);
 extern u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset);
 
-#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5)
+#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \
+       defined(CONFIG_SOC_DRA7XX)
 void omap44xx_prm_reconfigure_io_chain(void);
 #else
 static inline void omap44xx_prm_reconfigure_io_chain(void)
index f942349..584439b 100644 (file)
@@ -310,6 +310,7 @@ static struct platform_pwm_backlight_data cm_x300_backlight_data = {
        .max_brightness = 100,
        .dft_brightness = 100,
        .pwm_period_ns  = 10000,
+       .enable_gpio    = -1,
 };
 
 static struct platform_device cm_x300_backlight_device = {
index 2d4a7b4..3aa2646 100644 (file)
@@ -189,6 +189,7 @@ static struct platform_pwm_backlight_data income_backlight_data = {
        .max_brightness = 0x3ff,
        .dft_brightness = 0x1ff,
        .pwm_period_ns  = 1000000,
+       .enable_gpio    = -1,
 };
 
 static struct platform_device income_backlight = {
index fe2eb83..ab93441 100644 (file)
@@ -54,6 +54,7 @@ static struct platform_pwm_backlight_data ezx_backlight_data = {
        .max_brightness = 1023,
        .dft_brightness = 1023,
        .pwm_period_ns  = 78770,
+       .enable_gpio    = -1,
 };
 
 static struct platform_device ezx_backlight_device = {
index 133109e..a7c30eb 100644 (file)
@@ -561,6 +561,7 @@ static struct platform_pwm_backlight_data backlight_data = {
        .max_brightness = 200,
        .dft_brightness = 100,
        .pwm_period_ns  = 30923,
+       .enable_gpio    = -1,
 };
 
 static struct platform_device backlight = {
index 1255ee0..9f6ec16 100644 (file)
@@ -269,6 +269,7 @@ static struct platform_pwm_backlight_data lpd270_backlight_data = {
        .max_brightness = 1,
        .dft_brightness = 1,
        .pwm_period_ns  = 78770,
+       .enable_gpio    = -1,
 };
 
 static struct platform_device lpd270_backlight_device = {
index f44532f..fab30d6 100644 (file)
@@ -378,6 +378,7 @@ static struct platform_pwm_backlight_data backlight_data = {
        .max_brightness = 272,
        .dft_brightness = 100,
        .pwm_period_ns  = 30923,
+       .enable_gpio    = -1,
        .init           = magician_backlight_init,
        .notify         = magician_backlight_notify,
        .exit           = magician_backlight_exit,
index dd70343..08ccc07 100644 (file)
@@ -338,6 +338,7 @@ static struct platform_pwm_backlight_data mainstone_backlight_data = {
        .max_brightness = 1023,
        .dft_brightness = 1023,
        .pwm_period_ns  = 78770,
+       .enable_gpio    = -1,
 };
 
 static struct platform_device mainstone_backlight_device = {
index acc9d3c..f70583f 100644 (file)
@@ -186,6 +186,7 @@ static struct platform_pwm_backlight_data mioa701_backlight_data = {
        .max_brightness = 100,
        .dft_brightness = 50,
        .pwm_period_ns  = 4000 * 1024,  /* Fl = 250kHz */
+       .enable_gpio    = -1,
 };
 
 /*
index 17d4c53..e54a296 100644 (file)
@@ -322,6 +322,7 @@ static struct platform_pwm_backlight_data palm27x_backlight_data = {
        .max_brightness = 0xfe,
        .dft_brightness = 0x7e,
        .pwm_period_ns  = 3500 * 1024,
+       .enable_gpio    = -1,
        .init           = palm27x_backlight_init,
        .notify         = palm27x_backlight_notify,
        .exit           = palm27x_backlight_exit,
index 100b176..7691c97 100644 (file)
@@ -166,45 +166,12 @@ static inline void palmtc_keys_init(void) {}
  * Backlight
  ******************************************************************************/
 #if defined(CONFIG_BACKLIGHT_PWM) || defined(CONFIG_BACKLIGHT_PWM_MODULE)
-static int palmtc_backlight_init(struct device *dev)
-{
-       int ret;
-
-       ret = gpio_request(GPIO_NR_PALMTC_BL_POWER, "BL POWER");
-       if (ret)
-               goto err;
-       ret = gpio_direction_output(GPIO_NR_PALMTC_BL_POWER, 1);
-       if (ret)
-               goto err2;
-
-       return 0;
-
-err2:
-       gpio_free(GPIO_NR_PALMTC_BL_POWER);
-err:
-       return ret;
-}
-
-static int palmtc_backlight_notify(struct device *dev, int brightness)
-{
-       /* backlight is on when GPIO16 AF0 is high */
-       gpio_set_value(GPIO_NR_PALMTC_BL_POWER, brightness);
-       return brightness;
-}
-
-static void palmtc_backlight_exit(struct device *dev)
-{
-       gpio_free(GPIO_NR_PALMTC_BL_POWER);
-}
-
 static struct platform_pwm_backlight_data palmtc_backlight_data = {
        .pwm_id         = 1,
        .max_brightness = PALMTC_MAX_INTENSITY,
        .dft_brightness = PALMTC_MAX_INTENSITY,
        .pwm_period_ns  = PALMTC_PERIOD_NS,
-       .init           = palmtc_backlight_init,
-       .notify         = palmtc_backlight_notify,
-       .exit           = palmtc_backlight_exit,
+       .enable_gpio    = GPIO_NR_PALMTC_BL_POWER,
 };
 
 static struct platform_device palmtc_backlight = {
index 0742721..956fd24 100644 (file)
@@ -165,6 +165,7 @@ static struct platform_pwm_backlight_data palmte2_backlight_data = {
        .max_brightness = PALMTE2_MAX_INTENSITY,
        .dft_brightness = PALMTE2_MAX_INTENSITY,
        .pwm_period_ns  = PALMTE2_PERIOD_NS,
+       .enable_gpio    = -1,
        .init           = palmte2_backlight_init,
        .notify         = palmte2_backlight_notify,
        .exit           = palmte2_backlight_exit,
index 3133ba8..9a4e470 100644 (file)
@@ -153,6 +153,7 @@ static struct platform_pwm_backlight_data pcm990_backlight_data = {
        .max_brightness = 1023,
        .dft_brightness = 1023,
        .pwm_period_ns  = 78770,
+       .enable_gpio    = -1,
 };
 
 static struct platform_device pcm990_backlight_device = {
index 969b0ba..8386dc3 100644 (file)
@@ -539,6 +539,7 @@ static struct platform_pwm_backlight_data raumfeld_pwm_backlight_data = {
        .dft_brightness = 100,
        /* 10000 ns = 10 ms ^= 100 kHz */
        .pwm_period_ns  = 10000,
+       .enable_gpio    = -1,
 };
 
 static struct platform_device raumfeld_pwm_backlight_device = {
index 62aea3e..01de542 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <linux/i2c/pxa-i2c.h>
 #include <linux/i2c/pcf857x.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/smc91x.h>
 #include <linux/gpio.h>
 #include <linux/leds.h>
index 4680efe..a71da84 100644 (file)
@@ -175,6 +175,7 @@ static struct platform_pwm_backlight_data tavorevb_backlight_data[] = {
                .max_brightness = 100,
                .dft_brightness = 100,
                .pwm_period_ns  = 100000,
+               .enable_gpio    = -1,
        },
        [1] = {
                /* secondary backlight */
@@ -182,6 +183,7 @@ static struct platform_pwm_backlight_data tavorevb_backlight_data[] = {
                .max_brightness = 100,
                .dft_brightness = 100,
                .pwm_period_ns  = 100000,
+               .enable_gpio    = -1,
        },
 };
 
index 9c363c0..29905b1 100644 (file)
@@ -401,6 +401,7 @@ static struct platform_pwm_backlight_data viper_backlight_data = {
        .max_brightness = 100,
        .dft_brightness = 100,
        .pwm_period_ns  = 1000000,
+       .enable_gpio    = -1,
        .init           = viper_backlight_init,
        .notify         = viper_backlight_notify,
        .exit           = viper_backlight_exit,
index 2513d8f..e1a121b 100644 (file)
@@ -206,6 +206,7 @@ static struct platform_pwm_backlight_data z2_backlight_data[] = {
                .max_brightness = 1023,
                .dft_brightness = 0,
                .pwm_period_ns  = 1260320,
+               .enable_gpio    = -1,
        },
        [1] = {
                /* LCD Backlight */
@@ -213,6 +214,7 @@ static struct platform_pwm_backlight_data z2_backlight_data[] = {
                .max_brightness = 1023,
                .dft_brightness = 512,
                .pwm_period_ns  = 1260320,
+               .enable_gpio    = -1,
        },
 };
 
index 36cf7cf..77daea4 100644 (file)
@@ -125,6 +125,7 @@ static struct platform_pwm_backlight_data zylonite_backlight_data = {
        .max_brightness = 100,
        .dft_brightness = 100,
        .pwm_period_ns  = 10000,
+       .enable_gpio    = -1,
 };
 
 static struct platform_device zylonite_backlight_device = {
index 74dd479..952b6a0 100644 (file)
@@ -504,6 +504,7 @@ static struct platform_pwm_backlight_data backlight_data = {
        .dft_brightness = 50,
        /* tcnt = 0x31 */
        .pwm_period_ns  = 36296,
+       .enable_gpio    = -1,
        .init           = h1940_backlight_init,
        .notify         = h1940_backlight_notify,
        .exit           = h1940_backlight_exit,
index a83db46..4a18d49 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/io.h>
 #include <linux/serial_core.h>
 #include <linux/dm9000.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 #include <linux/platform_device.h>
 #include <linux/gpio_keys.h>
 #include <linux/i2c.h>
index 206b1f7..034b7fe 100644 (file)
@@ -522,6 +522,7 @@ static struct platform_pwm_backlight_data rx1950_backlight_data = {
        .max_brightness = 24,
        .dft_brightness = 4,
        .pwm_period_ns = 48000,
+       .enable_gpio = -1,
        .init = rx1950_backlight_init,
        .notify = rx1950_backlight_notify,
        .exit = rx1950_backlight_exit,
index aca7d16..758e31b 100644 (file)
@@ -114,6 +114,7 @@ static struct platform_pwm_backlight_data crag6410_backlight_data = {
        .max_brightness = 1000,
        .dft_brightness = 600,
        .pwm_period_ns  = 100000,       /* about 1kHz */
+       .enable_gpio    = -1,
 };
 
 static struct platform_device crag6410_backlight_device = {
index e806404..614a03a 100644 (file)
@@ -114,6 +114,7 @@ static struct platform_pwm_backlight_data hmt_backlight_data = {
        .max_brightness = 100 * 256,
        .dft_brightness = 40 * 256,
        .pwm_period_ns  = 1000000000 / (100 * 256 * 20),
+       .enable_gpio    = -1,
        .init           = hmt_bl_init,
        .notify         = hmt_bl_notify,
        .exit           = hmt_bl_exit,
index 0f47237..a6b338f 100644 (file)
@@ -151,6 +151,7 @@ static struct platform_pwm_backlight_data smartq_backlight_data = {
        .max_brightness = 1000,
        .dft_brightness = 600,
        .pwm_period_ns  = 1000000000 / (1000 * 20),
+       .enable_gpio    = -1,
        .init           = smartq_bl_init,
 };
 
index 2a7b32c..d5ea938 100644 (file)
@@ -625,6 +625,7 @@ static struct samsung_bl_gpio_info smdk6410_bl_gpio_info = {
 
 static struct platform_pwm_backlight_data smdk6410_bl_data = {
        .pwm_id = 1,
+       .enable_gpio = -1,
 };
 
 static struct s3c_hsotg_plat smdk6410_hsotg_pdata;
index 0b00304..9efdcc0 100644 (file)
@@ -223,6 +223,7 @@ static struct samsung_bl_gpio_info smdk6440_bl_gpio_info = {
 
 static struct platform_pwm_backlight_data smdk6440_bl_data = {
        .pwm_id = 1,
+       .enable_gpio = -1,
 };
 
 static void __init smdk6440_map_io(void)
index 5949296..c3cacc0 100644 (file)
@@ -242,6 +242,7 @@ static struct samsung_bl_gpio_info smdk6450_bl_gpio_info = {
 
 static struct platform_pwm_backlight_data smdk6450_bl_data = {
        .pwm_id = 1,
+       .enable_gpio = -1,
 };
 
 static void __init smdk6450_map_io(void)
index 7c57a22..9e256b9 100644 (file)
@@ -216,6 +216,7 @@ static struct samsung_bl_gpio_info smdkc100_bl_gpio_info = {
 
 static struct platform_pwm_backlight_data smdkc100_bl_data = {
        .pwm_id = 0,
+       .enable_gpio = -1,
 };
 
 static void __init smdkc100_map_io(void)
index 6d72bb9..f52cc15 100644 (file)
@@ -279,6 +279,7 @@ static struct samsung_bl_gpio_info smdkv210_bl_gpio_info = {
 static struct platform_pwm_backlight_data smdkv210_bl_data = {
        .pwm_id = 3,
        .pwm_period_ns = 1000,
+       .enable_gpio = -1,
 };
 
 static void __init smdkv210_map_io(void)
index 8bc8e4c..958e3cb 100644 (file)
@@ -423,6 +423,7 @@ static struct platform_pwm_backlight_data pwm_backlight_data = {
        .max_brightness = 255,
        .dft_brightness = 255,
        .pwm_period_ns = 33333, /* 30kHz */
+       .enable_gpio = -1,
 };
 
 static struct platform_device pwm_backlight_device = {
index 835833e..d71654b 100644 (file)
@@ -12,7 +12,7 @@ menuconfig ARCH_STI
        select HAVE_ARM_SCU if SMP
        select ARCH_REQUIRE_GPIOLIB
        select ARM_ERRATA_754322
-       select ARM_ERRATA_764369
+       select ARM_ERRATA_764369 if SMP
        select ARM_ERRATA_775420
        select PL310_ERRATA_753970 if CACHE_PL310
        select PL310_ERRATA_769419 if CACHE_PL310
@@ -30,7 +30,7 @@ config SOC_STIH415
        default y
        help
          This enables support for STMicroelectronics Digital Consumer
-         Electronics family StiH415 parts, primarily targetted at set-top-box
+         Electronics family StiH415 parts, primarily targeted at set-top-box
          and other digital audio/video applications using Flattned Device
          Trees.
 
@@ -39,7 +39,7 @@ config SOC_STIH416
        default y
        help
          This enables support for STMicroelectronics Digital Consumer
-         Electronics family StiH416 parts, primarily targetted at set-top-box
+         Electronics family StiH416 parts, primarily targeted at set-top-box
          and other digital audio/video applications using Flattened Device
          Trees.
 
index d7aa52e..bc47197 100644 (file)
@@ -114,7 +114,7 @@ static int do_dma_transfer(unsigned long apb_add,
        dma_desc->callback = apb_dma_complete;
        dma_desc->callback_param = NULL;
 
-       INIT_COMPLETION(tegra_apb_wait);
+       reinit_completion(&tegra_apb_wait);
 
        dmaengine_submit(dma_desc);
        dma_async_issue_pending(tegra_apb_dma_chan);
index ce553d5..7336817 100644 (file)
@@ -90,9 +90,9 @@ static void __init tegra_init_cache(void)
 
 static void __init tegra_init_early(void)
 {
-       tegra_cpu_reset_handler_init();
        tegra_apb_io_init();
        tegra_init_fuse();
+       tegra_cpu_reset_handler_init();
        tegra_init_cache();
        tegra_powergate_init();
        tegra_hotplug_init();
index 2a5907b..ff379ac 100644 (file)
@@ -65,7 +65,7 @@ static int do_adjust_pte(struct vm_area_struct *vma, unsigned long address,
        return ret;
 }
 
-#if USE_SPLIT_PTLOCKS
+#if USE_SPLIT_PTE_PTLOCKS
 /*
  * If we are using split PTE locks, then we need to take the page
  * lock here.  Otherwise we are using shared mm->page_table_lock
@@ -84,10 +84,10 @@ static inline void do_pte_unlock(spinlock_t *ptl)
 {
        spin_unlock(ptl);
 }
-#else /* !USE_SPLIT_PTLOCKS */
+#else /* !USE_SPLIT_PTE_PTLOCKS */
 static inline void do_pte_lock(spinlock_t *ptl) {}
 static inline void do_pte_unlock(spinlock_t *ptl) {}
-#endif /* USE_SPLIT_PTLOCKS */
+#endif /* USE_SPLIT_PTE_PTLOCKS */
 
 static int adjust_pte(struct vm_area_struct *vma, unsigned long address,
        unsigned long pfn)
index 78eeeca..580ef2d 100644 (file)
@@ -558,8 +558,8 @@ static void __init build_mem_type_table(void)
                mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WB;
                break;
        }
-       printk("Memory policy: ECC %sabled, Data cache %s\n",
-               ecc_mask ? "en" : "dis", cp->policy);
+       pr_info("Memory policy: %sData cache %s\n",
+               ecc_mask ? "ECC enabled, " : "", cp->policy);
 
        for (i = 0; i < ARRAY_SIZE(mem_types); i++) {
                struct mem_type *t = &mem_types[i];
index 5c668b7..55764a7 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/mach/arch.h>
 #include <asm/cputype.h>
 #include <asm/mpu.h>
+#include <asm/procinfo.h>
 
 #include "mm.h"
 
index 60920f6..bd17819 100644 (file)
@@ -92,7 +92,7 @@ ENDPROC(cpu_v7_dcache_clean_area)
 
 /* Suspend/resume support: derived from arch/arm/mach-s5pv210/sleep.S */
 .globl cpu_v7_suspend_size
-.equ   cpu_v7_suspend_size, 4 * 8
+.equ   cpu_v7_suspend_size, 4 * 9
 #ifdef CONFIG_ARM_CPU_SUSPEND
 ENTRY(cpu_v7_do_suspend)
        stmfd   sp!, {r4 - r10, lr}
@@ -101,13 +101,17 @@ ENTRY(cpu_v7_do_suspend)
        stmia   r0!, {r4 - r5}
 #ifdef CONFIG_MMU
        mrc     p15, 0, r6, c3, c0, 0   @ Domain ID
+#ifdef CONFIG_ARM_LPAE
+       mrrc    p15, 1, r5, r7, c2      @ TTB 1
+#else
        mrc     p15, 0, r7, c2, c0, 1   @ TTB 1
+#endif
        mrc     p15, 0, r11, c2, c0, 2  @ TTB control register
 #endif
        mrc     p15, 0, r8, c1, c0, 0   @ Control register
        mrc     p15, 0, r9, c1, c0, 1   @ Auxiliary control register
        mrc     p15, 0, r10, c1, c0, 2  @ Co-processor access control
-       stmia   r0, {r6 - r11}
+       stmia   r0, {r5 - r11}
        ldmfd   sp!, {r4 - r10, pc}
 ENDPROC(cpu_v7_do_suspend)
 
@@ -118,16 +122,19 @@ ENTRY(cpu_v7_do_resume)
        ldmia   r0!, {r4 - r5}
        mcr     p15, 0, r4, c13, c0, 0  @ FCSE/PID
        mcr     p15, 0, r5, c13, c0, 3  @ User r/o thread ID
-       ldmia   r0, {r6 - r11}
+       ldmia   r0, {r5 - r11}
 #ifdef CONFIG_MMU
        mcr     p15, 0, ip, c8, c7, 0   @ invalidate TLBs
        mcr     p15, 0, r6, c3, c0, 0   @ Domain ID
-#ifndef CONFIG_ARM_LPAE
+#ifdef CONFIG_ARM_LPAE
+       mcrr    p15, 0, r1, ip, c2      @ TTB 0
+       mcrr    p15, 1, r5, r7, c2      @ TTB 1
+#else
        ALT_SMP(orr     r1, r1, #TTB_FLAGS_SMP)
        ALT_UP(orr      r1, r1, #TTB_FLAGS_UP)
-#endif
        mcr     p15, 0, r1, c2, c0, 0   @ TTB 0
        mcr     p15, 0, r7, c2, c0, 1   @ TTB 1
+#endif
        mcr     p15, 0, r11, c2, c0, 2  @ TTB control register
        ldr     r4, =PRRR               @ PRRR
        ldr     r5, =NMRR               @ NMRR
index d51f956..be4ad0b 100644 (file)
@@ -70,6 +70,7 @@ static struct samsung_bl_drvdata samsung_dfl_bl_data __initdata = {
                .max_brightness = 255,
                .dft_brightness = 255,
                .pwm_period_ns  = 78770,
+               .enable_gpio    = -1,
                .init           = samsung_bl_init,
                .exit           = samsung_bl_exit,
        },
@@ -121,6 +122,10 @@ void __init samsung_bl_set(struct samsung_bl_gpio_info *gpio_info,
                samsung_bl_data->lth_brightness = bl_data->lth_brightness;
        if (bl_data->pwm_period_ns)
                samsung_bl_data->pwm_period_ns = bl_data->pwm_period_ns;
+       if (bl_data->enable_gpio >= 0)
+               samsung_bl_data->enable_gpio = bl_data->enable_gpio;
+       if (bl_data->enable_gpio_flags)
+               samsung_bl_data->enable_gpio_flags = bl_data->enable_gpio_flags;
        if (bl_data->init)
                samsung_bl_data->init = bl_data->init;
        if (bl_data->notify)
index 4384103..1296952 100644 (file)
@@ -1 +1 @@
-obj-y          := enlighten.o hypercall.o grant-table.o
+obj-y          := enlighten.o hypercall.o grant-table.o p2m.o mm.o
diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c
new file mode 100644 (file)
index 0000000..b0e77de
--- /dev/null
@@ -0,0 +1,65 @@
+#include <linux/bootmem.h>
+#include <linux/gfp.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/swiotlb.h>
+
+#include <xen/xen.h>
+#include <xen/interface/memory.h>
+#include <xen/swiotlb-xen.h>
+
+#include <asm/cacheflush.h>
+#include <asm/xen/page.h>
+#include <asm/xen/hypercall.h>
+#include <asm/xen/interface.h>
+
+int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
+                                unsigned int address_bits,
+                                dma_addr_t *dma_handle)
+{
+       if (!xen_initial_domain())
+               return -EINVAL;
+
+       /* we assume that dom0 is mapped 1:1 for now */
+       *dma_handle = pstart;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xen_create_contiguous_region);
+
+void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order)
+{
+       return;
+}
+EXPORT_SYMBOL_GPL(xen_destroy_contiguous_region);
+
+struct dma_map_ops *xen_dma_ops;
+EXPORT_SYMBOL_GPL(xen_dma_ops);
+
+static struct dma_map_ops xen_swiotlb_dma_ops = {
+       .mapping_error = xen_swiotlb_dma_mapping_error,
+       .alloc = xen_swiotlb_alloc_coherent,
+       .free = xen_swiotlb_free_coherent,
+       .sync_single_for_cpu = xen_swiotlb_sync_single_for_cpu,
+       .sync_single_for_device = xen_swiotlb_sync_single_for_device,
+       .sync_sg_for_cpu = xen_swiotlb_sync_sg_for_cpu,
+       .sync_sg_for_device = xen_swiotlb_sync_sg_for_device,
+       .map_sg = xen_swiotlb_map_sg_attrs,
+       .unmap_sg = xen_swiotlb_unmap_sg_attrs,
+       .map_page = xen_swiotlb_map_page,
+       .unmap_page = xen_swiotlb_unmap_page,
+       .dma_supported = xen_swiotlb_dma_supported,
+       .set_dma_mask = xen_swiotlb_set_dma_mask,
+};
+
+int __init xen_mm_init(void)
+{
+       if (!xen_initial_domain())
+               return 0;
+       xen_swiotlb_init(1, false);
+       xen_dma_ops = &xen_swiotlb_dma_ops;
+       return 0;
+}
+arch_initcall(xen_mm_init);
diff --git a/arch/arm/xen/p2m.c b/arch/arm/xen/p2m.c
new file mode 100644 (file)
index 0000000..23732cd
--- /dev/null
@@ -0,0 +1,208 @@
+#include <linux/bootmem.h>
+#include <linux/gfp.h>
+#include <linux/export.h>
+#include <linux/rwlock.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/swiotlb.h>
+
+#include <xen/xen.h>
+#include <xen/interface/memory.h>
+#include <xen/swiotlb-xen.h>
+
+#include <asm/cacheflush.h>
+#include <asm/xen/page.h>
+#include <asm/xen/hypercall.h>
+#include <asm/xen/interface.h>
+
+struct xen_p2m_entry {
+       unsigned long pfn;
+       unsigned long mfn;
+       unsigned long nr_pages;
+       struct rb_node rbnode_mach;
+       struct rb_node rbnode_phys;
+};
+
+rwlock_t p2m_lock;
+struct rb_root phys_to_mach = RB_ROOT;
+static struct rb_root mach_to_phys = RB_ROOT;
+
+static int xen_add_phys_to_mach_entry(struct xen_p2m_entry *new)
+{
+       struct rb_node **link = &phys_to_mach.rb_node;
+       struct rb_node *parent = NULL;
+       struct xen_p2m_entry *entry;
+       int rc = 0;
+
+       while (*link) {
+               parent = *link;
+               entry = rb_entry(parent, struct xen_p2m_entry, rbnode_phys);
+
+               if (new->mfn == entry->mfn)
+                       goto err_out;
+               if (new->pfn == entry->pfn)
+                       goto err_out;
+
+               if (new->pfn < entry->pfn)
+                       link = &(*link)->rb_left;
+               else
+                       link = &(*link)->rb_right;
+       }
+       rb_link_node(&new->rbnode_phys, parent, link);
+       rb_insert_color(&new->rbnode_phys, &phys_to_mach);
+       goto out;
+
+err_out:
+       rc = -EINVAL;
+       pr_warn("%s: cannot add pfn=%pa -> mfn=%pa: pfn=%pa -> mfn=%pa already exists\n",
+                       __func__, &new->pfn, &new->mfn, &entry->pfn, &entry->mfn);
+out:
+       return rc;
+}
+
+unsigned long __pfn_to_mfn(unsigned long pfn)
+{
+       struct rb_node *n = phys_to_mach.rb_node;
+       struct xen_p2m_entry *entry;
+       unsigned long irqflags;
+
+       read_lock_irqsave(&p2m_lock, irqflags);
+       while (n) {
+               entry = rb_entry(n, struct xen_p2m_entry, rbnode_phys);
+               if (entry->pfn <= pfn &&
+                               entry->pfn + entry->nr_pages > pfn) {
+                       read_unlock_irqrestore(&p2m_lock, irqflags);
+                       return entry->mfn + (pfn - entry->pfn);
+               }
+               if (pfn < entry->pfn)
+                       n = n->rb_left;
+               else
+                       n = n->rb_right;
+       }
+       read_unlock_irqrestore(&p2m_lock, irqflags);
+
+       return INVALID_P2M_ENTRY;
+}
+EXPORT_SYMBOL_GPL(__pfn_to_mfn);
+
+static int xen_add_mach_to_phys_entry(struct xen_p2m_entry *new)
+{
+       struct rb_node **link = &mach_to_phys.rb_node;
+       struct rb_node *parent = NULL;
+       struct xen_p2m_entry *entry;
+       int rc = 0;
+
+       while (*link) {
+               parent = *link;
+               entry = rb_entry(parent, struct xen_p2m_entry, rbnode_mach);
+
+               if (new->mfn == entry->mfn)
+                       goto err_out;
+               if (new->pfn == entry->pfn)
+                       goto err_out;
+
+               if (new->mfn < entry->mfn)
+                       link = &(*link)->rb_left;
+               else
+                       link = &(*link)->rb_right;
+       }
+       rb_link_node(&new->rbnode_mach, parent, link);
+       rb_insert_color(&new->rbnode_mach, &mach_to_phys);
+       goto out;
+
+err_out:
+       rc = -EINVAL;
+       pr_warn("%s: cannot add pfn=%pa -> mfn=%pa: pfn=%pa -> mfn=%pa already exists\n",
+                       __func__, &new->pfn, &new->mfn, &entry->pfn, &entry->mfn);
+out:
+       return rc;
+}
+
+unsigned long __mfn_to_pfn(unsigned long mfn)
+{
+       struct rb_node *n = mach_to_phys.rb_node;
+       struct xen_p2m_entry *entry;
+       unsigned long irqflags;
+
+       read_lock_irqsave(&p2m_lock, irqflags);
+       while (n) {
+               entry = rb_entry(n, struct xen_p2m_entry, rbnode_mach);
+               if (entry->mfn <= mfn &&
+                               entry->mfn + entry->nr_pages > mfn) {
+                       read_unlock_irqrestore(&p2m_lock, irqflags);
+                       return entry->pfn + (mfn - entry->mfn);
+               }
+               if (mfn < entry->mfn)
+                       n = n->rb_left;
+               else
+                       n = n->rb_right;
+       }
+       read_unlock_irqrestore(&p2m_lock, irqflags);
+
+       return INVALID_P2M_ENTRY;
+}
+EXPORT_SYMBOL_GPL(__mfn_to_pfn);
+
+bool __set_phys_to_machine_multi(unsigned long pfn,
+               unsigned long mfn, unsigned long nr_pages)
+{
+       int rc;
+       unsigned long irqflags;
+       struct xen_p2m_entry *p2m_entry;
+       struct rb_node *n = phys_to_mach.rb_node;
+
+       if (mfn == INVALID_P2M_ENTRY) {
+               write_lock_irqsave(&p2m_lock, irqflags);
+               while (n) {
+                       p2m_entry = rb_entry(n, struct xen_p2m_entry, rbnode_phys);
+                       if (p2m_entry->pfn <= pfn &&
+                                       p2m_entry->pfn + p2m_entry->nr_pages > pfn) {
+                               rb_erase(&p2m_entry->rbnode_mach, &mach_to_phys);
+                               rb_erase(&p2m_entry->rbnode_phys, &phys_to_mach);
+                               write_unlock_irqrestore(&p2m_lock, irqflags);
+                               kfree(p2m_entry);
+                               return true;
+                       }
+                       if (pfn < p2m_entry->pfn)
+                               n = n->rb_left;
+                       else
+                               n = n->rb_right;
+               }
+               write_unlock_irqrestore(&p2m_lock, irqflags);
+               return true;
+       }
+
+       p2m_entry = kzalloc(sizeof(struct xen_p2m_entry), GFP_NOWAIT);
+       if (!p2m_entry) {
+               pr_warn("cannot allocate xen_p2m_entry\n");
+               return false;
+       }
+       p2m_entry->pfn = pfn;
+       p2m_entry->nr_pages = nr_pages;
+       p2m_entry->mfn = mfn;
+
+       write_lock_irqsave(&p2m_lock, irqflags);
+       if ((rc = xen_add_phys_to_mach_entry(p2m_entry) < 0) ||
+               (rc = xen_add_mach_to_phys_entry(p2m_entry) < 0)) {
+               write_unlock_irqrestore(&p2m_lock, irqflags);
+               return false;
+       }
+       write_unlock_irqrestore(&p2m_lock, irqflags);
+       return true;
+}
+EXPORT_SYMBOL_GPL(__set_phys_to_machine_multi);
+
+bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn)
+{
+       return __set_phys_to_machine_multi(pfn, mfn, 1);
+}
+EXPORT_SYMBOL_GPL(__set_phys_to_machine);
+
+int p2m_init(void)
+{
+       rwlock_init(&p2m_lock);
+       return 0;
+}
+arch_initcall(p2m_init);
index bb0bf1b..88c8b6c 100644 (file)
@@ -143,7 +143,6 @@ config CPU_BIG_ENDIAN
 
 config SMP
        bool "Symmetric Multi-Processing"
-       select USE_GENERIC_SMP_HELPERS
        help
          This enables support for systems with more than one CPU.  If
          you say N here, the kernel will run on single and
@@ -221,6 +220,7 @@ config XEN_DOM0
 config XEN
        bool "Xen guest support on ARM64 (EXPERIMENTAL)"
        depends on ARM64 && OF
+       select SWIOTLB_XEN
        help
          Say Y if you want to run Linux in a Virtual Machine on Xen on ARM64.
 
index 8d18100..fd0c0c0 100644 (file)
 
 #include <asm-generic/dma-coherent.h>
 
+#include <xen/xen.h>
+#include <asm/xen/hypervisor.h>
+
 #define ARCH_HAS_DMA_GET_REQUIRED_MASK
 
+#define DMA_ERROR_CODE (~(dma_addr_t)0)
 extern struct dma_map_ops *dma_ops;
 
-static inline struct dma_map_ops *get_dma_ops(struct device *dev)
+static inline struct dma_map_ops *__generic_dma_ops(struct device *dev)
 {
        if (unlikely(!dev) || !dev->archdata.dma_ops)
                return dma_ops;
@@ -35,6 +39,14 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev)
                return dev->archdata.dma_ops;
 }
 
+static inline struct dma_map_ops *get_dma_ops(struct device *dev)
+{
+       if (xen_initial_domain())
+               return xen_dma_ops;
+       else
+               return __generic_dma_ops(dev);
+}
+
 #include <asm-generic/dma-mapping-common.h>
 
 static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
index b56e5b5..4cc813e 100644 (file)
 #ifdef __KERNEL__
 
 #include <linux/types.h>
+#include <linux/blk_types.h>
 
 #include <asm/byteorder.h>
 #include <asm/barrier.h>
 #include <asm/pgtable.h>
 
+#include <xen/xen.h>
+
 /*
  * Generic IO read/write.  These perform native-endian accesses.
  */
@@ -263,5 +266,12 @@ extern int devmem_is_allowed(unsigned long pfn);
  */
 #define xlate_dev_kmem_ptr(p)  p
 
+struct bio_vec;
+extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1,
+                                     const struct bio_vec *vec2);
+#define BIOVEC_PHYS_MERGEABLE(vec1, vec2)                              \
+       (__BIOVEC_PHYS_MERGEABLE(vec1, vec2) &&                         \
+        (!xen_domain() || xen_biovec_phys_mergeable(vec1, vec2)))
+
 #endif /* __KERNEL__ */
 #endif /* __ASM_IO_H */
index a5f28e2..c98ef47 100644 (file)
@@ -63,6 +63,7 @@
  * TAC:                Trap ACTLR
  * TSC:                Trap SMC
  * TSW:                Trap cache operations by set/way
+ * TWE:                Trap WFE
  * TWI:                Trap WFI
  * TIDCP:      Trap L2CTLR/L2ECTLR
  * BSU_IS:     Upgrade barriers to the inner shareable domain
@@ -72,8 +73,9 @@
  * FMO:                Override CPSR.F and enable signaling with VF
  * SWIO:       Turn set/way invalidates into set/way clean+invalidate
  */
-#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \
-                        HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \
+#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \
+                        HCR_BSU_IS | HCR_FB | HCR_TAC | \
+                        HCR_AMO | HCR_IMO | HCR_FMO | \
                         HCR_SWIO | HCR_TIDCP | HCR_RW)
 #define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
 
 
 #define ESR_EL2_EC_xABT_xFSR_EXTABT    0x10
 
+#define ESR_EL2_EC_WFI_ISS_WFE (1 << 0)
+
 #endif /* __ARM64_KVM_ARM_H__ */
index eec0738..dd8ecfc 100644 (file)
@@ -177,4 +177,65 @@ static inline u8 kvm_vcpu_trap_get_fault(const struct kvm_vcpu *vcpu)
        return kvm_vcpu_get_hsr(vcpu) & ESR_EL2_FSC_TYPE;
 }
 
+static inline unsigned long kvm_vcpu_get_mpidr(struct kvm_vcpu *vcpu)
+{
+       return vcpu_sys_reg(vcpu, MPIDR_EL1);
+}
+
+static inline void kvm_vcpu_set_be(struct kvm_vcpu *vcpu)
+{
+       if (vcpu_mode_is_32bit(vcpu))
+               *vcpu_cpsr(vcpu) |= COMPAT_PSR_E_BIT;
+       else
+               vcpu_sys_reg(vcpu, SCTLR_EL1) |= (1 << 25);
+}
+
+static inline bool kvm_vcpu_is_be(struct kvm_vcpu *vcpu)
+{
+       if (vcpu_mode_is_32bit(vcpu))
+               return !!(*vcpu_cpsr(vcpu) & COMPAT_PSR_E_BIT);
+
+       return !!(vcpu_sys_reg(vcpu, SCTLR_EL1) & (1 << 25));
+}
+
+static inline unsigned long vcpu_data_guest_to_host(struct kvm_vcpu *vcpu,
+                                                   unsigned long data,
+                                                   unsigned int len)
+{
+       if (kvm_vcpu_is_be(vcpu)) {
+               switch (len) {
+               case 1:
+                       return data & 0xff;
+               case 2:
+                       return be16_to_cpu(data & 0xffff);
+               case 4:
+                       return be32_to_cpu(data & 0xffffffff);
+               default:
+                       return be64_to_cpu(data);
+               }
+       }
+
+       return data;            /* Leave LE untouched */
+}
+
+static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
+                                                   unsigned long data,
+                                                   unsigned int len)
+{
+       if (kvm_vcpu_is_be(vcpu)) {
+               switch (len) {
+               case 1:
+                       return data & 0xff;
+               case 2:
+                       return cpu_to_be16(data & 0xffff);
+               case 4:
+                       return cpu_to_be32(data & 0xffffffff);
+               default:
+                       return cpu_to_be64(data);
+               }
+       }
+
+       return data;            /* Leave LE untouched */
+}
+
 #endif /* __ARM64_KVM_EMULATE_H__ */
index 0859a4d..5d85a02 100644 (file)
 
 #define KVM_VCPU_MAX_FEATURES 2
 
-/* We don't currently support large pages. */
-#define KVM_HPAGE_GFN_SHIFT(x) 0
-#define KVM_NR_PAGE_SIZES      1
-#define KVM_PAGES_PER_HPAGE(x) (1UL<<31)
-
 struct kvm_vcpu;
 int kvm_target_cpu(void);
 int kvm_reset_vcpu(struct kvm_vcpu *vcpu);
@@ -151,6 +146,7 @@ struct kvm_vcpu_stat {
 struct kvm_vcpu_init;
 int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
                        const struct kvm_vcpu_init *init);
+int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init);
 unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu);
 int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices);
 struct kvm_one_reg;
index efe609c..680f74e 100644 (file)
@@ -91,6 +91,7 @@ int kvm_mmu_init(void);
 void kvm_clear_hyp_idmap(void);
 
 #define        kvm_set_pte(ptep, pte)          set_pte(ptep, pte)
+#define        kvm_set_pmd(pmdp, pmd)          set_pmd(pmdp, pmd)
 
 static inline bool kvm_is_write_fault(unsigned long esr)
 {
@@ -116,13 +117,18 @@ static inline void kvm_set_s2pte_writable(pte_t *pte)
        pte_val(*pte) |= PTE_S2_RDWR;
 }
 
+static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
+{
+       pmd_val(*pmd) |= PMD_S2_RDWR;
+}
+
 struct kvm;
 
-static inline void coherent_icache_guest_page(struct kvm *kvm, gfn_t gfn)
+static inline void coherent_icache_guest_page(struct kvm *kvm, hva_t hva,
+                                             unsigned long size)
 {
        if (!icache_is_aliasing()) {            /* PIPT */
-               unsigned long hva = gfn_to_hva(kvm, gfn);
-               flush_icache_range(hva, hva + PAGE_SIZE);
+               flush_icache_range(hva, hva + size);
        } else if (!icache_is_aivivt()) {       /* non ASID-tagged VIVT */
                /* any kind of VIPT cache */
                __flush_icache_all();
index f214069..9bea6e7 100644 (file)
@@ -63,9 +63,12 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)
        struct page *pte;
 
        pte = alloc_pages(PGALLOC_GFP, 0);
-       if (pte)
-               pgtable_page_ctor(pte);
-
+       if (!pte)
+               return NULL;
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
+       }
        return pte;
 }
 
index d57e668..755f861 100644 (file)
@@ -85,6 +85,8 @@
 #define PTE_S2_RDONLY          (_AT(pteval_t, 1) << 6)   /* HAP[2:1] */
 #define PTE_S2_RDWR            (_AT(pteval_t, 3) << 6)   /* HAP[2:1] */
 
+#define PMD_S2_RDWR            (_AT(pmdval_t, 3) << 6)   /* HAP[2:1] */
+
 /*
  * Memory Attribute override for Stage-2 (MemAttr[3:0])
  */
index 23a3c47..720e70b 100644 (file)
@@ -89,12 +89,6 @@ static inline struct thread_info *current_thread_info(void)
 #endif
 
 /*
- * We use bit 30 of the preempt_count to indicate that kernel
- * preemption is occurring.  See <asm/hardirq.h>.
- */
-#define PREEMPT_ACTIVE 0x40000000
-
-/*
  * thread information flags:
  *  TIF_SYSCALL_TRACE  - syscall trace active
  *  TIF_SIGPENDING     - signal pending
diff --git a/arch/arm64/include/asm/xen/page-coherent.h b/arch/arm64/include/asm/xen/page-coherent.h
new file mode 100644 (file)
index 0000000..2820f1a
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef _ASM_ARM64_XEN_PAGE_COHERENT_H
+#define _ASM_ARM64_XEN_PAGE_COHERENT_H
+
+#include <asm/page.h>
+#include <linux/dma-attrs.h>
+#include <linux/dma-mapping.h>
+
+static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size,
+               dma_addr_t *dma_handle, gfp_t flags,
+               struct dma_attrs *attrs)
+{
+       return __generic_dma_ops(hwdev)->alloc(hwdev, size, dma_handle, flags, attrs);
+}
+
+static inline void xen_free_coherent_pages(struct device *hwdev, size_t size,
+               void *cpu_addr, dma_addr_t dma_handle,
+               struct dma_attrs *attrs)
+{
+       __generic_dma_ops(hwdev)->free(hwdev, size, cpu_addr, dma_handle, attrs);
+}
+
+static inline void xen_dma_map_page(struct device *hwdev, struct page *page,
+            unsigned long offset, size_t size, enum dma_data_direction dir,
+            struct dma_attrs *attrs)
+{
+       __generic_dma_ops(hwdev)->map_page(hwdev, page, offset, size, dir, attrs);
+}
+
+static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
+               size_t size, enum dma_data_direction dir,
+               struct dma_attrs *attrs)
+{
+       __generic_dma_ops(hwdev)->unmap_page(hwdev, handle, size, dir, attrs);
+}
+
+static inline void xen_dma_sync_single_for_cpu(struct device *hwdev,
+               dma_addr_t handle, size_t size, enum dma_data_direction dir)
+{
+       __generic_dma_ops(hwdev)->sync_single_for_cpu(hwdev, handle, size, dir);
+}
+
+static inline void xen_dma_sync_single_for_device(struct device *hwdev,
+               dma_addr_t handle, size_t size, enum dma_data_direction dir)
+{
+       __generic_dma_ops(hwdev)->sync_single_for_device(hwdev, handle, size, dir);
+}
+#endif /* _ASM_ARM64_XEN_PAGE_COHERENT_H */
index 21e9082..4480ab3 100644 (file)
@@ -21,6 +21,7 @@ config KVM
        select MMU_NOTIFIER
        select PREEMPT_NOTIFIERS
        select ANON_INODES
+       select HAVE_KVM_CPU_RELAX_INTERCEPT
        select KVM_MMIO
        select KVM_ARM_HOST
        select KVM_ARM_VGIC
index 2c3ff67..3f0731e 100644 (file)
@@ -248,6 +248,26 @@ int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
        return kvm_reset_vcpu(vcpu);
 }
 
+int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init)
+{
+       int target = kvm_target_cpu();
+
+       if (target < 0)
+               return -ENODEV;
+
+       memset(init, 0, sizeof(*init));
+
+       /*
+        * For now, we don't return any features.
+        * In future, we might use features to return target
+        * specific features available for the preferred
+        * target type.
+        */
+       init->target = (__u32)target;
+
+       return 0;
+}
+
 int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
 {
        return -EINVAL;
index 9beaca0..8da5606 100644 (file)
@@ -47,21 +47,29 @@ static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
 }
 
 /**
- * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest
+ * kvm_handle_wfx - handle a wait-for-interrupts or wait-for-event
+ *                 instruction executed by a guest
+ *
  * @vcpu:      the vcpu pointer
  *
- * Simply call kvm_vcpu_block(), which will halt execution of
+ * WFE: Yield the CPU and come back to this vcpu when the scheduler
+ * decides to.
+ * WFI: Simply call kvm_vcpu_block(), which will halt execution of
  * world-switches and schedule other host processes until there is an
  * incoming IRQ or FIQ to the VM.
  */
-static int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run)
+static int kvm_handle_wfx(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-       kvm_vcpu_block(vcpu);
+       if (kvm_vcpu_get_hsr(vcpu) & ESR_EL2_EC_WFI_ISS_WFE)
+               kvm_vcpu_on_spin(vcpu);
+       else
+               kvm_vcpu_block(vcpu);
+
        return 1;
 }
 
 static exit_handle_fn arm_exit_handlers[] = {
-       [ESR_EL2_EC_WFI]        = kvm_handle_wfi,
+       [ESR_EL2_EC_WFI]        = kvm_handle_wfx,
        [ESR_EL2_EC_CP15_32]    = kvm_handle_cp15_32,
        [ESR_EL2_EC_CP15_64]    = kvm_handle_cp15_64,
        [ESR_EL2_EC_CP14_MR]    = kvm_handle_cp14_access,
index be24040..74a8d87 100644 (file)
@@ -1,2 +1,2 @@
-xen-arm-y      += $(addprefix ../../arm/xen/, enlighten.o grant-table.o)
+xen-arm-y      += $(addprefix ../../arm/xen/, enlighten.o grant-table.o p2m.o mm.o)
 obj-y          := xen-arm.o hypercall.o
index 4488fa2..2ffc298 100644 (file)
@@ -8,6 +8,8 @@
  * published by the Free Software Foundation.
  */
 #include <asm/setup.h>
+#include <asm/thread_info.h>
+#include <asm/sysreg.h>
 
        /*
         * The kernel is loaded where we want it to be and all caches
        .section .init.text,"ax"
        .global _start
 _start:
-       /* Check if the boot loader actually provided a tag table */
-       lddpc   r0, magic_number
-       cp.w    r12, r0
-       brne    no_tag_table
-
        /* Initialize .bss */
        lddpc   r2, bss_start_addr
        lddpc   r3, end_addr
@@ -34,6 +31,25 @@ _start:
        cp      r2, r3
        brlo    1b
 
+       /* Initialize status register */
+       lddpc   r0, init_sr
+       mtsr    SYSREG_SR, r0
+
+       /* Set initial stack pointer */
+       lddpc   sp, stack_addr
+       sub     sp, -THREAD_SIZE
+
+#ifdef CONFIG_FRAME_POINTER
+       /* Mark last stack frame */
+       mov     lr, 0
+       mov     r7, 0
+#endif
+
+       /* Check if the boot loader actually provided a tag table */
+       lddpc   r0, magic_number
+       cp.w    r12, r0
+       brne    no_tag_table
+
        /*
         * Save the tag table address for later use. This must be done
         * _after_ .bss has been initialized...
@@ -53,8 +69,15 @@ bss_start_addr:
        .long   __bss_start
 end_addr:
        .long   _end
+init_sr:
+       .long   0x007f0000      /* Supervisor mode, everything masked */
+stack_addr:
+       .long   init_thread_union
+panic_addr:
+       .long   panic
 
 no_tag_table:
        sub     r12, pc, (. - 2f)
-       bral    panic
+       /* branch to panic() which can be far away with that construct */
+       lddpc   pc, panic_addr
 2:     .asciz  "Boot loader didn't provide correct magic number\n"
index 996cb65..45f563e 100644 (file)
@@ -16,6 +16,7 @@
 typedef u16    kprobe_opcode_t;
 #define BREAKPOINT_INSTRUCTION 0xd673  /* breakpoint */
 #define MAX_INSN_SIZE          2
+#define MAX_STACK_SIZE         64      /* 32 would probably be OK */
 
 #define kretprobe_blacklist_size 0
 
@@ -26,6 +27,19 @@ struct arch_specific_insn {
        kprobe_opcode_t insn[MAX_INSN_SIZE];
 };
 
+struct prev_kprobe {
+       struct kprobe *kp;
+       unsigned int status;
+};
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+       unsigned int kprobe_status;
+       struct prev_kprobe prev_kprobe;
+       struct pt_regs jprobe_saved_regs;
+       char jprobes_stack[MAX_STACK_SIZE];
+};
+
 extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
 extern int kprobe_exceptions_notify(struct notifier_block *self,
                                    unsigned long val, void *data);
index bc7e8ae..1aba19d 100644 (file)
@@ -68,7 +68,10 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
                return NULL;
 
        page = virt_to_page(pg);
-       pgtable_page_ctor(page);
+       if (!pgtable_page_ctor(page)) {
+               quicklist_free(QUICK_PT, NULL, pg);
+               return NULL;
+       }
 
        return page;
 }
index 6dc62e1..a978f3f 100644 (file)
@@ -66,8 +66,6 @@ static inline struct thread_info *current_thread_info(void)
 
 #endif /* !__ASSEMBLY__ */
 
-#define PREEMPT_ACTIVE         0x40000000
-
 /*
  * Thread information flags
  * - these are process state flags that various assembly files may need to access
index 3b85ead..08d8a3d 100644 (file)
@@ -2,35 +2,35 @@
 include include/uapi/asm-generic/Kbuild.asm
 
 header-y += auxvec.h
-header-y += bitsperlong.h
 header-y += byteorder.h
 header-y += cachectl.h
-header-y += errno.h
-header-y += fcntl.h
-header-y += ioctl.h
-header-y += ioctls.h
-header-y += ipcbuf.h
-header-y += kvm_para.h
-header-y += mman.h
 header-y += msgbuf.h
 header-y += param.h
-header-y += poll.h
 header-y += posix_types.h
 header-y += ptrace.h
-header-y += resource.h
 header-y += sembuf.h
 header-y += setup.h
 header-y += shmbuf.h
 header-y += sigcontext.h
-header-y += siginfo.h
 header-y += signal.h
 header-y += socket.h
 header-y += sockios.h
 header-y += stat.h
-header-y += statfs.h
 header-y += swab.h
 header-y += termbits.h
 header-y += termios.h
 header-y += types.h
 header-y += unistd.h
+generic-y += bitsperlong.h
+generic-y += errno.h
+generic-y += fcntl.h
+generic-y += ioctl.h
+generic-y += ioctls.h
+generic-y += ipcbuf.h
+generic-y += kvm_para.h
+generic-y += mman.h
 generic-y += param.h
+generic-y += poll.h
+generic-y += resource.h
+generic-y += siginfo.h
+generic-y += statfs.h
index d5dd435..4f02da3 100644 (file)
@@ -1,4 +1,4 @@
-#ifndef __ASM_AVR32_AUXVEC_H
-#define __ASM_AVR32_AUXVEC_H
+#ifndef _UAPI__ASM_AVR32_AUXVEC_H
+#define _UAPI__ASM_AVR32_AUXVEC_H
 
-#endif /* __ASM_AVR32_AUXVEC_H */
+#endif /* _UAPI__ASM_AVR32_AUXVEC_H */
diff --git a/arch/avr32/include/uapi/asm/bitsperlong.h b/arch/avr32/include/uapi/asm/bitsperlong.h
deleted file mode 100644 (file)
index 6dc0bb0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/bitsperlong.h>
index 50abc21..71242f0 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * AVR32 endian-conversion functions.
  */
-#ifndef __ASM_AVR32_BYTEORDER_H
-#define __ASM_AVR32_BYTEORDER_H
+#ifndef _UAPI__ASM_AVR32_BYTEORDER_H
+#define _UAPI__ASM_AVR32_BYTEORDER_H
 
 #include <linux/byteorder/big_endian.h>
 
-#endif /* __ASM_AVR32_BYTEORDER_H */
+#endif /* _UAPI__ASM_AVR32_BYTEORDER_H */
index 4faf1ce..573a958 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __ASM_AVR32_CACHECTL_H
-#define __ASM_AVR32_CACHECTL_H
+#ifndef _UAPI__ASM_AVR32_CACHECTL_H
+#define _UAPI__ASM_AVR32_CACHECTL_H
 
 /*
  * Operations that can be performed through the cacheflush system call
@@ -8,4 +8,4 @@
 /* Clean the data cache, then invalidate the icache */
 #define CACHE_IFLUSH   0
 
-#endif /* __ASM_AVR32_CACHECTL_H */
+#endif /* _UAPI__ASM_AVR32_CACHECTL_H */
diff --git a/arch/avr32/include/uapi/asm/errno.h b/arch/avr32/include/uapi/asm/errno.h
deleted file mode 100644 (file)
index 558a724..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_ERRNO_H
-#define __ASM_AVR32_ERRNO_H
-
-#include <asm-generic/errno.h>
-
-#endif /* __ASM_AVR32_ERRNO_H */
diff --git a/arch/avr32/include/uapi/asm/fcntl.h b/arch/avr32/include/uapi/asm/fcntl.h
deleted file mode 100644 (file)
index 14c0c44..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_FCNTL_H
-#define __ASM_AVR32_FCNTL_H
-
-#include <asm-generic/fcntl.h>
-
-#endif /* __ASM_AVR32_FCNTL_H */
diff --git a/arch/avr32/include/uapi/asm/ioctl.h b/arch/avr32/include/uapi/asm/ioctl.h
deleted file mode 100644 (file)
index c8472c1..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_IOCTL_H
-#define __ASM_AVR32_IOCTL_H
-
-#include <asm-generic/ioctl.h>
-
-#endif /* __ASM_AVR32_IOCTL_H */
diff --git a/arch/avr32/include/uapi/asm/ioctls.h b/arch/avr32/include/uapi/asm/ioctls.h
deleted file mode 100644 (file)
index 909cf66..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_IOCTLS_H
-#define __ASM_AVR32_IOCTLS_H
-
-#include <asm-generic/ioctls.h>
-
-#endif /* __ASM_AVR32_IOCTLS_H */
diff --git a/arch/avr32/include/uapi/asm/ipcbuf.h b/arch/avr32/include/uapi/asm/ipcbuf.h
deleted file mode 100644 (file)
index 84c7e51..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ipcbuf.h>
diff --git a/arch/avr32/include/uapi/asm/kvm_para.h b/arch/avr32/include/uapi/asm/kvm_para.h
deleted file mode 100644 (file)
index 14fab8f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/kvm_para.h>
diff --git a/arch/avr32/include/uapi/asm/mman.h b/arch/avr32/include/uapi/asm/mman.h
deleted file mode 100644 (file)
index 8eebf89..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/mman.h>
index ac18bc4..9eae6ef 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __ASM_AVR32_MSGBUF_H
-#define __ASM_AVR32_MSGBUF_H
+#ifndef _UAPI__ASM_AVR32_MSGBUF_H
+#define _UAPI__ASM_AVR32_MSGBUF_H
 
 /*
  * The msqid64_ds structure for i386 architecture.
@@ -28,4 +28,4 @@ struct msqid64_ds {
        unsigned long  __unused5;
 };
 
-#endif /* __ASM_AVR32_MSGBUF_H */
+#endif /* _UAPI__ASM_AVR32_MSGBUF_H */
diff --git a/arch/avr32/include/uapi/asm/poll.h b/arch/avr32/include/uapi/asm/poll.h
deleted file mode 100644 (file)
index c98509d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/poll.h>
index 9ba9e74..5b813a8 100644 (file)
@@ -5,8 +5,8 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#ifndef __ASM_AVR32_POSIX_TYPES_H
-#define __ASM_AVR32_POSIX_TYPES_H
+#ifndef _UAPI__ASM_AVR32_POSIX_TYPES_H
+#define _UAPI__ASM_AVR32_POSIX_TYPES_H
 
 /*
  * This file is generally used by user-level software, so you need to
@@ -34,4 +34,4 @@ typedef unsigned short  __kernel_old_dev_t;
 
 #include <asm-generic/posix_types.h>
 
-#endif /* __ASM_AVR32_POSIX_TYPES_H */
+#endif /* _UAPI__ASM_AVR32_POSIX_TYPES_H */
diff --git a/arch/avr32/include/uapi/asm/resource.h b/arch/avr32/include/uapi/asm/resource.h
deleted file mode 100644 (file)
index c6dd101..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_RESOURCE_H
-#define __ASM_AVR32_RESOURCE_H
-
-#include <asm-generic/resource.h>
-
-#endif /* __ASM_AVR32_RESOURCE_H */
index e472216..6c6f7cf 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __ASM_AVR32_SEMBUF_H
-#define __ASM_AVR32_SEMBUF_H
+#ifndef _UAPI__ASM_AVR32_SEMBUF_H
+#define _UAPI__ASM_AVR32_SEMBUF_H
 
 /*
 * The semid64_ds structure for AVR32 architecture.
@@ -22,4 +22,4 @@ struct semid64_ds {
         unsigned long   __unused4;
 };
 
-#endif /* __ASM_AVR32_SEMBUF_H */
+#endif /* _UAPI__ASM_AVR32_SEMBUF_H */
index e58aa93..a654df7 100644 (file)
@@ -13,5 +13,4 @@
 
 #define COMMAND_LINE_SIZE 256
 
-
 #endif /* _UAPI__ASM_AVR32_SETUP_H__ */
index c62fba4..b94cf8b 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __ASM_AVR32_SHMBUF_H
-#define __ASM_AVR32_SHMBUF_H
+#ifndef _UAPI__ASM_AVR32_SHMBUF_H
+#define _UAPI__ASM_AVR32_SHMBUF_H
 
 /*
  * The shmid64_ds structure for i386 architecture.
@@ -39,4 +39,4 @@ struct shminfo64 {
        unsigned long   __unused4;
 };
 
-#endif /* __ASM_AVR32_SHMBUF_H */
+#endif /* _UAPI__ASM_AVR32_SHMBUF_H */
index e04062b..27e56bf 100644 (file)
@@ -5,8 +5,8 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#ifndef __ASM_AVR32_SIGCONTEXT_H
-#define __ASM_AVR32_SIGCONTEXT_H
+#ifndef _UAPI__ASM_AVR32_SIGCONTEXT_H
+#define _UAPI__ASM_AVR32_SIGCONTEXT_H
 
 struct sigcontext {
        unsigned long   oldmask;
@@ -31,4 +31,4 @@ struct sigcontext {
        unsigned long   r0;
 };
 
-#endif /* __ASM_AVR32_SIGCONTEXT_H */
+#endif /* _UAPI__ASM_AVR32_SIGCONTEXT_H */
diff --git a/arch/avr32/include/uapi/asm/siginfo.h b/arch/avr32/include/uapi/asm/siginfo.h
deleted file mode 100644 (file)
index 5ee93f4..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _AVR32_SIGINFO_H
-#define _AVR32_SIGINFO_H
-
-#include <asm-generic/siginfo.h>
-
-#endif
index 1b77a93..ffe8c77 100644 (file)
@@ -118,5 +118,4 @@ typedef struct sigaltstack {
        size_t ss_size;
 } stack_t;
 
-
 #endif /* _UAPI__ASM_AVR32_SIGNAL_H */
index 4399364..cbf902e 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __ASM_AVR32_SOCKET_H
-#define __ASM_AVR32_SOCKET_H
+#ifndef _UAPI__ASM_AVR32_SOCKET_H
+#define _UAPI__ASM_AVR32_SOCKET_H
 
 #include <asm/sockios.h>
 
@@ -78,4 +78,4 @@
 
 #define SO_MAX_PACING_RATE     47
 
-#endif /* __ASM_AVR32_SOCKET_H */
+#endif /* _UAPI__ASM_AVR32_SOCKET_H */
index 0802d74..d047854 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __ASM_AVR32_SOCKIOS_H
-#define __ASM_AVR32_SOCKIOS_H
+#ifndef _UAPI__ASM_AVR32_SOCKIOS_H
+#define _UAPI__ASM_AVR32_SOCKIOS_H
 
 /* Socket-level I/O control calls. */
 #define FIOSETOWN      0x8901
@@ -10,4 +10,4 @@
 #define SIOCGSTAMP     0x8906          /* Get stamp (timeval) */
 #define SIOCGSTAMPNS   0x8907          /* Get stamp (timespec) */
 
-#endif /* __ASM_AVR32_SOCKIOS_H */
+#endif /* _UAPI__ASM_AVR32_SOCKIOS_H */
index e72881e..c06acef 100644 (file)
@@ -5,8 +5,8 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#ifndef __ASM_AVR32_STAT_H
-#define __ASM_AVR32_STAT_H
+#ifndef _UAPI__ASM_AVR32_STAT_H
+#define _UAPI__ASM_AVR32_STAT_H
 
 struct __old_kernel_stat {
         unsigned short st_dev;
@@ -76,4 +76,4 @@ struct stat64 {
        unsigned long   __unused2;
 };
 
-#endif /* __ASM_AVR32_STAT_H */
+#endif /* _UAPI__ASM_AVR32_STAT_H */
diff --git a/arch/avr32/include/uapi/asm/statfs.h b/arch/avr32/include/uapi/asm/statfs.h
deleted file mode 100644 (file)
index 2961bd1..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_STATFS_H
-#define __ASM_AVR32_STATFS_H
-
-#include <asm-generic/statfs.h>
-
-#endif /* __ASM_AVR32_STATFS_H */
index 14cc737..1a03549 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * AVR32 byteswapping functions.
  */
-#ifndef __ASM_AVR32_SWAB_H
-#define __ASM_AVR32_SWAB_H
+#ifndef _UAPI__ASM_AVR32_SWAB_H
+#define _UAPI__ASM_AVR32_SWAB_H
 
 #include <linux/types.h>
 #include <linux/compiler.h>
@@ -32,4 +32,4 @@ static inline __attribute_const__ __u32 __arch_swab32(__u32 val)
 #define __arch_swab32 __arch_swab32
 #endif
 
-#endif /* __ASM_AVR32_SWAB_H */
+#endif /* _UAPI__ASM_AVR32_SWAB_H */
index 366adc5..32789cc 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __ASM_AVR32_TERMBITS_H
-#define __ASM_AVR32_TERMBITS_H
+#ifndef _UAPI__ASM_AVR32_TERMBITS_H
+#define _UAPI__ASM_AVR32_TERMBITS_H
 
 #include <linux/posix_types.h>
 
@@ -193,4 +193,4 @@ struct ktermios {
 #define        TCSADRAIN       1
 #define        TCSAFLUSH       2
 
-#endif /* __ASM_AVR32_TERMBITS_H */
+#endif /* _UAPI__ASM_AVR32_TERMBITS_H */
index b8ef8ea..c8a0081 100644 (file)
@@ -46,5 +46,4 @@ struct termio {
 
 /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
 
-
 #endif /* _UAPI__ASM_AVR32_TERMIOS_H */
index bb34ad3..7c986c4 100644 (file)
@@ -5,4 +5,9 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#ifndef _UAPI__ASM_AVR32_TYPES_H
+#define _UAPI__ASM_AVR32_TYPES_H
+
 #include <asm-generic/int-ll64.h>
+
+#endif /* _UAPI__ASM_AVR32_TYPES_H */
index 3eaa687..8822bf4 100644 (file)
 #define __NR_eventfd           281
 #define __NR_setns             283
 
-
 #endif /* _UAPI__ASM_AVR32_UNISTD_H */
index 9899d3c..7301f48 100644 (file)
@@ -401,9 +401,10 @@ handle_critical:
        /* We should never get here... */
 bad_return:
        sub     r12, pc, (. - 1f)
-       bral    panic
+       lddpc   pc, 2f
        .align  2
 1:     .asciz  "Return from critical exception!"
+2:     .long   panic
 
        .align  1
 do_bus_error_write:
index 6163bd0..59eae6d 100644 (file)
 #include <linux/linkage.h>
 
 #include <asm/page.h>
-#include <asm/thread_info.h>
-#include <asm/sysreg.h>
 
        .section .init.text,"ax"
        .global kernel_entry
 kernel_entry:
-       /* Initialize status register */
-       lddpc   r0, init_sr
-       mtsr    SYSREG_SR, r0
-
-       /* Set initial stack pointer */
-       lddpc   sp, stack_addr
-       sub     sp, -THREAD_SIZE
-
-#ifdef CONFIG_FRAME_POINTER
-       /* Mark last stack frame */
-       mov     lr, 0
-       mov     r7, 0
-#endif
-
        /* Start the show */
        lddpc   pc, kernel_start_addr
 
        .align  2
-init_sr:
-       .long   0x007f0000      /* Supervisor mode, everything masked */
-stack_addr:
-       .long   init_thread_union
 kernel_start_addr:
        .long   start_kernel
index e887b57..9ceccef 100644 (file)
@@ -34,7 +34,6 @@ config BLACKFIN
        select ARCH_WANT_IPC_PARSE_VERSION
        select GENERIC_ATOMIC64
        select GENERIC_IRQ_PROBE
-       select USE_GENERIC_SMP_HELPERS if SMP
        select HAVE_NMI_WATCHDOG if NMI_WATCHDOG
        select GENERIC_SMP_IDLE_THREAD
        select ARCH_USES_GETTIMEOFFSET if !GENERIC_CLOCKEVENTS
index 13eb732..4ca39ab 100644 (file)
@@ -102,7 +102,7 @@ CONFIG_I2C_CHARDEV=y
 CONFIG_I2C_BLACKFIN_TWI=y
 CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ=100
 CONFIG_SPI=y
-CONFIG_SPI_BFIN6XX=y
+CONFIG_SPI_BFIN_V3=y
 CONFIG_GPIOLIB=y
 CONFIG_GPIO_SYSFS=y
 # CONFIG_HWMON is not set
index c078dd7..58b54a6 100644 (file)
@@ -12,9 +12,6 @@
 extern void ack_bad_irq(unsigned int irq);
 #define ack_bad_irq ack_bad_irq
 
-/* Define until common code gets sane defaults */
-#define HARDIRQ_BITS 9
-
 #include <asm-generic/hardirq.h>
 
 #endif
index 4ae1144..2fd04f1 100644 (file)
@@ -23,8 +23,7 @@
 /*
  * pm save bfin pint registers
  */
-struct bfin_pm_pint_save {
-       u32 mask_set;
+struct adi_pm_pint_save {
        u32 assign;
        u32 edge_set;
        u32 invert_set;
index 4fbf835..4b2a992 100644 (file)
 #include <mach/irq.h>
 
 /* init functions only */
-extern int __init init_arch_irq(void);
+extern int init_arch_irq(void);
 extern void init_exception_vectors(void);
-extern void __init program_IAR(void);
+extern void program_IAR(void);
 #ifdef init_mach_irq
-extern void __init init_mach_irq(void);
+extern void init_mach_irq(void);
 #else
 # define init_mach_irq()
 #endif
index 3894005..55f473b 100644 (file)
@@ -88,8 +88,6 @@ static inline struct thread_info *current_thread_info(void)
 #define TI_CPU         12
 #define TI_PREEMPT     16
 
-#define        PREEMPT_ACTIVE  0x4000000
-
 /*
  * thread information flag bit numbers
  */
index ed978f1..a017359 100644 (file)
 #include <linux/err.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-#include <asm/blackfin.h>
-#include <asm/gpio.h>
-#include <asm/portmux.h>
+#include <linux/gpio.h>
 #include <linux/irq.h>
-#include <asm/irq_handler.h>
 
 #if ANOMALY_05000311 || ANOMALY_05000323
 enum {
@@ -58,19 +55,6 @@ static struct gpio_port_t * const gpio_array[] = {
        (struct gpio_port_t *) FIO0_FLAG_D,
        (struct gpio_port_t *) FIO1_FLAG_D,
        (struct gpio_port_t *) FIO2_FLAG_D,
-#elif defined(CONFIG_BF54x) || defined(CONFIG_BF60x) 
-       (struct gpio_port_t *)PORTA_FER,
-       (struct gpio_port_t *)PORTB_FER,
-       (struct gpio_port_t *)PORTC_FER,
-       (struct gpio_port_t *)PORTD_FER,
-       (struct gpio_port_t *)PORTE_FER,
-       (struct gpio_port_t *)PORTF_FER,
-       (struct gpio_port_t *)PORTG_FER,
-# if defined(CONFIG_BF54x)
-       (struct gpio_port_t *)PORTH_FER,
-       (struct gpio_port_t *)PORTI_FER,
-       (struct gpio_port_t *)PORTJ_FER,
-# endif
 #else
 # error no gpio arrays defined
 #endif
@@ -169,12 +153,6 @@ DECLARE_RESERVED_MAP(gpio_irq, GPIO_BANK_NUM);
 
 inline int check_gpio(unsigned gpio)
 {
-#if defined(CONFIG_BF54x)
-       if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15
-           || gpio == GPIO_PH14 || gpio == GPIO_PH15
-           || gpio == GPIO_PJ14 || gpio == GPIO_PJ15)
-               return -EINVAL;
-#endif
        if (gpio >= MAX_BLACKFIN_GPIOS)
                return -EINVAL;
        return 0;
@@ -212,12 +190,6 @@ static void port_setup(unsigned gpio, unsigned short usage)
        else
                *port_fer[gpio_bank(gpio)] |= gpio_bit(gpio);
        SSYNC();
-#elif defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
-       if (usage == GPIO_USAGE)
-               gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio);
-       else
-               gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio);
-       SSYNC();
 #endif
 }
 
@@ -255,7 +227,7 @@ static int portmux_group_check(unsigned short per)
        u16 ident = P_IDENT(per);
        u16 function = P_FUNCT2MUX(per);
        s8 offset = port_mux[ident];
-       u16 m, pmux, pfunc;
+       u16 m, pmux, pfunc, mask;
 
        if (offset < 0)
                return 0;
@@ -270,10 +242,12 @@ static int portmux_group_check(unsigned short per)
                        continue;
 
                if (offset == 1)
-                       pfunc = (pmux >> offset) & 3;
+                       mask = 3;
                else
-                       pfunc = (pmux >> offset) & 1;
-               if (pfunc != function) {
+                       mask = 1;
+
+               pfunc = (pmux >> offset) & mask;
+               if (pfunc != (function & mask)) {
                        pr_err("pin group conflict! request pin %d func %d conflict with pin %d func %d\n",
                                ident, function, m, pfunc);
                        return -EINVAL;
@@ -288,43 +262,21 @@ static void portmux_setup(unsigned short per)
        u16 ident = P_IDENT(per);
        u16 function = P_FUNCT2MUX(per);
        s8 offset = port_mux[ident];
-       u16 pmux;
+       u16 pmux, mask;
 
        if (offset == -1)
                return;
 
        pmux = bfin_read_PORT_MUX();
-       if (offset != 1)
-               pmux &= ~(1 << offset);
+       if (offset == 1)
+               mask = 3;
        else
-               pmux &= ~(3 << 1);
-       pmux |= (function << offset);
-       bfin_write_PORT_MUX(pmux);
-}
-#elif defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
-inline void portmux_setup(unsigned short per)
-{
-       u16 ident = P_IDENT(per);
-       u16 function = P_FUNCT2MUX(per);
-       u32 pmux;
+               mask = 1;
 
-       pmux = gpio_array[gpio_bank(ident)]->port_mux;
+       pmux &= ~(mask << offset);
+       pmux |= ((function & mask) << offset);
 
-       pmux &= ~(0x3 << (2 * gpio_sub_n(ident)));
-       pmux |= (function & 0x3) << (2 * gpio_sub_n(ident));
-
-       gpio_array[gpio_bank(ident)]->port_mux = pmux;
-}
-
-inline u16 get_portmux(unsigned short per)
-{
-       u16 ident = P_IDENT(per);
-       u32 pmux = gpio_array[gpio_bank(ident)]->port_mux;
-       return (pmux >> (2 * gpio_sub_n(ident)) & 0x3);
-}
-static int portmux_group_check(unsigned short per)
-{
-       return 0;
+       bfin_write_PORT_MUX(pmux);
 }
 #elif defined(CONFIG_BF52x) || defined(CONFIG_BF51x)
 static int portmux_group_check(unsigned short per)
@@ -379,7 +331,6 @@ static int portmux_group_check(unsigned short per)
 }
 #endif
 
-#if !(defined(CONFIG_BF54x) || defined(CONFIG_BF60x))
 /***********************************************************
 *
 * FUNCTIONS: Blackfin General Purpose Ports Access Functions
@@ -572,7 +523,7 @@ static const unsigned int sic_iwr_irqs[] = {
 *************************************************************
 * MODIFICATION HISTORY :
 **************************************************************/
-int gpio_pm_wakeup_ctrl(unsigned gpio, unsigned ctrl)
+int bfin_gpio_pm_wakeup_ctrl(unsigned gpio, unsigned ctrl)
 {
        unsigned long flags;
 
@@ -591,7 +542,7 @@ int gpio_pm_wakeup_ctrl(unsigned gpio, unsigned ctrl)
        return 0;
 }
 
-int bfin_pm_standby_ctrl(unsigned ctrl)
+int bfin_gpio_pm_standby_ctrl(unsigned ctrl)
 {
        u16 bank, mask, i;
 
@@ -682,53 +633,6 @@ void bfin_gpio_pm_hibernate_restore(void)
 
 
 #endif
-#else /* CONFIG_BF54x || CONFIG_BF60x */
-#ifdef CONFIG_PM
-
-int bfin_pm_standby_ctrl(unsigned ctrl)
-{
-       return 0;
-}
-
-void bfin_gpio_pm_hibernate_suspend(void)
-{
-       int i, bank;
-
-       for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) {
-               bank = gpio_bank(i);
-
-               gpio_bank_saved[bank].fer = gpio_array[bank]->port_fer;
-               gpio_bank_saved[bank].mux = gpio_array[bank]->port_mux;
-               gpio_bank_saved[bank].data = gpio_array[bank]->data;
-               gpio_bank_saved[bank].inen = gpio_array[bank]->inen;
-               gpio_bank_saved[bank].dir = gpio_array[bank]->dir_set;
-       }
-}
-
-void bfin_gpio_pm_hibernate_restore(void)
-{
-       int i, bank;
-
-       for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) {
-               bank = gpio_bank(i);
-
-               gpio_array[bank]->port_mux = gpio_bank_saved[bank].mux;
-               gpio_array[bank]->port_fer = gpio_bank_saved[bank].fer;
-               gpio_array[bank]->inen = gpio_bank_saved[bank].inen;
-               gpio_array[bank]->data_set = gpio_bank_saved[bank].data
-                                               & gpio_bank_saved[bank].dir;
-               gpio_array[bank]->dir_set = gpio_bank_saved[bank].dir;
-       }
-}
-#endif
-
-unsigned short get_gpio_dir(unsigned gpio)
-{
-       return (0x01 & (gpio_array[gpio_bank(gpio)]->dir_clear >> gpio_sub_n(gpio)));
-}
-EXPORT_SYMBOL(get_gpio_dir);
-
-#endif /* CONFIG_BF54x || CONFIG_BF60x */
 
 /***********************************************************
 *
@@ -785,11 +689,7 @@ int peripheral_request(unsigned short per, const char *label)
                 * be requested and used by several drivers
                 */
 
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
-               if (!((per & P_MAYSHARE) && get_portmux(per) == P_FUNCT2MUX(per))) {
-#else
                if (!(per & P_MAYSHARE)) {
-#endif
                        /*
                         * Allow that the identical pin function can
                         * be requested from the same driver twice
@@ -938,12 +838,9 @@ int bfin_gpio_request(unsigned gpio, const char *label)
        if (unlikely(is_reserved(gpio_irq, gpio, 1))) {
                printk(KERN_NOTICE "bfin-gpio: GPIO %d is already reserved as gpio-irq!"
                       " (Documentation/blackfin/bfin-gpio-notes.txt)\n", gpio);
-       }
-#if !(defined(CONFIG_BF54x) || defined(CONFIG_BF60x))
-       else {  /* Reset POLAR setting when acquiring a gpio for the first time */
+       } else {        /* Reset POLAR setting when acquiring a gpio for the first time */
                set_gpio_polar(gpio, 0);
        }
-#endif
 
        reserve(gpio, gpio);
        set_label(gpio, label);
@@ -1112,11 +1009,7 @@ void bfin_gpio_irq_free(unsigned gpio)
 
 static inline void __bfin_gpio_direction_input(unsigned gpio)
 {
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
-       gpio_array[gpio_bank(gpio)]->dir_clear = gpio_bit(gpio);
-#else
        gpio_array[gpio_bank(gpio)]->dir &= ~gpio_bit(gpio);
-#endif
        gpio_array[gpio_bank(gpio)]->inen |= gpio_bit(gpio);
 }
 
@@ -1140,17 +1033,7 @@ EXPORT_SYMBOL(bfin_gpio_direction_input);
 
 void bfin_gpio_irq_prepare(unsigned gpio)
 {
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
-       unsigned long flags;
-#endif
-
        port_setup(gpio, GPIO_USAGE);
-
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
-       flags = hard_local_irq_save();
-       __bfin_gpio_direction_input(gpio);
-       hard_local_irq_restore(flags);
-#endif
 }
 
 void bfin_gpio_set_value(unsigned gpio, int arg)
@@ -1175,11 +1058,7 @@ int bfin_gpio_direction_output(unsigned gpio, int value)
 
        gpio_array[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio);
        gpio_set_value(gpio, value);
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
-       gpio_array[gpio_bank(gpio)]->dir_set = gpio_bit(gpio);
-#else
        gpio_array[gpio_bank(gpio)]->dir |= gpio_bit(gpio);
-#endif
 
        AWA_DUMMY_READ(dir);
        hard_local_irq_restore(flags);
@@ -1190,9 +1069,6 @@ EXPORT_SYMBOL(bfin_gpio_direction_output);
 
 int bfin_gpio_get_value(unsigned gpio)
 {
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
-       return (1 & (gpio_array[gpio_bank(gpio)]->data >> gpio_sub_n(gpio)));
-#else
        unsigned long flags;
 
        if (unlikely(get_gpio_edge(gpio))) {
@@ -1205,7 +1081,6 @@ int bfin_gpio_get_value(unsigned gpio)
                return ret;
        } else
                return get_gpio_data(gpio);
-#endif
 }
 EXPORT_SYMBOL(bfin_gpio_get_value);
 
index 94acb58..334ec7b 100644 (file)
@@ -377,40 +377,6 @@ config IRQ_PINT3
 
 endmenu
 
-comment "Pin Interrupt to Port Assignment"
-menu "Assignment"
-
-config PINTx_REASSIGN
-       bool "Reprogram PINT Assignment"
-       default y
-       help
-         The interrupt assignment registers controls the pin-to-interrupt
-         assignment in a byte-wide manner. Each option allows you to select
-         a set of pins (High/Low Byte) of an specific Port being mapped
-         to one of the four PIN Interrupts IRQ_PINTx.
-
-         You shouldn't change any of these unless you know exactly what you're doing.
-         Please consult the Blackfin BF54x Processor Hardware Reference Manual.
-
-config PINT0_ASSIGN
-       hex "PINT0_ASSIGN"
-       depends on PINTx_REASSIGN
-       default 0x00000101
-config PINT1_ASSIGN
-       hex "PINT1_ASSIGN"
-       depends on PINTx_REASSIGN
-       default 0x01010000
-config PINT2_ASSIGN
-       hex "PINT2_ASSIGN"
-       depends on PINTx_REASSIGN
-       default 0x07000101
-config PINT3_ASSIGN
-       hex "PINT3_ASSIGN"
-       depends on PINTx_REASSIGN
-       default 0x02020303
-
-endmenu
-
 endmenu
 
 endif
index 372eb54..d495000 100644 (file)
@@ -17,6 +17,9 @@
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/usb/musb.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/platform_data/pinctrl-adi2.h>
 #include <asm/bfin5xx_spi.h>
 #include <asm/dma.h>
 #include <asm/gpio.h>
@@ -241,6 +244,13 @@ static struct resource bfin_uart0_resources[] = {
                .end = UART0_RBR+2,
                .flags = IORESOURCE_MEM,
        },
+#ifdef CONFIG_EARLY_PRINTK
+       {
+               .start = PORTE_FER,
+               .end = PORTE_FER+2,
+               .flags = IORESOURCE_REG,
+       },
+#endif
        {
                .start = IRQ_UART0_TX,
                .end = IRQ_UART0_TX,
@@ -289,6 +299,13 @@ static struct resource bfin_uart1_resources[] = {
                .end = UART1_RBR+2,
                .flags = IORESOURCE_MEM,
        },
+#ifdef CONFIG_EARLY_PRINTK
+       {
+               .start = PORTH_FER,
+               .end = PORTH_FER+2,
+               .flags = IORESOURCE_REG,
+       },
+#endif
        {
                .start = IRQ_UART1_TX,
                .end = IRQ_UART1_TX,
@@ -353,6 +370,13 @@ static struct resource bfin_uart2_resources[] = {
                .end = UART2_RBR+2,
                .flags = IORESOURCE_MEM,
        },
+#ifdef CONFIG_EARLY_PRINTK
+       {
+               .start = PORTB_FER,
+               .end = PORTB_FER+2,
+               .flags = IORESOURCE_REG,
+       },
+#endif
        {
                .start = IRQ_UART2_TX,
                .end = IRQ_UART2_TX,
@@ -401,6 +425,13 @@ static struct resource bfin_uart3_resources[] = {
                .end = UART3_RBR+2,
                .flags = IORESOURCE_MEM,
        },
+#ifdef CONFIG_EARLY_PRINTK
+       {
+               .start = PORTB_FER,
+               .end = PORTB_FER+2,
+               .flags = IORESOURCE_REG,
+       },
+#endif
        {
                .start = IRQ_UART3_TX,
                .end = IRQ_UART3_TX,
@@ -1058,6 +1089,411 @@ static const struct ad7877_platform_data bfin_ad7877_ts_info = {
 };
 #endif
 
+#ifdef CONFIG_PINCTRL_ADI2
+
+# define ADI_PINT_DEVNAME "adi-gpio-pint"
+# define ADI_GPIO_DEVNAME "adi-gpio"
+# define ADI_PINCTRL_DEVNAME "pinctrl-adi2"
+
+static struct platform_device bfin_pinctrl_device = {
+       .name = ADI_PINCTRL_DEVNAME,
+       .id = 0,
+};
+
+static struct resource bfin_pint0_resources[] = {
+       {
+               .start = PINT0_MASK_SET,
+               .end = PINT0_LATCH + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PINT0,
+               .end = IRQ_PINT0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device bfin_pint0_device = {
+       .name = ADI_PINT_DEVNAME,
+       .id = 0,
+       .num_resources = ARRAY_SIZE(bfin_pint0_resources),
+       .resource = bfin_pint0_resources,
+};
+
+static struct resource bfin_pint1_resources[] = {
+       {
+               .start = PINT1_MASK_SET,
+               .end = PINT1_LATCH + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PINT1,
+               .end = IRQ_PINT1,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device bfin_pint1_device = {
+       .name = ADI_PINT_DEVNAME,
+       .id = 1,
+       .num_resources = ARRAY_SIZE(bfin_pint1_resources),
+       .resource = bfin_pint1_resources,
+};
+
+static struct resource bfin_pint2_resources[] = {
+       {
+               .start = PINT2_MASK_SET,
+               .end = PINT2_LATCH + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PINT2,
+               .end = IRQ_PINT2,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device bfin_pint2_device = {
+       .name = ADI_PINT_DEVNAME,
+       .id = 2,
+       .num_resources = ARRAY_SIZE(bfin_pint2_resources),
+       .resource = bfin_pint2_resources,
+};
+
+static struct resource bfin_pint3_resources[] = {
+       {
+               .start = PINT3_MASK_SET,
+               .end = PINT3_LATCH + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PINT3,
+               .end = IRQ_PINT3,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device bfin_pint3_device = {
+       .name = ADI_PINT_DEVNAME,
+       .id = 3,
+       .num_resources = ARRAY_SIZE(bfin_pint3_resources),
+       .resource = bfin_pint3_resources,
+};
+
+static struct resource bfin_gpa_resources[] = {
+       {
+               .start = PORTA_FER,
+               .end = PORTA_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {       /* optional */
+               .start = IRQ_PA0,
+               .end = IRQ_PA0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpa_pdata = {
+       .port_gpio_base = GPIO_PA0,     /* Optional */
+       .port_pin_base  = GPIO_PA0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 0,            /* PINT0 */
+       .pint_assign    = true,         /* PINT upper 16 bit */
+       .pint_map       = 0,            /* mapping mask in PINT */
+};
+
+static struct platform_device bfin_gpa_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 0,
+       .num_resources = ARRAY_SIZE(bfin_gpa_resources),
+       .resource = bfin_gpa_resources,
+       .dev = {
+               .platform_data = &bfin_gpa_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpb_resources[] = {
+       {
+               .start = PORTB_FER,
+               .end = PORTB_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PB0,
+               .end = IRQ_PB0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpb_pdata = {
+       .port_gpio_base = GPIO_PB0,
+       .port_pin_base  = GPIO_PB0,
+       .port_width     = 15,
+       .pint_id        = 0,
+       .pint_assign    = true,
+       .pint_map       = 1,
+};
+
+static struct platform_device bfin_gpb_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 1,
+       .num_resources = ARRAY_SIZE(bfin_gpb_resources),
+       .resource = bfin_gpb_resources,
+       .dev = {
+               .platform_data = &bfin_gpb_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpc_resources[] = {
+       {
+               .start = PORTC_FER,
+               .end = PORTC_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PC0,
+               .end = IRQ_PC0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpc_pdata = {
+       .port_gpio_base = GPIO_PC0,
+       .port_pin_base  = GPIO_PC0,
+       .port_width     = 14,
+       .pint_id        = 2,
+       .pint_assign    = true,
+       .pint_map       = 0,
+};
+
+static struct platform_device bfin_gpc_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 2,
+       .num_resources = ARRAY_SIZE(bfin_gpc_resources),
+       .resource = bfin_gpc_resources,
+       .dev = {
+               .platform_data = &bfin_gpc_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpd_resources[] = {
+       {
+               .start = PORTD_FER,
+               .end = PORTD_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PD0,
+               .end = IRQ_PD0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpd_pdata = {
+       .port_gpio_base = GPIO_PD0,
+       .port_pin_base  = GPIO_PD0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 2,
+       .pint_assign    = false,
+       .pint_map       = 1,
+};
+
+static struct platform_device bfin_gpd_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 3,
+       .num_resources = ARRAY_SIZE(bfin_gpd_resources),
+       .resource = bfin_gpd_resources,
+       .dev = {
+               .platform_data = &bfin_gpd_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpe_resources[] = {
+       {
+               .start = PORTE_FER,
+               .end = PORTE_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PE0,
+               .end = IRQ_PE0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpe_pdata = {
+       .port_gpio_base = GPIO_PE0,
+       .port_pin_base  = GPIO_PE0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 3,
+       .pint_assign    = true,
+       .pint_map       = 2,
+};
+
+static struct platform_device bfin_gpe_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 4,
+       .num_resources = ARRAY_SIZE(bfin_gpe_resources),
+       .resource = bfin_gpe_resources,
+       .dev = {
+               .platform_data = &bfin_gpe_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpf_resources[] = {
+       {
+               .start = PORTF_FER,
+               .end = PORTF_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PF0,
+               .end = IRQ_PF0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpf_pdata = {
+       .port_gpio_base = GPIO_PF0,
+       .port_pin_base  = GPIO_PF0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 3,
+       .pint_assign    = false,
+       .pint_map       = 3,
+};
+
+static struct platform_device bfin_gpf_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 5,
+       .num_resources = ARRAY_SIZE(bfin_gpf_resources),
+       .resource = bfin_gpf_resources,
+       .dev = {
+               .platform_data = &bfin_gpf_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpg_resources[] = {
+       {
+               .start = PORTG_FER,
+               .end = PORTG_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PG0,
+               .end = IRQ_PG0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpg_pdata = {
+       .port_gpio_base = GPIO_PG0,
+       .port_pin_base  = GPIO_PG0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = -1,
+};
+
+static struct platform_device bfin_gpg_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 6,
+       .num_resources = ARRAY_SIZE(bfin_gpg_resources),
+       .resource = bfin_gpg_resources,
+       .dev = {
+               .platform_data = &bfin_gpg_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gph_resources[] = {
+       {
+               .start = PORTH_FER,
+               .end = PORTH_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PH0,
+               .end = IRQ_PH0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gph_pdata = {
+       .port_gpio_base = GPIO_PH0,
+       .port_pin_base  = GPIO_PH0,
+       .port_width     = 14,
+       .pint_id        = -1,
+};
+
+static struct platform_device bfin_gph_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 7,
+       .num_resources = ARRAY_SIZE(bfin_gph_resources),
+       .resource = bfin_gph_resources,
+       .dev = {
+               .platform_data = &bfin_gph_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpi_resources[] = {
+       {
+               .start = PORTI_FER,
+               .end = PORTI_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PI0,
+               .end = IRQ_PI0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpi_pdata = {
+       .port_gpio_base = GPIO_PI0,
+       .port_pin_base  = GPIO_PI0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = -1,
+};
+
+static struct platform_device bfin_gpi_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 8,
+       .num_resources = ARRAY_SIZE(bfin_gpi_resources),
+       .resource = bfin_gpi_resources,
+       .dev = {
+               .platform_data = &bfin_gpi_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpj_resources[] = {
+       {
+               .start = PORTJ_FER,
+               .end = PORTJ_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PJ0,
+               .end = IRQ_PJ0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpj_pdata = {
+       .port_gpio_base = GPIO_PJ0,
+       .port_pin_base  = GPIO_PJ0,
+       .port_width     = 14,
+       .pint_id        = -1,
+};
+
+static struct platform_device bfin_gpj_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 9,
+       .num_resources = ARRAY_SIZE(bfin_gpj_resources),
+       .resource = bfin_gpj_resources,
+       .dev = {
+               .platform_data = &bfin_gpj_pdata, /* Passed to driver */
+       },
+};
+
+#endif
+
 static struct spi_board_info bfin_spi_board_info[] __initdata = {
 #if defined(CONFIG_MTD_M25P80) \
        || defined(CONFIG_MTD_M25P80_MODULE)
@@ -1066,7 +1502,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .modalias = "m25p80", /* Name of spi_driver for this device */
                .max_speed_hz = 25000000,     /* max spi clock (SCK) speed in HZ */
                .bus_num = 0, /* Framework bus number */
-               .chip_select = 1, /* SPI_SSEL1*/
+               .chip_select = MAX_CTRL_CS + GPIO_PE4, /* SPI_SSEL1*/
                .platform_data = &bfin_spi_flash_data,
                .controller_data = &spi_flash_chip_info,
                .mode = SPI_MODE_3,
@@ -1078,7 +1514,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .modalias = "ad183x",
                .max_speed_hz = 3125000,     /* max spi clock (SCK) speed in HZ */
                .bus_num = 1,
-               .chip_select = 4,
+               .chip_select = MAX_CTRL_CS + GPIO_PG6, /* SPI_SSEL2 */
        },
 #endif
 #if defined(CONFIG_TOUCHSCREEN_AD7877) || defined(CONFIG_TOUCHSCREEN_AD7877_MODULE)
@@ -1088,7 +1524,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .irq                    = IRQ_PB4,      /* old boards (<=Rev 1.3) use IRQ_PJ11 */
                .max_speed_hz           = 12500000,     /* max spi clock (SCK) speed in HZ */
                .bus_num                = 0,
-               .chip_select            = 2,
+               .chip_select            = MAX_CTRL_CS + GPIO_PE5, /* SPI_SSEL2 */
        },
 #endif
 #if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
@@ -1096,7 +1532,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .modalias = "spidev",
                .max_speed_hz = 3125000,     /* max spi clock (SCK) speed in HZ */
                .bus_num = 0,
-               .chip_select = 1,
+               .chip_select = MAX_CTRL_CS + GPIO_PE4, /* SPI_SSEL1 */
        },
 #endif
 #if defined(CONFIG_INPUT_ADXL34X_SPI) || defined(CONFIG_INPUT_ADXL34X_SPI_MODULE)
@@ -1106,7 +1542,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .irq                    = IRQ_PC5,
                .max_speed_hz           = 5000000,     /* max spi clock (SCK) speed in HZ */
                .bus_num                = 1,
-               .chip_select            = 2,
+               .chip_select            = MAX_CTRL_CS + GPIO_PG6, /* SPI_SSEL2 */
                .mode = SPI_MODE_3,
        },
 #endif
@@ -1152,7 +1588,7 @@ static struct resource bfin_spi1_resource[] = {
 
 /* SPI controller data */
 static struct bfin5xx_spi_master bf54x_spi_master_info0 = {
-       .num_chipselect = 4,
+       .num_chipselect = MAX_CTRL_CS + MAX_BLACKFIN_GPIOS,
        .enable_dma = 1,  /* master has the ability to do dma transfer */
        .pin_req = {P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0},
 };
@@ -1168,7 +1604,7 @@ static struct platform_device bf54x_spi_master0 = {
 };
 
 static struct bfin5xx_spi_master bf54x_spi_master_info1 = {
-       .num_chipselect = 4,
+       .num_chipselect = MAX_CTRL_CS + MAX_BLACKFIN_GPIOS,
        .enable_dma = 1,  /* master has the ability to do dma transfer */
        .pin_req = {P_SPI1_SCK, P_SPI1_MISO, P_SPI1_MOSI, 0},
 };
@@ -1508,6 +1944,23 @@ static struct platform_device bfin_ac97 = {
 static struct platform_device *ezkit_devices[] __initdata = {
 
        &bfin_dpmc,
+#if defined(CONFIG_PINCTRL_ADI2)
+       &bfin_pinctrl_device,
+       &bfin_pint0_device,
+       &bfin_pint1_device,
+       &bfin_pint2_device,
+       &bfin_pint3_device,
+       &bfin_gpa_device,
+       &bfin_gpb_device,
+       &bfin_gpc_device,
+       &bfin_gpd_device,
+       &bfin_gpe_device,
+       &bfin_gpf_device,
+       &bfin_gpg_device,
+       &bfin_gph_device,
+       &bfin_gpi_device,
+       &bfin_gpj_device,
+#endif
 
 #if defined(CONFIG_RTC_DRV_BFIN) || defined(CONFIG_RTC_DRV_BFIN_MODULE)
        &rtc_device,
@@ -1644,10 +2097,66 @@ static struct platform_device *ezkit_devices[] __initdata = {
 #endif
 };
 
+/* Pin control settings */
+static struct pinctrl_map __initdata bfin_pinmux_map[] = {
+       /* per-device maps */
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-uart.0",  "pinctrl-adi2.0", NULL, "uart0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-uart.1",  "pinctrl-adi2.0", NULL, "uart1"),
+#ifdef CONFIG_BFIN_UART1_CTSRTS
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-uart.1",  "pinctrl-adi2.0", NULL, "uart1_ctsrts"),
+#endif
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-uart.2",  "pinctrl-adi2.0", NULL, "uart2"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-uart.3",  "pinctrl-adi2.0", NULL, "uart3"),
+#ifdef CONFIG_BFIN_UART3_CTSRTS
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-uart.3",  "pinctrl-adi2.0", NULL, "uart3_ctsrts"),
+#endif
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_sir.0",  "pinctrl-adi2.0", NULL, "uart0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_sir.1",  "pinctrl-adi2.0", NULL, "uart1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_sir.2",  "pinctrl-adi2.0", NULL, "uart2"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_sir.3",  "pinctrl-adi2.0", NULL, "uart3"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-sdh.0",  "pinctrl-adi2.0", NULL, "rsi0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-spi.0",  "pinctrl-adi2.0", NULL, "spi0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-spi.1",  "pinctrl-adi2.0", NULL, "spi1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("i2c-bfin-twi.0",  "pinctrl-adi2.0", NULL, "twi0"),
+#if !defined(CONFIG_BF542)     /* The BF542 only has 1 TWI */
+       PIN_MAP_MUX_GROUP_DEFAULT("i2c-bfin-twi.1",  "pinctrl-adi2.0", NULL, "twi1"),
+#endif
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-rotary",  "pinctrl-adi2.0", NULL, "rotary"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_can.0",  "pinctrl-adi2.0", NULL, "can0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_can.1",  "pinctrl-adi2.0", NULL, "can1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bf54x-lq043",  "pinctrl-adi2.0", NULL, "ppi0_24b"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-i2s.0",  "pinctrl-adi2.0", NULL, "sport0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-tdm.0",  "pinctrl-adi2.0", NULL, "sport0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-ac97.0",  "pinctrl-adi2.0", NULL, "sport0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-i2s.1",  "pinctrl-adi2.0", NULL, "sport1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-tdm.1",  "pinctrl-adi2.0", NULL, "sport1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-ac97.1",  "pinctrl-adi2.0", NULL, "sport1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-i2s.2",  "pinctrl-adi2.0", NULL, "sport2"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-tdm.2",  "pinctrl-adi2.0", NULL, "sport2"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-ac97.2",  "pinctrl-adi2.0", NULL, "sport2"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-i2s.3",  "pinctrl-adi2.0", NULL, "sport3"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-tdm.3",  "pinctrl-adi2.0", NULL, "sport3"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-ac97.3",  "pinctrl-adi2.0", NULL, "sport3"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-sport-uart.0",  "pinctrl-adi2.0", NULL, "sport0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-sport-uart.1",  "pinctrl-adi2.0", NULL, "sport1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-sport-uart.2",  "pinctrl-adi2.0", NULL, "sport2"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-sport-uart.3",  "pinctrl-adi2.0", NULL, "sport3"),
+       PIN_MAP_MUX_GROUP_DEFAULT("pata-bf54x",  "pinctrl-adi2.0", NULL, "atapi"),
+#ifdef CONFIG_BF548_ATAPI_ALTERNATIVE_PORT
+       PIN_MAP_MUX_GROUP_DEFAULT("pata-bf54x",  "pinctrl-adi2.0", NULL, "atapi_alter"),
+#endif
+       PIN_MAP_MUX_GROUP_DEFAULT("bf5xx-nand.0",  "pinctrl-adi2.0", NULL, "nfc0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bf54x-keys",  "pinctrl-adi2.0", NULL, "keys_4x4"),
+};
+
 static int __init ezkit_init(void)
 {
        printk(KERN_INFO "%s(): registering device resources\n", __func__);
 
+       /* Initialize pinmuxing */
+       pinctrl_register_mappings(bfin_pinmux_map,
+                               ARRAY_SIZE(bfin_pinmux_map));
+
        i2c_register_board_info(0, bfin_i2c_board_info0,
                                ARRAY_SIZE(bfin_i2c_board_info0));
 #if !defined(CONFIG_BF542)     /* The BF542 only has 1 TWI */
@@ -1679,21 +2188,6 @@ static struct platform_device *ezkit_early_devices[] __initdata = {
        &bfin_uart3_device,
 #endif
 #endif
-
-#if defined(CONFIG_SERIAL_BFIN_SPORT_CONSOLE)
-#ifdef CONFIG_SERIAL_BFIN_SPORT0_UART
-       &bfin_sport0_uart_device,
-#endif
-#ifdef CONFIG_SERIAL_BFIN_SPORT1_UART
-       &bfin_sport1_uart_device,
-#endif
-#ifdef CONFIG_SERIAL_BFIN_SPORT2_UART
-       &bfin_sport2_uart_device,
-#endif
-#ifdef CONFIG_SERIAL_BFIN_SPORT3_UART
-       &bfin_sport3_uart_device,
-#endif
-#endif
 };
 
 void __init native_machine_early_platform_add_devices(void)
index be9edb2..006da1e 100644 (file)
@@ -194,14 +194,6 @@ struct gpio_port_t {
        unsigned int port_mux;
 };
 
-struct gpio_port_s {
-       unsigned short fer;
-       unsigned short data;
-       unsigned short dir;
-       unsigned short inen;
-       unsigned int mux;
-};
-
 #endif
 
 #include <mach-common/ports-a.h>
index 10dc142..cf7cb72 100644 (file)
 #include <linux/types.h>
 
 /*
- * bfin pint registers layout
+ * gpio pint registers layout
  */
 struct bfin_pint_regs {
        u32 mask_set;
index 2bcbf94..b0fca44 100644 (file)
@@ -9,48 +9,6 @@ source "arch/blackfin/mach-bf609/boards/Kconfig"
 
 menu "BF609 Specific Configuration"
 
-comment "Pin Interrupt to Port Assignment"
-menu "Assignment"
-
-config PINTx_REASSIGN
-       bool "Reprogram PINT Assignment"
-       default y
-       help
-         The interrupt assignment registers controls the pin-to-interrupt
-         assignment in a byte-wide manner. Each option allows you to select
-         a set of pins (High/Low Byte) of an specific Port being mapped
-         to one of the four PIN Interrupts IRQ_PINTx.
-
-         You shouldn't change any of these unless you know exactly what you're doing.
-         Please consult the Blackfin BF60x Processor Hardware Reference Manual.
-
-config PINT0_ASSIGN
-       hex "PINT0_ASSIGN"
-       depends on PINTx_REASSIGN
-       default 0x00000101
-config PINT1_ASSIGN
-       hex "PINT1_ASSIGN"
-       depends on PINTx_REASSIGN
-       default 0x00000101
-config PINT2_ASSIGN
-       hex "PINT2_ASSIGN"
-       depends on PINTx_REASSIGN
-       default 0x00000101
-config PINT3_ASSIGN
-       hex "PINT3_ASSIGN"
-       depends on PINTx_REASSIGN
-       default 0x00000101
-config PINT4_ASSIGN
-       hex "PINT3_ASSIGN"
-       depends on PINTx_REASSIGN
-       default 0x00000101
-config PINT5_ASSIGN
-       hex "PINT3_ASSIGN"
-       depends on PINTx_REASSIGN
-       default 0x00000101
-
-endmenu
-
 config SEC_IRQ_PRIORITY_LEVELS
        int "SEC interrupt priority levels"
        default 7
index d56a55a..82beedd 100644 (file)
@@ -17,6 +17,9 @@
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/usb/musb.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/platform_data/pinctrl-adi2.h>
 #include <asm/bfin_spi3.h>
 #include <asm/dma.h>
 #include <asm/gpio.h>
@@ -106,8 +109,6 @@ static struct platform_device bfin_rotary_device = {
 #include <linux/stmmac.h>
 #include <linux/phy.h>
 
-static unsigned short pins[] = P_RMII0;
-
 static struct stmmac_mdio_bus_data phy_private_data = {
        .phy_mask = 1,
 };
@@ -212,6 +213,18 @@ static struct resource bfin_uart0_resources[] = {
                .end = UART0_RXDIV+4,
                .flags = IORESOURCE_MEM,
        },
+#ifdef CONFIG_EARLY_PRINTK
+       {
+               .start = PORTD_FER,
+               .end = PORTD_FER+2,
+               .flags = IORESOURCE_REG,
+       },
+       {
+               .start = PORTD_MUX,
+               .end = PORTD_MUX+3,
+               .flags = IORESOURCE_REG,
+       },
+#endif
        {
                .start = IRQ_UART0_TX,
                .end = IRQ_UART0_TX,
@@ -276,6 +289,13 @@ static struct resource bfin_uart1_resources[] = {
                .end = UART1_RXDIV+4,
                .flags = IORESOURCE_MEM,
        },
+#ifdef CONFIG_EARLY_PRINTK
+       {
+               .start = PORTG_FER_SET,
+               .end = PORTG_FER_SET+2,
+               .flags = IORESOURCE_REG,
+       },
+#endif
        {
                .start = IRQ_UART1_TX,
                .end = IRQ_UART1_TX,
@@ -674,17 +694,12 @@ static struct mtd_partition ezkit_partitions[] = {
        },
 };
 
-int bf609_nor_flash_init(struct platform_device *dev)
+int bf609_nor_flash_init(struct platform_device *pdev)
 {
 #define CONFIG_SMC_GCTL_VAL     0x00000010
-       const unsigned short pins[] = {
-               P_A3, P_A4, P_A5, P_A6, P_A7, P_A8, P_A9, P_A10, P_A11, P_A12,
-               P_A13, P_A14, P_A15, P_A16, P_A17, P_A18, P_A19, P_A20, P_A21,
-               P_A22, P_A23, P_A24, P_A25, P_NORCK, 0,
-       };
-
-       peripheral_request_list(pins, "smc0");
 
+       if (!devm_pinctrl_get_select_default(&pdev->dev))
+               return -EBUSY;
        bfin_write32(SMC_GCTL, CONFIG_SMC_GCTL_VAL);
        bfin_write32(SMC_B0CTL, 0x01002011);
        bfin_write32(SMC_B0TIM, 0x08170977);
@@ -692,16 +707,9 @@ int bf609_nor_flash_init(struct platform_device *dev)
        return 0;
 }
 
-void bf609_nor_flash_exit(struct platform_device *dev)
+void bf609_nor_flash_exit(struct platform_device *pdev)
 {
-       const unsigned short pins[] = {
-               P_A3, P_A4, P_A5, P_A6, P_A7, P_A8, P_A9, P_A10, P_A11, P_A12,
-               P_A13, P_A14, P_A15, P_A16, P_A17, P_A18, P_A19, P_A20, P_A21,
-               P_A22, P_A23, P_A24, P_A25, P_NORCK, 0,
-       };
-
-       peripheral_free_list(pins);
-
+       devm_pinctrl_put(pdev->dev.pins->p);
        bfin_write32(SMC_GCTL, 0);
 }
 
@@ -1319,6 +1327,356 @@ static const struct ad7877_platform_data bfin_ad7877_ts_info = {
 };
 #endif
 
+#ifdef CONFIG_PINCTRL_ADI2
+
+# define ADI_PINT_DEVNAME "adi-gpio-pint"
+# define ADI_GPIO_DEVNAME "adi-gpio"
+# define ADI_PINCTRL_DEVNAME "pinctrl-adi2"
+
+static struct platform_device bfin_pinctrl_device = {
+       .name = ADI_PINCTRL_DEVNAME,
+       .id = 0,
+};
+
+static struct resource bfin_pint0_resources[] = {
+       {
+               .start = PINT0_MASK_SET,
+               .end = PINT0_LATCH + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PINT0,
+               .end = IRQ_PINT0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device bfin_pint0_device = {
+       .name = ADI_PINT_DEVNAME,
+       .id = 0,
+       .num_resources = ARRAY_SIZE(bfin_pint0_resources),
+       .resource = bfin_pint0_resources,
+};
+
+static struct resource bfin_pint1_resources[] = {
+       {
+               .start = PINT1_MASK_SET,
+               .end = PINT1_LATCH + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PINT1,
+               .end = IRQ_PINT1,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device bfin_pint1_device = {
+       .name = ADI_PINT_DEVNAME,
+       .id = 1,
+       .num_resources = ARRAY_SIZE(bfin_pint1_resources),
+       .resource = bfin_pint1_resources,
+};
+
+static struct resource bfin_pint2_resources[] = {
+       {
+               .start = PINT2_MASK_SET,
+               .end = PINT2_LATCH + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PINT2,
+               .end = IRQ_PINT2,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device bfin_pint2_device = {
+       .name = ADI_PINT_DEVNAME,
+       .id = 2,
+       .num_resources = ARRAY_SIZE(bfin_pint2_resources),
+       .resource = bfin_pint2_resources,
+};
+
+static struct resource bfin_pint3_resources[] = {
+       {
+               .start = PINT3_MASK_SET,
+               .end = PINT3_LATCH + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PINT3,
+               .end = IRQ_PINT3,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device bfin_pint3_device = {
+       .name = ADI_PINT_DEVNAME,
+       .id = 3,
+       .num_resources = ARRAY_SIZE(bfin_pint3_resources),
+       .resource = bfin_pint3_resources,
+};
+
+static struct resource bfin_pint4_resources[] = {
+       {
+               .start = PINT4_MASK_SET,
+               .end = PINT4_LATCH + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PINT4,
+               .end = IRQ_PINT4,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device bfin_pint4_device = {
+       .name = ADI_PINT_DEVNAME,
+       .id = 4,
+       .num_resources = ARRAY_SIZE(bfin_pint4_resources),
+       .resource = bfin_pint4_resources,
+};
+
+static struct resource bfin_pint5_resources[] = {
+       {
+               .start = PINT5_MASK_SET,
+               .end = PINT5_LATCH + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PINT5,
+               .end = IRQ_PINT5,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device bfin_pint5_device = {
+       .name = ADI_PINT_DEVNAME,
+       .id = 5,
+       .num_resources = ARRAY_SIZE(bfin_pint5_resources),
+       .resource = bfin_pint5_resources,
+};
+
+static struct resource bfin_gpa_resources[] = {
+       {
+               .start = PORTA_FER,
+               .end = PORTA_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {       /* optional */
+               .start = IRQ_PA0,
+               .end = IRQ_PA0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpa_pdata = {
+       .port_pin_base  = GPIO_PA0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 0,            /* PINT0 */
+       .pint_assign    = true,         /* PINT upper 16 bit */
+       .pint_map       = 0,            /* mapping mask in PINT */
+};
+
+static struct platform_device bfin_gpa_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 0,
+       .num_resources = ARRAY_SIZE(bfin_gpa_resources),
+       .resource = bfin_gpa_resources,
+       .dev = {
+               .platform_data = &bfin_gpa_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpb_resources[] = {
+       {
+               .start = PORTB_FER,
+               .end = PORTB_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PB0,
+               .end = IRQ_PB0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpb_pdata = {
+       .port_pin_base  = GPIO_PB0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 0,
+       .pint_assign    = false,
+       .pint_map       = 1,
+};
+
+static struct platform_device bfin_gpb_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 1,
+       .num_resources = ARRAY_SIZE(bfin_gpb_resources),
+       .resource = bfin_gpb_resources,
+       .dev = {
+               .platform_data = &bfin_gpb_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpc_resources[] = {
+       {
+               .start = PORTC_FER,
+               .end = PORTC_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PC0,
+               .end = IRQ_PC0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpc_pdata = {
+       .port_pin_base  = GPIO_PC0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 1,
+       .pint_assign    = false,
+       .pint_map       = 1,
+};
+
+static struct platform_device bfin_gpc_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 2,
+       .num_resources = ARRAY_SIZE(bfin_gpc_resources),
+       .resource = bfin_gpc_resources,
+       .dev = {
+               .platform_data = &bfin_gpc_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpd_resources[] = {
+       {
+               .start = PORTD_FER,
+               .end = PORTD_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PD0,
+               .end = IRQ_PD0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpd_pdata = {
+       .port_pin_base  = GPIO_PD0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 2,
+       .pint_assign    = false,
+       .pint_map       = 1,
+};
+
+static struct platform_device bfin_gpd_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 3,
+       .num_resources = ARRAY_SIZE(bfin_gpd_resources),
+       .resource = bfin_gpd_resources,
+       .dev = {
+               .platform_data = &bfin_gpd_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpe_resources[] = {
+       {
+               .start = PORTE_FER,
+               .end = PORTE_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PE0,
+               .end = IRQ_PE0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpe_pdata = {
+       .port_pin_base  = GPIO_PE0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 3,
+       .pint_assign    = false,
+       .pint_map       = 1,
+};
+
+static struct platform_device bfin_gpe_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 4,
+       .num_resources = ARRAY_SIZE(bfin_gpe_resources),
+       .resource = bfin_gpe_resources,
+       .dev = {
+               .platform_data = &bfin_gpe_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpf_resources[] = {
+       {
+               .start = PORTF_FER,
+               .end = PORTF_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PF0,
+               .end = IRQ_PF0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpf_pdata = {
+       .port_pin_base  = GPIO_PF0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 4,
+       .pint_assign    = false,
+       .pint_map       = 1,
+};
+
+static struct platform_device bfin_gpf_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 5,
+       .num_resources = ARRAY_SIZE(bfin_gpf_resources),
+       .resource = bfin_gpf_resources,
+       .dev = {
+               .platform_data = &bfin_gpf_pdata, /* Passed to driver */
+       },
+};
+
+static struct resource bfin_gpg_resources[] = {
+       {
+               .start = PORTG_FER,
+               .end = PORTG_MUX + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_PG0,
+               .end = IRQ_PG0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct adi_pinctrl_gpio_platform_data bfin_gpg_pdata = {
+       .port_pin_base  = GPIO_PG0,
+       .port_width     = GPIO_BANKSIZE,
+       .pint_id        = 5,
+       .pint_assign    = false,
+       .pint_map       = 1,
+};
+
+static struct platform_device bfin_gpg_device = {
+       .name = ADI_GPIO_DEVNAME,
+       .id = 6,
+       .num_resources = ARRAY_SIZE(bfin_gpg_resources),
+       .resource = bfin_gpg_resources,
+       .dev = {
+               .platform_data = &bfin_gpg_pdata, /* Passed to driver */
+       },
+};
+
+#endif
+
 #if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
 #include <linux/input.h>
 #include <linux/gpio_keys.h>
@@ -1349,7 +1707,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .modalias = "m25p80", /* Name of spi_driver for this device */
                .max_speed_hz = 25000000,     /* max spi clock (SCK) speed in HZ */
                .bus_num = 0, /* Framework bus number */
-               .chip_select = 1, /* SPI_SSEL1*/
+               .chip_select = MAX_CTRL_CS + GPIO_PD11, /* SPI_SSEL1*/
                .platform_data = &bfin_spi_flash_data,
                .controller_data = &spi_flash_chip_info,
                .mode = SPI_MODE_3,
@@ -1362,7 +1720,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .irq                    = IRQ_PD9,
                .max_speed_hz           = 12500000,     /* max spi clock (SCK) speed in HZ */
                .bus_num                = 0,
-               .chip_select            = 4,
+               .chip_select            = MAX_CTRL_CS + GPIO_PC15, /* SPI_SSEL4 */
        },
 #endif
 #if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
@@ -1370,7 +1728,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .modalias = "spidev",
                .max_speed_hz = 3125000,     /* max spi clock (SCK) speed in HZ */
                .bus_num = 0,
-               .chip_select = 1,
+               .chip_select = MAX_CTRL_CS + GPIO_PD11, /* SPI_SSEL1*/
                .controller_data = &spidev_chip_info,
        },
 #endif
@@ -1565,6 +1923,22 @@ static struct platform_device bfin_dpmc = {
 static struct platform_device *ezkit_devices[] __initdata = {
 
        &bfin_dpmc,
+#if defined(CONFIG_PINCTRL_ADI2)
+       &bfin_pinctrl_device,
+       &bfin_pint0_device,
+       &bfin_pint1_device,
+       &bfin_pint2_device,
+       &bfin_pint3_device,
+       &bfin_pint4_device,
+       &bfin_pint5_device,
+       &bfin_gpa_device,
+       &bfin_gpb_device,
+       &bfin_gpc_device,
+       &bfin_gpd_device,
+       &bfin_gpe_device,
+       &bfin_gpf_device,
+       &bfin_gpg_device,
+#endif
 
 #if defined(CONFIG_RTC_DRV_BFIN) || defined(CONFIG_RTC_DRV_BFIN_MODULE)
        &rtc_device,
@@ -1681,20 +2055,52 @@ static struct platform_device *ezkit_devices[] __initdata = {
 
 };
 
+/* Pin control settings */
+static struct pinctrl_map __initdata bfin_pinmux_map[] = {
+       /* per-device maps */
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-uart.0",  "pinctrl-adi2.0", NULL, "uart0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-uart.1",  "pinctrl-adi2.0", NULL, "uart1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_sir.0",  "pinctrl-adi2.0", NULL, "uart0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_sir.1",  "pinctrl-adi2.0", NULL, "uart1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-sdh.0",  "pinctrl-adi2.0", NULL, "rsi0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("stmmaceth.0",  "pinctrl-adi2.0", NULL, "eth0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-spi3.0",  "pinctrl-adi2.0", NULL, "spi0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-spi3.1",  "pinctrl-adi2.0", NULL, "spi1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("i2c-bfin-twi.0",  "pinctrl-adi2.0", NULL, "twi0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("i2c-bfin-twi.1",  "pinctrl-adi2.0", NULL, "twi1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-rotary",  "pinctrl-adi2.0", NULL, "rotary"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_can.0",  "pinctrl-adi2.0", NULL, "can0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("physmap-flash.0",  "pinctrl-adi2.0", NULL, "smc0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bf609_nl8048.2",  "pinctrl-adi2.0", NULL, "ppi2_16b"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_display.0",  "pinctrl-adi2.0", NULL, "ppi0_16b"),
+#if defined(CONFIG_VIDEO_MT9M114) || defined(CONFIG_VIDEO_MT9M114_MODULE)
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_capture.0",  "pinctrl-adi2.0", NULL, "ppi0_8b"),
+#elif defined(CONFIG_VIDEO_VS6624) || defined(CONFIG_VIDEO_VS6624_MODULE)
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_capture.0",  "pinctrl-adi2.0", NULL, "ppi0_16b"),
+#else
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin_capture.0",  "pinctrl-adi2.0", NULL, "ppi0_24b"),
+#endif
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-i2s.0",  "pinctrl-adi2.0", NULL, "sport0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-tdm.0",  "pinctrl-adi2.0", NULL, "sport0"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-i2s.1",  "pinctrl-adi2.0", NULL, "sport1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-tdm.1",  "pinctrl-adi2.0", NULL, "sport1"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-i2s.2",  "pinctrl-adi2.0", NULL, "sport2"),
+       PIN_MAP_MUX_GROUP_DEFAULT("bfin-tdm.2",  "pinctrl-adi2.0", NULL, "sport2"),
+};
+
 static int __init ezkit_init(void)
 {
        printk(KERN_INFO "%s(): registering device resources\n", __func__);
 
+       /* Initialize pinmuxing */
+       pinctrl_register_mappings(bfin_pinmux_map,
+                               ARRAY_SIZE(bfin_pinmux_map));
+
        i2c_register_board_info(0, bfin_i2c_board_info0,
                                ARRAY_SIZE(bfin_i2c_board_info0));
        i2c_register_board_info(1, bfin_i2c_board_info1,
                                ARRAY_SIZE(bfin_i2c_board_info1));
 
-#if defined(CONFIG_STMMAC_ETH) || defined(CONFIG_STMMAC_ETH_MODULE)
-       if (!peripheral_request_list(pins, "emac0"))
-               printk(KERN_ERR "%s(): request emac pins failed\n", __func__);
-#endif
-
        platform_add_devices(ezkit_devices, ARRAY_SIZE(ezkit_devices));
 
        spi_register_board_info(bfin_spi_board_info, ARRAY_SIZE(bfin_spi_board_info));
@@ -1713,18 +2119,6 @@ static struct platform_device *ezkit_early_devices[] __initdata = {
        &bfin_uart1_device,
 #endif
 #endif
-
-#if defined(CONFIG_SERIAL_BFIN_SPORT_CONSOLE)
-#ifdef CONFIG_SERIAL_BFIN_SPORT0_UART
-       &bfin_sport0_uart_device,
-#endif
-#ifdef CONFIG_SERIAL_BFIN_SPORT1_UART
-       &bfin_sport1_uart_device,
-#endif
-#ifdef CONFIG_SERIAL_BFIN_SPORT2_UART
-       &bfin_sport2_uart_device,
-#endif
-#endif
 };
 
 void __init native_machine_early_platform_add_devices(void)
index c32c8cc..0718251 100644 (file)
@@ -152,14 +152,6 @@ struct gpio_port_t {
        unsigned long revid;
 };
 
-struct gpio_port_s {
-       unsigned short fer;
-       unsigned short data;
-       unsigned short dir;
-       unsigned short inen;
-       unsigned int mux;
-};
-
 #endif
 
 #include <mach-common/ports-a.h>
index fa0843d..d1cb6a8 100644 (file)
 extern u8 sec_int_priority[];
 
 /*
- * bfin pint registers layout
+ * gpio pint registers layout
  */
 struct bfin_pint_regs {
        u32 mask_set;
index fe34191..c48bb71 100644 (file)
@@ -19,6 +19,7 @@
 #define P_MII0_CRS     (P_DEFINED | P_IDENT(GPIO_PC5) | P_FUNCT(0))
 #define P_MII0_ERxER   (P_DEFINED | P_IDENT(GPIO_PC4) | P_FUNCT(0))
 #define P_MII0_TxCLK   (P_DEFINED | P_IDENT(GPIO_PB14) | P_FUNCT(0))
+#define P_MII0_PTPPPS  (P_DEFINED | P_IDENT(GPIO_PB15) | P_FUNCT(0))
 
 #define P_RMII0 {\
        P_MII0_ETxD0, \
@@ -30,6 +31,7 @@
        P_MII0_TxCLK, \
        P_MII0_PHYINT, \
        P_MII0_CRS, \
+       P_MII0_PTPPPS, \
        P_MII0_MDC, \
        P_MII0_MDIO, 0}
 
@@ -44,6 +46,7 @@
 #define P_MII1_CRS     (P_DEFINED | P_IDENT(GPIO_PE13) | P_FUNCT(0))
 #define P_MII1_ERxER   (P_DEFINED | P_IDENT(GPIO_PE14) | P_FUNCT(0))
 #define P_MII1_TxCLK   (P_DEFINED | P_IDENT(GPIO_PG6) | P_FUNCT(0))
+#define P_MII1_PTPPPS  (P_DEFINED | P_IDENT(GPIO_PC9) | P_FUNCT(0))
 
 #define P_RMII1 {\
        P_MII1_ETxD0, \
@@ -55,6 +58,7 @@
        P_MII1_TxCLK, \
        P_MII1_PHYINT, \
        P_MII1_CRS, \
+       P_MII1_PTPPPS, \
        P_MII1_MDC, \
        P_MII1_MDIO, 0}
 
index d143fd8..ca75613 100644 (file)
@@ -704,10 +704,9 @@ static inline void bfin_set_irq_handler(unsigned irq, irq_flow_handler_t handle)
        __irq_set_handler_locked(irq, handle);
 }
 
-static DECLARE_BITMAP(gpio_enabled, MAX_BLACKFIN_GPIOS);
-extern void bfin_gpio_irq_prepare(unsigned gpio);
+#ifdef CONFIG_GPIO_ADI
 
-#if !BFIN_GPIO_PINT
+static DECLARE_BITMAP(gpio_enabled, MAX_BLACKFIN_GPIOS);
 
 static void bfin_gpio_ack_irq(struct irq_data *d)
 {
@@ -821,15 +820,6 @@ static int bfin_gpio_irq_type(struct irq_data *d, unsigned int type)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int bfin_gpio_set_wake(struct irq_data *d, unsigned int state)
-{
-       return gpio_pm_wakeup_ctrl(irq_to_gpio(d->irq), state);
-}
-#else
-# define bfin_gpio_set_wake NULL
-#endif
-
 static void bfin_demux_gpio_block(unsigned int irq)
 {
        unsigned int gpio, mask;
@@ -896,279 +886,40 @@ void bfin_demux_gpio_irq(unsigned int inta_irq,
        bfin_demux_gpio_block(irq);
 }
 
-#else
-
-#define NR_PINT_BITS           32
-#define IRQ_NOT_AVAIL          0xFF
-
-#define PINT_2_BANK(x)         ((x) >> 5)
-#define PINT_2_BIT(x)          ((x) & 0x1F)
-#define PINT_BIT(x)            (1 << (PINT_2_BIT(x)))
-
-static unsigned char irq2pint_lut[NR_PINTS];
-static unsigned char pint2irq_lut[NR_PINT_SYS_IRQS * NR_PINT_BITS];
-
-static struct bfin_pint_regs * const pint[NR_PINT_SYS_IRQS] = {
-       (struct bfin_pint_regs *)PINT0_MASK_SET,
-       (struct bfin_pint_regs *)PINT1_MASK_SET,
-       (struct bfin_pint_regs *)PINT2_MASK_SET,
-       (struct bfin_pint_regs *)PINT3_MASK_SET,
-#ifdef CONFIG_BF60x
-       (struct bfin_pint_regs *)PINT4_MASK_SET,
-       (struct bfin_pint_regs *)PINT5_MASK_SET,
-#endif
-};
-
-inline unsigned int get_irq_base(u32 bank, u8 bmap)
-{
-       unsigned int irq_base;
-
-#ifndef CONFIG_BF60x
-       if (bank < 2) {         /*PA-PB */
-               irq_base = IRQ_PA0 + bmap * 16;
-       } else {                /*PC-PJ */
-               irq_base = IRQ_PC0 + bmap * 16;
-       }
-#else
-       irq_base = IRQ_PA0 + bank * 16 + bmap * 16;
-#endif
-       return irq_base;
-}
-
-       /* Whenever PINTx_ASSIGN is altered init_pint_lut() must be executed! */
-void init_pint_lut(void)
-{
-       u16 bank, bit, irq_base, bit_pos;
-       u32 pint_assign;
-       u8 bmap;
-
-       memset(irq2pint_lut, IRQ_NOT_AVAIL, sizeof(irq2pint_lut));
-
-       for (bank = 0; bank < NR_PINT_SYS_IRQS; bank++) {
-
-               pint_assign = pint[bank]->assign;
-
-               for (bit = 0; bit < NR_PINT_BITS; bit++) {
-
-                       bmap = (pint_assign >> ((bit / 8) * 8)) & 0xFF;
-
-                       irq_base = get_irq_base(bank, bmap);
-
-                       irq_base += (bit % 8) + ((bit / 8) & 1 ? 8 : 0);
-                       bit_pos = bit + bank * NR_PINT_BITS;
-
-                       pint2irq_lut[bit_pos] = irq_base - SYS_IRQS;
-                       irq2pint_lut[irq_base - SYS_IRQS] = bit_pos;
-               }
-       }
-}
-
-static void bfin_gpio_ack_irq(struct irq_data *d)
-{
-       u32 pint_val = irq2pint_lut[d->irq - SYS_IRQS];
-       u32 pintbit = PINT_BIT(pint_val);
-       u32 bank = PINT_2_BANK(pint_val);
-
-       if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) {
-               if (pint[bank]->invert_set & pintbit)
-                       pint[bank]->invert_clear = pintbit;
-               else
-                       pint[bank]->invert_set = pintbit;
-       }
-       pint[bank]->request = pintbit;
-
-}
-
-static void bfin_gpio_mask_ack_irq(struct irq_data *d)
-{
-       u32 pint_val = irq2pint_lut[d->irq - SYS_IRQS];
-       u32 pintbit = PINT_BIT(pint_val);
-       u32 bank = PINT_2_BANK(pint_val);
-
-       if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) {
-               if (pint[bank]->invert_set & pintbit)
-                       pint[bank]->invert_clear = pintbit;
-               else
-                       pint[bank]->invert_set = pintbit;
-       }
-
-       pint[bank]->request = pintbit;
-       pint[bank]->mask_clear = pintbit;
-}
-
-static void bfin_gpio_mask_irq(struct irq_data *d)
-{
-       u32 pint_val = irq2pint_lut[d->irq - SYS_IRQS];
-
-       pint[PINT_2_BANK(pint_val)]->mask_clear = PINT_BIT(pint_val);
-}
-
-static void bfin_gpio_unmask_irq(struct irq_data *d)
-{
-       u32 pint_val = irq2pint_lut[d->irq - SYS_IRQS];
-       u32 pintbit = PINT_BIT(pint_val);
-       u32 bank = PINT_2_BANK(pint_val);
-
-       pint[bank]->mask_set = pintbit;
-}
-
-static unsigned int bfin_gpio_irq_startup(struct irq_data *d)
-{
-       unsigned int irq = d->irq;
-       u32 gpionr = irq_to_gpio(irq);
-       u32 pint_val = irq2pint_lut[irq - SYS_IRQS];
-
-       if (pint_val == IRQ_NOT_AVAIL) {
-               printk(KERN_ERR
-               "GPIO IRQ %d :Not in PINT Assign table "
-               "Reconfigure Interrupt to Port Assignemt\n", irq);
-               return -ENODEV;
-       }
-
-       if (__test_and_set_bit(gpionr, gpio_enabled))
-               bfin_gpio_irq_prepare(gpionr);
-
-       bfin_gpio_unmask_irq(d);
-
-       return 0;
-}
-
-static void bfin_gpio_irq_shutdown(struct irq_data *d)
-{
-       u32 gpionr = irq_to_gpio(d->irq);
-
-       bfin_gpio_mask_irq(d);
-       __clear_bit(gpionr, gpio_enabled);
-       bfin_gpio_irq_free(gpionr);
-}
-
-static int bfin_gpio_irq_type(struct irq_data *d, unsigned int type)
-{
-       unsigned int irq = d->irq;
-       int ret;
-       char buf[16];
-       u32 gpionr = irq_to_gpio(irq);
-       u32 pint_val = irq2pint_lut[irq - SYS_IRQS];
-       u32 pintbit = PINT_BIT(pint_val);
-       u32 bank = PINT_2_BANK(pint_val);
-
-       if (pint_val == IRQ_NOT_AVAIL)
-               return -ENODEV;
-
-       if (type == IRQ_TYPE_PROBE) {
-               /* only probe unenabled GPIO interrupt lines */
-               if (test_bit(gpionr, gpio_enabled))
-                       return 0;
-               type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
-       }
-
-       if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
-                   IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
-
-               snprintf(buf, 16, "gpio-irq%d", irq);
-               ret = bfin_gpio_irq_request(gpionr, buf);
-               if (ret)
-                       return ret;
-
-               if (__test_and_set_bit(gpionr, gpio_enabled))
-                       bfin_gpio_irq_prepare(gpionr);
-
-       } else {
-               __clear_bit(gpionr, gpio_enabled);
-               return 0;
-       }
-
-       if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)))
-               pint[bank]->invert_set = pintbit;       /* low or falling edge denoted by one */
-       else
-               pint[bank]->invert_clear = pintbit;     /* high or rising edge denoted by zero */
-
-       if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
-           == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) {
-               if (gpio_get_value(gpionr))
-                       pint[bank]->invert_set = pintbit;
-               else
-                       pint[bank]->invert_clear = pintbit;
-       }
-
-       if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) {
-               pint[bank]->edge_set = pintbit;
-               bfin_set_irq_handler(irq, handle_edge_irq);
-       } else {
-               pint[bank]->edge_clear = pintbit;
-               bfin_set_irq_handler(irq, handle_level_irq);
-       }
-
-       return 0;
-}
-
 #ifdef CONFIG_PM
-static struct bfin_pm_pint_save save_pint_reg[NR_PINT_SYS_IRQS];
-static u32 save_pint_sec_ctl[NR_PINT_SYS_IRQS];
 
 static int bfin_gpio_set_wake(struct irq_data *d, unsigned int state)
 {
-       u32 pint_irq;
-       u32 pint_val = irq2pint_lut[d->irq - SYS_IRQS];
-       u32 bank = PINT_2_BANK(pint_val);
-
-       switch (bank) {
-       case 0:
-               pint_irq = IRQ_PINT0;
-               break;
-       case 2:
-               pint_irq = IRQ_PINT2;
-               break;
-       case 3:
-               pint_irq = IRQ_PINT3;
-               break;
-       case 1:
-               pint_irq = IRQ_PINT1;
-               break;
-#ifdef CONFIG_BF60x
-       case 4:
-               pint_irq = IRQ_PINT4;
-               break;
-       case 5:
-               pint_irq = IRQ_PINT5;
-               break;
-#endif
-       default:
-               return -EINVAL;
-       }
+       return bfin_gpio_pm_wakeup_ctrl(irq_to_gpio(d->irq), state);
+}
 
-#ifndef SEC_GCTL
-       bfin_internal_set_wake(pint_irq, state);
-#endif
+#else
 
-       return 0;
-}
+# define bfin_gpio_set_wake NULL
 
-void bfin_pint_suspend(void)
-{
-       u32 bank;
+#endif
 
-       for (bank = 0; bank < NR_PINT_SYS_IRQS; bank++) {
-               save_pint_reg[bank].mask_set = pint[bank]->mask_set;
-               save_pint_reg[bank].assign = pint[bank]->assign;
-               save_pint_reg[bank].edge_set = pint[bank]->edge_set;
-               save_pint_reg[bank].invert_set = pint[bank]->invert_set;
-       }
-}
+static struct irq_chip bfin_gpio_irqchip = {
+       .name = "GPIO",
+       .irq_ack = bfin_gpio_ack_irq,
+       .irq_mask = bfin_gpio_mask_irq,
+       .irq_mask_ack = bfin_gpio_mask_ack_irq,
+       .irq_unmask = bfin_gpio_unmask_irq,
+       .irq_disable = bfin_gpio_mask_irq,
+       .irq_enable = bfin_gpio_unmask_irq,
+       .irq_set_type = bfin_gpio_irq_type,
+       .irq_startup = bfin_gpio_irq_startup,
+       .irq_shutdown = bfin_gpio_irq_shutdown,
+       .irq_set_wake = bfin_gpio_set_wake,
+};
 
-void bfin_pint_resume(void)
-{
-       u32 bank;
+#endif
 
-       for (bank = 0; bank < NR_PINT_SYS_IRQS; bank++) {
-               pint[bank]->mask_set = save_pint_reg[bank].mask_set;
-               pint[bank]->assign = save_pint_reg[bank].assign;
-               pint[bank]->edge_set = save_pint_reg[bank].edge_set;
-               pint[bank]->invert_set = save_pint_reg[bank].invert_set;
-       }
-}
+#ifdef CONFIG_PM
 
 #ifdef SEC_GCTL
+static u32 save_pint_sec_ctl[NR_PINT_SYS_IRQS];
+
 static int sec_suspend(void)
 {
        u32 bank;
@@ -1195,92 +946,10 @@ static struct syscore_ops sec_pm_syscore_ops = {
        .suspend = sec_suspend,
        .resume = sec_resume,
 };
-
-#endif
-#else
-# define bfin_gpio_set_wake NULL
-#endif
-
-void bfin_demux_gpio_irq(unsigned int inta_irq,
-                       struct irq_desc *desc)
-{
-       u32 bank, pint_val;
-       u32 request, irq;
-       u32 level_mask;
-       int umask = 0;
-       struct irq_chip *chip = irq_desc_get_chip(desc);
-
-       if (chip->irq_mask_ack) {
-               chip->irq_mask_ack(&desc->irq_data);
-       } else {
-               chip->irq_mask(&desc->irq_data);
-               if (chip->irq_ack)
-                       chip->irq_ack(&desc->irq_data);
-       }
-
-       switch (inta_irq) {
-       case IRQ_PINT0:
-               bank = 0;
-               break;
-       case IRQ_PINT2:
-               bank = 2;
-               break;
-       case IRQ_PINT3:
-               bank = 3;
-               break;
-       case IRQ_PINT1:
-               bank = 1;
-               break;
-#ifdef CONFIG_BF60x
-       case IRQ_PINT4:
-               bank = 4;
-               break;
-       case IRQ_PINT5:
-               bank = 5;
-               break;
 #endif
-       default:
-               return;
-       }
-
-       pint_val = bank * NR_PINT_BITS;
-
-       request = pint[bank]->request;
-
-       level_mask = pint[bank]->edge_set & request;
-
-       while (request) {
-               if (request & 1) {
-                       irq = pint2irq_lut[pint_val] + SYS_IRQS;
-                       if (level_mask & PINT_BIT(pint_val)) {
-                               umask = 1;
-                               chip->irq_unmask(&desc->irq_data);
-                       }
-                       bfin_handle_irq(irq);
-               }
-               pint_val++;
-               request >>= 1;
-       }
 
-       if (!umask)
-               chip->irq_unmask(&desc->irq_data);
-}
 #endif
 
-static struct irq_chip bfin_gpio_irqchip = {
-       .name = "GPIO",
-       .irq_ack = bfin_gpio_ack_irq,
-       .irq_mask = bfin_gpio_mask_irq,
-       .irq_mask_ack = bfin_gpio_mask_ack_irq,
-       .irq_unmask = bfin_gpio_unmask_irq,
-       .irq_disable = bfin_gpio_mask_irq,
-       .irq_enable = bfin_gpio_unmask_irq,
-       .irq_set_type = bfin_gpio_irq_type,
-       .irq_startup = bfin_gpio_irq_startup,
-       .irq_shutdown = bfin_gpio_irq_shutdown,
-       .irq_set_wake = bfin_gpio_set_wake,
-};
-
 void init_exception_vectors(void)
 {
        /* cannot program in software:
@@ -1331,17 +1000,6 @@ int __init init_arch_irq(void)
 
        local_irq_disable();
 
-#if BFIN_GPIO_PINT
-# ifdef CONFIG_PINTx_REASSIGN
-       pint[0]->assign = CONFIG_PINT0_ASSIGN;
-       pint[1]->assign = CONFIG_PINT1_ASSIGN;
-       pint[2]->assign = CONFIG_PINT2_ASSIGN;
-       pint[3]->assign = CONFIG_PINT3_ASSIGN;
-# endif
-       /* Whenever PINTx_ASSIGN is altered init_pint_lut() must be executed! */
-       init_pint_lut();
-#endif
-
        for (irq = 0; irq <= SYS_IRQS; irq++) {
                if (irq <= IRQ_CORETMR)
                        irq_set_chip(irq, &bfin_core_irqchip);
@@ -1349,12 +1007,8 @@ int __init init_arch_irq(void)
                        irq_set_chip(irq, &bfin_internal_irqchip);
 
                switch (irq) {
-#if BFIN_GPIO_PINT
-               case IRQ_PINT0:
-               case IRQ_PINT1:
-               case IRQ_PINT2:
-               case IRQ_PINT3:
-#elif defined(BF537_FAMILY)
+#if !BFIN_GPIO_PINT
+#if defined(BF537_FAMILY)
                case IRQ_PH_INTA_MAC_RX:
                case IRQ_PF_INTA_PG_INTA:
 #elif defined(BF533_FAMILY)
@@ -1372,6 +1026,7 @@ int __init init_arch_irq(void)
 #endif
                        irq_set_chained_handler(irq, bfin_demux_gpio_irq);
                        break;
+#endif
 #if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)
                case IRQ_MAC_ERROR:
                        irq_set_chained_handler(irq,
@@ -1419,10 +1074,12 @@ int __init init_arch_irq(void)
                                         handle_level_irq);
 #endif
        /* if configured as edge, then will be changed to do_edge_IRQ */
+#ifdef CONFIG_GPIO_ADI
        for (irq = GPIO_IRQ_BASE;
                irq < (GPIO_IRQ_BASE + MAX_BLACKFIN_GPIOS); irq++)
                irq_set_chip_and_handler(irq, &bfin_gpio_irqchip,
                                         handle_level_irq);
+#endif
        bfin_write_IMASK(0);
        CSYNC();
        ilat = bfin_read_ILAT();
@@ -1525,19 +1182,6 @@ int __init init_arch_irq(void)
 
        local_irq_disable();
 
-#if BFIN_GPIO_PINT
-# ifdef CONFIG_PINTx_REASSIGN
-       pint[0]->assign = CONFIG_PINT0_ASSIGN;
-       pint[1]->assign = CONFIG_PINT1_ASSIGN;
-       pint[2]->assign = CONFIG_PINT2_ASSIGN;
-       pint[3]->assign = CONFIG_PINT3_ASSIGN;
-       pint[4]->assign = CONFIG_PINT4_ASSIGN;
-       pint[5]->assign = CONFIG_PINT5_ASSIGN;
-# endif
-       /* Whenever PINTx_ASSIGN is altered init_pint_lut() must be executed! */
-       init_pint_lut();
-#endif
-
        for (irq = 0; irq <= SYS_IRQS; irq++) {
                if (irq <= IRQ_CORETMR) {
                        irq_set_chip_and_handler(irq, &bfin_core_irqchip,
@@ -1546,9 +1190,6 @@ int __init init_arch_irq(void)
                        if (irq == IRQ_CORETMR)
                                irq_set_handler(irq, handle_percpu_irq);
 #endif
-               } else if (irq >= BFIN_IRQ(21) && irq <= BFIN_IRQ(26)) {
-                       irq_set_chip(irq, &bfin_sec_irqchip);
-                       irq_set_chained_handler(irq, bfin_demux_gpio_irq);
                } else if (irq >= BFIN_IRQ(34) && irq <= BFIN_IRQ(37)) {
                        irq_set_chip_and_handler(irq, &bfin_sec_irqchip,
                                handle_percpu_irq);
@@ -1563,10 +1204,6 @@ int __init init_arch_irq(void)
                        __irq_set_preflow_handler(irq, bfin_sec_preflow_handler);
                }
        }
-       for (irq = GPIO_IRQ_BASE;
-               irq < (GPIO_IRQ_BASE + MAX_BLACKFIN_GPIOS); irq++)
-               irq_set_chip_and_handler(irq, &bfin_gpio_irqchip,
-                                       handle_level_irq);
 
        bfin_write_IMASK(0);
        CSYNC();
index 87bfe54..1387a94 100644 (file)
@@ -27,7 +27,7 @@ struct bfin_cpu_pm_fns *bfin_cpu_pm;
 
 void bfin_pm_suspend_standby_enter(void)
 {
-#ifndef CONFIG_BF60x
+#if !BFIN_GPIO_PINT
        bfin_pm_standby_setup();
 #endif
 
@@ -41,7 +41,7 @@ void bfin_pm_suspend_standby_enter(void)
 # endif
 #endif
 
-#ifndef CONFIG_BF60x
+#if !BFIN_GPIO_PINT
        bfin_pm_standby_restore();
 #endif
 
@@ -128,6 +128,7 @@ static void flushinv_all_dcache(void)
                                        if ((status & 0x3) != 0x3)
                                                continue;
 
+
                                        /* construct the address using the tag */
                                        addr = (status & 0xFFFFC800) | (subbank << 12) | (set << 5);
 
@@ -140,11 +141,14 @@ static void flushinv_all_dcache(void)
 
 int bfin_pm_suspend_mem_enter(void)
 {
-       int wakeup, ret;
+       int ret;
+#ifndef CONFIG_BF60x
+       int wakeup;
+#endif
 
        unsigned char *memptr = kmalloc(L1_CODE_LENGTH + L1_DATA_A_LENGTH
                                         + L1_DATA_B_LENGTH + L1_SCRATCH_LENGTH,
-                                         GFP_KERNEL);
+                                         GFP_ATOMIC);
 
        if (memptr == NULL) {
                panic("bf53x_suspend_l1_mem malloc failed");
@@ -170,10 +174,8 @@ int bfin_pm_suspend_mem_enter(void)
                return ret;
        }
 
+#ifdef CONFIG_GPIO_ADI
        bfin_gpio_pm_hibernate_suspend();
-
-#if BFIN_GPIO_PINT
-       bfin_pint_suspend();
 #endif
 
 #if defined(CONFIG_BFIN_EXTMEM_WRITEBACK) || defined(CONFIG_BFIN_L2_WRITEBACK)
@@ -194,11 +196,9 @@ int bfin_pm_suspend_mem_enter(void)
        _enable_icplb();
        _enable_dcplb();
 
-#if BFIN_GPIO_PINT
-       bfin_pint_resume();
-#endif
-
+#ifdef CONFIG_GPIO_ADI
        bfin_gpio_pm_hibernate_restore();
+#endif
        blackfin_dma_resume();
 
        kfree(memptr);
index 82f301c..2bbae07 100644 (file)
@@ -146,6 +146,7 @@ static irqreturn_t ipi_handler_int1(int irq, void *dev_instance)
 
        platform_clear_ipi(cpu, IRQ_SUPPLE_1);
 
+       smp_rmb();
        bfin_ipi_data = &__get_cpu_var(bfin_ipi);
        while ((pending = atomic_xchg(&bfin_ipi_data->bits, 0)) != 0) {
                msg = 0;
@@ -161,18 +162,20 @@ static irqreturn_t ipi_handler_int1(int irq, void *dev_instance)
                        case BFIN_IPI_CALL_FUNC:
                                generic_smp_call_function_interrupt();
                                break;
-
                        case BFIN_IPI_CALL_FUNC_SINGLE:
                                generic_smp_call_function_single_interrupt();
                                break;
-
                        case BFIN_IPI_CPU_STOP:
                                ipi_cpu_stop(cpu);
                                break;
+                       default:
+                               goto out;
                        }
                        atomic_dec(&bfin_ipi_data->count);
                } while (msg < BITS_PER_LONG);
+
        }
+out:
        return IRQ_HANDLED;
 }
 
@@ -198,10 +201,11 @@ void send_ipi(const struct cpumask *cpumask, enum ipi_message_type msg)
                bfin_ipi_data = &per_cpu(bfin_ipi, cpu);
                atomic_set_mask((1 << msg), &bfin_ipi_data->bits);
                atomic_inc(&bfin_ipi_data->count);
-               platform_send_ipi_cpu(cpu, IRQ_SUPPLE_1);
        }
-
        local_irq_restore(flags);
+       smp_wmb();
+       for_each_cpu(cpu, cpumask)
+               platform_send_ipi_cpu(cpu, IRQ_SUPPLE_1);
 }
 
 void arch_send_call_function_single_ipi(int cpu)
index 957dd00..77ea09b 100644 (file)
@@ -36,9 +36,6 @@ config GENERIC_HWEIGHT
 config GENERIC_BUG
        def_bool y
 
-config COMMON_CLKDEV
-       def_bool y
-
 config C6X_BIG_KERNEL
        bool "Build a big kernel"
        help
@@ -105,10 +102,6 @@ menu "Processor type and features"
 
 source "arch/c6x/platforms/Kconfig"
 
-config TMS320C6X_CACHES_ON
-       bool "L2 cache support"
-       default y
-
 config KERNEL_RAM_BASE_ADDRESS
        hex "Virtual address of memory base"
        default 0xe0000000 if SOC_TMS320C6455
index 4c8dc56..d4e9ef8 100644 (file)
@@ -84,8 +84,6 @@ struct thread_info *current_thread_info(void)
 #define put_thread_info(ti)    put_task_struct((ti)->task)
 #endif /* __ASSEMBLY__ */
 
-#define        PREEMPT_ACTIVE  0x10000000
-
 /*
  * thread information flag bit numbers
  * - pending work-to-be-done flags are in LSW
index 17bb12d..04126f7 100644 (file)
@@ -2,18 +2,6 @@
 #define __ASM_HARDIRQ_H
 
 #include <asm/irq.h>
-
-#define HARDIRQ_BITS   8
-
-/*
- * The hardirq mask has to be large enough to have
- * space for potentially all IRQ sources in the system
- * nesting on a single CPU:
- */
-#if (1 << HARDIRQ_BITS) < NR_IRQS
-# error HARDIRQ_BITS is too low!
-#endif
-
 #include <asm-generic/hardirq.h>
 
 #endif /* __ASM_HARDIRQ_H */
index 6da975d..235ece4 100644 (file)
@@ -32,7 +32,12 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long addres
 {
        struct page *pte;
        pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0);
-       pgtable_page_ctor(pte);
+       if (!pte)
+               return NULL;
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
+       }
        return pte;
 }
 
index 07c8c40..55dede1 100644 (file)
@@ -44,8 +44,6 @@ struct thread_info {
 
 #endif
 
-#define PREEMPT_ACTIVE         0x10000000
-
 /*
  * macros/functions for gaining access to the thread information structure
  */
index bebd7ea..af29e17 100644 (file)
@@ -52,8 +52,6 @@ struct thread_info {
 
 #endif
 
-#define PREEMPT_ACTIVE         0x10000000
-
 /*
  * macros/functions for gaining access to the thread information structure
  */
index f6084bc..41907d2 100644 (file)
@@ -37,11 +37,15 @@ pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address)
 #else
        page = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0);
 #endif
-       if (page) {
-               clear_highpage(page);
-               pgtable_page_ctor(page);
-               flush_dcache_page(page);
+       if (!page)
+               return NULL;
+
+       clear_highpage(page);
+       if (!pgtable_page_ctor(page)) {
+               __free_page(page);
+               return NULL;
        }
+       flush_dcache_page(page);
        return page;
 }
 
index 99041b0..09df260 100644 (file)
@@ -4,7 +4,6 @@ comment "Linux Kernel Configuration for Hexagon"
 config HEXAGON
        def_bool y
        select HAVE_OPROFILE
-       select USE_GENERIC_SMP_HELPERS if SMP
        # Other pending projects/to-do items.
        # select HAVE_REGS_AND_STACK_ACCESS_API
        # select HAVE_HW_BREAKPOINT if PERF_EVENTS
index 679bf6d..4c9d382 100644 (file)
@@ -65,10 +65,12 @@ static inline struct page *pte_alloc_one(struct mm_struct *mm,
        struct page *pte;
 
        pte = alloc_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO);
-
-       if (pte)
-               pgtable_page_ctor(pte);
-
+       if (!pte)
+               return NULL;
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
+       }
        return pte;
 }
 
index f7c3240..a59dad3 100644 (file)
@@ -73,10 +73,6 @@ struct thread_info {
 
 #endif  /* __ASSEMBLY__  */
 
-/*  looks like "linux/hardirq.h" uses this.  */
-
-#define PREEMPT_ACTIVE         0x10000000
-
 #ifndef __ASSEMBLY__
 
 #define INIT_THREAD_INFO(tsk)                   \
index 7740ab1..4e4119b 100644 (file)
@@ -6,6 +6,7 @@ menu "Processor type and features"
 
 config IA64
        bool
+       select ARCH_MIGHT_HAVE_PC_PARPORT
        select PCI if (!IA64_HP_SIM)
        select ACPI if (!IA64_HP_SIM)
        select PM if (!IA64_HP_SIM)
@@ -343,7 +344,6 @@ config FORCE_MAX_ZONEORDER
 
 config SMP
        bool "Symmetric multi-processing support"
-       select USE_GENERIC_SMP_HELPERS
        help
          This enables support for systems with more than one CPU. If you have
          a system with only one CPU, say N.  If you have a system with more
index d43daf1..4c530a8 100644 (file)
@@ -1992,7 +1992,7 @@ sba_connect_bus(struct pci_bus *bus)
        if (PCI_CONTROLLER(bus)->iommu)
                return;
 
-       handle = PCI_CONTROLLER(bus)->acpi_handle;
+       handle = acpi_device_handle(PCI_CONTROLLER(bus)->companion);
        if (!handle)
                return;
 
index 989dd3f..db95f57 100644 (file)
@@ -234,10 +234,6 @@ struct kvm_vm_data {
 #define KVM_REQ_PTC_G          32
 #define KVM_REQ_RESUME         33
 
-#define KVM_HPAGE_GFN_SHIFT(x) 0
-#define KVM_NR_PAGE_SIZES      1
-#define KVM_PAGES_PER_HPAGE(x) 1
-
 struct kvm;
 struct kvm_vcpu;
 
@@ -480,7 +476,7 @@ struct kvm_arch {
 
        struct list_head assigned_dev_head;
        struct iommu_domain *iommu_domain;
-       int iommu_flags;
+       bool iommu_noncoherent;
 
        unsigned long irq_sources_bitmap;
        unsigned long irq_states[KVM_IOAPIC_NUM_PINS];
index 80775f5..71fbaaa 100644 (file)
@@ -95,7 +95,7 @@ struct iospace_resource {
 };
 
 struct pci_controller {
-       void *acpi_handle;
+       struct acpi_device *companion;
        void *iommu;
        int segment;
        int node;               /* nearest node with memory or -1 for global allocation */
index 96a8d92..5767cdf 100644 (file)
@@ -91,7 +91,10 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long addr)
        if (!pg)
                return NULL;
        page = virt_to_page(pg);
-       pgtable_page_ctor(page);
+       if (!pgtable_page_ctor(page)) {
+               quicklist_free(0, NULL, pg);
+               return NULL;
+       }
        return page;
 }
 
index cade13d..5957cf6 100644 (file)
@@ -11,9 +11,6 @@
 #include <asm/processor.h>
 #include <asm/ptrace.h>
 
-#define PREEMPT_ACTIVE_BIT 30
-#define PREEMPT_ACTIVE (1 << PREEMPT_ACTIVE_BIT)
-
 #ifndef __ASSEMBLY__
 
 /*
diff --git a/arch/ia64/include/asm/xen/page-coherent.h b/arch/ia64/include/asm/xen/page-coherent.h
new file mode 100644 (file)
index 0000000..96e42f9
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _ASM_IA64_XEN_PAGE_COHERENT_H
+#define _ASM_IA64_XEN_PAGE_COHERENT_H
+
+#include <asm/page.h>
+#include <linux/dma-attrs.h>
+#include <linux/dma-mapping.h>
+
+static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size,
+               dma_addr_t *dma_handle, gfp_t flags,
+               struct dma_attrs *attrs)
+{
+       void *vstart = (void*)__get_free_pages(flags, get_order(size));
+       *dma_handle = virt_to_phys(vstart);
+       return vstart;
+}
+
+static inline void xen_free_coherent_pages(struct device *hwdev, size_t size,
+               void *cpu_addr, dma_addr_t dma_handle,
+               struct dma_attrs *attrs)
+{
+       free_pages((unsigned long) cpu_addr, get_order(size));
+}
+
+static inline void xen_dma_map_page(struct device *hwdev, struct page *page,
+            unsigned long offset, size_t size, enum dma_data_direction dir,
+            struct dma_attrs *attrs) { }
+
+static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
+               size_t size, enum dma_data_direction dir,
+               struct dma_attrs *attrs) { }
+
+static inline void xen_dma_sync_single_for_cpu(struct device *hwdev,
+               dma_addr_t handle, size_t size, enum dma_data_direction dir) { }
+
+static inline void xen_dma_sync_single_for_device(struct device *hwdev,
+               dma_addr_t handle, size_t size, enum dma_data_direction dir) { }
+
+#endif /* _ASM_IA64_XEN_PAGE_COHERENT_H */
index 7a53530..ddea607 100644 (file)
@@ -1169,21 +1169,8 @@ skip_rbs_switch:
 .work_pending:
        tbit.z p6,p0=r31,TIF_NEED_RESCHED       // is resched not needed?
 (p6)   br.cond.sptk.few .notify
-#ifdef CONFIG_PREEMPT
-(pKStk) dep r21=-1,r0,PREEMPT_ACTIVE_BIT,1
-       ;;
-(pKStk) st4 [r20]=r21
-#endif
-       SSM_PSR_I(p0, p6, r2)   // enable interrupts
-       br.call.spnt.many rp=schedule
+       br.call.spnt.many rp=preempt_schedule_irq
 .ret9: cmp.eq p6,p0=r0,r0      // p6 <- 1 (re-check)
-       RSM_PSR_I(p0, r2, r20)  // disable interrupts
-       ;;
-#ifdef CONFIG_PREEMPT
-(pKStk)        adds r20=TI_PRE_COUNT+IA64_TASK_SIZE,r13
-       ;;
-(pKStk)        st4 [r20]=r0            // preempt_count() <- 0
-#endif
 (pLvSys)br.cond.sptk.few  __paravirt_pending_syscall_end
        br.cond.sptk.many .work_processed_kernel
 
index f8280a7..074fde4 100644 (file)
@@ -947,7 +947,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
        case KPROBE_HIT_SSDONE:
                /*
                 * We increment the nmissed count for accounting,
-                * we can also use npre/npostfault count for accouting
+                * we can also use npre/npostfault count for accounting
                 * these specific fault cases.
                 */
                kprobes_inc_nmissed_count(cur);
index 5a9ff1c..cb59277 100644 (file)
@@ -2166,12 +2166,6 @@ static const struct file_operations pfm_file_ops = {
        .flush          = pfm_flush
 };
 
-static int
-pfmfs_delete_dentry(const struct dentry *dentry)
-{
-       return 1;
-}
-
 static char *pfmfs_dname(struct dentry *dentry, char *buffer, int buflen)
 {
        return dynamic_dname(dentry, buffer, buflen, "pfm:[%lu]",
@@ -2179,7 +2173,7 @@ static char *pfmfs_dname(struct dentry *dentry, char *buffer, int buflen)
 }
 
 static const struct dentry_operations pfmfs_dentry_operations = {
-       .d_delete = pfmfs_delete_dentry,
+       .d_delete = always_delete_dentry,
        .d_dname = pfmfs_dname,
 };
 
index bdfd878..985bf80 100644 (file)
@@ -1550,12 +1550,13 @@ int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
        return VM_FAULT_SIGBUS;
 }
 
-void kvm_arch_free_memslot(struct kvm_memory_slot *free,
+void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
                           struct kvm_memory_slot *dont)
 {
 }
 
-int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
+int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
+                           unsigned long npages)
 {
        return 0;
 }
index 2326790..9e4938d 100644 (file)
@@ -436,9 +436,9 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
        if (!controller)
                return NULL;
 
-       controller->acpi_handle = device->handle;
+       controller->companion = device;
 
-       pxm = acpi_get_pxm(controller->acpi_handle);
+       pxm = acpi_get_pxm(device->handle);
 #ifdef CONFIG_NUMA
        if (pxm >= 0)
                controller->node = pxm_to_node(pxm);
@@ -489,7 +489,7 @@ int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
 {
        struct pci_controller *controller = bridge->bus->sysdata;
 
-       ACPI_HANDLE_SET(&bridge->dev, controller->acpi_handle);
+       ACPI_COMPANION_SET(&bridge->dev, controller->companion);
        return 0;
 }
 
index b172539..0640739 100644 (file)
@@ -132,7 +132,7 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
        struct acpi_resource_vendor_typed *vendor;
 
 
-       handle = PCI_CONTROLLER(bus)->acpi_handle;
+       handle = acpi_device_handle(PCI_CONTROLLER(bus)->companion);
        status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS,
                                          &sn_uuid, &buffer);
        if (ACPI_FAILURE(status)) {
@@ -360,7 +360,7 @@ sn_acpi_get_pcidev_info(struct pci_dev *dev, struct pcidev_info **pcidev_info,
        acpi_status status;
        struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 
-       rootbus_handle = PCI_CONTROLLER(dev)->acpi_handle;
+       rootbus_handle = acpi_device_handle(PCI_CONTROLLER(dev)->companion);
         status = acpi_evaluate_integer(rootbus_handle, METHOD_NAME__SEG, NULL,
                                        &segment);
         if (ACPI_SUCCESS(status)) {
index 75661fb..09ef94a 100644 (file)
@@ -275,7 +275,6 @@ source "kernel/Kconfig.preempt"
 
 config SMP
        bool "Symmetric multi-processing support"
-       select USE_GENERIC_SMP_HELPERS
        ---help---
          This enables support for systems with more than one CPU. If you have
          a system with only one CPU, like most personal computers, say N. If
index 4c31c0a..5f2ac4f 100644 (file)
@@ -3,22 +3,6 @@
 #define __ASM_HARDIRQ_H
 
 #include <asm/irq.h>
-
-#if NR_IRQS > 256
-#define HARDIRQ_BITS   9
-#else
-#define HARDIRQ_BITS   8
-#endif
-
-/*
- * The hardirq mask has to be large enough to have
- * space for potentially all IRQ sources in the system
- * nesting on a single CPU:
- */
-#if (1 << HARDIRQ_BITS) < NR_IRQS
-# error HARDIRQ_BITS is too low!
-#endif
-
 #include <asm-generic/hardirq.h>
 
 #endif /* __ASM_HARDIRQ_H */
index a979a41..9fc78fc 100644 (file)
@@ -45,7 +45,7 @@ static inline void get_new_mmu_context(struct mm_struct *mm)
                   Flush all TLB and start new cycle. */
                local_flush_tlb_all();
                /* Fix version if needed.
-                  Note that we avoid version #0 to distingush NO_CONTEXT. */
+                  Note that we avoid version #0 to distinguish NO_CONTEXT. */
                if (!mc)
                        mmu_context_cache = mc = MMU_CONTEXT_FIRST_VERSION;
        }
index 0fc7361..2d55a06 100644 (file)
@@ -43,7 +43,12 @@ static __inline__ pgtable_t pte_alloc_one(struct mm_struct *mm,
 {
        struct page *pte = alloc_page(GFP_KERNEL|__GFP_ZERO);
 
-       pgtable_page_ctor(pte);
+       if (!pte)
+               return NULL;
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
+       }
        return pte;
 }
 
index c074f4c..0017170 100644 (file)
@@ -53,8 +53,6 @@ struct thread_info {
 
 #endif
 
-#define PREEMPT_ACTIVE         0x10000000
-
 #define THREAD_SIZE            (PAGE_SIZE << 1)
 #define THREAD_SIZE_ORDER      1
 /*
index 0c01543..7c3db99 100644 (file)
@@ -182,13 +182,7 @@ need_resched:
        ld      r4, PSW(sp)             ; interrupts off (exception path) ?
        and3    r4, r4, #0x4000
        beqz    r4, restore_all
-       LDIMM   (r4, PREEMPT_ACTIVE)
-       st      r4, @(TI_PRE_COUNT, r8)
-       ENABLE_INTERRUPTS(r4)
-       bl      schedule
-       ldi     r4, #0
-       st      r4, @(TI_PRE_COUNT, r8)
-       DISABLE_INTERRUPTS(r4)
+       bl      preempt_schedule_irq
        bra     need_resched
 #endif
 
index 311a300..75f25a8 100644 (file)
@@ -1,6 +1,7 @@
 config M68K
        bool
        default y
+       select ARCH_MIGHT_HAVE_PC_PARPORT if ISA
        select HAVE_IDE
        select HAVE_AOUT if MMU
        select HAVE_DEBUG_BUGVERBOSE
index db30ed2..6c61852 100644 (file)
@@ -5,17 +5,6 @@
 #include <linux/cache.h>
 #include <asm/irq.h>
 
-#define HARDIRQ_BITS   8
-
-/*
- * The hardirq mask has to be large enough to have
- * space for potentially all IRQ sources in the system
- * nesting on a single CPU:
- */
-#if (1 << HARDIRQ_BITS) < NR_IRQS
-# error HARDIRQ_BITS is too low!
-#endif
-
 #ifdef CONFIG_MMU
 
 static inline void ack_bad_irq(unsigned int irq)
index 313f3dd..f9924fb 100644 (file)
@@ -56,6 +56,10 @@ static inline struct page *pte_alloc_one(struct mm_struct *mm,
 
        if (!page)
                return NULL;
+       if (!pgtable_page_ctor(page)) {
+               __free_page(page);
+               return NULL;
+       }
 
        pte = kmap(page);
        if (pte) {
index 2f02f26..24bcba4 100644 (file)
@@ -29,18 +29,22 @@ static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 
 static inline pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address)
 {
-       struct page *page = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0);
+       struct page *page;
        pte_t *pte;
 
+       page = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0);
        if(!page)
                return NULL;
+       if (!pgtable_page_ctor(page)) {
+               __free_page(page);
+               return NULL;
+       }
 
        pte = kmap(page);
        __flush_page_to_ram(pte);
        flush_tlb_kernel_page(pte);
        nocache_page(pte);
        kunmap(page);
-       pgtable_page_ctor(page);
        return page;
 }
 
index 48d80d5..f868506 100644 (file)
@@ -59,7 +59,10 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
                return NULL;
 
        clear_highpage(page);
-       pgtable_page_ctor(page);
+       if (!pgtable_page_ctor(page)) {
+               __free_page(page);
+               return NULL;
+       }
        return page;
 
 }
index 126131f..21a4784 100644 (file)
@@ -35,8 +35,6 @@ struct thread_info {
 };
 #endif /* __ASSEMBLY__ */
 
-#define PREEMPT_ACTIVE         0x4000000
-
 #define INIT_THREAD_INFO(tsk)                  \
 {                                              \
        .task           = &tsk,                 \
index a78f564..b54ac7a 100644 (file)
@@ -45,7 +45,7 @@
 .globl system_call, buserr, trap, resume
 .globl sys_call_table
 .globl __sys_fork, __sys_clone, __sys_vfork
-.globl ret_from_interrupt, bad_interrupt
+.globl bad_interrupt
 .globl auto_irqhandler_fixup
 .globl user_irqvec_fixup
 
@@ -275,8 +275,6 @@ do_delayed_trace:
 ENTRY(auto_inthandler)
        SAVE_ALL_INT
        GET_CURRENT(%d0)
-       movel   %d0,%a1
-       addqb   #1,%a1@(TINFO_PREEMPT+1)
                                        |  put exception # in d0
        bfextu  %sp@(PT_OFF_FORMATVEC){#4,#10},%d0
        subw    #VEC_SPUR,%d0
@@ -286,32 +284,13 @@ ENTRY(auto_inthandler)
 auto_irqhandler_fixup = . + 2
        jsr     do_IRQ                  |  process the IRQ
        addql   #8,%sp                  |  pop parameters off stack
-
-ret_from_interrupt:
-       movel   %curptr@(TASK_STACK),%a1
-       subqb   #1,%a1@(TINFO_PREEMPT+1)
-       jeq     ret_from_last_interrupt
-2:     RESTORE_ALL
-
-       ALIGN
-ret_from_last_interrupt:
-       moveq   #(~ALLOWINT>>8)&0xff,%d0
-       andb    %sp@(PT_OFF_SR),%d0
-       jne     2b
-
-       /* check if we need to do software interrupts */
-       tstl    irq_stat+CPUSTAT_SOFTIRQ_PENDING
-       jeq     .Lret_from_exception
-       pea     ret_from_exception
-       jra     do_softirq
+       jra     ret_from_exception
 
 /* Handler for user defined interrupt vectors */
 
 ENTRY(user_inthandler)
        SAVE_ALL_INT
        GET_CURRENT(%d0)
-       movel   %d0,%a1
-       addqb   #1,%a1@(TINFO_PREEMPT+1)
                                        |  put exception # in d0
        bfextu  %sp@(PT_OFF_FORMATVEC){#4,#10},%d0
 user_irqvec_fixup = . + 2
@@ -321,29 +300,18 @@ user_irqvec_fixup = . + 2
        movel   %d0,%sp@-               |  put vector # on stack
        jsr     do_IRQ                  |  process the IRQ
        addql   #8,%sp                  |  pop parameters off stack
-
-       movel   %curptr@(TASK_STACK),%a1
-       subqb   #1,%a1@(TINFO_PREEMPT+1)
-       jeq     ret_from_last_interrupt
-       RESTORE_ALL
+       jra     ret_from_exception
 
 /* Handler for uninitialized and spurious interrupts */
 
 ENTRY(bad_inthandler)
        SAVE_ALL_INT
        GET_CURRENT(%d0)
-       movel   %d0,%a1
-       addqb   #1,%a1@(TINFO_PREEMPT+1)
 
        movel   %sp,%sp@-
        jsr     handle_badint
        addql   #4,%sp
-
-       movel   %curptr@(TASK_STACK),%a1
-       subqb   #1,%a1@(TINFO_PREEMPT+1)
-       jeq     ret_from_last_interrupt
-       RESTORE_ALL
-
+       jra     ret_from_exception
 
 resume:
        /*
index 4d7da38..077d3a7 100644 (file)
@@ -58,12 +58,6 @@ void __init init_IRQ(void)
 {
        int i;
 
-       /* assembly irq entry code relies on this... */
-       if (HARDIRQ_MASK != 0x00ff0000) {
-               extern void hardirq_mask_is_broken(void);
-               hardirq_mask_is_broken();
-       }
-
        for (i = IRQ_AUTO_1; i <= IRQ_AUTO_7; i++)
                irq_set_chip_and_handler(i, &auto_irq_chip, handle_simple_irq);
 
index 7f91c2f..23ac054 100644 (file)
@@ -27,7 +27,6 @@
 .globl ret_from_exception
 .globl ret_from_signal
 .globl sys_call_table
-.globl ret_from_interrupt
 .globl bad_interrupt
 .globl inthandler1
 .globl inthandler2
@@ -137,7 +136,7 @@ inthandler1:
        movel   #65,%sp@-               /*  put vector # on stack*/
        jbsr    process_int             /*  process the IRQ*/
 3:             addql   #8,%sp                  /*  pop parameters off stack*/
-       bra     ret_from_interrupt
+       bra     ret_from_exception
 
 inthandler2:
        SAVE_ALL_INT
@@ -148,7 +147,7 @@ inthandler2:
        movel   #66,%sp@-               /*  put vector # on stack*/
        jbsr    process_int             /*  process the IRQ*/
 3:             addql   #8,%sp                  /*  pop parameters off stack*/
-       bra     ret_from_interrupt
+       bra     ret_from_exception
 
 inthandler3:
        SAVE_ALL_INT
@@ -159,7 +158,7 @@ inthandler3:
        movel   #67,%sp@-               /*  put vector # on stack*/
        jbsr    process_int             /*  process the IRQ*/
 3:             addql   #8,%sp                  /*  pop parameters off stack*/
-       bra     ret_from_interrupt
+       bra     ret_from_exception
 
 inthandler4:
        SAVE_ALL_INT
@@ -170,7 +169,7 @@ inthandler4:
        movel   #68,%sp@-               /*  put vector # on stack*/
        jbsr    process_int             /*  process the IRQ*/
 3:             addql   #8,%sp                  /*  pop parameters off stack*/
-       bra     ret_from_interrupt
+       bra     ret_from_exception
 
 inthandler5:
        SAVE_ALL_INT
@@ -181,7 +180,7 @@ inthandler5:
        movel   #69,%sp@-               /*  put vector # on stack*/
        jbsr    process_int             /*  process the IRQ*/
 3:             addql   #8,%sp                  /*  pop parameters off stack*/
-       bra     ret_from_interrupt
+       bra     ret_from_exception
 
 inthandler6:
        SAVE_ALL_INT
@@ -192,7 +191,7 @@ inthandler6:
        movel   #70,%sp@-               /*  put vector # on stack*/
        jbsr    process_int             /*  process the IRQ*/
 3:             addql   #8,%sp                  /*  pop parameters off stack*/
-       bra     ret_from_interrupt
+       bra     ret_from_exception
 
 inthandler7:
        SAVE_ALL_INT
@@ -203,7 +202,7 @@ inthandler7:
        movel   #71,%sp@-               /*  put vector # on stack*/
        jbsr    process_int             /*  process the IRQ*/
 3:             addql   #8,%sp                  /*  pop parameters off stack*/
-       bra     ret_from_interrupt
+       bra     ret_from_exception
 
 inthandler:
        SAVE_ALL_INT
@@ -214,23 +213,7 @@ inthandler:
        movel   %d0,%sp@-               /*  put vector # on stack*/
        jbsr    process_int             /*  process the IRQ*/
 3:             addql   #8,%sp                  /*  pop parameters off stack*/
-       bra     ret_from_interrupt
-
-ret_from_interrupt:
-       jeq     1f
-2:
-       RESTORE_ALL
-1:
-       moveb   %sp@(PT_OFF_SR), %d0
-       and     #7, %d0
-       jhi     2b
-
-       /* check if we need to do software interrupts */
-       jeq     ret_from_exception
-
-       pea     ret_from_exception
-       jra     do_softirq
-
+       bra     ret_from_exception
 
 /*
  * Handler for uninitialized and spurious interrupts.
index 904fd9a..447c33e 100644 (file)
@@ -29,7 +29,6 @@
 .globl ret_from_exception
 .globl ret_from_signal
 .globl sys_call_table
-.globl ret_from_interrupt
 .globl bad_interrupt
 .globl inthandler
 
@@ -132,26 +131,9 @@ inthandler:
 
        movel   %sp,%sp@-
        movel   %d0,%sp@-               /*  put vector # on stack*/
-       jbsr    do_IRQ                  /*  process the IRQ*/
-3:             addql   #8,%sp                  /*  pop parameters off stack*/
-       bra     ret_from_interrupt
-
-ret_from_interrupt:
-       jeq     1f
-2:
-       RESTORE_ALL
-1:
-       moveb   %sp@(PT_OFF_SR), %d0
-       and     #7, %d0
-       jhi     2b
-       /* check if we need to do software interrupts */
-
-       movel   irq_stat+CPUSTAT_SOFTIRQ_PENDING,%d0
-       jeq     ret_from_exception
-
-       pea     ret_from_exception
-       jra     do_softirq
-
+       jbsr    do_IRQ                  /*  process the IRQ */
+       addql   #8,%sp                  /*  pop parameters off stack*/
+       jra     ret_from_exception
 
 /*
  * Handler for uninitialized and spurious interrupts.
index 36368eb..e56abd2 100644 (file)
@@ -111,7 +111,6 @@ config METAG_META21
 config SMP
        bool "Symmetric multi-processing support"
        depends on METAG_META21 && METAG_META21_MMU
-       select USE_GENERIC_SMP_HELPERS
        help
          This enables support for systems with more than one thread running
          Linux. If you have a system with only one thread running Linux,
index 275d928..3104df0 100644 (file)
@@ -52,8 +52,12 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
 {
        struct page *pte;
        pte = alloc_pages(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO, 0);
-       if (pte)
-               pgtable_page_ctor(pte);
+       if (!pte)
+               return NULL;
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
+       }
        return pte;
 }
 
index 7c4a330..b19e9c5 100644 (file)
@@ -46,8 +46,6 @@ struct thread_info {
 
 #endif
 
-#define PREEMPT_ACTIVE         0x10000000
-
 #ifdef CONFIG_4KSTACKS
 #define THREAD_SHIFT           12
 #else
index 655e1ca..e23cccd 100644 (file)
@@ -1,5 +1,6 @@
 config MICROBLAZE
        def_bool y
+       select ARCH_MIGHT_HAVE_PC_PARPORT
        select HAVE_MEMBLOCK
        select HAVE_MEMBLOCK_NODE_MAP
        select HAVE_FUNCTION_TRACER
index ebd3579..7fdf7fa 100644 (file)
@@ -122,8 +122,13 @@ static inline struct page *pte_alloc_one(struct mm_struct *mm,
 #endif
 
        ptepage = alloc_pages(flags, 0);
-       if (ptepage)
-               clear_highpage(ptepage);
+       if (!ptepage)
+               return NULL;
+       clear_highpage(ptepage);
+       if (!pgtable_page_ctor(ptepage)) {
+               __free_page(ptepage);
+               return NULL;
+       }
        return ptepage;
 }
 
@@ -158,8 +163,9 @@ extern inline void pte_free_slow(struct page *ptepage)
        __free_page(ptepage);
 }
 
-extern inline void pte_free(struct mm_struct *mm, struct page *ptepage)
+static inline void pte_free(struct mm_struct *mm, struct page *ptepage)
 {
+       pgtable_page_dtor(ptepage);
        __free_page(ptepage);
 }
 
index de26ea6..8c9d365 100644 (file)
@@ -106,8 +106,6 @@ static inline struct thread_info *current_thread_info(void)
 /* thread information allocation */
 #endif /* __ASSEMBLY__ */
 
-#define PREEMPT_ACTIVE         0x10000000
-
 /*
  * thread information flags
  * - these are process state flags that various assembly files may
index 17cc7ff..650de39 100644 (file)
@@ -1,6 +1,7 @@
 config MIPS
        bool
        default y
+       select ARCH_MIGHT_HAVE_PC_PARPORT
        select HAVE_CONTEXT_TRACKING
        select HAVE_GENERIC_DMA_COHERENT
        select HAVE_IDE
@@ -2125,7 +2126,6 @@ source "mm/Kconfig"
 config SMP
        bool "Multi-Processing support"
        depends on SYS_SUPPORTS_SMP
-       select USE_GENERIC_SMP_HELPERS
        help
          This enables support for systems with more than one CPU. If you have
          a system with only one CPU, like most personal computers, say N. If
index e2b4ad5..28e49f2 100644 (file)
@@ -351,7 +351,6 @@ CONFIG_USB_OHCI_HCD=y
 CONFIG_USB_OHCI_HCD_PLATFORM=y
 CONFIG_USB_STORAGE=y
 CONFIG_MMC=y
-CONFIG_MMC_CLKGATE=y
 CONFIG_MMC_AU1X=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
index 4d6fa0b..3296696 100644 (file)
 
 #define KVM_COALESCED_MMIO_PAGE_OFFSET 1
 
-/* Don't support huge pages */
-#define KVM_HPAGE_GFN_SHIFT(x) 0
-
-/* We don't currently support large pages. */
-#define KVM_NR_PAGE_SIZES      1
-#define KVM_PAGES_PER_HPAGE(x) 1
-
 
 
 /* Special address that contains the comm page, used for reducing # of traps */
index a76fe5a..df69bfd 100644 (file)
@@ -192,13 +192,13 @@ typedef struct {
        /* Number of packets processed by PIP */
        uint32_t packets;
        /*
-        * Number of indentified L2 multicast packets.  Does not
+        * Number of identified L2 multicast packets.   Does not
         * include broadcast packets.  Only includes packets whose
         * parse mode is SKIP_TO_L2
         */
        uint32_t multicast_packets;
        /*
-        * Number of indentified L2 broadcast packets.  Does not
+        * Number of identified L2 broadcast packets.   Does not
         * include multicast packets.  Only includes packets whose
         * parse mode is SKIP_TO_L2
         */
index 881d18b..b336037 100644 (file)
@@ -80,9 +80,12 @@ static inline struct page *pte_alloc_one(struct mm_struct *mm,
        struct page *pte;
 
        pte = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER);
-       if (pte) {
-               clear_highpage(pte);
-               pgtable_page_ctor(pte);
+       if (!pte)
+               return NULL;
+       clear_highpage(pte);
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
        }
        return pte;
 }
index f9b24bf..4f58ef6 100644 (file)
@@ -92,8 +92,6 @@ static inline struct thread_info *current_thread_info(void)
 
 #define STACK_WARN     (THREAD_SIZE / 8)
 
-#define PREEMPT_ACTIVE         0x10000000
-
 /*
  * thread information flags
  * - these are process state flags that various assembly files may need to
index a7b0445..73b3482 100644 (file)
@@ -198,12 +198,13 @@ kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
        return -ENOIOCTLCMD;
 }
 
-void kvm_arch_free_memslot(struct kvm_memory_slot *free,
+void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
                           struct kvm_memory_slot *dont)
 {
 }
 
-int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
+int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
+                           unsigned long npages)
 {
        return 0;
 }
index 6aaa160..8bde923 100644 (file)
@@ -181,7 +181,6 @@ endmenu
 config SMP
        bool "Symmetric multi-processing support"
        default y
-       select USE_GENERIC_SMP_HELPERS
        depends on MN10300_PROC_MN2WS0038 || MN10300_PROC_MN2WS0050
        ---help---
          This enables support for systems with more than one CPU. If you have
index c67c2b5..75dbe69 100644 (file)
@@ -71,7 +71,7 @@ static inline unsigned long allocate_mmu_context(struct mm_struct *mm)
                local_flush_tlb_all();
 
                /* fix the TLB version if needed (we avoid version #0 so as to
-                * distingush MMU_NO_CONTEXT) */
+                * distinguish MMU_NO_CONTEXT) */
                if (!mc)
                        *pmc = mc = MMU_CONTEXT_FIRST_VERSION;
        }
index 146bacf..0f25d5f 100644 (file)
@@ -46,6 +46,7 @@ static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 
 static inline void pte_free(struct mm_struct *mm, struct page *pte)
 {
+       pgtable_page_dtor(pte);
        __free_page(pte);
 }
 
index 224b426..bf280ea 100644 (file)
@@ -16,8 +16,6 @@
 
 #include <asm/page.h>
 
-#define PREEMPT_ACTIVE         0x10000000
-
 #ifdef CONFIG_4KSTACKS
 #define THREAD_SIZE            (4096)
 #define THREAD_SIZE_ORDER      (0)
index bd9ada6..e77a7c7 100644 (file)
@@ -78,8 +78,13 @@ struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
 #else
        pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0);
 #endif
-       if (pte)
-               clear_highpage(pte);
+       if (!pte)
+               return NULL;
+       clear_highpage(pte);
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
+       }
        return pte;
 }
 
index 4739b83..89076a6 100644 (file)
@@ -24,7 +24,7 @@ OBJCOPYFLAGS    := -O binary -R .note -R .comment -S
 LDFLAGS_vmlinux :=
 LIBGCC                 := $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name)
 
-KBUILD_CFLAGS  += -pipe -ffixed-r10
+KBUILD_CFLAGS  += -pipe -ffixed-r10 -D__linux__
 
 ifeq ($(CONFIG_OPENRISC_HAVE_INST_MUL),y)
        KBUILD_CFLAGS += $(call cc-option,-mhard-mul)
index ea172bd..42fe530 100644 (file)
@@ -1,9 +1,9 @@
 CONFIG_CROSS_COMPILE="or32-linux-"
+CONFIG_NO_HZ=y
 CONFIG_LOG_BUF_SHIFT=14
 CONFIG_BLK_DEV_INITRD=y
 # CONFIG_RD_GZIP is not set
 CONFIG_EXPERT=y
-# CONFIG_SYSCTL_SYSCALL is not set
 # CONFIG_KALLSYMS is not set
 # CONFIG_EPOLL is not set
 # CONFIG_TIMERFD is not set
@@ -15,7 +15,6 @@ CONFIG_SLOB=y
 CONFIG_MODULES=y
 # CONFIG_BLOCK is not set
 CONFIG_OPENRISC_BUILTIN_DTB="or1ksim"
-CONFIG_NO_HZ=y
 CONFIG_HZ_100=y
 CONFIG_NET=y
 CONFIG_PACKET=y
@@ -39,11 +38,8 @@ CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_FW_LOADER is not set
 CONFIG_PROC_DEVICETREE=y
 CONFIG_NETDEVICES=y
-CONFIG_MICREL_PHY=y
-CONFIG_NET_ETHERNET=y
 CONFIG_ETHOC=y
-# CONFIG_NETDEV_1000 is not set
-# CONFIG_NETDEV_10000 is not set
+CONFIG_MICREL_PHY=y
 # CONFIG_WLAN is not set
 # CONFIG_INPUT is not set
 # CONFIG_SERIO is not set
@@ -55,11 +51,9 @@ CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_SERIAL_OF_PLATFORM=y
 # CONFIG_HW_RANDOM is not set
 # CONFIG_HWMON is not set
-# CONFIG_MFD_SUPPORT is not set
 # CONFIG_USB_SUPPORT is not set
 # CONFIG_DNOTIFY is not set
 CONFIG_TMPFS=y
 CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
 # CONFIG_ENABLE_WARN_DEPRECATED is not set
 # CONFIG_ENABLE_MUST_CHECK is not set
index 7840562..da1951a 100644 (file)
@@ -65,6 +65,7 @@ generic-y += trace_clock.h
 generic-y += types.h
 generic-y += ucontext.h
 generic-y += user.h
+generic-y += vga.h
 generic-y += word-at-a-time.h
 generic-y += xor.h
 generic-y += preempt.h
index 05c39ec..21484e5 100644 (file)
@@ -78,8 +78,13 @@ static inline struct page *pte_alloc_one(struct mm_struct *mm,
 {
        struct page *pte;
        pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0);
-       if (pte)
-               clear_page(page_address(pte));
+       if (!pte)
+               return NULL;
+       clear_page(page_address(pte));
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
+       }
        return pte;
 }
 
@@ -90,6 +95,7 @@ static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 
 static inline void pte_free(struct mm_struct *mm, struct page *pte)
 {
+       pgtable_page_dtor(pte);
        __free_page(pte);
 }
 
index 10ff50f..ef872ae 100644 (file)
@@ -47,12 +47,10 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
                        *location = value;
                        break;
                case R_OR32_CONST:
-                       location = (uint16_t *)location + 1;
-                       *((uint16_t *)location) = (uint16_t) (value);
+                       *((uint16_t *)location + 1) = value;
                        break;
                case R_OR32_CONSTH:
-                       location = (uint16_t *)location + 1;
-                       *((uint16_t *)location) = (uint16_t) (value >> 16);
+                       *((uint16_t *)location + 1) = value >> 16;
                        break;
                case R_OR32_JUMPTARG:
                        value -= (uint32_t)location;
index 09a769b..4fc7ccc 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/device.h>
 #include <linux/of_platform.h>
 
+#include <asm/sections.h>
 #include <asm/segment.h>
 #include <asm/pgtable.h>
 #include <asm/types.h>
@@ -75,7 +76,7 @@ static unsigned long __init setup_memory(void)
 
        ram_start_pfn = PFN_UP(memory_start);
        /* free_ram_start_pfn is first page after kernel */
-       free_ram_start_pfn = PFN_UP(__pa(&_end));
+       free_ram_start_pfn = PFN_UP(__pa(_end));
        ram_end_pfn = PFN_DOWN(memblock_end_of_DRAM());
 
        max_pfn = ram_end_pfn;
@@ -207,15 +208,15 @@ void __init setup_cpuinfo(void)
  * Falls back on built-in device tree in case null pointer is passed.
  */
 
-void __init or32_early_setup(unsigned int fdt)
+void __init or32_early_setup(void *fdt)
 {
-       if (fdt) {
-               early_init_devtree((void*) fdt);
-               printk(KERN_INFO "FDT at 0x%08x\n", fdt);
-       } else {
-               early_init_devtree(__dtb_start);
-               printk(KERN_INFO "Compiled-in FDT at %p\n", __dtb_start);
+       if (fdt)
+               pr_info("FDT at %p\n", fdt);
+       else {
+               fdt = __dtb_start;
+               pr_info("Compiled-in FDT at %p\n", fdt);
        }
+       early_init_devtree(fdt);
 }
 
 static int __init openrisc_device_probe(void)
@@ -288,10 +289,10 @@ void __init setup_arch(char **cmdline_p)
        setup_cpuinfo();
 
        /* process 1's initial memory region is the kernel code/data */
-       init_mm.start_code = (unsigned long)&_stext;
-       init_mm.end_code = (unsigned long)&_etext;
-       init_mm.end_data = (unsigned long)&_edata;
-       init_mm.brk = (unsigned long)&_end;
+       init_mm.start_code = (unsigned long)_stext;
+       init_mm.end_code = (unsigned long)_etext;
+       init_mm.end_data = (unsigned long)_edata;
+       init_mm.brk = (unsigned long)_end;
 
 #ifdef CONFIG_BLK_DEV_INITRD
        initrd_start = (unsigned long)&__initrd_start;
index ee842a2..70b9ce4 100644 (file)
@@ -1,10 +1,8 @@
 #ifndef __OPENRISC_VMLINUX_H_
 #define __OPENRISC_VMLINUX_H_
 
-extern char _stext, _etext, _edata, _end;
 #ifdef CONFIG_BLK_DEV_INITRD
 extern char __initrd_start, __initrd_end;
-extern char __initramfs_start;
 #endif
 
 extern u32 __dtb_start[];
index 7dcde53..b5f1858 100644 (file)
@@ -1,6 +1,7 @@
 config PARISC
        def_bool y
        select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
+       select ARCH_MIGHT_HAVE_PC_PARPORT
        select HAVE_IDE
        select HAVE_OPROFILE
        select HAVE_FUNCTION_TRACER if 64BIT
@@ -226,7 +227,6 @@ endchoice
 
 config SMP
        bool "Symmetric multi-processing support"
-       select USE_GENERIC_SMP_HELPERS
        ---help---
          This enables support for systems with more than one CPU. If you have
          a system with only one CPU, like most personal computers, say N. If
index fc987a1..f213f5b 100644 (file)
@@ -121,8 +121,12 @@ static inline pgtable_t
 pte_alloc_one(struct mm_struct *mm, unsigned long address)
 {
        struct page *page = alloc_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
-       if (page)
-               pgtable_page_ctor(page);
+       if (!page)
+               return NULL;
+       if (!pgtable_page_ctor(page)) {
+               __free_page(page);
+               return NULL;
+       }
        return page;
 }
 
diff --git a/arch/parisc/include/asm/socket.h b/arch/parisc/include/asm/socket.h
new file mode 100644 (file)
index 0000000..748016c
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _ASM_SOCKET_H
+#define _ASM_SOCKET_H
+
+#include <uapi/asm/socket.h>
+
+/* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
+ * have to define SOCK_NONBLOCK to a different value here.
+ */
+#define SOCK_NONBLOCK  0x40000000
+
+#endif /* _ASM_SOCKET_H */
index bc7cf12..d5f97ea 100644 (file)
@@ -46,9 +46,6 @@ struct thread_info {
 #define THREAD_SIZE             (PAGE_SIZE << THREAD_SIZE_ORDER)
 #define THREAD_SHIFT            (PAGE_SHIFT + THREAD_SIZE_ORDER)
 
-#define PREEMPT_ACTIVE_BIT     28
-#define PREEMPT_ACTIVE         (1 << PREEMPT_ACTIVE_BIT)
-
 /*
  * thread information flags
  */
index 63f4dd0..4006964 100644 (file)
@@ -4,14 +4,11 @@
 /*
  * User space memory access functions
  */
-#include <asm/processor.h>
 #include <asm/page.h>
 #include <asm/cache.h>
 #include <asm/errno.h>
 #include <asm-generic/uaccess-unaligned.h>
 
-#include <linux/sched.h>
-
 #define VERIFY_READ 0
 #define VERIFY_WRITE 1
 
@@ -36,43 +33,12 @@ extern int __get_user_bad(void);
 extern int __put_kernel_bad(void);
 extern int __put_user_bad(void);
 
-
-/*
- * Test whether a block of memory is a valid user space address.
- * Returns 0 if the range is valid, nonzero otherwise.
- */
-static inline int __range_not_ok(unsigned long addr, unsigned long size,
-                                unsigned long limit)
+static inline long access_ok(int type, const void __user * addr,
+               unsigned long size)
 {
-       unsigned long __newaddr = addr + size;
-       return (__newaddr < addr || __newaddr > limit || size > limit);
+       return 1;
 }
 
-/**
- * access_ok: - Checks if a user space pointer is valid
- * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE.  Note that
- *        %VERIFY_WRITE is a superset of %VERIFY_READ - if it is safe
- *        to write to a block, it is always safe to read from it.
- * @addr: User space pointer to start of block to check
- * @size: Size of block to check
- *
- * Context: User context only.  This function may sleep.
- *
- * Checks if a pointer to a block of memory in user space is valid.
- *
- * Returns true (nonzero) if the memory block may be valid, false (zero)
- * if it is definitely invalid.
- *
- * Note that, depending on architecture, this function probably just
- * checks that the pointer is in the user space range - after calling
- * this function, memory access functions may still return -EFAULT.
- */
-#define access_ok(type, addr, size)                                    \
-(      __chk_user_ptr(addr),                                           \
-       !__range_not_ok((unsigned long) (__force void *) (addr),        \
-                       size, user_addr_max())                          \
-)
-
 #define put_user __put_user
 #define get_user __get_user
 
@@ -253,11 +219,7 @@ extern long lstrnlen_user(const char __user *,long);
 /*
  * Complex access routines -- macros
  */
-#ifdef CONFIG_COMPAT
-#define user_addr_max() (TASK_SIZE)
-#else
-#define user_addr_max() (DEFAULT_TASK_SIZE)
-#endif
+#define user_addr_max() (~0UL)
 
 #define strnlen_user lstrnlen_user
 #define strlen_user(str) lstrnlen_user(str, 0x7fffffffL)
index 7c614d0..f33113a 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef _ASM_SOCKET_H
-#define _ASM_SOCKET_H
+#ifndef _UAPI_ASM_SOCKET_H
+#define _UAPI_ASM_SOCKET_H
 
 #include <asm/sockios.h>
 
@@ -77,9 +77,4 @@
 
 #define SO_MAX_PACING_RATE     0x4048
 
-/* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
- * have to define SOCK_NONBLOCK to a different value here.
- */
-#define SOCK_NONBLOCK   0x40000000
-
-#endif /* _ASM_SOCKET_H */
+#endif /* _UAPI_ASM_SOCKET_H */
index b5507ec..413dc17 100644 (file)
@@ -161,7 +161,7 @@ static inline void prefetch_dst(const void *addr)
 /* Copy from a not-aligned src to an aligned dst, using shifts. Handles 4 words
  * per loop.  This code is derived from glibc. 
  */
-static inline unsigned long copy_dstaligned(unsigned long dst,
+static noinline unsigned long copy_dstaligned(unsigned long dst,
                                        unsigned long src, unsigned long len)
 {
        /* gcc complains that a2 and a3 may be uninitialized, but actually
@@ -276,7 +276,7 @@ handle_store_error:
 /* Returns PA_MEMCPY_OK, PA_MEMCPY_LOAD_ERROR or PA_MEMCPY_STORE_ERROR.
  * In case of an access fault the faulty address can be read from the per_cpu
  * exception data struct. */
-static unsigned long pa_memcpy_internal(void *dstp, const void *srcp,
+static noinline unsigned long pa_memcpy_internal(void *dstp, const void *srcp,
                                        unsigned long len)
 {
        register unsigned long src, dst, t1, t2, t3;
@@ -529,7 +529,7 @@ long probe_kernel_read(void *dst, const void *src, size_t size)
 {
        unsigned long addr = (unsigned long)src;
 
-       if (size < 0 || addr < PAGE_SIZE)
+       if (addr < PAGE_SIZE)
                return -EFAULT;
 
        /* check for I/O space F_EXTEND(0xfff00000) access as well? */
index 7584a5d..9d08c71 100644 (file)
@@ -282,16 +282,34 @@ bad_area:
 #endif
                switch (code) {
                case 15:        /* Data TLB miss fault/Data page fault */
+                       /* send SIGSEGV when outside of vma */
+                       if (!vma ||
+                           address < vma->vm_start || address > vma->vm_end) {
+                               si.si_signo = SIGSEGV;
+                               si.si_code = SEGV_MAPERR;
+                               break;
+                       }
+
+                       /* send SIGSEGV for wrong permissions */
+                       if ((vma->vm_flags & acc_type) != acc_type) {
+                               si.si_signo = SIGSEGV;
+                               si.si_code = SEGV_ACCERR;
+                               break;
+                       }
+
+                       /* probably address is outside of mapped file */
+                       /* fall through */
                case 17:        /* NA data TLB miss / page fault */
                case 18:        /* Unaligned access - PCXS only */
                        si.si_signo = SIGBUS;
-                       si.si_code = BUS_ADRERR;
+                       si.si_code = (code == 18) ? BUS_ADRALN : BUS_ADRERR;
                        break;
                case 16:        /* Non-access instruction TLB miss fault */
                case 26:        /* PCXL: Data memory access rights trap */
                default:
                        si.si_signo = SIGSEGV;
-                       si.si_code = SEGV_MAPERR;
+                       si.si_code = (code == 26) ? SEGV_ACCERR : SEGV_MAPERR;
+                       break;
                }
                si.si_errno = 0;
                si.si_addr = (void __user *) address;
index 2f898d6..b44b52c 100644 (file)
@@ -85,6 +85,7 @@ config GENERIC_HWEIGHT
 config PPC
        bool
        default y
+       select ARCH_MIGHT_HAVE_PC_PARPORT
        select BINFMT_ELF
        select OF
        select OF_EARLY_FLATTREE
@@ -106,7 +107,6 @@ config PPC
        select HAVE_MEMBLOCK_NODE_MAP
        select HAVE_DMA_ATTRS
        select HAVE_DMA_API_DEBUG
-       select USE_GENERIC_SMP_HELPERS if SMP
        select HAVE_OPROFILE
        select HAVE_DEBUG_KMEMLEAK
        select GENERIC_ATOMIC64 if PPC32
index 607acf5..8a24636 100644 (file)
@@ -111,6 +111,7 @@ endif
 endif
 
 CFLAGS-$(CONFIG_PPC64) := -mtraceback=no -mcall-aixdesc
+CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv1)
 CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,-mminimal-toc)
 CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mno-pointers-to-nested-functions)
 CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 $(MULTIPLEWORD)
index 4c617bf..4f6e482 100644 (file)
                reg = <0xe2000 0x1000>;
        };
 
-/include/ "qoriq-dma-0.dtsi"
+/include/ "elo3-dma-0.dtsi"
        dma@100300 {
                fsl,iommu-parent = <&pamu0>;
                fsl,liodn-reg = <&guts 0x580>; /* DMA1LIODNR */
        };
 
-/include/ "qoriq-dma-1.dtsi"
+/include/ "elo3-dma-1.dtsi"
        dma@101300 {
                fsl,iommu-parent = <&pamu0>;
                fsl,liodn-reg = <&guts 0x584>; /* DMA2LIODNR */
diff --git a/arch/powerpc/boot/dts/fsl/elo3-dma-0.dtsi b/arch/powerpc/boot/dts/fsl/elo3-dma-0.dtsi
new file mode 100644 (file)
index 0000000..3c210e0
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * QorIQ Elo3 DMA device tree stub [ controller @ offset 0x100000 ]
+ *
+ * Copyright 2013 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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.
+ */
+
+dma0: dma@100300 {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       compatible = "fsl,elo3-dma";
+       reg = <0x100300 0x4>,
+             <0x100600 0x4>;
+       ranges = <0x0 0x100100 0x500>;
+       dma-channel@0 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x0 0x80>;
+               interrupts = <28 2 0 0>;
+       };
+       dma-channel@80 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x80 0x80>;
+               interrupts = <29 2 0 0>;
+       };
+       dma-channel@100 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x100 0x80>;
+               interrupts = <30 2 0 0>;
+       };
+       dma-channel@180 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x180 0x80>;
+               interrupts = <31 2 0 0>;
+       };
+       dma-channel@300 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x300 0x80>;
+               interrupts = <76 2 0 0>;
+       };
+       dma-channel@380 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x380 0x80>;
+               interrupts = <77 2 0 0>;
+       };
+       dma-channel@400 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x400 0x80>;
+               interrupts = <78 2 0 0>;
+       };
+       dma-channel@480 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x480 0x80>;
+               interrupts = <79 2 0 0>;
+       };
+};
diff --git a/arch/powerpc/boot/dts/fsl/elo3-dma-1.dtsi b/arch/powerpc/boot/dts/fsl/elo3-dma-1.dtsi
new file mode 100644 (file)
index 0000000..cccf3bb
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * QorIQ Elo3 DMA device tree stub [ controller @ offset 0x101000 ]
+ *
+ * Copyright 2013 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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.
+ */
+
+dma1: dma@101300 {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       compatible = "fsl,elo3-dma";
+       reg = <0x101300 0x4>,
+             <0x101600 0x4>;
+       ranges = <0x0 0x101100 0x500>;
+       dma-channel@0 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x0 0x80>;
+               interrupts = <32 2 0 0>;
+       };
+       dma-channel@80 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x80 0x80>;
+               interrupts = <33 2 0 0>;
+       };
+       dma-channel@100 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x100 0x80>;
+               interrupts = <34 2 0 0>;
+       };
+       dma-channel@180 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x180 0x80>;
+               interrupts = <35 2 0 0>;
+       };
+       dma-channel@300 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x300 0x80>;
+               interrupts = <80 2 0 0>;
+       };
+       dma-channel@380 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x380 0x80>;
+               interrupts = <81 2 0 0>;
+       };
+       dma-channel@400 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x400 0x80>;
+               interrupts = <82 2 0 0>;
+       };
+       dma-channel@480 {
+               compatible = "fsl,eloplus-dma-channel";
+               reg = <0x480 0x80>;
+               interrupts = <83 2 0 0>;
+       };
+};
index 510afa3..4143a97 100644 (file)
                reg        = <0xea000 0x4000>;
        };
 
-/include/ "qoriq-dma-0.dtsi"
-/include/ "qoriq-dma-1.dtsi"
+/include/ "elo3-dma-0.dtsi"
+/include/ "elo3-dma-1.dtsi"
 
 /include/ "qoriq-espi-0.dtsi"
        spi@110000 {
diff --git a/arch/powerpc/configs/pseries_le_defconfig b/arch/powerpc/configs/pseries_le_defconfig
new file mode 100644 (file)
index 0000000..62771e0
--- /dev/null
@@ -0,0 +1,352 @@
+CONFIG_PPC64=y
+CONFIG_ALTIVEC=y
+CONFIG_VSX=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2048
+CONFIG_CPU_LITTLE_ENDIAN=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_KPROBES=y
+CONFIG_JUMP_LABEL=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_PPC_SPLPAR=y
+CONFIG_SCANLOG=m
+CONFIG_PPC_SMLPAR=y
+CONFIG_DTL=y
+# CONFIG_PPC_PMAC is not set
+CONFIG_RTAS_FLASH=m
+CONFIG_IBMEBUS=y
+CONFIG_HZ_100=y
+CONFIG_BINFMT_MISC=m
+CONFIG_PPC_TRANSACTIONAL_MEM=y
+CONFIG_KEXEC=y
+CONFIG_IRQ_ALL_CPUS=y
+CONFIG_MEMORY_HOTPLUG=y
+CONFIG_MEMORY_HOTREMOVE=y
+CONFIG_CMA=y
+CONFIG_PPC_64K_PAGES=y
+CONFIG_PPC_SUBPAGE_PROT=y
+CONFIG_SCHED_SMT=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_HOTPLUG_PCI_RPA=m
+CONFIG_HOTPLUG_PCI_RPA_DLPAR=m
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=m
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_NET_IPIP=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+# CONFIG_IPV6 is not set
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_UDPLITE=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_SCTP=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+CONFIG_BLK_DEV_FD=m
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=65536
+CONFIG_VIRTIO_BLK=m
+CONFIG_IDE=y
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_GENERIC=y
+CONFIG_BLK_DEV_AMD74XX=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_FC_ATTRS=y
+CONFIG_SCSI_CXGB3_ISCSI=m
+CONFIG_SCSI_CXGB4_ISCSI=m
+CONFIG_SCSI_BNX2_ISCSI=m
+CONFIG_BE2ISCSI=m
+CONFIG_SCSI_MPT2SAS=m
+CONFIG_SCSI_IBMVSCSI=y
+CONFIG_SCSI_IBMVFC=m
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_IPR=y
+CONFIG_SCSI_QLA_FC=m
+CONFIG_SCSI_QLA_ISCSI=m
+CONFIG_SCSI_LPFC=m
+CONFIG_SCSI_VIRTIO=m
+CONFIG_SCSI_DH=m
+CONFIG_SCSI_DH_RDAC=m
+CONFIG_SCSI_DH_ALUA=m
+CONFIG_ATA=y
+# CONFIG_ATA_SFF is not set
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=y
+CONFIG_MD_RAID0=y
+CONFIG_MD_RAID1=y
+CONFIG_MD_RAID10=m
+CONFIG_MD_RAID456=m
+CONFIG_MD_MULTIPATH=m
+CONFIG_MD_FAULTY=m
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_QL=m
+CONFIG_DM_MULTIPATH_ST=m
+CONFIG_DM_UEVENT=y
+CONFIG_BONDING=m
+CONFIG_DUMMY=m
+CONFIG_NETCONSOLE=y
+CONFIG_NETPOLL_TRAP=y
+CONFIG_TUN=m
+CONFIG_VIRTIO_NET=m
+CONFIG_VORTEX=y
+CONFIG_ACENIC=m
+CONFIG_ACENIC_OMIT_TIGON_I=y
+CONFIG_PCNET32=y
+CONFIG_TIGON3=y
+CONFIG_CHELSIO_T1=m
+CONFIG_BE2NET=m
+CONFIG_S2IO=m
+CONFIG_IBMVETH=y
+CONFIG_EHEA=y
+CONFIG_E100=y
+CONFIG_E1000=y
+CONFIG_E1000E=y
+CONFIG_IXGB=m
+CONFIG_IXGBE=m
+CONFIG_MLX4_EN=m
+CONFIG_MYRI10GE=m
+CONFIG_QLGE=m
+CONFIG_NETXEN_NIC=m
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPPOE=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_PCSPKR=m
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_ICOM=m
+CONFIG_SERIAL_JSM=m
+CONFIG_HVC_CONSOLE=y
+CONFIG_HVC_RTAS=y
+CONFIG_HVCS=m
+CONFIG_VIRTIO_CONSOLE=m
+CONFIG_IBM_BSR=m
+CONFIG_GEN_RTC=y
+CONFIG_RAW_DRIVER=y
+CONFIG_MAX_RAW_DEVS=1024
+CONFIG_FB=y
+CONFIG_FIRMWARE_EDID=y
+CONFIG_FB_OF=y
+CONFIG_FB_MATROX=y
+CONFIG_FB_MATROX_MILLENIUM=y
+CONFIG_FB_MATROX_MYSTIQUE=y
+CONFIG_FB_MATROX_G=y
+CONFIG_FB_RADEON=y
+CONFIG_FB_IBM_GXT4500=y
+CONFIG_LCD_PLATFORM=m
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_LOGO=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+CONFIG_USB_MON=m
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_HCD_PPC_OF is not set
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_STORAGE=m
+CONFIG_INFINIBAND=m
+CONFIG_INFINIBAND_USER_MAD=m
+CONFIG_INFINIBAND_USER_ACCESS=m
+CONFIG_INFINIBAND_MTHCA=m
+CONFIG_INFINIBAND_EHCA=m
+CONFIG_INFINIBAND_CXGB3=m
+CONFIG_INFINIBAND_CXGB4=m
+CONFIG_MLX4_INFINIBAND=m
+CONFIG_INFINIBAND_IPOIB=m
+CONFIG_INFINIBAND_IPOIB_CM=y
+CONFIG_INFINIBAND_SRP=m
+CONFIG_INFINIBAND_ISER=m
+CONFIG_VIRTIO_PCI=m
+CONFIG_VIRTIO_BALLOON=m
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT2_FS_XIP=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_REISERFS_FS=y
+CONFIG_REISERFS_FS_XATTR=y
+CONFIG_REISERFS_FS_POSIX_ACL=y
+CONFIG_REISERFS_FS_SECURITY=y
+CONFIG_JFS_FS=m
+CONFIG_JFS_POSIX_ACL=y
+CONFIG_JFS_SECURITY=y
+CONFIG_XFS_FS=m
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_NILFS2_FS=m
+CONFIG_AUTOFS4_FS=m
+CONFIG_FUSE_FS=m
+CONFIG_ISO9660_FS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_CRAMFS=m
+CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_PSTORE=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_CIFS=m
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_POSIX=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_CRC_T10DIF=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_STACKOVERFLOW=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_LATENCYTOP=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_CODE_PATCHING_SELFTEST=y
+CONFIG_FTR_FIXUP_SELFTEST=y
+CONFIG_MSI_BITMAP_SELFTEST=y
+CONFIG_XMON=y
+CONFIG_CRYPTO_TEST=m
+CONFIG_CRYPTO_PCBC=m
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_SALSA20=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_LZO=m
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_NX=y
+CONFIG_CRYPTO_DEV_NX_ENCRYPT=m
index 9b198d1..856f8de 100644 (file)
@@ -77,4 +77,8 @@ static inline unsigned int get_d(u32 inst)
        return inst & 0xffff;
 }
 
+static inline unsigned int get_oc(u32 inst)
+{
+       return (inst >> 11) & 0x7fff;
+}
 #endif /* __ASM_PPC_DISASSEMBLE_H__ */
index cc0655a..935b5e7 100644 (file)
@@ -31,6 +31,8 @@
 extern unsigned long randomize_et_dyn(unsigned long base);
 #define ELF_ET_DYN_BASE                (randomize_et_dyn(0x20000000))
 
+#define ELF_CORE_EFLAGS (is_elf2_task() ? 2 : 0)
+
 /*
  * Our registers are always unsigned longs, whether we're a 32 bit
  * process or 64 bit, on either a 64 bit or 32 bit kernel.
@@ -86,6 +88,8 @@ typedef elf_vrregset_t elf_fpxregset_t;
 #ifdef __powerpc64__
 # define SET_PERSONALITY(ex)                                   \
 do {                                                           \
+       if (((ex).e_flags & 0x3) == 2)                          \
+               set_thread_flag(TIF_ELF2ABI);                   \
        if ((ex).e_ident[EI_CLASS] == ELFCLASS32)               \
                set_thread_flag(TIF_32BIT);                     \
        else                                                    \
index cca12f0..894662a 100644 (file)
@@ -198,12 +198,27 @@ END_FTR_SECTION_NESTED(ftr,ftr,943)
        cmpwi   r10,0;                                                  \
        bne     do_kvm_##n
 
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
+/*
+ * If hv is possible, interrupts come into to the hv version
+ * of the kvmppc_interrupt code, which then jumps to the PR handler,
+ * kvmppc_interrupt_pr, if the guest is a PR guest.
+ */
+#define kvmppc_interrupt kvmppc_interrupt_hv
+#else
+#define kvmppc_interrupt kvmppc_interrupt_pr
+#endif
+
 #define __KVM_HANDLER(area, h, n)                                      \
 do_kvm_##n:                                                            \
        BEGIN_FTR_SECTION_NESTED(947)                                   \
        ld      r10,area+EX_CFAR(r13);                                  \
        std     r10,HSTATE_CFAR(r13);                                   \
        END_FTR_SECTION_NESTED(CPU_FTR_CFAR,CPU_FTR_CFAR,947);          \
+       BEGIN_FTR_SECTION_NESTED(948)                                   \
+       ld      r10,area+EX_PPR(r13);                                   \
+       std     r10,HSTATE_PPR(r13);                                    \
+       END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,948);    \
        ld      r10,area+EX_R10(r13);                                   \
        stw     r9,HSTATE_SCRATCH1(r13);                                \
        ld      r9,area+EX_R9(r13);                                     \
@@ -217,6 +232,10 @@ do_kvm_##n:                                                                \
        ld      r10,area+EX_R10(r13);                                   \
        beq     89f;                                                    \
        stw     r9,HSTATE_SCRATCH1(r13);                        \
+       BEGIN_FTR_SECTION_NESTED(948)                                   \
+       ld      r9,area+EX_PPR(r13);                                    \
+       std     r9,HSTATE_PPR(r13);                                     \
+       END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,948);    \
        ld      r9,area+EX_R9(r13);                                     \
        std     r12,HSTATE_SCRATCH0(r13);                       \
        li      r12,n;                                                  \
@@ -236,7 +255,7 @@ do_kvm_##n:                                                         \
 #define KVM_HANDLER_SKIP(area, h, n)
 #endif
 
-#ifdef CONFIG_KVM_BOOK3S_PR
+#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
 #define KVMTEST_PR(n)                  __KVMTEST(n)
 #define KVM_HANDLER_PR(area, h, n)     __KVM_HANDLER(area, h, n)
 #define KVM_HANDLER_PR_SKIP(area, h, n)        __KVM_HANDLER_SKIP(area, h, n)
index 0c7f2bf..d8b600b 100644 (file)
@@ -403,6 +403,8 @@ static inline unsigned long cmo_get_page_size(void)
 extern long pSeries_enable_reloc_on_exc(void);
 extern long pSeries_disable_reloc_on_exc(void);
 
+extern long pseries_big_endian_exceptions(void);
+
 #else
 
 #define pSeries_enable_reloc_on_exc()  do {} while (0)
index 851bac7..1bd92fd 100644 (file)
 #define BOOK3S_HFLAG_SLB                       0x2
 #define BOOK3S_HFLAG_PAIRED_SINGLE             0x4
 #define BOOK3S_HFLAG_NATIVE_PS                 0x8
+#define BOOK3S_HFLAG_MULTI_PGSIZE              0x10
+#define BOOK3S_HFLAG_NEW_TLBIE                 0x20
 
 #define RESUME_FLAG_NV          (1<<0)  /* Reload guest nonvolatile state? */
 #define RESUME_FLAG_HOST        (1<<1)  /* Resume host? */
 #define KVM_GUEST_MODE_NONE    0
 #define KVM_GUEST_MODE_GUEST   1
 #define KVM_GUEST_MODE_SKIP    2
+#define KVM_GUEST_MODE_GUEST_HV        3
+#define KVM_GUEST_MODE_HOST_HV 4
 
 #define KVM_INST_FETCH_FAILED  -1
 
index fa19e2f..4a594b7 100644 (file)
@@ -58,16 +58,18 @@ struct hpte_cache {
        struct hlist_node list_pte_long;
        struct hlist_node list_vpte;
        struct hlist_node list_vpte_long;
+#ifdef CONFIG_PPC_BOOK3S_64
+       struct hlist_node list_vpte_64k;
+#endif
        struct rcu_head rcu_head;
        u64 host_vpn;
        u64 pfn;
        ulong slot;
        struct kvmppc_pte pte;
+       int pagesize;
 };
 
 struct kvmppc_vcpu_book3s {
-       struct kvm_vcpu vcpu;
-       struct kvmppc_book3s_shadow_vcpu *shadow_vcpu;
        struct kvmppc_sid_map sid_map[SID_MAP_NUM];
        struct {
                u64 esid;
@@ -99,6 +101,9 @@ struct kvmppc_vcpu_book3s {
        struct hlist_head hpte_hash_pte_long[HPTEG_HASH_NUM_PTE_LONG];
        struct hlist_head hpte_hash_vpte[HPTEG_HASH_NUM_VPTE];
        struct hlist_head hpte_hash_vpte_long[HPTEG_HASH_NUM_VPTE_LONG];
+#ifdef CONFIG_PPC_BOOK3S_64
+       struct hlist_head hpte_hash_vpte_64k[HPTEG_HASH_NUM_VPTE_64K];
+#endif
        int hpte_cache_count;
        spinlock_t mmu_lock;
 };
@@ -107,8 +112,9 @@ struct kvmppc_vcpu_book3s {
 #define CONTEXT_GUEST          1
 #define CONTEXT_GUEST_END      2
 
-#define VSID_REAL      0x0fffffffffc00000ULL
-#define VSID_BAT       0x0fffffffffb00000ULL
+#define VSID_REAL      0x07ffffffffc00000ULL
+#define VSID_BAT       0x07ffffffffb00000ULL
+#define VSID_64K       0x0800000000000000ULL
 #define VSID_1T                0x1000000000000000ULL
 #define VSID_REAL_DR   0x2000000000000000ULL
 #define VSID_REAL_IR   0x4000000000000000ULL
@@ -118,11 +124,12 @@ extern void kvmppc_mmu_pte_flush(struct kvm_vcpu *vcpu, ulong ea, ulong ea_mask)
 extern void kvmppc_mmu_pte_vflush(struct kvm_vcpu *vcpu, u64 vp, u64 vp_mask);
 extern void kvmppc_mmu_pte_pflush(struct kvm_vcpu *vcpu, ulong pa_start, ulong pa_end);
 extern void kvmppc_set_msr(struct kvm_vcpu *vcpu, u64 new_msr);
-extern void kvmppc_set_pvr(struct kvm_vcpu *vcpu, u32 pvr);
 extern void kvmppc_mmu_book3s_64_init(struct kvm_vcpu *vcpu);
 extern void kvmppc_mmu_book3s_32_init(struct kvm_vcpu *vcpu);
 extern void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu);
-extern int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte);
+extern int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte,
+                              bool iswrite);
+extern void kvmppc_mmu_unmap_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte);
 extern int kvmppc_mmu_map_segment(struct kvm_vcpu *vcpu, ulong eaddr);
 extern void kvmppc_mmu_flush_segment(struct kvm_vcpu *vcpu, ulong eaddr, ulong seg_size);
 extern void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu);
@@ -134,6 +141,7 @@ extern long kvmppc_hv_find_lock_hpte(struct kvm *kvm, gva_t eaddr,
 
 extern void kvmppc_mmu_hpte_cache_map(struct kvm_vcpu *vcpu, struct hpte_cache *pte);
 extern struct hpte_cache *kvmppc_mmu_hpte_cache_next(struct kvm_vcpu *vcpu);
+extern void kvmppc_mmu_hpte_cache_free(struct hpte_cache *pte);
 extern void kvmppc_mmu_hpte_destroy(struct kvm_vcpu *vcpu);
 extern int kvmppc_mmu_hpte_init(struct kvm_vcpu *vcpu);
 extern void kvmppc_mmu_invalidate_pte(struct kvm_vcpu *vcpu, struct hpte_cache *pte);
@@ -151,7 +159,8 @@ extern void kvmppc_set_bat(struct kvm_vcpu *vcpu, struct kvmppc_bat *bat,
                           bool upper, u32 val);
 extern void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr);
 extern int kvmppc_emulate_paired_single(struct kvm_run *run, struct kvm_vcpu *vcpu);
-extern pfn_t kvmppc_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn);
+extern pfn_t kvmppc_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn, bool writing,
+                       bool *writable);
 extern void kvmppc_add_revmap_chain(struct kvm *kvm, struct revmap_entry *rev,
                        unsigned long *rmap, long pte_index, int realmode);
 extern void kvmppc_invalidate_hpte(struct kvm *kvm, unsigned long *hptep,
@@ -172,6 +181,8 @@ extern long kvmppc_do_h_remove(struct kvm *kvm, unsigned long flags,
                        unsigned long *hpret);
 extern long kvmppc_hv_get_dirty_log(struct kvm *kvm,
                        struct kvm_memory_slot *memslot, unsigned long *map);
+extern void kvmppc_update_lpcr(struct kvm *kvm, unsigned long lpcr,
+                       unsigned long mask);
 
 extern void kvmppc_entry_trampoline(void);
 extern void kvmppc_hv_entry_trampoline(void);
@@ -184,11 +195,9 @@ extern int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd);
 
 static inline struct kvmppc_vcpu_book3s *to_book3s(struct kvm_vcpu *vcpu)
 {
-       return container_of(vcpu, struct kvmppc_vcpu_book3s, vcpu);
+       return vcpu->arch.book3s;
 }
 
-extern void kvm_return_point(void);
-
 /* Also add subarch specific defines */
 
 #ifdef CONFIG_KVM_BOOK3S_32_HANDLER
@@ -198,203 +207,6 @@ extern void kvm_return_point(void);
 #include <asm/kvm_book3s_64.h>
 #endif
 
-#ifdef CONFIG_KVM_BOOK3S_PR
-
-static inline unsigned long kvmppc_interrupt_offset(struct kvm_vcpu *vcpu)
-{
-       return to_book3s(vcpu)->hior;
-}
-
-static inline void kvmppc_update_int_pending(struct kvm_vcpu *vcpu,
-                       unsigned long pending_now, unsigned long old_pending)
-{
-       if (pending_now)
-               vcpu->arch.shared->int_pending = 1;
-       else if (old_pending)
-               vcpu->arch.shared->int_pending = 0;
-}
-
-static inline void kvmppc_set_gpr(struct kvm_vcpu *vcpu, int num, ulong val)
-{
-       if ( num < 14 ) {
-               struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-               svcpu->gpr[num] = val;
-               svcpu_put(svcpu);
-               to_book3s(vcpu)->shadow_vcpu->gpr[num] = val;
-       } else
-               vcpu->arch.gpr[num] = val;
-}
-
-static inline ulong kvmppc_get_gpr(struct kvm_vcpu *vcpu, int num)
-{
-       if ( num < 14 ) {
-               struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-               ulong r = svcpu->gpr[num];
-               svcpu_put(svcpu);
-               return r;
-       } else
-               return vcpu->arch.gpr[num];
-}
-
-static inline void kvmppc_set_cr(struct kvm_vcpu *vcpu, u32 val)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       svcpu->cr = val;
-       svcpu_put(svcpu);
-       to_book3s(vcpu)->shadow_vcpu->cr = val;
-}
-
-static inline u32 kvmppc_get_cr(struct kvm_vcpu *vcpu)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       u32 r;
-       r = svcpu->cr;
-       svcpu_put(svcpu);
-       return r;
-}
-
-static inline void kvmppc_set_xer(struct kvm_vcpu *vcpu, u32 val)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       svcpu->xer = val;
-       to_book3s(vcpu)->shadow_vcpu->xer = val;
-       svcpu_put(svcpu);
-}
-
-static inline u32 kvmppc_get_xer(struct kvm_vcpu *vcpu)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       u32 r;
-       r = svcpu->xer;
-       svcpu_put(svcpu);
-       return r;
-}
-
-static inline void kvmppc_set_ctr(struct kvm_vcpu *vcpu, ulong val)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       svcpu->ctr = val;
-       svcpu_put(svcpu);
-}
-
-static inline ulong kvmppc_get_ctr(struct kvm_vcpu *vcpu)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       ulong r;
-       r = svcpu->ctr;
-       svcpu_put(svcpu);
-       return r;
-}
-
-static inline void kvmppc_set_lr(struct kvm_vcpu *vcpu, ulong val)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       svcpu->lr = val;
-       svcpu_put(svcpu);
-}
-
-static inline ulong kvmppc_get_lr(struct kvm_vcpu *vcpu)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       ulong r;
-       r = svcpu->lr;
-       svcpu_put(svcpu);
-       return r;
-}
-
-static inline void kvmppc_set_pc(struct kvm_vcpu *vcpu, ulong val)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       svcpu->pc = val;
-       svcpu_put(svcpu);
-}
-
-static inline ulong kvmppc_get_pc(struct kvm_vcpu *vcpu)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       ulong r;
-       r = svcpu->pc;
-       svcpu_put(svcpu);
-       return r;
-}
-
-static inline u32 kvmppc_get_last_inst(struct kvm_vcpu *vcpu)
-{
-       ulong pc = kvmppc_get_pc(vcpu);
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       u32 r;
-
-       /* Load the instruction manually if it failed to do so in the
-        * exit path */
-       if (svcpu->last_inst == KVM_INST_FETCH_FAILED)
-               kvmppc_ld(vcpu, &pc, sizeof(u32), &svcpu->last_inst, false);
-
-       r = svcpu->last_inst;
-       svcpu_put(svcpu);
-       return r;
-}
-
-/*
- * Like kvmppc_get_last_inst(), but for fetching a sc instruction.
- * Because the sc instruction sets SRR0 to point to the following
- * instruction, we have to fetch from pc - 4.
- */
-static inline u32 kvmppc_get_last_sc(struct kvm_vcpu *vcpu)
-{
-       ulong pc = kvmppc_get_pc(vcpu) - 4;
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       u32 r;
-
-       /* Load the instruction manually if it failed to do so in the
-        * exit path */
-       if (svcpu->last_inst == KVM_INST_FETCH_FAILED)
-               kvmppc_ld(vcpu, &pc, sizeof(u32), &svcpu->last_inst, false);
-
-       r = svcpu->last_inst;
-       svcpu_put(svcpu);
-       return r;
-}
-
-static inline ulong kvmppc_get_fault_dar(struct kvm_vcpu *vcpu)
-{
-       struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       ulong r;
-       r = svcpu->fault_dar;
-       svcpu_put(svcpu);
-       return r;
-}
-
-static inline bool kvmppc_critical_section(struct kvm_vcpu *vcpu)
-{
-       ulong crit_raw = vcpu->arch.shared->critical;
-       ulong crit_r1 = kvmppc_get_gpr(vcpu, 1);
-       bool crit;
-
-       /* Truncate crit indicators in 32 bit mode */
-       if (!(vcpu->arch.shared->msr & MSR_SF)) {
-               crit_raw &= 0xffffffff;
-               crit_r1 &= 0xffffffff;
-       }
-
-       /* Critical section when crit == r1 */
-       crit = (crit_raw == crit_r1);
-       /* ... and we're in supervisor mode */
-       crit = crit && !(vcpu->arch.shared->msr & MSR_PR);
-
-       return crit;
-}
-#else /* CONFIG_KVM_BOOK3S_PR */
-
-static inline unsigned long kvmppc_interrupt_offset(struct kvm_vcpu *vcpu)
-{
-       return 0;
-}
-
-static inline void kvmppc_update_int_pending(struct kvm_vcpu *vcpu,
-                       unsigned long pending_now, unsigned long old_pending)
-{
-}
-
 static inline void kvmppc_set_gpr(struct kvm_vcpu *vcpu, int num, ulong val)
 {
        vcpu->arch.gpr[num] = val;
@@ -489,12 +301,6 @@ static inline ulong kvmppc_get_fault_dar(struct kvm_vcpu *vcpu)
        return vcpu->arch.fault_dar;
 }
 
-static inline bool kvmppc_critical_section(struct kvm_vcpu *vcpu)
-{
-       return false;
-}
-#endif
-
 /* Magic register values loaded into r3 and r4 before the 'sc' assembly
  * instruction for the OSI hypercalls */
 #define OSI_SC_MAGIC_R3                        0x113724FA
index ce0ef6c..c720e0b 100644 (file)
@@ -22,7 +22,7 @@
 
 static inline struct kvmppc_book3s_shadow_vcpu *svcpu_get(struct kvm_vcpu *vcpu)
 {
-       return to_book3s(vcpu)->shadow_vcpu;
+       return vcpu->arch.shadow_vcpu;
 }
 
 static inline void svcpu_put(struct kvmppc_book3s_shadow_vcpu *svcpu)
index 86d638a..bf0fa8b 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef __ASM_KVM_BOOK3S_64_H__
 #define __ASM_KVM_BOOK3S_64_H__
 
-#ifdef CONFIG_KVM_BOOK3S_PR
+#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
 static inline struct kvmppc_book3s_shadow_vcpu *svcpu_get(struct kvm_vcpu *vcpu)
 {
        preempt_disable();
@@ -35,7 +35,7 @@ static inline void svcpu_put(struct kvmppc_book3s_shadow_vcpu *svcpu)
 
 #define SPAPR_TCE_SHIFT                12
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
 #define KVM_DEFAULT_HPT_ORDER  24      /* 16MB HPT by default */
 extern unsigned long kvm_rma_pages;
 #endif
@@ -278,7 +278,7 @@ static inline int is_vrma_hpte(unsigned long hpte_v)
                (HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)));
 }
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
 /*
  * Note modification of an HPTE; set the HPTE modified bit
  * if anyone is interested.
@@ -289,6 +289,6 @@ static inline void note_hpte_modification(struct kvm *kvm,
        if (atomic_read(&kvm->arch.hpte_mod_interest))
                rev->guest_rpte |= HPTE_GR_MODIFIED;
 }
-#endif /* CONFIG_KVM_BOOK3S_64_HV */
+#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
 
 #endif /* __ASM_KVM_BOOK3S_64_H__ */
index 9039d3c..0bd9348 100644 (file)
@@ -83,7 +83,7 @@ struct kvmppc_host_state {
        u8 restore_hid5;
        u8 napping;
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        u8 hwthread_req;
        u8 hwthread_state;
        u8 host_ipi;
@@ -101,6 +101,7 @@ struct kvmppc_host_state {
 #endif
 #ifdef CONFIG_PPC_BOOK3S_64
        u64 cfar;
+       u64 ppr;
 #endif
 };
 
@@ -108,14 +109,14 @@ struct kvmppc_book3s_shadow_vcpu {
        ulong gpr[14];
        u32 cr;
        u32 xer;
-
-       u32 fault_dsisr;
-       u32 last_inst;
        ulong ctr;
        ulong lr;
        ulong pc;
+
        ulong shadow_srr1;
        ulong fault_dar;
+       u32 fault_dsisr;
+       u32 last_inst;
 
 #ifdef CONFIG_PPC_BOOK3S_32
        u32     sr[16];                 /* Guest SRs */
index d3c1eb3..dd8f615 100644 (file)
 /* LPIDs we support with this build -- runtime limit may be lower */
 #define KVMPPC_NR_LPIDS                        64
 
-#define KVMPPC_INST_EHPRIV     0x7c00021c
+#define KVMPPC_INST_EHPRIV             0x7c00021c
+#define EHPRIV_OC_SHIFT                        11
+/* "ehpriv 1" : ehpriv with OC = 1 is used for debug emulation */
+#define EHPRIV_OC_DEBUG                        1
+#define KVMPPC_INST_EHPRIV_DEBUG       (KVMPPC_INST_EHPRIV | \
+                                        (EHPRIV_OC_DEBUG << EHPRIV_OC_SHIFT))
 
 static inline void kvmppc_set_gpr(struct kvm_vcpu *vcpu, int num, ulong val)
 {
index 3328353..237d1d2 100644 (file)
@@ -63,20 +63,17 @@ extern void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte);
 
 #endif
 
-/* We don't currently support large pages. */
-#define KVM_HPAGE_GFN_SHIFT(x) 0
-#define KVM_NR_PAGE_SIZES      1
-#define KVM_PAGES_PER_HPAGE(x) (1UL<<31)
-
 #define HPTEG_CACHE_NUM                        (1 << 15)
 #define HPTEG_HASH_BITS_PTE            13
 #define HPTEG_HASH_BITS_PTE_LONG       12
 #define HPTEG_HASH_BITS_VPTE           13
 #define HPTEG_HASH_BITS_VPTE_LONG      5
+#define HPTEG_HASH_BITS_VPTE_64K       11
 #define HPTEG_HASH_NUM_PTE             (1 << HPTEG_HASH_BITS_PTE)
 #define HPTEG_HASH_NUM_PTE_LONG                (1 << HPTEG_HASH_BITS_PTE_LONG)
 #define HPTEG_HASH_NUM_VPTE            (1 << HPTEG_HASH_BITS_VPTE)
 #define HPTEG_HASH_NUM_VPTE_LONG       (1 << HPTEG_HASH_BITS_VPTE_LONG)
+#define HPTEG_HASH_NUM_VPTE_64K                (1 << HPTEG_HASH_BITS_VPTE_64K)
 
 /* Physical Address Mask - allowed range of real mode RAM access */
 #define KVM_PAM                        0x0fffffffffffffffULL
@@ -89,6 +86,9 @@ struct lppaca;
 struct slb_shadow;
 struct dtl_entry;
 
+struct kvmppc_vcpu_book3s;
+struct kvmppc_book3s_shadow_vcpu;
+
 struct kvm_vm_stat {
        u32 remote_tlb_flush;
 };
@@ -224,15 +224,15 @@ struct revmap_entry {
 #define KVMPPC_GOT_PAGE                0x80
 
 struct kvm_arch_memory_slot {
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        unsigned long *rmap;
        unsigned long *slot_phys;
-#endif /* CONFIG_KVM_BOOK3S_64_HV */
+#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
 };
 
 struct kvm_arch {
        unsigned int lpid;
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        unsigned long hpt_virt;
        struct revmap_entry *revmap;
        unsigned int host_lpid;
@@ -256,7 +256,10 @@ struct kvm_arch {
        cpumask_t need_tlb_flush;
        struct kvmppc_vcore *vcores[KVM_MAX_VCORES];
        int hpt_cma_alloc;
-#endif /* CONFIG_KVM_BOOK3S_64_HV */
+#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
+#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
+       struct mutex hpt_mutex;
+#endif
 #ifdef CONFIG_PPC_BOOK3S_64
        struct list_head spapr_tce_tables;
        struct list_head rtas_tokens;
@@ -267,6 +270,7 @@ struct kvm_arch {
 #ifdef CONFIG_KVM_XICS
        struct kvmppc_xics *xics;
 #endif
+       struct kvmppc_ops *kvm_ops;
 };
 
 /*
@@ -294,6 +298,10 @@ struct kvmppc_vcore {
        u64 stolen_tb;
        u64 preempt_tb;
        struct kvm_vcpu *runner;
+       u64 tb_offset;          /* guest timebase - host timebase */
+       ulong lpcr;
+       u32 arch_compat;
+       ulong pcr;
 };
 
 #define VCORE_ENTRY_COUNT(vc)  ((vc)->entry_exit_count & 0xff)
@@ -328,6 +336,7 @@ struct kvmppc_pte {
        bool may_read           : 1;
        bool may_write          : 1;
        bool may_execute        : 1;
+       u8 page_size;           /* MMU_PAGE_xxx */
 };
 
 struct kvmppc_mmu {
@@ -340,7 +349,8 @@ struct kvmppc_mmu {
        /* book3s */
        void (*mtsrin)(struct kvm_vcpu *vcpu, u32 srnum, ulong value);
        u32  (*mfsrin)(struct kvm_vcpu *vcpu, u32 srnum);
-       int  (*xlate)(struct kvm_vcpu *vcpu, gva_t eaddr, struct kvmppc_pte *pte, bool data);
+       int  (*xlate)(struct kvm_vcpu *vcpu, gva_t eaddr,
+                     struct kvmppc_pte *pte, bool data, bool iswrite);
        void (*reset_msr)(struct kvm_vcpu *vcpu);
        void (*tlbie)(struct kvm_vcpu *vcpu, ulong addr, bool large);
        int  (*esid_to_vsid)(struct kvm_vcpu *vcpu, ulong esid, u64 *vsid);
@@ -360,6 +370,7 @@ struct kvmppc_slb {
        bool large      : 1;    /* PTEs are 16MB */
        bool tb         : 1;    /* 1TB segment */
        bool class      : 1;
+       u8 base_page_size;      /* MMU_PAGE_xxx */
 };
 
 # ifdef CONFIG_PPC_FSL_BOOK3E
@@ -377,17 +388,6 @@ struct kvmppc_slb {
 #define KVMPPC_EPR_USER                1 /* exit to userspace to fill EPR */
 #define KVMPPC_EPR_KERNEL      2 /* in-kernel irqchip */
 
-struct kvmppc_booke_debug_reg {
-       u32 dbcr0;
-       u32 dbcr1;
-       u32 dbcr2;
-#ifdef CONFIG_KVM_E500MC
-       u32 dbcr4;
-#endif
-       u64 iac[KVMPPC_BOOKE_MAX_IAC];
-       u64 dac[KVMPPC_BOOKE_MAX_DAC];
-};
-
 #define KVMPPC_IRQ_DEFAULT     0
 #define KVMPPC_IRQ_MPIC                1
 #define KVMPPC_IRQ_XICS                2
@@ -402,6 +402,10 @@ struct kvm_vcpu_arch {
        int slb_max;            /* 1 + index of last valid entry in slb[] */
        int slb_nr;             /* total number of entries in SLB */
        struct kvmppc_mmu mmu;
+       struct kvmppc_vcpu_book3s *book3s;
+#endif
+#ifdef CONFIG_PPC_BOOK3S_32
+       struct kvmppc_book3s_shadow_vcpu *shadow_vcpu;
 #endif
 
        ulong gpr[32];
@@ -463,6 +467,8 @@ struct kvm_vcpu_arch {
        u32 ctrl;
        ulong dabr;
        ulong cfar;
+       ulong ppr;
+       ulong shadow_srr1;
 #endif
        u32 vrsave; /* also USPRG0 */
        u32 mmucr;
@@ -498,6 +504,8 @@ struct kvm_vcpu_arch {
 
        u64 mmcr[3];
        u32 pmc[8];
+       u64 siar;
+       u64 sdar;
 
 #ifdef CONFIG_KVM_EXIT_TIMING
        struct mutex exit_timing_lock;
@@ -531,7 +539,10 @@ struct kvm_vcpu_arch {
        u32 eptcfg;
        u32 epr;
        u32 crit_save;
-       struct kvmppc_booke_debug_reg dbg_reg;
+       /* guest debug registers*/
+       struct debug_reg dbg_reg;
+       /* hardware visible debug registers when in guest state */
+       struct debug_reg shadow_dbg_reg;
 #endif
        gpa_t paddr_accessed;
        gva_t vaddr_accessed;
@@ -582,7 +593,7 @@ struct kvm_vcpu_arch {
        struct kvmppc_icp *icp; /* XICS presentation controller */
 #endif
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        struct kvm_vcpu_arch_shared shregs;
 
        unsigned long pgfault_addr;
index b15554a..c8317fb 100644 (file)
@@ -106,13 +106,6 @@ extern void kvmppc_core_queue_external(struct kvm_vcpu *vcpu,
                                        struct kvm_interrupt *irq);
 extern void kvmppc_core_dequeue_external(struct kvm_vcpu *vcpu);
 extern void kvmppc_core_flush_tlb(struct kvm_vcpu *vcpu);
-
-extern int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
-                                  unsigned int op, int *advance);
-extern int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn,
-                                    ulong val);
-extern int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn,
-                                    ulong *val);
 extern int kvmppc_core_check_requests(struct kvm_vcpu *vcpu);
 
 extern int kvmppc_booke_init(void);
@@ -135,17 +128,17 @@ extern long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
                                struct kvm_create_spapr_tce *args);
 extern long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
                             unsigned long ioba, unsigned long tce);
-extern long kvm_vm_ioctl_allocate_rma(struct kvm *kvm,
-                               struct kvm_allocate_rma *rma);
 extern struct kvm_rma_info *kvm_alloc_rma(void);
 extern void kvm_release_rma(struct kvm_rma_info *ri);
 extern struct page *kvm_alloc_hpt(unsigned long nr_pages);
 extern void kvm_release_hpt(struct page *page, unsigned long nr_pages);
 extern int kvmppc_core_init_vm(struct kvm *kvm);
 extern void kvmppc_core_destroy_vm(struct kvm *kvm);
-extern void kvmppc_core_free_memslot(struct kvm_memory_slot *free,
+extern void kvmppc_core_free_memslot(struct kvm *kvm,
+                                    struct kvm_memory_slot *free,
                                     struct kvm_memory_slot *dont);
-extern int kvmppc_core_create_memslot(struct kvm_memory_slot *slot,
+extern int kvmppc_core_create_memslot(struct kvm *kvm,
+                                     struct kvm_memory_slot *slot,
                                      unsigned long npages);
 extern int kvmppc_core_prepare_memory_region(struct kvm *kvm,
                                struct kvm_memory_slot *memslot,
@@ -177,6 +170,72 @@ extern int kvmppc_xics_get_xive(struct kvm *kvm, u32 irq, u32 *server,
 extern int kvmppc_xics_int_on(struct kvm *kvm, u32 irq);
 extern int kvmppc_xics_int_off(struct kvm *kvm, u32 irq);
 
+union kvmppc_one_reg {
+       u32     wval;
+       u64     dval;
+       vector128 vval;
+       u64     vsxval[2];
+       struct {
+               u64     addr;
+               u64     length;
+       }       vpaval;
+};
+
+struct kvmppc_ops {
+       struct module *owner;
+       int (*get_sregs)(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs);
+       int (*set_sregs)(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs);
+       int (*get_one_reg)(struct kvm_vcpu *vcpu, u64 id,
+                          union kvmppc_one_reg *val);
+       int (*set_one_reg)(struct kvm_vcpu *vcpu, u64 id,
+                          union kvmppc_one_reg *val);
+       void (*vcpu_load)(struct kvm_vcpu *vcpu, int cpu);
+       void (*vcpu_put)(struct kvm_vcpu *vcpu);
+       void (*set_msr)(struct kvm_vcpu *vcpu, u64 msr);
+       int (*vcpu_run)(struct kvm_run *run, struct kvm_vcpu *vcpu);
+       struct kvm_vcpu *(*vcpu_create)(struct kvm *kvm, unsigned int id);
+       void (*vcpu_free)(struct kvm_vcpu *vcpu);
+       int (*check_requests)(struct kvm_vcpu *vcpu);
+       int (*get_dirty_log)(struct kvm *kvm, struct kvm_dirty_log *log);
+       void (*flush_memslot)(struct kvm *kvm, struct kvm_memory_slot *memslot);
+       int (*prepare_memory_region)(struct kvm *kvm,
+                                    struct kvm_memory_slot *memslot,
+                                    struct kvm_userspace_memory_region *mem);
+       void (*commit_memory_region)(struct kvm *kvm,
+                                    struct kvm_userspace_memory_region *mem,
+                                    const struct kvm_memory_slot *old);
+       int (*unmap_hva)(struct kvm *kvm, unsigned long hva);
+       int (*unmap_hva_range)(struct kvm *kvm, unsigned long start,
+                          unsigned long end);
+       int (*age_hva)(struct kvm *kvm, unsigned long hva);
+       int (*test_age_hva)(struct kvm *kvm, unsigned long hva);
+       void (*set_spte_hva)(struct kvm *kvm, unsigned long hva, pte_t pte);
+       void (*mmu_destroy)(struct kvm_vcpu *vcpu);
+       void (*free_memslot)(struct kvm_memory_slot *free,
+                            struct kvm_memory_slot *dont);
+       int (*create_memslot)(struct kvm_memory_slot *slot,
+                             unsigned long npages);
+       int (*init_vm)(struct kvm *kvm);
+       void (*destroy_vm)(struct kvm *kvm);
+       int (*get_smmu_info)(struct kvm *kvm, struct kvm_ppc_smmu_info *info);
+       int (*emulate_op)(struct kvm_run *run, struct kvm_vcpu *vcpu,
+                         unsigned int inst, int *advance);
+       int (*emulate_mtspr)(struct kvm_vcpu *vcpu, int sprn, ulong spr_val);
+       int (*emulate_mfspr)(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val);
+       void (*fast_vcpu_kick)(struct kvm_vcpu *vcpu);
+       long (*arch_vm_ioctl)(struct file *filp, unsigned int ioctl,
+                             unsigned long arg);
+
+};
+
+extern struct kvmppc_ops *kvmppc_hv_ops;
+extern struct kvmppc_ops *kvmppc_pr_ops;
+
+static inline bool is_kvmppc_hv_enabled(struct kvm *kvm)
+{
+       return kvm->arch.kvm_ops == kvmppc_hv_ops;
+}
+
 /*
  * Cuts out inst bits with ordering according to spec.
  * That means the leftmost bit is zero. All given bits are included.
@@ -210,17 +269,6 @@ static inline u32 kvmppc_set_field(u64 inst, int msb, int lsb, int value)
        return r;
 }
 
-union kvmppc_one_reg {
-       u32     wval;
-       u64     dval;
-       vector128 vval;
-       u64     vsxval[2];
-       struct {
-               u64     addr;
-               u64     length;
-       }       vpaval;
-};
-
 #define one_reg_size(id)       \
        (1ul << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
 
@@ -245,10 +293,10 @@ union kvmppc_one_reg {
        __v;                                    \
 })
 
-void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs);
+int kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs);
 int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs);
 
-void kvmppc_get_sregs_ivor(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs);
+int kvmppc_get_sregs_ivor(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs);
 int kvmppc_set_sregs_ivor(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs);
 
 int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg);
@@ -260,7 +308,7 @@ void kvmppc_set_pid(struct kvm_vcpu *vcpu, u32 pid);
 
 struct openpic;
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
 extern void kvm_cma_reserve(void) __init;
 static inline void kvmppc_set_xics_phys(int cpu, unsigned long addr)
 {
@@ -269,10 +317,10 @@ static inline void kvmppc_set_xics_phys(int cpu, unsigned long addr)
 
 static inline u32 kvmppc_get_xics_latch(void)
 {
-       u32 xirr = get_paca()->kvm_hstate.saved_xirr;
+       u32 xirr;
 
+       xirr = get_paca()->kvm_hstate.saved_xirr;
        get_paca()->kvm_hstate.saved_xirr = 0;
-
        return xirr;
 }
 
@@ -281,7 +329,10 @@ static inline void kvmppc_set_host_ipi(int cpu, u8 host_ipi)
        paca[cpu].kvm_hstate.host_ipi = host_ipi;
 }
 
-extern void kvmppc_fast_vcpu_kick(struct kvm_vcpu *vcpu);
+static inline void kvmppc_fast_vcpu_kick(struct kvm_vcpu *vcpu)
+{
+       vcpu->kvm->arch.kvm_ops->fast_vcpu_kick(vcpu);
+}
 
 #else
 static inline void __init kvm_cma_reserve(void)
index a5954ce..b6ea9e0 100644 (file)
@@ -166,7 +166,7 @@ struct paca_struct {
        struct dtl_entry *dtl_curr;     /* pointer corresponding to dtl_ridx */
 
 #ifdef CONFIG_KVM_BOOK3S_HANDLER
-#ifdef CONFIG_KVM_BOOK3S_PR
+#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
        /* We use this to store guest state in */
        struct kvmppc_book3s_shadow_vcpu shadow_vcpu;
 #endif
index f65e27b..16cb92d 100644 (file)
@@ -91,7 +91,10 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
        if (!pte)
                return NULL;
        page = virt_to_page(pte);
-       pgtable_page_ctor(page);
+       if (!pgtable_page_ctor(page)) {
+               __free_page(page);
+               return NULL;
+       }
        return page;
 }
 
index a63b045..12c32c5 100644 (file)
@@ -287,6 +287,32 @@ static inline long disable_reloc_on_exceptions(void) {
        return plpar_set_mode(0, 3, 0, 0);
 }
 
+/*
+ * Take exceptions in big endian mode on this partition
+ *
+ * Note: this call has a partition wide scope and can take a while to complete.
+ * If it returns H_LONG_BUSY_* it should be retried periodically until it
+ * returns H_SUCCESS.
+ */
+static inline long enable_big_endian_exceptions(void)
+{
+       /* mflags = 0: big endian exceptions */
+       return plpar_set_mode(0, 4, 0, 0);
+}
+
+/*
+ * Take exceptions in little endian mode on this partition
+ *
+ * Note: this call has a partition wide scope and can take a while to complete.
+ * If it returns H_LONG_BUSY_* it should be retried periodically until it
+ * returns H_SUCCESS.
+ */
+static inline long enable_little_endian_exceptions(void)
+{
+       /* mflags = 1: little endian exceptions */
+       return plpar_set_mode(1, 4, 0, 0);
+}
+
 static inline long plapr_set_ciabr(unsigned long ciabr)
 {
        return plpar_set_mode(0, 1, ciabr, 0);
index 7794b2b..fc14a38 100644 (file)
@@ -208,6 +208,7 @@ struct debug_reg {
 
 struct thread_struct {
        unsigned long   ksp;            /* Kernel stack pointer */
+
 #ifdef CONFIG_PPC64
        unsigned long   ksp_vsid;
 #endif
@@ -221,6 +222,7 @@ struct thread_struct {
        void            *pgdir;         /* root of page-table tree */
        unsigned long   ksp_limit;      /* if ksp <= ksp_limit stack overflow */
 #endif
+       /* Debug Registers */
        struct debug_reg debug;
        struct thread_fp_state  fp_state;
        struct thread_fp_state  *fp_save_area;
index 0156702..576ad88 100644 (file)
@@ -40,7 +40,7 @@
 #define _PAGE_U1       0x010000
 #define _PAGE_U0       0x020000
 #define _PAGE_ACCESSED 0x040000
-#define _PAGE_LENDIAN  0x080000
+#define _PAGE_ENDIAN   0x080000
 #define _PAGE_GUARDED  0x100000
 #define _PAGE_COHERENT 0x200000 /* M: enforce memory coherence */
 #define _PAGE_NO_CACHE 0x400000 /* I: cache inhibit */
index 126f6e9..5c45787 100644 (file)
 #define SPRN_TBRU      0x10D   /* Time Base Read Upper Register (user, R/O) */
 #define SPRN_TBWL      0x11C   /* Time Base Lower Register (super, R/W) */
 #define SPRN_TBWU      0x11D   /* Time Base Upper Register (super, R/W) */
+#define SPRN_TBU40     0x11E   /* Timebase upper 40 bits (hyper, R/W) */
 #define SPRN_SPURR     0x134   /* Scaled PURR */
 #define SPRN_HSPRG0    0x130   /* Hypervisor Scratch 0 */
 #define SPRN_HSPRG1    0x131   /* Hypervisor Scratch 1 */
 #define   LPCR_ISL     (1ul << (63-2))
 #define   LPCR_VC_SH   (63-2)
 #define   LPCR_DPFD_SH (63-11)
+#define   LPCR_DPFD    (7ul << LPCR_DPFD_SH)
 #define   LPCR_VRMASD  (0x1ful << (63-16))
 #define   LPCR_VRMA_L  (1ul << (63-12))
 #define   LPCR_VRMA_LP0        (1ul << (63-15))
 #define     LPCR_PECE2 0x00001000      /* machine check etc can cause exit */
 #define   LPCR_MER     0x00000800      /* Mediated External Exception */
 #define   LPCR_MER_SH  11
+#define   LPCR_TC      0x00000200      /* Translation control */
 #define   LPCR_LPES    0x0000000c
 #define   LPCR_LPES0   0x00000008      /* LPAR Env selector 0 */
 #define   LPCR_LPES1   0x00000004      /* LPAR Env selector 1 */
 #define   LPID_RSVD    0x3ff           /* Reserved LPID for partn switching */
 #define        SPRN_HMER       0x150   /* Hardware m? error recovery */
 #define        SPRN_HMEER      0x151   /* Hardware m? enable error recovery */
+#define SPRN_PCR       0x152   /* Processor compatibility register */
+#define   PCR_VEC_DIS  (1ul << (63-0)) /* Vec. disable (bit NA since POWER8) */
+#define   PCR_VSX_DIS  (1ul << (63-1)) /* VSX disable (bit NA since POWER8) */
+#define   PCR_ARCH_205 0x2             /* Architecture 2.05 */
 #define        SPRN_HEIR       0x153   /* Hypervisor Emulated Instruction Register */
 #define SPRN_TLBINDEXR 0x154   /* P7 TLB control register */
 #define SPRN_TLBVPNR   0x155   /* P7 TLB control register */
 #define         HID4_RMLS2_SH   (63 - 2)       /* Real mode limit bottom 2 bits */
 #define         HID4_LPID5_SH   (63 - 6)       /* partition ID bottom 4 bits */
 #define         HID4_RMOR_SH    (63 - 22)      /* real mode offset (16 bits) */
+#define  HID4_RMOR      (0xFFFFul << HID4_RMOR_SH)
 #define  HID4_LPES1     (1 << (63-57)) /* LPAR env. sel. bit 1 */
 #define  HID4_RMLS0_SH  (63 - 58)      /* Real mode limit top bit */
 #define         HID4_LPID1_SH   0              /* partition ID top 2 bits */
 #define PVR_BE         0x0070
 #define PVR_PA6T       0x0090
 
+/* "Logical" PVR values defined in PAPR, representing architecture levels */
+#define PVR_ARCH_204   0x0f000001
+#define PVR_ARCH_205   0x0f000002
+#define PVR_ARCH_206   0x0f000003
+#define PVR_ARCH_206p  0x0f100003
+#define PVR_ARCH_207   0x0f000004
+
 /* Macros for setting and retrieving special purpose registers */
 #ifndef __ASSEMBLY__
 #define mfmsr()                ({unsigned long rval; \
index 98da78e..084e080 100644 (file)
@@ -33,6 +33,7 @@ extern int boot_cpuid;
 extern int spinning_secondaries;
 
 extern void cpu_die(void);
+extern int cpu_to_chip_id(int cpu);
 
 #ifdef CONFIG_SMP
 
@@ -112,7 +113,6 @@ static inline struct cpumask *cpu_core_mask(int cpu)
 }
 
 extern int cpu_to_core_id(int cpu);
-extern int cpu_to_chip_id(int cpu);
 
 /* Since OpenPIC has only 4 IPIs, we use slightly different message numbers.
  *
index ba7b197..9854c56 100644 (file)
@@ -82,8 +82,6 @@ static inline struct thread_info *current_thread_info(void)
 
 #endif /* __ASSEMBLY__ */
 
-#define PREEMPT_ACTIVE         0x10000000
-
 /*
  * thread information flag bit numbers
  */
@@ -107,6 +105,9 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_EMULATE_STACK_STORE        16      /* Is an instruction emulation
                                                for stack store? */
 #define TIF_MEMDIE             17      /* is terminating due to OOM killer */
+#if defined(CONFIG_PPC64)
+#define TIF_ELF2ABI            18      /* function descriptors must die! */
+#endif
 
 /* as above, but as bit values */
 #define _TIF_SYSCALL_TRACE     (1<<TIF_SYSCALL_TRACE)
@@ -185,6 +186,12 @@ static inline bool test_thread_local_flags(unsigned int flags)
 #define is_32bit_task()        (1)
 #endif
 
+#if defined(CONFIG_PPC64)
+#define is_elf2_task() (test_thread_flag(TIF_ELF2ABI))
+#else
+#define is_elf2_task() (0)
+#endif
+
 #endif /* !__ASSEMBLY__ */
 
 #endif /* __KERNEL__ */
index 0fb1a6e..6836ec7 100644 (file)
@@ -27,6 +27,7 @@
 #define __KVM_HAVE_PPC_SMT
 #define __KVM_HAVE_IRQCHIP
 #define __KVM_HAVE_IRQ_LINE
+#define __KVM_HAVE_GUEST_DEBUG
 
 struct kvm_regs {
        __u64 pc;
@@ -269,7 +270,24 @@ struct kvm_fpu {
        __u64 fpr[32];
 };
 
+/*
+ * Defines for h/w breakpoint, watchpoint (read, write or both) and
+ * software breakpoint.
+ * These are used as "type" in KVM_SET_GUEST_DEBUG ioctl and "status"
+ * for KVM_DEBUG_EXIT.
+ */
+#define KVMPPC_DEBUG_NONE              0x0
+#define KVMPPC_DEBUG_BREAKPOINT                (1UL << 1)
+#define KVMPPC_DEBUG_WATCH_WRITE       (1UL << 2)
+#define KVMPPC_DEBUG_WATCH_READ                (1UL << 3)
 struct kvm_debug_exit_arch {
+       __u64 address;
+       /*
+        * exiting to userspace because of h/w breakpoint, watchpoint
+        * (read, write or both) and software breakpoint.
+        */
+       __u32 status;
+       __u32 reserved;
 };
 
 /* for KVM_SET_GUEST_DEBUG */
@@ -281,10 +299,6 @@ struct kvm_guest_debug_arch {
                 * Type denotes h/w breakpoint, read watchpoint, write
                 * watchpoint or watchpoint (both read and write).
                 */
-#define KVMPPC_DEBUG_NONE              0x0
-#define KVMPPC_DEBUG_BREAKPOINT                (1UL << 1)
-#define KVMPPC_DEBUG_WATCH_WRITE       (1UL << 2)
-#define KVMPPC_DEBUG_WATCH_READ                (1UL << 3)
                __u32 type;
                __u32 reserved;
        } bp[16];
@@ -429,6 +443,11 @@ struct kvm_get_htab_header {
 #define KVM_REG_PPC_MMCR0      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x10)
 #define KVM_REG_PPC_MMCR1      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x11)
 #define KVM_REG_PPC_MMCRA      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x12)
+#define KVM_REG_PPC_MMCR2      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x13)
+#define KVM_REG_PPC_MMCRS      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x14)
+#define KVM_REG_PPC_SIAR       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x15)
+#define KVM_REG_PPC_SDAR       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x16)
+#define KVM_REG_PPC_SIER       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x17)
 
 #define KVM_REG_PPC_PMC1       (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x18)
 #define KVM_REG_PPC_PMC2       (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x19)
@@ -499,6 +518,65 @@ struct kvm_get_htab_header {
 #define KVM_REG_PPC_TLB3PS     (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x9a)
 #define KVM_REG_PPC_EPTCFG     (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x9b)
 
+/* Timebase offset */
+#define KVM_REG_PPC_TB_OFFSET  (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x9c)
+
+/* POWER8 registers */
+#define KVM_REG_PPC_SPMC1      (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x9d)
+#define KVM_REG_PPC_SPMC2      (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x9e)
+#define KVM_REG_PPC_IAMR       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x9f)
+#define KVM_REG_PPC_TFHAR      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa0)
+#define KVM_REG_PPC_TFIAR      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa1)
+#define KVM_REG_PPC_TEXASR     (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa2)
+#define KVM_REG_PPC_FSCR       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa3)
+#define KVM_REG_PPC_PSPB       (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xa4)
+#define KVM_REG_PPC_EBBHR      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa5)
+#define KVM_REG_PPC_EBBRR      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa6)
+#define KVM_REG_PPC_BESCR      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa7)
+#define KVM_REG_PPC_TAR                (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa8)
+#define KVM_REG_PPC_DPDES      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa9)
+#define KVM_REG_PPC_DAWR       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xaa)
+#define KVM_REG_PPC_DAWRX      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xab)
+#define KVM_REG_PPC_CIABR      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xac)
+#define KVM_REG_PPC_IC         (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xad)
+#define KVM_REG_PPC_VTB                (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xae)
+#define KVM_REG_PPC_CSIGR      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xaf)
+#define KVM_REG_PPC_TACR       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb0)
+#define KVM_REG_PPC_TCSCR      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb1)
+#define KVM_REG_PPC_PID                (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb2)
+#define KVM_REG_PPC_ACOP       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb3)
+
+#define KVM_REG_PPC_VRSAVE     (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb4)
+#define KVM_REG_PPC_LPCR       (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb5)
+#define KVM_REG_PPC_PPR                (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb6)
+
+/* Architecture compatibility level */
+#define KVM_REG_PPC_ARCH_COMPAT        (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb7)
+
+/* Transactional Memory checkpointed state:
+ * This is all GPRs, all VSX regs and a subset of SPRs
+ */
+#define KVM_REG_PPC_TM         (KVM_REG_PPC | 0x80000000)
+/* TM GPRs */
+#define KVM_REG_PPC_TM_GPR0    (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0)
+#define KVM_REG_PPC_TM_GPR(n)  (KVM_REG_PPC_TM_GPR0 + (n))
+#define KVM_REG_PPC_TM_GPR31   (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x1f)
+/* TM VSX */
+#define KVM_REG_PPC_TM_VSR0    (KVM_REG_PPC_TM | KVM_REG_SIZE_U128 | 0x20)
+#define KVM_REG_PPC_TM_VSR(n)  (KVM_REG_PPC_TM_VSR0 + (n))
+#define KVM_REG_PPC_TM_VSR63   (KVM_REG_PPC_TM | KVM_REG_SIZE_U128 | 0x5f)
+/* TM SPRS */
+#define KVM_REG_PPC_TM_CR      (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x60)
+#define KVM_REG_PPC_TM_LR      (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x61)
+#define KVM_REG_PPC_TM_CTR     (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x62)
+#define KVM_REG_PPC_TM_FPSCR   (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x63)
+#define KVM_REG_PPC_TM_AMR     (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x64)
+#define KVM_REG_PPC_TM_PPR     (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x65)
+#define KVM_REG_PPC_TM_VRSAVE  (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x66)
+#define KVM_REG_PPC_TM_VSCR    (KVM_REG_PPC_TM | KVM_REG_SIZE_U32 | 0x67)
+#define KVM_REG_PPC_TM_DSCR    (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x68)
+#define KVM_REG_PPC_TM_TAR     (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x69)
+
 /* PPC64 eXternal Interrupt Controller Specification */
 #define KVM_DEV_XICS_GRP_SOURCES       1       /* 64-bit source attributes */
 
index e60a369..2ea5cc0 100644 (file)
@@ -439,7 +439,7 @@ int main(void)
        DEFINE(VCPU_LR, offsetof(struct kvm_vcpu, arch.lr));
        DEFINE(VCPU_CR, offsetof(struct kvm_vcpu, arch.cr));
        DEFINE(VCPU_PC, offsetof(struct kvm_vcpu, arch.pc));
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        DEFINE(VCPU_MSR, offsetof(struct kvm_vcpu, arch.shregs.msr));
        DEFINE(VCPU_SRR0, offsetof(struct kvm_vcpu, arch.shregs.srr0));
        DEFINE(VCPU_SRR1, offsetof(struct kvm_vcpu, arch.shregs.srr1));
@@ -470,7 +470,7 @@ int main(void)
        DEFINE(KVM_LPID, offsetof(struct kvm, arch.lpid));
 
        /* book3s */
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        DEFINE(KVM_SDR1, offsetof(struct kvm, arch.sdr1));
        DEFINE(KVM_HOST_LPID, offsetof(struct kvm, arch.host_lpid));
        DEFINE(KVM_HOST_LPCR, offsetof(struct kvm, arch.host_lpcr));
@@ -502,6 +502,8 @@ int main(void)
        DEFINE(VCPU_PRODDED, offsetof(struct kvm_vcpu, arch.prodded));
        DEFINE(VCPU_MMCR, offsetof(struct kvm_vcpu, arch.mmcr));
        DEFINE(VCPU_PMC, offsetof(struct kvm_vcpu, arch.pmc));
+       DEFINE(VCPU_SIAR, offsetof(struct kvm_vcpu, arch.siar));
+       DEFINE(VCPU_SDAR, offsetof(struct kvm_vcpu, arch.sdar));
        DEFINE(VCPU_SLB, offsetof(struct kvm_vcpu, arch.slb));
        DEFINE(VCPU_SLB_MAX, offsetof(struct kvm_vcpu, arch.slb_max));
        DEFINE(VCPU_SLB_NR, offsetof(struct kvm_vcpu, arch.slb_nr));
@@ -511,18 +513,22 @@ int main(void)
        DEFINE(VCPU_TRAP, offsetof(struct kvm_vcpu, arch.trap));
        DEFINE(VCPU_PTID, offsetof(struct kvm_vcpu, arch.ptid));
        DEFINE(VCPU_CFAR, offsetof(struct kvm_vcpu, arch.cfar));
+       DEFINE(VCPU_PPR, offsetof(struct kvm_vcpu, arch.ppr));
+       DEFINE(VCPU_SHADOW_SRR1, offsetof(struct kvm_vcpu, arch.shadow_srr1));
        DEFINE(VCORE_ENTRY_EXIT, offsetof(struct kvmppc_vcore, entry_exit_count));
        DEFINE(VCORE_NAP_COUNT, offsetof(struct kvmppc_vcore, nap_count));
        DEFINE(VCORE_IN_GUEST, offsetof(struct kvmppc_vcore, in_guest));
        DEFINE(VCORE_NAPPING_THREADS, offsetof(struct kvmppc_vcore, napping_threads));
-       DEFINE(VCPU_SVCPU, offsetof(struct kvmppc_vcpu_book3s, shadow_vcpu) -
-                          offsetof(struct kvmppc_vcpu_book3s, vcpu));
+       DEFINE(VCORE_TB_OFFSET, offsetof(struct kvmppc_vcore, tb_offset));
+       DEFINE(VCORE_LPCR, offsetof(struct kvmppc_vcore, lpcr));
+       DEFINE(VCORE_PCR, offsetof(struct kvmppc_vcore, pcr));
        DEFINE(VCPU_SLB_E, offsetof(struct kvmppc_slb, orige));
        DEFINE(VCPU_SLB_V, offsetof(struct kvmppc_slb, origv));
        DEFINE(VCPU_SLB_SIZE, sizeof(struct kvmppc_slb));
 
 #ifdef CONFIG_PPC_BOOK3S_64
-#ifdef CONFIG_KVM_BOOK3S_PR
+#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
+       DEFINE(PACA_SVCPU, offsetof(struct paca_struct, shadow_vcpu));
 # define SVCPU_FIELD(x, f)     DEFINE(x, offsetof(struct paca_struct, shadow_vcpu.f))
 #else
 # define SVCPU_FIELD(x, f)
@@ -574,7 +580,7 @@ int main(void)
        HSTATE_FIELD(HSTATE_RESTORE_HID5, restore_hid5);
        HSTATE_FIELD(HSTATE_NAPPING, napping);
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        HSTATE_FIELD(HSTATE_HWTHREAD_REQ, hwthread_req);
        HSTATE_FIELD(HSTATE_HWTHREAD_STATE, hwthread_state);
        HSTATE_FIELD(HSTATE_KVM_VCPU, kvm_vcpu);
@@ -590,10 +596,11 @@ int main(void)
        HSTATE_FIELD(HSTATE_DABR, dabr);
        HSTATE_FIELD(HSTATE_DECEXP, dec_expires);
        DEFINE(IPI_PRIORITY, IPI_PRIORITY);
-#endif /* CONFIG_KVM_BOOK3S_64_HV */
+#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
 
 #ifdef CONFIG_PPC_BOOK3S_64
        HSTATE_FIELD(HSTATE_CFAR, cfar);
+       HSTATE_FIELD(HSTATE_PPR, ppr);
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
 #else /* CONFIG_PPC_BOOK3S */
index 6713020..4bd687d 100644 (file)
@@ -686,6 +686,15 @@ void eeh_save_bars(struct eeh_dev *edev)
 
        for (i = 0; i < 16; i++)
                eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
+
+       /*
+        * For PCI bridges including root port, we need enable bus
+        * master explicitly. Otherwise, it can't fetch IODA table
+        * entries correctly. So we cache the bit in advance so that
+        * we can restore it after reset, either PHB range or PE range.
+        */
+       if (edev->mode & EEH_DEV_BRIDGE)
+               edev->config_space[1] |= PCI_COMMAND_MASTER;
 }
 
 /**
index d27c5af..72d748b 100644 (file)
@@ -74,8 +74,13 @@ static int eeh_event_handler(void * dummy)
                pe = event->pe;
                if (pe) {
                        eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
-                       pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n",
-                                pe->phb->global_number, pe->addr);
+                       if (pe->type & EEH_PE_PHB)
+                               pr_info("EEH: Detected error on PHB#%d\n",
+                                        pe->phb->global_number);
+                       else
+                               pr_info("EEH: Detected PCI bus error on "
+                                       "PHB#%d-PE#%x\n",
+                                       pe->phb->global_number, pe->addr);
                        eeh_handle_event(pe);
                        eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
                } else {
index 3a9ed6a..9f905e4 100644 (file)
@@ -126,7 +126,7 @@ BEGIN_FTR_SECTION
        bgt     cr1,.
        GET_PACA(r13)
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        li      r0,KVM_HWTHREAD_IN_KERNEL
        stb     r0,HSTATE_HWTHREAD_STATE(r13)
        /* Order setting hwthread_state vs. testing hwthread_req */
@@ -425,7 +425,7 @@ data_access_check_stab:
        mfspr   r9,SPRN_DSISR
        srdi    r10,r10,60
        rlwimi  r10,r9,16,0x20
-#ifdef CONFIG_KVM_BOOK3S_PR
+#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
        lbz     r9,HSTATE_IN_GUEST(r13)
        rlwimi  r10,r9,8,0x300
 #endif
@@ -650,6 +650,32 @@ slb_miss_user_pseries:
        b       .                               /* prevent spec. execution */
 #endif /* __DISABLED__ */
 
+#ifdef CONFIG_KVM_BOOK3S_64_HANDLER
+kvmppc_skip_interrupt:
+       /*
+        * Here all GPRs are unchanged from when the interrupt happened
+        * except for r13, which is saved in SPRG_SCRATCH0.
+        */
+       mfspr   r13, SPRN_SRR0
+       addi    r13, r13, 4
+       mtspr   SPRN_SRR0, r13
+       GET_SCRATCH0(r13)
+       rfid
+       b       .
+
+kvmppc_skip_Hinterrupt:
+       /*
+        * Here all GPRs are unchanged from when the interrupt happened
+        * except for r13, which is saved in SPRG_SCRATCH0.
+        */
+       mfspr   r13, SPRN_HSRR0
+       addi    r13, r13, 4
+       mtspr   SPRN_HSRR0, r13
+       GET_SCRATCH0(r13)
+       hrfid
+       b       .
+#endif
+
 /*
  * Code from here down to __end_handlers is invoked from the
  * exception prologs above.  Because the prologs assemble the
index e11863f..847e40e 100644 (file)
@@ -84,7 +84,7 @@ _GLOBAL(power7_nap)
        std     r9,_MSR(r1)
        std     r1,PACAR1(r13)
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        /* Tell KVM we're napping */
        li      r4,KVM_HWTHREAD_IN_NAP
        stb     r4,HSTATE_HWTHREAD_STATE(r13)
index 2156ea9..90fab64 100644 (file)
@@ -429,7 +429,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
        case KPROBE_HIT_SSDONE:
                /*
                 * We increment the nmissed count for accounting,
-                * we can also use npre/npostfault count for accouting
+                * we can also use npre/npostfault count for accounting
                 * these specific fault cases.
                 */
                kprobes_inc_nmissed_count(cur);
index 75c2d10..3386d8a 100644 (file)
@@ -858,17 +858,21 @@ void show_regs(struct pt_regs * regs)
        printk("MSR: "REG" ", regs->msr);
        printbits(regs->msr, msr_bits);
        printk("  CR: %08lx  XER: %08lx\n", regs->ccr, regs->xer);
-#ifdef CONFIG_PPC64
-       printk("SOFTE: %ld\n", regs->softe);
-#endif
        trap = TRAP(regs);
        if ((regs->trap != 0xc00) && cpu_has_feature(CPU_FTR_CFAR))
-               printk("CFAR: "REG"\n", regs->orig_gpr3);
-       if (trap == 0x300 || trap == 0x600)
+               printk("CFAR: "REG" ", regs->orig_gpr3);
+       if (trap == 0x200 || trap == 0x300 || trap == 0x600)
 #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
-               printk("DEAR: "REG", ESR: "REG"\n", regs->dar, regs->dsisr);
+               printk("DEAR: "REG" ESR: "REG" ", regs->dar, regs->dsisr);
 #else
-               printk("DAR: "REG", DSISR: %08lx\n", regs->dar, regs->dsisr);
+               printk("DAR: "REG" DSISR: %08lx ", regs->dar, regs->dsisr);
+#endif
+#ifdef CONFIG_PPC64
+       printk("SOFTE: %ld ", regs->softe);
+#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+       if (MSR_TM_ACTIVE(regs->msr))
+               printk("\nPACATMSCRATCH: %016llx ", get_paca()->tm_scratch);
 #endif
 
        for (i = 0;  i < 32;  i++) {
@@ -887,9 +891,6 @@ void show_regs(struct pt_regs * regs)
        printk("NIP ["REG"] %pS\n", regs->nip, (void *)regs->nip);
        printk("LR ["REG"] %pS\n", regs->link, (void *)regs->link);
 #endif
-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
-       printk("PACATMSCRATCH [%llx]\n", get_paca()->tm_scratch);
-#endif
        show_stack(current, (unsigned long *) regs->gpr[1]);
        if (!user_mode(regs))
                show_instructions(regs);
@@ -1086,25 +1087,45 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)
        regs->msr = MSR_USER;
 #else
        if (!is_32bit_task()) {
-               unsigned long entry, toc;
+               unsigned long entry;
 
-               /* start is a relocated pointer to the function descriptor for
-                * the elf _start routine.  The first entry in the function
-                * descriptor is the entry address of _start and the second
-                * entry is the TOC value we need to use.
-                */
-               __get_user(entry, (unsigned long __user *)start);
-               __get_user(toc, (unsigned long __user *)start+1);
+               if (is_elf2_task()) {
+                       /* Look ma, no function descriptors! */
+                       entry = start;
 
-               /* Check whether the e_entry function descriptor entries
-                * need to be relocated before we can use them.
-                */
-               if (load_addr != 0) {
-                       entry += load_addr;
-                       toc   += load_addr;
+                       /*
+                        * Ulrich says:
+                        *   The latest iteration of the ABI requires that when
+                        *   calling a function (at its global entry point),
+                        *   the caller must ensure r12 holds the entry point
+                        *   address (so that the function can quickly
+                        *   establish addressability).
+                        */
+                       regs->gpr[12] = start;
+                       /* Make sure that's restored on entry to userspace. */
+                       set_thread_flag(TIF_RESTOREALL);
+               } else {
+                       unsigned long toc;
+
+                       /* start is a relocated pointer to the function
+                        * descriptor for the elf _start routine.  The first
+                        * entry in the function descriptor is the entry
+                        * address of _start and the second entry is the TOC
+                        * value we need to use.
+                        */
+                       __get_user(entry, (unsigned long __user *)start);
+                       __get_user(toc, (unsigned long __user *)start+1);
+
+                       /* Check whether the e_entry function descriptor entries
+                        * need to be relocated before we can use them.
+                        */
+                       if (load_addr != 0) {
+                               entry += load_addr;
+                               toc   += load_addr;
+                       }
+                       regs->gpr[2] = toc;
                }
                regs->nip = entry;
-               regs->gpr[2] = toc;
                regs->msr = MSR_USER64;
        } else {
                regs->nip = start;
index f3a4709..fa0ad8a 100644 (file)
@@ -777,6 +777,26 @@ int of_get_ibm_chip_id(struct device_node *np)
        return -1;
 }
 
+/**
+ * cpu_to_chip_id - Return the cpus chip-id
+ * @cpu: The logical cpu number.
+ *
+ * Return the value of the ibm,chip-id property corresponding to the given
+ * logical cpu number. If the chip-id can not be found, returns -1.
+ */
+int cpu_to_chip_id(int cpu)
+{
+       struct device_node *np;
+
+       np = of_get_cpu_node(cpu, NULL);
+       if (!np)
+               return -1;
+
+       of_node_put(np);
+       return of_get_ibm_chip_id(np);
+}
+EXPORT_SYMBOL(cpu_to_chip_id);
+
 #ifdef CONFIG_PPC_PSERIES
 /*
  * Fix up the uninitialized fields in a new device node:
index 749778e..1844298 100644 (file)
@@ -457,7 +457,15 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
                if (copy_vsx_to_user(&frame->mc_vsregs, current))
                        return 1;
                msr |= MSR_VSX;
-       }
+       } else if (!ctx_has_vsx_region)
+               /*
+                * With a small context structure we can't hold the VSX
+                * registers, hence clear the MSR value to indicate the state
+                * was not saved.
+                */
+               msr &= ~MSR_VSX;
+
+
 #endif /* CONFIG_VSX */
 #ifdef CONFIG_SPE
        /* save spe registers */
index b3c6157..e66f67b 100644 (file)
@@ -701,12 +701,6 @@ badframe:
 int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info,
                sigset_t *set, struct pt_regs *regs)
 {
-       /* Handler is *really* a pointer to the function descriptor for
-        * the signal routine.  The first entry in the function
-        * descriptor is the entry address of signal and the second
-        * entry is the TOC value we need to use.
-        */
-       func_descr_t __user *funct_desc_ptr;
        struct rt_sigframe __user *frame;
        unsigned long newsp = 0;
        long err = 0;
@@ -766,19 +760,32 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info,
                        goto badframe;
                regs->link = (unsigned long) &frame->tramp[0];
        }
-       funct_desc_ptr = (func_descr_t __user *) ka->sa.sa_handler;
 
        /* Allocate a dummy caller frame for the signal handler. */
        newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE;
        err |= put_user(regs->gpr[1], (unsigned long __user *)newsp);
 
        /* Set up "regs" so we "return" to the signal handler. */
-       err |= get_user(regs->nip, &funct_desc_ptr->entry);
+       if (is_elf2_task()) {
+               regs->nip = (unsigned long) ka->sa.sa_handler;
+               regs->gpr[12] = regs->nip;
+       } else {
+               /* Handler is *really* a pointer to the function descriptor for
+                * the signal routine.  The first entry in the function
+                * descriptor is the entry address of signal and the second
+                * entry is the TOC value we need to use.
+                */
+               func_descr_t __user *funct_desc_ptr =
+                       (func_descr_t __user *) ka->sa.sa_handler;
+
+               err |= get_user(regs->nip, &funct_desc_ptr->entry);
+               err |= get_user(regs->gpr[2], &funct_desc_ptr->toc);
+       }
+
        /* enter the signal handler in native-endian mode */
        regs->msr &= ~MSR_LE;
        regs->msr |= (MSR_KERNEL & MSR_LE);
        regs->gpr[1] = newsp;
-       err |= get_user(regs->gpr[2], &funct_desc_ptr->toc);
        regs->gpr[3] = signr;
        regs->result = 0;
        if (ka->sa.sa_flags & SA_SIGINFO) {
index 930cd8a..a3b64f3 100644 (file)
@@ -597,22 +597,6 @@ out:
        return id;
 }
 
-/* Return the value of the chip-id property corresponding
- * to the given logical cpu.
- */
-int cpu_to_chip_id(int cpu)
-{
-       struct device_node *np;
-
-       np = of_get_cpu_node(cpu, NULL);
-       if (!np)
-               return -1;
-
-       of_node_put(np);
-       return of_get_ibm_chip_id(np);
-}
-EXPORT_SYMBOL(cpu_to_chip_id);
-
 /* Helper routines for cpu to core mapping */
 int cpu_core_index_of_thread(int cpu)
 {
index 192b051..b3b1441 100644 (file)
@@ -213,8 +213,6 @@ static u64 scan_dispatch_log(u64 stop_tb)
        if (i == be64_to_cpu(vpa->dtl_idx))
                return 0;
        while (i < be64_to_cpu(vpa->dtl_idx)) {
-               if (dtl_consumer)
-                       dtl_consumer(dtl, i);
                dtb = be64_to_cpu(dtl->timebase);
                tb_delta = be32_to_cpu(dtl->enqueue_to_dispatch_time) +
                        be32_to_cpu(dtl->ready_to_enqueue_time);
@@ -227,6 +225,8 @@ static u64 scan_dispatch_log(u64 stop_tb)
                }
                if (dtb > stop_tb)
                        break;
+               if (dtl_consumer)
+                       dtl_consumer(dtl, i);
                stolen += tb_delta;
                ++i;
                ++dtl;
index 62c3dd8..907a472 100644 (file)
@@ -1529,7 +1529,7 @@ static void handle_debug(struct pt_regs *regs, unsigned long debug_status)
         * back on or not.
         */
        if (DBCR_ACTIVE_EVENTS(current->thread.debug.dbcr0,
-           current->thread.debug.dbcr1))
+                              current->thread.debug.dbcr1))
                regs->msr |= MSR_DE;
        else
                /* Make sure the IDM flag is off */
index 45ea281..542c6f4 100644 (file)
@@ -142,6 +142,13 @@ V_FUNCTION_END(__kernel_sigtramp_rt64)
 /* Size of CR reg in DWARF unwind info. */
 #define CRSIZE 4
 
+/* Offset of CR reg within a full word. */
+#ifdef __LITTLE_ENDIAN__
+#define CROFF 0
+#else
+#define CROFF (RSIZE - CRSIZE)
+#endif
+
 /* This is the offset of the VMX reg pointer.  */
 #define VREGS  48*RSIZE+33*8
 
@@ -181,7 +188,14 @@ V_FUNCTION_END(__kernel_sigtramp_rt64)
   rsave (31, 31*RSIZE);                                                        \
   rsave (67, 32*RSIZE);                /* ap, used as temp for nip */          \
   rsave (65, 36*RSIZE);                /* lr */                                \
-  rsave (70, 38*RSIZE + (RSIZE - CRSIZE)) /* cr */
+  rsave (68, 38*RSIZE + CROFF);        /* cr fields */                         \
+  rsave (69, 38*RSIZE + CROFF);                                                \
+  rsave (70, 38*RSIZE + CROFF);                                                \
+  rsave (71, 38*RSIZE + CROFF);                                                \
+  rsave (72, 38*RSIZE + CROFF);                                                \
+  rsave (73, 38*RSIZE + CROFF);                                                \
+  rsave (74, 38*RSIZE + CROFF);                                                \
+  rsave (75, 38*RSIZE + CROFF)
 
 /* Describe where the FP regs are saved.  */
 #define EH_FRAME_FP \
index e7d0c88..76a6482 100644 (file)
@@ -1419,7 +1419,7 @@ struct vio_dev *vio_register_device_node(struct device_node *of_node)
 
                /* needed to ensure proper operation of coherent allocations
                 * later, in case driver doesn't set it explicitly */
-               dma_set_mask_and_coherent(&viodev->dev, DMA_BIT_MASK(64));
+               dma_coerce_mask_and_coherent(&viodev->dev, DMA_BIT_MASK(64));
        }
 
        /* register with generic device framework */
index 2f5c6b6..93221e8 100644 (file)
 #include "44x_tlb.h"
 #include "booke.h"
 
-void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+static void kvmppc_core_vcpu_load_44x(struct kvm_vcpu *vcpu, int cpu)
 {
        kvmppc_booke_vcpu_load(vcpu, cpu);
        kvmppc_44x_tlb_load(vcpu);
 }
 
-void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
+static void kvmppc_core_vcpu_put_44x(struct kvm_vcpu *vcpu)
 {
        kvmppc_44x_tlb_put(vcpu);
        kvmppc_booke_vcpu_put(vcpu);
@@ -114,29 +114,32 @@ int kvmppc_core_vcpu_translate(struct kvm_vcpu *vcpu,
        return 0;
 }
 
-void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
+static int kvmppc_core_get_sregs_44x(struct kvm_vcpu *vcpu,
+                                     struct kvm_sregs *sregs)
 {
-       kvmppc_get_sregs_ivor(vcpu, sregs);
+       return kvmppc_get_sregs_ivor(vcpu, sregs);
 }
 
-int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
+static int kvmppc_core_set_sregs_44x(struct kvm_vcpu *vcpu,
+                                    struct kvm_sregs *sregs)
 {
        return kvmppc_set_sregs_ivor(vcpu, sregs);
 }
 
-int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id,
-                       union kvmppc_one_reg *val)
+static int kvmppc_get_one_reg_44x(struct kvm_vcpu *vcpu, u64 id,
+                                 union kvmppc_one_reg *val)
 {
        return -EINVAL;
 }
 
-int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id,
-                      union kvmppc_one_reg *val)
+static int kvmppc_set_one_reg_44x(struct kvm_vcpu *vcpu, u64 id,
+                                 union kvmppc_one_reg *val)
 {
        return -EINVAL;
 }
 
-struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
+static struct kvm_vcpu *kvmppc_core_vcpu_create_44x(struct kvm *kvm,
+                                                   unsigned int id)
 {
        struct kvmppc_vcpu_44x *vcpu_44x;
        struct kvm_vcpu *vcpu;
@@ -167,7 +170,7 @@ out:
        return ERR_PTR(err);
 }
 
-void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
+static void kvmppc_core_vcpu_free_44x(struct kvm_vcpu *vcpu)
 {
        struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
 
@@ -176,28 +179,53 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
        kmem_cache_free(kvm_vcpu_cache, vcpu_44x);
 }
 
-int kvmppc_core_init_vm(struct kvm *kvm)
+static int kvmppc_core_init_vm_44x(struct kvm *kvm)
 {
        return 0;
 }
 
-void kvmppc_core_destroy_vm(struct kvm *kvm)
+static void kvmppc_core_destroy_vm_44x(struct kvm *kvm)
 {
 }
 
+static struct kvmppc_ops kvm_ops_44x = {
+       .get_sregs = kvmppc_core_get_sregs_44x,
+       .set_sregs = kvmppc_core_set_sregs_44x,
+       .get_one_reg = kvmppc_get_one_reg_44x,
+       .set_one_reg = kvmppc_set_one_reg_44x,
+       .vcpu_load   = kvmppc_core_vcpu_load_44x,
+       .vcpu_put    = kvmppc_core_vcpu_put_44x,
+       .vcpu_create = kvmppc_core_vcpu_create_44x,
+       .vcpu_free   = kvmppc_core_vcpu_free_44x,
+       .mmu_destroy  = kvmppc_mmu_destroy_44x,
+       .init_vm = kvmppc_core_init_vm_44x,
+       .destroy_vm = kvmppc_core_destroy_vm_44x,
+       .emulate_op = kvmppc_core_emulate_op_44x,
+       .emulate_mtspr = kvmppc_core_emulate_mtspr_44x,
+       .emulate_mfspr = kvmppc_core_emulate_mfspr_44x,
+};
+
 static int __init kvmppc_44x_init(void)
 {
        int r;
 
        r = kvmppc_booke_init();
        if (r)
-               return r;
+               goto err_out;
+
+       r = kvm_init(NULL, sizeof(struct kvmppc_vcpu_44x), 0, THIS_MODULE);
+       if (r)
+               goto err_out;
+       kvm_ops_44x.owner = THIS_MODULE;
+       kvmppc_pr_ops = &kvm_ops_44x;
 
-       return kvm_init(NULL, sizeof(struct kvmppc_vcpu_44x), 0, THIS_MODULE);
+err_out:
+       return r;
 }
 
 static void __exit kvmppc_44x_exit(void)
 {
+       kvmppc_pr_ops = NULL;
        kvmppc_booke_exit();
 }
 
index 35ec0a8..92c9ab4 100644 (file)
@@ -91,8 +91,8 @@ static int emulate_mfdcr(struct kvm_vcpu *vcpu, int rt, int dcrn)
        return EMULATE_DONE;
 }
 
-int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
-                           unsigned int inst, int *advance)
+int kvmppc_core_emulate_op_44x(struct kvm_run *run, struct kvm_vcpu *vcpu,
+                              unsigned int inst, int *advance)
 {
        int emulated = EMULATE_DONE;
        int dcrn = get_dcrn(inst);
@@ -152,7 +152,7 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
        return emulated;
 }
 
-int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
+int kvmppc_core_emulate_mtspr_44x(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
 {
        int emulated = EMULATE_DONE;
 
@@ -172,7 +172,7 @@ int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
        return emulated;
 }
 
-int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val)
+int kvmppc_core_emulate_mfspr_44x(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val)
 {
        int emulated = EMULATE_DONE;
 
index ed03854..0deef10 100644 (file)
@@ -268,7 +268,7 @@ static void kvmppc_44x_shadow_release(struct kvmppc_vcpu_44x *vcpu_44x,
        trace_kvm_stlb_inval(stlb_index);
 }
 
-void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
+void kvmppc_mmu_destroy_44x(struct kvm_vcpu *vcpu)
 {
        struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
        int i;
index e593ff2..141b202 100644 (file)
@@ -35,17 +35,20 @@ config KVM_BOOK3S_64_HANDLER
        bool
        select KVM_BOOK3S_HANDLER
 
-config KVM_BOOK3S_PR
+config KVM_BOOK3S_PR_POSSIBLE
        bool
        select KVM_MMIO
        select MMU_NOTIFIER
 
+config KVM_BOOK3S_HV_POSSIBLE
+       bool
+
 config KVM_BOOK3S_32
        tristate "KVM support for PowerPC book3s_32 processors"
        depends on PPC_BOOK3S_32 && !SMP && !PTE_64BIT
        select KVM
        select KVM_BOOK3S_32_HANDLER
-       select KVM_BOOK3S_PR
+       select KVM_BOOK3S_PR_POSSIBLE
        ---help---
          Support running unmodified book3s_32 guest kernels
          in virtual machines on book3s_32 host processors.
@@ -60,6 +63,7 @@ config KVM_BOOK3S_64
        depends on PPC_BOOK3S_64
        select KVM_BOOK3S_64_HANDLER
        select KVM
+       select KVM_BOOK3S_PR_POSSIBLE if !KVM_BOOK3S_HV_POSSIBLE
        ---help---
          Support running unmodified book3s_64 and book3s_32 guest kernels
          in virtual machines on book3s_64 host processors.
@@ -70,8 +74,9 @@ config KVM_BOOK3S_64
          If unsure, say N.
 
 config KVM_BOOK3S_64_HV
-       bool "KVM support for POWER7 and PPC970 using hypervisor mode in host"
+       tristate "KVM support for POWER7 and PPC970 using hypervisor mode in host"
        depends on KVM_BOOK3S_64
+       select KVM_BOOK3S_HV_POSSIBLE
        select MMU_NOTIFIER
        select CMA
        ---help---
@@ -90,9 +95,20 @@ config KVM_BOOK3S_64_HV
          If unsure, say N.
 
 config KVM_BOOK3S_64_PR
-       def_bool y
-       depends on KVM_BOOK3S_64 && !KVM_BOOK3S_64_HV
-       select KVM_BOOK3S_PR
+       tristate "KVM support without using hypervisor mode in host"
+       depends on KVM_BOOK3S_64
+       select KVM_BOOK3S_PR_POSSIBLE
+       ---help---
+         Support running guest kernels in virtual machines on processors
+         without using hypervisor mode in the host, by running the
+         guest in user mode (problem state) and emulating all
+         privileged instructions and registers.
+
+         This is not as fast as using hypervisor mode, but works on
+         machines where hypervisor mode is not available or not usable,
+         and can emulate processors that are different from the host
+         processor, including emulating 32-bit processors on a 64-bit
+         host.
 
 config KVM_BOOKE_HV
        bool
index 6646c95..ce569b6 100644 (file)
@@ -53,41 +53,51 @@ kvm-e500mc-objs := \
        e500_emulate.o
 kvm-objs-$(CONFIG_KVM_E500MC) := $(kvm-e500mc-objs)
 
-kvm-book3s_64-objs-$(CONFIG_KVM_BOOK3S_64_PR) := \
-       $(KVM)/coalesced_mmio.o \
+kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HANDLER) := \
+       book3s_64_vio_hv.o
+
+kvm-pr-y := \
        fpu.o \
        book3s_paired_singles.o \
        book3s_pr.o \
        book3s_pr_papr.o \
-       book3s_64_vio_hv.o \
        book3s_emulate.o \
        book3s_interrupts.o \
        book3s_mmu_hpte.o \
        book3s_64_mmu_host.o \
        book3s_64_mmu.o \
        book3s_32_mmu.o
-kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_PR) := \
+
+ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
+kvm-book3s_64-module-objs := \
+       $(KVM)/coalesced_mmio.o
+
+kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HANDLER) += \
        book3s_rmhandlers.o
+endif
 
-kvm-book3s_64-objs-$(CONFIG_KVM_BOOK3S_64_HV) := \
+kvm-hv-y += \
        book3s_hv.o \
        book3s_hv_interrupts.o \
        book3s_64_mmu_hv.o
+
 kvm-book3s_64-builtin-xics-objs-$(CONFIG_KVM_XICS) := \
        book3s_hv_rm_xics.o
-kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HV) := \
+
+ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
+kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HANDLER) += \
        book3s_hv_rmhandlers.o \
        book3s_hv_rm_mmu.o \
-       book3s_64_vio_hv.o \
        book3s_hv_ras.o \
        book3s_hv_builtin.o \
        book3s_hv_cma.o \
        $(kvm-book3s_64-builtin-xics-objs-y)
+endif
 
 kvm-book3s_64-objs-$(CONFIG_KVM_XICS) += \
        book3s_xics.o
 
-kvm-book3s_64-module-objs := \
+kvm-book3s_64-module-objs += \
        $(KVM)/kvm_main.o \
        $(KVM)/eventfd.o \
        powerpc.o \
@@ -123,4 +133,7 @@ obj-$(CONFIG_KVM_E500MC) += kvm.o
 obj-$(CONFIG_KVM_BOOK3S_64) += kvm.o
 obj-$(CONFIG_KVM_BOOK3S_32) += kvm.o
 
+obj-$(CONFIG_KVM_BOOK3S_64_PR) += kvm-pr.o
+obj-$(CONFIG_KVM_BOOK3S_64_HV) += kvm-hv.o
+
 obj-y += $(kvm-book3s_64-builtin-objs-y)
index 700df6f..8912608 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/vmalloc.h>
 #include <linux/highmem.h>
 
+#include "book3s.h"
 #include "trace.h"
 
 #define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU
@@ -69,6 +70,50 @@ void kvmppc_core_load_guest_debugstate(struct kvm_vcpu *vcpu)
 {
 }
 
+static inline unsigned long kvmppc_interrupt_offset(struct kvm_vcpu *vcpu)
+{
+       if (!is_kvmppc_hv_enabled(vcpu->kvm))
+               return to_book3s(vcpu)->hior;
+       return 0;
+}
+
+static inline void kvmppc_update_int_pending(struct kvm_vcpu *vcpu,
+                       unsigned long pending_now, unsigned long old_pending)
+{
+       if (is_kvmppc_hv_enabled(vcpu->kvm))
+               return;
+       if (pending_now)
+               vcpu->arch.shared->int_pending = 1;
+       else if (old_pending)
+               vcpu->arch.shared->int_pending = 0;
+}
+
+static inline bool kvmppc_critical_section(struct kvm_vcpu *vcpu)
+{
+       ulong crit_raw;
+       ulong crit_r1;
+       bool crit;
+
+       if (is_kvmppc_hv_enabled(vcpu->kvm))
+               return false;
+
+       crit_raw = vcpu->arch.shared->critical;
+       crit_r1 = kvmppc_get_gpr(vcpu, 1);
+
+       /* Truncate crit indicators in 32 bit mode */
+       if (!(vcpu->arch.shared->msr & MSR_SF)) {
+               crit_raw &= 0xffffffff;
+               crit_r1 &= 0xffffffff;
+       }
+
+       /* Critical section when crit == r1 */
+       crit = (crit_raw == crit_r1);
+       /* ... and we're in supervisor mode */
+       crit = crit && !(vcpu->arch.shared->msr & MSR_PR);
+
+       return crit;
+}
+
 void kvmppc_inject_interrupt(struct kvm_vcpu *vcpu, int vec, u64 flags)
 {
        vcpu->arch.shared->srr0 = kvmppc_get_pc(vcpu);
@@ -126,28 +171,32 @@ void kvmppc_book3s_queue_irqprio(struct kvm_vcpu *vcpu, unsigned int vec)
        printk(KERN_INFO "Queueing interrupt %x\n", vec);
 #endif
 }
-
+EXPORT_SYMBOL_GPL(kvmppc_book3s_queue_irqprio);
 
 void kvmppc_core_queue_program(struct kvm_vcpu *vcpu, ulong flags)
 {
        /* might as well deliver this straight away */
        kvmppc_inject_interrupt(vcpu, BOOK3S_INTERRUPT_PROGRAM, flags);
 }
+EXPORT_SYMBOL_GPL(kvmppc_core_queue_program);
 
 void kvmppc_core_queue_dec(struct kvm_vcpu *vcpu)
 {
        kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_DECREMENTER);
 }
+EXPORT_SYMBOL_GPL(kvmppc_core_queue_dec);
 
 int kvmppc_core_pending_dec(struct kvm_vcpu *vcpu)
 {
        return test_bit(BOOK3S_IRQPRIO_DECREMENTER, &vcpu->arch.pending_exceptions);
 }
+EXPORT_SYMBOL_GPL(kvmppc_core_pending_dec);
 
 void kvmppc_core_dequeue_dec(struct kvm_vcpu *vcpu)
 {
        kvmppc_book3s_dequeue_irqprio(vcpu, BOOK3S_INTERRUPT_DECREMENTER);
 }
+EXPORT_SYMBOL_GPL(kvmppc_core_dequeue_dec);
 
 void kvmppc_core_queue_external(struct kvm_vcpu *vcpu,
                                 struct kvm_interrupt *irq)
@@ -285,8 +334,10 @@ int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(kvmppc_core_prepare_to_enter);
 
-pfn_t kvmppc_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn)
+pfn_t kvmppc_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn, bool writing,
+                       bool *writable)
 {
        ulong mp_pa = vcpu->arch.magic_page_pa;
 
@@ -302,20 +353,23 @@ pfn_t kvmppc_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn)
 
                pfn = (pfn_t)virt_to_phys((void*)shared_page) >> PAGE_SHIFT;
                get_page(pfn_to_page(pfn));
+               if (writable)
+                       *writable = true;
                return pfn;
        }
 
-       return gfn_to_pfn(vcpu->kvm, gfn);
+       return gfn_to_pfn_prot(vcpu->kvm, gfn, writing, writable);
 }
+EXPORT_SYMBOL_GPL(kvmppc_gfn_to_pfn);
 
 static int kvmppc_xlate(struct kvm_vcpu *vcpu, ulong eaddr, bool data,
-                        struct kvmppc_pte *pte)
+                       bool iswrite, struct kvmppc_pte *pte)
 {
        int relocated = (vcpu->arch.shared->msr & (data ? MSR_DR : MSR_IR));
        int r;
 
        if (relocated) {
-               r = vcpu->arch.mmu.xlate(vcpu, eaddr, pte, data);
+               r = vcpu->arch.mmu.xlate(vcpu, eaddr, pte, data, iswrite);
        } else {
                pte->eaddr = eaddr;
                pte->raddr = eaddr & KVM_PAM;
@@ -361,7 +415,7 @@ int kvmppc_st(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr,
 
        vcpu->stat.st++;
 
-       if (kvmppc_xlate(vcpu, *eaddr, data, &pte))
+       if (kvmppc_xlate(vcpu, *eaddr, data, true, &pte))
                return -ENOENT;
 
        *eaddr = pte.raddr;
@@ -374,6 +428,7 @@ int kvmppc_st(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr,
 
        return EMULATE_DONE;
 }
+EXPORT_SYMBOL_GPL(kvmppc_st);
 
 int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr,
                      bool data)
@@ -383,7 +438,7 @@ int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr,
 
        vcpu->stat.ld++;
 
-       if (kvmppc_xlate(vcpu, *eaddr, data, &pte))
+       if (kvmppc_xlate(vcpu, *eaddr, data, false, &pte))
                goto nopte;
 
        *eaddr = pte.raddr;
@@ -404,6 +459,7 @@ nopte:
 mmio:
        return EMULATE_DO_MMIO;
 }
+EXPORT_SYMBOL_GPL(kvmppc_ld);
 
 int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
 {
@@ -419,6 +475,18 @@ void kvmppc_subarch_vcpu_uninit(struct kvm_vcpu *vcpu)
 {
 }
 
+int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
+                                 struct kvm_sregs *sregs)
+{
+       return vcpu->kvm->arch.kvm_ops->get_sregs(vcpu, sregs);
+}
+
+int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
+                                 struct kvm_sregs *sregs)
+{
+       return vcpu->kvm->arch.kvm_ops->set_sregs(vcpu, sregs);
+}
+
 int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
 {
        int i;
@@ -495,8 +563,7 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
        if (size > sizeof(val))
                return -EINVAL;
 
-       r = kvmppc_get_one_reg(vcpu, reg->id, &val);
-
+       r = vcpu->kvm->arch.kvm_ops->get_one_reg(vcpu, reg->id, &val);
        if (r == -EINVAL) {
                r = 0;
                switch (reg->id) {
@@ -528,6 +595,9 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
                        }
                        val = get_reg_val(reg->id, vcpu->arch.vscr.u[3]);
                        break;
+               case KVM_REG_PPC_VRSAVE:
+                       val = get_reg_val(reg->id, vcpu->arch.vrsave);
+                       break;
 #endif /* CONFIG_ALTIVEC */
                case KVM_REG_PPC_DEBUG_INST: {
                        u32 opcode = INS_TW;
@@ -572,8 +642,7 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
        if (copy_from_user(&val, (char __user *)(unsigned long)reg->addr, size))
                return -EFAULT;
 
-       r = kvmppc_set_one_reg(vcpu, reg->id, &val);
-
+       r = vcpu->kvm->arch.kvm_ops->set_one_reg(vcpu, reg->id, &val);
        if (r == -EINVAL) {
                r = 0;
                switch (reg->id) {
@@ -605,6 +674,13 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
                        }
                        vcpu->arch.vscr.u[3] = set_reg_val(reg->id, val);
                        break;
+               case KVM_REG_PPC_VRSAVE:
+                       if (!cpu_has_feature(CPU_FTR_ALTIVEC)) {
+                               r = -ENXIO;
+                               break;
+                       }
+                       vcpu->arch.vrsave = set_reg_val(reg->id, val);
+                       break;
 #endif /* CONFIG_ALTIVEC */
 #ifdef CONFIG_KVM_XICS
                case KVM_REG_PPC_ICP_STATE:
@@ -625,6 +701,27 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
        return r;
 }
 
+void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+       vcpu->kvm->arch.kvm_ops->vcpu_load(vcpu, cpu);
+}
+
+void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
+{
+       vcpu->kvm->arch.kvm_ops->vcpu_put(vcpu);
+}
+
+void kvmppc_set_msr(struct kvm_vcpu *vcpu, u64 msr)
+{
+       vcpu->kvm->arch.kvm_ops->set_msr(vcpu, msr);
+}
+EXPORT_SYMBOL_GPL(kvmppc_set_msr);
+
+int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
+{
+       return vcpu->kvm->arch.kvm_ops->vcpu_run(kvm_run, vcpu);
+}
+
 int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
                                   struct kvm_translation *tr)
 {
@@ -644,3 +741,141 @@ void kvmppc_decrementer_func(unsigned long data)
        kvmppc_core_queue_dec(vcpu);
        kvm_vcpu_kick(vcpu);
 }
+
+struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
+{
+       return kvm->arch.kvm_ops->vcpu_create(kvm, id);
+}
+
+void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
+{
+       vcpu->kvm->arch.kvm_ops->vcpu_free(vcpu);
+}
+
+int kvmppc_core_check_requests(struct kvm_vcpu *vcpu)
+{
+       return vcpu->kvm->arch.kvm_ops->check_requests(vcpu);
+}
+
+int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
+{
+       return kvm->arch.kvm_ops->get_dirty_log(kvm, log);
+}
+
+void kvmppc_core_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
+                             struct kvm_memory_slot *dont)
+{
+       kvm->arch.kvm_ops->free_memslot(free, dont);
+}
+
+int kvmppc_core_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
+                              unsigned long npages)
+{
+       return kvm->arch.kvm_ops->create_memslot(slot, npages);
+}
+
+void kvmppc_core_flush_memslot(struct kvm *kvm, struct kvm_memory_slot *memslot)
+{
+       kvm->arch.kvm_ops->flush_memslot(kvm, memslot);
+}
+
+int kvmppc_core_prepare_memory_region(struct kvm *kvm,
+                               struct kvm_memory_slot *memslot,
+                               struct kvm_userspace_memory_region *mem)
+{
+       return kvm->arch.kvm_ops->prepare_memory_region(kvm, memslot, mem);
+}
+
+void kvmppc_core_commit_memory_region(struct kvm *kvm,
+                               struct kvm_userspace_memory_region *mem,
+                               const struct kvm_memory_slot *old)
+{
+       kvm->arch.kvm_ops->commit_memory_region(kvm, mem, old);
+}
+
+int kvm_unmap_hva(struct kvm *kvm, unsigned long hva)
+{
+       return kvm->arch.kvm_ops->unmap_hva(kvm, hva);
+}
+EXPORT_SYMBOL_GPL(kvm_unmap_hva);
+
+int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end)
+{
+       return kvm->arch.kvm_ops->unmap_hva_range(kvm, start, end);
+}
+
+int kvm_age_hva(struct kvm *kvm, unsigned long hva)
+{
+       return kvm->arch.kvm_ops->age_hva(kvm, hva);
+}
+
+int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
+{
+       return kvm->arch.kvm_ops->test_age_hva(kvm, hva);
+}
+
+void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte)
+{
+       kvm->arch.kvm_ops->set_spte_hva(kvm, hva, pte);
+}
+
+void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
+{
+       vcpu->kvm->arch.kvm_ops->mmu_destroy(vcpu);
+}
+
+int kvmppc_core_init_vm(struct kvm *kvm)
+{
+
+#ifdef CONFIG_PPC64
+       INIT_LIST_HEAD(&kvm->arch.spapr_tce_tables);
+       INIT_LIST_HEAD(&kvm->arch.rtas_tokens);
+#endif
+
+       return kvm->arch.kvm_ops->init_vm(kvm);
+}
+
+void kvmppc_core_destroy_vm(struct kvm *kvm)
+{
+       kvm->arch.kvm_ops->destroy_vm(kvm);
+
+#ifdef CONFIG_PPC64
+       kvmppc_rtas_tokens_free(kvm);
+       WARN_ON(!list_empty(&kvm->arch.spapr_tce_tables));
+#endif
+}
+
+int kvmppc_core_check_processor_compat(void)
+{
+       /*
+        * We always return 0 for book3s. We check
+        * for compatability while loading the HV
+        * or PR module
+        */
+       return 0;
+}
+
+static int kvmppc_book3s_init(void)
+{
+       int r;
+
+       r = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE);
+       if (r)
+               return r;
+#ifdef CONFIG_KVM_BOOK3S_32
+       r = kvmppc_book3s_init_pr();
+#endif
+       return r;
+
+}
+
+static void kvmppc_book3s_exit(void)
+{
+#ifdef CONFIG_KVM_BOOK3S_32
+       kvmppc_book3s_exit_pr();
+#endif
+       kvm_exit();
+}
+
+module_init(kvmppc_book3s_init);
+module_exit(kvmppc_book3s_exit);
diff --git a/arch/powerpc/kvm/book3s.h b/arch/powerpc/kvm/book3s.h
new file mode 100644 (file)
index 0000000..4bf956c
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright IBM Corporation, 2013
+ * 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 the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License or (at your optional) any later version of the license.
+ *
+ */
+
+#ifndef __POWERPC_KVM_BOOK3S_H__
+#define __POWERPC_KVM_BOOK3S_H__
+
+extern void kvmppc_core_flush_memslot_hv(struct kvm *kvm,
+                                        struct kvm_memory_slot *memslot);
+extern int kvm_unmap_hva_hv(struct kvm *kvm, unsigned long hva);
+extern int kvm_unmap_hva_range_hv(struct kvm *kvm, unsigned long start,
+                                 unsigned long end);
+extern int kvm_age_hva_hv(struct kvm *kvm, unsigned long hva);
+extern int kvm_test_age_hva_hv(struct kvm *kvm, unsigned long hva);
+extern void kvm_set_spte_hva_hv(struct kvm *kvm, unsigned long hva, pte_t pte);
+
+extern void kvmppc_mmu_destroy_pr(struct kvm_vcpu *vcpu);
+extern int kvmppc_core_emulate_op_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
+                                    unsigned int inst, int *advance);
+extern int kvmppc_core_emulate_mtspr_pr(struct kvm_vcpu *vcpu,
+                                       int sprn, ulong spr_val);
+extern int kvmppc_core_emulate_mfspr_pr(struct kvm_vcpu *vcpu,
+                                       int sprn, ulong *spr_val);
+extern int kvmppc_book3s_init_pr(void);
+extern void kvmppc_book3s_exit_pr(void);
+
+#endif
index c8cefdd..76a64ce 100644 (file)
@@ -84,7 +84,8 @@ static inline bool sr_nx(u32 sr_raw)
 }
 
 static int kvmppc_mmu_book3s_32_xlate_bat(struct kvm_vcpu *vcpu, gva_t eaddr,
-                                         struct kvmppc_pte *pte, bool data);
+                                         struct kvmppc_pte *pte, bool data,
+                                         bool iswrite);
 static int kvmppc_mmu_book3s_32_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
                                             u64 *vsid);
 
@@ -99,7 +100,7 @@ static u64 kvmppc_mmu_book3s_32_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr,
        u64 vsid;
        struct kvmppc_pte pte;
 
-       if (!kvmppc_mmu_book3s_32_xlate_bat(vcpu, eaddr, &pte, data))
+       if (!kvmppc_mmu_book3s_32_xlate_bat(vcpu, eaddr, &pte, data, false))
                return pte.vpage;
 
        kvmppc_mmu_book3s_32_esid_to_vsid(vcpu, eaddr >> SID_SHIFT, &vsid);
@@ -111,10 +112,11 @@ static void kvmppc_mmu_book3s_32_reset_msr(struct kvm_vcpu *vcpu)
        kvmppc_set_msr(vcpu, 0);
 }
 
-static hva_t kvmppc_mmu_book3s_32_get_pteg(struct kvmppc_vcpu_book3s *vcpu_book3s,
+static hva_t kvmppc_mmu_book3s_32_get_pteg(struct kvm_vcpu *vcpu,
                                      u32 sre, gva_t eaddr,
                                      bool primary)
 {
+       struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
        u32 page, hash, pteg, htabmask;
        hva_t r;
 
@@ -132,7 +134,7 @@ static hva_t kvmppc_mmu_book3s_32_get_pteg(struct kvmppc_vcpu_book3s *vcpu_book3
                kvmppc_get_pc(&vcpu_book3s->vcpu), eaddr, vcpu_book3s->sdr1, pteg,
                sr_vsid(sre));
 
-       r = gfn_to_hva(vcpu_book3s->vcpu.kvm, pteg >> PAGE_SHIFT);
+       r = gfn_to_hva(vcpu->kvm, pteg >> PAGE_SHIFT);
        if (kvm_is_error_hva(r))
                return r;
        return r | (pteg & ~PAGE_MASK);
@@ -145,7 +147,8 @@ static u32 kvmppc_mmu_book3s_32_get_ptem(u32 sre, gva_t eaddr, bool primary)
 }
 
 static int kvmppc_mmu_book3s_32_xlate_bat(struct kvm_vcpu *vcpu, gva_t eaddr,
-                                         struct kvmppc_pte *pte, bool data)
+                                         struct kvmppc_pte *pte, bool data,
+                                         bool iswrite)
 {
        struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
        struct kvmppc_bat *bat;
@@ -186,8 +189,7 @@ static int kvmppc_mmu_book3s_32_xlate_bat(struct kvm_vcpu *vcpu, gva_t eaddr,
                                printk(KERN_INFO "BAT is not readable!\n");
                                continue;
                        }
-                       if (!pte->may_write) {
-                               /* let's treat r/o BATs as not-readable for now */
+                       if (iswrite && !pte->may_write) {
                                dprintk_pte("BAT is read-only!\n");
                                continue;
                        }
@@ -201,9 +203,8 @@ static int kvmppc_mmu_book3s_32_xlate_bat(struct kvm_vcpu *vcpu, gva_t eaddr,
 
 static int kvmppc_mmu_book3s_32_xlate_pte(struct kvm_vcpu *vcpu, gva_t eaddr,
                                     struct kvmppc_pte *pte, bool data,
-                                    bool primary)
+                                    bool iswrite, bool primary)
 {
-       struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
        u32 sre;
        hva_t ptegp;
        u32 pteg[16];
@@ -218,7 +219,7 @@ static int kvmppc_mmu_book3s_32_xlate_pte(struct kvm_vcpu *vcpu, gva_t eaddr,
 
        pte->vpage = kvmppc_mmu_book3s_32_ea_to_vp(vcpu, eaddr, data);
 
-       ptegp = kvmppc_mmu_book3s_32_get_pteg(vcpu_book3s, sre, eaddr, primary);
+       ptegp = kvmppc_mmu_book3s_32_get_pteg(vcpu, sre, eaddr, primary);
        if (kvm_is_error_hva(ptegp)) {
                printk(KERN_INFO "KVM: Invalid PTEG!\n");
                goto no_page_found;
@@ -258,9 +259,6 @@ static int kvmppc_mmu_book3s_32_xlate_pte(struct kvm_vcpu *vcpu, gva_t eaddr,
                                        break;
                        }
 
-                       if ( !pte->may_read )
-                               continue;
-
                        dprintk_pte("MMU: Found PTE -> %x %x - %x\n",
                                    pteg[i], pteg[i+1], pp);
                        found = 1;
@@ -271,19 +269,23 @@ static int kvmppc_mmu_book3s_32_xlate_pte(struct kvm_vcpu *vcpu, gva_t eaddr,
        /* Update PTE C and A bits, so the guest's swapper knows we used the
           page */
        if (found) {
-               u32 oldpte = pteg[i+1];
-
-               if (pte->may_read)
-                       pteg[i+1] |= PTEG_FLAG_ACCESSED;
-               if (pte->may_write)
-                       pteg[i+1] |= PTEG_FLAG_DIRTY;
-               else
-                       dprintk_pte("KVM: Mapping read-only page!\n");
-
-               /* Write back into the PTEG */
-               if (pteg[i+1] != oldpte)
-                       copy_to_user((void __user *)ptegp, pteg, sizeof(pteg));
-
+               u32 pte_r = pteg[i+1];
+               char __user *addr = (char __user *) &pteg[i+1];
+
+               /*
+                * Use single-byte writes to update the HPTE, to
+                * conform to what real hardware does.
+                */
+               if (pte->may_read && !(pte_r & PTEG_FLAG_ACCESSED)) {
+                       pte_r |= PTEG_FLAG_ACCESSED;
+                       put_user(pte_r >> 8, addr + 2);
+               }
+               if (iswrite && pte->may_write && !(pte_r & PTEG_FLAG_DIRTY)) {
+                       pte_r |= PTEG_FLAG_DIRTY;
+                       put_user(pte_r, addr + 3);
+               }
+               if (!pte->may_read || (iswrite && !pte->may_write))
+                       return -EPERM;
                return 0;
        }
 
@@ -302,12 +304,14 @@ no_page_found:
 }
 
 static int kvmppc_mmu_book3s_32_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
-                                     struct kvmppc_pte *pte, bool data)
+                                     struct kvmppc_pte *pte, bool data,
+                                     bool iswrite)
 {
        int r;
        ulong mp_ea = vcpu->arch.magic_page_ea;
 
        pte->eaddr = eaddr;
+       pte->page_size = MMU_PAGE_4K;
 
        /* Magic page override */
        if (unlikely(mp_ea) &&
@@ -323,11 +327,13 @@ static int kvmppc_mmu_book3s_32_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
                return 0;
        }
 
-       r = kvmppc_mmu_book3s_32_xlate_bat(vcpu, eaddr, pte, data);
+       r = kvmppc_mmu_book3s_32_xlate_bat(vcpu, eaddr, pte, data, iswrite);
        if (r < 0)
-              r = kvmppc_mmu_book3s_32_xlate_pte(vcpu, eaddr, pte, data, true);
+               r = kvmppc_mmu_book3s_32_xlate_pte(vcpu, eaddr, pte,
+                                                  data, iswrite, true);
        if (r < 0)
-              r = kvmppc_mmu_book3s_32_xlate_pte(vcpu, eaddr, pte, data, false);
+               r = kvmppc_mmu_book3s_32_xlate_pte(vcpu, eaddr, pte,
+                                                  data, iswrite, false);
 
        return r;
 }
@@ -347,7 +353,12 @@ static void kvmppc_mmu_book3s_32_mtsrin(struct kvm_vcpu *vcpu, u32 srnum,
 
 static void kvmppc_mmu_book3s_32_tlbie(struct kvm_vcpu *vcpu, ulong ea, bool large)
 {
-       kvmppc_mmu_pte_flush(vcpu, ea, 0x0FFFF000);
+       int i;
+       struct kvm_vcpu *v;
+
+       /* flush this VA on all cpus */
+       kvm_for_each_vcpu(i, v, vcpu->kvm)
+               kvmppc_mmu_pte_flush(v, ea, 0x0FFFF000);
 }
 
 static int kvmppc_mmu_book3s_32_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
index 00e619b..3a0abd2 100644 (file)
@@ -138,7 +138,8 @@ static u32 *kvmppc_mmu_get_pteg(struct kvm_vcpu *vcpu, u32 vsid, u32 eaddr,
 
 extern char etext[];
 
-int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte)
+int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte,
+                       bool iswrite)
 {
        pfn_t hpaddr;
        u64 vpn;
@@ -152,9 +153,11 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte)
        bool evict = false;
        struct hpte_cache *pte;
        int r = 0;
+       bool writable;
 
        /* Get host physical address for gpa */
-       hpaddr = kvmppc_gfn_to_pfn(vcpu, orig_pte->raddr >> PAGE_SHIFT);
+       hpaddr = kvmppc_gfn_to_pfn(vcpu, orig_pte->raddr >> PAGE_SHIFT,
+                                  iswrite, &writable);
        if (is_error_noslot_pfn(hpaddr)) {
                printk(KERN_INFO "Couldn't get guest page for gfn %lx!\n",
                                 orig_pte->eaddr);
@@ -204,7 +207,7 @@ next_pteg:
                (primary ? 0 : PTE_SEC);
        pteg1 = hpaddr | PTE_M | PTE_R | PTE_C;
 
-       if (orig_pte->may_write) {
+       if (orig_pte->may_write && writable) {
                pteg1 |= PP_RWRW;
                mark_page_dirty(vcpu->kvm, orig_pte->raddr >> PAGE_SHIFT);
        } else {
@@ -259,6 +262,11 @@ out:
        return r;
 }
 
+void kvmppc_mmu_unmap_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte)
+{
+       kvmppc_mmu_pte_vflush(vcpu, pte->vpage, 0xfffffffffULL);
+}
+
 static struct kvmppc_sid_map *create_sid_map(struct kvm_vcpu *vcpu, u64 gvsid)
 {
        struct kvmppc_sid_map *map;
@@ -341,7 +349,7 @@ void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu)
        svcpu_put(svcpu);
 }
 
-void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
+void kvmppc_mmu_destroy_pr(struct kvm_vcpu *vcpu)
 {
        int i;
 
index 7e345e0..83da1f8 100644 (file)
@@ -107,9 +107,20 @@ static u64 kvmppc_mmu_book3s_64_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr,
        return kvmppc_slb_calc_vpn(slb, eaddr);
 }
 
+static int mmu_pagesize(int mmu_pg)
+{
+       switch (mmu_pg) {
+       case MMU_PAGE_64K:
+               return 16;
+       case MMU_PAGE_16M:
+               return 24;
+       }
+       return 12;
+}
+
 static int kvmppc_mmu_book3s_64_get_pagesize(struct kvmppc_slb *slbe)
 {
-       return slbe->large ? 24 : 12;
+       return mmu_pagesize(slbe->base_page_size);
 }
 
 static u32 kvmppc_mmu_book3s_64_get_page(struct kvmppc_slb *slbe, gva_t eaddr)
@@ -119,11 +130,11 @@ static u32 kvmppc_mmu_book3s_64_get_page(struct kvmppc_slb *slbe, gva_t eaddr)
        return ((eaddr & kvmppc_slb_offset_mask(slbe)) >> p);
 }
 
-static hva_t kvmppc_mmu_book3s_64_get_pteg(
-                               struct kvmppc_vcpu_book3s *vcpu_book3s,
+static hva_t kvmppc_mmu_book3s_64_get_pteg(struct kvm_vcpu *vcpu,
                                struct kvmppc_slb *slbe, gva_t eaddr,
                                bool second)
 {
+       struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
        u64 hash, pteg, htabsize;
        u32 ssize;
        hva_t r;
@@ -148,10 +159,10 @@ static hva_t kvmppc_mmu_book3s_64_get_pteg(
 
        /* When running a PAPR guest, SDR1 contains a HVA address instead
            of a GPA */
-       if (vcpu_book3s->vcpu.arch.papr_enabled)
+       if (vcpu->arch.papr_enabled)
                r = pteg;
        else
-               r = gfn_to_hva(vcpu_book3s->vcpu.kvm, pteg >> PAGE_SHIFT);
+               r = gfn_to_hva(vcpu->kvm, pteg >> PAGE_SHIFT);
 
        if (kvm_is_error_hva(r))
                return r;
@@ -166,18 +177,38 @@ static u64 kvmppc_mmu_book3s_64_get_avpn(struct kvmppc_slb *slbe, gva_t eaddr)
        avpn = kvmppc_mmu_book3s_64_get_page(slbe, eaddr);
        avpn |= slbe->vsid << (kvmppc_slb_sid_shift(slbe) - p);
 
-       if (p < 24)
-               avpn >>= ((80 - p) - 56) - 8;
+       if (p < 16)
+               avpn >>= ((80 - p) - 56) - 8;   /* 16 - p */
        else
-               avpn <<= 8;
+               avpn <<= p - 16;
 
        return avpn;
 }
 
+/*
+ * Return page size encoded in the second word of a HPTE, or
+ * -1 for an invalid encoding for the base page size indicated by
+ * the SLB entry.  This doesn't handle mixed pagesize segments yet.
+ */
+static int decode_pagesize(struct kvmppc_slb *slbe, u64 r)
+{
+       switch (slbe->base_page_size) {
+       case MMU_PAGE_64K:
+               if ((r & 0xf000) == 0x1000)
+                       return MMU_PAGE_64K;
+               break;
+       case MMU_PAGE_16M:
+               if ((r & 0xff000) == 0)
+                       return MMU_PAGE_16M;
+               break;
+       }
+       return -1;
+}
+
 static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
-                               struct kvmppc_pte *gpte, bool data)
+                                     struct kvmppc_pte *gpte, bool data,
+                                     bool iswrite)
 {
-       struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
        struct kvmppc_slb *slbe;
        hva_t ptegp;
        u64 pteg[16];
@@ -189,6 +220,7 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
        u8 pp, key = 0;
        bool found = false;
        bool second = false;
+       int pgsize;
        ulong mp_ea = vcpu->arch.magic_page_ea;
 
        /* Magic page override */
@@ -202,6 +234,7 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
                gpte->may_execute = true;
                gpte->may_read = true;
                gpte->may_write = true;
+               gpte->page_size = MMU_PAGE_4K;
 
                return 0;
        }
@@ -222,8 +255,12 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
        v_mask = SLB_VSID_B | HPTE_V_AVPN | HPTE_V_LARGE | HPTE_V_VALID |
                HPTE_V_SECONDARY;
 
+       pgsize = slbe->large ? MMU_PAGE_16M : MMU_PAGE_4K;
+
+       mutex_lock(&vcpu->kvm->arch.hpt_mutex);
+
 do_second:
-       ptegp = kvmppc_mmu_book3s_64_get_pteg(vcpu_book3s, slbe, eaddr, second);
+       ptegp = kvmppc_mmu_book3s_64_get_pteg(vcpu, slbe, eaddr, second);
        if (kvm_is_error_hva(ptegp))
                goto no_page_found;
 
@@ -240,6 +277,13 @@ do_second:
        for (i=0; i<16; i+=2) {
                /* Check all relevant fields of 1st dword */
                if ((pteg[i] & v_mask) == v_val) {
+                       /* If large page bit is set, check pgsize encoding */
+                       if (slbe->large &&
+                           (vcpu->arch.hflags & BOOK3S_HFLAG_MULTI_PGSIZE)) {
+                               pgsize = decode_pagesize(slbe, pteg[i+1]);
+                               if (pgsize < 0)
+                                       continue;
+                       }
                        found = true;
                        break;
                }
@@ -256,13 +300,15 @@ do_second:
        v = pteg[i];
        r = pteg[i+1];
        pp = (r & HPTE_R_PP) | key;
-       eaddr_mask = 0xFFF;
+       if (r & HPTE_R_PP0)
+               pp |= 8;
 
        gpte->eaddr = eaddr;
        gpte->vpage = kvmppc_mmu_book3s_64_ea_to_vp(vcpu, eaddr, data);
-       if (slbe->large)
-               eaddr_mask = 0xFFFFFF;
+
+       eaddr_mask = (1ull << mmu_pagesize(pgsize)) - 1;
        gpte->raddr = (r & HPTE_R_RPN & ~eaddr_mask) | (eaddr & eaddr_mask);
+       gpte->page_size = pgsize;
        gpte->may_execute = ((r & HPTE_R_N) ? false : true);
        gpte->may_read = false;
        gpte->may_write = false;
@@ -277,6 +323,7 @@ do_second:
        case 3:
        case 5:
        case 7:
+       case 10:
                gpte->may_read = true;
                break;
        }
@@ -287,30 +334,37 @@ do_second:
 
        /* Update PTE R and C bits, so the guest's swapper knows we used the
         * page */
-       if (gpte->may_read) {
-               /* Set the accessed flag */
+       if (gpte->may_read && !(r & HPTE_R_R)) {
+               /*
+                * Set the accessed flag.
+                * We have to write this back with a single byte write
+                * because another vcpu may be accessing this on
+                * non-PAPR platforms such as mac99, and this is
+                * what real hardware does.
+                */
+               char __user *addr = (char __user *) &pteg[i+1];
                r |= HPTE_R_R;
+               put_user(r >> 8, addr + 6);
        }
-       if (data && gpte->may_write) {
-               /* Set the dirty flag -- XXX even if not writing */
+       if (iswrite && gpte->may_write && !(r & HPTE_R_C)) {
+               /* Set the dirty flag */
+               /* Use a single byte write */
+               char __user *addr = (char __user *) &pteg[i+1];
                r |= HPTE_R_C;
+               put_user(r, addr + 7);
        }
 
-       /* Write back into the PTEG */
-       if (pteg[i+1] != r) {
-               pteg[i+1] = r;
-               copy_to_user((void __user *)ptegp, pteg, sizeof(pteg));
-       }
+       mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
 
-       if (!gpte->may_read)
+       if (!gpte->may_read || (iswrite && !gpte->may_write))
                return -EPERM;
        return 0;
 
 no_page_found:
+       mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
        return -ENOENT;
 
 no_seg_found:
-
        dprintk("KVM MMU: Trigger segment fault\n");
        return -EINVAL;
 }
@@ -345,6 +399,21 @@ static void kvmppc_mmu_book3s_64_slbmte(struct kvm_vcpu *vcpu, u64 rs, u64 rb)
        slbe->nx    = (rs & SLB_VSID_N) ? 1 : 0;
        slbe->class = (rs & SLB_VSID_C) ? 1 : 0;
 
+       slbe->base_page_size = MMU_PAGE_4K;
+       if (slbe->large) {
+               if (vcpu->arch.hflags & BOOK3S_HFLAG_MULTI_PGSIZE) {
+                       switch (rs & SLB_VSID_LP) {
+                       case SLB_VSID_LP_00:
+                               slbe->base_page_size = MMU_PAGE_16M;
+                               break;
+                       case SLB_VSID_LP_01:
+                               slbe->base_page_size = MMU_PAGE_64K;
+                               break;
+                       }
+               } else
+                       slbe->base_page_size = MMU_PAGE_16M;
+       }
+
        slbe->orige = rb & (ESID_MASK | SLB_ESID_V);
        slbe->origv = rs;
 
@@ -460,14 +529,45 @@ static void kvmppc_mmu_book3s_64_tlbie(struct kvm_vcpu *vcpu, ulong va,
                                       bool large)
 {
        u64 mask = 0xFFFFFFFFFULL;
+       long i;
+       struct kvm_vcpu *v;
 
        dprintk("KVM MMU: tlbie(0x%lx)\n", va);
 
-       if (large)
-               mask = 0xFFFFFF000ULL;
-       kvmppc_mmu_pte_vflush(vcpu, va >> 12, mask);
+       /*
+        * The tlbie instruction changed behaviour starting with
+        * POWER6.  POWER6 and later don't have the large page flag
+        * in the instruction but in the RB value, along with bits
+        * indicating page and segment sizes.
+        */
+       if (vcpu->arch.hflags & BOOK3S_HFLAG_NEW_TLBIE) {
+               /* POWER6 or later */
+               if (va & 1) {           /* L bit */
+                       if ((va & 0xf000) == 0x1000)
+                               mask = 0xFFFFFFFF0ULL;  /* 64k page */
+                       else
+                               mask = 0xFFFFFF000ULL;  /* 16M page */
+               }
+       } else {
+               /* older processors, e.g. PPC970 */
+               if (large)
+                       mask = 0xFFFFFF000ULL;
+       }
+       /* flush this VA on all vcpus */
+       kvm_for_each_vcpu(i, v, vcpu->kvm)
+               kvmppc_mmu_pte_vflush(v, va >> 12, mask);
 }
 
+#ifdef CONFIG_PPC_64K_PAGES
+static int segment_contains_magic_page(struct kvm_vcpu *vcpu, ulong esid)
+{
+       ulong mp_ea = vcpu->arch.magic_page_ea;
+
+       return mp_ea && !(vcpu->arch.shared->msr & MSR_PR) &&
+               (mp_ea >> SID_SHIFT) == esid;
+}
+#endif
+
 static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
                                             u64 *vsid)
 {
@@ -475,11 +575,13 @@ static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
        struct kvmppc_slb *slb;
        u64 gvsid = esid;
        ulong mp_ea = vcpu->arch.magic_page_ea;
+       int pagesize = MMU_PAGE_64K;
 
        if (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) {
                slb = kvmppc_mmu_book3s_64_find_slbe(vcpu, ea);
                if (slb) {
                        gvsid = slb->vsid;
+                       pagesize = slb->base_page_size;
                        if (slb->tb) {
                                gvsid <<= SID_SHIFT_1T - SID_SHIFT;
                                gvsid |= esid & ((1ul << (SID_SHIFT_1T - SID_SHIFT)) - 1);
@@ -490,28 +592,41 @@ static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
 
        switch (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) {
        case 0:
-               *vsid = VSID_REAL | esid;
+               gvsid = VSID_REAL | esid;
                break;
        case MSR_IR:
-               *vsid = VSID_REAL_IR | gvsid;
+               gvsid |= VSID_REAL_IR;
                break;
        case MSR_DR:
-               *vsid = VSID_REAL_DR | gvsid;
+               gvsid |= VSID_REAL_DR;
                break;
        case MSR_DR|MSR_IR:
                if (!slb)
                        goto no_slb;
 
-               *vsid = gvsid;
                break;
        default:
                BUG();
                break;
        }
 
+#ifdef CONFIG_PPC_64K_PAGES
+       /*
+        * Mark this as a 64k segment if the host is using
+        * 64k pages, the host MMU supports 64k pages and
+        * the guest segment page size is >= 64k,
+        * but not if this segment contains the magic page.
+        */
+       if (pagesize >= MMU_PAGE_64K &&
+           mmu_psize_defs[MMU_PAGE_64K].shift &&
+           !segment_contains_magic_page(vcpu, esid))
+               gvsid |= VSID_64K;
+#endif
+
        if (vcpu->arch.shared->msr & MSR_PR)
-               *vsid |= VSID_PR;
+               gvsid |= VSID_PR;
 
+       *vsid = gvsid;
        return 0;
 
 no_slb:
index e524052..0d513af 100644 (file)
 #include <asm/machdep.h>
 #include <asm/mmu_context.h>
 #include <asm/hw_irq.h>
-#include "trace.h"
+#include "trace_pr.h"
 
 #define PTE_SIZE 12
 
 void kvmppc_mmu_invalidate_pte(struct kvm_vcpu *vcpu, struct hpte_cache *pte)
 {
        ppc_md.hpte_invalidate(pte->slot, pte->host_vpn,
-                              MMU_PAGE_4K, MMU_PAGE_4K, MMU_SEGSIZE_256M,
+                              pte->pagesize, pte->pagesize, MMU_SEGSIZE_256M,
                               false);
 }
 
@@ -78,7 +78,8 @@ static struct kvmppc_sid_map *find_sid_vsid(struct kvm_vcpu *vcpu, u64 gvsid)
        return NULL;
 }
 
-int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte)
+int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte,
+                       bool iswrite)
 {
        unsigned long vpn;
        pfn_t hpaddr;
@@ -90,16 +91,26 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte)
        int attempt = 0;
        struct kvmppc_sid_map *map;
        int r = 0;
+       int hpsize = MMU_PAGE_4K;
+       bool writable;
+       unsigned long mmu_seq;
+       struct kvm *kvm = vcpu->kvm;
+       struct hpte_cache *cpte;
+       unsigned long gfn = orig_pte->raddr >> PAGE_SHIFT;
+       unsigned long pfn;
+
+       /* used to check for invalidations in progress */
+       mmu_seq = kvm->mmu_notifier_seq;
+       smp_rmb();
 
        /* Get host physical address for gpa */
-       hpaddr = kvmppc_gfn_to_pfn(vcpu, orig_pte->raddr >> PAGE_SHIFT);
-       if (is_error_noslot_pfn(hpaddr)) {
-               printk(KERN_INFO "Couldn't get guest page for gfn %lx!\n", orig_pte->eaddr);
+       pfn = kvmppc_gfn_to_pfn(vcpu, gfn, iswrite, &writable);
+       if (is_error_noslot_pfn(pfn)) {
+               printk(KERN_INFO "Couldn't get guest page for gfn %lx!\n", gfn);
                r = -EINVAL;
                goto out;
        }
-       hpaddr <<= PAGE_SHIFT;
-       hpaddr |= orig_pte->raddr & (~0xfffULL & ~PAGE_MASK);
+       hpaddr = pfn << PAGE_SHIFT;
 
        /* and write the mapping ea -> hpa into the pt */
        vcpu->arch.mmu.esid_to_vsid(vcpu, orig_pte->eaddr >> SID_SHIFT, &vsid);
@@ -117,20 +128,39 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte)
                goto out;
        }
 
-       vsid = map->host_vsid;
-       vpn = hpt_vpn(orig_pte->eaddr, vsid, MMU_SEGSIZE_256M);
+       vpn = hpt_vpn(orig_pte->eaddr, map->host_vsid, MMU_SEGSIZE_256M);
 
-       if (!orig_pte->may_write)
-               rflags |= HPTE_R_PP;
-       else
-               mark_page_dirty(vcpu->kvm, orig_pte->raddr >> PAGE_SHIFT);
+       kvm_set_pfn_accessed(pfn);
+       if (!orig_pte->may_write || !writable)
+               rflags |= PP_RXRX;
+       else {
+               mark_page_dirty(vcpu->kvm, gfn);
+               kvm_set_pfn_dirty(pfn);
+       }
 
        if (!orig_pte->may_execute)
                rflags |= HPTE_R_N;
        else
-               kvmppc_mmu_flush_icache(hpaddr >> PAGE_SHIFT);
+               kvmppc_mmu_flush_icache(pfn);
+
+       /*
+        * Use 64K pages if possible; otherwise, on 64K page kernels,
+        * we need to transfer 4 more bits from guest real to host real addr.
+        */
+       if (vsid & VSID_64K)
+               hpsize = MMU_PAGE_64K;
+       else
+               hpaddr |= orig_pte->raddr & (~0xfffULL & ~PAGE_MASK);
+
+       hash = hpt_hash(vpn, mmu_psize_defs[hpsize].shift, MMU_SEGSIZE_256M);
 
-       hash = hpt_hash(vpn, PTE_SIZE, MMU_SEGSIZE_256M);
+       cpte = kvmppc_mmu_hpte_cache_next(vcpu);
+
+       spin_lock(&kvm->mmu_lock);
+       if (!cpte || mmu_notifier_retry(kvm, mmu_seq)) {
+               r = -EAGAIN;
+               goto out_unlock;
+       }
 
 map_again:
        hpteg = ((hash & htab_hash_mask) * HPTES_PER_GROUP);
@@ -139,11 +169,11 @@ map_again:
        if (attempt > 1)
                if (ppc_md.hpte_remove(hpteg) < 0) {
                        r = -1;
-                       goto out;
+                       goto out_unlock;
                }
 
        ret = ppc_md.hpte_insert(hpteg, vpn, hpaddr, rflags, vflags,
-                                MMU_PAGE_4K, MMU_PAGE_4K, MMU_SEGSIZE_256M);
+                                hpsize, hpsize, MMU_SEGSIZE_256M);
 
        if (ret < 0) {
                /* If we couldn't map a primary PTE, try a secondary */
@@ -152,8 +182,6 @@ map_again:
                attempt++;
                goto map_again;
        } else {
-               struct hpte_cache *pte = kvmppc_mmu_hpte_cache_next(vcpu);
-
                trace_kvm_book3s_64_mmu_map(rflags, hpteg,
                                            vpn, hpaddr, orig_pte);
 
@@ -164,19 +192,37 @@ map_again:
                        hpteg = ((hash & htab_hash_mask) * HPTES_PER_GROUP);
                }
 
-               pte->slot = hpteg + (ret & 7);
-               pte->host_vpn = vpn;
-               pte->pte = *orig_pte;
-               pte->pfn = hpaddr >> PAGE_SHIFT;
+               cpte->slot = hpteg + (ret & 7);
+               cpte->host_vpn = vpn;
+               cpte->pte = *orig_pte;
+               cpte->pfn = pfn;
+               cpte->pagesize = hpsize;
 
-               kvmppc_mmu_hpte_cache_map(vcpu, pte);
+               kvmppc_mmu_hpte_cache_map(vcpu, cpte);
+               cpte = NULL;
        }
-       kvm_release_pfn_clean(hpaddr >> PAGE_SHIFT);
+
+out_unlock:
+       spin_unlock(&kvm->mmu_lock);
+       kvm_release_pfn_clean(pfn);
+       if (cpte)
+               kvmppc_mmu_hpte_cache_free(cpte);
 
 out:
        return r;
 }
 
+void kvmppc_mmu_unmap_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte)
+{
+       u64 mask = 0xfffffffffULL;
+       u64 vsid;
+
+       vcpu->arch.mmu.esid_to_vsid(vcpu, pte->eaddr >> SID_SHIFT, &vsid);
+       if (vsid & VSID_64K)
+               mask = 0xffffffff0ULL;
+       kvmppc_mmu_pte_vflush(vcpu, pte->vpage, mask);
+}
+
 static struct kvmppc_sid_map *create_sid_map(struct kvm_vcpu *vcpu, u64 gvsid)
 {
        struct kvmppc_sid_map *map;
@@ -291,6 +337,12 @@ int kvmppc_mmu_map_segment(struct kvm_vcpu *vcpu, ulong eaddr)
        slb_vsid &= ~SLB_VSID_KP;
        slb_esid |= slb_index;
 
+#ifdef CONFIG_PPC_64K_PAGES
+       /* Set host segment base page size to 64K if possible */
+       if (gvsid & VSID_64K)
+               slb_vsid |= mmu_psize_defs[MMU_PAGE_64K].sllp;
+#endif
+
        svcpu->slb[slb_index].esid = slb_esid;
        svcpu->slb[slb_index].vsid = slb_vsid;
 
@@ -326,7 +378,7 @@ void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu)
        svcpu_put(svcpu);
 }
 
-void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
+void kvmppc_mmu_destroy_pr(struct kvm_vcpu *vcpu)
 {
        kvmppc_mmu_hpte_destroy(vcpu);
        __destroy_context(to_book3s(vcpu)->context_id[0]);
index 043eec8..f3ff587 100644 (file)
@@ -260,10 +260,6 @@ int kvmppc_mmu_hv_init(void)
        return 0;
 }
 
-void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
-{
-}
-
 static void kvmppc_mmu_book3s_64_hv_reset_msr(struct kvm_vcpu *vcpu)
 {
        kvmppc_set_msr(vcpu, MSR_SF | MSR_ME);
@@ -451,7 +447,7 @@ static unsigned long kvmppc_mmu_get_real_addr(unsigned long v, unsigned long r,
 }
 
 static int kvmppc_mmu_book3s_64_hv_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
-                       struct kvmppc_pte *gpte, bool data)
+                       struct kvmppc_pte *gpte, bool data, bool iswrite)
 {
        struct kvm *kvm = vcpu->kvm;
        struct kvmppc_slb *slbe;
@@ -906,21 +902,22 @@ static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
        return 0;
 }
 
-int kvm_unmap_hva(struct kvm *kvm, unsigned long hva)
+int kvm_unmap_hva_hv(struct kvm *kvm, unsigned long hva)
 {
        if (kvm->arch.using_mmu_notifiers)
                kvm_handle_hva(kvm, hva, kvm_unmap_rmapp);
        return 0;
 }
 
-int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end)
+int kvm_unmap_hva_range_hv(struct kvm *kvm, unsigned long start, unsigned long end)
 {
        if (kvm->arch.using_mmu_notifiers)
                kvm_handle_hva_range(kvm, start, end, kvm_unmap_rmapp);
        return 0;
 }
 
-void kvmppc_core_flush_memslot(struct kvm *kvm, struct kvm_memory_slot *memslot)
+void kvmppc_core_flush_memslot_hv(struct kvm *kvm,
+                                 struct kvm_memory_slot *memslot)
 {
        unsigned long *rmapp;
        unsigned long gfn;
@@ -994,7 +991,7 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
        return ret;
 }
 
-int kvm_age_hva(struct kvm *kvm, unsigned long hva)
+int kvm_age_hva_hv(struct kvm *kvm, unsigned long hva)
 {
        if (!kvm->arch.using_mmu_notifiers)
                return 0;
@@ -1032,14 +1029,14 @@ static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
        return ret;
 }
 
-int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
+int kvm_test_age_hva_hv(struct kvm *kvm, unsigned long hva)
 {
        if (!kvm->arch.using_mmu_notifiers)
                return 0;
        return kvm_handle_hva(kvm, hva, kvm_test_age_rmapp);
 }
 
-void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte)
+void kvm_set_spte_hva_hv(struct kvm *kvm, unsigned long hva, pte_t pte)
 {
        if (!kvm->arch.using_mmu_notifiers)
                return;
@@ -1512,9 +1509,8 @@ static ssize_t kvm_htab_write(struct file *file, const char __user *buf,
 
                                kvm->arch.vrma_slb_v = senc | SLB_VSID_B_1T |
                                        (VRMA_VSID << SLB_VSID_SHIFT_1T);
-                               lpcr = kvm->arch.lpcr & ~LPCR_VRMASD;
-                               lpcr |= senc << (LPCR_VRMASD_SH - 4);
-                               kvm->arch.lpcr = lpcr;
+                               lpcr = senc << (LPCR_VRMASD_SH - 4);
+                               kvmppc_update_lpcr(kvm, lpcr, LPCR_VRMASD);
                                rma_setup = 1;
                        }
                        ++i;
index 30c2f3b..2c25f54 100644 (file)
@@ -74,3 +74,4 @@ long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
        /* Didn't find the liobn, punt it to userspace */
        return H_TOO_HARD;
 }
+EXPORT_SYMBOL_GPL(kvmppc_h_put_tce);
index 360ce68..99d40f8 100644 (file)
@@ -86,8 +86,8 @@ static bool spr_allowed(struct kvm_vcpu *vcpu, enum priv_level level)
        return true;
 }
 
-int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
-                           unsigned int inst, int *advance)
+int kvmppc_core_emulate_op_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
+                             unsigned int inst, int *advance)
 {
        int emulated = EMULATE_DONE;
        int rt = get_rt(inst);
@@ -172,7 +172,7 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        vcpu->arch.mmu.tlbie(vcpu, addr, large);
                        break;
                }
-#ifdef CONFIG_KVM_BOOK3S_64_PR
+#ifdef CONFIG_PPC_BOOK3S_64
                case OP_31_XOP_FAKE_SC1:
                {
                        /* SC 1 papr hypercalls */
@@ -267,12 +267,9 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
 
                        r = kvmppc_st(vcpu, &addr, 32, zeros, true);
                        if ((r == -ENOENT) || (r == -EPERM)) {
-                               struct kvmppc_book3s_shadow_vcpu *svcpu;
-
-                               svcpu = svcpu_get(vcpu);
                                *advance = 0;
                                vcpu->arch.shared->dar = vaddr;
-                               svcpu->fault_dar = vaddr;
+                               vcpu->arch.fault_dar = vaddr;
 
                                dsisr = DSISR_ISSTORE;
                                if (r == -ENOENT)
@@ -281,8 +278,7 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                                        dsisr |= DSISR_PROTFAULT;
 
                                vcpu->arch.shared->dsisr = dsisr;
-                               svcpu->fault_dsisr = dsisr;
-                               svcpu_put(svcpu);
+                               vcpu->arch.fault_dsisr = dsisr;
 
                                kvmppc_book3s_queue_irqprio(vcpu,
                                        BOOK3S_INTERRUPT_DATA_STORAGE);
@@ -349,7 +345,7 @@ static struct kvmppc_bat *kvmppc_find_bat(struct kvm_vcpu *vcpu, int sprn)
        return bat;
 }
 
-int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
+int kvmppc_core_emulate_mtspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
 {
        int emulated = EMULATE_DONE;
 
@@ -472,7 +468,7 @@ unprivileged:
        return emulated;
 }
 
-int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val)
+int kvmppc_core_emulate_mfspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val)
 {
        int emulated = EMULATE_DONE;
 
index 7057a02..852989a 100644 (file)
 #include <linux/export.h>
 #include <asm/kvm_book3s.h>
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
 EXPORT_SYMBOL_GPL(kvmppc_hv_entry_trampoline);
-#else
+#endif
+#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
 EXPORT_SYMBOL_GPL(kvmppc_entry_trampoline);
 EXPORT_SYMBOL_GPL(kvmppc_load_up_fpu);
 #ifdef CONFIG_ALTIVEC
index 62a2b5a..072287f 100644 (file)
@@ -52,6 +52,9 @@
 #include <linux/vmalloc.h>
 #include <linux/highmem.h>
 #include <linux/hugetlb.h>
+#include <linux/module.h>
+
+#include "book3s.h"
 
 /* #define EXIT_DEBUG */
 /* #define EXIT_DEBUG_SIMPLE */
@@ -66,7 +69,7 @@
 static void kvmppc_end_cede(struct kvm_vcpu *vcpu);
 static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu);
 
-void kvmppc_fast_vcpu_kick(struct kvm_vcpu *vcpu)
+static void kvmppc_fast_vcpu_kick_hv(struct kvm_vcpu *vcpu)
 {
        int me;
        int cpu = vcpu->cpu;
@@ -125,7 +128,7 @@ void kvmppc_fast_vcpu_kick(struct kvm_vcpu *vcpu)
  * purely defensive; they should never fail.)
  */
 
-void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+static void kvmppc_core_vcpu_load_hv(struct kvm_vcpu *vcpu, int cpu)
 {
        struct kvmppc_vcore *vc = vcpu->arch.vcore;
 
@@ -143,7 +146,7 @@ void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
        spin_unlock(&vcpu->arch.tbacct_lock);
 }
 
-void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
+static void kvmppc_core_vcpu_put_hv(struct kvm_vcpu *vcpu)
 {
        struct kvmppc_vcore *vc = vcpu->arch.vcore;
 
@@ -155,17 +158,46 @@ void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
        spin_unlock(&vcpu->arch.tbacct_lock);
 }
 
-void kvmppc_set_msr(struct kvm_vcpu *vcpu, u64 msr)
+static void kvmppc_set_msr_hv(struct kvm_vcpu *vcpu, u64 msr)
 {
        vcpu->arch.shregs.msr = msr;
        kvmppc_end_cede(vcpu);
 }
 
-void kvmppc_set_pvr(struct kvm_vcpu *vcpu, u32 pvr)
+void kvmppc_set_pvr_hv(struct kvm_vcpu *vcpu, u32 pvr)
 {
        vcpu->arch.pvr = pvr;
 }
 
+int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat)
+{
+       unsigned long pcr = 0;
+       struct kvmppc_vcore *vc = vcpu->arch.vcore;
+
+       if (arch_compat) {
+               if (!cpu_has_feature(CPU_FTR_ARCH_206))
+                       return -EINVAL; /* 970 has no compat mode support */
+
+               switch (arch_compat) {
+               case PVR_ARCH_205:
+                       pcr = PCR_ARCH_205;
+                       break;
+               case PVR_ARCH_206:
+               case PVR_ARCH_206p:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       spin_lock(&vc->lock);
+       vc->arch_compat = arch_compat;
+       vc->pcr = pcr;
+       spin_unlock(&vc->lock);
+
+       return 0;
+}
+
 void kvmppc_dump_regs(struct kvm_vcpu *vcpu)
 {
        int r;
@@ -195,7 +227,7 @@ void kvmppc_dump_regs(struct kvm_vcpu *vcpu)
                pr_err("  ESID = %.16llx VSID = %.16llx\n",
                       vcpu->arch.slb[r].orige, vcpu->arch.slb[r].origv);
        pr_err("lpcr = %.16lx sdr1 = %.16lx last_inst = %.8x\n",
-              vcpu->kvm->arch.lpcr, vcpu->kvm->arch.sdr1,
+              vcpu->arch.vcore->lpcr, vcpu->kvm->arch.sdr1,
               vcpu->arch.last_inst);
 }
 
@@ -489,7 +521,7 @@ static void kvmppc_create_dtl_entry(struct kvm_vcpu *vcpu,
        memset(dt, 0, sizeof(struct dtl_entry));
        dt->dispatch_reason = 7;
        dt->processor_id = vc->pcpu + vcpu->arch.ptid;
-       dt->timebase = now;
+       dt->timebase = now + vc->tb_offset;
        dt->enqueue_to_dispatch_time = stolen;
        dt->srr0 = kvmppc_get_pc(vcpu);
        dt->srr1 = vcpu->arch.shregs.msr;
@@ -538,6 +570,15 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
                }
                break;
        case H_CONFER:
+               target = kvmppc_get_gpr(vcpu, 4);
+               if (target == -1)
+                       break;
+               tvcpu = kvmppc_find_vcpu(vcpu->kvm, target);
+               if (!tvcpu) {
+                       ret = H_PARAMETER;
+                       break;
+               }
+               kvm_vcpu_yield_to(tvcpu);
                break;
        case H_REGISTER_VPA:
                ret = do_h_register_vpa(vcpu, kvmppc_get_gpr(vcpu, 4),
@@ -576,8 +617,8 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
        return RESUME_GUEST;
 }
 
-static int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
-                             struct task_struct *tsk)
+static int kvmppc_handle_exit_hv(struct kvm_run *run, struct kvm_vcpu *vcpu,
+                                struct task_struct *tsk)
 {
        int r = RESUME_HOST;
 
@@ -671,16 +712,16 @@ static int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                printk(KERN_EMERG "trap=0x%x | pc=0x%lx | msr=0x%llx\n",
                        vcpu->arch.trap, kvmppc_get_pc(vcpu),
                        vcpu->arch.shregs.msr);
+               run->hw.hardware_exit_reason = vcpu->arch.trap;
                r = RESUME_HOST;
-               BUG();
                break;
        }
 
        return r;
 }
 
-int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
-                                 struct kvm_sregs *sregs)
+static int kvm_arch_vcpu_ioctl_get_sregs_hv(struct kvm_vcpu *vcpu,
+                                           struct kvm_sregs *sregs)
 {
        int i;
 
@@ -694,12 +735,12 @@ int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
        return 0;
 }
 
-int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
-                                 struct kvm_sregs *sregs)
+static int kvm_arch_vcpu_ioctl_set_sregs_hv(struct kvm_vcpu *vcpu,
+                                           struct kvm_sregs *sregs)
 {
        int i, j;
 
-       kvmppc_set_pvr(vcpu, sregs->pvr);
+       kvmppc_set_pvr_hv(vcpu, sregs->pvr);
 
        j = 0;
        for (i = 0; i < vcpu->arch.slb_nr; i++) {
@@ -714,7 +755,23 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
        return 0;
 }
 
-int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
+static void kvmppc_set_lpcr(struct kvm_vcpu *vcpu, u64 new_lpcr)
+{
+       struct kvmppc_vcore *vc = vcpu->arch.vcore;
+       u64 mask;
+
+       spin_lock(&vc->lock);
+       /*
+        * Userspace can only modify DPFD (default prefetch depth),
+        * ILE (interrupt little-endian) and TC (translation control).
+        */
+       mask = LPCR_DPFD | LPCR_ILE | LPCR_TC;
+       vc->lpcr = (vc->lpcr & ~mask) | (new_lpcr & mask);
+       spin_unlock(&vc->lock);
+}
+
+static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
+                                union kvmppc_one_reg *val)
 {
        int r = 0;
        long int i;
@@ -749,6 +806,12 @@ int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
                i = id - KVM_REG_PPC_PMC1;
                *val = get_reg_val(id, vcpu->arch.pmc[i]);
                break;
+       case KVM_REG_PPC_SIAR:
+               *val = get_reg_val(id, vcpu->arch.siar);
+               break;
+       case KVM_REG_PPC_SDAR:
+               *val = get_reg_val(id, vcpu->arch.sdar);
+               break;
 #ifdef CONFIG_VSX
        case KVM_REG_PPC_FPR0 ... KVM_REG_PPC_FPR31:
                if (cpu_has_feature(CPU_FTR_VSX)) {
@@ -787,6 +850,18 @@ int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
                val->vpaval.length = vcpu->arch.dtl.len;
                spin_unlock(&vcpu->arch.vpa_update_lock);
                break;
+       case KVM_REG_PPC_TB_OFFSET:
+               *val = get_reg_val(id, vcpu->arch.vcore->tb_offset);
+               break;
+       case KVM_REG_PPC_LPCR:
+               *val = get_reg_val(id, vcpu->arch.vcore->lpcr);
+               break;
+       case KVM_REG_PPC_PPR:
+               *val = get_reg_val(id, vcpu->arch.ppr);
+               break;
+       case KVM_REG_PPC_ARCH_COMPAT:
+               *val = get_reg_val(id, vcpu->arch.vcore->arch_compat);
+               break;
        default:
                r = -EINVAL;
                break;
@@ -795,7 +870,8 @@ int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
        return r;
 }
 
-int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
+static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
+                                union kvmppc_one_reg *val)
 {
        int r = 0;
        long int i;
@@ -833,6 +909,12 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
                i = id - KVM_REG_PPC_PMC1;
                vcpu->arch.pmc[i] = set_reg_val(id, *val);
                break;
+       case KVM_REG_PPC_SIAR:
+               vcpu->arch.siar = set_reg_val(id, *val);
+               break;
+       case KVM_REG_PPC_SDAR:
+               vcpu->arch.sdar = set_reg_val(id, *val);
+               break;
 #ifdef CONFIG_VSX
        case KVM_REG_PPC_FPR0 ... KVM_REG_PPC_FPR31:
                if (cpu_has_feature(CPU_FTR_VSX)) {
@@ -880,6 +962,20 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
                len -= len % sizeof(struct dtl_entry);
                r = set_vpa(vcpu, &vcpu->arch.dtl, addr, len);
                break;
+       case KVM_REG_PPC_TB_OFFSET:
+               /* round up to multiple of 2^24 */
+               vcpu->arch.vcore->tb_offset =
+                       ALIGN(set_reg_val(id, *val), 1UL << 24);
+               break;
+       case KVM_REG_PPC_LPCR:
+               kvmppc_set_lpcr(vcpu, set_reg_val(id, *val));
+               break;
+       case KVM_REG_PPC_PPR:
+               vcpu->arch.ppr = set_reg_val(id, *val);
+               break;
+       case KVM_REG_PPC_ARCH_COMPAT:
+               r = kvmppc_set_arch_compat(vcpu, set_reg_val(id, *val));
+               break;
        default:
                r = -EINVAL;
                break;
@@ -888,14 +984,8 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
        return r;
 }
 
-int kvmppc_core_check_processor_compat(void)
-{
-       if (cpu_has_feature(CPU_FTR_HVMODE))
-               return 0;
-       return -EIO;
-}
-
-struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
+static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm,
+                                                  unsigned int id)
 {
        struct kvm_vcpu *vcpu;
        int err = -EINVAL;
@@ -919,8 +1009,7 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
        vcpu->arch.mmcr[0] = MMCR0_FC;
        vcpu->arch.ctrl = CTRL_RUNLATCH;
        /* default to host PVR, since we can't spoof it */
-       vcpu->arch.pvr = mfspr(SPRN_PVR);
-       kvmppc_set_pvr(vcpu, vcpu->arch.pvr);
+       kvmppc_set_pvr_hv(vcpu, mfspr(SPRN_PVR));
        spin_lock_init(&vcpu->arch.vpa_update_lock);
        spin_lock_init(&vcpu->arch.tbacct_lock);
        vcpu->arch.busy_preempt = TB_NIL;
@@ -940,6 +1029,7 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
                        spin_lock_init(&vcore->lock);
                        init_waitqueue_head(&vcore->wq);
                        vcore->preempt_tb = TB_NIL;
+                       vcore->lpcr = kvm->arch.lpcr;
                }
                kvm->arch.vcores[core] = vcore;
                kvm->arch.online_vcores++;
@@ -972,7 +1062,7 @@ static void unpin_vpa(struct kvm *kvm, struct kvmppc_vpa *vpa)
                                        vpa->dirty);
 }
 
-void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
+static void kvmppc_core_vcpu_free_hv(struct kvm_vcpu *vcpu)
 {
        spin_lock(&vcpu->arch.vpa_update_lock);
        unpin_vpa(vcpu->kvm, &vcpu->arch.dtl);
@@ -983,6 +1073,12 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
        kmem_cache_free(kvm_vcpu_cache, vcpu);
 }
 
+static int kvmppc_core_check_requests_hv(struct kvm_vcpu *vcpu)
+{
+       /* Indicate we want to get back into the guest */
+       return 1;
+}
+
 static void kvmppc_set_timer(struct kvm_vcpu *vcpu)
 {
        unsigned long dec_nsec, now;
@@ -1264,8 +1360,8 @@ static void kvmppc_run_core(struct kvmppc_vcore *vc)
 
                ret = RESUME_GUEST;
                if (vcpu->arch.trap)
-                       ret = kvmppc_handle_exit(vcpu->arch.kvm_run, vcpu,
-                                                vcpu->arch.run_task);
+                       ret = kvmppc_handle_exit_hv(vcpu->arch.kvm_run, vcpu,
+                                                   vcpu->arch.run_task);
 
                vcpu->arch.ret = ret;
                vcpu->arch.trap = 0;
@@ -1424,7 +1520,7 @@ static int kvmppc_run_vcpu(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
        return vcpu->arch.ret;
 }
 
-int kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu)
+static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu)
 {
        int r;
        int srcu_idx;
@@ -1546,7 +1642,8 @@ static const struct file_operations kvm_rma_fops = {
        .release        = kvm_rma_release,
 };
 
-long kvm_vm_ioctl_allocate_rma(struct kvm *kvm, struct kvm_allocate_rma *ret)
+static long kvm_vm_ioctl_allocate_rma(struct kvm *kvm,
+                                     struct kvm_allocate_rma *ret)
 {
        long fd;
        struct kvm_rma_info *ri;
@@ -1592,7 +1689,8 @@ static void kvmppc_add_seg_page_size(struct kvm_ppc_one_seg_page_size **sps,
        (*sps)++;
 }
 
-int kvm_vm_ioctl_get_smmu_info(struct kvm *kvm, struct kvm_ppc_smmu_info *info)
+static int kvm_vm_ioctl_get_smmu_info_hv(struct kvm *kvm,
+                                        struct kvm_ppc_smmu_info *info)
 {
        struct kvm_ppc_one_seg_page_size *sps;
 
@@ -1613,7 +1711,8 @@ int kvm_vm_ioctl_get_smmu_info(struct kvm *kvm, struct kvm_ppc_smmu_info *info)
 /*
  * Get (and clear) the dirty memory log for a memory slot.
  */
-int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
+static int kvm_vm_ioctl_get_dirty_log_hv(struct kvm *kvm,
+                                        struct kvm_dirty_log *log)
 {
        struct kvm_memory_slot *memslot;
        int r;
@@ -1667,8 +1766,8 @@ static void unpin_slot(struct kvm_memory_slot *memslot)
        }
 }
 
-void kvmppc_core_free_memslot(struct kvm_memory_slot *free,
-                             struct kvm_memory_slot *dont)
+static void kvmppc_core_free_memslot_hv(struct kvm_memory_slot *free,
+                                       struct kvm_memory_slot *dont)
 {
        if (!dont || free->arch.rmap != dont->arch.rmap) {
                vfree(free->arch.rmap);
@@ -1681,8 +1780,8 @@ void kvmppc_core_free_memslot(struct kvm_memory_slot *free,
        }
 }
 
-int kvmppc_core_create_memslot(struct kvm_memory_slot *slot,
-                              unsigned long npages)
+static int kvmppc_core_create_memslot_hv(struct kvm_memory_slot *slot,
+                                        unsigned long npages)
 {
        slot->arch.rmap = vzalloc(npages * sizeof(*slot->arch.rmap));
        if (!slot->arch.rmap)
@@ -1692,9 +1791,9 @@ int kvmppc_core_create_memslot(struct kvm_memory_slot *slot,
        return 0;
 }
 
-int kvmppc_core_prepare_memory_region(struct kvm *kvm,
-                                     struct kvm_memory_slot *memslot,
-                                     struct kvm_userspace_memory_region *mem)
+static int kvmppc_core_prepare_memory_region_hv(struct kvm *kvm,
+                                       struct kvm_memory_slot *memslot,
+                                       struct kvm_userspace_memory_region *mem)
 {
        unsigned long *phys;
 
@@ -1710,9 +1809,9 @@ int kvmppc_core_prepare_memory_region(struct kvm *kvm,
        return 0;
 }
 
-void kvmppc_core_commit_memory_region(struct kvm *kvm,
-                                     struct kvm_userspace_memory_region *mem,
-                                     const struct kvm_memory_slot *old)
+static void kvmppc_core_commit_memory_region_hv(struct kvm *kvm,
+                               struct kvm_userspace_memory_region *mem,
+                               const struct kvm_memory_slot *old)
 {
        unsigned long npages = mem->memory_size >> PAGE_SHIFT;
        struct kvm_memory_slot *memslot;
@@ -1729,6 +1828,37 @@ void kvmppc_core_commit_memory_region(struct kvm *kvm,
        }
 }
 
+/*
+ * Update LPCR values in kvm->arch and in vcores.
+ * Caller must hold kvm->lock.
+ */
+void kvmppc_update_lpcr(struct kvm *kvm, unsigned long lpcr, unsigned long mask)
+{
+       long int i;
+       u32 cores_done = 0;
+
+       if ((kvm->arch.lpcr & mask) == lpcr)
+               return;
+
+       kvm->arch.lpcr = (kvm->arch.lpcr & ~mask) | lpcr;
+
+       for (i = 0; i < KVM_MAX_VCORES; ++i) {
+               struct kvmppc_vcore *vc = kvm->arch.vcores[i];
+               if (!vc)
+                       continue;
+               spin_lock(&vc->lock);
+               vc->lpcr = (vc->lpcr & ~mask) | lpcr;
+               spin_unlock(&vc->lock);
+               if (++cores_done >= kvm->arch.online_vcores)
+                       break;
+       }
+}
+
+static void kvmppc_mmu_destroy_hv(struct kvm_vcpu *vcpu)
+{
+       return;
+}
+
 static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
 {
        int err = 0;
@@ -1737,7 +1867,8 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
        unsigned long hva;
        struct kvm_memory_slot *memslot;
        struct vm_area_struct *vma;
-       unsigned long lpcr, senc;
+       unsigned long lpcr = 0, senc;
+       unsigned long lpcr_mask = 0;
        unsigned long psize, porder;
        unsigned long rma_size;
        unsigned long rmls;
@@ -1802,9 +1933,9 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
                senc = slb_pgsize_encoding(psize);
                kvm->arch.vrma_slb_v = senc | SLB_VSID_B_1T |
                        (VRMA_VSID << SLB_VSID_SHIFT_1T);
-               lpcr = kvm->arch.lpcr & ~LPCR_VRMASD;
-               lpcr |= senc << (LPCR_VRMASD_SH - 4);
-               kvm->arch.lpcr = lpcr;
+               lpcr_mask = LPCR_VRMASD;
+               /* the -4 is to account for senc values starting at 0x10 */
+               lpcr = senc << (LPCR_VRMASD_SH - 4);
 
                /* Create HPTEs in the hash page table for the VRMA */
                kvmppc_map_vrma(vcpu, memslot, porder);
@@ -1825,23 +1956,21 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
                kvm->arch.rma = ri;
 
                /* Update LPCR and RMOR */
-               lpcr = kvm->arch.lpcr;
                if (cpu_has_feature(CPU_FTR_ARCH_201)) {
                        /* PPC970; insert RMLS value (split field) in HID4 */
-                       lpcr &= ~((1ul << HID4_RMLS0_SH) |
-                                 (3ul << HID4_RMLS2_SH));
-                       lpcr |= ((rmls >> 2) << HID4_RMLS0_SH) |
+                       lpcr_mask = (1ul << HID4_RMLS0_SH) |
+                               (3ul << HID4_RMLS2_SH) | HID4_RMOR;
+                       lpcr = ((rmls >> 2) << HID4_RMLS0_SH) |
                                ((rmls & 3) << HID4_RMLS2_SH);
                        /* RMOR is also in HID4 */
                        lpcr |= ((ri->base_pfn >> (26 - PAGE_SHIFT)) & 0xffff)
                                << HID4_RMOR_SH;
                } else {
                        /* POWER7 */
-                       lpcr &= ~(LPCR_VPM0 | LPCR_VRMA_L);
-                       lpcr |= rmls << LPCR_RMLS_SH;
+                       lpcr_mask = LPCR_VPM0 | LPCR_VRMA_L | LPCR_RMLS;
+                       lpcr = rmls << LPCR_RMLS_SH;
                        kvm->arch.rmor = ri->base_pfn << PAGE_SHIFT;
                }
-               kvm->arch.lpcr = lpcr;
                pr_info("KVM: Using RMO at %lx size %lx (LPCR = %lx)\n",
                        ri->base_pfn << PAGE_SHIFT, rma_size, lpcr);
 
@@ -1860,6 +1989,8 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
                }
        }
 
+       kvmppc_update_lpcr(kvm, lpcr, lpcr_mask);
+
        /* Order updates to kvm->arch.lpcr etc. vs. rma_setup_done */
        smp_wmb();
        kvm->arch.rma_setup_done = 1;
@@ -1875,7 +2006,7 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
        goto out_srcu;
 }
 
-int kvmppc_core_init_vm(struct kvm *kvm)
+static int kvmppc_core_init_vm_hv(struct kvm *kvm)
 {
        unsigned long lpcr, lpid;
 
@@ -1893,9 +2024,6 @@ int kvmppc_core_init_vm(struct kvm *kvm)
         */
        cpumask_setall(&kvm->arch.need_tlb_flush);
 
-       INIT_LIST_HEAD(&kvm->arch.spapr_tce_tables);
-       INIT_LIST_HEAD(&kvm->arch.rtas_tokens);
-
        kvm->arch.rma = NULL;
 
        kvm->arch.host_sdr1 = mfspr(SPRN_SDR1);
@@ -1931,61 +2059,162 @@ int kvmppc_core_init_vm(struct kvm *kvm)
        return 0;
 }
 
-void kvmppc_core_destroy_vm(struct kvm *kvm)
+static void kvmppc_free_vcores(struct kvm *kvm)
+{
+       long int i;
+
+       for (i = 0; i < KVM_MAX_VCORES; ++i)
+               kfree(kvm->arch.vcores[i]);
+       kvm->arch.online_vcores = 0;
+}
+
+static void kvmppc_core_destroy_vm_hv(struct kvm *kvm)
 {
        uninhibit_secondary_onlining();
 
+       kvmppc_free_vcores(kvm);
        if (kvm->arch.rma) {
                kvm_release_rma(kvm->arch.rma);
                kvm->arch.rma = NULL;
        }
 
-       kvmppc_rtas_tokens_free(kvm);
-
        kvmppc_free_hpt(kvm);
-       WARN_ON(!list_empty(&kvm->arch.spapr_tce_tables));
 }
 
-/* These are stubs for now */
-void kvmppc_mmu_pte_pflush(struct kvm_vcpu *vcpu, ulong pa_start, ulong pa_end)
+/* We don't need to emulate any privileged instructions or dcbz */
+static int kvmppc_core_emulate_op_hv(struct kvm_run *run, struct kvm_vcpu *vcpu,
+                                    unsigned int inst, int *advance)
 {
+       return EMULATE_FAIL;
 }
 
-/* We don't need to emulate any privileged instructions or dcbz */
-int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
-                           unsigned int inst, int *advance)
+static int kvmppc_core_emulate_mtspr_hv(struct kvm_vcpu *vcpu, int sprn,
+                                       ulong spr_val)
 {
        return EMULATE_FAIL;
 }
 
-int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
+static int kvmppc_core_emulate_mfspr_hv(struct kvm_vcpu *vcpu, int sprn,
+                                       ulong *spr_val)
 {
        return EMULATE_FAIL;
 }
 
-int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val)
+static int kvmppc_core_check_processor_compat_hv(void)
 {
-       return EMULATE_FAIL;
+       if (!cpu_has_feature(CPU_FTR_HVMODE))
+               return -EIO;
+       return 0;
 }
 
-static int kvmppc_book3s_hv_init(void)
+static long kvm_arch_vm_ioctl_hv(struct file *filp,
+                                unsigned int ioctl, unsigned long arg)
 {
-       int r;
+       struct kvm *kvm __maybe_unused = filp->private_data;
+       void __user *argp = (void __user *)arg;
+       long r;
 
-       r = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE);
+       switch (ioctl) {
 
-       if (r)
+       case KVM_ALLOCATE_RMA: {
+               struct kvm_allocate_rma rma;
+               struct kvm *kvm = filp->private_data;
+
+               r = kvm_vm_ioctl_allocate_rma(kvm, &rma);
+               if (r >= 0 && copy_to_user(argp, &rma, sizeof(rma)))
+                       r = -EFAULT;
+               break;
+       }
+
+       case KVM_PPC_ALLOCATE_HTAB: {
+               u32 htab_order;
+
+               r = -EFAULT;
+               if (get_user(htab_order, (u32 __user *)argp))
+                       break;
+               r = kvmppc_alloc_reset_hpt(kvm, &htab_order);
+               if (r)
+                       break;
+               r = -EFAULT;
+               if (put_user(htab_order, (u32 __user *)argp))
+                       break;
+               r = 0;
+               break;
+       }
+
+       case KVM_PPC_GET_HTAB_FD: {
+               struct kvm_get_htab_fd ghf;
+
+               r = -EFAULT;
+               if (copy_from_user(&ghf, argp, sizeof(ghf)))
+                       break;
+               r = kvm_vm_ioctl_get_htab_fd(kvm, &ghf);
+               break;
+       }
+
+       default:
+               r = -ENOTTY;
+       }
+
+       return r;
+}
+
+static struct kvmppc_ops kvm_ops_hv = {
+       .get_sregs = kvm_arch_vcpu_ioctl_get_sregs_hv,
+       .set_sregs = kvm_arch_vcpu_ioctl_set_sregs_hv,
+       .get_one_reg = kvmppc_get_one_reg_hv,
+       .set_one_reg = kvmppc_set_one_reg_hv,
+       .vcpu_load   = kvmppc_core_vcpu_load_hv,
+       .vcpu_put    = kvmppc_core_vcpu_put_hv,
+       .set_msr     = kvmppc_set_msr_hv,
+       .vcpu_run    = kvmppc_vcpu_run_hv,
+       .vcpu_create = kvmppc_core_vcpu_create_hv,
+       .vcpu_free   = kvmppc_core_vcpu_free_hv,
+       .check_requests = kvmppc_core_check_requests_hv,
+       .get_dirty_log  = kvm_vm_ioctl_get_dirty_log_hv,
+       .flush_memslot  = kvmppc_core_flush_memslot_hv,
+       .prepare_memory_region = kvmppc_core_prepare_memory_region_hv,
+       .commit_memory_region  = kvmppc_core_commit_memory_region_hv,
+       .unmap_hva = kvm_unmap_hva_hv,
+       .unmap_hva_range = kvm_unmap_hva_range_hv,
+       .age_hva  = kvm_age_hva_hv,
+       .test_age_hva = kvm_test_age_hva_hv,
+       .set_spte_hva = kvm_set_spte_hva_hv,
+       .mmu_destroy  = kvmppc_mmu_destroy_hv,
+       .free_memslot = kvmppc_core_free_memslot_hv,
+       .create_memslot = kvmppc_core_create_memslot_hv,
+       .init_vm =  kvmppc_core_init_vm_hv,
+       .destroy_vm = kvmppc_core_destroy_vm_hv,
+       .get_smmu_info = kvm_vm_ioctl_get_smmu_info_hv,
+       .emulate_op = kvmppc_core_emulate_op_hv,
+       .emulate_mtspr = kvmppc_core_emulate_mtspr_hv,
+       .emulate_mfspr = kvmppc_core_emulate_mfspr_hv,
+       .fast_vcpu_kick = kvmppc_fast_vcpu_kick_hv,
+       .arch_vm_ioctl  = kvm_arch_vm_ioctl_hv,
+};
+
+static int kvmppc_book3s_init_hv(void)
+{
+       int r;
+       /*
+        * FIXME!! Do we need to check on all cpus ?
+        */
+       r = kvmppc_core_check_processor_compat_hv();
+       if (r < 0)
                return r;
 
-       r = kvmppc_mmu_hv_init();
+       kvm_ops_hv.owner = THIS_MODULE;
+       kvmppc_hv_ops = &kvm_ops_hv;
 
+       r = kvmppc_mmu_hv_init();
        return r;
 }
 
-static void kvmppc_book3s_hv_exit(void)
+static void kvmppc_book3s_exit_hv(void)
 {
-       kvm_exit();
+       kvmppc_hv_ops = NULL;
 }
 
-module_init(kvmppc_book3s_hv_init);
-module_exit(kvmppc_book3s_hv_exit);
+module_init(kvmppc_book3s_init_hv);
+module_exit(kvmppc_book3s_exit_hv);
+MODULE_LICENSE("GPL");
index 37f1cc4..928142c 100644 (file)
@@ -158,9 +158,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
  * Interrupts are enabled again at this point.
  */
 
-.global kvmppc_handler_highmem
-kvmppc_handler_highmem:
-
        /*
         * Register usage at this point:
         *
index c71103b..bc8de75 100644 (file)
 #error Need to fix lppaca and SLB shadow accesses in little endian mode
 #endif
 
-/*****************************************************************************
- *                                                                           *
- *        Real Mode handlers that need to be in the linear mapping           *
- *                                                                           *
- ****************************************************************************/
-
-       .globl  kvmppc_skip_interrupt
-kvmppc_skip_interrupt:
-       mfspr   r13,SPRN_SRR0
-       addi    r13,r13,4
-       mtspr   SPRN_SRR0,r13
-       GET_SCRATCH0(r13)
-       rfid
-       b       .
-
-       .globl  kvmppc_skip_Hinterrupt
-kvmppc_skip_Hinterrupt:
-       mfspr   r13,SPRN_HSRR0
-       addi    r13,r13,4
-       mtspr   SPRN_HSRR0,r13
-       GET_SCRATCH0(r13)
-       hrfid
-       b       .
-
 /*
  * Call kvmppc_hv_entry in real mode.
  * Must be called with interrupts hard-disabled.
@@ -66,8 +42,11 @@ kvmppc_skip_Hinterrupt:
  * LR = return address to continue at after eventually re-enabling MMU
  */
 _GLOBAL(kvmppc_hv_entry_trampoline)
+       mflr    r0
+       std     r0, PPC_LR_STKOFF(r1)
+       stdu    r1, -112(r1)
        mfmsr   r10
-       LOAD_REG_ADDR(r5, kvmppc_hv_entry)
+       LOAD_REG_ADDR(r5, kvmppc_call_hv_entry)
        li      r0,MSR_RI
        andc    r0,r10,r0
        li      r6,MSR_IR | MSR_DR
@@ -77,11 +56,103 @@ _GLOBAL(kvmppc_hv_entry_trampoline)
        mtsrr1  r6
        RFI
 
-/******************************************************************************
- *                                                                            *
- *                               Entry code                                   *
- *                                                                            *
- *****************************************************************************/
+kvmppc_call_hv_entry:
+       bl      kvmppc_hv_entry
+
+       /* Back from guest - restore host state and return to caller */
+
+       /* Restore host DABR and DABRX */
+       ld      r5,HSTATE_DABR(r13)
+       li      r6,7
+       mtspr   SPRN_DABR,r5
+       mtspr   SPRN_DABRX,r6
+
+       /* Restore SPRG3 */
+       ld      r3,PACA_SPRG3(r13)
+       mtspr   SPRN_SPRG3,r3
+
+       /*
+        * Reload DEC.  HDEC interrupts were disabled when
+        * we reloaded the host's LPCR value.
+        */
+       ld      r3, HSTATE_DECEXP(r13)
+       mftb    r4
+       subf    r4, r4, r3
+       mtspr   SPRN_DEC, r4
+
+       /* Reload the host's PMU registers */
+       ld      r3, PACALPPACAPTR(r13)  /* is the host using the PMU? */
+       lbz     r4, LPPACA_PMCINUSE(r3)
+       cmpwi   r4, 0
+       beq     23f                     /* skip if not */
+       lwz     r3, HSTATE_PMC(r13)
+       lwz     r4, HSTATE_PMC + 4(r13)
+       lwz     r5, HSTATE_PMC + 8(r13)
+       lwz     r6, HSTATE_PMC + 12(r13)
+       lwz     r8, HSTATE_PMC + 16(r13)
+       lwz     r9, HSTATE_PMC + 20(r13)
+BEGIN_FTR_SECTION
+       lwz     r10, HSTATE_PMC + 24(r13)
+       lwz     r11, HSTATE_PMC + 28(r13)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
+       mtspr   SPRN_PMC1, r3
+       mtspr   SPRN_PMC2, r4
+       mtspr   SPRN_PMC3, r5
+       mtspr   SPRN_PMC4, r6
+       mtspr   SPRN_PMC5, r8
+       mtspr   SPRN_PMC6, r9
+BEGIN_FTR_SECTION
+       mtspr   SPRN_PMC7, r10
+       mtspr   SPRN_PMC8, r11
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
+       ld      r3, HSTATE_MMCR(r13)
+       ld      r4, HSTATE_MMCR + 8(r13)
+       ld      r5, HSTATE_MMCR + 16(r13)
+       mtspr   SPRN_MMCR1, r4
+       mtspr   SPRN_MMCRA, r5
+       mtspr   SPRN_MMCR0, r3
+       isync
+23:
+
+       /*
+        * For external and machine check interrupts, we need
+        * to call the Linux handler to process the interrupt.
+        * We do that by jumping to absolute address 0x500 for
+        * external interrupts, or the machine_check_fwnmi label
+        * for machine checks (since firmware might have patched
+        * the vector area at 0x200).  The [h]rfid at the end of the
+        * handler will return to the book3s_hv_interrupts.S code.
+        * For other interrupts we do the rfid to get back
+        * to the book3s_hv_interrupts.S code here.
+        */
+       ld      r8, 112+PPC_LR_STKOFF(r1)
+       addi    r1, r1, 112
+       ld      r7, HSTATE_HOST_MSR(r13)
+
+       cmpwi   cr1, r12, BOOK3S_INTERRUPT_MACHINE_CHECK
+       cmpwi   r12, BOOK3S_INTERRUPT_EXTERNAL
+BEGIN_FTR_SECTION
+       beq     11f
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
+
+       /* RFI into the highmem handler, or branch to interrupt handler */
+       mfmsr   r6
+       li      r0, MSR_RI
+       andc    r6, r6, r0
+       mtmsrd  r6, 1                   /* Clear RI in MSR */
+       mtsrr0  r8
+       mtsrr1  r7
+       beqa    0x500                   /* external interrupt (PPC970) */
+       beq     cr1, 13f                /* machine check */
+       RFI
+
+       /* On POWER7, we have external interrupts set to use HSRR0/1 */
+11:    mtspr   SPRN_HSRR0, r8
+       mtspr   SPRN_HSRR1, r7
+       ba      0x500
+
+13:    b       machine_check_fwnmi
+
 
 /*
  * We come in here when wakened from nap mode on a secondary hw thread.
@@ -137,7 +208,7 @@ kvm_start_guest:
        cmpdi   r4,0
        /* if we have no vcpu to run, go back to sleep */
        beq     kvm_no_guest
-       b       kvmppc_hv_entry
+       b       30f
 
 27:    /* XXX should handle hypervisor maintenance interrupts etc. here */
        b       kvm_no_guest
@@ -147,6 +218,57 @@ kvm_start_guest:
        stw     r8,HSTATE_SAVED_XIRR(r13)
        b       kvm_no_guest
 
+30:    bl      kvmppc_hv_entry
+
+       /* Back from the guest, go back to nap */
+       /* Clear our vcpu pointer so we don't come back in early */
+       li      r0, 0
+       std     r0, HSTATE_KVM_VCPU(r13)
+       lwsync
+       /* Clear any pending IPI - we're an offline thread */
+       ld      r5, HSTATE_XICS_PHYS(r13)
+       li      r7, XICS_XIRR
+       lwzcix  r3, r5, r7              /* ack any pending interrupt */
+       rlwinm. r0, r3, 0, 0xffffff     /* any pending? */
+       beq     37f
+       sync
+       li      r0, 0xff
+       li      r6, XICS_MFRR
+       stbcix  r0, r5, r6              /* clear the IPI */
+       stwcix  r3, r5, r7              /* EOI it */
+37:    sync
+
+       /* increment the nap count and then go to nap mode */
+       ld      r4, HSTATE_KVM_VCORE(r13)
+       addi    r4, r4, VCORE_NAP_COUNT
+       lwsync                          /* make previous updates visible */
+51:    lwarx   r3, 0, r4
+       addi    r3, r3, 1
+       stwcx.  r3, 0, r4
+       bne     51b
+
+kvm_no_guest:
+       li      r0, KVM_HWTHREAD_IN_NAP
+       stb     r0, HSTATE_HWTHREAD_STATE(r13)
+       li      r3, LPCR_PECE0
+       mfspr   r4, SPRN_LPCR
+       rlwimi  r4, r3, 0, LPCR_PECE0 | LPCR_PECE1
+       mtspr   SPRN_LPCR, r4
+       isync
+       std     r0, HSTATE_SCRATCH0(r13)
+       ptesync
+       ld      r0, HSTATE_SCRATCH0(r13)
+1:     cmpd    r0, r0
+       bne     1b
+       nap
+       b       .
+
+/******************************************************************************
+ *                                                                            *
+ *                               Entry code                                   *
+ *                                                                            *
+ *****************************************************************************/
+
 .global kvmppc_hv_entry
 kvmppc_hv_entry:
 
@@ -159,7 +281,8 @@ kvmppc_hv_entry:
         * all other volatile GPRS = free
         */
        mflr    r0
-       std     r0, HSTATE_VMHANDLER(r13)
+       std     r0, PPC_LR_STKOFF(r1)
+       stdu    r1, -112(r1)
 
        /* Set partition DABR */
        /* Do this before re-enabling PMU to avoid P7 DABR corruption bug */
@@ -200,8 +323,12 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
        ld      r3, VCPU_MMCR(r4)
        ld      r5, VCPU_MMCR + 8(r4)
        ld      r6, VCPU_MMCR + 16(r4)
+       ld      r7, VCPU_SIAR(r4)
+       ld      r8, VCPU_SDAR(r4)
        mtspr   SPRN_MMCR1, r5
        mtspr   SPRN_MMCRA, r6
+       mtspr   SPRN_SIAR, r7
+       mtspr   SPRN_SDAR, r8
        mtspr   SPRN_MMCR0, r3
        isync
 
@@ -254,22 +381,15 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
        /* Save R1 in the PACA */
        std     r1, HSTATE_HOST_R1(r13)
 
-       /* Increment yield count if they have a VPA */
-       ld      r3, VCPU_VPA(r4)
-       cmpdi   r3, 0
-       beq     25f
-       lwz     r5, LPPACA_YIELDCOUNT(r3)
-       addi    r5, r5, 1
-       stw     r5, LPPACA_YIELDCOUNT(r3)
-       li      r6, 1
-       stb     r6, VCPU_VPA_DIRTY(r4)
-25:
        /* Load up DAR and DSISR */
        ld      r5, VCPU_DAR(r4)
        lwz     r6, VCPU_DSISR(r4)
        mtspr   SPRN_DAR, r5
        mtspr   SPRN_DSISR, r6
 
+       li      r6, KVM_GUEST_MODE_HOST_HV
+       stb     r6, HSTATE_IN_GUEST(r13)
+
 BEGIN_FTR_SECTION
        /* Restore AMR and UAMOR, set AMOR to all 1s */
        ld      r5,VCPU_AMR(r4)
@@ -343,7 +463,28 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
        bdnz    28b
        ptesync
 
-22:    li      r0,1
+       /* Add timebase offset onto timebase */
+22:    ld      r8,VCORE_TB_OFFSET(r5)
+       cmpdi   r8,0
+       beq     37f
+       mftb    r6              /* current host timebase */
+       add     r8,r8,r6
+       mtspr   SPRN_TBU40,r8   /* update upper 40 bits */
+       mftb    r7              /* check if lower 24 bits overflowed */
+       clrldi  r6,r6,40
+       clrldi  r7,r7,40
+       cmpld   r7,r6
+       bge     37f
+       addis   r8,r8,0x100     /* if so, increment upper 40 bits */
+       mtspr   SPRN_TBU40,r8
+
+       /* Load guest PCR value to select appropriate compat mode */
+37:    ld      r7, VCORE_PCR(r5)
+       cmpdi   r7, 0
+       beq     38f
+       mtspr   SPRN_PCR, r7
+38:
+       li      r0,1
        stb     r0,VCORE_IN_GUEST(r5)   /* signal secondaries to continue */
        b       10f
 
@@ -353,12 +494,22 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
        beq     20b
 
        /* Set LPCR and RMOR. */
-10:    ld      r8,KVM_LPCR(r9)
+10:    ld      r8,VCORE_LPCR(r5)
        mtspr   SPRN_LPCR,r8
        ld      r8,KVM_RMOR(r9)
        mtspr   SPRN_RMOR,r8
        isync
 
+       /* Increment yield count if they have a VPA */
+       ld      r3, VCPU_VPA(r4)
+       cmpdi   r3, 0
+       beq     25f
+       lwz     r5, LPPACA_YIELDCOUNT(r3)
+       addi    r5, r5, 1
+       stw     r5, LPPACA_YIELDCOUNT(r3)
+       li      r6, 1
+       stb     r6, VCPU_VPA_DIRTY(r4)
+25:
        /* Check if HDEC expires soon */
        mfspr   r3,SPRN_HDEC
        cmpwi   r3,10
@@ -405,7 +556,8 @@ toc_tlbie_lock:
        bne     24b
        isync
 
-       ld      r7,KVM_LPCR(r9)         /* use kvm->arch.lpcr to store HID4 */
+       ld      r5,HSTATE_KVM_VCORE(r13)
+       ld      r7,VCORE_LPCR(r5)       /* use vcore->lpcr to store HID4 */
        li      r0,0x18f
        rotldi  r0,r0,HID4_LPID5_SH     /* all lpid bits in HID4 = 1 */
        or      r0,r7,r0
@@ -541,7 +693,7 @@ fast_guest_return:
        mtspr   SPRN_HSRR1,r11
 
        /* Activate guest mode, so faults get handled by KVM */
-       li      r9, KVM_GUEST_MODE_GUEST
+       li      r9, KVM_GUEST_MODE_GUEST_HV
        stb     r9, HSTATE_IN_GUEST(r13)
 
        /* Enter guest */
@@ -550,13 +702,15 @@ BEGIN_FTR_SECTION
        ld      r5, VCPU_CFAR(r4)
        mtspr   SPRN_CFAR, r5
 END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
+BEGIN_FTR_SECTION
+       ld      r0, VCPU_PPR(r4)
+END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
 
        ld      r5, VCPU_LR(r4)
        lwz     r6, VCPU_CR(r4)
        mtlr    r5
        mtcr    r6
 
-       ld      r0, VCPU_GPR(R0)(r4)
        ld      r1, VCPU_GPR(R1)(r4)
        ld      r2, VCPU_GPR(R2)(r4)
        ld      r3, VCPU_GPR(R3)(r4)
@@ -570,6 +724,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
        ld      r12, VCPU_GPR(R12)(r4)
        ld      r13, VCPU_GPR(R13)(r4)
 
+BEGIN_FTR_SECTION
+       mtspr   SPRN_PPR, r0
+END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
+       ld      r0, VCPU_GPR(R0)(r4)
        ld      r4, VCPU_GPR(R4)(r4)
 
        hrfid
@@ -584,8 +742,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
 /*
  * We come here from the first-level interrupt handlers.
  */
-       .globl  kvmppc_interrupt
-kvmppc_interrupt:
+       .globl  kvmppc_interrupt_hv
+kvmppc_interrupt_hv:
        /*
         * Register contents:
         * R12          = interrupt vector
@@ -595,6 +753,19 @@ kvmppc_interrupt:
         */
        /* abuse host_r2 as third scratch area; we get r2 from PACATOC(r13) */
        std     r9, HSTATE_HOST_R2(r13)
+
+       lbz     r9, HSTATE_IN_GUEST(r13)
+       cmpwi   r9, KVM_GUEST_MODE_HOST_HV
+       beq     kvmppc_bad_host_intr
+#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
+       cmpwi   r9, KVM_GUEST_MODE_GUEST
+       ld      r9, HSTATE_HOST_R2(r13)
+       beq     kvmppc_interrupt_pr
+#endif
+       /* We're now back in the host but in guest MMU context */
+       li      r9, KVM_GUEST_MODE_HOST_HV
+       stb     r9, HSTATE_IN_GUEST(r13)
+
        ld      r9, HSTATE_KVM_VCPU(r13)
 
        /* Save registers */
@@ -620,6 +791,10 @@ BEGIN_FTR_SECTION
        ld      r3, HSTATE_CFAR(r13)
        std     r3, VCPU_CFAR(r9)
 END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
+BEGIN_FTR_SECTION
+       ld      r4, HSTATE_PPR(r13)
+       std     r4, VCPU_PPR(r9)
+END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
 
        /* Restore R1/R2 so we can handle faults */
        ld      r1, HSTATE_HOST_R1(r13)
@@ -642,10 +817,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
        std     r3, VCPU_GPR(R13)(r9)
        std     r4, VCPU_LR(r9)
 
-       /* Unset guest mode */
-       li      r0, KVM_GUEST_MODE_NONE
-       stb     r0, HSTATE_IN_GUEST(r13)
-
        stw     r12,VCPU_TRAP(r9)
 
        /* Save HEIR (HV emulation assist reg) in last_inst
@@ -696,46 +867,11 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
         * set, we know the host wants us out so let's do it now
         */
 do_ext_interrupt:
-       lbz     r0, HSTATE_HOST_IPI(r13)
-       cmpwi   r0, 0
-       bne     ext_interrupt_to_host
-
-       /* Now read the interrupt from the ICP */
-       ld      r5, HSTATE_XICS_PHYS(r13)
-       li      r7, XICS_XIRR
-       cmpdi   r5, 0
-       beq-    ext_interrupt_to_host
-       lwzcix  r3, r5, r7
-       rlwinm. r0, r3, 0, 0xffffff
-       sync
-       beq     3f              /* if nothing pending in the ICP */
-
-       /* We found something in the ICP...
-        *
-        * If it's not an IPI, stash it in the PACA and return to
-        * the host, we don't (yet) handle directing real external
-        * interrupts directly to the guest
-        */
-       cmpwi   r0, XICS_IPI
-       bne     ext_stash_for_host
-
-       /* It's an IPI, clear the MFRR and EOI it */
-       li      r0, 0xff
-       li      r6, XICS_MFRR
-       stbcix  r0, r5, r6              /* clear the IPI */
-       stwcix  r3, r5, r7              /* EOI it */
-       sync
-
-       /* We need to re-check host IPI now in case it got set in the
-        * meantime. If it's clear, we bounce the interrupt to the
-        * guest
-        */
-       lbz     r0, HSTATE_HOST_IPI(r13)
-       cmpwi   r0, 0
-       bne-    1f
+       bl      kvmppc_read_intr
+       cmpdi   r3, 0
+       bgt     ext_interrupt_to_host
 
        /* Allright, looks like an IPI for the guest, we need to set MER */
-3:
        /* Check if any CPU is heading out to the host, if so head out too */
        ld      r5, HSTATE_KVM_VCORE(r13)
        lwz     r0, VCORE_ENTRY_EXIT(r5)
@@ -764,27 +900,9 @@ do_ext_interrupt:
        mtspr   SPRN_LPCR, r8
        b       fast_guest_return
 
-       /* We raced with the host, we need to resend that IPI, bummer */
-1:     li      r0, IPI_PRIORITY
-       stbcix  r0, r5, r6              /* set the IPI */
-       sync
-       b       ext_interrupt_to_host
-
-ext_stash_for_host:
-       /* It's not an IPI and it's for the host, stash it in the PACA
-        * before exit, it will be picked up by the host ICP driver
-        */
-       stw     r3, HSTATE_SAVED_XIRR(r13)
 ext_interrupt_to_host:
 
 guest_exit_cont:               /* r9 = vcpu, r12 = trap, r13 = paca */
-       /* Save DEC */
-       mfspr   r5,SPRN_DEC
-       mftb    r6
-       extsw   r5,r5
-       add     r5,r5,r6
-       std     r5,VCPU_DEC_EXPIRES(r9)
-
        /* Save more register state  */
        mfdar   r6
        mfdsisr r7
@@ -954,7 +1072,30 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
        mtspr   SPRN_SDR1,r6            /* switch to partition page table */
        mtspr   SPRN_LPID,r7
        isync
-       li      r0,0
+
+       /* Subtract timebase offset from timebase */
+       ld      r8,VCORE_TB_OFFSET(r5)
+       cmpdi   r8,0
+       beq     17f
+       mftb    r6                      /* current host timebase */
+       subf    r8,r8,r6
+       mtspr   SPRN_TBU40,r8           /* update upper 40 bits */
+       mftb    r7                      /* check if lower 24 bits overflowed */
+       clrldi  r6,r6,40
+       clrldi  r7,r7,40
+       cmpld   r7,r6
+       bge     17f
+       addis   r8,r8,0x100             /* if so, increment upper 40 bits */
+       mtspr   SPRN_TBU40,r8
+
+       /* Reset PCR */
+17:    ld      r0, VCORE_PCR(r5)
+       cmpdi   r0, 0
+       beq     18f
+       li      r0, 0
+       mtspr   SPRN_PCR, r0
+18:
+       /* Signal secondary CPUs to continue */
        stb     r0,VCORE_IN_GUEST(r5)
        lis     r8,0x7fff               /* MAX_INT@h */
        mtspr   SPRN_HDEC,r8
@@ -1052,6 +1193,13 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
 1:     addi    r8,r8,16
        .endr
 
+       /* Save DEC */
+       mfspr   r5,SPRN_DEC
+       mftb    r6
+       extsw   r5,r5
+       add     r5,r5,r6
+       std     r5,VCPU_DEC_EXPIRES(r9)
+
        /* Save and reset AMR and UAMOR before turning on the MMU */
 BEGIN_FTR_SECTION
        mfspr   r5,SPRN_AMR
@@ -1062,6 +1210,10 @@ BEGIN_FTR_SECTION
        mtspr   SPRN_AMR,r6
 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 
+       /* Unset guest mode */
+       li      r0, KVM_GUEST_MODE_NONE
+       stb     r0, HSTATE_IN_GUEST(r13)
+
        /* Switch DSCR back to host value */
 BEGIN_FTR_SECTION
        mfspr   r8, SPRN_DSCR
@@ -1134,9 +1286,13 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
        std     r3, VCPU_MMCR(r9)       /* if not, set saved MMCR0 to FC */
        b       22f
 21:    mfspr   r5, SPRN_MMCR1
+       mfspr   r7, SPRN_SIAR
+       mfspr   r8, SPRN_SDAR
        std     r4, VCPU_MMCR(r9)
        std     r5, VCPU_MMCR + 8(r9)
        std     r6, VCPU_MMCR + 16(r9)
+       std     r7, VCPU_SIAR(r9)
+       std     r8, VCPU_SDAR(r9)
        mfspr   r3, SPRN_PMC1
        mfspr   r4, SPRN_PMC2
        mfspr   r5, SPRN_PMC3
@@ -1158,103 +1314,30 @@ BEGIN_FTR_SECTION
        stw     r11, VCPU_PMC + 28(r9)
 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
 22:
+       ld      r0, 112+PPC_LR_STKOFF(r1)
+       addi    r1, r1, 112
+       mtlr    r0
+       blr
+secondary_too_late:
+       ld      r5,HSTATE_KVM_VCORE(r13)
+       HMT_LOW
+13:    lbz     r3,VCORE_IN_GUEST(r5)
+       cmpwi   r3,0
+       bne     13b
+       HMT_MEDIUM
+       li      r0, KVM_GUEST_MODE_NONE
+       stb     r0, HSTATE_IN_GUEST(r13)
+       ld      r11,PACA_SLBSHADOWPTR(r13)
 
-       /* Secondary threads go off to take a nap on POWER7 */
-BEGIN_FTR_SECTION
-       lwz     r0,VCPU_PTID(r9)
-       cmpwi   r0,0
-       bne     secondary_nap
-END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
-
-       /* Restore host DABR and DABRX */
-       ld      r5,HSTATE_DABR(r13)
-       li      r6,7
-       mtspr   SPRN_DABR,r5
-       mtspr   SPRN_DABRX,r6
-
-       /* Restore SPRG3 */
-       ld      r3,PACA_SPRG3(r13)
-       mtspr   SPRN_SPRG3,r3
-
-       /*
-        * Reload DEC.  HDEC interrupts were disabled when
-        * we reloaded the host's LPCR value.
-        */
-       ld      r3, HSTATE_DECEXP(r13)
-       mftb    r4
-       subf    r4, r4, r3
-       mtspr   SPRN_DEC, r4
-
-       /* Reload the host's PMU registers */
-       ld      r3, PACALPPACAPTR(r13)  /* is the host using the PMU? */
-       lbz     r4, LPPACA_PMCINUSE(r3)
-       cmpwi   r4, 0
-       beq     23f                     /* skip if not */
-       lwz     r3, HSTATE_PMC(r13)
-       lwz     r4, HSTATE_PMC + 4(r13)
-       lwz     r5, HSTATE_PMC + 8(r13)
-       lwz     r6, HSTATE_PMC + 12(r13)
-       lwz     r8, HSTATE_PMC + 16(r13)
-       lwz     r9, HSTATE_PMC + 20(r13)
-BEGIN_FTR_SECTION
-       lwz     r10, HSTATE_PMC + 24(r13)
-       lwz     r11, HSTATE_PMC + 28(r13)
-END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
-       mtspr   SPRN_PMC1, r3
-       mtspr   SPRN_PMC2, r4
-       mtspr   SPRN_PMC3, r5
-       mtspr   SPRN_PMC4, r6
-       mtspr   SPRN_PMC5, r8
-       mtspr   SPRN_PMC6, r9
-BEGIN_FTR_SECTION
-       mtspr   SPRN_PMC7, r10
-       mtspr   SPRN_PMC8, r11
-END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
-       ld      r3, HSTATE_MMCR(r13)
-       ld      r4, HSTATE_MMCR + 8(r13)
-       ld      r5, HSTATE_MMCR + 16(r13)
-       mtspr   SPRN_MMCR1, r4
-       mtspr   SPRN_MMCRA, r5
-       mtspr   SPRN_MMCR0, r3
-       isync
-23:
-       /*
-        * For external and machine check interrupts, we need
-        * to call the Linux handler to process the interrupt.
-        * We do that by jumping to absolute address 0x500 for
-        * external interrupts, or the machine_check_fwnmi label
-        * for machine checks (since firmware might have patched
-        * the vector area at 0x200).  The [h]rfid at the end of the
-        * handler will return to the book3s_hv_interrupts.S code.
-        * For other interrupts we do the rfid to get back
-        * to the book3s_hv_interrupts.S code here.
-        */
-       ld      r8, HSTATE_VMHANDLER(r13)
-       ld      r7, HSTATE_HOST_MSR(r13)
-
-       cmpwi   cr1, r12, BOOK3S_INTERRUPT_MACHINE_CHECK
-       cmpwi   r12, BOOK3S_INTERRUPT_EXTERNAL
-BEGIN_FTR_SECTION
-       beq     11f
-END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
-
-       /* RFI into the highmem handler, or branch to interrupt handler */
-       mfmsr   r6
-       li      r0, MSR_RI
-       andc    r6, r6, r0
-       mtmsrd  r6, 1                   /* Clear RI in MSR */
-       mtsrr0  r8
-       mtsrr1  r7
-       beqa    0x500                   /* external interrupt (PPC970) */
-       beq     cr1, 13f                /* machine check */
-       RFI
-
-       /* On POWER7, we have external interrupts set to use HSRR0/1 */
-11:    mtspr   SPRN_HSRR0, r8
-       mtspr   SPRN_HSRR1, r7
-       ba      0x500
-
-13:    b       machine_check_fwnmi
+       .rept   SLB_NUM_BOLTED
+       ld      r5,SLBSHADOW_SAVEAREA(r11)
+       ld      r6,SLBSHADOW_SAVEAREA+8(r11)
+       andis.  r7,r5,SLB_ESID_V@h
+       beq     1f
+       slbmte  r6,r5
+1:     addi    r11,r11,16
+       .endr
+       b       22b
 
 /*
  * Check whether an HDSI is an HPTE not found fault or something else.
@@ -1333,7 +1416,7 @@ fast_interrupt_c_return:
        stw     r8, VCPU_LAST_INST(r9)
 
        /* Unset guest mode. */
-       li      r0, KVM_GUEST_MODE_NONE
+       li      r0, KVM_GUEST_MODE_HOST_HV
        stb     r0, HSTATE_IN_GUEST(r13)
        b       guest_exit_cont
 
@@ -1701,67 +1784,70 @@ machine_check_realmode:
        rotldi  r11, r11, 63
        b       fast_interrupt_c_return
 
-secondary_too_late:
-       ld      r5,HSTATE_KVM_VCORE(r13)
-       HMT_LOW
-13:    lbz     r3,VCORE_IN_GUEST(r5)
-       cmpwi   r3,0
-       bne     13b
-       HMT_MEDIUM
-       ld      r11,PACA_SLBSHADOWPTR(r13)
-
-       .rept   SLB_NUM_BOLTED
-       ld      r5,SLBSHADOW_SAVEAREA(r11)
-       ld      r6,SLBSHADOW_SAVEAREA+8(r11)
-       andis.  r7,r5,SLB_ESID_V@h
-       beq     1f
-       slbmte  r6,r5
-1:     addi    r11,r11,16
-       .endr
+/*
+ * Determine what sort of external interrupt is pending (if any).
+ * Returns:
+ *     0 if no interrupt is pending
+ *     1 if an interrupt is pending that needs to be handled by the host
+ *     -1 if there was a guest wakeup IPI (which has now been cleared)
+ */
+kvmppc_read_intr:
+       /* see if a host IPI is pending */
+       li      r3, 1
+       lbz     r0, HSTATE_HOST_IPI(r13)
+       cmpwi   r0, 0
+       bne     1f
 
-secondary_nap:
-       /* Clear our vcpu pointer so we don't come back in early */
-       li      r0, 0
-       std     r0, HSTATE_KVM_VCPU(r13)
-       lwsync
-       /* Clear any pending IPI - assume we're a secondary thread */
-       ld      r5, HSTATE_XICS_PHYS(r13)
+       /* Now read the interrupt from the ICP */
+       ld      r6, HSTATE_XICS_PHYS(r13)
        li      r7, XICS_XIRR
-       lwzcix  r3, r5, r7              /* ack any pending interrupt */
-       rlwinm. r0, r3, 0, 0xffffff     /* any pending? */
-       beq     37f
+       cmpdi   r6, 0
+       beq-    1f
+       lwzcix  r0, r6, r7
+       rlwinm. r3, r0, 0, 0xffffff
        sync
-       li      r0, 0xff
-       li      r6, XICS_MFRR
-       stbcix  r0, r5, r6              /* clear the IPI */
-       stwcix  r3, r5, r7              /* EOI it */
-37:    sync
+       beq     1f                      /* if nothing pending in the ICP */
 
-       /* increment the nap count and then go to nap mode */
-       ld      r4, HSTATE_KVM_VCORE(r13)
-       addi    r4, r4, VCORE_NAP_COUNT
-       lwsync                          /* make previous updates visible */
-51:    lwarx   r3, 0, r4
-       addi    r3, r3, 1
-       stwcx.  r3, 0, r4
-       bne     51b
+       /* We found something in the ICP...
+        *
+        * If it's not an IPI, stash it in the PACA and return to
+        * the host, we don't (yet) handle directing real external
+        * interrupts directly to the guest
+        */
+       cmpwi   r3, XICS_IPI            /* if there is, is it an IPI? */
+       li      r3, 1
+       bne     42f
 
-kvm_no_guest:
-       li      r0, KVM_HWTHREAD_IN_NAP
-       stb     r0, HSTATE_HWTHREAD_STATE(r13)
+       /* It's an IPI, clear the MFRR and EOI it */
+       li      r3, 0xff
+       li      r8, XICS_MFRR
+       stbcix  r3, r6, r8              /* clear the IPI */
+       stwcix  r0, r6, r7              /* EOI it */
+       sync
 
-       li      r3, LPCR_PECE0
-       mfspr   r4, SPRN_LPCR
-       rlwimi  r4, r3, 0, LPCR_PECE0 | LPCR_PECE1
-       mtspr   SPRN_LPCR, r4
-       isync
-       std     r0, HSTATE_SCRATCH0(r13)
-       ptesync
-       ld      r0, HSTATE_SCRATCH0(r13)
-1:     cmpd    r0, r0
-       bne     1b
-       nap
-       b       .
+       /* We need to re-check host IPI now in case it got set in the
+        * meantime. If it's clear, we bounce the interrupt to the
+        * guest
+        */
+       lbz     r0, HSTATE_HOST_IPI(r13)
+       cmpwi   r0, 0
+       bne-    43f
+
+       /* OK, it's an IPI for us */
+       li      r3, -1
+1:     blr
+
+42:    /* It's not an IPI and it's for the host, stash it in the PACA
+        * before exit, it will be picked up by the host ICP driver
+        */
+       stw     r0, HSTATE_SAVED_XIRR(r13)
+       b       1b
+
+43:    /* We raced with the host, we need to resend that IPI, bummer */
+       li      r0, IPI_PRIORITY
+       stbcix  r0, r6, r8              /* set the IPI */
+       sync
+       b       1b
 
 /*
  * Save away FP, VMX and VSX registers.
@@ -1879,3 +1965,11 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
        lwz     r7,VCPU_VRSAVE(r4)
        mtspr   SPRN_VRSAVE,r7
        blr
+
+/*
+ * We come here if we get any exception or interrupt while we are
+ * executing host real mode code while in guest MMU context.
+ * For now just spin, but we should do something better.
+ */
+kvmppc_bad_host_intr:
+       b       .
index 17cfae5..f4dd041 100644 (file)
 
 #if defined(CONFIG_PPC_BOOK3S_64)
 #define FUNC(name)             GLUE(.,name)
+#define GET_SHADOW_VCPU(reg)    addi   reg, r13, PACA_SVCPU
+
 #elif defined(CONFIG_PPC_BOOK3S_32)
 #define FUNC(name)             name
+#define GET_SHADOW_VCPU(reg)   lwz     reg, (THREAD + THREAD_KVM_SVCPU)(r2)
+
 #endif /* CONFIG_PPC_BOOK3S_XX */
 
 #define VCPU_LOAD_NVGPRS(vcpu) \
@@ -87,8 +91,14 @@ kvm_start_entry:
        VCPU_LOAD_NVGPRS(r4)
 
 kvm_start_lightweight:
+       /* Copy registers into shadow vcpu so we can access them in real mode */
+       GET_SHADOW_VCPU(r3)
+       bl      FUNC(kvmppc_copy_to_svcpu)
+       nop
+       REST_GPR(4, r1)
 
 #ifdef CONFIG_PPC_BOOK3S_64
+       /* Get the dcbz32 flag */
        PPC_LL  r3, VCPU_HFLAGS(r4)
        rldicl  r3, r3, 0, 63           /* r3 &= 1 */
        stb     r3, HSTATE_RESTORE_HID5(r13)
@@ -111,9 +121,6 @@ kvm_start_lightweight:
  *
  */
 
-.global kvmppc_handler_highmem
-kvmppc_handler_highmem:
-
        /*
         * Register usage at this point:
         *
@@ -125,18 +132,31 @@ kvmppc_handler_highmem:
         *
         */
 
-       /* R7 = vcpu */
-       PPC_LL  r7, GPR4(r1)
+       /* Transfer reg values from shadow vcpu back to vcpu struct */
+       /* On 64-bit, interrupts are still off at this point */
+       PPC_LL  r3, GPR4(r1)            /* vcpu pointer */
+       GET_SHADOW_VCPU(r4)
+       bl      FUNC(kvmppc_copy_from_svcpu)
+       nop
 
 #ifdef CONFIG_PPC_BOOK3S_64
+       /* Re-enable interrupts */
+       ld      r3, HSTATE_HOST_MSR(r13)
+       ori     r3, r3, MSR_EE
+       MTMSR_EERI(r3)
+
        /*
         * Reload kernel SPRG3 value.
         * No need to save guest value as usermode can't modify SPRG3.
         */
        ld      r3, PACA_SPRG3(r13)
        mtspr   SPRN_SPRG3, r3
+
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
+       /* R7 = vcpu */
+       PPC_LL  r7, GPR4(r1)
+
        PPC_STL r14, VCPU_GPR(R14)(r7)
        PPC_STL r15, VCPU_GPR(R15)(r7)
        PPC_STL r16, VCPU_GPR(R16)(r7)
@@ -161,7 +181,7 @@ kvmppc_handler_highmem:
 
        /* Restore r3 (kvm_run) and r4 (vcpu) */
        REST_2GPRS(3, r1)
-       bl      FUNC(kvmppc_handle_exit)
+       bl      FUNC(kvmppc_handle_exit_pr)
 
        /* If RESUME_GUEST, get back in the loop */
        cmpwi   r3, RESUME_GUEST
index da8b13c..5a1ab12 100644 (file)
@@ -28,7 +28,7 @@
 #include <asm/mmu_context.h>
 #include <asm/hw_irq.h>
 
-#include "trace.h"
+#include "trace_pr.h"
 
 #define PTE_SIZE       12
 
@@ -56,6 +56,14 @@ static inline u64 kvmppc_mmu_hash_vpte_long(u64 vpage)
                       HPTEG_HASH_BITS_VPTE_LONG);
 }
 
+#ifdef CONFIG_PPC_BOOK3S_64
+static inline u64 kvmppc_mmu_hash_vpte_64k(u64 vpage)
+{
+       return hash_64((vpage & 0xffffffff0ULL) >> 4,
+                      HPTEG_HASH_BITS_VPTE_64K);
+}
+#endif
+
 void kvmppc_mmu_hpte_cache_map(struct kvm_vcpu *vcpu, struct hpte_cache *pte)
 {
        u64 index;
@@ -83,6 +91,15 @@ void kvmppc_mmu_hpte_cache_map(struct kvm_vcpu *vcpu, struct hpte_cache *pte)
        hlist_add_head_rcu(&pte->list_vpte_long,
                           &vcpu3s->hpte_hash_vpte_long[index]);
 
+#ifdef CONFIG_PPC_BOOK3S_64
+       /* Add to vPTE_64k list */
+       index = kvmppc_mmu_hash_vpte_64k(pte->pte.vpage);
+       hlist_add_head_rcu(&pte->list_vpte_64k,
+                          &vcpu3s->hpte_hash_vpte_64k[index]);
+#endif
+
+       vcpu3s->hpte_cache_count++;
+
        spin_unlock(&vcpu3s->mmu_lock);
 }
 
@@ -113,10 +130,13 @@ static void invalidate_pte(struct kvm_vcpu *vcpu, struct hpte_cache *pte)
        hlist_del_init_rcu(&pte->list_pte_long);
        hlist_del_init_rcu(&pte->list_vpte);
        hlist_del_init_rcu(&pte->list_vpte_long);
+#ifdef CONFIG_PPC_BOOK3S_64
+       hlist_del_init_rcu(&pte->list_vpte_64k);
+#endif
+       vcpu3s->hpte_cache_count--;
 
        spin_unlock(&vcpu3s->mmu_lock);
 
-       vcpu3s->hpte_cache_count--;
        call_rcu(&pte->rcu_head, free_pte_rcu);
 }
 
@@ -219,6 +239,29 @@ static void kvmppc_mmu_pte_vflush_short(struct kvm_vcpu *vcpu, u64 guest_vp)
        rcu_read_unlock();
 }
 
+#ifdef CONFIG_PPC_BOOK3S_64
+/* Flush with mask 0xffffffff0 */
+static void kvmppc_mmu_pte_vflush_64k(struct kvm_vcpu *vcpu, u64 guest_vp)
+{
+       struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
+       struct hlist_head *list;
+       struct hpte_cache *pte;
+       u64 vp_mask = 0xffffffff0ULL;
+
+       list = &vcpu3s->hpte_hash_vpte_64k[
+               kvmppc_mmu_hash_vpte_64k(guest_vp)];
+
+       rcu_read_lock();
+
+       /* Check the list for matching entries and invalidate */
+       hlist_for_each_entry_rcu(pte, list, list_vpte_64k)
+               if ((pte->pte.vpage & vp_mask) == guest_vp)
+                       invalidate_pte(vcpu, pte);
+
+       rcu_read_unlock();
+}
+#endif
+
 /* Flush with mask 0xffffff000 */
 static void kvmppc_mmu_pte_vflush_long(struct kvm_vcpu *vcpu, u64 guest_vp)
 {
@@ -249,6 +292,11 @@ void kvmppc_mmu_pte_vflush(struct kvm_vcpu *vcpu, u64 guest_vp, u64 vp_mask)
        case 0xfffffffffULL:
                kvmppc_mmu_pte_vflush_short(vcpu, guest_vp);
                break;
+#ifdef CONFIG_PPC_BOOK3S_64
+       case 0xffffffff0ULL:
+               kvmppc_mmu_pte_vflush_64k(vcpu, guest_vp);
+               break;
+#endif
        case 0xffffff000ULL:
                kvmppc_mmu_pte_vflush_long(vcpu, guest_vp);
                break;
@@ -285,15 +333,19 @@ struct hpte_cache *kvmppc_mmu_hpte_cache_next(struct kvm_vcpu *vcpu)
        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
        struct hpte_cache *pte;
 
-       pte = kmem_cache_zalloc(hpte_cache, GFP_KERNEL);
-       vcpu3s->hpte_cache_count++;
-
        if (vcpu3s->hpte_cache_count == HPTEG_CACHE_NUM)
                kvmppc_mmu_pte_flush_all(vcpu);
 
+       pte = kmem_cache_zalloc(hpte_cache, GFP_KERNEL);
+
        return pte;
 }
 
+void kvmppc_mmu_hpte_cache_free(struct hpte_cache *pte)
+{
+       kmem_cache_free(hpte_cache, pte);
+}
+
 void kvmppc_mmu_hpte_destroy(struct kvm_vcpu *vcpu)
 {
        kvmppc_mmu_pte_flush(vcpu, 0, 0);
@@ -320,6 +372,10 @@ int kvmppc_mmu_hpte_init(struct kvm_vcpu *vcpu)
                                  ARRAY_SIZE(vcpu3s->hpte_hash_vpte));
        kvmppc_mmu_hpte_init_hash(vcpu3s->hpte_hash_vpte_long,
                                  ARRAY_SIZE(vcpu3s->hpte_hash_vpte_long));
+#ifdef CONFIG_PPC_BOOK3S_64
+       kvmppc_mmu_hpte_init_hash(vcpu3s->hpte_hash_vpte_64k,
+                                 ARRAY_SIZE(vcpu3s->hpte_hash_vpte_64k));
+#endif
 
        spin_lock_init(&vcpu3s->mmu_lock);
 
index c0b48f9..fe14ca3 100644 (file)
 #include <linux/sched.h>
 #include <linux/vmalloc.h>
 #include <linux/highmem.h>
+#include <linux/module.h>
 
-#include "trace.h"
+#include "book3s.h"
+
+#define CREATE_TRACE_POINTS
+#include "trace_pr.h"
 
 /* #define EXIT_DEBUG */
 /* #define DEBUG_EXT */
@@ -56,29 +60,25 @@ static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr,
 #define HW_PAGE_SIZE PAGE_SIZE
 #endif
 
-void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+static void kvmppc_core_vcpu_load_pr(struct kvm_vcpu *vcpu, int cpu)
 {
 #ifdef CONFIG_PPC_BOOK3S_64
        struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
        memcpy(svcpu->slb, to_book3s(vcpu)->slb_shadow, sizeof(svcpu->slb));
-       memcpy(&get_paca()->shadow_vcpu, to_book3s(vcpu)->shadow_vcpu,
-              sizeof(get_paca()->shadow_vcpu));
        svcpu->slb_max = to_book3s(vcpu)->slb_shadow_max;
        svcpu_put(svcpu);
 #endif
        vcpu->cpu = smp_processor_id();
 #ifdef CONFIG_PPC_BOOK3S_32
-       current->thread.kvm_shadow_vcpu = to_book3s(vcpu)->shadow_vcpu;
+       current->thread.kvm_shadow_vcpu = vcpu->arch.shadow_vcpu;
 #endif
 }
 
-void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
+static void kvmppc_core_vcpu_put_pr(struct kvm_vcpu *vcpu)
 {
 #ifdef CONFIG_PPC_BOOK3S_64
        struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
        memcpy(to_book3s(vcpu)->slb_shadow, svcpu->slb, sizeof(svcpu->slb));
-       memcpy(to_book3s(vcpu)->shadow_vcpu, &get_paca()->shadow_vcpu,
-              sizeof(get_paca()->shadow_vcpu));
        to_book3s(vcpu)->slb_shadow_max = svcpu->slb_max;
        svcpu_put(svcpu);
 #endif
@@ -87,7 +87,61 @@ void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
        vcpu->cpu = -1;
 }
 
-int kvmppc_core_check_requests(struct kvm_vcpu *vcpu)
+/* Copy data needed by real-mode code from vcpu to shadow vcpu */
+void kvmppc_copy_to_svcpu(struct kvmppc_book3s_shadow_vcpu *svcpu,
+                         struct kvm_vcpu *vcpu)
+{
+       svcpu->gpr[0] = vcpu->arch.gpr[0];
+       svcpu->gpr[1] = vcpu->arch.gpr[1];
+       svcpu->gpr[2] = vcpu->arch.gpr[2];
+       svcpu->gpr[3] = vcpu->arch.gpr[3];
+       svcpu->gpr[4] = vcpu->arch.gpr[4];
+       svcpu->gpr[5] = vcpu->arch.gpr[5];
+       svcpu->gpr[6] = vcpu->arch.gpr[6];
+       svcpu->gpr[7] = vcpu->arch.gpr[7];
+       svcpu->gpr[8] = vcpu->arch.gpr[8];
+       svcpu->gpr[9] = vcpu->arch.gpr[9];
+       svcpu->gpr[10] = vcpu->arch.gpr[10];
+       svcpu->gpr[11] = vcpu->arch.gpr[11];
+       svcpu->gpr[12] = vcpu->arch.gpr[12];
+       svcpu->gpr[13] = vcpu->arch.gpr[13];
+       svcpu->cr  = vcpu->arch.cr;
+       svcpu->xer = vcpu->arch.xer;
+       svcpu->ctr = vcpu->arch.ctr;
+       svcpu->lr  = vcpu->arch.lr;
+       svcpu->pc  = vcpu->arch.pc;
+}
+
+/* Copy data touched by real-mode code from shadow vcpu back to vcpu */
+void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu,
+                           struct kvmppc_book3s_shadow_vcpu *svcpu)
+{
+       vcpu->arch.gpr[0] = svcpu->gpr[0];
+       vcpu->arch.gpr[1] = svcpu->gpr[1];
+       vcpu->arch.gpr[2] = svcpu->gpr[2];
+       vcpu->arch.gpr[3] = svcpu->gpr[3];
+       vcpu->arch.gpr[4] = svcpu->gpr[4];
+       vcpu->arch.gpr[5] = svcpu->gpr[5];
+       vcpu->arch.gpr[6] = svcpu->gpr[6];
+       vcpu->arch.gpr[7] = svcpu->gpr[7];
+       vcpu->arch.gpr[8] = svcpu->gpr[8];
+       vcpu->arch.gpr[9] = svcpu->gpr[9];
+       vcpu->arch.gpr[10] = svcpu->gpr[10];
+       vcpu->arch.gpr[11] = svcpu->gpr[11];
+       vcpu->arch.gpr[12] = svcpu->gpr[12];
+       vcpu->arch.gpr[13] = svcpu->gpr[13];
+       vcpu->arch.cr  = svcpu->cr;
+       vcpu->arch.xer = svcpu->xer;
+       vcpu->arch.ctr = svcpu->ctr;
+       vcpu->arch.lr  = svcpu->lr;
+       vcpu->arch.pc  = svcpu->pc;
+       vcpu->arch.shadow_srr1 = svcpu->shadow_srr1;
+       vcpu->arch.fault_dar   = svcpu->fault_dar;
+       vcpu->arch.fault_dsisr = svcpu->fault_dsisr;
+       vcpu->arch.last_inst   = svcpu->last_inst;
+}
+
+static int kvmppc_core_check_requests_pr(struct kvm_vcpu *vcpu)
 {
        int r = 1; /* Indicate we want to get back into the guest */
 
@@ -100,44 +154,69 @@ int kvmppc_core_check_requests(struct kvm_vcpu *vcpu)
 }
 
 /************* MMU Notifiers *************/
+static void do_kvm_unmap_hva(struct kvm *kvm, unsigned long start,
+                            unsigned long end)
+{
+       long i;
+       struct kvm_vcpu *vcpu;
+       struct kvm_memslots *slots;
+       struct kvm_memory_slot *memslot;
+
+       slots = kvm_memslots(kvm);
+       kvm_for_each_memslot(memslot, slots) {
+               unsigned long hva_start, hva_end;
+               gfn_t gfn, gfn_end;
+
+               hva_start = max(start, memslot->userspace_addr);
+               hva_end = min(end, memslot->userspace_addr +
+                                       (memslot->npages << PAGE_SHIFT));
+               if (hva_start >= hva_end)
+                       continue;
+               /*
+                * {gfn(page) | page intersects with [hva_start, hva_end)} =
+                * {gfn, gfn+1, ..., gfn_end-1}.
+                */
+               gfn = hva_to_gfn_memslot(hva_start, memslot);
+               gfn_end = hva_to_gfn_memslot(hva_end + PAGE_SIZE - 1, memslot);
+               kvm_for_each_vcpu(i, vcpu, kvm)
+                       kvmppc_mmu_pte_pflush(vcpu, gfn << PAGE_SHIFT,
+                                             gfn_end << PAGE_SHIFT);
+       }
+}
 
-int kvm_unmap_hva(struct kvm *kvm, unsigned long hva)
+static int kvm_unmap_hva_pr(struct kvm *kvm, unsigned long hva)
 {
        trace_kvm_unmap_hva(hva);
 
-       /*
-        * Flush all shadow tlb entries everywhere. This is slow, but
-        * we are 100% sure that we catch the to be unmapped page
-        */
-       kvm_flush_remote_tlbs(kvm);
+       do_kvm_unmap_hva(kvm, hva, hva + PAGE_SIZE);
 
        return 0;
 }
 
-int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end)
+static int kvm_unmap_hva_range_pr(struct kvm *kvm, unsigned long start,
+                                 unsigned long end)
 {
-       /* kvm_unmap_hva flushes everything anyways */
-       kvm_unmap_hva(kvm, start);
+       do_kvm_unmap_hva(kvm, start, end);
 
        return 0;
 }
 
-int kvm_age_hva(struct kvm *kvm, unsigned long hva)
+static int kvm_age_hva_pr(struct kvm *kvm, unsigned long hva)
 {
        /* XXX could be more clever ;) */
        return 0;
 }
 
-int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
+static int kvm_test_age_hva_pr(struct kvm *kvm, unsigned long hva)
 {
        /* XXX could be more clever ;) */
        return 0;
 }
 
-void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte)
+static void kvm_set_spte_hva_pr(struct kvm *kvm, unsigned long hva, pte_t pte)
 {
        /* The page will get remapped properly on its next fault */
-       kvm_unmap_hva(kvm, hva);
+       do_kvm_unmap_hva(kvm, hva, hva + PAGE_SIZE);
 }
 
 /*****************************************/
@@ -159,7 +238,7 @@ static void kvmppc_recalc_shadow_msr(struct kvm_vcpu *vcpu)
        vcpu->arch.shadow_msr = smsr;
 }
 
-void kvmppc_set_msr(struct kvm_vcpu *vcpu, u64 msr)
+static void kvmppc_set_msr_pr(struct kvm_vcpu *vcpu, u64 msr)
 {
        ulong old_msr = vcpu->arch.shared->msr;
 
@@ -219,7 +298,7 @@ void kvmppc_set_msr(struct kvm_vcpu *vcpu, u64 msr)
                kvmppc_handle_ext(vcpu, BOOK3S_INTERRUPT_FP_UNAVAIL, MSR_FP);
 }
 
-void kvmppc_set_pvr(struct kvm_vcpu *vcpu, u32 pvr)
+void kvmppc_set_pvr_pr(struct kvm_vcpu *vcpu, u32 pvr)
 {
        u32 host_pvr;
 
@@ -256,6 +335,23 @@ void kvmppc_set_pvr(struct kvm_vcpu *vcpu, u32 pvr)
        if (!strcmp(cur_cpu_spec->platform, "ppc-cell-be"))
                to_book3s(vcpu)->msr_mask &= ~(MSR_FE0 | MSR_FE1);
 
+       /*
+        * If they're asking for POWER6 or later, set the flag
+        * indicating that we can do multiple large page sizes
+        * and 1TB segments.
+        * Also set the flag that indicates that tlbie has the large
+        * page bit in the RB operand instead of the instruction.
+        */
+       switch (PVR_VER(pvr)) {
+       case PVR_POWER6:
+       case PVR_POWER7:
+       case PVR_POWER7p:
+       case PVR_POWER8:
+               vcpu->arch.hflags |= BOOK3S_HFLAG_MULTI_PGSIZE |
+                       BOOK3S_HFLAG_NEW_TLBIE;
+               break;
+       }
+
 #ifdef CONFIG_PPC_BOOK3S_32
        /* 32 bit Book3S always has 32 byte dcbz */
        vcpu->arch.hflags |= BOOK3S_HFLAG_DCBZ32;
@@ -334,6 +430,7 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
                            ulong eaddr, int vec)
 {
        bool data = (vec == BOOK3S_INTERRUPT_DATA_STORAGE);
+       bool iswrite = false;
        int r = RESUME_GUEST;
        int relocated;
        int page_found = 0;
@@ -344,10 +441,12 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
        u64 vsid;
 
        relocated = data ? dr : ir;
+       if (data && (vcpu->arch.fault_dsisr & DSISR_ISSTORE))
+               iswrite = true;
 
        /* Resolve real address if translation turned on */
        if (relocated) {
-               page_found = vcpu->arch.mmu.xlate(vcpu, eaddr, &pte, data);
+               page_found = vcpu->arch.mmu.xlate(vcpu, eaddr, &pte, data, iswrite);
        } else {
                pte.may_execute = true;
                pte.may_read = true;
@@ -355,6 +454,7 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
                pte.raddr = eaddr & KVM_PAM;
                pte.eaddr = eaddr;
                pte.vpage = eaddr >> 12;
+               pte.page_size = MMU_PAGE_64K;
        }
 
        switch (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) {
@@ -388,22 +488,18 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
 
        if (page_found == -ENOENT) {
                /* Page not found in guest PTE entries */
-               struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
                vcpu->arch.shared->dar = kvmppc_get_fault_dar(vcpu);
-               vcpu->arch.shared->dsisr = svcpu->fault_dsisr;
+               vcpu->arch.shared->dsisr = vcpu->arch.fault_dsisr;
                vcpu->arch.shared->msr |=
-                       (svcpu->shadow_srr1 & 0x00000000f8000000ULL);
-               svcpu_put(svcpu);
+                       vcpu->arch.shadow_srr1 & 0x00000000f8000000ULL;
                kvmppc_book3s_queue_irqprio(vcpu, vec);
        } else if (page_found == -EPERM) {
                /* Storage protection */
-               struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
                vcpu->arch.shared->dar = kvmppc_get_fault_dar(vcpu);
-               vcpu->arch.shared->dsisr = svcpu->fault_dsisr & ~DSISR_NOHPTE;
+               vcpu->arch.shared->dsisr = vcpu->arch.fault_dsisr & ~DSISR_NOHPTE;
                vcpu->arch.shared->dsisr |= DSISR_PROTFAULT;
                vcpu->arch.shared->msr |=
-                       svcpu->shadow_srr1 & 0x00000000f8000000ULL;
-               svcpu_put(svcpu);
+                       vcpu->arch.shadow_srr1 & 0x00000000f8000000ULL;
                kvmppc_book3s_queue_irqprio(vcpu, vec);
        } else if (page_found == -EINVAL) {
                /* Page not found in guest SLB */
@@ -411,12 +507,20 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
                kvmppc_book3s_queue_irqprio(vcpu, vec + 0x80);
        } else if (!is_mmio &&
                   kvmppc_visible_gfn(vcpu, pte.raddr >> PAGE_SHIFT)) {
+               if (data && !(vcpu->arch.fault_dsisr & DSISR_NOHPTE)) {
+                       /*
+                        * There is already a host HPTE there, presumably
+                        * a read-only one for a page the guest thinks
+                        * is writable, so get rid of it first.
+                        */
+                       kvmppc_mmu_unmap_page(vcpu, &pte);
+               }
                /* The guest's PTE is not mapped yet. Map on the host */
-               kvmppc_mmu_map_page(vcpu, &pte);
+               kvmppc_mmu_map_page(vcpu, &pte, iswrite);
                if (data)
                        vcpu->stat.sp_storage++;
                else if (vcpu->arch.mmu.is_dcbz32(vcpu) &&
-                       (!(vcpu->arch.hflags & BOOK3S_HFLAG_DCBZ32)))
+                        (!(vcpu->arch.hflags & BOOK3S_HFLAG_DCBZ32)))
                        kvmppc_patch_dcbz(vcpu, &pte);
        } else {
                /* MMIO */
@@ -619,13 +723,15 @@ static void kvmppc_handle_lost_ext(struct kvm_vcpu *vcpu)
 
        if (lost_ext & MSR_FP)
                kvmppc_load_up_fpu();
+#ifdef CONFIG_ALTIVEC
        if (lost_ext & MSR_VEC)
                kvmppc_load_up_altivec();
+#endif
        current->thread.regs->msr |= lost_ext;
 }
 
-int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
-                       unsigned int exit_nr)
+int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
+                         unsigned int exit_nr)
 {
        int r = RESUME_HOST;
        int s;
@@ -643,25 +749,32 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
        switch (exit_nr) {
        case BOOK3S_INTERRUPT_INST_STORAGE:
        {
-               struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-               ulong shadow_srr1 = svcpu->shadow_srr1;
+               ulong shadow_srr1 = vcpu->arch.shadow_srr1;
                vcpu->stat.pf_instruc++;
 
 #ifdef CONFIG_PPC_BOOK3S_32
                /* We set segments as unused segments when invalidating them. So
                 * treat the respective fault as segment fault. */
-               if (svcpu->sr[kvmppc_get_pc(vcpu) >> SID_SHIFT] == SR_INVALID) {
-                       kvmppc_mmu_map_segment(vcpu, kvmppc_get_pc(vcpu));
-                       r = RESUME_GUEST;
+               {
+                       struct kvmppc_book3s_shadow_vcpu *svcpu;
+                       u32 sr;
+
+                       svcpu = svcpu_get(vcpu);
+                       sr = svcpu->sr[kvmppc_get_pc(vcpu) >> SID_SHIFT];
                        svcpu_put(svcpu);
-                       break;
+                       if (sr == SR_INVALID) {
+                               kvmppc_mmu_map_segment(vcpu, kvmppc_get_pc(vcpu));
+                               r = RESUME_GUEST;
+                               break;
+                       }
                }
 #endif
-               svcpu_put(svcpu);
 
                /* only care about PTEG not found errors, but leave NX alone */
                if (shadow_srr1 & 0x40000000) {
+                       int idx = srcu_read_lock(&vcpu->kvm->srcu);
                        r = kvmppc_handle_pagefault(run, vcpu, kvmppc_get_pc(vcpu), exit_nr);
+                       srcu_read_unlock(&vcpu->kvm->srcu, idx);
                        vcpu->stat.sp_instruc++;
                } else if (vcpu->arch.mmu.is_dcbz32(vcpu) &&
                          (!(vcpu->arch.hflags & BOOK3S_HFLAG_DCBZ32))) {
@@ -682,25 +795,36 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
        case BOOK3S_INTERRUPT_DATA_STORAGE:
        {
                ulong dar = kvmppc_get_fault_dar(vcpu);
-               struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-               u32 fault_dsisr = svcpu->fault_dsisr;
+               u32 fault_dsisr = vcpu->arch.fault_dsisr;
                vcpu->stat.pf_storage++;
 
 #ifdef CONFIG_PPC_BOOK3S_32
                /* We set segments as unused segments when invalidating them. So
                 * treat the respective fault as segment fault. */
-               if ((svcpu->sr[dar >> SID_SHIFT]) == SR_INVALID) {
-                       kvmppc_mmu_map_segment(vcpu, dar);
-                       r = RESUME_GUEST;
+               {
+                       struct kvmppc_book3s_shadow_vcpu *svcpu;
+                       u32 sr;
+
+                       svcpu = svcpu_get(vcpu);
+                       sr = svcpu->sr[dar >> SID_SHIFT];
                        svcpu_put(svcpu);
-                       break;
+                       if (sr == SR_INVALID) {
+                               kvmppc_mmu_map_segment(vcpu, dar);
+                               r = RESUME_GUEST;
+                               break;
+                       }
                }
 #endif
-               svcpu_put(svcpu);
 
-               /* The only case we need to handle is missing shadow PTEs */
-               if (fault_dsisr & DSISR_NOHPTE) {
+               /*
+                * We need to handle missing shadow PTEs, and
+                * protection faults due to us mapping a page read-only
+                * when the guest thinks it is writable.
+                */
+               if (fault_dsisr & (DSISR_NOHPTE | DSISR_PROTFAULT)) {
+                       int idx = srcu_read_lock(&vcpu->kvm->srcu);
                        r = kvmppc_handle_pagefault(run, vcpu, dar, exit_nr);
+                       srcu_read_unlock(&vcpu->kvm->srcu, idx);
                } else {
                        vcpu->arch.shared->dar = dar;
                        vcpu->arch.shared->dsisr = fault_dsisr;
@@ -743,13 +867,10 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
        case BOOK3S_INTERRUPT_H_EMUL_ASSIST:
        {
                enum emulation_result er;
-               struct kvmppc_book3s_shadow_vcpu *svcpu;
                ulong flags;
 
 program_interrupt:
-               svcpu = svcpu_get(vcpu);
-               flags = svcpu->shadow_srr1 & 0x1f0000ull;
-               svcpu_put(svcpu);
+               flags = vcpu->arch.shadow_srr1 & 0x1f0000ull;
 
                if (vcpu->arch.shared->msr & MSR_PR) {
 #ifdef EXIT_DEBUG
@@ -798,7 +919,7 @@ program_interrupt:
                        ulong cmd = kvmppc_get_gpr(vcpu, 3);
                        int i;
 
-#ifdef CONFIG_KVM_BOOK3S_64_PR
+#ifdef CONFIG_PPC_BOOK3S_64
                        if (kvmppc_h_pr(vcpu, cmd) == EMULATE_DONE) {
                                r = RESUME_GUEST;
                                break;
@@ -881,9 +1002,7 @@ program_interrupt:
                break;
        default:
        {
-               struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-               ulong shadow_srr1 = svcpu->shadow_srr1;
-               svcpu_put(svcpu);
+               ulong shadow_srr1 = vcpu->arch.shadow_srr1;
                /* Ugh - bork here! What did we get? */
                printk(KERN_EMERG "exit_nr=0x%x | pc=0x%lx | msr=0x%lx\n",
                        exit_nr, kvmppc_get_pc(vcpu), shadow_srr1);
@@ -920,8 +1039,8 @@ program_interrupt:
        return r;
 }
 
-int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
-                                  struct kvm_sregs *sregs)
+static int kvm_arch_vcpu_ioctl_get_sregs_pr(struct kvm_vcpu *vcpu,
+                                           struct kvm_sregs *sregs)
 {
        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
        int i;
@@ -947,13 +1066,13 @@ int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
        return 0;
 }
 
-int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
-                                  struct kvm_sregs *sregs)
+static int kvm_arch_vcpu_ioctl_set_sregs_pr(struct kvm_vcpu *vcpu,
+                                           struct kvm_sregs *sregs)
 {
        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
        int i;
 
-       kvmppc_set_pvr(vcpu, sregs->pvr);
+       kvmppc_set_pvr_pr(vcpu, sregs->pvr);
 
        vcpu3s->sdr1 = sregs->u.s.sdr1;
        if (vcpu->arch.hflags & BOOK3S_HFLAG_SLB) {
@@ -983,7 +1102,8 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
        return 0;
 }
 
-int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
+static int kvmppc_get_one_reg_pr(struct kvm_vcpu *vcpu, u64 id,
+                                union kvmppc_one_reg *val)
 {
        int r = 0;
 
@@ -1012,7 +1132,8 @@ int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
        return r;
 }
 
-int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
+static int kvmppc_set_one_reg_pr(struct kvm_vcpu *vcpu, u64 id,
+                                union kvmppc_one_reg *val)
 {
        int r = 0;
 
@@ -1042,28 +1163,30 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
        return r;
 }
 
-int kvmppc_core_check_processor_compat(void)
-{
-       return 0;
-}
-
-struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
+static struct kvm_vcpu *kvmppc_core_vcpu_create_pr(struct kvm *kvm,
+                                                  unsigned int id)
 {
        struct kvmppc_vcpu_book3s *vcpu_book3s;
        struct kvm_vcpu *vcpu;
        int err = -ENOMEM;
        unsigned long p;
 
-       vcpu_book3s = vzalloc(sizeof(struct kvmppc_vcpu_book3s));
-       if (!vcpu_book3s)
+       vcpu = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL);
+       if (!vcpu)
                goto out;
 
-       vcpu_book3s->shadow_vcpu =
-               kzalloc(sizeof(*vcpu_book3s->shadow_vcpu), GFP_KERNEL);
-       if (!vcpu_book3s->shadow_vcpu)
+       vcpu_book3s = vzalloc(sizeof(struct kvmppc_vcpu_book3s));
+       if (!vcpu_book3s)
                goto free_vcpu;
+       vcpu->arch.book3s = vcpu_book3s;
+
+#ifdef CONFIG_KVM_BOOK3S_32
+       vcpu->arch.shadow_vcpu =
+               kzalloc(sizeof(*vcpu->arch.shadow_vcpu), GFP_KERNEL);
+       if (!vcpu->arch.shadow_vcpu)
+               goto free_vcpu3s;
+#endif
 
-       vcpu = &vcpu_book3s->vcpu;
        err = kvm_vcpu_init(vcpu, kvm, id);
        if (err)
                goto free_shadow_vcpu;
@@ -1076,13 +1199,19 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
        vcpu->arch.shared = (void *)(p + PAGE_SIZE - 4096);
 
 #ifdef CONFIG_PPC_BOOK3S_64
-       /* default to book3s_64 (970fx) */
+       /*
+        * Default to the same as the host if we're on sufficiently
+        * recent machine that we have 1TB segments;
+        * otherwise default to PPC970FX.
+        */
        vcpu->arch.pvr = 0x3C0301;
+       if (mmu_has_feature(MMU_FTR_1T_SEGMENT))
+               vcpu->arch.pvr = mfspr(SPRN_PVR);
 #else
        /* default to book3s_32 (750) */
        vcpu->arch.pvr = 0x84202;
 #endif
-       kvmppc_set_pvr(vcpu, vcpu->arch.pvr);
+       kvmppc_set_pvr_pr(vcpu, vcpu->arch.pvr);
        vcpu->arch.slb_nr = 64;
 
        vcpu->arch.shadow_msr = MSR_USER64;
@@ -1096,24 +1225,31 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
 uninit_vcpu:
        kvm_vcpu_uninit(vcpu);
 free_shadow_vcpu:
-       kfree(vcpu_book3s->shadow_vcpu);
-free_vcpu:
+#ifdef CONFIG_KVM_BOOK3S_32
+       kfree(vcpu->arch.shadow_vcpu);
+free_vcpu3s:
+#endif
        vfree(vcpu_book3s);
+free_vcpu:
+       kmem_cache_free(kvm_vcpu_cache, vcpu);
 out:
        return ERR_PTR(err);
 }
 
-void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
+static void kvmppc_core_vcpu_free_pr(struct kvm_vcpu *vcpu)
 {
        struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
 
        free_page((unsigned long)vcpu->arch.shared & PAGE_MASK);
        kvm_vcpu_uninit(vcpu);
-       kfree(vcpu_book3s->shadow_vcpu);
+#ifdef CONFIG_KVM_BOOK3S_32
+       kfree(vcpu->arch.shadow_vcpu);
+#endif
        vfree(vcpu_book3s);
+       kmem_cache_free(kvm_vcpu_cache, vcpu);
 }
 
-int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
+static int kvmppc_vcpu_run_pr(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
 {
        int ret;
        struct thread_fp_state fp;
@@ -1216,8 +1352,8 @@ out:
 /*
  * Get (and clear) the dirty memory log for a memory slot.
  */
-int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
-                                     struct kvm_dirty_log *log)
+static int kvm_vm_ioctl_get_dirty_log_pr(struct kvm *kvm,
+                                        struct kvm_dirty_log *log)
 {
        struct kvm_memory_slot *memslot;
        struct kvm_vcpu *vcpu;
@@ -1252,67 +1388,100 @@ out:
        return r;
 }
 
-#ifdef CONFIG_PPC64
-int kvm_vm_ioctl_get_smmu_info(struct kvm *kvm, struct kvm_ppc_smmu_info *info)
+static void kvmppc_core_flush_memslot_pr(struct kvm *kvm,
+                                        struct kvm_memory_slot *memslot)
 {
-       info->flags = KVM_PPC_1T_SEGMENTS;
-
-       /* SLB is always 64 entries */
-       info->slb_size = 64;
-
-       /* Standard 4k base page size segment */
-       info->sps[0].page_shift = 12;
-       info->sps[0].slb_enc = 0;
-       info->sps[0].enc[0].page_shift = 12;
-       info->sps[0].enc[0].pte_enc = 0;
-
-       /* Standard 16M large page size segment */
-       info->sps[1].page_shift = 24;
-       info->sps[1].slb_enc = SLB_VSID_L;
-       info->sps[1].enc[0].page_shift = 24;
-       info->sps[1].enc[0].pte_enc = 0;
+       return;
+}
 
+static int kvmppc_core_prepare_memory_region_pr(struct kvm *kvm,
+                                       struct kvm_memory_slot *memslot,
+                                       struct kvm_userspace_memory_region *mem)
+{
        return 0;
 }
-#endif /* CONFIG_PPC64 */
 
-void kvmppc_core_free_memslot(struct kvm_memory_slot *free,
-                             struct kvm_memory_slot *dont)
+static void kvmppc_core_commit_memory_region_pr(struct kvm *kvm,
+                               struct kvm_userspace_memory_region *mem,
+                               const struct kvm_memory_slot *old)
 {
+       return;
 }
 
-int kvmppc_core_create_memslot(struct kvm_memory_slot *slot,
-                              unsigned long npages)
+static void kvmppc_core_free_memslot_pr(struct kvm_memory_slot *free,
+                                       struct kvm_memory_slot *dont)
 {
-       return 0;
+       return;
 }
 
-int kvmppc_core_prepare_memory_region(struct kvm *kvm,
-                                     struct kvm_memory_slot *memslot,
-                                     struct kvm_userspace_memory_region *mem)
+static int kvmppc_core_create_memslot_pr(struct kvm_memory_slot *slot,
+                                        unsigned long npages)
 {
        return 0;
 }
 
-void kvmppc_core_commit_memory_region(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
-                               const struct kvm_memory_slot *old)
+
+#ifdef CONFIG_PPC64
+static int kvm_vm_ioctl_get_smmu_info_pr(struct kvm *kvm,
+                                        struct kvm_ppc_smmu_info *info)
 {
-}
+       long int i;
+       struct kvm_vcpu *vcpu;
+
+       info->flags = 0;
+
+       /* SLB is always 64 entries */
+       info->slb_size = 64;
+
+       /* Standard 4k base page size segment */
+       info->sps[0].page_shift = 12;
+       info->sps[0].slb_enc = 0;
+       info->sps[0].enc[0].page_shift = 12;
+       info->sps[0].enc[0].pte_enc = 0;
+
+       /*
+        * 64k large page size.
+        * We only want to put this in if the CPUs we're emulating
+        * support it, but unfortunately we don't have a vcpu easily
+        * to hand here to test.  Just pick the first vcpu, and if
+        * that doesn't exist yet, report the minimum capability,
+        * i.e., no 64k pages.
+        * 1T segment support goes along with 64k pages.
+        */
+       i = 1;
+       vcpu = kvm_get_vcpu(kvm, 0);
+       if (vcpu && (vcpu->arch.hflags & BOOK3S_HFLAG_MULTI_PGSIZE)) {
+               info->flags = KVM_PPC_1T_SEGMENTS;
+               info->sps[i].page_shift = 16;
+               info->sps[i].slb_enc = SLB_VSID_L | SLB_VSID_LP_01;
+               info->sps[i].enc[0].page_shift = 16;
+               info->sps[i].enc[0].pte_enc = 1;
+               ++i;
+       }
+
+       /* Standard 16M large page size segment */
+       info->sps[i].page_shift = 24;
+       info->sps[i].slb_enc = SLB_VSID_L;
+       info->sps[i].enc[0].page_shift = 24;
+       info->sps[i].enc[0].pte_enc = 0;
 
-void kvmppc_core_flush_memslot(struct kvm *kvm, struct kvm_memory_slot *memslot)
+       return 0;
+}
+#else
+static int kvm_vm_ioctl_get_smmu_info_pr(struct kvm *kvm,
+                                        struct kvm_ppc_smmu_info *info)
 {
+       /* We should not get called */
+       BUG();
 }
+#endif /* CONFIG_PPC64 */
 
 static unsigned int kvm_global_user_count = 0;
 static DEFINE_SPINLOCK(kvm_global_user_count_lock);
 
-int kvmppc_core_init_vm(struct kvm *kvm)
+static int kvmppc_core_init_vm_pr(struct kvm *kvm)
 {
-#ifdef CONFIG_PPC64
-       INIT_LIST_HEAD(&kvm->arch.spapr_tce_tables);
-       INIT_LIST_HEAD(&kvm->arch.rtas_tokens);
-#endif
+       mutex_init(&kvm->arch.hpt_mutex);
 
        if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
                spin_lock(&kvm_global_user_count_lock);
@@ -1323,7 +1492,7 @@ int kvmppc_core_init_vm(struct kvm *kvm)
        return 0;
 }
 
-void kvmppc_core_destroy_vm(struct kvm *kvm)
+static void kvmppc_core_destroy_vm_pr(struct kvm *kvm)
 {
 #ifdef CONFIG_PPC64
        WARN_ON(!list_empty(&kvm->arch.spapr_tce_tables));
@@ -1338,26 +1507,81 @@ void kvmppc_core_destroy_vm(struct kvm *kvm)
        }
 }
 
-static int kvmppc_book3s_init(void)
+static int kvmppc_core_check_processor_compat_pr(void)
 {
-       int r;
+       /* we are always compatible */
+       return 0;
+}
 
-       r = kvm_init(NULL, sizeof(struct kvmppc_vcpu_book3s), 0,
-                    THIS_MODULE);
+static long kvm_arch_vm_ioctl_pr(struct file *filp,
+                                unsigned int ioctl, unsigned long arg)
+{
+       return -ENOTTY;
+}
 
-       if (r)
+static struct kvmppc_ops kvm_ops_pr = {
+       .get_sregs = kvm_arch_vcpu_ioctl_get_sregs_pr,
+       .set_sregs = kvm_arch_vcpu_ioctl_set_sregs_pr,
+       .get_one_reg = kvmppc_get_one_reg_pr,
+       .set_one_reg = kvmppc_set_one_reg_pr,
+       .vcpu_load   = kvmppc_core_vcpu_load_pr,
+       .vcpu_put    = kvmppc_core_vcpu_put_pr,
+       .set_msr     = kvmppc_set_msr_pr,
+       .vcpu_run    = kvmppc_vcpu_run_pr,
+       .vcpu_create = kvmppc_core_vcpu_create_pr,
+       .vcpu_free   = kvmppc_core_vcpu_free_pr,
+       .check_requests = kvmppc_core_check_requests_pr,
+       .get_dirty_log = kvm_vm_ioctl_get_dirty_log_pr,
+       .flush_memslot = kvmppc_core_flush_memslot_pr,
+       .prepare_memory_region = kvmppc_core_prepare_memory_region_pr,
+       .commit_memory_region = kvmppc_core_commit_memory_region_pr,
+       .unmap_hva = kvm_unmap_hva_pr,
+       .unmap_hva_range = kvm_unmap_hva_range_pr,
+       .age_hva  = kvm_age_hva_pr,
+       .test_age_hva = kvm_test_age_hva_pr,
+       .set_spte_hva = kvm_set_spte_hva_pr,
+       .mmu_destroy  = kvmppc_mmu_destroy_pr,
+       .free_memslot = kvmppc_core_free_memslot_pr,
+       .create_memslot = kvmppc_core_create_memslot_pr,
+       .init_vm = kvmppc_core_init_vm_pr,
+       .destroy_vm = kvmppc_core_destroy_vm_pr,
+       .get_smmu_info = kvm_vm_ioctl_get_smmu_info_pr,
+       .emulate_op = kvmppc_core_emulate_op_pr,
+       .emulate_mtspr = kvmppc_core_emulate_mtspr_pr,
+       .emulate_mfspr = kvmppc_core_emulate_mfspr_pr,
+       .fast_vcpu_kick = kvm_vcpu_kick,
+       .arch_vm_ioctl  = kvm_arch_vm_ioctl_pr,
+};
+
+
+int kvmppc_book3s_init_pr(void)
+{
+       int r;
+
+       r = kvmppc_core_check_processor_compat_pr();
+       if (r < 0)
                return r;
 
-       r = kvmppc_mmu_hpte_sysinit();
+       kvm_ops_pr.owner = THIS_MODULE;
+       kvmppc_pr_ops = &kvm_ops_pr;
 
+       r = kvmppc_mmu_hpte_sysinit();
        return r;
 }
 
-static void kvmppc_book3s_exit(void)
+void kvmppc_book3s_exit_pr(void)
 {
+       kvmppc_pr_ops = NULL;
        kvmppc_mmu_hpte_sysexit();
-       kvm_exit();
 }
 
-module_init(kvmppc_book3s_init);
-module_exit(kvmppc_book3s_exit);
+/*
+ * We only support separate modules for book3s 64
+ */
+#ifdef CONFIG_PPC_BOOK3S_64
+
+module_init(kvmppc_book3s_init_pr);
+module_exit(kvmppc_book3s_exit_pr);
+
+MODULE_LICENSE("GPL");
+#endif
index da0e0bc..5efa97b 100644 (file)
@@ -21,6 +21,8 @@
 #include <asm/kvm_ppc.h>
 #include <asm/kvm_book3s.h>
 
+#define HPTE_SIZE      16              /* bytes per HPT entry */
+
 static unsigned long get_pteg_addr(struct kvm_vcpu *vcpu, long pte_index)
 {
        struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
@@ -40,32 +42,41 @@ static int kvmppc_h_pr_enter(struct kvm_vcpu *vcpu)
        long pte_index = kvmppc_get_gpr(vcpu, 5);
        unsigned long pteg[2 * 8];
        unsigned long pteg_addr, i, *hpte;
+       long int ret;
 
+       i = pte_index & 7;
        pte_index &= ~7UL;
        pteg_addr = get_pteg_addr(vcpu, pte_index);
 
+       mutex_lock(&vcpu->kvm->arch.hpt_mutex);
        copy_from_user(pteg, (void __user *)pteg_addr, sizeof(pteg));
        hpte = pteg;
 
+       ret = H_PTEG_FULL;
        if (likely((flags & H_EXACT) == 0)) {
-               pte_index &= ~7UL;
                for (i = 0; ; ++i) {
                        if (i == 8)
-                               return H_PTEG_FULL;
+                               goto done;
                        if ((*hpte & HPTE_V_VALID) == 0)
                                break;
                        hpte += 2;
                }
        } else {
-               i = kvmppc_get_gpr(vcpu, 5) & 7UL;
                hpte += i * 2;
+               if (*hpte & HPTE_V_VALID)
+                       goto done;
        }
 
        hpte[0] = kvmppc_get_gpr(vcpu, 6);
        hpte[1] = kvmppc_get_gpr(vcpu, 7);
-       copy_to_user((void __user *)pteg_addr, pteg, sizeof(pteg));
-       kvmppc_set_gpr(vcpu, 3, H_SUCCESS);
+       pteg_addr += i * HPTE_SIZE;
+       copy_to_user((void __user *)pteg_addr, hpte, HPTE_SIZE);
        kvmppc_set_gpr(vcpu, 4, pte_index | i);
+       ret = H_SUCCESS;
+
+ done:
+       mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
+       kvmppc_set_gpr(vcpu, 3, ret);
 
        return EMULATE_DONE;
 }
@@ -77,26 +88,31 @@ static int kvmppc_h_pr_remove(struct kvm_vcpu *vcpu)
        unsigned long avpn = kvmppc_get_gpr(vcpu, 6);
        unsigned long v = 0, pteg, rb;
        unsigned long pte[2];
+       long int ret;
 
        pteg = get_pteg_addr(vcpu, pte_index);
+       mutex_lock(&vcpu->kvm->arch.hpt_mutex);
        copy_from_user(pte, (void __user *)pteg, sizeof(pte));
 
+       ret = H_NOT_FOUND;
        if ((pte[0] & HPTE_V_VALID) == 0 ||
            ((flags & H_AVPN) && (pte[0] & ~0x7fUL) != avpn) ||
-           ((flags & H_ANDCOND) && (pte[0] & avpn) != 0)) {
-               kvmppc_set_gpr(vcpu, 3, H_NOT_FOUND);
-               return EMULATE_DONE;
-       }
+           ((flags & H_ANDCOND) && (pte[0] & avpn) != 0))
+               goto done;
 
        copy_to_user((void __user *)pteg, &v, sizeof(v));
 
        rb = compute_tlbie_rb(pte[0], pte[1], pte_index);
        vcpu->arch.mmu.tlbie(vcpu, rb, rb & 1 ? true : false);
 
-       kvmppc_set_gpr(vcpu, 3, H_SUCCESS);
+       ret = H_SUCCESS;
        kvmppc_set_gpr(vcpu, 4, pte[0]);
        kvmppc_set_gpr(vcpu, 5, pte[1]);
 
+ done:
+       mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
+       kvmppc_set_gpr(vcpu, 3, ret);
+
        return EMULATE_DONE;
 }
 
@@ -124,6 +140,7 @@ static int kvmppc_h_pr_bulk_remove(struct kvm_vcpu *vcpu)
        int paramnr = 4;
        int ret = H_SUCCESS;
 
+       mutex_lock(&vcpu->kvm->arch.hpt_mutex);
        for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) {
                unsigned long tsh = kvmppc_get_gpr(vcpu, paramnr+(2*i));
                unsigned long tsl = kvmppc_get_gpr(vcpu, paramnr+(2*i)+1);
@@ -172,6 +189,7 @@ static int kvmppc_h_pr_bulk_remove(struct kvm_vcpu *vcpu)
                }
                kvmppc_set_gpr(vcpu, paramnr+(2*i), tsh);
        }
+       mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
        kvmppc_set_gpr(vcpu, 3, ret);
 
        return EMULATE_DONE;
@@ -184,15 +202,16 @@ static int kvmppc_h_pr_protect(struct kvm_vcpu *vcpu)
        unsigned long avpn = kvmppc_get_gpr(vcpu, 6);
        unsigned long rb, pteg, r, v;
        unsigned long pte[2];
+       long int ret;
 
        pteg = get_pteg_addr(vcpu, pte_index);
+       mutex_lock(&vcpu->kvm->arch.hpt_mutex);
        copy_from_user(pte, (void __user *)pteg, sizeof(pte));
 
+       ret = H_NOT_FOUND;
        if ((pte[0] & HPTE_V_VALID) == 0 ||
-           ((flags & H_AVPN) && (pte[0] & ~0x7fUL) != avpn)) {
-               kvmppc_set_gpr(vcpu, 3, H_NOT_FOUND);
-               return EMULATE_DONE;
-       }
+           ((flags & H_AVPN) && (pte[0] & ~0x7fUL) != avpn))
+               goto done;
 
        v = pte[0];
        r = pte[1];
@@ -207,8 +226,11 @@ static int kvmppc_h_pr_protect(struct kvm_vcpu *vcpu)
        rb = compute_tlbie_rb(v, r, pte_index);
        vcpu->arch.mmu.tlbie(vcpu, rb, rb & 1 ? true : false);
        copy_to_user((void __user *)pteg, pte, sizeof(pte));
+       ret = H_SUCCESS;
 
-       kvmppc_set_gpr(vcpu, 3, H_SUCCESS);
+ done:
+       mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
+       kvmppc_set_gpr(vcpu, 3, ret);
 
        return EMULATE_DONE;
 }
index 8f7633e..a38c4c9 100644 (file)
 
 #define FUNC(name)             GLUE(.,name)
 
-       .globl  kvmppc_skip_interrupt
-kvmppc_skip_interrupt:
-       /*
-        * Here all GPRs are unchanged from when the interrupt happened
-        * except for r13, which is saved in SPRG_SCRATCH0.
-        */
-       mfspr   r13, SPRN_SRR0
-       addi    r13, r13, 4
-       mtspr   SPRN_SRR0, r13
-       GET_SCRATCH0(r13)
-       rfid
-       b       .
-
-       .globl  kvmppc_skip_Hinterrupt
-kvmppc_skip_Hinterrupt:
-       /*
-        * Here all GPRs are unchanged from when the interrupt happened
-        * except for r13, which is saved in SPRG_SCRATCH0.
-        */
-       mfspr   r13, SPRN_HSRR0
-       addi    r13, r13, 4
-       mtspr   SPRN_HSRR0, r13
-       GET_SCRATCH0(r13)
-       hrfid
-       b       .
-
 #elif defined(CONFIG_PPC_BOOK3S_32)
 
 #define FUNC(name)             name
@@ -179,11 +153,15 @@ _GLOBAL(kvmppc_entry_trampoline)
 
        li      r6, MSR_IR | MSR_DR
        andc    r6, r5, r6      /* Clear DR and IR in MSR value */
+#ifdef CONFIG_PPC_BOOK3S_32
        /*
         * Set EE in HOST_MSR so that it's enabled when we get into our
-        * C exit handler function
+        * C exit handler function.  On 64-bit we delay enabling
+        * interrupts until we have finished transferring stuff
+        * to or from the PACA.
         */
        ori     r5, r5, MSR_EE
+#endif
        mtsrr0  r7
        mtsrr1  r6
        RFI
index 3219ba8..cf95cde 100644 (file)
@@ -260,6 +260,7 @@ fail:
         */
        return rc;
 }
+EXPORT_SYMBOL_GPL(kvmppc_rtas_hcall);
 
 void kvmppc_rtas_tokens_free(struct kvm *kvm)
 {
index 1abe478..bc50c97 100644 (file)
@@ -161,8 +161,8 @@ kvmppc_handler_trampoline_enter_end:
 .global kvmppc_handler_trampoline_exit
 kvmppc_handler_trampoline_exit:
 
-.global kvmppc_interrupt
-kvmppc_interrupt:
+.global kvmppc_interrupt_pr
+kvmppc_interrupt_pr:
 
        /* Register usage at this point:
         *
index a3a5cb8..02a17dc 100644 (file)
@@ -818,7 +818,7 @@ int kvmppc_xics_hcall(struct kvm_vcpu *vcpu, u32 req)
        }
 
        /* Check for real mode returning too hard */
-       if (xics->real_mode)
+       if (xics->real_mode && is_kvmppc_hv_enabled(vcpu->kvm))
                return kvmppc_xics_rm_complete(vcpu, req);
 
        switch (req) {
@@ -840,6 +840,7 @@ int kvmppc_xics_hcall(struct kvm_vcpu *vcpu, u32 req)
 
        return rc;
 }
+EXPORT_SYMBOL_GPL(kvmppc_xics_hcall);
 
 
 /* -- Initialisation code etc. -- */
@@ -1250,13 +1251,13 @@ static int kvmppc_xics_create(struct kvm_device *dev, u32 type)
 
        xics_debugfs_init(xics);
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        if (cpu_has_feature(CPU_FTR_ARCH_206)) {
                /* Enable real mode support */
                xics->real_mode = ENABLE_REALMODE;
                xics->real_mode_dbg = DEBUG_REALMODE;
        }
-#endif /* CONFIG_KVM_BOOK3S_64_HV */
+#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
 
        return 0;
 }
index 5133199..53e65a2 100644 (file)
@@ -40,7 +40,9 @@
 
 #include "timing.h"
 #include "booke.h"
-#include "trace.h"
+
+#define CREATE_TRACE_POINTS
+#include "trace_booke.h"
 
 unsigned long kvmppc_booke_handlers;
 
@@ -133,6 +135,29 @@ static void kvmppc_vcpu_sync_fpu(struct kvm_vcpu *vcpu)
 #endif
 }
 
+static void kvmppc_vcpu_sync_debug(struct kvm_vcpu *vcpu)
+{
+       /* Synchronize guest's desire to get debug interrupts into shadow MSR */
+#ifndef CONFIG_KVM_BOOKE_HV
+       vcpu->arch.shadow_msr &= ~MSR_DE;
+       vcpu->arch.shadow_msr |= vcpu->arch.shared->msr & MSR_DE;
+#endif
+
+       /* Force enable debug interrupts when user space wants to debug */
+       if (vcpu->guest_debug) {
+#ifdef CONFIG_KVM_BOOKE_HV
+               /*
+                * Since there is no shadow MSR, sync MSR_DE into the guest
+                * visible MSR.
+                */
+               vcpu->arch.shared->msr |= MSR_DE;
+#else
+               vcpu->arch.shadow_msr |= MSR_DE;
+               vcpu->arch.shared->msr &= ~MSR_DE;
+#endif
+       }
+}
+
 /*
  * Helper function for "full" MSR writes.  No need to call this if only
  * EE/CE/ME/DE/RI are changing.
@@ -150,6 +175,7 @@ void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr)
        kvmppc_mmu_msr_notify(vcpu, old_msr);
        kvmppc_vcpu_sync_spe(vcpu);
        kvmppc_vcpu_sync_fpu(vcpu);
+       kvmppc_vcpu_sync_debug(vcpu);
 }
 
 static void kvmppc_booke_queue_irqprio(struct kvm_vcpu *vcpu,
@@ -655,6 +681,7 @@ int kvmppc_core_check_requests(struct kvm_vcpu *vcpu)
 int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
 {
        int ret, s;
+       struct thread_struct thread;
 #ifdef CONFIG_PPC_FPU
        struct thread_fp_state fp;
        int fpexc_mode;
@@ -695,6 +722,12 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
        kvmppc_load_guest_fp(vcpu);
 #endif
 
+       /* Switch to guest debug context */
+       thread.debug = vcpu->arch.shadow_dbg_reg;
+       switch_booke_debug_regs(&thread);
+       thread.debug = current->thread.debug;
+       current->thread.debug = vcpu->arch.shadow_dbg_reg;
+
        kvmppc_fix_ee_before_entry();
 
        ret = __kvmppc_vcpu_run(kvm_run, vcpu);
@@ -702,6 +735,10 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
        /* No need for kvm_guest_exit. It's done in handle_exit.
           We also get here with interrupts enabled. */
 
+       /* Switch back to user space debug context */
+       switch_booke_debug_regs(&thread);
+       current->thread.debug = thread.debug;
+
 #ifdef CONFIG_PPC_FPU
        kvmppc_save_guest_fp(vcpu);
 
@@ -757,6 +794,30 @@ static int emulation_exit(struct kvm_run *run, struct kvm_vcpu *vcpu)
        }
 }
 
+static int kvmppc_handle_debug(struct kvm_run *run, struct kvm_vcpu *vcpu)
+{
+       struct debug_reg *dbg_reg = &(vcpu->arch.shadow_dbg_reg);
+       u32 dbsr = vcpu->arch.dbsr;
+
+       run->debug.arch.status = 0;
+       run->debug.arch.address = vcpu->arch.pc;
+
+       if (dbsr & (DBSR_IAC1 | DBSR_IAC2 | DBSR_IAC3 | DBSR_IAC4)) {
+               run->debug.arch.status |= KVMPPC_DEBUG_BREAKPOINT;
+       } else {
+               if (dbsr & (DBSR_DAC1W | DBSR_DAC2W))
+                       run->debug.arch.status |= KVMPPC_DEBUG_WATCH_WRITE;
+               else if (dbsr & (DBSR_DAC1R | DBSR_DAC2R))
+                       run->debug.arch.status |= KVMPPC_DEBUG_WATCH_READ;
+               if (dbsr & (DBSR_DAC1R | DBSR_DAC1W))
+                       run->debug.arch.address = dbg_reg->dac1;
+               else if (dbsr & (DBSR_DAC2R | DBSR_DAC2W))
+                       run->debug.arch.address = dbg_reg->dac2;
+       }
+
+       return RESUME_HOST;
+}
+
 static void kvmppc_fill_pt_regs(struct pt_regs *regs)
 {
        ulong r1, ip, msr, lr;
@@ -817,6 +878,11 @@ static void kvmppc_restart_interrupt(struct kvm_vcpu *vcpu,
        case BOOKE_INTERRUPT_CRITICAL:
                unknown_exception(&regs);
                break;
+       case BOOKE_INTERRUPT_DEBUG:
+               /* Save DBSR before preemption is enabled */
+               vcpu->arch.dbsr = mfspr(SPRN_DBSR);
+               kvmppc_clear_dbsr();
+               break;
        }
 }
 
@@ -1134,18 +1200,10 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
        }
 
        case BOOKE_INTERRUPT_DEBUG: {
-               u32 dbsr;
-
-               vcpu->arch.pc = mfspr(SPRN_CSRR0);
-
-               /* clear IAC events in DBSR register */
-               dbsr = mfspr(SPRN_DBSR);
-               dbsr &= DBSR_IAC1 | DBSR_IAC2 | DBSR_IAC3 | DBSR_IAC4;
-               mtspr(SPRN_DBSR, dbsr);
-
-               run->exit_reason = KVM_EXIT_DEBUG;
+               r = kvmppc_handle_debug(run, vcpu);
+               if (r == RESUME_HOST)
+                       run->exit_reason = KVM_EXIT_DEBUG;
                kvmppc_account_exit(vcpu, DEBUG_EXITS);
-               r = RESUME_HOST;
                break;
        }
 
@@ -1196,7 +1254,7 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
        kvmppc_set_msr(vcpu, 0);
 
 #ifndef CONFIG_KVM_BOOKE_HV
-       vcpu->arch.shadow_msr = MSR_USER | MSR_DE | MSR_IS | MSR_DS;
+       vcpu->arch.shadow_msr = MSR_USER | MSR_IS | MSR_DS;
        vcpu->arch.shadow_pid = 1;
        vcpu->arch.shared->msr = 0;
 #endif
@@ -1358,7 +1416,7 @@ static int set_sregs_arch206(struct kvm_vcpu *vcpu,
        return 0;
 }
 
-void kvmppc_get_sregs_ivor(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
+int kvmppc_get_sregs_ivor(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
 {
        sregs->u.e.features |= KVM_SREGS_E_IVOR;
 
@@ -1378,6 +1436,7 @@ void kvmppc_get_sregs_ivor(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
        sregs->u.e.ivor_low[13] = vcpu->arch.ivor[BOOKE_IRQPRIO_DTLB_MISS];
        sregs->u.e.ivor_low[14] = vcpu->arch.ivor[BOOKE_IRQPRIO_ITLB_MISS];
        sregs->u.e.ivor_low[15] = vcpu->arch.ivor[BOOKE_IRQPRIO_DEBUG];
+       return 0;
 }
 
 int kvmppc_set_sregs_ivor(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
@@ -1412,8 +1471,7 @@ int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
 
        get_sregs_base(vcpu, sregs);
        get_sregs_arch206(vcpu, sregs);
-       kvmppc_core_get_sregs(vcpu, sregs);
-       return 0;
+       return vcpu->kvm->arch.kvm_ops->get_sregs(vcpu, sregs);
 }
 
 int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
@@ -1432,7 +1490,7 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
        if (ret < 0)
                return ret;
 
-       return kvmppc_core_set_sregs(vcpu, sregs);
+       return vcpu->kvm->arch.kvm_ops->set_sregs(vcpu, sregs);
 }
 
 int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
@@ -1440,7 +1498,6 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
        int r = 0;
        union kvmppc_one_reg val;
        int size;
-       long int i;
 
        size = one_reg_size(reg->id);
        if (size > sizeof(val))
@@ -1448,16 +1505,24 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
 
        switch (reg->id) {
        case KVM_REG_PPC_IAC1:
+               val = get_reg_val(reg->id, vcpu->arch.dbg_reg.iac1);
+               break;
        case KVM_REG_PPC_IAC2:
+               val = get_reg_val(reg->id, vcpu->arch.dbg_reg.iac2);
+               break;
+#if CONFIG_PPC_ADV_DEBUG_IACS > 2
        case KVM_REG_PPC_IAC3:
+               val = get_reg_val(reg->id, vcpu->arch.dbg_reg.iac3);
+               break;
        case KVM_REG_PPC_IAC4:
-               i = reg->id - KVM_REG_PPC_IAC1;
-               val = get_reg_val(reg->id, vcpu->arch.dbg_reg.iac[i]);
+               val = get_reg_val(reg->id, vcpu->arch.dbg_reg.iac4);
                break;
+#endif
        case KVM_REG_PPC_DAC1:
+               val = get_reg_val(reg->id, vcpu->arch.dbg_reg.dac1);
+               break;
        case KVM_REG_PPC_DAC2:
-               i = reg->id - KVM_REG_PPC_DAC1;
-               val = get_reg_val(reg->id, vcpu->arch.dbg_reg.dac[i]);
+               val = get_reg_val(reg->id, vcpu->arch.dbg_reg.dac2);
                break;
        case KVM_REG_PPC_EPR: {
                u32 epr = get_guest_epr(vcpu);
@@ -1476,10 +1541,13 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
                val = get_reg_val(reg->id, vcpu->arch.tsr);
                break;
        case KVM_REG_PPC_DEBUG_INST:
-               val = get_reg_val(reg->id, KVMPPC_INST_EHPRIV);
+               val = get_reg_val(reg->id, KVMPPC_INST_EHPRIV_DEBUG);
+               break;
+       case KVM_REG_PPC_VRSAVE:
+               val = get_reg_val(reg->id, vcpu->arch.vrsave);
                break;
        default:
-               r = kvmppc_get_one_reg(vcpu, reg->id, &val);
+               r = vcpu->kvm->arch.kvm_ops->get_one_reg(vcpu, reg->id, &val);
                break;
        }
 
@@ -1497,7 +1565,6 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
        int r = 0;
        union kvmppc_one_reg val;
        int size;
-       long int i;
 
        size = one_reg_size(reg->id);
        if (size > sizeof(val))
@@ -1508,16 +1575,24 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
 
        switch (reg->id) {
        case KVM_REG_PPC_IAC1:
+               vcpu->arch.dbg_reg.iac1 = set_reg_val(reg->id, val);
+               break;
        case KVM_REG_PPC_IAC2:
+               vcpu->arch.dbg_reg.iac2 = set_reg_val(reg->id, val);
+               break;
+#if CONFIG_PPC_ADV_DEBUG_IACS > 2
        case KVM_REG_PPC_IAC3:
+               vcpu->arch.dbg_reg.iac3 = set_reg_val(reg->id, val);
+               break;
        case KVM_REG_PPC_IAC4:
-               i = reg->id - KVM_REG_PPC_IAC1;
-               vcpu->arch.dbg_reg.iac[i] = set_reg_val(reg->id, val);
+               vcpu->arch.dbg_reg.iac4 = set_reg_val(reg->id, val);
                break;
+#endif
        case KVM_REG_PPC_DAC1:
+               vcpu->arch.dbg_reg.dac1 = set_reg_val(reg->id, val);
+               break;
        case KVM_REG_PPC_DAC2:
-               i = reg->id - KVM_REG_PPC_DAC1;
-               vcpu->arch.dbg_reg.dac[i] = set_reg_val(reg->id, val);
+               vcpu->arch.dbg_reg.dac2 = set_reg_val(reg->id, val);
                break;
        case KVM_REG_PPC_EPR: {
                u32 new_epr = set_reg_val(reg->id, val);
@@ -1551,20 +1626,17 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
                kvmppc_set_tcr(vcpu, tcr);
                break;
        }
+       case KVM_REG_PPC_VRSAVE:
+               vcpu->arch.vrsave = set_reg_val(reg->id, val);
+               break;
        default:
-               r = kvmppc_set_one_reg(vcpu, reg->id, &val);
+               r = vcpu->kvm->arch.kvm_ops->set_one_reg(vcpu, reg->id, &val);
                break;
        }
 
        return r;
 }
 
-int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
-                                        struct kvm_guest_debug *dbg)
-{
-       return -EINVAL;
-}
-
 int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
 {
        return -ENOTSUPP;
@@ -1589,12 +1661,12 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
        return -ENOTSUPP;
 }
 
-void kvmppc_core_free_memslot(struct kvm_memory_slot *free,
+void kvmppc_core_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
                              struct kvm_memory_slot *dont)
 {
 }
 
-int kvmppc_core_create_memslot(struct kvm_memory_slot *slot,
+int kvmppc_core_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
                               unsigned long npages)
 {
        return 0;
@@ -1670,6 +1742,157 @@ void kvmppc_decrementer_func(unsigned long data)
        kvmppc_set_tsr_bits(vcpu, TSR_DIS);
 }
 
+static int kvmppc_booke_add_breakpoint(struct debug_reg *dbg_reg,
+                                      uint64_t addr, int index)
+{
+       switch (index) {
+       case 0:
+               dbg_reg->dbcr0 |= DBCR0_IAC1;
+               dbg_reg->iac1 = addr;
+               break;
+       case 1:
+               dbg_reg->dbcr0 |= DBCR0_IAC2;
+               dbg_reg->iac2 = addr;
+               break;
+#if CONFIG_PPC_ADV_DEBUG_IACS > 2
+       case 2:
+               dbg_reg->dbcr0 |= DBCR0_IAC3;
+               dbg_reg->iac3 = addr;
+               break;
+       case 3:
+               dbg_reg->dbcr0 |= DBCR0_IAC4;
+               dbg_reg->iac4 = addr;
+               break;
+#endif
+       default:
+               return -EINVAL;
+       }
+
+       dbg_reg->dbcr0 |= DBCR0_IDM;
+       return 0;
+}
+
+static int kvmppc_booke_add_watchpoint(struct debug_reg *dbg_reg, uint64_t addr,
+                                      int type, int index)
+{
+       switch (index) {
+       case 0:
+               if (type & KVMPPC_DEBUG_WATCH_READ)
+                       dbg_reg->dbcr0 |= DBCR0_DAC1R;
+               if (type & KVMPPC_DEBUG_WATCH_WRITE)
+                       dbg_reg->dbcr0 |= DBCR0_DAC1W;
+               dbg_reg->dac1 = addr;
+               break;
+       case 1:
+               if (type & KVMPPC_DEBUG_WATCH_READ)
+                       dbg_reg->dbcr0 |= DBCR0_DAC2R;
+               if (type & KVMPPC_DEBUG_WATCH_WRITE)
+                       dbg_reg->dbcr0 |= DBCR0_DAC2W;
+               dbg_reg->dac2 = addr;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dbg_reg->dbcr0 |= DBCR0_IDM;
+       return 0;
+}
+void kvm_guest_protect_msr(struct kvm_vcpu *vcpu, ulong prot_bitmap, bool set)
+{
+       /* XXX: Add similar MSR protection for BookE-PR */
+#ifdef CONFIG_KVM_BOOKE_HV
+       BUG_ON(prot_bitmap & ~(MSRP_UCLEP | MSRP_DEP | MSRP_PMMP));
+       if (set) {
+               if (prot_bitmap & MSR_UCLE)
+                       vcpu->arch.shadow_msrp |= MSRP_UCLEP;
+               if (prot_bitmap & MSR_DE)
+                       vcpu->arch.shadow_msrp |= MSRP_DEP;
+               if (prot_bitmap & MSR_PMM)
+                       vcpu->arch.shadow_msrp |= MSRP_PMMP;
+       } else {
+               if (prot_bitmap & MSR_UCLE)
+                       vcpu->arch.shadow_msrp &= ~MSRP_UCLEP;
+               if (prot_bitmap & MSR_DE)
+                       vcpu->arch.shadow_msrp &= ~MSRP_DEP;
+               if (prot_bitmap & MSR_PMM)
+                       vcpu->arch.shadow_msrp &= ~MSRP_PMMP;
+       }
+#endif
+}
+
+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
+                                        struct kvm_guest_debug *dbg)
+{
+       struct debug_reg *dbg_reg;
+       int n, b = 0, w = 0;
+
+       if (!(dbg->control & KVM_GUESTDBG_ENABLE)) {
+               vcpu->arch.shadow_dbg_reg.dbcr0 = 0;
+               vcpu->guest_debug = 0;
+               kvm_guest_protect_msr(vcpu, MSR_DE, false);
+               return 0;
+       }
+
+       kvm_guest_protect_msr(vcpu, MSR_DE, true);
+       vcpu->guest_debug = dbg->control;
+       vcpu->arch.shadow_dbg_reg.dbcr0 = 0;
+       /* Set DBCR0_EDM in guest visible DBCR0 register. */
+       vcpu->arch.dbg_reg.dbcr0 = DBCR0_EDM;
+
+       if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
+               vcpu->arch.shadow_dbg_reg.dbcr0 |= DBCR0_IDM | DBCR0_IC;
+
+       /* Code below handles only HW breakpoints */
+       dbg_reg = &(vcpu->arch.shadow_dbg_reg);
+
+#ifdef CONFIG_KVM_BOOKE_HV
+       /*
+        * On BookE-HV (e500mc) the guest is always executed with MSR.GS=1
+        * DBCR1 and DBCR2 are set to trigger debug events when MSR.PR is 0
+        */
+       dbg_reg->dbcr1 = 0;
+       dbg_reg->dbcr2 = 0;
+#else
+       /*
+        * On BookE-PR (e500v2) the guest is always executed with MSR.PR=1
+        * We set DBCR1 and DBCR2 to only trigger debug events when MSR.PR
+        * is set.
+        */
+       dbg_reg->dbcr1 = DBCR1_IAC1US | DBCR1_IAC2US | DBCR1_IAC3US |
+                         DBCR1_IAC4US;
+       dbg_reg->dbcr2 = DBCR2_DAC1US | DBCR2_DAC2US;
+#endif
+
+       if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP))
+               return 0;
+
+       for (n = 0; n < (KVMPPC_BOOKE_IAC_NUM + KVMPPC_BOOKE_DAC_NUM); n++) {
+               uint64_t addr = dbg->arch.bp[n].addr;
+               uint32_t type = dbg->arch.bp[n].type;
+
+               if (type == KVMPPC_DEBUG_NONE)
+                       continue;
+
+               if (type & !(KVMPPC_DEBUG_WATCH_READ |
+                            KVMPPC_DEBUG_WATCH_WRITE |
+                            KVMPPC_DEBUG_BREAKPOINT))
+                       return -EINVAL;
+
+               if (type & KVMPPC_DEBUG_BREAKPOINT) {
+                       /* Setting H/W breakpoint */
+                       if (kvmppc_booke_add_breakpoint(dbg_reg, addr, b++))
+                               return -EINVAL;
+               } else {
+                       /* Setting H/W watchpoint */
+                       if (kvmppc_booke_add_watchpoint(dbg_reg, addr,
+                                                       type, w++))
+                               return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 {
        vcpu->cpu = smp_processor_id();
@@ -1680,6 +1903,44 @@ void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu)
 {
        current->thread.kvm_vcpu = NULL;
        vcpu->cpu = -1;
+
+       /* Clear pending debug event in DBSR */
+       kvmppc_clear_dbsr();
+}
+
+void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
+{
+       vcpu->kvm->arch.kvm_ops->mmu_destroy(vcpu);
+}
+
+int kvmppc_core_init_vm(struct kvm *kvm)
+{
+       return kvm->arch.kvm_ops->init_vm(kvm);
+}
+
+struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
+{
+       return kvm->arch.kvm_ops->vcpu_create(kvm, id);
+}
+
+void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
+{
+       vcpu->kvm->arch.kvm_ops->vcpu_free(vcpu);
+}
+
+void kvmppc_core_destroy_vm(struct kvm *kvm)
+{
+       kvm->arch.kvm_ops->destroy_vm(kvm);
+}
+
+void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+       vcpu->kvm->arch.kvm_ops->vcpu_load(vcpu, cpu);
+}
+
+void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
+{
+       vcpu->kvm->arch.kvm_ops->vcpu_put(vcpu);
 }
 
 int __init kvmppc_booke_init(void)
index 5fd1ba6..09bfd9b 100644 (file)
@@ -99,6 +99,30 @@ enum int_class {
 
 void kvmppc_set_pending_interrupt(struct kvm_vcpu *vcpu, enum int_class type);
 
+extern void kvmppc_mmu_destroy_44x(struct kvm_vcpu *vcpu);
+extern int kvmppc_core_emulate_op_44x(struct kvm_run *run, struct kvm_vcpu *vcpu,
+                                     unsigned int inst, int *advance);
+extern int kvmppc_core_emulate_mtspr_44x(struct kvm_vcpu *vcpu, int sprn,
+                                        ulong spr_val);
+extern int kvmppc_core_emulate_mfspr_44x(struct kvm_vcpu *vcpu, int sprn,
+                                        ulong *spr_val);
+extern void kvmppc_mmu_destroy_e500(struct kvm_vcpu *vcpu);
+extern int kvmppc_core_emulate_op_e500(struct kvm_run *run,
+                                      struct kvm_vcpu *vcpu,
+                                      unsigned int inst, int *advance);
+extern int kvmppc_core_emulate_mtspr_e500(struct kvm_vcpu *vcpu, int sprn,
+                                         ulong spr_val);
+extern int kvmppc_core_emulate_mfspr_e500(struct kvm_vcpu *vcpu, int sprn,
+                                         ulong *spr_val);
+extern void kvmppc_mmu_destroy_e500(struct kvm_vcpu *vcpu);
+extern int kvmppc_core_emulate_op_e500(struct kvm_run *run,
+                                      struct kvm_vcpu *vcpu,
+                                      unsigned int inst, int *advance);
+extern int kvmppc_core_emulate_mtspr_e500(struct kvm_vcpu *vcpu, int sprn,
+                                         ulong spr_val);
+extern int kvmppc_core_emulate_mfspr_e500(struct kvm_vcpu *vcpu, int sprn,
+                                         ulong *spr_val);
+
 /*
  * Load up guest vcpu FP state if it's needed.
  * It also set the MSR_FP in thread so that host know
@@ -129,4 +153,9 @@ static inline void kvmppc_save_guest_fp(struct kvm_vcpu *vcpu)
                giveup_fpu(current);
 #endif
 }
+
+static inline void kvmppc_clear_dbsr(void)
+{
+       mtspr(SPRN_DBSR, mfspr(SPRN_DBSR));
+}
 #endif /* __KVM_BOOKE_H__ */
index ce6b73c..497b142 100644 (file)
@@ -305,7 +305,7 @@ void kvmppc_core_load_guest_debugstate(struct kvm_vcpu *vcpu)
 {
 }
 
-void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+static void kvmppc_core_vcpu_load_e500(struct kvm_vcpu *vcpu, int cpu)
 {
        kvmppc_booke_vcpu_load(vcpu, cpu);
 
@@ -313,7 +313,7 @@ void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
        kvmppc_e500_recalc_shadow_pid(to_e500(vcpu));
 }
 
-void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
+static void kvmppc_core_vcpu_put_e500(struct kvm_vcpu *vcpu)
 {
 #ifdef CONFIG_SPE
        if (vcpu->arch.shadow_msr & MSR_SPE)
@@ -367,7 +367,8 @@ int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu)
        return 0;
 }
 
-void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
+static int kvmppc_core_get_sregs_e500(struct kvm_vcpu *vcpu,
+                                     struct kvm_sregs *sregs)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
 
@@ -388,9 +389,11 @@ void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
 
        kvmppc_get_sregs_ivor(vcpu, sregs);
        kvmppc_get_sregs_e500_tlb(vcpu, sregs);
+       return 0;
 }
 
-int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
+static int kvmppc_core_set_sregs_e500(struct kvm_vcpu *vcpu,
+                                     struct kvm_sregs *sregs)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
        int ret;
@@ -425,21 +428,22 @@ int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
        return kvmppc_set_sregs_ivor(vcpu, sregs);
 }
 
-int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id,
-                       union kvmppc_one_reg *val)
+static int kvmppc_get_one_reg_e500(struct kvm_vcpu *vcpu, u64 id,
+                                  union kvmppc_one_reg *val)
 {
        int r = kvmppc_get_one_reg_e500_tlb(vcpu, id, val);
        return r;
 }
 
-int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id,
-                      union kvmppc_one_reg *val)
+static int kvmppc_set_one_reg_e500(struct kvm_vcpu *vcpu, u64 id,
+                                  union kvmppc_one_reg *val)
 {
        int r = kvmppc_get_one_reg_e500_tlb(vcpu, id, val);
        return r;
 }
 
-struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
+static struct kvm_vcpu *kvmppc_core_vcpu_create_e500(struct kvm *kvm,
+                                                    unsigned int id)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500;
        struct kvm_vcpu *vcpu;
@@ -481,7 +485,7 @@ out:
        return ERR_PTR(err);
 }
 
-void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
+static void kvmppc_core_vcpu_free_e500(struct kvm_vcpu *vcpu)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
 
@@ -492,15 +496,32 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
        kmem_cache_free(kvm_vcpu_cache, vcpu_e500);
 }
 
-int kvmppc_core_init_vm(struct kvm *kvm)
+static int kvmppc_core_init_vm_e500(struct kvm *kvm)
 {
        return 0;
 }
 
-void kvmppc_core_destroy_vm(struct kvm *kvm)
+static void kvmppc_core_destroy_vm_e500(struct kvm *kvm)
 {
 }
 
+static struct kvmppc_ops kvm_ops_e500 = {
+       .get_sregs = kvmppc_core_get_sregs_e500,
+       .set_sregs = kvmppc_core_set_sregs_e500,
+       .get_one_reg = kvmppc_get_one_reg_e500,
+       .set_one_reg = kvmppc_set_one_reg_e500,
+       .vcpu_load   = kvmppc_core_vcpu_load_e500,
+       .vcpu_put    = kvmppc_core_vcpu_put_e500,
+       .vcpu_create = kvmppc_core_vcpu_create_e500,
+       .vcpu_free   = kvmppc_core_vcpu_free_e500,
+       .mmu_destroy  = kvmppc_mmu_destroy_e500,
+       .init_vm = kvmppc_core_init_vm_e500,
+       .destroy_vm = kvmppc_core_destroy_vm_e500,
+       .emulate_op = kvmppc_core_emulate_op_e500,
+       .emulate_mtspr = kvmppc_core_emulate_mtspr_e500,
+       .emulate_mfspr = kvmppc_core_emulate_mfspr_e500,
+};
+
 static int __init kvmppc_e500_init(void)
 {
        int r, i;
@@ -512,11 +533,11 @@ static int __init kvmppc_e500_init(void)
 
        r = kvmppc_core_check_processor_compat();
        if (r)
-               return r;
+               goto err_out;
 
        r = kvmppc_booke_init();
        if (r)
-               return r;
+               goto err_out;
 
        /* copy extra E500 exception handlers */
        ivor[0] = mfspr(SPRN_IVOR32);
@@ -534,11 +555,19 @@ static int __init kvmppc_e500_init(void)
        flush_icache_range(kvmppc_booke_handlers, kvmppc_booke_handlers +
                           ivor[max_ivor] + handler_len);
 
-       return kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), 0, THIS_MODULE);
+       r = kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), 0, THIS_MODULE);
+       if (r)
+               goto err_out;
+       kvm_ops_e500.owner = THIS_MODULE;
+       kvmppc_pr_ops = &kvm_ops_e500;
+
+err_out:
+       return r;
 }
 
 static void __exit kvmppc_e500_exit(void)
 {
+       kvmppc_pr_ops = NULL;
        kvmppc_booke_exit();
 }
 
index c2e5e98..4fd9650 100644 (file)
@@ -117,7 +117,7 @@ static inline struct kvmppc_vcpu_e500 *to_e500(struct kvm_vcpu *vcpu)
 #define E500_TLB_USER_PERM_MASK (MAS3_UX|MAS3_UR|MAS3_UW)
 #define E500_TLB_SUPER_PERM_MASK (MAS3_SX|MAS3_SR|MAS3_SW)
 #define MAS2_ATTRIB_MASK \
-         (MAS2_X0 | MAS2_X1)
+         (MAS2_X0 | MAS2_X1 | MAS2_E | MAS2_G)
 #define MAS3_ATTRIB_MASK \
          (MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3 \
           | E500_TLB_USER_PERM_MASK | E500_TLB_SUPER_PERM_MASK)
index b10a012..89b7f82 100644 (file)
@@ -26,6 +26,7 @@
 #define XOP_TLBRE   946
 #define XOP_TLBWE   978
 #define XOP_TLBILX  18
+#define XOP_EHPRIV  270
 
 #ifdef CONFIG_KVM_E500MC
 static int dbell2prio(ulong param)
@@ -82,8 +83,28 @@ static int kvmppc_e500_emul_msgsnd(struct kvm_vcpu *vcpu, int rb)
 }
 #endif
 
-int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
-                           unsigned int inst, int *advance)
+static int kvmppc_e500_emul_ehpriv(struct kvm_run *run, struct kvm_vcpu *vcpu,
+                                  unsigned int inst, int *advance)
+{
+       int emulated = EMULATE_DONE;
+
+       switch (get_oc(inst)) {
+       case EHPRIV_OC_DEBUG:
+               run->exit_reason = KVM_EXIT_DEBUG;
+               run->debug.arch.address = vcpu->arch.pc;
+               run->debug.arch.status = 0;
+               kvmppc_account_exit(vcpu, DEBUG_EXITS);
+               emulated = EMULATE_EXIT_USER;
+               *advance = 0;
+               break;
+       default:
+               emulated = EMULATE_FAIL;
+       }
+       return emulated;
+}
+
+int kvmppc_core_emulate_op_e500(struct kvm_run *run, struct kvm_vcpu *vcpu,
+                               unsigned int inst, int *advance)
 {
        int emulated = EMULATE_DONE;
        int ra = get_ra(inst);
@@ -130,6 +151,11 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        emulated = kvmppc_e500_emul_tlbivax(vcpu, ea);
                        break;
 
+               case XOP_EHPRIV:
+                       emulated = kvmppc_e500_emul_ehpriv(run, vcpu, inst,
+                                                          advance);
+                       break;
+
                default:
                        emulated = EMULATE_FAIL;
                }
@@ -146,7 +172,7 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
        return emulated;
 }
 
-int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
+int kvmppc_core_emulate_mtspr_e500(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
        int emulated = EMULATE_DONE;
@@ -237,7 +263,7 @@ int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
        return emulated;
 }
 
-int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val)
+int kvmppc_core_emulate_mfspr_e500(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
        int emulated = EMULATE_DONE;
index 6d6f153..ebca6b8 100644 (file)
@@ -32,7 +32,7 @@
 #include <asm/kvm_ppc.h>
 
 #include "e500.h"
-#include "trace.h"
+#include "trace_booke.h"
 #include "timing.h"
 #include "e500_mmu_host.h"
 
@@ -536,7 +536,7 @@ gpa_t kvmppc_mmu_xlate(struct kvm_vcpu *vcpu, unsigned int index,
        return get_tlb_raddr(gtlbe) | (eaddr & pgmask);
 }
 
-void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
+void kvmppc_mmu_destroy_e500(struct kvm_vcpu *vcpu)
 {
 }
 
index c65593a..ecf2247 100644 (file)
 #include <asm/kvm_ppc.h>
 
 #include "e500.h"
-#include "trace.h"
 #include "timing.h"
 #include "e500_mmu_host.h"
 
+#include "trace_booke.h"
+
 #define to_htlb1_esel(esel) (host_tlb_params[1].entries - (esel) - 1)
 
 static struct kvmppc_e500_tlb_params host_tlb_params[E500_TLB_NUM];
@@ -253,6 +254,9 @@ static inline void kvmppc_e500_ref_setup(struct tlbe_ref *ref,
        ref->pfn = pfn;
        ref->flags |= E500_TLB_VALID;
 
+       /* Mark the page accessed */
+       kvm_set_pfn_accessed(pfn);
+
        if (tlbe_is_writable(gtlbe))
                kvm_set_pfn_dirty(pfn);
 }
index 19c8379..4132cd2 100644 (file)
@@ -110,7 +110,7 @@ void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr)
 
 static DEFINE_PER_CPU(struct kvm_vcpu *, last_vcpu_on_cpu);
 
-void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+static void kvmppc_core_vcpu_load_e500mc(struct kvm_vcpu *vcpu, int cpu)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
 
@@ -147,7 +147,7 @@ void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
        kvmppc_load_guest_fp(vcpu);
 }
 
-void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
+static void kvmppc_core_vcpu_put_e500mc(struct kvm_vcpu *vcpu)
 {
        vcpu->arch.eplc = mfspr(SPRN_EPLC);
        vcpu->arch.epsc = mfspr(SPRN_EPSC);
@@ -204,7 +204,8 @@ int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu)
        return 0;
 }
 
-void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
+static int kvmppc_core_get_sregs_e500mc(struct kvm_vcpu *vcpu,
+                                       struct kvm_sregs *sregs)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
 
@@ -224,10 +225,11 @@ void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
        sregs->u.e.ivor_high[4] = vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL];
        sregs->u.e.ivor_high[5] = vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL_CRIT];
 
-       kvmppc_get_sregs_ivor(vcpu, sregs);
+       return kvmppc_get_sregs_ivor(vcpu, sregs);
 }
 
-int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
+static int kvmppc_core_set_sregs_e500mc(struct kvm_vcpu *vcpu,
+                                       struct kvm_sregs *sregs)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
        int ret;
@@ -260,21 +262,22 @@ int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
        return kvmppc_set_sregs_ivor(vcpu, sregs);
 }
 
-int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id,
-                       union kvmppc_one_reg *val)
+static int kvmppc_get_one_reg_e500mc(struct kvm_vcpu *vcpu, u64 id,
+                             union kvmppc_one_reg *val)
 {
        int r = kvmppc_get_one_reg_e500_tlb(vcpu, id, val);
        return r;
 }
 
-int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id,
-                      union kvmppc_one_reg *val)
+static int kvmppc_set_one_reg_e500mc(struct kvm_vcpu *vcpu, u64 id,
+                             union kvmppc_one_reg *val)
 {
        int r = kvmppc_set_one_reg_e500_tlb(vcpu, id, val);
        return r;
 }
 
-struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
+static struct kvm_vcpu *kvmppc_core_vcpu_create_e500mc(struct kvm *kvm,
+                                                      unsigned int id)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500;
        struct kvm_vcpu *vcpu;
@@ -315,7 +318,7 @@ out:
        return ERR_PTR(err);
 }
 
-void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
+static void kvmppc_core_vcpu_free_e500mc(struct kvm_vcpu *vcpu)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
 
@@ -325,7 +328,7 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
        kmem_cache_free(kvm_vcpu_cache, vcpu_e500);
 }
 
-int kvmppc_core_init_vm(struct kvm *kvm)
+static int kvmppc_core_init_vm_e500mc(struct kvm *kvm)
 {
        int lpid;
 
@@ -337,27 +340,52 @@ int kvmppc_core_init_vm(struct kvm *kvm)
        return 0;
 }
 
-void kvmppc_core_destroy_vm(struct kvm *kvm)
+static void kvmppc_core_destroy_vm_e500mc(struct kvm *kvm)
 {
        kvmppc_free_lpid(kvm->arch.lpid);
 }
 
+static struct kvmppc_ops kvm_ops_e500mc = {
+       .get_sregs = kvmppc_core_get_sregs_e500mc,
+       .set_sregs = kvmppc_core_set_sregs_e500mc,
+       .get_one_reg = kvmppc_get_one_reg_e500mc,
+       .set_one_reg = kvmppc_set_one_reg_e500mc,
+       .vcpu_load   = kvmppc_core_vcpu_load_e500mc,
+       .vcpu_put    = kvmppc_core_vcpu_put_e500mc,
+       .vcpu_create = kvmppc_core_vcpu_create_e500mc,
+       .vcpu_free   = kvmppc_core_vcpu_free_e500mc,
+       .mmu_destroy  = kvmppc_mmu_destroy_e500,
+       .init_vm = kvmppc_core_init_vm_e500mc,
+       .destroy_vm = kvmppc_core_destroy_vm_e500mc,
+       .emulate_op = kvmppc_core_emulate_op_e500,
+       .emulate_mtspr = kvmppc_core_emulate_mtspr_e500,
+       .emulate_mfspr = kvmppc_core_emulate_mfspr_e500,
+};
+
 static int __init kvmppc_e500mc_init(void)
 {
        int r;
 
        r = kvmppc_booke_init();
        if (r)
-               return r;
+               goto err_out;
 
        kvmppc_init_lpid(64);
        kvmppc_claim_lpid(0); /* host */
 
-       return kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), 0, THIS_MODULE);
+       r = kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), 0, THIS_MODULE);
+       if (r)
+               goto err_out;
+       kvm_ops_e500mc.owner = THIS_MODULE;
+       kvmppc_pr_ops = &kvm_ops_e500mc;
+
+err_out:
+       return r;
 }
 
 static void __exit kvmppc_e500mc_exit(void)
 {
+       kvmppc_pr_ops = NULL;
        kvmppc_booke_exit();
 }
 
index 751cd45..2f9a087 100644 (file)
@@ -130,8 +130,8 @@ static int kvmppc_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
        case SPRN_PIR: break;
 
        default:
-               emulated = kvmppc_core_emulate_mtspr(vcpu, sprn,
-                                                    spr_val);
+               emulated = vcpu->kvm->arch.kvm_ops->emulate_mtspr(vcpu, sprn,
+                                                                 spr_val);
                if (emulated == EMULATE_FAIL)
                        printk(KERN_INFO "mtspr: unknown spr "
                                "0x%x\n", sprn);
@@ -191,8 +191,8 @@ static int kvmppc_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
                spr_val = kvmppc_get_dec(vcpu, get_tb());
                break;
        default:
-               emulated = kvmppc_core_emulate_mfspr(vcpu, sprn,
-                                                    &spr_val);
+               emulated = vcpu->kvm->arch.kvm_ops->emulate_mfspr(vcpu, sprn,
+                                                                 &spr_val);
                if (unlikely(emulated == EMULATE_FAIL)) {
                        printk(KERN_INFO "mfspr: unknown spr "
                                "0x%x\n", sprn);
@@ -464,7 +464,8 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
        }
 
        if (emulated == EMULATE_FAIL) {
-               emulated = kvmppc_core_emulate_op(run, vcpu, inst, &advance);
+               emulated = vcpu->kvm->arch.kvm_ops->emulate_op(run, vcpu, inst,
+                                                              &advance);
                if (emulated == EMULATE_AGAIN) {
                        advance = 0;
                } else if (emulated == EMULATE_FAIL) {
@@ -483,3 +484,4 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
 
        return emulated;
 }
+EXPORT_SYMBOL_GPL(kvmppc_emulate_instruction);
index 07c0106..9ae9768 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/fs.h>
 #include <linux/slab.h>
 #include <linux/file.h>
+#include <linux/module.h>
 #include <asm/cputable.h>
 #include <asm/uaccess.h>
 #include <asm/kvm_ppc.h>
 #define CREATE_TRACE_POINTS
 #include "trace.h"
 
+struct kvmppc_ops *kvmppc_hv_ops;
+EXPORT_SYMBOL_GPL(kvmppc_hv_ops);
+struct kvmppc_ops *kvmppc_pr_ops;
+EXPORT_SYMBOL_GPL(kvmppc_pr_ops);
+
+
 int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
 {
        return !!(v->arch.pending_exceptions) ||
@@ -50,7 +57,6 @@ int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
        return 1;
 }
 
-#ifndef CONFIG_KVM_BOOK3S_64_HV
 /*
  * Common checks before entering the guest world.  Call with interrupts
  * disabled.
@@ -125,7 +131,7 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu)
 
        return r;
 }
-#endif /* CONFIG_KVM_BOOK3S_64_HV */
+EXPORT_SYMBOL_GPL(kvmppc_prepare_to_enter);
 
 int kvmppc_kvm_pv(struct kvm_vcpu *vcpu)
 {
@@ -179,6 +185,7 @@ int kvmppc_kvm_pv(struct kvm_vcpu *vcpu)
 
        return r;
 }
+EXPORT_SYMBOL_GPL(kvmppc_kvm_pv);
 
 int kvmppc_sanity_check(struct kvm_vcpu *vcpu)
 {
@@ -192,11 +199,9 @@ int kvmppc_sanity_check(struct kvm_vcpu *vcpu)
        if ((vcpu->arch.cpu_type != KVM_CPU_3S_64) && vcpu->arch.papr_enabled)
                goto out;
 
-#ifdef CONFIG_KVM_BOOK3S_64_HV
        /* HV KVM can only do PAPR mode for now */
-       if (!vcpu->arch.papr_enabled)
+       if (!vcpu->arch.papr_enabled && is_kvmppc_hv_enabled(vcpu->kvm))
                goto out;
-#endif
 
 #ifdef CONFIG_KVM_BOOKE_HV
        if (!cpu_has_feature(CPU_FTR_EMB_HV))
@@ -209,6 +214,7 @@ out:
        vcpu->arch.sane = r;
        return r ? 0 : -EINVAL;
 }
+EXPORT_SYMBOL_GPL(kvmppc_sanity_check);
 
 int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu)
 {
@@ -243,6 +249,7 @@ int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu)
 
        return r;
 }
+EXPORT_SYMBOL_GPL(kvmppc_emulate_mmio);
 
 int kvm_arch_hardware_enable(void *garbage)
 {
@@ -269,10 +276,35 @@ void kvm_arch_check_processor_compat(void *rtn)
 
 int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 {
-       if (type)
-               return -EINVAL;
-
+       struct kvmppc_ops *kvm_ops = NULL;
+       /*
+        * if we have both HV and PR enabled, default is HV
+        */
+       if (type == 0) {
+               if (kvmppc_hv_ops)
+                       kvm_ops = kvmppc_hv_ops;
+               else
+                       kvm_ops = kvmppc_pr_ops;
+               if (!kvm_ops)
+                       goto err_out;
+       } else  if (type == KVM_VM_PPC_HV) {
+               if (!kvmppc_hv_ops)
+                       goto err_out;
+               kvm_ops = kvmppc_hv_ops;
+       } else if (type == KVM_VM_PPC_PR) {
+               if (!kvmppc_pr_ops)
+                       goto err_out;
+               kvm_ops = kvmppc_pr_ops;
+       } else
+               goto err_out;
+
+       if (kvm_ops->owner && !try_module_get(kvm_ops->owner))
+               return -ENOENT;
+
+       kvm->arch.kvm_ops = kvm_ops;
        return kvmppc_core_init_vm(kvm);
+err_out:
+       return -EINVAL;
 }
 
 void kvm_arch_destroy_vm(struct kvm *kvm)
@@ -292,6 +324,9 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
        kvmppc_core_destroy_vm(kvm);
 
        mutex_unlock(&kvm->lock);
+
+       /* drop the module reference */
+       module_put(kvm->arch.kvm_ops->owner);
 }
 
 void kvm_arch_sync_events(struct kvm *kvm)
@@ -301,6 +336,10 @@ void kvm_arch_sync_events(struct kvm *kvm)
 int kvm_dev_ioctl_check_extension(long ext)
 {
        int r;
+       /* FIXME!!
+        * Should some of this be vm ioctl ? is it possible now ?
+        */
+       int hv_enabled = kvmppc_hv_ops ? 1 : 0;
 
        switch (ext) {
 #ifdef CONFIG_BOOKE
@@ -320,22 +359,26 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_DEVICE_CTRL:
                r = 1;
                break;
-#ifndef CONFIG_KVM_BOOK3S_64_HV
        case KVM_CAP_PPC_PAIRED_SINGLES:
        case KVM_CAP_PPC_OSI:
        case KVM_CAP_PPC_GET_PVINFO:
 #if defined(CONFIG_KVM_E500V2) || defined(CONFIG_KVM_E500MC)
        case KVM_CAP_SW_TLB:
 #endif
-#ifdef CONFIG_KVM_MPIC
-       case KVM_CAP_IRQ_MPIC:
-#endif
-               r = 1;
+               /* We support this only for PR */
+               r = !hv_enabled;
                break;
+#ifdef CONFIG_KVM_MMIO
        case KVM_CAP_COALESCED_MMIO:
                r = KVM_COALESCED_MMIO_PAGE_OFFSET;
                break;
 #endif
+#ifdef CONFIG_KVM_MPIC
+       case KVM_CAP_IRQ_MPIC:
+               r = 1;
+               break;
+#endif
+
 #ifdef CONFIG_PPC_BOOK3S_64
        case KVM_CAP_SPAPR_TCE:
        case KVM_CAP_PPC_ALLOC_HTAB:
@@ -346,32 +389,37 @@ int kvm_dev_ioctl_check_extension(long ext)
                r = 1;
                break;
 #endif /* CONFIG_PPC_BOOK3S_64 */
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        case KVM_CAP_PPC_SMT:
-               r = threads_per_core;
+               if (hv_enabled)
+                       r = threads_per_core;
+               else
+                       r = 0;
                break;
        case KVM_CAP_PPC_RMA:
-               r = 1;
+               r = hv_enabled;
                /* PPC970 requires an RMA */
-               if (cpu_has_feature(CPU_FTR_ARCH_201))
+               if (r && cpu_has_feature(CPU_FTR_ARCH_201))
                        r = 2;
                break;
 #endif
        case KVM_CAP_SYNC_MMU:
-#ifdef CONFIG_KVM_BOOK3S_64_HV
-               r = cpu_has_feature(CPU_FTR_ARCH_206) ? 1 : 0;
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
+               if (hv_enabled)
+                       r = cpu_has_feature(CPU_FTR_ARCH_206) ? 1 : 0;
+               else
+                       r = 0;
 #elif defined(KVM_ARCH_WANT_MMU_NOTIFIER)
                r = 1;
 #else
                r = 0;
-               break;
 #endif
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+               break;
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        case KVM_CAP_PPC_HTAB_FD:
-               r = 1;
+               r = hv_enabled;
                break;
 #endif
-               break;
        case KVM_CAP_NR_VCPUS:
                /*
                 * Recommending a number of CPUs is somewhat arbitrary; we
@@ -379,11 +427,10 @@ int kvm_dev_ioctl_check_extension(long ext)
                 * will have secondary threads "offline"), and for other KVM
                 * implementations just count online CPUs.
                 */
-#ifdef CONFIG_KVM_BOOK3S_64_HV
-               r = num_present_cpus();
-#else
-               r = num_online_cpus();
-#endif
+               if (hv_enabled)
+                       r = num_present_cpus();
+               else
+                       r = num_online_cpus();
                break;
        case KVM_CAP_MAX_VCPUS:
                r = KVM_MAX_VCPUS;
@@ -407,15 +454,16 @@ long kvm_arch_dev_ioctl(struct file *filp,
        return -EINVAL;
 }
 
-void kvm_arch_free_memslot(struct kvm_memory_slot *free,
+void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
                           struct kvm_memory_slot *dont)
 {
-       kvmppc_core_free_memslot(free, dont);
+       kvmppc_core_free_memslot(kvm, free, dont);
 }
 
-int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
+int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
+                           unsigned long npages)
 {
-       return kvmppc_core_create_memslot(slot, npages);
+       return kvmppc_core_create_memslot(kvm, slot, npages);
 }
 
 void kvm_arch_memslots_updated(struct kvm *kvm)
@@ -659,6 +707,7 @@ int kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu,
 
        return EMULATE_DO_MMIO;
 }
+EXPORT_SYMBOL_GPL(kvmppc_handle_load);
 
 /* Same as above, but sign extends */
 int kvmppc_handle_loads(struct kvm_run *run, struct kvm_vcpu *vcpu,
@@ -720,6 +769,7 @@ int kvmppc_handle_store(struct kvm_run *run, struct kvm_vcpu *vcpu,
 
        return EMULATE_DO_MMIO;
 }
+EXPORT_SYMBOL_GPL(kvmppc_handle_store);
 
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
@@ -1024,52 +1074,12 @@ long kvm_arch_vm_ioctl(struct file *filp,
                r = kvm_vm_ioctl_create_spapr_tce(kvm, &create_tce);
                goto out;
        }
-#endif /* CONFIG_PPC_BOOK3S_64 */
-
-#ifdef CONFIG_KVM_BOOK3S_64_HV
-       case KVM_ALLOCATE_RMA: {
-               struct kvm_allocate_rma rma;
-               struct kvm *kvm = filp->private_data;
-
-               r = kvm_vm_ioctl_allocate_rma(kvm, &rma);
-               if (r >= 0 && copy_to_user(argp, &rma, sizeof(rma)))
-                       r = -EFAULT;
-               break;
-       }
-
-       case KVM_PPC_ALLOCATE_HTAB: {
-               u32 htab_order;
-
-               r = -EFAULT;
-               if (get_user(htab_order, (u32 __user *)argp))
-                       break;
-               r = kvmppc_alloc_reset_hpt(kvm, &htab_order);
-               if (r)
-                       break;
-               r = -EFAULT;
-               if (put_user(htab_order, (u32 __user *)argp))
-                       break;
-               r = 0;
-               break;
-       }
-
-       case KVM_PPC_GET_HTAB_FD: {
-               struct kvm_get_htab_fd ghf;
-
-               r = -EFAULT;
-               if (copy_from_user(&ghf, argp, sizeof(ghf)))
-                       break;
-               r = kvm_vm_ioctl_get_htab_fd(kvm, &ghf);
-               break;
-       }
-#endif /* CONFIG_KVM_BOOK3S_64_HV */
-
-#ifdef CONFIG_PPC_BOOK3S_64
        case KVM_PPC_GET_SMMU_INFO: {
                struct kvm_ppc_smmu_info info;
+               struct kvm *kvm = filp->private_data;
 
                memset(&info, 0, sizeof(info));
-               r = kvm_vm_ioctl_get_smmu_info(kvm, &info);
+               r = kvm->arch.kvm_ops->get_smmu_info(kvm, &info);
                if (r >= 0 && copy_to_user(argp, &info, sizeof(info)))
                        r = -EFAULT;
                break;
@@ -1080,11 +1090,15 @@ long kvm_arch_vm_ioctl(struct file *filp,
                r = kvm_vm_ioctl_rtas_define_token(kvm, argp);
                break;
        }
-#endif /* CONFIG_PPC_BOOK3S_64 */
+       default: {
+               struct kvm *kvm = filp->private_data;
+               r = kvm->arch.kvm_ops->arch_vm_ioctl(filp, ioctl, arg);
+       }
+#else /* CONFIG_PPC_BOOK3S_64 */
        default:
                r = -ENOTTY;
+#endif
        }
-
 out:
        return r;
 }
@@ -1106,22 +1120,26 @@ long kvmppc_alloc_lpid(void)
 
        return lpid;
 }
+EXPORT_SYMBOL_GPL(kvmppc_alloc_lpid);
 
 void kvmppc_claim_lpid(long lpid)
 {
        set_bit(lpid, lpid_inuse);
 }
+EXPORT_SYMBOL_GPL(kvmppc_claim_lpid);
 
 void kvmppc_free_lpid(long lpid)
 {
        clear_bit(lpid, lpid_inuse);
 }
+EXPORT_SYMBOL_GPL(kvmppc_free_lpid);
 
 void kvmppc_init_lpid(unsigned long nr_lpids_param)
 {
        nr_lpids = min_t(unsigned long, KVMPPC_NR_LPIDS, nr_lpids_param);
        memset(lpid_inuse, 0, sizeof(lpid_inuse));
 }
+EXPORT_SYMBOL_GPL(kvmppc_init_lpid);
 
 int kvm_arch_init(void *opaque)
 {
@@ -1130,4 +1148,5 @@ int kvm_arch_init(void *opaque)
 
 void kvm_arch_exit(void)
 {
+
 }
index e326489..2e0e67e 100644 (file)
@@ -31,126 +31,6 @@ TRACE_EVENT(kvm_ppc_instr,
                  __entry->inst, __entry->pc, __entry->emulate)
 );
 
-#ifdef CONFIG_PPC_BOOK3S
-#define kvm_trace_symbol_exit \
-       {0x100, "SYSTEM_RESET"}, \
-       {0x200, "MACHINE_CHECK"}, \
-       {0x300, "DATA_STORAGE"}, \
-       {0x380, "DATA_SEGMENT"}, \
-       {0x400, "INST_STORAGE"}, \
-       {0x480, "INST_SEGMENT"}, \
-       {0x500, "EXTERNAL"}, \
-       {0x501, "EXTERNAL_LEVEL"}, \
-       {0x502, "EXTERNAL_HV"}, \
-       {0x600, "ALIGNMENT"}, \
-       {0x700, "PROGRAM"}, \
-       {0x800, "FP_UNAVAIL"}, \
-       {0x900, "DECREMENTER"}, \
-       {0x980, "HV_DECREMENTER"}, \
-       {0xc00, "SYSCALL"}, \
-       {0xd00, "TRACE"}, \
-       {0xe00, "H_DATA_STORAGE"}, \
-       {0xe20, "H_INST_STORAGE"}, \
-       {0xe40, "H_EMUL_ASSIST"}, \
-       {0xf00, "PERFMON"}, \
-       {0xf20, "ALTIVEC"}, \
-       {0xf40, "VSX"}
-#else
-#define kvm_trace_symbol_exit \
-       {0, "CRITICAL"}, \
-       {1, "MACHINE_CHECK"}, \
-       {2, "DATA_STORAGE"}, \
-       {3, "INST_STORAGE"}, \
-       {4, "EXTERNAL"}, \
-       {5, "ALIGNMENT"}, \
-       {6, "PROGRAM"}, \
-       {7, "FP_UNAVAIL"}, \
-       {8, "SYSCALL"}, \
-       {9, "AP_UNAVAIL"}, \
-       {10, "DECREMENTER"}, \
-       {11, "FIT"}, \
-       {12, "WATCHDOG"}, \
-       {13, "DTLB_MISS"}, \
-       {14, "ITLB_MISS"}, \
-       {15, "DEBUG"}, \
-       {32, "SPE_UNAVAIL"}, \
-       {33, "SPE_FP_DATA"}, \
-       {34, "SPE_FP_ROUND"}, \
-       {35, "PERFORMANCE_MONITOR"}, \
-       {36, "DOORBELL"}, \
-       {37, "DOORBELL_CRITICAL"}, \
-       {38, "GUEST_DBELL"}, \
-       {39, "GUEST_DBELL_CRIT"}, \
-       {40, "HV_SYSCALL"}, \
-       {41, "HV_PRIV"}
-#endif
-
-TRACE_EVENT(kvm_exit,
-       TP_PROTO(unsigned int exit_nr, struct kvm_vcpu *vcpu),
-       TP_ARGS(exit_nr, vcpu),
-
-       TP_STRUCT__entry(
-               __field(        unsigned int,   exit_nr         )
-               __field(        unsigned long,  pc              )
-               __field(        unsigned long,  msr             )
-               __field(        unsigned long,  dar             )
-#ifdef CONFIG_KVM_BOOK3S_PR
-               __field(        unsigned long,  srr1            )
-#endif
-               __field(        unsigned long,  last_inst       )
-       ),
-
-       TP_fast_assign(
-#ifdef CONFIG_KVM_BOOK3S_PR
-               struct kvmppc_book3s_shadow_vcpu *svcpu;
-#endif
-               __entry->exit_nr        = exit_nr;
-               __entry->pc             = kvmppc_get_pc(vcpu);
-               __entry->dar            = kvmppc_get_fault_dar(vcpu);
-               __entry->msr            = vcpu->arch.shared->msr;
-#ifdef CONFIG_KVM_BOOK3S_PR
-               svcpu = svcpu_get(vcpu);
-               __entry->srr1           = svcpu->shadow_srr1;
-               svcpu_put(svcpu);
-#endif
-               __entry->last_inst      = vcpu->arch.last_inst;
-       ),
-
-       TP_printk("exit=%s"
-               " | pc=0x%lx"
-               " | msr=0x%lx"
-               " | dar=0x%lx"
-#ifdef CONFIG_KVM_BOOK3S_PR
-               " | srr1=0x%lx"
-#endif
-               " | last_inst=0x%lx"
-               ,
-               __print_symbolic(__entry->exit_nr, kvm_trace_symbol_exit),
-               __entry->pc,
-               __entry->msr,
-               __entry->dar,
-#ifdef CONFIG_KVM_BOOK3S_PR
-               __entry->srr1,
-#endif
-               __entry->last_inst
-               )
-);
-
-TRACE_EVENT(kvm_unmap_hva,
-       TP_PROTO(unsigned long hva),
-       TP_ARGS(hva),
-
-       TP_STRUCT__entry(
-               __field(        unsigned long,  hva             )
-       ),
-
-       TP_fast_assign(
-               __entry->hva            = hva;
-       ),
-
-       TP_printk("unmap hva 0x%lx\n", __entry->hva)
-);
-
 TRACE_EVENT(kvm_stlb_inval,
        TP_PROTO(unsigned int stlb_index),
        TP_ARGS(stlb_index),
@@ -236,315 +116,6 @@ TRACE_EVENT(kvm_check_requests,
                __entry->cpu_nr, __entry->requests)
 );
 
-
-/*************************************************************************
- *                         Book3S trace points                           *
- *************************************************************************/
-
-#ifdef CONFIG_KVM_BOOK3S_PR
-
-TRACE_EVENT(kvm_book3s_reenter,
-       TP_PROTO(int r, struct kvm_vcpu *vcpu),
-       TP_ARGS(r, vcpu),
-
-       TP_STRUCT__entry(
-               __field(        unsigned int,   r               )
-               __field(        unsigned long,  pc              )
-       ),
-
-       TP_fast_assign(
-               __entry->r              = r;
-               __entry->pc             = kvmppc_get_pc(vcpu);
-       ),
-
-       TP_printk("reentry r=%d | pc=0x%lx", __entry->r, __entry->pc)
-);
-
-#ifdef CONFIG_PPC_BOOK3S_64
-
-TRACE_EVENT(kvm_book3s_64_mmu_map,
-       TP_PROTO(int rflags, ulong hpteg, ulong va, pfn_t hpaddr,
-                struct kvmppc_pte *orig_pte),
-       TP_ARGS(rflags, hpteg, va, hpaddr, orig_pte),
-
-       TP_STRUCT__entry(
-               __field(        unsigned char,          flag_w          )
-               __field(        unsigned char,          flag_x          )
-               __field(        unsigned long,          eaddr           )
-               __field(        unsigned long,          hpteg           )
-               __field(        unsigned long,          va              )
-               __field(        unsigned long long,     vpage           )
-               __field(        unsigned long,          hpaddr          )
-       ),
-
-       TP_fast_assign(
-               __entry->flag_w = ((rflags & HPTE_R_PP) == 3) ? '-' : 'w';
-               __entry->flag_x = (rflags & HPTE_R_N) ? '-' : 'x';
-               __entry->eaddr  = orig_pte->eaddr;
-               __entry->hpteg  = hpteg;
-               __entry->va     = va;
-               __entry->vpage  = orig_pte->vpage;
-               __entry->hpaddr = hpaddr;
-       ),
-
-       TP_printk("KVM: %c%c Map 0x%lx: [%lx] 0x%lx (0x%llx) -> %lx",
-                 __entry->flag_w, __entry->flag_x, __entry->eaddr,
-                 __entry->hpteg, __entry->va, __entry->vpage, __entry->hpaddr)
-);
-
-#endif /* CONFIG_PPC_BOOK3S_64 */
-
-TRACE_EVENT(kvm_book3s_mmu_map,
-       TP_PROTO(struct hpte_cache *pte),
-       TP_ARGS(pte),
-
-       TP_STRUCT__entry(
-               __field(        u64,            host_vpn        )
-               __field(        u64,            pfn             )
-               __field(        ulong,          eaddr           )
-               __field(        u64,            vpage           )
-               __field(        ulong,          raddr           )
-               __field(        int,            flags           )
-       ),
-
-       TP_fast_assign(
-               __entry->host_vpn       = pte->host_vpn;
-               __entry->pfn            = pte->pfn;
-               __entry->eaddr          = pte->pte.eaddr;
-               __entry->vpage          = pte->pte.vpage;
-               __entry->raddr          = pte->pte.raddr;
-               __entry->flags          = (pte->pte.may_read ? 0x4 : 0) |
-                                         (pte->pte.may_write ? 0x2 : 0) |
-                                         (pte->pte.may_execute ? 0x1 : 0);
-       ),
-
-       TP_printk("Map: hvpn=%llx pfn=%llx ea=%lx vp=%llx ra=%lx [%x]",
-                 __entry->host_vpn, __entry->pfn, __entry->eaddr,
-                 __entry->vpage, __entry->raddr, __entry->flags)
-);
-
-TRACE_EVENT(kvm_book3s_mmu_invalidate,
-       TP_PROTO(struct hpte_cache *pte),
-       TP_ARGS(pte),
-
-       TP_STRUCT__entry(
-               __field(        u64,            host_vpn        )
-               __field(        u64,            pfn             )
-               __field(        ulong,          eaddr           )
-               __field(        u64,            vpage           )
-               __field(        ulong,          raddr           )
-               __field(        int,            flags           )
-       ),
-
-       TP_fast_assign(
-               __entry->host_vpn       = pte->host_vpn;
-               __entry->pfn            = pte->pfn;
-               __entry->eaddr          = pte->pte.eaddr;
-               __entry->vpage          = pte->pte.vpage;
-               __entry->raddr          = pte->pte.raddr;
-               __entry->flags          = (pte->pte.may_read ? 0x4 : 0) |
-                                         (pte->pte.may_write ? 0x2 : 0) |
-                                         (pte->pte.may_execute ? 0x1 : 0);
-       ),
-
-       TP_printk("Flush: hva=%llx pfn=%llx ea=%lx vp=%llx ra=%lx [%x]",
-                 __entry->host_vpn, __entry->pfn, __entry->eaddr,
-                 __entry->vpage, __entry->raddr, __entry->flags)
-);
-
-TRACE_EVENT(kvm_book3s_mmu_flush,
-       TP_PROTO(const char *type, struct kvm_vcpu *vcpu, unsigned long long p1,
-                unsigned long long p2),
-       TP_ARGS(type, vcpu, p1, p2),
-
-       TP_STRUCT__entry(
-               __field(        int,                    count           )
-               __field(        unsigned long long,     p1              )
-               __field(        unsigned long long,     p2              )
-               __field(        const char *,           type            )
-       ),
-
-       TP_fast_assign(
-               __entry->count          = to_book3s(vcpu)->hpte_cache_count;
-               __entry->p1             = p1;
-               __entry->p2             = p2;
-               __entry->type           = type;
-       ),
-
-       TP_printk("Flush %d %sPTEs: %llx - %llx",
-                 __entry->count, __entry->type, __entry->p1, __entry->p2)
-);
-
-TRACE_EVENT(kvm_book3s_slb_found,
-       TP_PROTO(unsigned long long gvsid, unsigned long long hvsid),
-       TP_ARGS(gvsid, hvsid),
-
-       TP_STRUCT__entry(
-               __field(        unsigned long long,     gvsid           )
-               __field(        unsigned long long,     hvsid           )
-       ),
-
-       TP_fast_assign(
-               __entry->gvsid          = gvsid;
-               __entry->hvsid          = hvsid;
-       ),
-
-       TP_printk("%llx -> %llx", __entry->gvsid, __entry->hvsid)
-);
-
-TRACE_EVENT(kvm_book3s_slb_fail,
-       TP_PROTO(u16 sid_map_mask, unsigned long long gvsid),
-       TP_ARGS(sid_map_mask, gvsid),
-
-       TP_STRUCT__entry(
-               __field(        unsigned short,         sid_map_mask    )
-               __field(        unsigned long long,     gvsid           )
-       ),
-
-       TP_fast_assign(
-               __entry->sid_map_mask   = sid_map_mask;
-               __entry->gvsid          = gvsid;
-       ),
-
-       TP_printk("%x/%x: %llx", __entry->sid_map_mask,
-                 SID_MAP_MASK - __entry->sid_map_mask, __entry->gvsid)
-);
-
-TRACE_EVENT(kvm_book3s_slb_map,
-       TP_PROTO(u16 sid_map_mask, unsigned long long gvsid,
-                unsigned long long hvsid),
-       TP_ARGS(sid_map_mask, gvsid, hvsid),
-
-       TP_STRUCT__entry(
-               __field(        unsigned short,         sid_map_mask    )
-               __field(        unsigned long long,     guest_vsid      )
-               __field(        unsigned long long,     host_vsid       )
-       ),
-
-       TP_fast_assign(
-               __entry->sid_map_mask   = sid_map_mask;
-               __entry->guest_vsid     = gvsid;
-               __entry->host_vsid      = hvsid;
-       ),
-
-       TP_printk("%x: %llx -> %llx", __entry->sid_map_mask,
-                 __entry->guest_vsid, __entry->host_vsid)
-);
-
-TRACE_EVENT(kvm_book3s_slbmte,
-       TP_PROTO(u64 slb_vsid, u64 slb_esid),
-       TP_ARGS(slb_vsid, slb_esid),
-
-       TP_STRUCT__entry(
-               __field(        u64,    slb_vsid        )
-               __field(        u64,    slb_esid        )
-       ),
-
-       TP_fast_assign(
-               __entry->slb_vsid       = slb_vsid;
-               __entry->slb_esid       = slb_esid;
-       ),
-
-       TP_printk("%llx, %llx", __entry->slb_vsid, __entry->slb_esid)
-);
-
-#endif /* CONFIG_PPC_BOOK3S */
-
-
-/*************************************************************************
- *                         Book3E trace points                           *
- *************************************************************************/
-
-#ifdef CONFIG_BOOKE
-
-TRACE_EVENT(kvm_booke206_stlb_write,
-       TP_PROTO(__u32 mas0, __u32 mas8, __u32 mas1, __u64 mas2, __u64 mas7_3),
-       TP_ARGS(mas0, mas8, mas1, mas2, mas7_3),
-
-       TP_STRUCT__entry(
-               __field(        __u32,  mas0            )
-               __field(        __u32,  mas8            )
-               __field(        __u32,  mas1            )
-               __field(        __u64,  mas2            )
-               __field(        __u64,  mas7_3          )
-       ),
-
-       TP_fast_assign(
-               __entry->mas0           = mas0;
-               __entry->mas8           = mas8;
-               __entry->mas1           = mas1;
-               __entry->mas2           = mas2;
-               __entry->mas7_3         = mas7_3;
-       ),
-
-       TP_printk("mas0=%x mas8=%x mas1=%x mas2=%llx mas7_3=%llx",
-               __entry->mas0, __entry->mas8, __entry->mas1,
-               __entry->mas2, __entry->mas7_3)
-);
-
-TRACE_EVENT(kvm_booke206_gtlb_write,
-       TP_PROTO(__u32 mas0, __u32 mas1, __u64 mas2, __u64 mas7_3),
-       TP_ARGS(mas0, mas1, mas2, mas7_3),
-
-       TP_STRUCT__entry(
-               __field(        __u32,  mas0            )
-               __field(        __u32,  mas1            )
-               __field(        __u64,  mas2            )
-               __field(        __u64,  mas7_3          )
-       ),
-
-       TP_fast_assign(
-               __entry->mas0           = mas0;
-               __entry->mas1           = mas1;
-               __entry->mas2           = mas2;
-               __entry->mas7_3         = mas7_3;
-       ),
-
-       TP_printk("mas0=%x mas1=%x mas2=%llx mas7_3=%llx",
-               __entry->mas0, __entry->mas1,
-               __entry->mas2, __entry->mas7_3)
-);
-
-TRACE_EVENT(kvm_booke206_ref_release,
-       TP_PROTO(__u64 pfn, __u32 flags),
-       TP_ARGS(pfn, flags),
-
-       TP_STRUCT__entry(
-               __field(        __u64,  pfn             )
-               __field(        __u32,  flags           )
-       ),
-
-       TP_fast_assign(
-               __entry->pfn            = pfn;
-               __entry->flags          = flags;
-       ),
-
-       TP_printk("pfn=%llx flags=%x",
-               __entry->pfn, __entry->flags)
-);
-
-TRACE_EVENT(kvm_booke_queue_irqprio,
-       TP_PROTO(struct kvm_vcpu *vcpu, unsigned int priority),
-       TP_ARGS(vcpu, priority),
-
-       TP_STRUCT__entry(
-               __field(        __u32,  cpu_nr          )
-               __field(        __u32,  priority                )
-               __field(        unsigned long,  pending         )
-       ),
-
-       TP_fast_assign(
-               __entry->cpu_nr         = vcpu->vcpu_id;
-               __entry->priority       = priority;
-               __entry->pending        = vcpu->arch.pending_exceptions;
-       ),
-
-       TP_printk("vcpu=%x prio=%x pending=%lx",
-               __entry->cpu_nr, __entry->priority, __entry->pending)
-);
-
-#endif
-
 #endif /* _TRACE_KVM_H */
 
 /* This part must be outside protection */
diff --git a/arch/powerpc/kvm/trace_booke.h b/arch/powerpc/kvm/trace_booke.h
new file mode 100644 (file)
index 0000000..f7537cf
--- /dev/null
@@ -0,0 +1,177 @@
+#if !defined(_TRACE_KVM_BOOKE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_KVM_BOOKE_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kvm_booke
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace_booke
+
+#define kvm_trace_symbol_exit \
+       {0, "CRITICAL"}, \
+       {1, "MACHINE_CHECK"}, \
+       {2, "DATA_STORAGE"}, \
+       {3, "INST_STORAGE"}, \
+       {4, "EXTERNAL"}, \
+       {5, "ALIGNMENT"}, \
+       {6, "PROGRAM"}, \
+       {7, "FP_UNAVAIL"}, \
+       {8, "SYSCALL"}, \
+       {9, "AP_UNAVAIL"}, \
+       {10, "DECREMENTER"}, \
+       {11, "FIT"}, \
+       {12, "WATCHDOG"}, \
+       {13, "DTLB_MISS"}, \
+       {14, "ITLB_MISS"}, \
+       {15, "DEBUG"}, \
+       {32, "SPE_UNAVAIL"}, \
+       {33, "SPE_FP_DATA"}, \
+       {34, "SPE_FP_ROUND"}, \
+       {35, "PERFORMANCE_MONITOR"}, \
+       {36, "DOORBELL"}, \
+       {37, "DOORBELL_CRITICAL"}, \
+       {38, "GUEST_DBELL"}, \
+       {39, "GUEST_DBELL_CRIT"}, \
+       {40, "HV_SYSCALL"}, \
+       {41, "HV_PRIV"}
+
+TRACE_EVENT(kvm_exit,
+       TP_PROTO(unsigned int exit_nr, struct kvm_vcpu *vcpu),
+       TP_ARGS(exit_nr, vcpu),
+
+       TP_STRUCT__entry(
+               __field(        unsigned int,   exit_nr         )
+               __field(        unsigned long,  pc              )
+               __field(        unsigned long,  msr             )
+               __field(        unsigned long,  dar             )
+               __field(        unsigned long,  last_inst       )
+       ),
+
+       TP_fast_assign(
+               __entry->exit_nr        = exit_nr;
+               __entry->pc             = kvmppc_get_pc(vcpu);
+               __entry->dar            = kvmppc_get_fault_dar(vcpu);
+               __entry->msr            = vcpu->arch.shared->msr;
+               __entry->last_inst      = vcpu->arch.last_inst;
+       ),
+
+       TP_printk("exit=%s"
+               " | pc=0x%lx"
+               " | msr=0x%lx"
+               " | dar=0x%lx"
+               " | last_inst=0x%lx"
+               ,
+               __print_symbolic(__entry->exit_nr, kvm_trace_symbol_exit),
+               __entry->pc,
+               __entry->msr,
+               __entry->dar,
+               __entry->last_inst
+               )
+);
+
+TRACE_EVENT(kvm_unmap_hva,
+       TP_PROTO(unsigned long hva),
+       TP_ARGS(hva),
+
+       TP_STRUCT__entry(
+               __field(        unsigned long,  hva             )
+       ),
+
+       TP_fast_assign(
+               __entry->hva            = hva;
+       ),
+
+       TP_printk("unmap hva 0x%lx\n", __entry->hva)
+);
+
+TRACE_EVENT(kvm_booke206_stlb_write,
+       TP_PROTO(__u32 mas0, __u32 mas8, __u32 mas1, __u64 mas2, __u64 mas7_3),
+       TP_ARGS(mas0, mas8, mas1, mas2, mas7_3),
+
+       TP_STRUCT__entry(
+               __field(        __u32,  mas0            )
+               __field(        __u32,  mas8            )
+               __field(        __u32,  mas1            )
+               __field(        __u64,  mas2            )
+               __field(        __u64,  mas7_3          )
+       ),
+
+       TP_fast_assign(
+               __entry->mas0           = mas0;
+               __entry->mas8           = mas8;
+               __entry->mas1           = mas1;
+               __entry->mas2           = mas2;
+               __entry->mas7_3         = mas7_3;
+       ),
+
+       TP_printk("mas0=%x mas8=%x mas1=%x mas2=%llx mas7_3=%llx",
+               __entry->mas0, __entry->mas8, __entry->mas1,
+               __entry->mas2, __entry->mas7_3)
+);
+
+TRACE_EVENT(kvm_booke206_gtlb_write,
+       TP_PROTO(__u32 mas0, __u32 mas1, __u64 mas2, __u64 mas7_3),
+       TP_ARGS(mas0, mas1, mas2, mas7_3),
+
+       TP_STRUCT__entry(
+               __field(        __u32,  mas0            )
+               __field(        __u32,  mas1            )
+               __field(        __u64,  mas2            )
+               __field(        __u64,  mas7_3          )
+       ),
+
+       TP_fast_assign(
+               __entry->mas0           = mas0;
+               __entry->mas1           = mas1;
+               __entry->mas2           = mas2;
+               __entry->mas7_3         = mas7_3;
+       ),
+
+       TP_printk("mas0=%x mas1=%x mas2=%llx mas7_3=%llx",
+               __entry->mas0, __entry->mas1,
+               __entry->mas2, __entry->mas7_3)
+);
+
+TRACE_EVENT(kvm_booke206_ref_release,
+       TP_PROTO(__u64 pfn, __u32 flags),
+       TP_ARGS(pfn, flags),
+
+       TP_STRUCT__entry(
+               __field(        __u64,  pfn             )
+               __field(        __u32,  flags           )
+       ),
+
+       TP_fast_assign(
+               __entry->pfn            = pfn;
+               __entry->flags          = flags;
+       ),
+
+       TP_printk("pfn=%llx flags=%x",
+               __entry->pfn, __entry->flags)
+);
+
+TRACE_EVENT(kvm_booke_queue_irqprio,
+       TP_PROTO(struct kvm_vcpu *vcpu, unsigned int priority),
+       TP_ARGS(vcpu, priority),
+
+       TP_STRUCT__entry(
+               __field(        __u32,  cpu_nr          )
+               __field(        __u32,  priority                )
+               __field(        unsigned long,  pending         )
+       ),
+
+       TP_fast_assign(
+               __entry->cpu_nr         = vcpu->vcpu_id;
+               __entry->priority       = priority;
+               __entry->pending        = vcpu->arch.pending_exceptions;
+       ),
+
+       TP_printk("vcpu=%x prio=%x pending=%lx",
+               __entry->cpu_nr, __entry->priority, __entry->pending)
+);
+
+#endif
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/arch/powerpc/kvm/trace_pr.h b/arch/powerpc/kvm/trace_pr.h
new file mode 100644 (file)
index 0000000..8b22e47
--- /dev/null
@@ -0,0 +1,297 @@
+
+#if !defined(_TRACE_KVM_PR_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_KVM_PR_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kvm_pr
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace_pr
+
+#define kvm_trace_symbol_exit \
+       {0x100, "SYSTEM_RESET"}, \
+       {0x200, "MACHINE_CHECK"}, \
+       {0x300, "DATA_STORAGE"}, \
+       {0x380, "DATA_SEGMENT"}, \
+       {0x400, "INST_STORAGE"}, \
+       {0x480, "INST_SEGMENT"}, \
+       {0x500, "EXTERNAL"}, \
+       {0x501, "EXTERNAL_LEVEL"}, \
+       {0x502, "EXTERNAL_HV"}, \
+       {0x600, "ALIGNMENT"}, \
+       {0x700, "PROGRAM"}, \
+       {0x800, "FP_UNAVAIL"}, \
+       {0x900, "DECREMENTER"}, \
+       {0x980, "HV_DECREMENTER"}, \
+       {0xc00, "SYSCALL"}, \
+       {0xd00, "TRACE"}, \
+       {0xe00, "H_DATA_STORAGE"}, \
+       {0xe20, "H_INST_STORAGE"}, \
+       {0xe40, "H_EMUL_ASSIST"}, \
+       {0xf00, "PERFMON"}, \
+       {0xf20, "ALTIVEC"}, \
+       {0xf40, "VSX"}
+
+TRACE_EVENT(kvm_book3s_reenter,
+       TP_PROTO(int r, struct kvm_vcpu *vcpu),
+       TP_ARGS(r, vcpu),
+
+       TP_STRUCT__entry(
+               __field(        unsigned int,   r               )
+               __field(        unsigned long,  pc              )
+       ),
+
+       TP_fast_assign(
+               __entry->r              = r;
+               __entry->pc             = kvmppc_get_pc(vcpu);
+       ),
+
+       TP_printk("reentry r=%d | pc=0x%lx", __entry->r, __entry->pc)
+);
+
+#ifdef CONFIG_PPC_BOOK3S_64
+
+TRACE_EVENT(kvm_book3s_64_mmu_map,
+       TP_PROTO(int rflags, ulong hpteg, ulong va, pfn_t hpaddr,
+                struct kvmppc_pte *orig_pte),
+       TP_ARGS(rflags, hpteg, va, hpaddr, orig_pte),
+
+       TP_STRUCT__entry(
+               __field(        unsigned char,          flag_w          )
+               __field(        unsigned char,          flag_x          )
+               __field(        unsigned long,          eaddr           )
+               __field(        unsigned long,          hpteg           )
+               __field(        unsigned long,          va              )
+               __field(        unsigned long long,     vpage           )
+               __field(        unsigned long,          hpaddr          )
+       ),
+
+       TP_fast_assign(
+               __entry->flag_w = ((rflags & HPTE_R_PP) == 3) ? '-' : 'w';
+               __entry->flag_x = (rflags & HPTE_R_N) ? '-' : 'x';
+               __entry->eaddr  = orig_pte->eaddr;
+               __entry->hpteg  = hpteg;
+               __entry->va     = va;
+               __entry->vpage  = orig_pte->vpage;
+               __entry->hpaddr = hpaddr;
+       ),
+
+       TP_printk("KVM: %c%c Map 0x%lx: [%lx] 0x%lx (0x%llx) -> %lx",
+                 __entry->flag_w, __entry->flag_x, __entry->eaddr,
+                 __entry->hpteg, __entry->va, __entry->vpage, __entry->hpaddr)
+);
+
+#endif /* CONFIG_PPC_BOOK3S_64 */
+
+TRACE_EVENT(kvm_book3s_mmu_map,
+       TP_PROTO(struct hpte_cache *pte),
+       TP_ARGS(pte),
+
+       TP_STRUCT__entry(
+               __field(        u64,            host_vpn        )
+               __field(        u64,            pfn             )
+               __field(        ulong,          eaddr           )
+               __field(        u64,            vpage           )
+               __field(        ulong,          raddr           )
+               __field(        int,            flags           )
+       ),
+
+       TP_fast_assign(
+               __entry->host_vpn       = pte->host_vpn;
+               __entry->pfn            = pte->pfn;
+               __entry->eaddr          = pte->pte.eaddr;
+               __entry->vpage          = pte->pte.vpage;
+               __entry->raddr          = pte->pte.raddr;
+               __entry->flags          = (pte->pte.may_read ? 0x4 : 0) |
+                                         (pte->pte.may_write ? 0x2 : 0) |
+                                         (pte->pte.may_execute ? 0x1 : 0);
+       ),
+
+       TP_printk("Map: hvpn=%llx pfn=%llx ea=%lx vp=%llx ra=%lx [%x]",
+                 __entry->host_vpn, __entry->pfn, __entry->eaddr,
+                 __entry->vpage, __entry->raddr, __entry->flags)
+);
+
+TRACE_EVENT(kvm_book3s_mmu_invalidate,
+       TP_PROTO(struct hpte_cache *pte),
+       TP_ARGS(pte),
+
+       TP_STRUCT__entry(
+               __field(        u64,            host_vpn        )
+               __field(        u64,            pfn             )
+               __field(        ulong,          eaddr           )
+               __field(        u64,            vpage           )
+               __field(        ulong,          raddr           )
+               __field(        int,            flags           )
+       ),
+
+       TP_fast_assign(
+               __entry->host_vpn       = pte->host_vpn;
+               __entry->pfn            = pte->pfn;
+               __entry->eaddr          = pte->pte.eaddr;
+               __entry->vpage          = pte->pte.vpage;
+               __entry->raddr          = pte->pte.raddr;
+               __entry->flags          = (pte->pte.may_read ? 0x4 : 0) |
+                                         (pte->pte.may_write ? 0x2 : 0) |
+                                         (pte->pte.may_execute ? 0x1 : 0);
+       ),
+
+       TP_printk("Flush: hva=%llx pfn=%llx ea=%lx vp=%llx ra=%lx [%x]",
+                 __entry->host_vpn, __entry->pfn, __entry->eaddr,
+                 __entry->vpage, __entry->raddr, __entry->flags)
+);
+
+TRACE_EVENT(kvm_book3s_mmu_flush,
+       TP_PROTO(const char *type, struct kvm_vcpu *vcpu, unsigned long long p1,
+                unsigned long long p2),
+       TP_ARGS(type, vcpu, p1, p2),
+
+       TP_STRUCT__entry(
+               __field(        int,                    count           )
+               __field(        unsigned long long,     p1              )
+               __field(        unsigned long long,     p2              )
+               __field(        const char *,           type            )
+       ),
+
+       TP_fast_assign(
+               __entry->count          = to_book3s(vcpu)->hpte_cache_count;
+               __entry->p1             = p1;
+               __entry->p2             = p2;
+               __entry->type           = type;
+       ),
+
+       TP_printk("Flush %d %sPTEs: %llx - %llx",
+                 __entry->count, __entry->type, __entry->p1, __entry->p2)
+);
+
+TRACE_EVENT(kvm_book3s_slb_found,
+       TP_PROTO(unsigned long long gvsid, unsigned long long hvsid),
+       TP_ARGS(gvsid, hvsid),
+
+       TP_STRUCT__entry(
+               __field(        unsigned long long,     gvsid           )
+               __field(        unsigned long long,     hvsid           )
+       ),
+
+       TP_fast_assign(
+               __entry->gvsid          = gvsid;
+               __entry->hvsid          = hvsid;
+       ),
+
+       TP_printk("%llx -> %llx", __entry->gvsid, __entry->hvsid)
+);
+
+TRACE_EVENT(kvm_book3s_slb_fail,
+       TP_PROTO(u16 sid_map_mask, unsigned long long gvsid),
+       TP_ARGS(sid_map_mask, gvsid),
+
+       TP_STRUCT__entry(
+               __field(        unsigned short,         sid_map_mask    )
+               __field(        unsigned long long,     gvsid           )
+       ),
+
+       TP_fast_assign(
+               __entry->sid_map_mask   = sid_map_mask;
+               __entry->gvsid          = gvsid;
+       ),
+
+       TP_printk("%x/%x: %llx", __entry->sid_map_mask,
+                 SID_MAP_MASK - __entry->sid_map_mask, __entry->gvsid)
+);
+
+TRACE_EVENT(kvm_book3s_slb_map,
+       TP_PROTO(u16 sid_map_mask, unsigned long long gvsid,
+                unsigned long long hvsid),
+       TP_ARGS(sid_map_mask, gvsid, hvsid),
+
+       TP_STRUCT__entry(
+               __field(        unsigned short,         sid_map_mask    )
+               __field(        unsigned long long,     guest_vsid      )
+               __field(        unsigned long long,     host_vsid       )
+       ),
+
+       TP_fast_assign(
+               __entry->sid_map_mask   = sid_map_mask;
+               __entry->guest_vsid     = gvsid;
+               __entry->host_vsid      = hvsid;
+       ),
+
+       TP_printk("%x: %llx -> %llx", __entry->sid_map_mask,
+                 __entry->guest_vsid, __entry->host_vsid)
+);
+
+TRACE_EVENT(kvm_book3s_slbmte,
+       TP_PROTO(u64 slb_vsid, u64 slb_esid),
+       TP_ARGS(slb_vsid, slb_esid),
+
+       TP_STRUCT__entry(
+               __field(        u64,    slb_vsid        )
+               __field(        u64,    slb_esid        )
+       ),
+
+       TP_fast_assign(
+               __entry->slb_vsid       = slb_vsid;
+               __entry->slb_esid       = slb_esid;
+       ),
+
+       TP_printk("%llx, %llx", __entry->slb_vsid, __entry->slb_esid)
+);
+
+TRACE_EVENT(kvm_exit,
+       TP_PROTO(unsigned int exit_nr, struct kvm_vcpu *vcpu),
+       TP_ARGS(exit_nr, vcpu),
+
+       TP_STRUCT__entry(
+               __field(        unsigned int,   exit_nr         )
+               __field(        unsigned long,  pc              )
+               __field(        unsigned long,  msr             )
+               __field(        unsigned long,  dar             )
+               __field(        unsigned long,  srr1            )
+               __field(        unsigned long,  last_inst       )
+       ),
+
+       TP_fast_assign(
+               __entry->exit_nr        = exit_nr;
+               __entry->pc             = kvmppc_get_pc(vcpu);
+               __entry->dar            = kvmppc_get_fault_dar(vcpu);
+               __entry->msr            = vcpu->arch.shared->msr;
+               __entry->srr1           = vcpu->arch.shadow_srr1;
+               __entry->last_inst      = vcpu->arch.last_inst;
+       ),
+
+       TP_printk("exit=%s"
+               " | pc=0x%lx"
+               " | msr=0x%lx"
+               " | dar=0x%lx"
+               " | srr1=0x%lx"
+               " | last_inst=0x%lx"
+               ,
+               __print_symbolic(__entry->exit_nr, kvm_trace_symbol_exit),
+               __entry->pc,
+               __entry->msr,
+               __entry->dar,
+               __entry->srr1,
+               __entry->last_inst
+               )
+);
+
+TRACE_EVENT(kvm_unmap_hva,
+       TP_PROTO(unsigned long hva),
+       TP_ARGS(hva),
+
+       TP_STRUCT__entry(
+               __field(        unsigned long,  hva             )
+       ),
+
+       TP_fast_assign(
+               __entry->hva            = hva;
+       ),
+
+       TP_printk("unmap hva 0x%lx\n", __entry->hva)
+);
+
+#endif /* _TRACE_KVM_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 6936547..c5f734e 100644 (file)
@@ -123,6 +123,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
        struct mm_struct *mm = current->mm;
        unsigned long addr, len, end;
        unsigned long next;
+       unsigned long flags;
        pgd_t *pgdp;
        int nr = 0;
 
@@ -156,7 +157,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
         * So long as we atomically load page table pointers versus teardown,
         * we can follow the address down to the the page and take a ref on it.
         */
-       local_irq_disable();
+       local_irq_save(flags);
 
        pgdp = pgd_offset(mm, addr);
        do {
@@ -179,7 +180,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
                        break;
        } while (pgdp++, addr = next, addr != end);
 
-       local_irq_enable();
+       local_irq_restore(flags);
 
        return nr;
 }
index 6c856fb..5b96017 100644 (file)
@@ -121,7 +121,10 @@ pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address)
        ptepage = alloc_pages(flags, 0);
        if (!ptepage)
                return NULL;
-       pgtable_page_ctor(ptepage);
+       if (!pgtable_page_ctor(ptepage)) {
+               __free_page(ptepage);
+               return NULL;
+       }
        return ptepage;
 }
 
index 536eec7..9d95786 100644 (file)
@@ -378,6 +378,10 @@ static pte_t *__alloc_for_cache(struct mm_struct *mm, int kernel)
                                       __GFP_REPEAT | __GFP_ZERO);
        if (!page)
                return NULL;
+       if (!kernel && !pgtable_page_ctor(page)) {
+               __free_page(page);
+               return NULL;
+       }
 
        ret = page_address(page);
        spin_lock(&mm->page_table_lock);
@@ -392,9 +396,6 @@ static pte_t *__alloc_for_cache(struct mm_struct *mm, int kernel)
        }
        spin_unlock(&mm->page_table_lock);
 
-       if (!kernel)
-               pgtable_page_ctor(page);
-
        return (pte_t *)ret;
 }
 
index 3e99c14..7ce9cf3 100644 (file)
@@ -258,7 +258,7 @@ static bool slice_scan_available(unsigned long addr,
                slice = GET_HIGH_SLICE_INDEX(addr);
                *boundary_addr = (slice + end) ?
                        ((slice + end) << SLICE_HIGH_SHIFT) : SLICE_LOW_TOP;
-               return !!(available.high_slices & (1u << slice));
+               return !!(available.high_slices & (1ul << slice));
        }
 }
 
index c2a566f..132f872 100644 (file)
@@ -403,3 +403,14 @@ config PPC_DOORBELL
        default n
 
 endmenu
+
+config CPU_LITTLE_ENDIAN
+       bool "Build little endian kernel"
+       default n
+       help
+         This option selects whether a big endian or little endian kernel will
+         be built.
+
+         Note that if cross compiling a little endian kernel,
+         CROSS_COMPILE must point to a toolchain capable of targeting
+         little endian powerpc.
index fc536f2..7553b6a 100644 (file)
@@ -452,7 +452,7 @@ static int kw_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize,
         */
        if (use_irq) {
                /* Clear completion */
-               INIT_COMPLETION(host->complete);
+               reinit_completion(&host->complete);
                /* Ack stale interrupts */
                kw_write_reg(reg_isr, kw_read_reg(reg_isr));
                /* Arm timeout */
@@ -717,7 +717,7 @@ static int pmu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize,
                        return -EINVAL;
                }
 
-               INIT_COMPLETION(comp);
+               reinit_completion(&comp);
                req->data[0] = PMU_I2C_CMD;
                req->reply[0] = 0xff;
                req->nbytes = sizeof(struct pmu_i2c_hdr) + 1;
@@ -748,7 +748,7 @@ static int pmu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize,
 
                hdr->bus = PMU_I2C_BUS_STATUS;
 
-               INIT_COMPLETION(comp);
+               reinit_completion(&comp);
                req->data[0] = PMU_I2C_CMD;
                req->reply[0] = 0xff;
                req->nbytes = 2;
index 8844628..1cb160d 100644 (file)
@@ -19,6 +19,7 @@
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/machdep.h>
+#include <asm/smp.h>
 
 
 struct powernv_rng {
index 7fbc25b..ccb633e 100644 (file)
@@ -189,8 +189,9 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
        struct eeh_dev *edev;
        struct eeh_pe pe;
        struct pci_dn *pdn = PCI_DN(dn);
-       const u32 *class_code, *vendor_id, *device_id;
-       const u32 *regs;
+       const __be32 *classp, *vendorp, *devicep;
+       u32 class_code;
+       const __be32 *regs;
        u32 pcie_flags;
        int enable = 0;
        int ret;
@@ -201,22 +202,24 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
                return NULL;
 
        /* Retrieve class/vendor/device IDs */
-       class_code = of_get_property(dn, "class-code", NULL);
-       vendor_id  = of_get_property(dn, "vendor-id", NULL);
-       device_id  = of_get_property(dn, "device-id", NULL);
+       classp = of_get_property(dn, "class-code", NULL);
+       vendorp = of_get_property(dn, "vendor-id", NULL);
+       devicep = of_get_property(dn, "device-id", NULL);
 
        /* Skip for bad OF node or PCI-ISA bridge */
-       if (!class_code || !vendor_id || !device_id)
+       if (!classp || !vendorp || !devicep)
                return NULL;
        if (dn->type && !strcmp(dn->type, "isa"))
                return NULL;
 
+       class_code = of_read_number(classp, 1);
+
        /*
         * Update class code and mode of eeh device. We need
         * correctly reflects that current device is root port
         * or PCIe switch downstream port.
         */
-       edev->class_code = *class_code;
+       edev->class_code = class_code;
        edev->pcie_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_EXP);
        edev->mode &= 0xFFFFFF00;
        if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {
@@ -243,12 +246,12 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
        /* Initialize the fake PE */
        memset(&pe, 0, sizeof(struct eeh_pe));
        pe.phb = edev->phb;
-       pe.config_addr = regs[0];
+       pe.config_addr = of_read_number(regs, 1);
 
        /* Enable EEH on the device */
        ret = eeh_ops->set_option(&pe, EEH_OPT_ENABLE);
        if (!ret) {
-               edev->config_addr = regs[0];
+               edev->config_addr = of_read_number(regs, 1);
                /* Retrieve PE address */
                edev->pe_config_addr = eeh_ops->get_pe_addr(&pe);
                pe.addr = edev->pe_config_addr;
index 356bc75..4fca3de 100644 (file)
@@ -245,6 +245,23 @@ static void pSeries_lpar_hptab_clear(void)
                                        &(ptes[j].pteh), &(ptes[j].ptel));
                }
        }
+
+#ifdef __LITTLE_ENDIAN__
+       /* Reset exceptions to big endian */
+       if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
+               long rc;
+
+               rc = pseries_big_endian_exceptions();
+               /*
+                * At this point it is unlikely panic() will get anything
+                * out to the user, but at least this will stop us from
+                * continuing on further and creating an even more
+                * difficult to debug situation.
+                */
+               if (rc)
+                       panic("Could not enable big endian exceptions");
+       }
+#endif
 }
 
 /*
index 057fc89..7bfaf58 100644 (file)
@@ -31,7 +31,7 @@
 #define NVRW_CNT 0x20
 
 /*
- * Set oops header version to distingush between old and new format header.
+ * Set oops header version to distinguish between old and new format header.
  * lnx,oops-log partition max size is 4000, header version > 4000 will
  * help in identifying new header.
  */
index a702f1c..72a1027 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/of.h>
 #include <asm/archrandom.h>
 #include <asm/machdep.h>
+#include <asm/plpar_wrappers.h>
 
 
 static int pseries_get_random_long(unsigned long *v)
index 1f97e2b..c1f1908 100644 (file)
@@ -442,6 +442,32 @@ static void pSeries_machine_kexec(struct kimage *image)
 }
 #endif
 
+#ifdef __LITTLE_ENDIAN__
+long pseries_big_endian_exceptions(void)
+{
+       long rc;
+
+       while (1) {
+               rc = enable_big_endian_exceptions();
+               if (!H_IS_LONG_BUSY(rc))
+                       return rc;
+               mdelay(get_longbusy_msecs(rc));
+       }
+}
+
+static long pseries_little_endian_exceptions(void)
+{
+       long rc;
+
+       while (1) {
+               rc = enable_little_endian_exceptions();
+               if (!H_IS_LONG_BUSY(rc))
+                       return rc;
+               mdelay(get_longbusy_msecs(rc));
+       }
+}
+#endif
+
 static void __init pSeries_setup_arch(void)
 {
        panic_timeout = 10;
@@ -698,6 +724,22 @@ static int __init pSeries_probe(void)
        /* Now try to figure out if we are running on LPAR */
        of_scan_flat_dt(pseries_probe_fw_features, NULL);
 
+#ifdef __LITTLE_ENDIAN__
+       if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
+               long rc;
+               /*
+                * Tell the hypervisor that we want our exceptions to
+                * be taken in little endian mode. If this fails we don't
+                * want to use BUG() because it will trigger an exception.
+                */
+               rc = pseries_little_endian_exceptions();
+               if (rc) {
+                       ppc_md.progress("H_SET_MODE LE exception fail", 0);
+                       panic("Could not enable little endian exceptions");
+               }
+       }
+#endif
+
        if (firmware_has_feature(FW_FEATURE_LPAR))
                hpte_init_lpar();
        else
index 5f997e7..16a2552 100644 (file)
@@ -106,7 +106,7 @@ static int pseries_prepare_late(void)
        atomic_set(&suspend_data.done, 0);
        atomic_set(&suspend_data.error, 0);
        suspend_data.complete = &suspend_work;
-       INIT_COMPLETION(suspend_work);
+       reinit_completion(&suspend_work);
        return 0;
 }
 
index 8ef53bc..aaa46b3 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/of.h>
 #include <linux/smp.h>
 #include <linux/time.h>
+#include <linux/of_fdt.h>
 
 #include <asm/machdep.h>
 #include <asm/udbg.h>
index d18e6cc..a3c87f3 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/kernel.h>
 #include <linux/of.h>
 #include <linux/io.h>
+#include <linux/of_address.h>
 
 #include "wsp.h"
 
index 2d3b1dd..9cd92e6 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/smp.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index cb565bf..3f67298 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/of.h>
 #include <linux/slab.h>
 #include <linux/time.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
 
 #include <asm/reg_a2.h>
 #include <asm/irq.h>
index 508ec82..a87b414 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/of.h>
 #include <linux/smp.h>
 #include <linux/time.h>
+#include <linux/of_fdt.h>
 
 #include <asm/machdep.h>
 #include <asm/udbg.h>
index 8928507..6538b4d 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/of.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
+#include <linux/of_address.h>
 
 #include <asm/cputhreads.h>
 #include <asm/reg_a2.h>
index ddb6efe..58cd1f0 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/smp.h>
 #include <linux/delay.h>
 #include <linux/time.h>
+#include <linux/of_address.h>
 
 #include <asm/scom.h>
 
index f75d7e5..314fced 100644 (file)
@@ -141,7 +141,6 @@ config S390
        select OLD_SIGACTION
        select OLD_SIGSUSPEND3
        select SYSCTL_EXCEPTION_TRACE
-       select USE_GENERIC_SMP_HELPERS if SMP
        select VIRT_CPU_ACCOUNTING
        select VIRT_TO_BUS
 
index f2737a0..9a42ece 100644 (file)
@@ -21,6 +21,6 @@ $(obj)/bzImage: $(obj)/compressed/vmlinux FORCE
 $(obj)/compressed/vmlinux: FORCE
        $(Q)$(MAKE) $(build)=$(obj)/compressed $@
 
-install: $(CONFIGURE) $(obj)/image
-       sh -x  $(srctree)/$(obj)/install.sh $(KERNELRELEASE) $(obj)/image \
+install: $(CONFIGURE) $(obj)/bzImage
+       sh -x  $(srctree)/$(obj)/install.sh $(KERNELRELEASE) $(obj)/bzImage \
              System.map "$(INSTALL_PATH)"
index 9b69c0b..4e63f1a 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef __ASM_CTL_REG_H
 #define __ASM_CTL_REG_H
 
+#include <linux/bug.h>
+
 #ifdef CONFIG_64BIT
 # define __CTL_LOAD    "lctlg"
 # define __CTL_STORE   "stctg"
index dc9200c..6702630 100644 (file)
@@ -111,18 +111,7 @@ struct scm_driver {
 int scm_driver_register(struct scm_driver *scmdrv);
 void scm_driver_unregister(struct scm_driver *scmdrv);
 
-int scm_start_aob(struct aob *aob);
+int eadm_start_aob(struct aob *aob);
 void scm_irq_handler(struct aob *aob, int error);
 
-struct eadm_ops {
-       int (*eadm_start) (struct aob *aob);
-       struct module *owner;
-};
-
-int scm_get_ref(void);
-void scm_put_ref(void);
-
-void register_eadm_ops(struct eadm_ops *ops);
-void unregister_eadm_ops(struct eadm_ops *ops);
-
 #endif /* _ASM_S390_EADM_H */
index a908d29..b7eabaa 100644 (file)
@@ -18,8 +18,6 @@
 #define __ARCH_HAS_DO_SOFTIRQ
 #define __ARCH_IRQ_EXIT_IRQS_DISABLED
 
-#define HARDIRQ_BITS   8
-
 static inline void ack_bad_irq(unsigned int irq)
 {
        printk(KERN_CRIT "unexpected IRQ trap at vector %02x\n", irq);
index e87ecaa..d5bc375 100644 (file)
@@ -38,13 +38,6 @@ struct sca_block {
        struct sca_entry cpu[64];
 } __attribute__((packed));
 
-#define KVM_NR_PAGE_SIZES 2
-#define KVM_HPAGE_GFN_SHIFT(x) (((x) - 1) * 8)
-#define KVM_HPAGE_SHIFT(x) (PAGE_SHIFT + KVM_HPAGE_GFN_SHIFT(x))
-#define KVM_HPAGE_SIZE(x) (1UL << KVM_HPAGE_SHIFT(x))
-#define KVM_HPAGE_MASK(x)      (~(KVM_HPAGE_SIZE(x) - 1))
-#define KVM_PAGES_PER_HPAGE(x) (KVM_HPAGE_SIZE(x) / PAGE_SIZE)
-
 #define CPUSTAT_STOPPED    0x80000000
 #define CPUSTAT_WAIT       0x10000000
 #define CPUSTAT_ECALL_PEND 0x08000000
@@ -220,7 +213,6 @@ struct kvm_s390_interrupt_info {
 /* for local_interrupt.action_flags */
 #define ACTION_STORE_ON_STOP           (1<<0)
 #define ACTION_STOP_ON_STOP            (1<<1)
-#define ACTION_RELOADVCPU_ON_STOP      (1<<2)
 
 struct kvm_s390_local_interrupt {
        spinlock_t lock;
index 1cc185d..c129ab2 100644 (file)
@@ -63,9 +63,10 @@ enum zpci_state {
 };
 
 struct zpci_bar_struct {
+       struct resource *res;           /* bus resource */
        u32             val;            /* bar start & 3 flag bits */
-       u8              size;           /* order 2 exponent */
        u16             map_idx;        /* index into bar mapping array */
+       u8              size;           /* order 2 exponent */
 };
 
 /* Private data per function */
@@ -97,6 +98,7 @@ struct zpci_dev {
        unsigned long   iommu_pages;
        unsigned int    next_bit;
 
+       char res_name[16];
        struct zpci_bar_struct bars[PCI_BAR_COUNT];
 
        u64             start_dma;      /* Start of available DMA addresses */
@@ -122,12 +124,10 @@ static inline bool zdev_enabled(struct zpci_dev *zdev)
   Prototypes
 ----------------------------------------------------------------------------- */
 /* Base stuff */
-struct zpci_dev *zpci_alloc_device(void);
 int zpci_create_device(struct zpci_dev *);
 int zpci_enable_device(struct zpci_dev *);
 int zpci_disable_device(struct zpci_dev *);
 void zpci_stop_device(struct zpci_dev *);
-void zpci_free_device(struct zpci_dev *);
 int zpci_register_ioat(struct zpci_dev *, u8, u64, u64, u64);
 int zpci_unregister_ioat(struct zpci_dev *, u8);
 
index 7dc7f9c..30ef748 100644 (file)
@@ -43,7 +43,6 @@ struct sclp_cpu_info {
 int sclp_get_cpu_info(struct sclp_cpu_info *info);
 int sclp_cpu_configure(u8 cpu);
 int sclp_cpu_deconfigure(u8 cpu);
-void sclp_facilities_detect(void);
 unsigned long long sclp_get_rnmax(void);
 unsigned long long sclp_get_rzm(void);
 int sclp_sdias_blk_count(void);
@@ -57,5 +56,7 @@ bool sclp_has_vt220(void);
 int sclp_pci_configure(u32 fid);
 int sclp_pci_deconfigure(u32 fid);
 int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode);
+unsigned long sclp_get_hsa_size(void);
+void sclp_early_detect(void);
 
 #endif /* _ASM_S390_SCLP_H */
index df802ee..94cfbe4 100644 (file)
@@ -107,9 +107,6 @@ void create_mem_hole(struct mem_chunk mem_chunk[], unsigned long addr,
 #define MACHINE_HAS_RRBM       (S390_lowcore.machine_flags & MACHINE_FLAG_RRBM)
 #endif /* CONFIG_64BIT */
 
-#define ZFCPDUMP_HSA_SIZE      (32UL<<20)
-#define ZFCPDUMP_HSA_SIZE_MAX  (64UL<<20)
-
 /*
  * Console mode. Override with conmode=
  */
index eb5f64d..10e0fcd 100644 (file)
@@ -111,6 +111,4 @@ static inline struct thread_info *current_thread_info(void)
 #define is_32bit_task()                (1)
 #endif
 
-#define PREEMPT_ACTIVE         0x4000000
-
 #endif /* _ASM_THREAD_INFO_H */
index f45b2ab..d7658c4 100644 (file)
@@ -95,7 +95,7 @@ static void *elfcorehdr_newmem;
 /*
  * Copy one page from zfcpdump "oldmem"
  *
- * For pages below ZFCPDUMP_HSA_SIZE memory from the HSA is copied. Otherwise
+ * For pages below HSA size memory from the HSA is copied. Otherwise
  * real memory copy is used.
  */
 static ssize_t copy_oldmem_page_zfcpdump(char *buf, size_t csize,
@@ -103,7 +103,7 @@ static ssize_t copy_oldmem_page_zfcpdump(char *buf, size_t csize,
 {
        int rc;
 
-       if (src < ZFCPDUMP_HSA_SIZE) {
+       if (src < sclp_get_hsa_size()) {
                rc = memcpy_hsa(buf, src, csize, userbuf);
        } else {
                if (userbuf)
@@ -188,18 +188,19 @@ static int remap_oldmem_pfn_range_kdump(struct vm_area_struct *vma,
 /*
  * Remap "oldmem" for zfcpdump
  *
- * We only map available memory above ZFCPDUMP_HSA_SIZE. Memory below
- * ZFCPDUMP_HSA_SIZE is read on demand using the copy_oldmem_page() function.
+ * We only map available memory above HSA size. Memory below HSA size
+ * is read on demand using the copy_oldmem_page() function.
  */
 static int remap_oldmem_pfn_range_zfcpdump(struct vm_area_struct *vma,
                                           unsigned long from,
                                           unsigned long pfn,
                                           unsigned long size, pgprot_t prot)
 {
+       unsigned long hsa_end = sclp_get_hsa_size();
        unsigned long size_hsa;
 
-       if (pfn < ZFCPDUMP_HSA_SIZE >> PAGE_SHIFT) {
-               size_hsa = min(size, ZFCPDUMP_HSA_SIZE - (pfn << PAGE_SHIFT));
+       if (pfn < hsa_end >> PAGE_SHIFT) {
+               size_hsa = min(size, hsa_end - (pfn << PAGE_SHIFT));
                if (size == size_hsa)
                        return 0;
                size -= size_hsa;
@@ -238,9 +239,9 @@ int copy_from_oldmem(void *dest, void *src, size_t count)
                                return rc;
                }
        } else {
-               if ((unsigned long) src < ZFCPDUMP_HSA_SIZE) {
-                       copied = min(count,
-                                    ZFCPDUMP_HSA_SIZE - (unsigned long) src);
+               unsigned long hsa_end = sclp_get_hsa_size();
+               if ((unsigned long) src < hsa_end) {
+                       copied = min(count, hsa_end - (unsigned long) src);
                        rc = memcpy_hsa(dest, (unsigned long) src, copied, 0);
                        if (rc)
                                return rc;
@@ -580,6 +581,9 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
        /* If elfcorehdr= has been passed via cmdline, we use that one */
        if (elfcorehdr_addr != ELFCORE_ADDR_MAX)
                return 0;
+       /* If we cannot get HSA size for zfcpdump return error */
+       if (ipl_info.type == IPL_TYPE_FCP_DUMP && !sclp_get_hsa_size())
+               return -ENODEV;
        mem_chunk_cnt = get_mem_chunk_cnt();
 
        alloc_size = 0x1000 + get_cpu_cnt() * 0x300 +
index 96543ac..fca20b5 100644 (file)
@@ -483,7 +483,7 @@ void __init startup_init(void)
        detect_diag44();
        detect_machine_facilities();
        setup_topology();
-       sclp_facilities_detect();
+       sclp_early_detect();
 #ifdef CONFIG_DYNAMIC_FTRACE
        S390_lowcore.ftrace_func = (unsigned long)ftrace_caller;
 #endif
index 59a9c35..bc71a7b 100644 (file)
@@ -680,7 +680,7 @@ static int __kprobes kprobe_trap_handler(struct pt_regs *regs, int trapnr)
        case KPROBE_HIT_SSDONE:
                /*
                 * We increment the nmissed count for accounting,
-                * we can also use npre/npostfault count for accouting
+                * we can also use npre/npostfault count for accounting
                 * these specific fault cases.
                 */
                kprobes_inc_nmissed_count(p);
index ffe1c53..4444875 100644 (file)
@@ -471,8 +471,9 @@ static void __init setup_memory_end(void)
 
 
 #ifdef CONFIG_ZFCPDUMP
-       if (ipl_info.type == IPL_TYPE_FCP_DUMP && !OLDMEM_BASE) {
-               memory_end = ZFCPDUMP_HSA_SIZE;
+       if (ipl_info.type == IPL_TYPE_FCP_DUMP &&
+           !OLDMEM_BASE && sclp_get_hsa_size()) {
+               memory_end = sclp_get_hsa_size();
                memory_end_set = 1;
        }
 #endif
@@ -586,7 +587,7 @@ static unsigned long __init find_crash_base(unsigned long crash_size,
                crash_base = (chunk->addr + chunk->size) - crash_size;
                if (crash_base < crash_size)
                        continue;
-               if (crash_base < ZFCPDUMP_HSA_SIZE_MAX)
+               if (crash_base < sclp_get_hsa_size())
                        continue;
                if (crash_base < (unsigned long) INITRD_START + INITRD_SIZE)
                        continue;
index 3a74d8a..78d967f 100644 (file)
@@ -107,14 +107,13 @@ static int __diag_ipl_functions(struct kvm_vcpu *vcpu)
 
 static int __diag_virtio_hypercall(struct kvm_vcpu *vcpu)
 {
-       int ret, idx;
+       int ret;
 
        /* No virtio-ccw notification? Get out quickly. */
        if (!vcpu->kvm->arch.css_support ||
            (vcpu->run->s.regs.gprs[1] != KVM_S390_VIRTIO_CCW_NOTIFY))
                return -EOPNOTSUPP;
 
-       idx = srcu_read_lock(&vcpu->kvm->srcu);
        /*
         * The layout is as follows:
         * - gpr 2 contains the subchannel id (passed as addr)
@@ -125,7 +124,6 @@ static int __diag_virtio_hypercall(struct kvm_vcpu *vcpu)
                                      vcpu->run->s.regs.gprs[2],
                                      8, &vcpu->run->s.regs.gprs[3],
                                      vcpu->run->s.regs.gprs[4]);
-       srcu_read_unlock(&vcpu->kvm->srcu, idx);
 
        /*
         * Return cookie in gpr 2, but don't overwrite the register if the
index 99d789e..374a439 100644 (file)
 #include <asm/uaccess.h>
 #include "kvm-s390.h"
 
+/* Convert real to absolute address by applying the prefix of the CPU */
+static inline unsigned long kvm_s390_real_to_abs(struct kvm_vcpu *vcpu,
+                                                unsigned long gaddr)
+{
+       unsigned long prefix  = vcpu->arch.sie_block->prefix;
+       if (gaddr < 2 * PAGE_SIZE)
+               gaddr += prefix;
+       else if (gaddr >= prefix && gaddr < prefix + 2 * PAGE_SIZE)
+               gaddr -= prefix;
+       return gaddr;
+}
+
 static inline void __user *__gptr_to_uptr(struct kvm_vcpu *vcpu,
                                          void __user *gptr,
                                          int prefixing)
 {
-       unsigned long prefix  = vcpu->arch.sie_block->prefix;
        unsigned long gaddr = (unsigned long) gptr;
        unsigned long uaddr;
 
-       if (prefixing) {
-               if (gaddr < 2 * PAGE_SIZE)
-                       gaddr += prefix;
-               else if ((gaddr >= prefix) && (gaddr < prefix + 2 * PAGE_SIZE))
-                       gaddr -= prefix;
-       }
+       if (prefixing)
+               gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
        uaddr = gmap_fault(gaddr, vcpu->arch.gmap);
        if (IS_ERR_VALUE(uaddr))
                uaddr = -EFAULT;
index 5ee56e5..5ddbbde 100644 (file)
@@ -62,12 +62,6 @@ static int handle_stop(struct kvm_vcpu *vcpu)
 
        trace_kvm_s390_stop_request(vcpu->arch.local_int.action_bits);
 
-       if (vcpu->arch.local_int.action_bits & ACTION_RELOADVCPU_ON_STOP) {
-               vcpu->arch.local_int.action_bits &= ~ACTION_RELOADVCPU_ON_STOP;
-               rc = SIE_INTERCEPT_RERUNVCPU;
-               vcpu->run->exit_reason = KVM_EXIT_INTR;
-       }
-
        if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
                atomic_set_mask(CPUSTAT_STOPPED,
                                &vcpu->arch.sie_block->cpuflags);
index 7f1f7ac..5f79d2d 100644 (file)
@@ -436,6 +436,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)
        hrtimer_start(&vcpu->arch.ckc_timer, ktime_set (0, sltime) , HRTIMER_MODE_REL);
        VCPU_EVENT(vcpu, 5, "enabled wait via clock comparator: %llx ns", sltime);
 no_timer:
+       srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
        spin_lock(&vcpu->arch.local_int.float_int->lock);
        spin_lock_bh(&vcpu->arch.local_int.lock);
        add_wait_queue(&vcpu->wq, &wait);
@@ -455,6 +456,8 @@ no_timer:
        remove_wait_queue(&vcpu->wq, &wait);
        spin_unlock_bh(&vcpu->arch.local_int.lock);
        spin_unlock(&vcpu->arch.local_int.float_int->lock);
+       vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
+
        hrtimer_try_to_cancel(&vcpu->arch.ckc_timer);
        return 0;
 }
index ed8064c..569494e 100644 (file)
@@ -695,9 +695,9 @@ static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
        return 0;
 }
 
-static int __vcpu_run(struct kvm_vcpu *vcpu)
+static int vcpu_pre_run(struct kvm_vcpu *vcpu)
 {
-       int rc;
+       int rc, cpuflags;
 
        memcpy(&vcpu->arch.sie_block->gg14, &vcpu->run->s.regs.gprs[14], 16);
 
@@ -715,28 +715,24 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
                return rc;
 
        vcpu->arch.sie_block->icptcode = 0;
-       VCPU_EVENT(vcpu, 6, "entering sie flags %x",
-                  atomic_read(&vcpu->arch.sie_block->cpuflags));
-       trace_kvm_s390_sie_enter(vcpu,
-                                atomic_read(&vcpu->arch.sie_block->cpuflags));
+       cpuflags = atomic_read(&vcpu->arch.sie_block->cpuflags);
+       VCPU_EVENT(vcpu, 6, "entering sie flags %x", cpuflags);
+       trace_kvm_s390_sie_enter(vcpu, cpuflags);
 
-       /*
-        * As PF_VCPU will be used in fault handler, between guest_enter
-        * and guest_exit should be no uaccess.
-        */
-       preempt_disable();
-       kvm_guest_enter();
-       preempt_enable();
-       rc = sie64a(vcpu->arch.sie_block, vcpu->run->s.regs.gprs);
-       kvm_guest_exit();
+       return 0;
+}
+
+static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
+{
+       int rc;
 
        VCPU_EVENT(vcpu, 6, "exit sie icptcode %d",
                   vcpu->arch.sie_block->icptcode);
        trace_kvm_s390_sie_exit(vcpu, vcpu->arch.sie_block->icptcode);
 
-       if (rc > 0)
+       if (exit_reason >= 0) {
                rc = 0;
-       if (rc < 0) {
+       } else {
                if (kvm_is_ucontrol(vcpu->kvm)) {
                        rc = SIE_INTERCEPT_UCONTROL;
                } else {
@@ -747,6 +743,49 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
        }
 
        memcpy(&vcpu->run->s.regs.gprs[14], &vcpu->arch.sie_block->gg14, 16);
+
+       if (rc == 0) {
+               if (kvm_is_ucontrol(vcpu->kvm))
+                       rc = -EOPNOTSUPP;
+               else
+                       rc = kvm_handle_sie_intercept(vcpu);
+       }
+
+       return rc;
+}
+
+static int __vcpu_run(struct kvm_vcpu *vcpu)
+{
+       int rc, exit_reason;
+
+       /*
+        * We try to hold kvm->srcu during most of vcpu_run (except when run-
+        * ning the guest), so that memslots (and other stuff) are protected
+        */
+       vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
+
+       do {
+               rc = vcpu_pre_run(vcpu);
+               if (rc)
+                       break;
+
+               srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
+               /*
+                * As PF_VCPU will be used in fault handler, between
+                * guest_enter and guest_exit should be no uaccess.
+                */
+               preempt_disable();
+               kvm_guest_enter();
+               preempt_enable();
+               exit_reason = sie64a(vcpu->arch.sie_block,
+                                    vcpu->run->s.regs.gprs);
+               kvm_guest_exit();
+               vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
+
+               rc = vcpu_post_run(vcpu, exit_reason);
+       } while (!signal_pending(current) && !rc);
+
+       srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
        return rc;
 }
 
@@ -755,7 +794,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
        int rc;
        sigset_t sigsaved;
 
-rerun_vcpu:
        if (vcpu->sigset_active)
                sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
 
@@ -788,19 +826,7 @@ rerun_vcpu:
        }
 
        might_fault();
-
-       do {
-               rc = __vcpu_run(vcpu);
-               if (rc)
-                       break;
-               if (kvm_is_ucontrol(vcpu->kvm))
-                       rc = -EOPNOTSUPP;
-               else
-                       rc = kvm_handle_sie_intercept(vcpu);
-       } while (!signal_pending(current) && !rc);
-
-       if (rc == SIE_INTERCEPT_RERUNVCPU)
-               goto rerun_vcpu;
+       rc = __vcpu_run(vcpu);
 
        if (signal_pending(current) && !rc) {
                kvm_run->exit_reason = KVM_EXIT_INTR;
@@ -958,6 +984,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 {
        struct kvm_vcpu *vcpu = filp->private_data;
        void __user *argp = (void __user *)arg;
+       int idx;
        long r;
 
        switch (ioctl) {
@@ -971,7 +998,9 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
                break;
        }
        case KVM_S390_STORE_STATUS:
+               idx = srcu_read_lock(&vcpu->kvm->srcu);
                r = kvm_s390_vcpu_store_status(vcpu, arg);
+               srcu_read_unlock(&vcpu->kvm->srcu, idx);
                break;
        case KVM_S390_SET_INITIAL_PSW: {
                psw_t psw;
@@ -1067,12 +1096,13 @@ int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
        return VM_FAULT_SIGBUS;
 }
 
-void kvm_arch_free_memslot(struct kvm_memory_slot *free,
+void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
                           struct kvm_memory_slot *dont)
 {
 }
 
-int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
+int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
+                           unsigned long npages)
 {
        return 0;
 }
index dc99f1c..b44912a 100644 (file)
@@ -28,8 +28,7 @@ typedef int (*intercept_handler_t)(struct kvm_vcpu *vcpu);
 extern unsigned long *vfacilities;
 
 /* negativ values are error codes, positive values for internal conditions */
-#define SIE_INTERCEPT_RERUNVCPU                (1<<0)
-#define SIE_INTERCEPT_UCONTROL         (1<<1)
+#define SIE_INTERCEPT_UCONTROL         (1<<0)
 int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu);
 
 #define VM_EVENT(d_kvm, d_loglevel, d_string, d_args...)\
@@ -91,8 +90,10 @@ static inline void kvm_s390_get_base_disp_sse(struct kvm_vcpu *vcpu,
 
 static inline void kvm_s390_get_regs_rre(struct kvm_vcpu *vcpu, int *r1, int *r2)
 {
-       *r1 = (vcpu->arch.sie_block->ipb & 0x00f00000) >> 20;
-       *r2 = (vcpu->arch.sie_block->ipb & 0x000f0000) >> 16;
+       if (r1)
+               *r1 = (vcpu->arch.sie_block->ipb & 0x00f00000) >> 20;
+       if (r2)
+               *r2 = (vcpu->arch.sie_block->ipb & 0x000f0000) >> 16;
 }
 
 static inline u64 kvm_s390_get_base_disp_rsy(struct kvm_vcpu *vcpu)
index 59200ee..2440602 100644 (file)
 #include "kvm-s390.h"
 #include "trace.h"
 
+/* Handle SCK (SET CLOCK) interception */
+static int handle_set_clock(struct kvm_vcpu *vcpu)
+{
+       struct kvm_vcpu *cpup;
+       s64 hostclk, val;
+       u64 op2;
+       int i;
+
+       if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
+               return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
+
+       op2 = kvm_s390_get_base_disp_s(vcpu);
+       if (op2 & 7)    /* Operand must be on a doubleword boundary */
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+       if (get_guest(vcpu, val, (u64 __user *) op2))
+               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+
+       if (store_tod_clock(&hostclk)) {
+               kvm_s390_set_psw_cc(vcpu, 3);
+               return 0;
+       }
+       val = (val - hostclk) & ~0x3fUL;
+
+       mutex_lock(&vcpu->kvm->lock);
+       kvm_for_each_vcpu(i, cpup, vcpu->kvm)
+               cpup->arch.sie_block->epoch = val;
+       mutex_unlock(&vcpu->kvm->lock);
+
+       kvm_s390_set_psw_cc(vcpu, 0);
+       return 0;
+}
+
 static int handle_set_prefix(struct kvm_vcpu *vcpu)
 {
        u64 operand2;
@@ -128,6 +160,33 @@ static int handle_skey(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+static int handle_test_block(struct kvm_vcpu *vcpu)
+{
+       unsigned long hva;
+       gpa_t addr;
+       int reg2;
+
+       if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
+               return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
+
+       kvm_s390_get_regs_rre(vcpu, NULL, &reg2);
+       addr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
+       addr = kvm_s390_real_to_abs(vcpu, addr);
+
+       hva = gfn_to_hva(vcpu->kvm, gpa_to_gfn(addr));
+       if (kvm_is_error_hva(hva))
+               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       /*
+        * We don't expect errors on modern systems, and do not care
+        * about storage keys (yet), so let's just clear the page.
+        */
+       if (clear_user((void __user *)hva, PAGE_SIZE) != 0)
+               return -EFAULT;
+       kvm_s390_set_psw_cc(vcpu, 0);
+       vcpu->run->s.regs.gprs[0] = 0;
+       return 0;
+}
+
 static int handle_tpi(struct kvm_vcpu *vcpu)
 {
        struct kvm_s390_interrupt_info *inti;
@@ -438,12 +497,14 @@ out_exception:
 
 static const intercept_handler_t b2_handlers[256] = {
        [0x02] = handle_stidp,
+       [0x04] = handle_set_clock,
        [0x10] = handle_set_prefix,
        [0x11] = handle_store_prefix,
        [0x12] = handle_store_cpu_address,
        [0x29] = handle_skey,
        [0x2a] = handle_skey,
        [0x2b] = handle_skey,
+       [0x2c] = handle_test_block,
        [0x30] = handle_io_inst,
        [0x31] = handle_io_inst,
        [0x32] = handle_io_inst,
index 0a2e5e0..e794c88 100644 (file)
@@ -772,7 +772,11 @@ static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
                __free_page(page);
                return NULL;
        }
-       pgtable_page_ctor(page);
+       if (!pgtable_page_ctor(page)) {
+               kfree(mp);
+               __free_page(page);
+               return NULL;
+       }
        mp->vmaddr = vmaddr & PMD_MASK;
        INIT_LIST_HEAD(&mp->mapper);
        page->index = (unsigned long) mp;
@@ -902,7 +906,10 @@ unsigned long *page_table_alloc(struct mm_struct *mm, unsigned long vmaddr)
                page = alloc_page(GFP_KERNEL|__GFP_REPEAT);
                if (!page)
                        return NULL;
-               pgtable_page_ctor(page);
+               if (!pgtable_page_ctor(page)) {
+                       __free_page(page);
+                       return NULL;
+               }
                atomic_set(&page->_mapcount, 1);
                table = (unsigned long *) page_to_phys(page);
                clear_table(table, _PAGE_INVALID, PAGE_SIZE);
@@ -1244,11 +1251,11 @@ void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
        assert_spin_locked(&mm->page_table_lock);
 
        /* FIFO */
-       if (!mm->pmd_huge_pte)
+       if (!pmd_huge_pte(mm, pmdp))
                INIT_LIST_HEAD(lh);
        else
-               list_add(lh, (struct list_head *) mm->pmd_huge_pte);
-       mm->pmd_huge_pte = pgtable;
+               list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp));
+       pmd_huge_pte(mm, pmdp) = pgtable;
 }
 
 pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
@@ -1260,12 +1267,12 @@ pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
        assert_spin_locked(&mm->page_table_lock);
 
        /* FIFO */
-       pgtable = mm->pmd_huge_pte;
+       pgtable = pmd_huge_pte(mm, pmdp);
        lh = (struct list_head *) pgtable;
        if (list_empty(lh))
-               mm->pmd_huge_pte = NULL;
+               pmd_huge_pte(mm, pmdp) = NULL;
        else {
-               mm->pmd_huge_pte = (pgtable_t) lh->next;
+               pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next;
                list_del(lh);
        }
        ptep = (pte_t *) pgtable;
index 0c9a177..bf7c73d 100644 (file)
@@ -530,20 +530,6 @@ static void zpci_unmap_resources(struct zpci_dev *zdev)
        }
 }
 
-struct zpci_dev *zpci_alloc_device(void)
-{
-       struct zpci_dev *zdev;
-
-       /* Alloc memory for our private pci device data */
-       zdev = kzalloc(sizeof(*zdev), GFP_KERNEL);
-       return zdev ? : ERR_PTR(-ENOMEM);
-}
-
-void zpci_free_device(struct zpci_dev *zdev)
-{
-       kfree(zdev);
-}
-
 int pcibios_add_platform_entries(struct pci_dev *pdev)
 {
        return zpci_sysfs_add_device(&pdev->dev);
@@ -579,37 +565,6 @@ static void zpci_irq_exit(void)
        unregister_adapter_interrupt(&zpci_airq);
 }
 
-static struct resource *zpci_alloc_bus_resource(unsigned long start, unsigned long size,
-                                               unsigned long flags, int domain)
-{
-       struct resource *r;
-       char *name;
-       int rc;
-
-       r = kzalloc(sizeof(*r), GFP_KERNEL);
-       if (!r)
-               return ERR_PTR(-ENOMEM);
-       r->start = start;
-       r->end = r->start + size - 1;
-       r->flags = flags;
-       r->parent = &iomem_resource;
-       name = kmalloc(18, GFP_KERNEL);
-       if (!name) {
-               kfree(r);
-               return ERR_PTR(-ENOMEM);
-       }
-       sprintf(name, "PCI Bus: %04x:%02x", domain, ZPCI_BUS_NR);
-       r->name = name;
-
-       rc = request_resource(&iomem_resource, r);
-       if (rc) {
-               kfree(r->name);
-               kfree(r);
-               return ERR_PTR(-ENOMEM);
-       }
-       return r;
-}
-
 static int zpci_alloc_iomap(struct zpci_dev *zdev)
 {
        int entry;
@@ -633,6 +588,82 @@ static void zpci_free_iomap(struct zpci_dev *zdev, int entry)
        spin_unlock(&zpci_iomap_lock);
 }
 
+static struct resource *__alloc_res(struct zpci_dev *zdev, unsigned long start,
+                                   unsigned long size, unsigned long flags)
+{
+       struct resource *r;
+
+       r = kzalloc(sizeof(*r), GFP_KERNEL);
+       if (!r)
+               return NULL;
+
+       r->start = start;
+       r->end = r->start + size - 1;
+       r->flags = flags;
+       r->name = zdev->res_name;
+
+       if (request_resource(&iomem_resource, r)) {
+               kfree(r);
+               return NULL;
+       }
+       return r;
+}
+
+static int zpci_setup_bus_resources(struct zpci_dev *zdev,
+                                   struct list_head *resources)
+{
+       unsigned long addr, size, flags;
+       struct resource *res;
+       int i, entry;
+
+       snprintf(zdev->res_name, sizeof(zdev->res_name),
+                "PCI Bus %04x:%02x", zdev->domain, ZPCI_BUS_NR);
+
+       for (i = 0; i < PCI_BAR_COUNT; i++) {
+               if (!zdev->bars[i].size)
+                       continue;
+               entry = zpci_alloc_iomap(zdev);
+               if (entry < 0)
+                       return entry;
+               zdev->bars[i].map_idx = entry;
+
+               /* only MMIO is supported */
+               flags = IORESOURCE_MEM;
+               if (zdev->bars[i].val & 8)
+                       flags |= IORESOURCE_PREFETCH;
+               if (zdev->bars[i].val & 4)
+                       flags |= IORESOURCE_MEM_64;
+
+               addr = ZPCI_IOMAP_ADDR_BASE + ((u64) entry << 48);
+
+               size = 1UL << zdev->bars[i].size;
+
+               res = __alloc_res(zdev, addr, size, flags);
+               if (!res) {
+                       zpci_free_iomap(zdev, entry);
+                       return -ENOMEM;
+               }
+               zdev->bars[i].res = res;
+               pci_add_resource(resources, res);
+       }
+
+       return 0;
+}
+
+static void zpci_cleanup_bus_resources(struct zpci_dev *zdev)
+{
+       int i;
+
+       for (i = 0; i < PCI_BAR_COUNT; i++) {
+               if (!zdev->bars[i].size)
+                       continue;
+
+               zpci_free_iomap(zdev, zdev->bars[i].map_idx);
+               release_resource(zdev->bars[i].res);
+               kfree(zdev->bars[i].res);
+       }
+}
+
 int pcibios_add_device(struct pci_dev *pdev)
 {
        struct zpci_dev *zdev = get_zdev(pdev);
@@ -729,52 +760,6 @@ struct dev_pm_ops pcibios_pm_ops = {
 };
 #endif /* CONFIG_HIBERNATE_CALLBACKS */
 
-static int zpci_scan_bus(struct zpci_dev *zdev)
-{
-       struct resource *res;
-       LIST_HEAD(resources);
-       int i;
-
-       /* allocate mapping entry for each used bar */
-       for (i = 0; i < PCI_BAR_COUNT; i++) {
-               unsigned long addr, size, flags;
-               int entry;
-
-               if (!zdev->bars[i].size)
-                       continue;
-               entry = zpci_alloc_iomap(zdev);
-               if (entry < 0)
-                       return entry;
-               zdev->bars[i].map_idx = entry;
-
-               /* only MMIO is supported */
-               flags = IORESOURCE_MEM;
-               if (zdev->bars[i].val & 8)
-                       flags |= IORESOURCE_PREFETCH;
-               if (zdev->bars[i].val & 4)
-                       flags |= IORESOURCE_MEM_64;
-
-               addr = ZPCI_IOMAP_ADDR_BASE + ((u64) entry << 48);
-
-               size = 1UL << zdev->bars[i].size;
-
-               res = zpci_alloc_bus_resource(addr, size, flags, zdev->domain);
-               if (IS_ERR(res)) {
-                       zpci_free_iomap(zdev, entry);
-                       return PTR_ERR(res);
-               }
-               pci_add_resource(&resources, res);
-       }
-
-       zdev->bus = pci_scan_root_bus(NULL, ZPCI_BUS_NR, &pci_root_ops,
-                                     zdev, &resources);
-       if (!zdev->bus)
-               return -EIO;
-
-       zdev->bus->max_bus_speed = zdev->max_bus_speed;
-       return 0;
-}
-
 static int zpci_alloc_domain(struct zpci_dev *zdev)
 {
        spin_lock(&zpci_domain_lock);
@@ -795,6 +780,41 @@ static void zpci_free_domain(struct zpci_dev *zdev)
        spin_unlock(&zpci_domain_lock);
 }
 
+void pcibios_remove_bus(struct pci_bus *bus)
+{
+       struct zpci_dev *zdev = get_zdev_by_bus(bus);
+
+       zpci_exit_slot(zdev);
+       zpci_cleanup_bus_resources(zdev);
+       zpci_free_domain(zdev);
+
+       spin_lock(&zpci_list_lock);
+       list_del(&zdev->entry);
+       spin_unlock(&zpci_list_lock);
+
+       kfree(zdev);
+}
+
+static int zpci_scan_bus(struct zpci_dev *zdev)
+{
+       LIST_HEAD(resources);
+       int ret;
+
+       ret = zpci_setup_bus_resources(zdev, &resources);
+       if (ret)
+               return ret;
+
+       zdev->bus = pci_scan_root_bus(NULL, ZPCI_BUS_NR, &pci_root_ops,
+                                     zdev, &resources);
+       if (!zdev->bus) {
+               zpci_cleanup_bus_resources(zdev);
+               return -EIO;
+       }
+
+       zdev->bus->max_bus_speed = zdev->max_bus_speed;
+       return 0;
+}
+
 int zpci_enable_device(struct zpci_dev *zdev)
 {
        int rc;
index 8414798..c747394 100644 (file)
@@ -155,9 +155,9 @@ int clp_add_pci_device(u32 fid, u32 fh, int configured)
        int rc;
 
        zpci_dbg(3, "add fid:%x, fh:%x, c:%d\n", fid, fh, configured);
-       zdev = zpci_alloc_device();
-       if (IS_ERR(zdev))
-               return PTR_ERR(zdev);
+       zdev = kzalloc(sizeof(*zdev), GFP_KERNEL);
+       if (!zdev)
+               return -ENOMEM;
 
        zdev->fh = fh;
        zdev->fid = fid;
@@ -178,7 +178,7 @@ int clp_add_pci_device(u32 fid, u32 fh, int configured)
        return 0;
 
 error:
-       zpci_free_device(zdev);
+       kfree(zdev);
        return rc;
 }
 
index 278e671..800f064 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <asm/pci_debug.h>
+#include <asm/sclp.h>
 
 /* Content Code Description for PCI Function Error */
 struct zpci_ccdf_err {
@@ -42,10 +43,27 @@ struct zpci_ccdf_avail {
        u16 pec;                        /* PCI event code */
 } __packed;
 
-static void zpci_event_log_avail(struct zpci_ccdf_avail *ccdf)
+void zpci_event_error(void *data)
 {
+       struct zpci_ccdf_err *ccdf = data;
+       struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
+
+       zpci_err("error CCDF:\n");
+       zpci_err_hex(ccdf, sizeof(*ccdf));
+
+       if (!zdev)
+               return;
+
+       pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n",
+              pci_name(zdev->pdev), ccdf->pec, ccdf->fid);
+}
+
+void zpci_event_availability(void *data)
+{
+       struct zpci_ccdf_avail *ccdf = data;
        struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
        struct pci_dev *pdev = zdev ? zdev->pdev : NULL;
+       int ret;
 
        pr_info("%s: Event 0x%x reconfigured PCI function 0x%x\n",
                pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid);
@@ -53,36 +71,47 @@ static void zpci_event_log_avail(struct zpci_ccdf_avail *ccdf)
        zpci_err_hex(ccdf, sizeof(*ccdf));
 
        switch (ccdf->pec) {
-       case 0x0301:
-               zpci_enable_device(zdev);
+       case 0x0301: /* Standby -> Configured */
+               if (!zdev || zdev->state == ZPCI_FN_STATE_CONFIGURED)
+                       break;
+               zdev->state = ZPCI_FN_STATE_CONFIGURED;
+               ret = zpci_enable_device(zdev);
+               if (ret)
+                       break;
+               pci_rescan_bus(zdev->bus);
                break;
-       case 0x0302:
+       case 0x0302: /* Reserved -> Standby */
                clp_add_pci_device(ccdf->fid, ccdf->fh, 0);
                break;
-       case 0x0306:
+       case 0x0303: /* Deconfiguration requested */
+               if (pdev)
+                       pci_stop_and_remove_bus_device(pdev);
+
+               ret = zpci_disable_device(zdev);
+               if (ret)
+                       break;
+
+               ret = sclp_pci_deconfigure(zdev->fid);
+               zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, ret);
+               if (!ret)
+                       zdev->state = ZPCI_FN_STATE_STANDBY;
+
+               break;
+       case 0x0304: /* Configured -> Standby */
+               if (pdev)
+                       pci_stop_and_remove_bus_device(pdev);
+
+               zpci_disable_device(zdev);
+               zdev->state = ZPCI_FN_STATE_STANDBY;
+               break;
+       case 0x0306: /* 0x308 or 0x302 for multiple devices */
                clp_rescan_pci_devices();
                break;
+       case 0x0308: /* Standby -> Reserved */
+               pci_stop_root_bus(zdev->bus);
+               pci_remove_root_bus(zdev->bus);
+               break;
        default:
                break;
        }
 }
-
-void zpci_event_error(void *data)
-{
-       struct zpci_ccdf_err *ccdf = data;
-       struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
-
-       zpci_err("error CCDF:\n");
-       zpci_err_hex(ccdf, sizeof(*ccdf));
-
-       if (!zdev)
-               return;
-
-       pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n",
-              pci_name(zdev->pdev), ccdf->pec, ccdf->fid);
-}
-
-void zpci_event_availability(void *data)
-{
-       zpci_event_log_avail(data);
-}
index 716b3fd..2e06765 100644 (file)
@@ -54,9 +54,12 @@ static inline struct page *pte_alloc_one(struct mm_struct *mm,
        struct page *pte;
 
        pte = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER);
-       if (pte) {
-               clear_highpage(pte);
-               pgtable_page_ctor(pte);
+       if (!pte)
+               return NULL;
+       clear_highpage(pte);
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
        }
        return pte;
 }
index 1425cc0..656b7ad 100644 (file)
@@ -72,8 +72,6 @@ register struct thread_info *__current_thread_info __asm__("r28");
 
 #endif /* !__ASSEMBLY__ */
 
-#define PREEMPT_ACTIVE         0x10000000
-
 /*
  * thread information flags
  * - these are process state flags that various assembly files may need to
index 224f4bc..9b0979f 100644 (file)
@@ -1,5 +1,6 @@
 config SUPERH
        def_bool y
+       select ARCH_MIGHT_HAVE_PC_PARPORT
        select EXPERT
        select CLKDEV_LOOKUP
        select HAVE_IDE if HAS_IOPORT
@@ -711,7 +712,6 @@ config CC_STACKPROTECTOR
 config SMP
        bool "Symmetric multi-processing support"
        depends on SYS_SUPPORTS_SMP
-       select USE_GENERIC_SMP_HELPERS
        ---help---
          This enables support for systems with more than one CPU. If you have
          a system with only one CPU, like most personal computers, say N. If
index 1fa8be4..122f737 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/mmc/sh_mmcif.h>
 #include <linux/mmc/sh_mobile_sdhi.h>
 #include <linux/mtd/physmap.h>
+#include <linux/mfd/tmio.h>
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
index 21c5088..b9d9489 100644 (file)
@@ -81,7 +81,7 @@ static inline void get_mmu_context(struct mm_struct *mm, unsigned int cpu)
 
                /*
                 * Fix version; Note that we avoid version #0
-                * to distingush NO_CONTEXT.
+                * to distinguish NO_CONTEXT.
                 */
                if (!asid)
                        asid = MMU_CONTEXT_FIRST_VERSION;
index 8c00785..a33673b 100644 (file)
@@ -47,7 +47,10 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
        if (!pg)
                return NULL;
        page = virt_to_page(pg);
-       pgtable_page_ctor(page);
+       if (!pgtable_page_ctor(page)) {
+               quicklist_free(QUICK_PT, NULL, pg);
+               return NULL;
+       }
        return page;
 }
 
index 45a9366..ad27ffa 100644 (file)
@@ -41,8 +41,6 @@ struct thread_info {
 
 #endif
 
-#define PREEMPT_ACTIVE         0x10000000
-
 #if defined(CONFIG_4KSTACKS)
 #define THREAD_SHIFT   12
 #else
index 9b6e4be..ca46834 100644 (file)
@@ -108,7 +108,7 @@ need_resched:
        and     #(0xf0>>1), r0          ! interrupts off (exception path)?
        cmp/eq  #(0xf0>>1), r0
        bt      noresched
-       mov.l   3f, r0
+       mov.l   1f, r0
        jsr     @r0                     ! call preempt_schedule_irq
         nop
        bra     need_resched
@@ -119,9 +119,7 @@ noresched:
         nop
 
        .align 2
-1:     .long   PREEMPT_ACTIVE
-2:     .long   schedule
-3:     .long   preempt_schedule_irq
+1:     .long   preempt_schedule_irq
 #endif
 
 ENTRY(resume_userspace)
index 78c4fdb..d4f7a6a 100644 (file)
@@ -12,6 +12,7 @@ config 64BIT
 config SPARC
        bool
        default y
+       select ARCH_MIGHT_HAVE_PC_PARPORT if SPARC64 && PCI
        select OF
        select OF_PROMTREE
        select HAVE_IDE
@@ -28,7 +29,6 @@ config SPARC
        select HAVE_ARCH_JUMP_LABEL
        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
        select HAVE_BPF_JIT
@@ -64,6 +64,7 @@ config SPARC64
        select HAVE_DYNAMIC_FTRACE
        select HAVE_FTRACE_MCOUNT_RECORD
        select HAVE_SYSCALL_TRACEPOINTS
+       select HAVE_CONTEXT_TRACKING
        select HAVE_DEBUG_KMEMLEAK
        select RTC_DRV_CMOS
        select RTC_DRV_BQ4802
index 1620076..ee93923 100644 (file)
@@ -7,7 +7,6 @@
 #ifndef __SPARC_HARDIRQ_H
 #define __SPARC_HARDIRQ_H
 
-#define HARDIRQ_BITS    8
 #include <asm-generic/hardirq.h>
 
 #endif /* __SPARC_HARDIRQ_H */
index 7c29fd1..f478ff1 100644 (file)
@@ -14,6 +14,4 @@
 
 void ack_bad_irq(unsigned int irq);
 
-#define HARDIRQ_BITS   8
-
 #endif /* !(__SPARC64_HARDIRQ_H) */
index 76092c4..f668797 100644 (file)
@@ -93,7 +93,6 @@ typedef struct {
        spinlock_t              lock;
        unsigned long           sparc64_ctx_val;
        unsigned long           huge_pte_count;
-       struct page             *pgtable_page;
        struct tsb_config       tsb_block[MM_NUM_TSBS];
        struct hv_tsb_descr     tsb_descr[MM_NUM_TSBS];
 } mm_context_t;
index e155388..aac53fc 100644 (file)
 #define DCACHE_ALIASING_POSSIBLE
 #endif
 
-#define HPAGE_SHIFT            22
+#define HPAGE_SHIFT            23
+#define REAL_HPAGE_SHIFT       22
+
+#define REAL_HPAGE_SIZE                (_AC(1,UL) << REAL_HPAGE_SHIFT)
 
 #if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
 #define HPAGE_SIZE             (_AC(1,UL) << HPAGE_SHIFT)
@@ -53,8 +56,8 @@ extern void copy_user_page(void *to, void *from, unsigned long vaddr, struct pag
 /* These are used to make use of C type-checking.. */
 typedef struct { unsigned long pte; } pte_t;
 typedef struct { unsigned long iopte; } iopte_t;
-typedef struct { unsigned int pmd; } pmd_t;
-typedef struct { unsigned int pgd; } pgd_t;
+typedef struct { unsigned long pmd; } pmd_t;
+typedef struct { unsigned long pgd; } pgd_t;
 typedef struct { unsigned long pgprot; } pgprot_t;
 
 #define pte_val(x)     ((x).pte)
@@ -73,8 +76,8 @@ typedef struct { unsigned long pgprot; } pgprot_t;
 /* .. while these make it easier on the compiler */
 typedef unsigned long pte_t;
 typedef unsigned long iopte_t;
-typedef unsigned int pmd_t;
-typedef unsigned int pgd_t;
+typedef unsigned long pmd_t;
+typedef unsigned long pgd_t;
 typedef unsigned long pgprot_t;
 
 #define pte_val(x)     (x)
@@ -93,18 +96,44 @@ typedef unsigned long pgprot_t;
 
 typedef pte_t *pgtable_t;
 
+/* These two values define the virtual address space range in which we
+ * must forbid 64-bit user processes from making mappings.  It used to
+ * represent precisely the virtual address space hole present in most
+ * early sparc64 chips including UltraSPARC-I.  But now it also is
+ * further constrained by the limits of our page tables, which is
+ * 43-bits of virtual address.
+ */
+#define SPARC64_VA_HOLE_TOP    _AC(0xfffffc0000000000,UL)
+#define SPARC64_VA_HOLE_BOTTOM _AC(0x0000040000000000,UL)
+
+/* The next two defines specify the actual exclusion region we
+ * enforce, wherein we use a 4GB red zone on each side of the VA hole.
+ */
+#define VA_EXCLUDE_START (SPARC64_VA_HOLE_BOTTOM - (1UL << 32UL))
+#define VA_EXCLUDE_END   (SPARC64_VA_HOLE_TOP + (1UL << 32UL))
+
 #define TASK_UNMAPPED_BASE     (test_thread_flag(TIF_32BIT) ? \
-                                (_AC(0x0000000070000000,UL)) : \
-                                (_AC(0xfffff80000000000,UL) + (1UL << 32UL)))
+                                _AC(0x0000000070000000,UL) : \
+                                VA_EXCLUDE_END)
 
 #include <asm-generic/memory_model.h>
 
+#define PAGE_OFFSET_BY_BITS(X) (-(_AC(1,UL) << (X)))
+extern unsigned long PAGE_OFFSET;
+
 #endif /* !(__ASSEMBLY__) */
 
-/* We used to stick this into a hard-coded global register (%g4)
- * but that does not make sense anymore.
+/* The maximum number of physical memory address bits we support, this
+ * is used to size various tables used to manage kernel TLB misses and
+ * also the sparsemem code.
+ */
+#define MAX_PHYS_ADDRESS_BITS  47
+
+/* These two shift counts are used when indexing sparc64_valid_addr_bitmap
+ * and kpte_linear_bitmap.
  */
-#define PAGE_OFFSET            _AC(0xFFFFF80000000000,UL)
+#define ILOG2_4MB              22
+#define ILOG2_256MB            28
 
 #ifndef __ASSEMBLY__
 
index 3676031..8358dc1 100644 (file)
 /* PMD_SHIFT determines the size of the area a second-level page
  * table can map
  */
-#define PMD_SHIFT      (PAGE_SHIFT + (PAGE_SHIFT-4))
+#define PMD_SHIFT      (PAGE_SHIFT + (PAGE_SHIFT-3))
 #define PMD_SIZE       (_AC(1,UL) << PMD_SHIFT)
 #define PMD_MASK       (~(PMD_SIZE-1))
-#define PMD_BITS       (PAGE_SHIFT - 2)
+#define PMD_BITS       (PAGE_SHIFT - 3)
 
 /* PGDIR_SHIFT determines what a third-level page table entry can map */
-#define PGDIR_SHIFT    (PAGE_SHIFT + (PAGE_SHIFT-4) + PMD_BITS)
+#define PGDIR_SHIFT    (PAGE_SHIFT + (PAGE_SHIFT-3) + PMD_BITS)
 #define PGDIR_SIZE     (_AC(1,UL) << PGDIR_SHIFT)
 #define PGDIR_MASK     (~(PGDIR_SIZE-1))
-#define PGDIR_BITS     (PAGE_SHIFT - 2)
+#define PGDIR_BITS     (PAGE_SHIFT - 3)
 
-#if (PGDIR_SHIFT + PGDIR_BITS) != 44
+#if (PGDIR_SHIFT + PGDIR_BITS) != 43
 #error Page table parameters do not cover virtual address space properly.
 #endif
 
 #error PMD_SHIFT must equal HPAGE_SHIFT for transparent huge pages.
 #endif
 
-/* PMDs point to PTE tables which are 4K aligned.  */
-#define PMD_PADDR      _AC(0xfffffffe,UL)
-#define PMD_PADDR_SHIFT        _AC(11,UL)
-
-#define PMD_ISHUGE     _AC(0x00000001,UL)
-
-/* This is the PMD layout when PMD_ISHUGE is set.  With 4MB huge
- * pages, this frees up a bunch of bits in the layout that we can
- * use for the protection settings and software metadata.
- */
-#define PMD_HUGE_PADDR         _AC(0xfffff800,UL)
-#define PMD_HUGE_PROTBITS      _AC(0x000007ff,UL)
-#define PMD_HUGE_PRESENT       _AC(0x00000400,UL)
-#define PMD_HUGE_WRITE         _AC(0x00000200,UL)
-#define PMD_HUGE_DIRTY         _AC(0x00000100,UL)
-#define PMD_HUGE_ACCESSED      _AC(0x00000080,UL)
-#define PMD_HUGE_EXEC          _AC(0x00000040,UL)
-#define PMD_HUGE_SPLITTING     _AC(0x00000020,UL)
-
-/* PGDs point to PMD tables which are 8K aligned.  */
-#define PGD_PADDR      _AC(0xfffffffc,UL)
-#define PGD_PADDR_SHIFT        _AC(11,UL)
-
 #ifndef __ASSEMBLY__
 
 #include <linux/sched.h>
 
 /* Entries per page directory level. */
-#define PTRS_PER_PTE   (1UL << (PAGE_SHIFT-4))
+#define PTRS_PER_PTE   (1UL << (PAGE_SHIFT-3))
 #define PTRS_PER_PMD   (1UL << PMD_BITS)
 #define PTRS_PER_PGD   (1UL << PGDIR_BITS)
 
 #define _PAGE_VALID      _AC(0x8000000000000000,UL) /* Valid TTE            */
 #define _PAGE_R                  _AC(0x8000000000000000,UL) /* Keep ref bit uptodate*/
 #define _PAGE_SPECIAL     _AC(0x0200000000000000,UL) /* Special page         */
+#define _PAGE_PMD_HUGE    _AC(0x0100000000000000,UL) /* Huge page            */
 
 /* Advertise support for _PAGE_SPECIAL */
 #define __HAVE_ARCH_PTE_SPECIAL
 #define _PAGE_IE_4U      _AC(0x0800000000000000,UL) /* Invert Endianness    */
 #define _PAGE_SOFT2_4U   _AC(0x07FC000000000000,UL) /* Software bits, set 2 */
 #define _PAGE_SPECIAL_4U  _AC(0x0200000000000000,UL) /* Special page         */
+#define _PAGE_PMD_HUGE_4U _AC(0x0100000000000000,UL) /* Huge page            */
 #define _PAGE_RES1_4U    _AC(0x0002000000000000,UL) /* Reserved             */
 #define _PAGE_SZ32MB_4U          _AC(0x0001000000000000,UL) /* (Panther) 32MB page  */
 #define _PAGE_SZ256MB_4U  _AC(0x2001000000000000,UL) /* (Panther) 256MB page */
 #define _PAGE_READ_4V    _AC(0x0800000000000000,UL) /* Readable SW Bit      */
 #define _PAGE_WRITE_4V   _AC(0x0400000000000000,UL) /* Writable SW Bit      */
 #define _PAGE_SPECIAL_4V  _AC(0x0200000000000000,UL) /* Special page         */
+#define _PAGE_PMD_HUGE_4V _AC(0x0100000000000000,UL) /* Huge page            */
 #define _PAGE_PADDR_4V   _AC(0x00FFFFFFFFFFE000,UL) /* paddr[55:13]         */
 #define _PAGE_IE_4V      _AC(0x0000000000001000,UL) /* Invert Endianness    */
 #define _PAGE_E_4V       _AC(0x0000000000000800,UL) /* side-Effect          */
 #define _PAGE_SZBITS_4U        _PAGE_SZ8K_4U
 #define _PAGE_SZBITS_4V        _PAGE_SZ8K_4V
 
+#if REAL_HPAGE_SHIFT != 22
+#error REAL_HPAGE_SHIFT and _PAGE_SZHUGE_foo must match up
+#endif
+
 #define _PAGE_SZHUGE_4U        _PAGE_SZ4MB_4U
 #define _PAGE_SZHUGE_4V        _PAGE_SZ4MB_4V
 
@@ -239,16 +223,13 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot)
 #define mk_pte(page, pgprot)   pfn_pte(page_to_pfn(page), (pgprot))
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-extern pmd_t pfn_pmd(unsigned long page_nr, pgprot_t pgprot);
-#define mk_pmd(page, pgprot)   pfn_pmd(page_to_pfn(page), (pgprot))
-
-extern pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot);
-
-static inline pmd_t pmd_mkhuge(pmd_t pmd)
+static inline pmd_t pfn_pmd(unsigned long page_nr, pgprot_t pgprot)
 {
-       /* Do nothing, mk_pmd() does this part.  */
-       return pmd;
+       pte_t pte = pfn_pte(page_nr, pgprot);
+
+       return __pmd(pte_val(pte));
 }
+#define mk_pmd(page, pgprot)   pfn_pmd(page_to_pfn(page), (pgprot))
 #endif
 
 /* This one can be done with two shifts.  */
@@ -309,14 +290,25 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t prot)
        : "=r" (mask), "=r" (tmp)
        : "i" (_PAGE_PADDR_4U | _PAGE_MODIFIED_4U | _PAGE_ACCESSED_4U |
               _PAGE_CP_4U | _PAGE_CV_4U | _PAGE_E_4U | _PAGE_PRESENT_4U |
-              _PAGE_SPECIAL),
+              _PAGE_SPECIAL | _PAGE_PMD_HUGE | _PAGE_SZALL_4U),
          "i" (_PAGE_PADDR_4V | _PAGE_MODIFIED_4V | _PAGE_ACCESSED_4V |
               _PAGE_CP_4V | _PAGE_CV_4V | _PAGE_E_4V | _PAGE_PRESENT_4V |
-              _PAGE_SPECIAL));
+              _PAGE_SPECIAL | _PAGE_PMD_HUGE | _PAGE_SZALL_4V));
 
        return __pte((pte_val(pte) & mask) | (pgprot_val(prot) & ~mask));
 }
 
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
+{
+       pte_t pte = __pte(pmd_val(pmd));
+
+       pte = pte_modify(pte, newprot);
+
+       return __pmd(pte_val(pte));
+}
+#endif
+
 static inline pte_t pgoff_to_pte(unsigned long off)
 {
        off <<= PAGE_SHIFT;
@@ -357,7 +349,7 @@ static inline pgprot_t pgprot_noncached(pgprot_t prot)
  */
 #define pgprot_noncached pgprot_noncached
 
-#ifdef CONFIG_HUGETLB_PAGE
+#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
 static inline pte_t pte_mkhuge(pte_t pte)
 {
        unsigned long mask;
@@ -375,6 +367,17 @@ static inline pte_t pte_mkhuge(pte_t pte)
 
        return __pte(pte_val(pte) | mask);
 }
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+static inline pmd_t pmd_mkhuge(pmd_t pmd)
+{
+       pte_t pte = __pte(pmd_val(pmd));
+
+       pte = pte_mkhuge(pte);
+       pte_val(pte) |= _PAGE_PMD_HUGE;
+
+       return __pmd(pte_val(pte));
+}
+#endif
 #endif
 
 static inline pte_t pte_mkdirty(pte_t pte)
@@ -626,91 +629,130 @@ static inline unsigned long pte_special(pte_t pte)
        return pte_val(pte) & _PAGE_SPECIAL;
 }
 
-static inline int pmd_large(pmd_t pmd)
+static inline unsigned long pmd_large(pmd_t pmd)
 {
-       return (pmd_val(pmd) & (PMD_ISHUGE | PMD_HUGE_PRESENT)) ==
-               (PMD_ISHUGE | PMD_HUGE_PRESENT);
+       pte_t pte = __pte(pmd_val(pmd));
+
+       return (pte_val(pte) & _PAGE_PMD_HUGE) && pte_present(pte);
 }
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-static inline int pmd_young(pmd_t pmd)
+static inline unsigned long pmd_young(pmd_t pmd)
 {
-       return pmd_val(pmd) & PMD_HUGE_ACCESSED;
+       pte_t pte = __pte(pmd_val(pmd));
+
+       return pte_young(pte);
 }
 
-static inline int pmd_write(pmd_t pmd)
+static inline unsigned long pmd_write(pmd_t pmd)
 {
-       return pmd_val(pmd) & PMD_HUGE_WRITE;
+       pte_t pte = __pte(pmd_val(pmd));
+
+       return pte_write(pte);
 }
 
 static inline unsigned long pmd_pfn(pmd_t pmd)
 {
-       unsigned long val = pmd_val(pmd) & PMD_HUGE_PADDR;
+       pte_t pte = __pte(pmd_val(pmd));
 
-       return val >> (PAGE_SHIFT - PMD_PADDR_SHIFT);
+       return pte_pfn(pte);
 }
 
-static inline int pmd_trans_splitting(pmd_t pmd)
+static inline unsigned long pmd_trans_huge(pmd_t pmd)
 {
-       return (pmd_val(pmd) & (PMD_ISHUGE|PMD_HUGE_SPLITTING)) ==
-               (PMD_ISHUGE|PMD_HUGE_SPLITTING);
+       pte_t pte = __pte(pmd_val(pmd));
+
+       return pte_val(pte) & _PAGE_PMD_HUGE;
 }
 
-static inline int pmd_trans_huge(pmd_t pmd)
+static inline unsigned long pmd_trans_splitting(pmd_t pmd)
 {
-       return pmd_val(pmd) & PMD_ISHUGE;
+       pte_t pte = __pte(pmd_val(pmd));
+
+       return pmd_trans_huge(pmd) && pte_special(pte);
 }
 
 #define has_transparent_hugepage() 1
 
 static inline pmd_t pmd_mkold(pmd_t pmd)
 {
-       pmd_val(pmd) &= ~PMD_HUGE_ACCESSED;
-       return pmd;
+       pte_t pte = __pte(pmd_val(pmd));
+
+       pte = pte_mkold(pte);
+
+       return __pmd(pte_val(pte));
 }
 
 static inline pmd_t pmd_wrprotect(pmd_t pmd)
 {
-       pmd_val(pmd) &= ~PMD_HUGE_WRITE;
-       return pmd;
+       pte_t pte = __pte(pmd_val(pmd));
+
+       pte = pte_wrprotect(pte);
+
+       return __pmd(pte_val(pte));
 }
 
 static inline pmd_t pmd_mkdirty(pmd_t pmd)
 {
-       pmd_val(pmd) |= PMD_HUGE_DIRTY;
-       return pmd;
+       pte_t pte = __pte(pmd_val(pmd));
+
+       pte = pte_mkdirty(pte);
+
+       return __pmd(pte_val(pte));
 }
 
 static inline pmd_t pmd_mkyoung(pmd_t pmd)
 {
-       pmd_val(pmd) |= PMD_HUGE_ACCESSED;
-       return pmd;
+       pte_t pte = __pte(pmd_val(pmd));
+
+       pte = pte_mkyoung(pte);
+
+       return __pmd(pte_val(pte));
 }
 
 static inline pmd_t pmd_mkwrite(pmd_t pmd)
 {
-       pmd_val(pmd) |= PMD_HUGE_WRITE;
-       return pmd;
+       pte_t pte = __pte(pmd_val(pmd));
+
+       pte = pte_mkwrite(pte);
+
+       return __pmd(pte_val(pte));
 }
 
 static inline pmd_t pmd_mknotpresent(pmd_t pmd)
 {
-       pmd_val(pmd) &= ~PMD_HUGE_PRESENT;
+       unsigned long mask;
+
+       if (tlb_type == hypervisor)
+               mask = _PAGE_PRESENT_4V;
+       else
+               mask = _PAGE_PRESENT_4U;
+
+       pmd_val(pmd) &= ~mask;
+
        return pmd;
 }
 
 static inline pmd_t pmd_mksplitting(pmd_t pmd)
 {
-       pmd_val(pmd) |= PMD_HUGE_SPLITTING;
-       return pmd;
+       pte_t pte = __pte(pmd_val(pmd));
+
+       pte = pte_mkspecial(pte);
+
+       return __pmd(pte_val(pte));
 }
 
-extern pgprot_t pmd_pgprot(pmd_t entry);
+static inline pgprot_t pmd_pgprot(pmd_t entry)
+{
+       unsigned long val = pmd_val(entry);
+
+       return __pgprot(val);
+}
 #endif
 
 static inline int pmd_present(pmd_t pmd)
 {
-       return pmd_val(pmd) != 0U;
+       return pmd_val(pmd) != 0UL;
 }
 
 #define pmd_none(pmd)                  (!pmd_val(pmd))
@@ -728,33 +770,32 @@ static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr,
 
 static inline void pmd_set(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep)
 {
-       unsigned long val = __pa((unsigned long) (ptep)) >> PMD_PADDR_SHIFT;
+       unsigned long val = __pa((unsigned long) (ptep));
 
        pmd_val(*pmdp) = val;
 }
 
 #define pud_set(pudp, pmdp)    \
-       (pud_val(*(pudp)) = (__pa((unsigned long) (pmdp)) >> PGD_PADDR_SHIFT))
+       (pud_val(*(pudp)) = (__pa((unsigned long) (pmdp))))
 static inline unsigned long __pmd_page(pmd_t pmd)
 {
-       unsigned long paddr = (unsigned long) pmd_val(pmd);
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-       if (pmd_val(pmd) & PMD_ISHUGE)
-               paddr &= PMD_HUGE_PADDR;
-#endif
-       paddr <<= PMD_PADDR_SHIFT;
-       return ((unsigned long) __va(paddr));
+       pte_t pte = __pte(pmd_val(pmd));
+       unsigned long pfn;
+
+       pfn = pte_pfn(pte);
+
+       return ((unsigned long) __va(pfn << PAGE_SHIFT));
 }
 #define pmd_page(pmd)                  virt_to_page((void *)__pmd_page(pmd))
 #define pud_page_vaddr(pud)            \
-       ((unsigned long) __va((((unsigned long)pud_val(pud))<<PGD_PADDR_SHIFT)))
+       ((unsigned long) __va(pud_val(pud)))
 #define pud_page(pud)                  virt_to_page((void *)pud_page_vaddr(pud))
 #define pmd_bad(pmd)                   (0)
-#define pmd_clear(pmdp)                        (pmd_val(*(pmdp)) = 0U)
+#define pmd_clear(pmdp)                        (pmd_val(*(pmdp)) = 0UL)
 #define pud_none(pud)                  (!pud_val(pud))
 #define pud_bad(pud)                   (0)
 #define pud_present(pud)               (pud_val(pud) != 0U)
-#define pud_clear(pudp)                        (pud_val(*(pudp)) = 0U)
+#define pud_clear(pudp)                        (pud_val(*(pudp)) = 0UL)
 
 /* Same in both SUN4V and SUN4U.  */
 #define pte_none(pte)                  (!pte_val(pte))
@@ -789,7 +830,7 @@ static inline pmd_t pmdp_get_and_clear(struct mm_struct *mm,
                                       pmd_t *pmdp)
 {
        pmd_t pmd = *pmdp;
-       set_pmd_at(mm, addr, pmdp, __pmd(0U));
+       set_pmd_at(mm, addr, pmdp, __pmd(0UL));
        return pmd;
 }
 
@@ -837,8 +878,8 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
 })
 #endif
 
-extern pgd_t swapper_pg_dir[2048];
-extern pmd_t swapper_low_pmd_dir[2048];
+extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
+extern pmd_t swapper_low_pmd_dir[PTRS_PER_PMD];
 
 extern void paging_init(void);
 extern unsigned long find_ecache_flush_span(unsigned long size);
index b99d4e4..e5e1752 100644 (file)
@@ -3,9 +3,11 @@
 
 #ifdef __KERNEL__
 
+#include <asm/page.h>
+
 #define SECTION_SIZE_BITS       30
-#define MAX_PHYSADDR_BITS       42
-#define MAX_PHYSMEM_BITS        42
+#define MAX_PHYSADDR_BITS       MAX_PHYS_ADDRESS_BITS
+#define MAX_PHYSMEM_BITS        MAX_PHYS_ADDRESS_BITS
 
 #endif /* !(__KERNEL__) */
 
index dd38075..96efa7a 100644 (file)
@@ -105,8 +105,6 @@ register struct thread_info *current_thread_info_reg asm("g6");
 #define TI_W_SAVED     0x250
 /* #define TI_RESTART_BLOCK 0x25n */ /* Nobody cares */
 
-#define PREEMPT_ACTIVE         0x4000000
-
 /*
  * thread information flag bit numbers
  */
index d5e5042..a5f01ac 100644 (file)
@@ -111,8 +111,6 @@ struct thread_info {
 #define THREAD_SHIFT PAGE_SHIFT
 #endif /* PAGE_SHIFT == 13 */
 
-#define PREEMPT_ACTIVE         0x10000000
-
 /*
  * macros/functions for gaining access to the thread information structure
  */
@@ -192,7 +190,7 @@ register struct thread_info *current_thread_info_reg asm("g6");
 #define TIF_UNALIGNED          5       /* allowed to do unaligned accesses */
 /* flag bit 6 is available */
 #define TIF_32BIT              7       /* 32-bit binary */
-/* flag bit 8 is available */
+#define TIF_NOHZ               8       /* in adaptive nohz mode */
 #define TIF_SECCOMP            9       /* secure computing */
 #define TIF_SYSCALL_AUDIT      10      /* syscall auditing active */
 #define TIF_SYSCALL_TRACEPOINT 11      /* syscall tracepoint instrumentation */
@@ -210,6 +208,7 @@ register struct thread_info *current_thread_info_reg asm("g6");
 #define _TIF_NEED_RESCHED      (1<<TIF_NEED_RESCHED)
 #define _TIF_UNALIGNED         (1<<TIF_UNALIGNED)
 #define _TIF_32BIT             (1<<TIF_32BIT)
+#define _TIF_NOHZ              (1<<TIF_NOHZ)
 #define _TIF_SECCOMP           (1<<TIF_SECCOMP)
 #define _TIF_SYSCALL_AUDIT     (1<<TIF_SYSCALL_AUDIT)
 #define _TIF_SYSCALL_TRACEPOINT        (1<<TIF_SYSCALL_TRACEPOINT)
index f0d6a97..3c3c89f 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef _SPARC64_TLBFLUSH_H
 #define _SPARC64_TLBFLUSH_H
 
-#include <linux/mm.h>
 #include <asm/mmu_context.h>
 
 /* TSB flush operations. */
index e696432..2230f80 100644 (file)
@@ -142,98 +142,39 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
        or              REG1, %lo(swapper_pg_dir), REG1; \
        sllx            VADDR, 64 - (PGDIR_SHIFT + PGDIR_BITS), REG2; \
        srlx            REG2, 64 - PAGE_SHIFT, REG2; \
-       andn            REG2, 0x3, REG2; \
-       lduw            [REG1 + REG2], REG1; \
+       andn            REG2, 0x7, REG2; \
+       ld            [REG1 + REG2], REG1; \
        brz,pn          REG1, FAIL_LABEL; \
         sllx           VADDR, 64 - (PMD_SHIFT + PMD_BITS), REG2; \
        srlx            REG2, 64 - PAGE_SHIFT, REG2; \
-       sllx            REG1, PGD_PADDR_SHIFT, REG1; \
-       andn            REG2, 0x3, REG2; \
-       lduwa           [REG1 + REG2] ASI_PHYS_USE_EC, REG1; \
+       andn            REG2, 0x7, REG2; \
+       ldxa            [REG1 + REG2] ASI_PHYS_USE_EC, REG1; \
        brz,pn          REG1, FAIL_LABEL; \
         sllx           VADDR, 64 - PMD_SHIFT, REG2; \
-       srlx            REG2, 64 - (PAGE_SHIFT - 1), REG2; \
-       sllx            REG1, PMD_PADDR_SHIFT, REG1; \
+       srlx            REG2, 64 - PAGE_SHIFT, REG2; \
        andn            REG2, 0x7, REG2; \
        add             REG1, REG2, REG1;
 
-       /* These macros exists only to make the PMD translator below
-        * easier to read.  It hides the ELF section switch for the
-        * sun4v code patching.
-        */
-#define OR_PTE_BIT_1INSN(REG, NAME)                    \
-661:   or              REG, _PAGE_##NAME##_4U, REG;    \
-       .section        .sun4v_1insn_patch, "ax";       \
-       .word           661b;                           \
-       or              REG, _PAGE_##NAME##_4V, REG;    \
-       .previous;
-
-#define OR_PTE_BIT_2INSN(REG, TMP, NAME)               \
-661:   sethi           %hi(_PAGE_##NAME##_4U), TMP;    \
-       or              REG, TMP, REG;                  \
-       .section        .sun4v_2insn_patch, "ax";       \
-       .word           661b;                           \
-       mov             -1, TMP;                        \
-       or              REG, _PAGE_##NAME##_4V, REG;    \
-       .previous;
-
-       /* Load into REG the PTE value for VALID, CACHE, and SZHUGE.  */
-#define BUILD_PTE_VALID_SZHUGE_CACHE(REG)                                 \
-661:   sethi           %uhi(_PAGE_VALID|_PAGE_SZHUGE_4U), REG;            \
-       .section        .sun4v_1insn_patch, "ax";                          \
-       .word           661b;                                              \
-       sethi           %uhi(_PAGE_VALID), REG;                            \
-       .previous;                                                         \
-       sllx            REG, 32, REG;                                      \
-661:   or              REG, _PAGE_CP_4U|_PAGE_CV_4U, REG;                 \
-       .section        .sun4v_1insn_patch, "ax";                          \
-       .word           661b;                                              \
-       or              REG, _PAGE_CP_4V|_PAGE_CV_4V|_PAGE_SZHUGE_4V, REG; \
-       .previous;
-
        /* PMD has been loaded into REG1, interpret the value, seeing
         * if it is a HUGE PMD or a normal one.  If it is not valid
         * then jump to FAIL_LABEL.  If it is a HUGE PMD, and it
         * translates to a valid PTE, branch to PTE_LABEL.
         *
-        * We translate the PMD by hand, one bit at a time,
-        * constructing the huge PTE.
-        *
-        * So we construct the PTE in REG2 as follows:
-        *
-        * 1) Extract the PMD PFN from REG1 and place it into REG2.
-        *
-        * 2) Translate PMD protection bits in REG1 into REG2, one bit
-        *    at a time using andcc tests on REG1 and OR's into REG2.
-        *
-        *    Only two bits to be concerned with here, EXEC and WRITE.
-        *    Now REG1 is freed up and we can use it as a temporary.
-        *
-        * 3) Construct the VALID, CACHE, and page size PTE bits in
-        *    REG1, OR with REG2 to form final PTE.
+        * We have to propagate the 4MB bit of the virtual address
+        * because we are fabricating 8MB pages using 4MB hw pages.
         */
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 #define USER_PGTABLE_CHECK_PMD_HUGE(VADDR, REG1, REG2, FAIL_LABEL, PTE_LABEL) \
-       brz,pn          REG1, FAIL_LABEL;                                     \
-        andcc          REG1, PMD_ISHUGE, %g0;                                \
-       be,pt           %xcc, 700f;                                           \
-        and            REG1, PMD_HUGE_PRESENT|PMD_HUGE_ACCESSED, REG2;       \
-       cmp             REG2, PMD_HUGE_PRESENT|PMD_HUGE_ACCESSED;             \
-       bne,pn          %xcc, FAIL_LABEL;                                     \
-        andn           REG1, PMD_HUGE_PROTBITS, REG2;                        \
-       sllx            REG2, PMD_PADDR_SHIFT, REG2;                          \
-       /* REG2 now holds PFN << PAGE_SHIFT */                                \
-       andcc           REG1, PMD_HUGE_WRITE, %g0;                            \
-       bne,a,pt        %xcc, 1f;                                             \
-        OR_PTE_BIT_1INSN(REG2, W);                                           \
-1:     andcc           REG1, PMD_HUGE_EXEC, %g0;                             \
-       be,pt           %xcc, 1f;                                             \
-        nop;                                                                 \
-       OR_PTE_BIT_2INSN(REG2, REG1, EXEC);                                   \
-       /* REG1 can now be clobbered, build final PTE */                      \
-1:     BUILD_PTE_VALID_SZHUGE_CACHE(REG1);                                   \
-       ba,pt           %xcc, PTE_LABEL;                                      \
-        or             REG1, REG2, REG1;                                     \
+       brz,pn          REG1, FAIL_LABEL;               \
+        sethi          %uhi(_PAGE_PMD_HUGE), REG2;     \
+       sllx            REG2, 32, REG2;                 \
+       andcc           REG1, REG2, %g0;                \
+       be,pt           %xcc, 700f;                     \
+        sethi          %hi(4 * 1024 * 1024), REG2;     \
+       andn            REG1, REG2, REG1;               \
+       and             VADDR, REG2, REG2;              \
+       brlz,pt         REG1, PTE_LABEL;                \
+        or             REG1, REG2, REG1;               \
 700:
 #else
 #define USER_PGTABLE_CHECK_PMD_HUGE(VADDR, REG1, REG2, FAIL_LABEL, PTE_LABEL) \
@@ -253,18 +194,16 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
 #define USER_PGTABLE_WALK_TL1(VADDR, PHYS_PGD, REG1, REG2, FAIL_LABEL) \
        sllx            VADDR, 64 - (PGDIR_SHIFT + PGDIR_BITS), REG2; \
        srlx            REG2, 64 - PAGE_SHIFT, REG2; \
-       andn            REG2, 0x3, REG2; \
-       lduwa           [PHYS_PGD + REG2] ASI_PHYS_USE_EC, REG1; \
+       andn            REG2, 0x7, REG2; \
+       ldxa            [PHYS_PGD + REG2] ASI_PHYS_USE_EC, REG1; \
        brz,pn          REG1, FAIL_LABEL; \
         sllx           VADDR, 64 - (PMD_SHIFT + PMD_BITS), REG2; \
        srlx            REG2, 64 - PAGE_SHIFT, REG2; \
-       sllx            REG1, PGD_PADDR_SHIFT, REG1; \
-       andn            REG2, 0x3, REG2; \
-       lduwa           [REG1 + REG2] ASI_PHYS_USE_EC, REG1; \
+       andn            REG2, 0x7, REG2; \
+       ldxa            [REG1 + REG2] ASI_PHYS_USE_EC, REG1; \
        USER_PGTABLE_CHECK_PMD_HUGE(VADDR, REG1, REG2, FAIL_LABEL, 800f) \
        sllx            VADDR, 64 - PMD_SHIFT, REG2; \
-       srlx            REG2, 64 - (PAGE_SHIFT - 1), REG2; \
-       sllx            REG1, PMD_PADDR_SHIFT, REG1; \
+       srlx            REG2, 64 - PAGE_SHIFT, REG2; \
        andn            REG2, 0x7, REG2; \
        add             REG1, REG2, REG1; \
        ldxa            [REG1] ASI_PHYS_USE_EC, REG1; \
index 9c179fb..140966f 100644 (file)
@@ -88,7 +88,6 @@ extern asmlinkage void syscall_trace_leave(struct pt_regs *regs);
 
 extern void bad_trap_tl1(struct pt_regs *regs, long lvl);
 
-extern void do_fpe_common(struct pt_regs *regs);
 extern void do_fpieee(struct pt_regs *regs);
 extern void do_fpother(struct pt_regs *regs);
 extern void do_tof(struct pt_regs *regs);
index 53c0a82..60b19f5 100644 (file)
@@ -159,11 +159,12 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
 
 asmlinkage void kgdb_trap(unsigned long trap_level, struct pt_regs *regs)
 {
+       enum ctx_state prev_state = exception_enter();
        unsigned long flags;
 
        if (user_mode(regs)) {
                bad_trap(regs, trap_level);
-               return;
+               goto out;
        }
 
        flushw_all();
@@ -171,6 +172,8 @@ asmlinkage void kgdb_trap(unsigned long trap_level, struct pt_regs *regs)
        local_irq_save(flags);
        kgdb_handle_exception(0x172, SIGTRAP, 0, regs);
        local_irq_restore(flags);
+out:
+       exception_exit(prev_state);
 }
 
 int kgdb_arch_init(void)
index e722121..1b09735 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/module.h>
 #include <linux/kdebug.h>
 #include <linux/slab.h>
+#include <linux/context_tracking.h>
 #include <asm/signal.h>
 #include <asm/cacheflush.h>
 #include <asm/uaccess.h>
@@ -349,7 +350,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
        case KPROBE_HIT_SSDONE:
                /*
                 * We increment the nmissed count for accounting,
-                * we can also use npre/npostfault count for accouting
+                * we can also use npre/npostfault count for accounting
                 * these specific fault cases.
                 */
                kprobes_inc_nmissed_count(cur);
@@ -418,12 +419,14 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
 asmlinkage void __kprobes kprobe_trap(unsigned long trap_level,
                                      struct pt_regs *regs)
 {
+       enum ctx_state prev_state = exception_enter();
+
        BUG_ON(trap_level != 0x170 && trap_level != 0x171);
 
        if (user_mode(regs)) {
                local_irq_enable();
                bad_trap(regs, trap_level);
-               return;
+               goto out;
        }
 
        /* trap_level == 0x170 --> ta 0x70
@@ -433,6 +436,8 @@ asmlinkage void __kprobes kprobe_trap(unsigned long trap_level,
                       (trap_level == 0x170) ? "debug" : "debug_2",
                       regs, 0, trap_level, SIGTRAP) != NOTIFY_STOP)
                bad_trap(regs, trap_level);
+out:
+       exception_exit(prev_state);
 }
 
 /* Jprobes support.  */
index fde5a41..542e96a 100644 (file)
@@ -153,12 +153,19 @@ kvmap_dtlb_tsb4m_miss:
        /* Clear the PAGE_OFFSET top virtual bits, shift
         * down to get PFN, and make sure PFN is in range.
         */
-       sllx            %g4, 21, %g5
+661:   sllx            %g4, 0, %g5
+       .section        .page_offset_shift_patch, "ax"
+       .word           661b
+       .previous
 
        /* Check to see if we know about valid memory at the 4MB
         * chunk this physical address will reside within.
         */
-       srlx            %g5, 21 + 41, %g2
+661:   srlx            %g5, MAX_PHYS_ADDRESS_BITS, %g2
+       .section        .page_offset_shift_patch, "ax"
+       .word           661b
+       .previous
+
        brnz,pn         %g2, kvmap_dtlb_longpath
         nop
 
@@ -176,7 +183,11 @@ valid_addr_bitmap_patch:
        or              %g7, %lo(sparc64_valid_addr_bitmap), %g7
        .previous
 
-       srlx            %g5, 21 + 22, %g2
+661:   srlx            %g5, ILOG2_4MB, %g2
+       .section        .page_offset_shift_patch, "ax"
+       .word           661b
+       .previous
+
        srlx            %g2, 6, %g5
        and             %g2, 63, %g2
        sllx            %g5, 3, %g5
@@ -189,9 +200,18 @@ valid_addr_bitmap_patch:
 2:      sethi          %hi(kpte_linear_bitmap), %g2
 
        /* Get the 256MB physical address index. */
-       sllx            %g4, 21, %g5
+661:   sllx            %g4, 0, %g5
+       .section        .page_offset_shift_patch, "ax"
+       .word           661b
+       .previous
+
        or              %g2, %lo(kpte_linear_bitmap), %g2
-       srlx            %g5, 21 + 28, %g5
+
+661:   srlx            %g5, ILOG2_256MB, %g5
+       .section        .page_offset_shift_patch, "ax"
+       .word           661b
+       .previous
+
        and             %g5, (32 - 1), %g7
 
        /* Divide by 32 to get the offset into the bitmask.  */
index bc4d3f5..cb02145 100644 (file)
@@ -398,8 +398,8 @@ static void apb_fake_ranges(struct pci_dev *dev,
        apb_calc_first_last(map, &first, &last);
        res = bus->resource[1];
        res->flags = IORESOURCE_MEM;
-       region.start = (first << 21);
-       region.end = (last << 21) + ((1 << 21) - 1);
+       region.start = (first << 29);
+       region.end = (last << 29) + ((1 << 29) - 1);
        pcibios_bus_to_resource(dev, res, &region);
 }
 
index baebab2..32a280e 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/elfcore.h>
 #include <linux/sysrq.h>
 #include <linux/nmi.h>
+#include <linux/context_tracking.h>
 
 #include <asm/uaccess.h>
 #include <asm/page.h>
@@ -557,6 +558,7 @@ void fault_in_user_windows(void)
 
 barf:
        set_thread_wsaved(window + 1);
+       user_exit();
        do_exit(SIGILL);
 }
 
index 773c1f2..c13c9f2 100644 (file)
@@ -27,6 +27,7 @@
 #include <trace/syscall.h>
 #include <linux/compat.h>
 #include <linux/elf.h>
+#include <linux/context_tracking.h>
 
 #include <asm/asi.h>
 #include <asm/pgtable.h>
@@ -1066,6 +1067,9 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
        /* do the secure computing check first */
        secure_computing_strict(regs->u_regs[UREG_G1]);
 
+       if (test_thread_flag(TIF_NOHZ))
+               user_exit();
+
        if (test_thread_flag(TIF_SYSCALL_TRACE))
                ret = tracehook_report_syscall_entry(regs);
 
@@ -1086,6 +1090,9 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 
 asmlinkage void syscall_trace_leave(struct pt_regs *regs)
 {
+       if (test_thread_flag(TIF_NOHZ))
+               user_exit();
+
        audit_syscall_exit(regs);
 
        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
@@ -1093,4 +1100,7 @@ asmlinkage void syscall_trace_leave(struct pt_regs *regs)
 
        if (test_thread_flag(TIF_SYSCALL_TRACE))
                tracehook_report_syscall_exit(regs, 0);
+
+       if (test_thread_flag(TIF_NOHZ))
+               user_enter();
 }
index afa2a9e..39f0c66 100644 (file)
 #define                RTRAP_PSTATE_IRQOFF     (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV)
 #define                RTRAP_PSTATE_AG_IRQOFF  (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV|PSTATE_AG)
 
+#ifdef CONFIG_CONTEXT_TRACKING
+# define SCHEDULE_USER schedule_user
+#else
+# define SCHEDULE_USER schedule
+#endif
+
                .text
                .align                  32
 __handle_preemption:
-               call                    schedule
+               call                    SCHEDULE_USER
                 wrpr                   %g0, RTRAP_PSTATE, %pstate
                ba,pt                   %xcc, __handle_preemption_continue
                 wrpr                   %g0, RTRAP_PSTATE_IRQOFF, %pstate
@@ -306,12 +312,10 @@ to_kernel:
                 nop
                cmp                     %l4, 0
                bne,pn                  %xcc, kern_fpucheck
-                sethi                  %hi(PREEMPT_ACTIVE), %l6
-               stw                     %l6, [%g6 + TI_PRE_COUNT]
-               call                    schedule
+                nop
+               call                    preempt_schedule_irq
                 nop
                ba,pt                   %xcc, rtrap
-                stw                    %g0, [%g6 + TI_PRE_COUNT]
 #endif
 kern_fpucheck: ldub                    [%g6 + TI_FPDEPTH], %l5
                brz,pt                  %l5, rt_continue
index 35923e8..cd91d01 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/tty.h>
 #include <linux/binfmts.h>
 #include <linux/bitops.h>
+#include <linux/context_tracking.h>
 
 #include <asm/uaccess.h>
 #include <asm/ptrace.h>
@@ -43,6 +44,7 @@ asmlinkage void sparc64_set_context(struct pt_regs *regs)
 {
        struct ucontext __user *ucp = (struct ucontext __user *)
                regs->u_regs[UREG_I0];
+       enum ctx_state prev_state = exception_enter();
        mc_gregset_t __user *grp;
        unsigned long pc, npc, tstate;
        unsigned long fp, i7;
@@ -129,16 +131,19 @@ asmlinkage void sparc64_set_context(struct pt_regs *regs)
        }
        if (err)
                goto do_sigsegv;
-
+out:
+       exception_exit(prev_state);
        return;
 do_sigsegv:
        force_sig(SIGSEGV, current);
+       goto out;
 }
 
 asmlinkage void sparc64_get_context(struct pt_regs *regs)
 {
        struct ucontext __user *ucp = (struct ucontext __user *)
                regs->u_regs[UREG_I0];
+       enum ctx_state prev_state = exception_enter();
        mc_gregset_t __user *grp;
        mcontext_t __user *mcp;
        unsigned long fp, i7;
@@ -220,10 +225,12 @@ asmlinkage void sparc64_get_context(struct pt_regs *regs)
        }
        if (err)
                goto do_sigsegv;
-
+out:
+       exception_exit(prev_state);
        return;
 do_sigsegv:
        force_sig(SIGSEGV, current);
+       goto out;
 }
 
 struct rt_signal_frame {
@@ -528,11 +535,13 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
 
 void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long thread_info_flags)
 {
+       user_exit();
        if (thread_info_flags & _TIF_SIGPENDING)
                do_signal(regs, orig_i0);
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
        }
+       user_enter();
 }
 
index e142545..b66a533 100644 (file)
@@ -1399,8 +1399,13 @@ void __init smp_cpus_done(unsigned int max_cpus)
 
 void smp_send_reschedule(int cpu)
 {
-       xcall_deliver((u64) &xcall_receive_signal, 0, 0,
-                     cpumask_of(cpu));
+       if (cpu == smp_processor_id()) {
+               WARN_ON_ONCE(preemptible());
+               set_softint(1 << PIL_SMP_RECEIVE_SIGNAL);
+       } else {
+               xcall_deliver((u64) &xcall_receive_signal,
+                             0, 0, cpumask_of(cpu));
+       }
 }
 
 void __irq_entry smp_receive_signal_client(int irq, struct pt_regs *regs)
index bde867f..e0c09bf 100644 (file)
@@ -182,7 +182,7 @@ sun4v_tsb_miss_common:
        cmp     %g5, -1
        be,pt   %xcc, 80f
         nop
-       COMPUTE_TSB_PTR(%g5, %g4, HPAGE_SHIFT, %g2, %g7)
+       COMPUTE_TSB_PTR(%g5, %g4, REAL_HPAGE_SHIFT, %g2, %g7)
 
        /* That clobbered %g2, reload it.  */
        ldxa    [%g0] ASI_SCRATCHPAD, %g2
index 51561b8..beb0b5a 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/personality.h>
 #include <linux/random.h>
 #include <linux/export.h>
+#include <linux/context_tracking.h>
 
 #include <asm/uaccess.h>
 #include <asm/utrap.h>
@@ -39,9 +40,6 @@ asmlinkage unsigned long sys_getpagesize(void)
        return PAGE_SIZE;
 }
 
-#define VA_EXCLUDE_START (0x0000080000000000UL - (1UL << 32UL))
-#define VA_EXCLUDE_END   (0xfffff80000000000UL + (1UL << 32UL))
-
 /* Does addr --> addr+len fall within 4GB of the VA-space hole or
  * overflow past the end of the 64-bit address space?
  */
@@ -499,6 +497,7 @@ asmlinkage unsigned long c_sys_nis_syscall(struct pt_regs *regs)
 
 asmlinkage void sparc_breakpoint(struct pt_regs *regs)
 {
+       enum ctx_state prev_state = exception_enter();
        siginfo_t info;
 
        if (test_thread_flag(TIF_32BIT)) {
@@ -517,6 +516,7 @@ asmlinkage void sparc_breakpoint(struct pt_regs *regs)
 #ifdef DEBUG_SPARC_BREAKPOINT
        printk ("TRAP: Returning to space: PC=%lx nPC=%lx\n", regs->tpc, regs->tnpc);
 #endif
+       exception_exit(prev_state);
 }
 
 extern void check_pending(int signum);
index d950197..87729ff 100644 (file)
@@ -52,7 +52,7 @@ sys32_rt_sigreturn:
 #endif
        .align  32
 1:     ldx     [%g6 + TI_FLAGS], %l5
-       andcc   %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT), %g0
+       andcc   %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT|_TIF_NOHZ), %g0
        be,pt   %icc, rtrap
         nop
        call    syscall_trace_leave
@@ -184,7 +184,7 @@ linux_sparc_syscall32:
 
        srl     %i3, 0, %o3                             ! IEU0
        srl     %i2, 0, %o2                             ! IEU0  Group
-       andcc   %l0, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT), %g0
+       andcc   %l0, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT|_TIF_NOHZ), %g0
        bne,pn  %icc, linux_syscall_trace32             ! CTI
         mov    %i0, %l5                                ! IEU1
 5:     call    %l7                                     ! CTI   Group brk forced
@@ -207,7 +207,7 @@ linux_sparc_syscall:
 
        mov     %i3, %o3                                ! IEU1
        mov     %i4, %o4                                ! IEU0  Group
-       andcc   %l0, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT), %g0
+       andcc   %l0, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT|_TIF_NOHZ), %g0
        bne,pn  %icc, linux_syscall_trace               ! CTI   Group
         mov    %i0, %l5                                ! IEU0
 2:     call    %l7                                     ! CTI   Group brk forced
@@ -223,7 +223,7 @@ ret_sys_call:
 
        cmp     %o0, -ERESTART_RESTARTBLOCK
        bgeu,pn %xcc, 1f
-        andcc  %l0, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT), %g0
+        andcc  %l0, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT|_TIF_NOHZ), %g0
        ldx     [%sp + PTREGS_OFF + PT_V9_TNPC], %l1 ! pc = npc
 
 2:
index b3f833a..4ced92f 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/ftrace.h>
 #include <linux/reboot.h>
 #include <linux/gfp.h>
+#include <linux/context_tracking.h>
 
 #include <asm/smp.h>
 #include <asm/delay.h>
@@ -186,11 +187,12 @@ EXPORT_SYMBOL_GPL(unregister_dimm_printer);
 
 void spitfire_insn_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
 {
+       enum ctx_state prev_state = exception_enter();
        siginfo_t info;
 
        if (notify_die(DIE_TRAP, "instruction access exception", regs,
                       0, 0x8, SIGTRAP) == NOTIFY_STOP)
-               return;
+               goto out;
 
        if (regs->tstate & TSTATE_PRIV) {
                printk("spitfire_insn_access_exception: SFSR[%016lx] "
@@ -207,6 +209,8 @@ void spitfire_insn_access_exception(struct pt_regs *regs, unsigned long sfsr, un
        info.si_addr = (void __user *)regs->tpc;
        info.si_trapno = 0;
        force_sig_info(SIGSEGV, &info, current);
+out:
+       exception_exit(prev_state);
 }
 
 void spitfire_insn_access_exception_tl1(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
@@ -260,11 +264,12 @@ void sun4v_insn_access_exception_tl1(struct pt_regs *regs, unsigned long addr, u
 
 void spitfire_data_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
 {
+       enum ctx_state prev_state = exception_enter();
        siginfo_t info;
 
        if (notify_die(DIE_TRAP, "data access exception", regs,
                       0, 0x30, SIGTRAP) == NOTIFY_STOP)
-               return;
+               goto out;
 
        if (regs->tstate & TSTATE_PRIV) {
                /* Test if this comes from uaccess places. */
@@ -280,7 +285,7 @@ void spitfire_data_access_exception(struct pt_regs *regs, unsigned long sfsr, un
 #endif
                        regs->tpc = entry->fixup;
                        regs->tnpc = regs->tpc + 4;
-                       return;
+                       goto out;
                }
                /* Shit... */
                printk("spitfire_data_access_exception: SFSR[%016lx] "
@@ -294,6 +299,8 @@ void spitfire_data_access_exception(struct pt_regs *regs, unsigned long sfsr, un
        info.si_addr = (void __user *)sfar;
        info.si_trapno = 0;
        force_sig_info(SIGSEGV, &info, current);
+out:
+       exception_exit(prev_state);
 }
 
 void spitfire_data_access_exception_tl1(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
@@ -1994,6 +2001,7 @@ static void sun4v_log_error(struct pt_regs *regs, struct sun4v_error_entry *ent,
  */
 void sun4v_resum_error(struct pt_regs *regs, unsigned long offset)
 {
+       enum ctx_state prev_state = exception_enter();
        struct sun4v_error_entry *ent, local_copy;
        struct trap_per_cpu *tb;
        unsigned long paddr;
@@ -2022,12 +2030,14 @@ void sun4v_resum_error(struct pt_regs *regs, unsigned long offset)
                pr_info("Shutdown request, %u seconds...\n",
                        local_copy.err_secs);
                orderly_poweroff(true);
-               return;
+               goto out;
        }
 
        sun4v_log_error(regs, &local_copy, cpu,
                        KERN_ERR "RESUMABLE ERROR",
                        &sun4v_resum_oflow_cnt);
+out:
+       exception_exit(prev_state);
 }
 
 /* If we try to printk() we'll probably make matters worse, by trying
@@ -2152,7 +2162,7 @@ void hypervisor_tlbop_error_xcall(unsigned long err, unsigned long op)
               err, op);
 }
 
-void do_fpe_common(struct pt_regs *regs)
+static void do_fpe_common(struct pt_regs *regs)
 {
        if (regs->tstate & TSTATE_PRIV) {
                regs->tpc = regs->tnpc;
@@ -2188,23 +2198,28 @@ void do_fpe_common(struct pt_regs *regs)
 
 void do_fpieee(struct pt_regs *regs)
 {
+       enum ctx_state prev_state = exception_enter();
+
        if (notify_die(DIE_TRAP, "fpu exception ieee", regs,
                       0, 0x24, SIGFPE) == NOTIFY_STOP)
-               return;
+               goto out;
 
        do_fpe_common(regs);
+out:
+       exception_exit(prev_state);
 }
 
 extern int do_mathemu(struct pt_regs *, struct fpustate *, bool);
 
 void do_fpother(struct pt_regs *regs)
 {
+       enum ctx_state prev_state = exception_enter();
        struct fpustate *f = FPUSTATE;
        int ret = 0;
 
        if (notify_die(DIE_TRAP, "fpu exception other", regs,
                       0, 0x25, SIGFPE) == NOTIFY_STOP)
-               return;
+               goto out;
 
        switch ((current_thread_info()->xfsr[0] & 0x1c000)) {
        case (2 << 14): /* unfinished_FPop */
@@ -2213,17 +2228,20 @@ void do_fpother(struct pt_regs *regs)
                break;
        }
        if (ret)
-               return;
+               goto out;
        do_fpe_common(regs);
+out:
+       exception_exit(prev_state);
 }
 
 void do_tof(struct pt_regs *regs)
 {
+       enum ctx_state prev_state = exception_enter();
        siginfo_t info;
 
        if (notify_die(DIE_TRAP, "tagged arithmetic overflow", regs,
                       0, 0x26, SIGEMT) == NOTIFY_STOP)
-               return;
+               goto out;
 
        if (regs->tstate & TSTATE_PRIV)
                die_if_kernel("Penguin overflow trap from kernel mode", regs);
@@ -2237,15 +2255,18 @@ void do_tof(struct pt_regs *regs)
        info.si_addr = (void __user *)regs->tpc;
        info.si_trapno = 0;
        force_sig_info(SIGEMT, &info, current);
+out:
+       exception_exit(prev_state);
 }
 
 void do_div0(struct pt_regs *regs)
 {
+       enum ctx_state prev_state = exception_enter();
        siginfo_t info;
 
        if (notify_die(DIE_TRAP, "integer division by zero", regs,
                       0, 0x28, SIGFPE) == NOTIFY_STOP)
-               return;
+               goto out;
 
        if (regs->tstate & TSTATE_PRIV)
                die_if_kernel("TL0: Kernel divide by zero.", regs);
@@ -2259,6 +2280,8 @@ void do_div0(struct pt_regs *regs)
        info.si_addr = (void __user *)regs->tpc;
        info.si_trapno = 0;
        force_sig_info(SIGFPE, &info, current);
+out:
+       exception_exit(prev_state);
 }
 
 static void instruction_dump(unsigned int *pc)
@@ -2415,6 +2438,7 @@ extern int handle_ldf_stq(u32 insn, struct pt_regs *regs);
 
 void do_illegal_instruction(struct pt_regs *regs)
 {
+       enum ctx_state prev_state = exception_enter();
        unsigned long pc = regs->tpc;
        unsigned long tstate = regs->tstate;
        u32 insn;
@@ -2422,7 +2446,7 @@ void do_illegal_instruction(struct pt_regs *regs)
 
        if (notify_die(DIE_TRAP, "illegal instruction", regs,
                       0, 0x10, SIGILL) == NOTIFY_STOP)
-               return;
+               goto out;
 
        if (tstate & TSTATE_PRIV)
                die_if_kernel("Kernel illegal instruction", regs);
@@ -2431,14 +2455,14 @@ void do_illegal_instruction(struct pt_regs *regs)
        if (get_user(insn, (u32 __user *) pc) != -EFAULT) {
                if ((insn & 0xc1ffc000) == 0x81700000) /* POPC */ {
                        if (handle_popc(insn, regs))
-                               return;
+                               goto out;
                } else if ((insn & 0xc1580000) == 0xc1100000) /* LDQ/STQ */ {
                        if (handle_ldf_stq(insn, regs))
-                               return;
+                               goto out;
                } else if (tlb_type == hypervisor) {
                        if ((insn & VIS_OPCODE_MASK) == VIS_OPCODE_VAL) {
                                if (!vis_emul(regs, insn))
-                                       return;
+                                       goto out;
                        } else {
                                struct fpustate *f = FPUSTATE;
 
@@ -2448,7 +2472,7 @@ void do_illegal_instruction(struct pt_regs *regs)
                                 * Trap in the %fsr to unimplemented_FPop.
                                 */
                                if (do_mathemu(regs, f, true))
-                                       return;
+                                       goto out;
                        }
                }
        }
@@ -2458,21 +2482,24 @@ void do_illegal_instruction(struct pt_regs *regs)
        info.si_addr = (void __user *)pc;
        info.si_trapno = 0;
        force_sig_info(SIGILL, &info, current);
+out:
+       exception_exit(prev_state);
 }
 
 extern void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn);
 
 void mem_address_unaligned(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
 {
+       enum ctx_state prev_state = exception_enter();
        siginfo_t info;
 
        if (notify_die(DIE_TRAP, "memory address unaligned", regs,
                       0, 0x34, SIGSEGV) == NOTIFY_STOP)
-               return;
+               goto out;
 
        if (regs->tstate & TSTATE_PRIV) {
                kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc));
-               return;
+               goto out;
        }
        info.si_signo = SIGBUS;
        info.si_errno = 0;
@@ -2480,6 +2507,8 @@ void mem_address_unaligned(struct pt_regs *regs, unsigned long sfar, unsigned lo
        info.si_addr = (void __user *)sfar;
        info.si_trapno = 0;
        force_sig_info(SIGBUS, &info, current);
+out:
+       exception_exit(prev_state);
 }
 
 void sun4v_do_mna(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
@@ -2504,11 +2533,12 @@ void sun4v_do_mna(struct pt_regs *regs, unsigned long addr, unsigned long type_c
 
 void do_privop(struct pt_regs *regs)
 {
+       enum ctx_state prev_state = exception_enter();
        siginfo_t info;
 
        if (notify_die(DIE_TRAP, "privileged operation", regs,
                       0, 0x11, SIGILL) == NOTIFY_STOP)
-               return;
+               goto out;
 
        if (test_thread_flag(TIF_32BIT)) {
                regs->tpc &= 0xffffffff;
@@ -2520,6 +2550,8 @@ void do_privop(struct pt_regs *regs)
        info.si_addr = (void __user *)regs->tpc;
        info.si_trapno = 0;
        force_sig_info(SIGILL, &info, current);
+out:
+       exception_exit(prev_state);
 }
 
 void do_privact(struct pt_regs *regs)
@@ -2530,99 +2562,116 @@ void do_privact(struct pt_regs *regs)
 /* Trap level 1 stuff or other traps we should never see... */
 void do_cee(struct pt_regs *regs)
 {
+       exception_enter();
        die_if_kernel("TL0: Cache Error Exception", regs);
 }
 
 void do_cee_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: Cache Error Exception", regs);
 }
 
 void do_dae_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: Data Access Exception", regs);
 }
 
 void do_iae_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: Instruction Access Exception", regs);
 }
 
 void do_div0_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: DIV0 Exception", regs);
 }
 
 void do_fpdis_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: FPU Disabled", regs);
 }
 
 void do_fpieee_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: FPU IEEE Exception", regs);
 }
 
 void do_fpother_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: FPU Other Exception", regs);
 }
 
 void do_ill_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: Illegal Instruction Exception", regs);
 }
 
 void do_irq_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: IRQ Exception", regs);
 }
 
 void do_lddfmna_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: LDDF Exception", regs);
 }
 
 void do_stdfmna_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: STDF Exception", regs);
 }
 
 void do_paw(struct pt_regs *regs)
 {
+       exception_enter();
        die_if_kernel("TL0: Phys Watchpoint Exception", regs);
 }
 
 void do_paw_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: Phys Watchpoint Exception", regs);
 }
 
 void do_vaw(struct pt_regs *regs)
 {
+       exception_enter();
        die_if_kernel("TL0: Virt Watchpoint Exception", regs);
 }
 
 void do_vaw_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: Virt Watchpoint Exception", regs);
 }
 
 void do_tof_tl1(struct pt_regs *regs)
 {
+       exception_enter();
        dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
        die_if_kernel("TL1: Tag Overflow Exception", regs);
 }
index a313e4a..14158d4 100644 (file)
@@ -75,7 +75,7 @@ tsb_miss_page_table_walk:
        mov             512, %g7
        andn            %g5, 0x7, %g5
        sllx            %g7, %g6, %g7
-       srlx            %g4, HPAGE_SHIFT, %g6
+       srlx            %g4, REAL_HPAGE_SHIFT, %g6
        sub             %g7, 1, %g7
        and             %g6, %g7, %g6
        sllx            %g6, 4, %g6
index 8201c25..3c1a7cb 100644 (file)
 #include <linux/bitops.h>
 #include <linux/perf_event.h>
 #include <linux/ratelimit.h>
+#include <linux/context_tracking.h>
 #include <asm/fpumacro.h>
 #include <asm/cacheflush.h>
 
+#include "entry.h"
+
 enum direction {
        load,    /* ld, ldd, ldh, ldsh */
        store,   /* st, std, sth, stsh */
@@ -418,9 +421,6 @@ int handle_popc(u32 insn, struct pt_regs *regs)
 
 extern void do_fpother(struct pt_regs *regs);
 extern void do_privact(struct pt_regs *regs);
-extern void spitfire_data_access_exception(struct pt_regs *regs,
-                                          unsigned long sfsr,
-                                          unsigned long sfar);
 extern void sun4v_data_access_exception(struct pt_regs *regs,
                                        unsigned long addr,
                                        unsigned long type_ctx);
@@ -578,6 +578,7 @@ void handle_ld_nf(u32 insn, struct pt_regs *regs)
 
 void handle_lddfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
 {
+       enum ctx_state prev_state = exception_enter();
        unsigned long pc = regs->tpc;
        unsigned long tstate = regs->tstate;
        u32 insn;
@@ -632,13 +633,16 @@ daex:
                        sun4v_data_access_exception(regs, sfar, sfsr);
                else
                        spitfire_data_access_exception(regs, sfsr, sfar);
-               return;
+               goto out;
        }
        advance(regs);
+out:
+       exception_exit(prev_state);
 }
 
 void handle_stdfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
 {
+       enum ctx_state prev_state = exception_enter();
        unsigned long pc = regs->tpc;
        unsigned long tstate = regs->tstate;
        u32 insn;
@@ -680,7 +684,9 @@ daex:
                        sun4v_data_access_exception(regs, sfar, sfsr);
                else
                        spitfire_data_access_exception(regs, sfsr, sfar);
-               return;
+               goto out;
        }
        advance(regs);
+out:
+       exception_exit(prev_state);
 }
index 0bacceb..932ff90 100644 (file)
@@ -122,6 +122,11 @@ SECTIONS
                *(.swapper_4m_tsb_phys_patch)
                __swapper_4m_tsb_phys_patch_end = .;
        }
+       .page_offset_shift_patch : {
+               __page_offset_shift_patch = .;
+               *(.page_offset_shift_patch)
+               __page_offset_shift_patch_end = .;
+       }
        .popc_3insn_patch : {
                __popc_3insn_patch = .;
                *(.popc_3insn_patch)
index 77e531f..46272df 100644 (file)
@@ -37,10 +37,10 @@ _clear_page:                /* %o0=dest */
        .globl          clear_user_page
 clear_user_page:       /* %o0=dest, %o1=vaddr */
        lduw            [%g6 + TI_PRE_COUNT], %o2
-       sethi           %uhi(PAGE_OFFSET), %g2
+       sethi           %hi(PAGE_OFFSET), %g2
        sethi           %hi(PAGE_SIZE), %o4
 
-       sllx            %g2, 32, %g2
+       ldx             [%g2 + %lo(PAGE_OFFSET)], %g2
        sethi           %hi(PAGE_KERNEL_LOCKED), %g3
 
        ldx             [%g3 + %lo(PAGE_KERNEL_LOCKED)], %g3
index 4d2df32..dd16c61 100644 (file)
        .type           copy_user_page,#function
 copy_user_page:                /* %o0=dest, %o1=src, %o2=vaddr */
        lduw            [%g6 + TI_PRE_COUNT], %o4
-       sethi           %uhi(PAGE_OFFSET), %g2
+       sethi           %hi(PAGE_OFFSET), %g2
        sethi           %hi(PAGE_SIZE), %o3
 
-       sllx            %g2, 32, %g2
+       ldx             [%g2 + %lo(PAGE_OFFSET)], %g2
        sethi           %hi(PAGE_KERNEL_LOCKED), %g3
 
        ldx             [%g3 + %lo(PAGE_KERNEL_LOCKED)], %g3
index 2ebec26..69bb818 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/kprobes.h>
 #include <linux/kdebug.h>
 #include <linux/percpu.h>
+#include <linux/context_tracking.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -272,6 +273,7 @@ static void noinline __kprobes bogus_32bit_fault_address(struct pt_regs *regs,
 
 asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)
 {
+       enum ctx_state prev_state = exception_enter();
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma;
        unsigned int insn = 0;
@@ -282,7 +284,7 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)
        fault_code = get_thread_fault_code();
 
        if (notify_page_fault(regs))
-               return;
+               goto exit_exception;
 
        si_code = SEGV_MAPERR;
        address = current_thread_info()->fault_address;
@@ -313,7 +315,7 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)
                        /* Valid, no problems... */
                } else {
                        bad_kernel_pc(regs, address);
-                       return;
+                       goto exit_exception;
                }
        } else
                flags |= FAULT_FLAG_USER;
@@ -430,7 +432,7 @@ good_area:
        fault = handle_mm_fault(mm, vma, address, flags);
 
        if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
-               return;
+               goto exit_exception;
 
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
@@ -482,6 +484,8 @@ good_area:
 
        }
 #endif
+exit_exception:
+       exception_exit(prev_state);
        return;
 
        /*
@@ -494,7 +498,7 @@ bad_area:
 
 handle_kernel_fault:
        do_kernel_fault(regs, si_code, fault_code, insn, address);
-       return;
+       goto exit_exception;
 
 /*
  * We ran out of memory, or some other thing happened to us that made
@@ -505,7 +509,7 @@ out_of_memory:
        up_read(&mm->mmap_sem);
        if (!(regs->tstate & TSTATE_PRIV)) {
                pagefault_out_of_memory();
-               return;
+               goto exit_exception;
        }
        goto handle_kernel_fault;
 
index 01ee23d..c4d3da6 100644 (file)
@@ -71,13 +71,12 @@ static int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
                        int *nr)
 {
        struct page *head, *page, *tail;
-       u32 mask;
        int refs;
 
-       mask = PMD_HUGE_PRESENT;
-       if (write)
-               mask |= PMD_HUGE_WRITE;
-       if ((pmd_val(pmd) & mask) != mask)
+       if (!pmd_large(pmd))
+               return 0;
+
+       if (write && !pmd_write(pmd))
                return 0;
 
        refs = 0;
index 9639964..3096317 100644 (file)
@@ -21,8 +21,6 @@
 /* Slightly simplified from the non-hugepage variant because by
  * definition we don't have to worry about any page coloring stuff
  */
-#define VA_EXCLUDE_START (0x0000080000000000UL - (1UL << 32UL))
-#define VA_EXCLUDE_END   (0xfffff80000000000UL + (1UL << 32UL))
 
 static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *filp,
                                                        unsigned long addr,
index ed82eda..5322e53 100644 (file)
@@ -354,7 +354,7 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *
 
 #if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
        if (mm->context.huge_pte_count && is_hugetlb_pte(pte))
-               __update_mmu_tsb_insert(mm, MM_TSB_HUGE, HPAGE_SHIFT,
+               __update_mmu_tsb_insert(mm, MM_TSB_HUGE, REAL_HPAGE_SHIFT,
                                        address, pte_val(pte));
        else
 #endif
@@ -1557,6 +1557,96 @@ unsigned long __init find_ecache_flush_span(unsigned long size)
        return ~0UL;
 }
 
+unsigned long PAGE_OFFSET;
+EXPORT_SYMBOL(PAGE_OFFSET);
+
+static void __init page_offset_shift_patch_one(unsigned int *insn, unsigned long phys_bits)
+{
+       unsigned long final_shift;
+       unsigned int val = *insn;
+       unsigned int cnt;
+
+       /* We are patching in ilog2(max_supported_phys_address), and
+        * we are doing so in a manner similar to a relocation addend.
+        * That is, we are adding the shift value to whatever value
+        * is in the shift instruction count field already.
+        */
+       cnt = (val & 0x3f);
+       val &= ~0x3f;
+
+       /* If we are trying to shift >= 64 bits, clear the destination
+        * register.  This can happen when phys_bits ends up being equal
+        * to MAX_PHYS_ADDRESS_BITS.
+        */
+       final_shift = (cnt + (64 - phys_bits));
+       if (final_shift >= 64) {
+               unsigned int rd = (val >> 25) & 0x1f;
+
+               val = 0x80100000 | (rd << 25);
+       } else {
+               val |= final_shift;
+       }
+       *insn = val;
+
+       __asm__ __volatile__("flush     %0"
+                            : /* no outputs */
+                            : "r" (insn));
+}
+
+static void __init page_offset_shift_patch(unsigned long phys_bits)
+{
+       extern unsigned int __page_offset_shift_patch;
+       extern unsigned int __page_offset_shift_patch_end;
+       unsigned int *p;
+
+       p = &__page_offset_shift_patch;
+       while (p < &__page_offset_shift_patch_end) {
+               unsigned int *insn = (unsigned int *)(unsigned long)*p;
+
+               page_offset_shift_patch_one(insn, phys_bits);
+
+               p++;
+       }
+}
+
+static void __init setup_page_offset(void)
+{
+       unsigned long max_phys_bits = 40;
+
+       if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+               max_phys_bits = 42;
+       } else if (tlb_type == hypervisor) {
+               switch (sun4v_chip_type) {
+               case SUN4V_CHIP_NIAGARA1:
+               case SUN4V_CHIP_NIAGARA2:
+                       max_phys_bits = 39;
+                       break;
+               case SUN4V_CHIP_NIAGARA3:
+                       max_phys_bits = 43;
+                       break;
+               case SUN4V_CHIP_NIAGARA4:
+               case SUN4V_CHIP_NIAGARA5:
+               case SUN4V_CHIP_SPARC64X:
+               default:
+                       max_phys_bits = 47;
+                       break;
+               }
+       }
+
+       if (max_phys_bits > MAX_PHYS_ADDRESS_BITS) {
+               prom_printf("MAX_PHYS_ADDRESS_BITS is too small, need %lu\n",
+                           max_phys_bits);
+               prom_halt();
+       }
+
+       PAGE_OFFSET = PAGE_OFFSET_BY_BITS(max_phys_bits);
+
+       pr_info("PAGE_OFFSET is 0x%016lx (max_phys_bits == %lu)\n",
+               PAGE_OFFSET, max_phys_bits);
+
+       page_offset_shift_patch(max_phys_bits);
+}
+
 static void __init tsb_phys_patch(void)
 {
        struct tsb_ldquad_phys_patch_entry *pquad;
@@ -1722,7 +1812,7 @@ static void __init sun4v_linear_pte_xor_finalize(void)
 #ifndef CONFIG_DEBUG_PAGEALLOC
        if (cpu_pgsz_mask & HV_PGSZ_MASK_256MB) {
                kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZ256MB_4V) ^
-                       0xfffff80000000000UL;
+                       PAGE_OFFSET;
                kern_linear_pte_xor[1] |= (_PAGE_CP_4V | _PAGE_CV_4V |
                                           _PAGE_P_4V | _PAGE_W_4V);
        } else {
@@ -1731,7 +1821,7 @@ static void __init sun4v_linear_pte_xor_finalize(void)
 
        if (cpu_pgsz_mask & HV_PGSZ_MASK_2GB) {
                kern_linear_pte_xor[2] = (_PAGE_VALID | _PAGE_SZ2GB_4V) ^
-                       0xfffff80000000000UL;
+                       PAGE_OFFSET;
                kern_linear_pte_xor[2] |= (_PAGE_CP_4V | _PAGE_CV_4V |
                                           _PAGE_P_4V | _PAGE_W_4V);
        } else {
@@ -1740,7 +1830,7 @@ static void __init sun4v_linear_pte_xor_finalize(void)
 
        if (cpu_pgsz_mask & HV_PGSZ_MASK_16GB) {
                kern_linear_pte_xor[3] = (_PAGE_VALID | _PAGE_SZ16GB_4V) ^
-                       0xfffff80000000000UL;
+                       PAGE_OFFSET;
                kern_linear_pte_xor[3] |= (_PAGE_CP_4V | _PAGE_CV_4V |
                                           _PAGE_P_4V | _PAGE_W_4V);
        } else {
@@ -1752,7 +1842,7 @@ static void __init sun4v_linear_pte_xor_finalize(void)
 /* paging_init() sets up the page tables */
 
 static unsigned long last_valid_pfn;
-pgd_t swapper_pg_dir[2048];
+pgd_t swapper_pg_dir[PTRS_PER_PGD];
 
 static void sun4u_pgprot_init(void);
 static void sun4v_pgprot_init(void);
@@ -1763,6 +1853,8 @@ void __init paging_init(void)
        unsigned long real_end, i;
        int node;
 
+       setup_page_offset();
+
        /* These build time checkes make sure that the dcache_dirty_cpu()
         * page->flags usage will work.
         *
@@ -2261,10 +2353,10 @@ static void __init sun4u_pgprot_init(void)
                     __ACCESS_BITS_4U | _PAGE_E_4U);
 
 #ifdef CONFIG_DEBUG_PAGEALLOC
-       kern_linear_pte_xor[0] = _PAGE_VALID ^ 0xfffff80000000000UL;
+       kern_linear_pte_xor[0] = _PAGE_VALID ^ PAGE_OFFSET;
 #else
        kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4U) ^
-               0xfffff80000000000UL;
+               PAGE_OFFSET;
 #endif
        kern_linear_pte_xor[0] |= (_PAGE_CP_4U | _PAGE_CV_4U |
                                   _PAGE_P_4U | _PAGE_W_4U);
@@ -2308,10 +2400,10 @@ static void __init sun4v_pgprot_init(void)
        _PAGE_CACHE = _PAGE_CACHE_4V;
 
 #ifdef CONFIG_DEBUG_PAGEALLOC
-       kern_linear_pte_xor[0] = _PAGE_VALID ^ 0xfffff80000000000UL;
+       kern_linear_pte_xor[0] = _PAGE_VALID ^ PAGE_OFFSET;
 #else
        kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4V) ^
-               0xfffff80000000000UL;
+               PAGE_OFFSET;
 #endif
        kern_linear_pte_xor[0] |= (_PAGE_CP_4V | _PAGE_CV_4V |
                                   _PAGE_P_4V | _PAGE_W_4V);
@@ -2455,53 +2547,13 @@ void __flush_tlb_all(void)
                             : : "r" (pstate));
 }
 
-static pte_t *get_from_cache(struct mm_struct *mm)
-{
-       struct page *page;
-       pte_t *ret;
-
-       spin_lock(&mm->page_table_lock);
-       page = mm->context.pgtable_page;
-       ret = NULL;
-       if (page) {
-               void *p = page_address(page);
-
-               mm->context.pgtable_page = NULL;
-
-               ret = (pte_t *) (p + (PAGE_SIZE / 2));
-       }
-       spin_unlock(&mm->page_table_lock);
-
-       return ret;
-}
-
-static struct page *__alloc_for_cache(struct mm_struct *mm)
-{
-       struct page *page = alloc_page(GFP_KERNEL | __GFP_NOTRACK |
-                                      __GFP_REPEAT | __GFP_ZERO);
-
-       if (page) {
-               spin_lock(&mm->page_table_lock);
-               if (!mm->context.pgtable_page) {
-                       atomic_set(&page->_count, 2);
-                       mm->context.pgtable_page = page;
-               }
-               spin_unlock(&mm->page_table_lock);
-       }
-       return page;
-}
-
 pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
                            unsigned long address)
 {
-       struct page *page;
-       pte_t *pte;
-
-       pte = get_from_cache(mm);
-       if (pte)
-               return pte;
+       struct page *page = alloc_page(GFP_KERNEL | __GFP_NOTRACK |
+                                      __GFP_REPEAT | __GFP_ZERO);
+       pte_t *pte = NULL;
 
-       page = __alloc_for_cache(mm);
        if (page)
                pte = (pte_t *) page_address(page);
 
@@ -2511,36 +2563,28 @@ pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
 pgtable_t pte_alloc_one(struct mm_struct *mm,
                        unsigned long address)
 {
-       struct page *page;
-       pte_t *pte;
-
-       pte = get_from_cache(mm);
-       if (pte)
-               return pte;
-
-       page = __alloc_for_cache(mm);
-       if (page) {
-               pgtable_page_ctor(page);
-               pte = (pte_t *) page_address(page);
+       struct page *page = alloc_page(GFP_KERNEL | __GFP_NOTRACK |
+                                      __GFP_REPEAT | __GFP_ZERO);
+       if (!page)
+               return NULL;
+       if (!pgtable_page_ctor(page)) {
+               free_hot_cold_page(page, 0);
+               return NULL;
        }
-
-       return pte;
+       return (pte_t *) page_address(page);
 }
 
 void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 {
-       struct page *page = virt_to_page(pte);
-       if (put_page_testzero(page))
-               free_hot_cold_page(page, 0);
+       free_page((unsigned long)pte);
 }
 
 static void __pte_free(pgtable_t pte)
 {
        struct page *page = virt_to_page(pte);
-       if (put_page_testzero(page)) {
-               pgtable_page_dtor(page);
-               free_hot_cold_page(page, 0);
-       }
+
+       pgtable_page_dtor(page);
+       __free_page(page);
 }
 
 void pte_free(struct mm_struct *mm, pgtable_t pte)
@@ -2557,124 +2601,27 @@ void pgtable_free(void *table, bool is_page)
 }
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-static pmd_t pmd_set_protbits(pmd_t pmd, pgprot_t pgprot, bool for_modify)
-{
-       if (pgprot_val(pgprot) & _PAGE_VALID)
-               pmd_val(pmd) |= PMD_HUGE_PRESENT;
-       if (tlb_type == hypervisor) {
-               if (pgprot_val(pgprot) & _PAGE_WRITE_4V)
-                       pmd_val(pmd) |= PMD_HUGE_WRITE;
-               if (pgprot_val(pgprot) & _PAGE_EXEC_4V)
-                       pmd_val(pmd) |= PMD_HUGE_EXEC;
-
-               if (!for_modify) {
-                       if (pgprot_val(pgprot) & _PAGE_ACCESSED_4V)
-                               pmd_val(pmd) |= PMD_HUGE_ACCESSED;
-                       if (pgprot_val(pgprot) & _PAGE_MODIFIED_4V)
-                               pmd_val(pmd) |= PMD_HUGE_DIRTY;
-               }
-       } else {
-               if (pgprot_val(pgprot) & _PAGE_WRITE_4U)
-                       pmd_val(pmd) |= PMD_HUGE_WRITE;
-               if (pgprot_val(pgprot) & _PAGE_EXEC_4U)
-                       pmd_val(pmd) |= PMD_HUGE_EXEC;
-
-               if (!for_modify) {
-                       if (pgprot_val(pgprot) & _PAGE_ACCESSED_4U)
-                               pmd_val(pmd) |= PMD_HUGE_ACCESSED;
-                       if (pgprot_val(pgprot) & _PAGE_MODIFIED_4U)
-                               pmd_val(pmd) |= PMD_HUGE_DIRTY;
-               }
-       }
-
-       return pmd;
-}
-
-pmd_t pfn_pmd(unsigned long page_nr, pgprot_t pgprot)
-{
-       pmd_t pmd;
-
-       pmd_val(pmd) = (page_nr << ((PAGE_SHIFT - PMD_PADDR_SHIFT)));
-       pmd_val(pmd) |= PMD_ISHUGE;
-       pmd = pmd_set_protbits(pmd, pgprot, false);
-       return pmd;
-}
-
-pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
-{
-       pmd_val(pmd) &= ~(PMD_HUGE_PRESENT |
-                         PMD_HUGE_WRITE |
-                         PMD_HUGE_EXEC);
-       pmd = pmd_set_protbits(pmd, newprot, true);
-       return pmd;
-}
-
-pgprot_t pmd_pgprot(pmd_t entry)
-{
-       unsigned long pte = 0;
-
-       if (pmd_val(entry) & PMD_HUGE_PRESENT)
-               pte |= _PAGE_VALID;
-
-       if (tlb_type == hypervisor) {
-               if (pmd_val(entry) & PMD_HUGE_PRESENT)
-                       pte |= _PAGE_PRESENT_4V;
-               if (pmd_val(entry) & PMD_HUGE_EXEC)
-                       pte |= _PAGE_EXEC_4V;
-               if (pmd_val(entry) & PMD_HUGE_WRITE)
-                       pte |= _PAGE_W_4V;
-               if (pmd_val(entry) & PMD_HUGE_ACCESSED)
-                       pte |= _PAGE_ACCESSED_4V;
-               if (pmd_val(entry) & PMD_HUGE_DIRTY)
-                       pte |= _PAGE_MODIFIED_4V;
-               pte |= _PAGE_CP_4V|_PAGE_CV_4V;
-       } else {
-               if (pmd_val(entry) & PMD_HUGE_PRESENT)
-                       pte |= _PAGE_PRESENT_4U;
-               if (pmd_val(entry) & PMD_HUGE_EXEC)
-                       pte |= _PAGE_EXEC_4U;
-               if (pmd_val(entry) & PMD_HUGE_WRITE)
-                       pte |= _PAGE_W_4U;
-               if (pmd_val(entry) & PMD_HUGE_ACCESSED)
-                       pte |= _PAGE_ACCESSED_4U;
-               if (pmd_val(entry) & PMD_HUGE_DIRTY)
-                       pte |= _PAGE_MODIFIED_4U;
-               pte |= _PAGE_CP_4U|_PAGE_CV_4U;
-       }
-
-       return __pgprot(pte);
-}
-
 void update_mmu_cache_pmd(struct vm_area_struct *vma, unsigned long addr,
                          pmd_t *pmd)
 {
        unsigned long pte, flags;
        struct mm_struct *mm;
        pmd_t entry = *pmd;
-       pgprot_t prot;
 
        if (!pmd_large(entry) || !pmd_young(entry))
                return;
 
-       pte = (pmd_val(entry) & ~PMD_HUGE_PROTBITS);
-       pte <<= PMD_PADDR_SHIFT;
-       pte |= _PAGE_VALID;
-
-       prot = pmd_pgprot(entry);
-
-       if (tlb_type == hypervisor)
-               pgprot_val(prot) |= _PAGE_SZHUGE_4V;
-       else
-               pgprot_val(prot) |= _PAGE_SZHUGE_4U;
+       pte = pmd_val(entry);
 
-       pte |= pgprot_val(prot);
+       /* We are fabricating 8MB pages using 4MB real hw pages.  */
+       pte |= (addr & (1UL << REAL_HPAGE_SHIFT));
 
        mm = vma->vm_mm;
 
        spin_lock_irqsave(&mm->context.lock, flags);
 
        if (mm->context.tsb_block[MM_TSB_HUGE].tsb != NULL)
-               __update_mmu_tsb_insert(mm, MM_TSB_HUGE, HPAGE_SHIFT,
+               __update_mmu_tsb_insert(mm, MM_TSB_HUGE, REAL_HPAGE_SHIFT,
                                        addr, pte);
 
        spin_unlock_irqrestore(&mm->context.lock, flags);
index 0661aa6..5d3782d 100644 (file)
@@ -1,11 +1,13 @@
 #ifndef _SPARC64_MM_INIT_H
 #define _SPARC64_MM_INIT_H
 
+#include <asm/page.h>
+
 /* Most of the symbols in this file are defined in init.c and
  * marked non-static so that assembler code can get at them.
  */
 
-#define MAX_PHYS_ADDRESS       (1UL << 41UL)
+#define MAX_PHYS_ADDRESS       (1UL << MAX_PHYS_ADDRESS_BITS)
 #define KPTE_BITMAP_CHUNK_SZ           (256UL * 1024UL * 1024UL)
 #define KPTE_BITMAP_BYTES      \
        ((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 4)
index 5d721df..869023a 100644 (file)
@@ -345,7 +345,10 @@ pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address)
        if ((pte = (unsigned long)pte_alloc_one_kernel(mm, address)) == 0)
                return NULL;
        page = pfn_to_page(__nocache_pa(pte) >> PAGE_SHIFT);
-       pgtable_page_ctor(page);
+       if (!pgtable_page_ctor(page)) {
+               __free_page(page);
+               return NULL;
+       }
        return page;
 }
 
index 7a91f28..ad3bf4b 100644 (file)
@@ -161,8 +161,8 @@ void set_pmd_at(struct mm_struct *mm, unsigned long addr,
        if (mm == &init_mm)
                return;
 
-       if ((pmd_val(pmd) ^ pmd_val(orig)) & PMD_ISHUGE) {
-               if (pmd_val(pmd) & PMD_ISHUGE)
+       if ((pmd_val(pmd) ^ pmd_val(orig)) & _PAGE_PMD_HUGE) {
+               if (pmd_val(pmd) & _PAGE_PMD_HUGE)
                        mm->context.huge_pte_count++;
                else
                        mm->context.huge_pte_count--;
@@ -178,13 +178,16 @@ void set_pmd_at(struct mm_struct *mm, unsigned long addr,
        }
 
        if (!pmd_none(orig)) {
-               bool exec = ((pmd_val(orig) & PMD_HUGE_EXEC) != 0);
+               pte_t orig_pte = __pte(pmd_val(orig));
+               bool exec = pte_exec(orig_pte);
 
                addr &= HPAGE_MASK;
-               if (pmd_val(orig) & PMD_ISHUGE)
+               if (pmd_trans_huge(orig)) {
                        tlb_batch_add_one(mm, addr, exec);
-               else
+                       tlb_batch_add_one(mm, addr + REAL_HPAGE_SIZE, exec);
+               } else {
                        tlb_batch_pmd_scan(mm, addr, orig, exec);
+               }
        }
 }
 
@@ -196,11 +199,11 @@ void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
        assert_spin_locked(&mm->page_table_lock);
 
        /* FIFO */
-       if (!mm->pmd_huge_pte)
+       if (!pmd_huge_pte(mm, pmdp))
                INIT_LIST_HEAD(lh);
        else
-               list_add(lh, (struct list_head *) mm->pmd_huge_pte);
-       mm->pmd_huge_pte = pgtable;
+               list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp));
+       pmd_huge_pte(mm, pmdp) = pgtable;
 }
 
 pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
@@ -211,12 +214,12 @@ pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
        assert_spin_locked(&mm->page_table_lock);
 
        /* FIFO */
-       pgtable = mm->pmd_huge_pte;
+       pgtable = pmd_huge_pte(mm, pmdp);
        lh = (struct list_head *) pgtable;
        if (list_empty(lh))
-               mm->pmd_huge_pte = NULL;
+               pmd_huge_pte(mm, pmdp) = NULL;
        else {
-               mm->pmd_huge_pte = (pgtable_t) lh->next;
+               pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next;
                list_del(lh);
        }
        pte_val(pgtable[0]) = 0;
index 2cc3bce..3b3a360 100644 (file)
@@ -87,7 +87,7 @@ void flush_tsb_user(struct tlb_batch *tb)
                nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
                if (tlb_type == cheetah_plus || tlb_type == hypervisor)
                        base = __pa(base);
-               __flush_tsb_one(tb, HPAGE_SHIFT, base, nentries);
+               __flush_tsb_one(tb, REAL_HPAGE_SHIFT, base, nentries);
        }
 #endif
        spin_unlock_irqrestore(&mm->context.lock, flags);
@@ -111,7 +111,7 @@ void flush_tsb_user_page(struct mm_struct *mm, unsigned long vaddr)
                nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
                if (tlb_type == cheetah_plus || tlb_type == hypervisor)
                        base = __pa(base);
-               __flush_tsb_one_entry(base, vaddr, HPAGE_SHIFT, nentries);
+               __flush_tsb_one_entry(base, vaddr, REAL_HPAGE_SHIFT, nentries);
        }
 #endif
        spin_unlock_irqrestore(&mm->context.lock, flags);
@@ -472,8 +472,6 @@ int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
        mm->context.huge_pte_count = 0;
 #endif
 
-       mm->context.pgtable_page = NULL;
-
        /* copy_mm() copies over the parent's mm_struct before calling
         * us, so we need to zero out the TSB pointer or else tsb_grow()
         * will be confused and think there is an older TSB to free up.
@@ -512,17 +510,10 @@ static void tsb_destroy_one(struct tsb_config *tp)
 void destroy_context(struct mm_struct *mm)
 {
        unsigned long flags, i;
-       struct page *page;
 
        for (i = 0; i < MM_NUM_TSBS; i++)
                tsb_destroy_one(&mm->context.tsb_block[i]);
 
-       page = mm->context.pgtable_page;
-       if (page && put_page_testzero(page)) {
-               pgtable_page_dtor(page);
-               free_hot_cold_page(page, 0);
-       }
-
        spin_lock_irqsave(&ctx_alloc_lock, flags);
 
        if (CTX_VALID(mm->context)) {
index 432aa0c..b4f4733 100644 (file)
@@ -153,10 +153,10 @@ __spitfire_flush_tlb_mm_slow:
        .globl          __flush_icache_page
 __flush_icache_page:   /* %o0 = phys_page */
        srlx            %o0, PAGE_SHIFT, %o0
-       sethi           %uhi(PAGE_OFFSET), %g1
+       sethi           %hi(PAGE_OFFSET), %g1
        sllx            %o0, PAGE_SHIFT, %o0
        sethi           %hi(PAGE_SIZE), %g2
-       sllx            %g1, 32, %g1
+       ldx             [%g1 + %lo(PAGE_OFFSET)], %g1
        add             %o0, %g1, %o0
 1:     subcc           %g2, 32, %g2
        bne,pt          %icc, 1b
@@ -178,8 +178,8 @@ __flush_icache_page:        /* %o0 = phys_page */
        .align          64
        .globl          __flush_dcache_page
 __flush_dcache_page:   /* %o0=kaddr, %o1=flush_icache */
-       sethi           %uhi(PAGE_OFFSET), %g1
-       sllx            %g1, 32, %g1
+       sethi           %hi(PAGE_OFFSET), %g1
+       ldx             [%g1 + %lo(PAGE_OFFSET)], %g1
        sub             %o0, %g1, %o0                   ! physical address
        srlx            %o0, 11, %o0                    ! make D-cache TAG
        sethi           %hi(1 << 14), %o2               ! D-cache size
@@ -287,8 +287,8 @@ __cheetah_flush_tlb_pending:        /* 27 insns */
 
 #ifdef DCACHE_ALIASING_POSSIBLE
 __cheetah_flush_dcache_page: /* 11 insns */
-       sethi           %uhi(PAGE_OFFSET), %g1
-       sllx            %g1, 32, %g1
+       sethi           %hi(PAGE_OFFSET), %g1
+       ldx             [%g1 + %lo(PAGE_OFFSET)], %g1
        sub             %o0, %g1, %o0
        sethi           %hi(PAGE_SIZE), %o4
 1:     subcc           %o4, (1 << 5), %o4
index d45a2c4..b3692ce 100644 (file)
@@ -8,7 +8,6 @@ config TILE
        select HAVE_KVM if !TILEGX
        select GENERIC_FIND_FIRST_BIT
        select SYSCTL_EXCEPTION_TRACE
-       select USE_GENERIC_SMP_HELPERS
        select CC_OPTIMIZE_FOR_SIZE
        select HAVE_DEBUG_KMEMLEAK
        select GENERIC_IRQ_PROBE
index 822390f..54110af 100644 (file)
@@ -42,6 +42,4 @@ DECLARE_PER_CPU(irq_cpustat_t, irq_stat);
 
 #include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
 
-#define HARDIRQ_BITS   8
-
 #endif /* _ASM_TILE_HARDIRQ_H */
index b8aa6df..729aa10 100644 (file)
@@ -113,8 +113,6 @@ extern void _cpu_idle(void);
 
 #endif /* !__ASSEMBLY__ */
 
-#define PREEMPT_ACTIVE         0x10000000
-
 /*
  * Thread information flags that various assembly files may need to access.
  * Keep flags accessed frequently in low bits, particular since it makes
index 4fd9ec0..5e86eac 100644 (file)
@@ -241,6 +241,11 @@ struct page *pgtable_alloc_one(struct mm_struct *mm, unsigned long address,
        if (p == NULL)
                return NULL;
 
+       if (!pgtable_page_ctor(p)) {
+               __free_pages(p, L2_USER_PGTABLE_ORDER);
+               return NULL;
+       }
+
        /*
         * Make every page have a page_count() of one, not just the first.
         * We don't use __GFP_COMP since it doesn't look like it works
@@ -251,7 +256,6 @@ struct page *pgtable_alloc_one(struct mm_struct *mm, unsigned long address,
                inc_zone_page_state(p+i, NR_PAGETABLE);
        }
 
-       pgtable_page_ctor(p);
        return p;
 }
 
index b9d7c42..f10738d 100644 (file)
@@ -6,10 +6,6 @@ config STDERR_CONSOLE
        help
          console driver which dumps all printk messages to stderr.
 
-config STDIO_CONSOLE
-       bool
-       default y
-
 config SSL
        bool "Virtual serial line"
        help
index 8ddea1f..21ca44c 100644 (file)
@@ -1,8 +1,3 @@
-config DEFCONFIG_LIST
-       string
-       option defconfig_list
-       default "arch/$ARCH/defconfig"
-
 config UML
        bool
        default y
index 133f7de..48d92bb 100644 (file)
@@ -6,6 +6,17 @@
 # Licensed under the GPL
 #
 
+# select defconfig based on actual architecture
+ifeq ($(SUBARCH),x86)
+  ifeq ($(shell uname -m),x86_64)
+        KBUILD_DEFCONFIG := x86_64_defconfig
+  else
+        KBUILD_DEFCONFIG := i386_defconfig
+  endif
+else
+        KBUILD_DEFCONFIG := $(SUBARCH)_defconfig
+endif
+
 ARCH_DIR := arch/um
 OS := $(shell uname -s)
 # We require bash because the vmlinux link and loader script cpp use bash
diff --git a/arch/um/configs/i386_defconfig b/arch/um/configs/i386_defconfig
new file mode 100644 (file)
index 0000000..a12bf68
--- /dev/null
@@ -0,0 +1,76 @@
+CONFIG_3_LEVEL_PGTABLES=y
+# CONFIG_COMPACTION is not set
+CONFIG_BINFMT_MISC=m
+CONFIG_HOSTFS=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_KERNEL_STACK_ORDER=1
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_BLK_CGROUP=y
+# CONFIG_PID_NS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_IOSCHED_CFQ=m
+CONFIG_SSL=y
+CONFIG_NULL_CHAN=y
+CONFIG_PORT_CHAN=y
+CONFIG_PTY_CHAN=y
+CONFIG_TTY_CHAN=y
+CONFIG_XTERM_CHAN=y
+CONFIG_CON_CHAN="pts"
+CONFIG_SSL_CHAN="pts"
+CONFIG_UML_SOUND=m
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_BLK_DEV_UBD=y
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_DUMMY=m
+CONFIG_TUN=m
+CONFIG_PPP=m
+CONFIG_SLIP=m
+CONFIG_LEGACY_PTY_COUNT=32
+# CONFIG_HW_RANDOM is not set
+CONFIG_UML_RANDOM=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_INET_LRO is not set
+# CONFIG_IPV6 is not set
+CONFIG_UML_NET=y
+CONFIG_UML_NET_ETHERTAP=y
+CONFIG_UML_NET_TUNTAP=y
+CONFIG_UML_NET_SLIP=y
+CONFIG_UML_NET_DAEMON=y
+CONFIG_UML_NET_MCAST=y
+CONFIG_UML_NET_SLIRP=y
+CONFIG_EXT4_FS=y
+CONFIG_REISERFS_FS=y
+CONFIG_QUOTA=y
+CONFIG_AUTOFS4_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_NLS=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_KERNEL=y
diff --git a/arch/um/configs/x86_64_defconfig b/arch/um/configs/x86_64_defconfig
new file mode 100644 (file)
index 0000000..3aab117
--- /dev/null
@@ -0,0 +1,75 @@
+# CONFIG_COMPACTION is not set
+CONFIG_BINFMT_MISC=m
+CONFIG_HOSTFS=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_BLK_CGROUP=y
+# CONFIG_PID_NS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_IOSCHED_CFQ=m
+CONFIG_SSL=y
+CONFIG_NULL_CHAN=y
+CONFIG_PORT_CHAN=y
+CONFIG_PTY_CHAN=y
+CONFIG_TTY_CHAN=y
+CONFIG_XTERM_CHAN=y
+CONFIG_CON_CHAN="pts"
+CONFIG_SSL_CHAN="pts"
+CONFIG_UML_SOUND=m
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_BLK_DEV_UBD=y
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_DUMMY=m
+CONFIG_TUN=m
+CONFIG_PPP=m
+CONFIG_SLIP=m
+CONFIG_LEGACY_PTY_COUNT=32
+# CONFIG_HW_RANDOM is not set
+CONFIG_UML_RANDOM=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_INET_LRO is not set
+# CONFIG_IPV6 is not set
+CONFIG_UML_NET=y
+CONFIG_UML_NET_ETHERTAP=y
+CONFIG_UML_NET_TUNTAP=y
+CONFIG_UML_NET_SLIP=y
+CONFIG_UML_NET_DAEMON=y
+CONFIG_UML_NET_MCAST=y
+CONFIG_UML_NET_SLIRP=y
+CONFIG_EXT4_FS=y
+CONFIG_REISERFS_FS=y
+CONFIG_QUOTA=y
+CONFIG_AUTOFS4_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_NLS=y
+CONFIG_DEBUG_INFO=y
+CONFIG_FRAME_WARN=1024
+CONFIG_DEBUG_KERNEL=y
diff --git a/arch/um/defconfig b/arch/um/defconfig
deleted file mode 100644 (file)
index 2665e6b..0000000
+++ /dev/null
@@ -1,899 +0,0 @@
-#
-# Automatically generated file; DO NOT EDIT.
-# User Mode Linux/i386 3.3.0 Kernel Configuration
-#
-CONFIG_DEFCONFIG_LIST="arch/$ARCH/defconfig"
-CONFIG_UML=y
-CONFIG_MMU=y
-CONFIG_NO_IOMEM=y
-# CONFIG_TRACE_IRQFLAGS_SUPPORT is not set
-CONFIG_LOCKDEP_SUPPORT=y
-# CONFIG_STACKTRACE_SUPPORT is not set
-CONFIG_GENERIC_CALIBRATE_DELAY=y
-CONFIG_GENERIC_BUG=y
-CONFIG_GENERIC_CLOCKEVENTS=y
-CONFIG_HZ=100
-
-#
-# UML-specific options
-#
-
-#
-# Host processor type and features
-#
-# CONFIG_M486 is not set
-# CONFIG_M586 is not set
-# CONFIG_M586TSC is not set
-# CONFIG_M586MMX is not set
-CONFIG_M686=y
-# CONFIG_MPENTIUMII is not set
-# CONFIG_MPENTIUMIII is not set
-# CONFIG_MPENTIUMM is not set
-# CONFIG_MPENTIUM4 is not set
-# CONFIG_MK6 is not set
-# CONFIG_MK7 is not set
-# CONFIG_MK8 is not set
-# CONFIG_MCRUSOE is not set
-# CONFIG_MEFFICEON is not set
-# CONFIG_MWINCHIPC6 is not set
-# CONFIG_MWINCHIP3D is not set
-# CONFIG_MELAN is not set
-# CONFIG_MGEODEGX1 is not set
-# CONFIG_MGEODE_LX is not set
-# CONFIG_MCYRIXIII is not set
-# CONFIG_MVIAC3_2 is not set
-# CONFIG_MVIAC7 is not set
-# CONFIG_MCORE2 is not set
-# CONFIG_MATOM is not set
-# CONFIG_X86_GENERIC is not set
-CONFIG_X86_INTERNODE_CACHE_SHIFT=5
-CONFIG_X86_CMPXCHG=y
-CONFIG_X86_L1_CACHE_SHIFT=5
-CONFIG_X86_XADD=y
-CONFIG_X86_PPRO_FENCE=y
-CONFIG_X86_WP_WORKS_OK=y
-CONFIG_X86_INVLPG=y
-CONFIG_X86_BSWAP=y
-CONFIG_X86_POPAD_OK=y
-CONFIG_X86_USE_PPRO_CHECKSUM=y
-CONFIG_X86_TSC=y
-CONFIG_X86_CMPXCHG64=y
-CONFIG_X86_CMOV=y
-CONFIG_X86_MINIMUM_CPU_FAMILY=5
-CONFIG_CPU_SUP_INTEL=y
-CONFIG_CPU_SUP_CYRIX_32=y
-CONFIG_CPU_SUP_AMD=y
-CONFIG_CPU_SUP_CENTAUR=y
-CONFIG_CPU_SUP_TRANSMETA_32=y
-CONFIG_CPU_SUP_UMC_32=y
-CONFIG_UML_X86=y
-# CONFIG_64BIT is not set
-CONFIG_X86_32=y
-# CONFIG_X86_64 is not set
-# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
-CONFIG_RWSEM_GENERIC_SPINLOCK=y
-# CONFIG_3_LEVEL_PGTABLES is not set
-CONFIG_ARCH_HAS_SC_SIGNALS=y
-CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA=y
-CONFIG_GENERIC_HWEIGHT=y
-# CONFIG_STATIC_LINK is not set
-CONFIG_SELECT_MEMORY_MODEL=y
-CONFIG_FLATMEM_MANUAL=y
-CONFIG_FLATMEM=y
-CONFIG_FLAT_NODE_MEM_MAP=y
-CONFIG_PAGEFLAGS_EXTENDED=y
-CONFIG_SPLIT_PTLOCK_CPUS=4
-# CONFIG_COMPACTION is not set
-# CONFIG_PHYS_ADDR_T_64BIT is not set
-CONFIG_ZONE_DMA_FLAG=0
-CONFIG_VIRT_TO_BUS=y
-# CONFIG_KSM is not set
-CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
-CONFIG_NEED_PER_CPU_KM=y
-# CONFIG_CLEANCACHE is not set
-CONFIG_TICK_ONESHOT=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
-CONFIG_LD_SCRIPT_DYN=y
-CONFIG_BINFMT_ELF=y
-CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
-CONFIG_HAVE_AOUT=y
-# CONFIG_BINFMT_AOUT is not set
-CONFIG_BINFMT_MISC=m
-CONFIG_HOSTFS=y
-# CONFIG_HPPFS is not set
-CONFIG_MCONSOLE=y
-CONFIG_MAGIC_SYSRQ=y
-CONFIG_KERNEL_STACK_ORDER=0
-# CONFIG_MMAPPER is not set
-CONFIG_NO_DMA=y
-
-#
-# General setup
-#
-CONFIG_EXPERIMENTAL=y
-CONFIG_BROKEN_ON_SMP=y
-CONFIG_INIT_ENV_ARG_LIMIT=128
-CONFIG_CROSS_COMPILE=""
-CONFIG_LOCALVERSION=""
-CONFIG_LOCALVERSION_AUTO=y
-CONFIG_DEFAULT_HOSTNAME="(none)"
-CONFIG_SWAP=y
-CONFIG_SYSVIPC=y
-CONFIG_SYSVIPC_SYSCTL=y
-CONFIG_POSIX_MQUEUE=y
-CONFIG_POSIX_MQUEUE_SYSCTL=y
-CONFIG_BSD_PROCESS_ACCT=y
-# CONFIG_BSD_PROCESS_ACCT_V3 is not set
-# CONFIG_FHANDLE is not set
-# CONFIG_TASKSTATS is not set
-# CONFIG_AUDIT is not set
-
-#
-# IRQ subsystem
-#
-CONFIG_GENERIC_IRQ_SHOW=y
-
-#
-# RCU Subsystem
-#
-CONFIG_TINY_RCU=y
-# CONFIG_PREEMPT_RCU is not set
-# CONFIG_RCU_TRACE is not set
-# CONFIG_TREE_RCU_TRACE is not set
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=14
-CONFIG_CGROUPS=y
-# CONFIG_CGROUP_DEBUG is not set
-CONFIG_CGROUP_FREEZER=y
-CONFIG_CGROUP_DEVICE=y
-CONFIG_CPUSETS=y
-CONFIG_PROC_PID_CPUSET=y
-CONFIG_CGROUP_CPUACCT=y
-CONFIG_RESOURCE_COUNTERS=y
-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=y
-# CONFIG_DEBUG_BLK_CGROUP is not set
-# CONFIG_CHECKPOINT_RESTORE is not set
-CONFIG_NAMESPACES=y
-CONFIG_UTS_NS=y
-CONFIG_IPC_NS=y
-# CONFIG_USER_NS is not set
-# CONFIG_PID_NS is not set
-CONFIG_NET_NS=y
-# CONFIG_SCHED_AUTOGROUP is not set
-CONFIG_MM_OWNER=y
-CONFIG_SYSFS_DEPRECATED=y
-# CONFIG_SYSFS_DEPRECATED_V2 is not set
-# CONFIG_RELAY is not set
-# CONFIG_BLK_DEV_INITRD is not set
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
-CONFIG_SYSCTL=y
-CONFIG_ANON_INODES=y
-# CONFIG_EXPERT is not set
-CONFIG_UID16=y
-# CONFIG_SYSCTL_SYSCALL is not set
-CONFIG_KALLSYMS=y
-# CONFIG_KALLSYMS_ALL is not set
-CONFIG_HOTPLUG=y
-CONFIG_PRINTK=y
-CONFIG_BUG=y
-CONFIG_ELF_CORE=y
-CONFIG_BASE_FULL=y
-CONFIG_FUTEX=y
-CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
-CONFIG_SHMEM=y
-CONFIG_AIO=y
-# CONFIG_EMBEDDED is not set
-
-#
-# Kernel Performance Events And Counters
-#
-CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_COMPAT_BRK=y
-CONFIG_SLAB=y
-# CONFIG_SLUB is not set
-# CONFIG_PROFILING is not set
-
-#
-# GCOV-based kernel profiling
-#
-# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
-CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
-CONFIG_BASE_SMALL=0
-CONFIG_MODULES=y
-# CONFIG_MODULE_FORCE_LOAD is not set
-CONFIG_MODULE_UNLOAD=y
-# CONFIG_MODULE_FORCE_UNLOAD is not set
-# CONFIG_MODVERSIONS is not set
-# CONFIG_MODULE_SRCVERSION_ALL is not set
-CONFIG_BLOCK=y
-CONFIG_LBDAF=y
-# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_BLK_DEV_BSGLIB is not set
-# CONFIG_BLK_DEV_INTEGRITY is not set
-
-#
-# Partition Types
-#
-# CONFIG_PARTITION_ADVANCED is not set
-CONFIG_MSDOS_PARTITION=y
-
-#
-# IO Schedulers
-#
-CONFIG_IOSCHED_NOOP=y
-CONFIG_IOSCHED_DEADLINE=y
-CONFIG_IOSCHED_CFQ=m
-# CONFIG_CFQ_GROUP_IOSCHED is not set
-CONFIG_DEFAULT_DEADLINE=y
-# CONFIG_DEFAULT_CFQ is not set
-# CONFIG_DEFAULT_NOOP is not set
-CONFIG_DEFAULT_IOSCHED="deadline"
-# CONFIG_INLINE_SPIN_TRYLOCK is not set
-# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
-# CONFIG_INLINE_SPIN_LOCK is not set
-# CONFIG_INLINE_SPIN_LOCK_BH is not set
-# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
-# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
-CONFIG_INLINE_SPIN_UNLOCK=y
-# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
-CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
-# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
-# CONFIG_INLINE_READ_TRYLOCK is not set
-# CONFIG_INLINE_READ_LOCK is not set
-# CONFIG_INLINE_READ_LOCK_BH is not set
-# CONFIG_INLINE_READ_LOCK_IRQ is not set
-# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
-CONFIG_INLINE_READ_UNLOCK=y
-# CONFIG_INLINE_READ_UNLOCK_BH is not set
-CONFIG_INLINE_READ_UNLOCK_IRQ=y
-# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
-# CONFIG_INLINE_WRITE_TRYLOCK is not set
-# CONFIG_INLINE_WRITE_LOCK is not set
-# CONFIG_INLINE_WRITE_LOCK_BH is not set
-# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
-# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
-CONFIG_INLINE_WRITE_UNLOCK=y
-# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
-CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
-# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
-# CONFIG_MUTEX_SPIN_ON_OWNER is not set
-CONFIG_FREEZER=y
-
-#
-# UML Character Devices
-#
-CONFIG_STDERR_CONSOLE=y
-CONFIG_STDIO_CONSOLE=y
-CONFIG_SSL=y
-CONFIG_NULL_CHAN=y
-CONFIG_PORT_CHAN=y
-CONFIG_PTY_CHAN=y
-CONFIG_TTY_CHAN=y
-CONFIG_XTERM_CHAN=y
-# CONFIG_NOCONFIG_CHAN is not set
-CONFIG_CON_ZERO_CHAN="fd:0,fd:1"
-CONFIG_CON_CHAN="xterm"
-CONFIG_SSL_CHAN="pts"
-CONFIG_UML_SOUND=m
-CONFIG_SOUND=m
-CONFIG_SOUND_OSS_CORE=y
-CONFIG_HOSTAUDIO=m
-
-#
-# Device Drivers
-#
-
-#
-# Generic Driver Options
-#
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_DEVTMPFS=y
-CONFIG_DEVTMPFS_MOUNT=y
-CONFIG_STANDALONE=y
-CONFIG_PREVENT_FIRMWARE_BUILD=y
-CONFIG_FW_LOADER=y
-CONFIG_FIRMWARE_IN_KERNEL=y
-CONFIG_EXTRA_FIRMWARE=""
-# CONFIG_DEBUG_DRIVER is not set
-# CONFIG_DEBUG_DEVRES is not set
-# CONFIG_SYS_HYPERVISOR is not set
-CONFIG_GENERIC_CPU_DEVICES=y
-# CONFIG_DMA_SHARED_BUFFER is not set
-# CONFIG_CONNECTOR is not set
-# CONFIG_MTD is not set
-CONFIG_BLK_DEV=y
-CONFIG_BLK_DEV_UBD=y
-# CONFIG_BLK_DEV_UBD_SYNC is not set
-CONFIG_BLK_DEV_COW_COMMON=y
-CONFIG_BLK_DEV_LOOP=m
-CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
-# CONFIG_BLK_DEV_CRYPTOLOOP is not set
-
-#
-# DRBD disabled because PROC_FS, INET or CONNECTOR not selected
-#
-CONFIG_BLK_DEV_NBD=m
-# CONFIG_BLK_DEV_RAM is not set
-# CONFIG_ATA_OVER_ETH is not set
-# CONFIG_BLK_DEV_RBD is not set
-
-#
-# Misc devices
-#
-# CONFIG_ENCLOSURE_SERVICES is not set
-# CONFIG_C2PORT is not set
-
-#
-# EEPROM support
-#
-# CONFIG_EEPROM_93CX6 is not set
-
-#
-# Texas Instruments shared transport line discipline
-#
-
-#
-# Altera FPGA firmware download module
-#
-
-#
-# SCSI device support
-#
-CONFIG_SCSI_MOD=y
-# CONFIG_RAID_ATTRS is not set
-# CONFIG_SCSI is not set
-# CONFIG_SCSI_DMA is not set
-# CONFIG_SCSI_NETLINK is not set
-# CONFIG_MD is not set
-CONFIG_NETDEVICES=y
-CONFIG_NET_CORE=y
-# CONFIG_BONDING is not set
-CONFIG_DUMMY=m
-# CONFIG_EQUALIZER is not set
-# CONFIG_MII is not set
-# CONFIG_NET_TEAM is not set
-# CONFIG_MACVLAN is not set
-# CONFIG_NETCONSOLE is not set
-# CONFIG_NETPOLL is not set
-# CONFIG_NET_POLL_CONTROLLER is not set
-CONFIG_TUN=m
-# CONFIG_VETH is not set
-
-#
-# CAIF transport drivers
-#
-CONFIG_ETHERNET=y
-CONFIG_NET_VENDOR_CHELSIO=y
-CONFIG_NET_VENDOR_INTEL=y
-CONFIG_NET_VENDOR_I825XX=y
-CONFIG_NET_VENDOR_MARVELL=y
-CONFIG_NET_VENDOR_NATSEMI=y
-CONFIG_NET_VENDOR_8390=y
-# CONFIG_PHYLIB is not set
-CONFIG_PPP=m
-# CONFIG_PPP_BSDCOMP is not set
-# CONFIG_PPP_DEFLATE is not set
-# CONFIG_PPP_FILTER is not set
-# CONFIG_PPP_MPPE is not set
-# CONFIG_PPP_MULTILINK is not set
-# CONFIG_PPPOE is not set
-# CONFIG_PPP_ASYNC is not set
-# CONFIG_PPP_SYNC_TTY is not set
-CONFIG_SLIP=m
-CONFIG_SLHC=m
-# CONFIG_SLIP_COMPRESSED is not set
-# CONFIG_SLIP_SMART is not set
-# CONFIG_SLIP_MODE_SLIP6 is not set
-CONFIG_WLAN=y
-# CONFIG_HOSTAP is not set
-
-#
-# Enable WiMAX (Networking options) to see the WiMAX drivers
-#
-# CONFIG_WAN is not set
-
-#
-# Character devices
-#
-CONFIG_UNIX98_PTYS=y
-# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
-CONFIG_LEGACY_PTYS=y
-CONFIG_LEGACY_PTY_COUNT=32
-# CONFIG_N_GSM is not set
-# CONFIG_TRACE_SINK is not set
-CONFIG_DEVKMEM=y
-# CONFIG_HW_RANDOM is not set
-CONFIG_UML_RANDOM=y
-# CONFIG_R3964 is not set
-# CONFIG_NSC_GPIO is not set
-# CONFIG_RAW_DRIVER is not set
-
-#
-# PPS support
-#
-# CONFIG_PPS is not set
-
-#
-# PPS generators support
-#
-
-#
-# PTP clock support
-#
-
-#
-# Enable Device Drivers -> PPS to see the PTP clock options.
-#
-# CONFIG_POWER_SUPPLY is not set
-# CONFIG_THERMAL is not set
-# CONFIG_WATCHDOG is not set
-# CONFIG_REGULATOR is not set
-CONFIG_SOUND_OSS_CORE_PRECLAIM=y
-# CONFIG_MEMSTICK is not set
-# CONFIG_NEW_LEDS is not set
-# CONFIG_ACCESSIBILITY is not set
-# CONFIG_AUXDISPLAY is not set
-# CONFIG_UIO is not set
-
-#
-# Virtio drivers
-#
-# CONFIG_VIRTIO_BALLOON is not set
-
-#
-# Microsoft Hyper-V guest support
-#
-# CONFIG_STAGING is not set
-
-#
-# Hardware Spinlock drivers
-#
-CONFIG_IOMMU_SUPPORT=y
-# CONFIG_VIRT_DRIVERS is not set
-# CONFIG_PM_DEVFREQ is not set
-CONFIG_NET=y
-
-#
-# Networking options
-#
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-# CONFIG_UNIX_DIAG is not set
-CONFIG_XFRM=y
-# CONFIG_XFRM_USER is not set
-# CONFIG_XFRM_SUB_POLICY is not set
-# CONFIG_XFRM_MIGRATE is not set
-# CONFIG_XFRM_STATISTICS is not set
-# CONFIG_NET_KEY is not set
-CONFIG_INET=y
-# CONFIG_IP_MULTICAST is not set
-# CONFIG_IP_ADVANCED_ROUTER is not set
-# CONFIG_IP_PNP is not set
-# CONFIG_NET_IPIP is not set
-# CONFIG_NET_IPGRE_DEMUX is not set
-# CONFIG_ARPD is not set
-# CONFIG_SYN_COOKIES is not set
-# CONFIG_INET_AH is not set
-# CONFIG_INET_ESP is not set
-# CONFIG_INET_IPCOMP is not set
-# CONFIG_INET_XFRM_TUNNEL is not set
-# CONFIG_INET_TUNNEL is not set
-CONFIG_INET_XFRM_MODE_TRANSPORT=y
-CONFIG_INET_XFRM_MODE_TUNNEL=y
-CONFIG_INET_XFRM_MODE_BEET=y
-# CONFIG_INET_LRO is not set
-CONFIG_INET_DIAG=y
-CONFIG_INET_TCP_DIAG=y
-# CONFIG_INET_UDP_DIAG is not set
-# CONFIG_TCP_CONG_ADVANCED is not set
-CONFIG_TCP_CONG_CUBIC=y
-CONFIG_DEFAULT_TCP_CONG="cubic"
-# CONFIG_TCP_MD5SIG is not set
-# CONFIG_IPV6 is not set
-# CONFIG_NETWORK_SECMARK is not set
-# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
-# CONFIG_NETFILTER is not set
-# CONFIG_IP_DCCP is not set
-# CONFIG_IP_SCTP is not set
-# CONFIG_RDS is not set
-# CONFIG_TIPC is not set
-# CONFIG_ATM is not set
-# CONFIG_L2TP is not set
-# CONFIG_BRIDGE is not set
-# CONFIG_NET_DSA is not set
-# CONFIG_VLAN_8021Q is not set
-# CONFIG_DECNET is not set
-# CONFIG_LLC2 is not set
-# CONFIG_IPX is not set
-# CONFIG_ATALK is not set
-# CONFIG_X25 is not set
-# CONFIG_LAPB is not set
-# CONFIG_ECONET is not set
-# CONFIG_WAN_ROUTER is not set
-# CONFIG_PHONET is not set
-# CONFIG_IEEE802154 is not set
-# CONFIG_NET_SCHED is not set
-# CONFIG_DCB is not set
-# CONFIG_BATMAN_ADV is not set
-# CONFIG_OPENVSWITCH is not set
-# CONFIG_NETPRIO_CGROUP is not set
-CONFIG_BQL=y
-
-#
-# Network testing
-#
-# CONFIG_NET_PKTGEN is not set
-# CONFIG_HAMRADIO is not set
-# CONFIG_CAN is not set
-# CONFIG_IRDA is not set
-# CONFIG_BT is not set
-# CONFIG_AF_RXRPC is not set
-CONFIG_WIRELESS=y
-# CONFIG_CFG80211 is not set
-# CONFIG_LIB80211 is not set
-
-#
-# CFG80211 needs to be enabled for MAC80211
-#
-# CONFIG_WIMAX is not set
-# CONFIG_RFKILL is not set
-# CONFIG_NET_9P is not set
-# CONFIG_CAIF is not set
-# CONFIG_CEPH_LIB is not set
-# CONFIG_NFC is not set
-
-#
-# UML Network Devices
-#
-CONFIG_UML_NET=y
-CONFIG_UML_NET_ETHERTAP=y
-CONFIG_UML_NET_TUNTAP=y
-CONFIG_UML_NET_SLIP=y
-CONFIG_UML_NET_DAEMON=y
-# CONFIG_UML_NET_VDE is not set
-CONFIG_UML_NET_MCAST=y
-# CONFIG_UML_NET_PCAP is not set
-CONFIG_UML_NET_SLIRP=y
-
-#
-# File systems
-#
-# CONFIG_EXT2_FS is not set
-# CONFIG_EXT3_FS is not set
-CONFIG_EXT4_FS=y
-CONFIG_EXT4_USE_FOR_EXT23=y
-CONFIG_EXT4_FS_XATTR=y
-# CONFIG_EXT4_FS_POSIX_ACL is not set
-# CONFIG_EXT4_FS_SECURITY is not set
-# CONFIG_EXT4_DEBUG is not set
-CONFIG_JBD2=y
-CONFIG_FS_MBCACHE=y
-CONFIG_REISERFS_FS=y
-# CONFIG_REISERFS_CHECK is not set
-# CONFIG_REISERFS_PROC_INFO is not set
-# CONFIG_REISERFS_FS_XATTR is not set
-# CONFIG_JFS_FS is not set
-# CONFIG_XFS_FS is not set
-# CONFIG_GFS2_FS is not set
-# CONFIG_BTRFS_FS is not set
-# CONFIG_NILFS2_FS is not set
-# CONFIG_FS_POSIX_ACL is not set
-CONFIG_FILE_LOCKING=y
-CONFIG_FSNOTIFY=y
-CONFIG_DNOTIFY=y
-CONFIG_INOTIFY_USER=y
-# CONFIG_FANOTIFY is not set
-CONFIG_QUOTA=y
-# CONFIG_QUOTA_NETLINK_INTERFACE is not set
-CONFIG_PRINT_QUOTA_WARNING=y
-# CONFIG_QUOTA_DEBUG is not set
-# CONFIG_QFMT_V1 is not set
-# CONFIG_QFMT_V2 is not set
-CONFIG_QUOTACTL=y
-CONFIG_AUTOFS4_FS=m
-# CONFIG_FUSE_FS is not set
-
-#
-# Caches
-#
-# CONFIG_FSCACHE is not set
-
-#
-# CD-ROM/DVD Filesystems
-#
-CONFIG_ISO9660_FS=m
-CONFIG_JOLIET=y
-# CONFIG_ZISOFS is not set
-# CONFIG_UDF_FS is not set
-
-#
-# DOS/FAT/NT Filesystems
-#
-# CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
-# CONFIG_NTFS_FS is not set
-
-#
-# Pseudo filesystems
-#
-CONFIG_PROC_FS=y
-CONFIG_PROC_KCORE=y
-CONFIG_PROC_SYSCTL=y
-CONFIG_PROC_PAGE_MONITOR=y
-CONFIG_SYSFS=y
-CONFIG_TMPFS=y
-# CONFIG_TMPFS_POSIX_ACL is not set
-# CONFIG_TMPFS_XATTR is not set
-# CONFIG_HUGETLB_PAGE is not set
-# CONFIG_CONFIGFS_FS is not set
-CONFIG_MISC_FILESYSTEMS=y
-# CONFIG_ADFS_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_HFS_FS is not set
-# CONFIG_HFSPLUS_FS is not set
-# CONFIG_BEFS_FS is not set
-# CONFIG_BFS_FS is not set
-# CONFIG_EFS_FS is not set
-# CONFIG_LOGFS is not set
-# CONFIG_CRAMFS is not set
-# CONFIG_SQUASHFS is not set
-# CONFIG_VXFS_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_OMFS_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_PSTORE is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_UFS_FS is not set
-CONFIG_NETWORK_FILESYSTEMS=y
-# CONFIG_NFS_FS is not set
-# CONFIG_NFSD is not set
-# CONFIG_CEPH_FS is not set
-# CONFIG_CIFS is not set
-# CONFIG_NCP_FS is not set
-# CONFIG_CODA_FS is not set
-# CONFIG_AFS_FS is not set
-CONFIG_NLS=y
-CONFIG_NLS_DEFAULT="iso8859-1"
-# CONFIG_NLS_CODEPAGE_437 is not set
-# CONFIG_NLS_CODEPAGE_737 is not set
-# CONFIG_NLS_CODEPAGE_775 is not set
-# CONFIG_NLS_CODEPAGE_850 is not set
-# CONFIG_NLS_CODEPAGE_852 is not set
-# CONFIG_NLS_CODEPAGE_855 is not set
-# CONFIG_NLS_CODEPAGE_857 is not set
-# CONFIG_NLS_CODEPAGE_860 is not set
-# CONFIG_NLS_CODEPAGE_861 is not set
-# CONFIG_NLS_CODEPAGE_862 is not set
-# CONFIG_NLS_CODEPAGE_863 is not set
-# CONFIG_NLS_CODEPAGE_864 is not set
-# CONFIG_NLS_CODEPAGE_865 is not set
-# CONFIG_NLS_CODEPAGE_866 is not set
-# CONFIG_NLS_CODEPAGE_869 is not set
-# CONFIG_NLS_CODEPAGE_936 is not set
-# CONFIG_NLS_CODEPAGE_950 is not set
-# CONFIG_NLS_CODEPAGE_932 is not set
-# CONFIG_NLS_CODEPAGE_949 is not set
-# CONFIG_NLS_CODEPAGE_874 is not set
-# CONFIG_NLS_ISO8859_8 is not set
-# CONFIG_NLS_CODEPAGE_1250 is not set
-# CONFIG_NLS_CODEPAGE_1251 is not set
-# CONFIG_NLS_ASCII is not set
-# CONFIG_NLS_ISO8859_1 is not set
-# CONFIG_NLS_ISO8859_2 is not set
-# CONFIG_NLS_ISO8859_3 is not set
-# CONFIG_NLS_ISO8859_4 is not set
-# CONFIG_NLS_ISO8859_5 is not set
-# CONFIG_NLS_ISO8859_6 is not set
-# CONFIG_NLS_ISO8859_7 is not set
-# CONFIG_NLS_ISO8859_9 is not set
-# CONFIG_NLS_ISO8859_13 is not set
-# CONFIG_NLS_ISO8859_14 is not set
-# CONFIG_NLS_ISO8859_15 is not set
-# CONFIG_NLS_KOI8_R is not set
-# CONFIG_NLS_KOI8_U is not set
-# CONFIG_NLS_UTF8 is not set
-
-#
-# Security options
-#
-# CONFIG_KEYS is not set
-# CONFIG_SECURITY_DMESG_RESTRICT is not set
-# CONFIG_SECURITY is not set
-# CONFIG_SECURITYFS is not set
-CONFIG_DEFAULT_SECURITY_DAC=y
-CONFIG_DEFAULT_SECURITY=""
-CONFIG_CRYPTO=y
-
-#
-# Crypto core or helper
-#
-# CONFIG_CRYPTO_FIPS is not set
-CONFIG_CRYPTO_ALGAPI=m
-CONFIG_CRYPTO_ALGAPI2=m
-CONFIG_CRYPTO_RNG=m
-CONFIG_CRYPTO_RNG2=m
-# CONFIG_CRYPTO_MANAGER is not set
-# CONFIG_CRYPTO_MANAGER2 is not set
-# CONFIG_CRYPTO_USER is not set
-# CONFIG_CRYPTO_GF128MUL is not set
-# CONFIG_CRYPTO_NULL is not set
-# CONFIG_CRYPTO_CRYPTD is not set
-# CONFIG_CRYPTO_AUTHENC is not set
-# CONFIG_CRYPTO_TEST is not set
-
-#
-# Authenticated Encryption with Associated Data
-#
-# CONFIG_CRYPTO_CCM is not set
-# CONFIG_CRYPTO_GCM is not set
-# CONFIG_CRYPTO_SEQIV is not set
-
-#
-# Block modes
-#
-# CONFIG_CRYPTO_CBC is not set
-# CONFIG_CRYPTO_CTR is not set
-# CONFIG_CRYPTO_CTS is not set
-# CONFIG_CRYPTO_ECB is not set
-# CONFIG_CRYPTO_LRW is not set
-# CONFIG_CRYPTO_PCBC is not set
-# CONFIG_CRYPTO_XTS is not set
-
-#
-# Hash modes
-#
-# CONFIG_CRYPTO_HMAC is not set
-# CONFIG_CRYPTO_XCBC is not set
-# CONFIG_CRYPTO_VMAC is not set
-
-#
-# Digest
-#
-# CONFIG_CRYPTO_CRC32C is not set
-# CONFIG_CRYPTO_GHASH is not set
-# CONFIG_CRYPTO_MD4 is not set
-# CONFIG_CRYPTO_MD5 is not set
-# CONFIG_CRYPTO_MICHAEL_MIC is not set
-# CONFIG_CRYPTO_RMD128 is not set
-# CONFIG_CRYPTO_RMD160 is not set
-# CONFIG_CRYPTO_RMD256 is not set
-# CONFIG_CRYPTO_RMD320 is not set
-# CONFIG_CRYPTO_SHA1 is not set
-# CONFIG_CRYPTO_SHA256 is not set
-# CONFIG_CRYPTO_SHA512 is not set
-# CONFIG_CRYPTO_TGR192 is not set
-# CONFIG_CRYPTO_WP512 is not set
-
-#
-# Ciphers
-#
-CONFIG_CRYPTO_AES=m
-# CONFIG_CRYPTO_AES_586 is not set
-# CONFIG_CRYPTO_ANUBIS is not set
-# CONFIG_CRYPTO_ARC4 is not set
-# CONFIG_CRYPTO_BLOWFISH is not set
-# CONFIG_CRYPTO_CAMELLIA is not set
-# CONFIG_CRYPTO_CAST5 is not set
-# CONFIG_CRYPTO_CAST6 is not set
-# CONFIG_CRYPTO_DES is not set
-# CONFIG_CRYPTO_FCRYPT is not set
-# CONFIG_CRYPTO_KHAZAD is not set
-# CONFIG_CRYPTO_SALSA20 is not set
-# CONFIG_CRYPTO_SALSA20_586 is not set
-# CONFIG_CRYPTO_SEED is not set
-# CONFIG_CRYPTO_SERPENT is not set
-# CONFIG_CRYPTO_TEA is not set
-# CONFIG_CRYPTO_TWOFISH is not set
-# CONFIG_CRYPTO_TWOFISH_586 is not set
-
-#
-# Compression
-#
-# CONFIG_CRYPTO_DEFLATE is not set
-# CONFIG_CRYPTO_ZLIB is not set
-# CONFIG_CRYPTO_LZO is not set
-
-#
-# Random Number Generation
-#
-CONFIG_CRYPTO_ANSI_CPRNG=m
-# CONFIG_CRYPTO_USER_API_HASH is not set
-# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
-CONFIG_CRYPTO_HW=y
-# CONFIG_BINARY_PRINTF is not set
-
-#
-# Library routines
-#
-CONFIG_BITREVERSE=y
-CONFIG_GENERIC_FIND_FIRST_BIT=y
-CONFIG_GENERIC_IO=y
-# CONFIG_CRC_CCITT is not set
-CONFIG_CRC16=y
-# CONFIG_CRC_T10DIF is not set
-# CONFIG_CRC_ITU_T is not set
-CONFIG_CRC32=y
-# CONFIG_CRC7 is not set
-# CONFIG_LIBCRC32C is not set
-# CONFIG_CRC8 is not set
-# CONFIG_XZ_DEC is not set
-# CONFIG_XZ_DEC_BCJ is not set
-CONFIG_DQL=y
-CONFIG_NLATTR=y
-# CONFIG_AVERAGE is not set
-# CONFIG_CORDIC is not set
-
-#
-# Kernel hacking
-#
-# CONFIG_PRINTK_TIME is not set
-CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
-CONFIG_ENABLE_WARN_DEPRECATED=y
-CONFIG_ENABLE_MUST_CHECK=y
-CONFIG_FRAME_WARN=1024
-# CONFIG_STRIP_ASM_SYMS is not set
-# CONFIG_UNUSED_SYMBOLS is not set
-# CONFIG_DEBUG_FS is not set
-# CONFIG_DEBUG_SECTION_MISMATCH is not set
-CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
-# CONFIG_LOCKUP_DETECTOR is not set
-# CONFIG_HARDLOCKUP_DETECTOR is not set
-# CONFIG_DETECT_HUNG_TASK is not set
-CONFIG_SCHED_DEBUG=y
-# CONFIG_SCHEDSTATS is not set
-# CONFIG_TIMER_STATS is not set
-# CONFIG_DEBUG_OBJECTS is not set
-# CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
-# CONFIG_DEBUG_SPINLOCK is not set
-# CONFIG_DEBUG_MUTEXES is not set
-# CONFIG_SPARSE_RCU_POINTER is not set
-# CONFIG_DEBUG_ATOMIC_SLEEP is not set
-# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
-# CONFIG_DEBUG_STACK_USAGE is not set
-# CONFIG_DEBUG_KOBJECT is not set
-CONFIG_DEBUG_BUGVERBOSE=y
-CONFIG_DEBUG_INFO=y
-# CONFIG_DEBUG_INFO_REDUCED is not set
-# CONFIG_DEBUG_VM is not set
-# CONFIG_DEBUG_WRITECOUNT is not set
-CONFIG_DEBUG_MEMORY_INIT=y
-# CONFIG_DEBUG_LIST is not set
-# CONFIG_TEST_LIST_SORT is not set
-# CONFIG_DEBUG_SG is not set
-# CONFIG_DEBUG_NOTIFIERS is not set
-# CONFIG_DEBUG_CREDENTIALS is not set
-CONFIG_FRAME_POINTER=y
-# CONFIG_BOOT_PRINTK_DELAY is not set
-# CONFIG_RCU_TORTURE_TEST is not set
-# CONFIG_BACKTRACE_SELF_TEST is not set
-# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
-# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
-# CONFIG_FAULT_INJECTION is not set
-# CONFIG_SYSCTL_SYSCALL_CHECK is not set
-# CONFIG_DEBUG_PAGEALLOC is not set
-# CONFIG_ATOMIC64_SELFTEST is not set
-# CONFIG_SAMPLES is not set
-# CONFIG_TEST_KSTRTOX is not set
-# CONFIG_GPROF is not set
-# CONFIG_GCOV is not set
-CONFIG_EARLY_PRINTK=y
index 3df3bd5..29880c9 100644 (file)
@@ -645,11 +645,9 @@ void mconsole_sysrq(struct mc_request *req)
 
 static void stack_proc(void *arg)
 {
-       struct task_struct *from = current, *to = arg;
+       struct task_struct *task = arg;
 
-       to->thread.saved_task = from;
-       rcu_user_hooks_switch(from, to);
-       switch_to(from, to, from);
+       show_stack(task, NULL);
 }
 
 /*
index c03cd5a..d89b02b 100644 (file)
@@ -19,8 +19,8 @@ struct task_struct;
 struct mm_struct;
 
 struct thread_struct {
-       struct task_struct *saved_task;
        struct pt_regs regs;
+       struct pt_regs *segv_regs;
        int singlestep_syscall;
        void *fault_addr;
        jmp_buf *fault_catcher;
index 2c8eeb2..1c5b2a8 100644 (file)
@@ -60,8 +60,6 @@ static inline struct thread_info *current_thread_info(void)
 
 #endif
 
-#define PREEMPT_ACTIVE         0x10000000
-
 #define TIF_SYSCALL_TRACE      0       /* syscall trace active */
 #define TIF_SIGPENDING         1       /* signal pending */
 #define TIF_NEED_RESCHED       2       /* rescheduling necessary */
index 694c792..41c8c77 100644 (file)
@@ -44,7 +44,6 @@ struct cpu_task {
 
 extern struct cpu_task cpu_tasks[];
 
-extern unsigned long low_physmem;
 extern unsigned long high_physmem;
 extern unsigned long uml_physmem;
 extern unsigned long uml_reserved;
@@ -52,8 +51,6 @@ extern unsigned long end_vm;
 extern unsigned long start_vm;
 extern unsigned long long highmem;
 
-extern unsigned long _stext, _etext, _sdata, _edata, __bss_start, _end;
-extern unsigned long _unprotected_end;
 extern unsigned long brk_start;
 
 extern unsigned long host_task_size;
index 021104d..75298d3 100644 (file)
@@ -227,6 +227,7 @@ extern void block_signals(void);
 extern void unblock_signals(void);
 extern int get_signals(void);
 extern int set_signals(int enable);
+extern int os_is_signal_stack(void);
 
 /* util.c */
 extern void stack_protections(unsigned long address);
index 7ddb64b..8636e90 100644 (file)
@@ -279,8 +279,12 @@ pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address)
        struct page *pte;
 
        pte = alloc_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
-       if (pte)
-               pgtable_page_ctor(pte);
+       if (!pte)
+               return NULL;
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
+       }
        return pte;
 }
 
index bbcef52..eecc414 100644 (file)
@@ -82,19 +82,8 @@ void *__switch_to(struct task_struct *from, struct task_struct *to)
        to->thread.prev_sched = from;
        set_current(to);
 
-       do {
-               current->thread.saved_task = NULL;
-
-               switch_threads(&from->thread.switch_buf,
-                              &to->thread.switch_buf);
-
-               arch_switch_to(current);
-
-               if (current->thread.saved_task)
-                       show_regs(&(current->thread.regs));
-               to = current->thread.saved_task;
-               from = current;
-       } while (current->thread.saved_task);
+       switch_threads(&from->thread.switch_buf, &to->thread.switch_buf);
+       arch_switch_to(current);
 
        return current->thread.prev_sched;
 }
index 0dc4d1c..4d6fdf6 100644 (file)
@@ -1,6 +1,10 @@
 /*
  * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
+ * Copyright (C) 2013 Richard Weinberger <richrd@nod.at>
+ *
+ * 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/kallsyms.h>
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <asm/sysrq.h>
+#include <os.h>
 
-/* Catch non-i386 SUBARCH's. */
-#if !defined(CONFIG_UML_X86) || defined(CONFIG_64BIT)
-void show_trace(struct task_struct *task, unsigned long * stack)
+struct stack_frame {
+       struct stack_frame *next_frame;
+       unsigned long return_address;
+};
+
+static void print_stack_trace(unsigned long *sp, unsigned long bp)
 {
+       int reliable;
        unsigned long addr;
+       struct stack_frame *frame = (struct stack_frame *)bp;
 
-       if (!stack) {
-               stack = (unsigned long*) &stack;
-               WARN_ON(1);
-       }
-
-       printk(KERN_INFO "Call Trace: \n");
-       while (((long) stack & (THREAD_SIZE-1)) != 0) {
-               addr = *stack;
+       printk(KERN_INFO "Call Trace:\n");
+       while (((long) sp & (THREAD_SIZE-1)) != 0) {
+               addr = *sp;
                if (__kernel_text_address(addr)) {
-                       printk(KERN_INFO "%08lx:  [<%08lx>]",
-                              (unsigned long) stack, addr);
-                       print_symbol(KERN_CONT " %s", addr);
+                       reliable = 0;
+                       if ((unsigned long) sp == bp + sizeof(long)) {
+                               frame = frame ? frame->next_frame : NULL;
+                               bp = (unsigned long)frame;
+                               reliable = 1;
+                       }
+
+                       printk(KERN_INFO " [<%08lx>]", addr);
+                       printk(KERN_CONT " %s", reliable ? "" : "? ");
+                       print_symbol(KERN_CONT "%s", addr);
                        printk(KERN_CONT "\n");
                }
-               stack++;
+               sp++;
        }
        printk(KERN_INFO "\n");
 }
-#endif
 
-/*Stolen from arch/i386/kernel/traps.c */
-static const int kstack_depth_to_print = 24;
+static unsigned long get_frame_pointer(struct task_struct *task,
+                                      struct pt_regs *segv_regs)
+{
+       if (!task || task == current)
+               return segv_regs ? PT_REGS_BP(segv_regs) : current_bp();
+       else
+               return KSTK_EBP(task);
+}
 
-/* This recently started being used in arch-independent code too, as in
- * kernel/sched/core.c.*/
-void show_stack(struct task_struct *task, unsigned long *esp)
+static unsigned long *get_stack_pointer(struct task_struct *task,
+                                       struct pt_regs *segv_regs)
 {
-       unsigned long *stack;
+       if (!task || task == current)
+               return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp();
+       else
+               return (unsigned long *)KSTK_ESP(task);
+}
+
+void show_stack(struct task_struct *task, unsigned long *stack)
+{
+       unsigned long *sp = stack, bp = 0;
+       struct pt_regs *segv_regs = current->thread.segv_regs;
        int i;
 
-       if (esp == NULL) {
-               if (task != current && task != NULL) {
-                       esp = (unsigned long *) KSTK_ESP(task);
-               } else {
-                       esp = (unsigned long *) &esp;
-               }
+       if (!segv_regs && os_is_signal_stack()) {
+               printk(KERN_ERR "Received SIGSEGV in SIGSEGV handler,"
+                               " aborting stack trace!\n");
+               return;
        }
 
-       stack = esp;
-       for (i = 0; i < kstack_depth_to_print; i++) {
+#ifdef CONFIG_FRAME_POINTER
+       bp = get_frame_pointer(task, segv_regs);
+#endif
+
+       if (!stack)
+               sp = get_stack_pointer(task, segv_regs);
+
+       printk(KERN_INFO "Stack:\n");
+       stack = sp;
+       for (i = 0; i < 3 * STACKSLOTS_PER_LINE; i++) {
                if (kstack_end(stack))
                        break;
-               if (i && ((i % 8) == 0))
-                       printk(KERN_INFO "       ");
-               printk(KERN_CONT "%08lx ", *stack++);
+               if (i && ((i % STACKSLOTS_PER_LINE) == 0))
+                       printk(KERN_CONT "\n");
+               printk(KERN_CONT " %08lx", *stack++);
        }
+       printk(KERN_CONT "\n");
 
-       show_trace(task, esp);
+       print_stack_trace(sp, bp);
 }
index 5c3aef7..974b874 100644 (file)
@@ -206,9 +206,12 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
        int is_write = FAULT_WRITE(fi);
        unsigned long address = FAULT_ADDRESS(fi);
 
+       if (regs)
+               current->thread.segv_regs = container_of(regs, struct pt_regs, regs);
+
        if (!is_user && (address >= start_vm) && (address < end_vm)) {
                flush_tlb_kernel_vm();
-               return 0;
+               goto out;
        }
        else if (current->mm == NULL) {
                show_regs(container_of(regs, struct pt_regs, regs));
@@ -230,7 +233,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
 
        catcher = current->thread.fault_catcher;
        if (!err)
-               return 0;
+               goto out;
        else if (catcher != NULL) {
                current->thread.fault_addr = (void *) address;
                UML_LONGJMP(catcher, 1);
@@ -238,7 +241,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
        else if (current->thread.fault_addr != NULL)
                panic("fault_addr set but no fault catcher");
        else if (!is_user && arch_fixup(ip, regs))
-               return 0;
+               goto out;
 
        if (!is_user) {
                show_regs(container_of(regs, struct pt_regs, regs));
@@ -262,6 +265,11 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
                current->thread.arch.faultinfo = fi;
                force_sig_info(SIGSEGV, &si, current);
        }
+
+out:
+       if (regs)
+               current->thread.segv_regs = NULL;
+
        return 0;
 }
 
index 87df5e3..016adf0 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/sched.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
+#include <asm/sections.h>
 #include <asm/setup.h>
 #include <as-layout.h>
 #include <arch.h>
@@ -234,7 +235,6 @@ static int panic_exit(struct notifier_block *self, unsigned long unused1,
                      void *unused2)
 {
        bust_spinlocks(1);
-       show_regs(&(current->thread.regs));
        bust_spinlocks(0);
        uml_exitcode = 1;
        os_dump_core();
index 905924b..7b605e4 100644 (file)
@@ -304,3 +304,11 @@ int set_signals(int enable)
 
        return ret;
 }
+
+int os_is_signal_stack(void)
+{
+       stack_t ss;
+       sigaltstack(NULL, &ss);
+
+       return ss.ss_flags & SS_ONSTACK;
+}
index 82cdd89..a7ba27b 100644 (file)
@@ -1,5 +1,6 @@
 config UNICORE32
        def_bool y
+       select ARCH_MIGHT_HAVE_PC_PARPORT
        select HAVE_MEMBLOCK
        select HAVE_GENERIC_DMA_COHERENT
        select HAVE_DMA_ATTRS
index 0213e37..2e02d13 100644 (file)
@@ -51,12 +51,14 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)
        struct page *pte;
 
        pte = alloc_pages(PGALLOC_GFP, 0);
-       if (pte) {
-               if (!PageHighMem(pte)) {
-                       void *page = page_address(pte);
-                       clean_dcache_area(page, PTRS_PER_PTE * sizeof(pte_t));
-               }
-               pgtable_page_ctor(pte);
+       if (!pte)
+               return NULL;
+       if (!PageHighMem(pte)) {
+               void *page = page_address(pte);
+               clean_dcache_area(page, PTRS_PER_PTE * sizeof(pte_t));
+       }
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
        }
 
        return pte;
index 818b4a1..af36d8e 100644 (file)
@@ -118,12 +118,6 @@ static inline struct thread_info *current_thread_info(void)
 #endif
 
 /*
- * We use bit 30 of the preempt_count to indicate that kernel
- * preemption is occurring.  See <asm/hardirq.h>.
- */
-#define PREEMPT_ACTIVE 0x40000000
-
-/*
  * thread information flags:
  *  TIF_SYSCALL_TRACE  - syscall trace active
  *  TIF_SIGPENDING     - signal pending
index 181108b..0c6618e 100644 (file)
@@ -54,6 +54,7 @@ static struct platform_pwm_backlight_data nb0916_backlight_data = {
        .max_brightness = 100,
        .dft_brightness = 100,
        .pwm_period_ns  = 70 * 1024,
+       .enable_gpio    = -1,
 };
 
 static struct gpio_keys_button nb0916_gpio_keys[] = {
index 6e3e1cb..e903c71 100644 (file)
@@ -22,6 +22,7 @@ config X86_64
 config X86
        def_bool y
        select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
+       select ARCH_MIGHT_HAVE_PC_PARPORT
        select HAVE_AOUT if X86_32
        select HAVE_UNSTABLE_SCHED_CLOCK
        select ARCH_SUPPORTS_NUMA_BALANCING
@@ -90,7 +91,6 @@ config X86
        select GENERIC_IRQ_SHOW
        select GENERIC_CLOCKEVENTS_MIN_ADJUST
        select IRQ_FORCED_THREADING
-       select USE_GENERIC_SMP_HELPERS if SMP
        select HAVE_BPF_JIT if X86_64
        select HAVE_ARCH_TRANSPARENT_HUGEPAGE
        select CLKEVT_I8253
@@ -1885,6 +1885,10 @@ config USE_PERCPU_NUMA_NODE_ID
        def_bool y
        depends on NUMA
 
+config ARCH_ENABLE_SPLIT_PMD_PTLOCK
+       def_bool y
+       depends on X86_64 || X86_PAE
+
 menu "Power management and ACPI options"
 
 config ARCH_HIBERNATION_HEADER
index 15f960c..24ec121 100644 (file)
@@ -274,13 +274,17 @@ struct x86_emulate_ctxt {
 
        bool guest_mode; /* guest running a nested guest */
        bool perm_ok; /* do not check permissions if true */
-       bool only_vendor_specific_insn;
+       bool ud;        /* inject an #UD if host doesn't support insn */
 
        bool have_exception;
        struct x86_exception exception;
 
-       /* decode cache */
-       u8 twobyte;
+       /*
+        * decode cache
+        */
+
+       /* current opcode length in bytes */
+       u8 opcode_len;
        u8 b;
        u8 intercept;
        u8 lock_prefix;
index c76ff74..ae5d783 100644 (file)
 #define KVM_HPAGE_MASK(x)      (~(KVM_HPAGE_SIZE(x) - 1))
 #define KVM_PAGES_PER_HPAGE(x) (KVM_HPAGE_SIZE(x) / PAGE_SIZE)
 
+static inline gfn_t gfn_to_index(gfn_t gfn, gfn_t base_gfn, int level)
+{
+       /* KVM_HPAGE_GFN_SHIFT(PT_PAGE_TABLE_LEVEL) must be 0. */
+       return (gfn >> KVM_HPAGE_GFN_SHIFT(level)) -
+               (base_gfn >> KVM_HPAGE_GFN_SHIFT(level));
+}
+
 #define SELECTOR_TI_MASK (1 << 2)
 #define SELECTOR_RPL_MASK 0x03
 
@@ -253,7 +260,6 @@ struct kvm_pio_request {
  * mode.
  */
 struct kvm_mmu {
-       void (*new_cr3)(struct kvm_vcpu *vcpu);
        void (*set_cr3)(struct kvm_vcpu *vcpu, unsigned long root);
        unsigned long (*get_cr3)(struct kvm_vcpu *vcpu);
        u64 (*get_pdptr)(struct kvm_vcpu *vcpu, int index);
@@ -261,7 +267,6 @@ struct kvm_mmu {
                          bool prefault);
        void (*inject_page_fault)(struct kvm_vcpu *vcpu,
                                  struct x86_exception *fault);
-       void (*free)(struct kvm_vcpu *vcpu);
        gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva, u32 access,
                            struct x86_exception *exception);
        gpa_t (*translate_gpa)(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access);
@@ -389,6 +394,8 @@ struct kvm_vcpu_arch {
 
        struct fpu guest_fpu;
        u64 xcr0;
+       u64 guest_supported_xcr0;
+       u32 guest_xstate_size;
 
        struct kvm_pio_request pio;
        void *pio_data;
@@ -557,7 +564,9 @@ struct kvm_arch {
 
        struct list_head assigned_dev_head;
        struct iommu_domain *iommu_domain;
-       int iommu_flags;
+       bool iommu_noncoherent;
+#define __KVM_HAVE_ARCH_NONCOHERENT_DMA
+       atomic_t noncoherent_dma_count;
        struct kvm_pic *vpic;
        struct kvm_ioapic *vioapic;
        struct kvm_pit *vpit;
@@ -780,11 +789,11 @@ void kvm_mmu_module_exit(void);
 
 void kvm_mmu_destroy(struct kvm_vcpu *vcpu);
 int kvm_mmu_create(struct kvm_vcpu *vcpu);
-int kvm_mmu_setup(struct kvm_vcpu *vcpu);
+void kvm_mmu_setup(struct kvm_vcpu *vcpu);
 void kvm_mmu_set_mask_ptes(u64 user_mask, u64 accessed_mask,
                u64 dirty_mask, u64 nx_mask, u64 x_mask);
 
-int kvm_mmu_reset_context(struct kvm_vcpu *vcpu);
+void kvm_mmu_reset_context(struct kvm_vcpu *vcpu);
 void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot);
 void kvm_mmu_write_protect_pt_masked(struct kvm *kvm,
                                     struct kvm_memory_slot *slot,
@@ -922,13 +931,11 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
 int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code,
                       void *insn, int insn_len);
 void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva);
+void kvm_mmu_new_cr3(struct kvm_vcpu *vcpu);
 
 void kvm_enable_tdp(void);
 void kvm_disable_tdp(void);
 
-int complete_pio(struct kvm_vcpu *vcpu);
-bool kvm_check_iopl(struct kvm_vcpu *vcpu);
-
 static inline gpa_t translate_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access)
 {
        return gpa;
index 7d74432..947b5c4 100644 (file)
@@ -15,7 +15,7 @@ struct pci_sysdata {
        int             domain;         /* PCI domain */
        int             node;           /* NUMA node */
 #ifdef CONFIG_ACPI
-       void            *acpi;          /* ACPI-specific data */
+       struct acpi_device *companion;  /* ACPI companion device */
 #endif
 #ifdef CONFIG_X86_64
        void            *iommu;         /* IOMMU private data */
index b4389a4..c4412e9 100644 (file)
@@ -80,12 +80,21 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd,
 #if PAGETABLE_LEVELS > 2
 static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
 {
-       return (pmd_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT);
+       struct page *page;
+       page = alloc_pages(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO, 0);
+       if (!page)
+               return NULL;
+       if (!pgtable_pmd_page_ctor(page)) {
+               __free_pages(page, 0);
+               return NULL;
+       }
+       return (pmd_t *)page_address(page);
 }
 
 static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
 {
        BUG_ON((unsigned long)pmd & (PAGE_SIZE-1));
+       pgtable_pmd_page_dtor(virt_to_page(pmd));
        free_page((unsigned long)pmd);
 }
 
index be8269b..d6b078e 100644 (file)
@@ -14,6 +14,8 @@ void pvclock_read_wallclock(struct pvclock_wall_clock *wall,
                            struct timespec *ts);
 void pvclock_resume(void);
 
+void pvclock_touch_watchdogs(void);
+
 /*
  * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
  * yielding a 64-bit result.
index c46a46b..3ba3de4 100644 (file)
@@ -153,8 +153,6 @@ struct thread_info {
 #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY)
 #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW)
 
-#define PREEMPT_ACTIVE         0x10000000
-
 #ifdef CONFIG_X86_32
 
 #define STACK_WARN     (THREAD_SIZE/8)
diff --git a/arch/x86/include/asm/xen/page-coherent.h b/arch/x86/include/asm/xen/page-coherent.h
new file mode 100644 (file)
index 0000000..7f02fe4
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _ASM_X86_XEN_PAGE_COHERENT_H
+#define _ASM_X86_XEN_PAGE_COHERENT_H
+
+#include <asm/page.h>
+#include <linux/dma-attrs.h>
+#include <linux/dma-mapping.h>
+
+static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size,
+               dma_addr_t *dma_handle, gfp_t flags,
+               struct dma_attrs *attrs)
+{
+       void *vstart = (void*)__get_free_pages(flags, get_order(size));
+       *dma_handle = virt_to_phys(vstart);
+       return vstart;
+}
+
+static inline void xen_free_coherent_pages(struct device *hwdev, size_t size,
+               void *cpu_addr, dma_addr_t dma_handle,
+               struct dma_attrs *attrs)
+{
+       free_pages((unsigned long) cpu_addr, get_order(size));
+}
+
+static inline void xen_dma_map_page(struct device *hwdev, struct page *page,
+            unsigned long offset, size_t size, enum dma_data_direction dir,
+            struct dma_attrs *attrs) { }
+
+static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
+               size_t size, enum dma_data_direction dir,
+               struct dma_attrs *attrs) { }
+
+static inline void xen_dma_sync_single_for_cpu(struct device *hwdev,
+               dma_addr_t handle, size_t size, enum dma_data_direction dir) { }
+
+static inline void xen_dma_sync_single_for_device(struct device *hwdev,
+               dma_addr_t handle, size_t size, enum dma_data_direction dir) { }
+
+#endif /* _ASM_X86_XEN_PAGE_COHERENT_H */
index 5d9a303..d3a8778 100644 (file)
@@ -211,9 +211,9 @@ struct kvm_cpuid_entry2 {
        __u32 padding[3];
 };
 
-#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX 1
-#define KVM_CPUID_FLAG_STATEFUL_FUNC    2
-#define KVM_CPUID_FLAG_STATE_READ_NEXT  4
+#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX                BIT(0)
+#define KVM_CPUID_FLAG_STATEFUL_FUNC           BIT(1)
+#define KVM_CPUID_FLAG_STATE_READ_NEXT         BIT(2)
 
 /* for KVM_SET_CPUID2 */
 struct kvm_cpuid2 {
index bb04650..37813b5 100644 (file)
 #define MSR_PP1_ENERGY_STATUS          0x00000641
 #define MSR_PP1_POLICY                 0x00000642
 
+#define MSR_CORE_C1_RES                        0x00000660
+
 #define MSR_AMD64_MC0_MASK             0xc0010044
 
 #define MSR_IA32_MCx_CTL(x)            (MSR_IA32_MC0_CTL + 4*(x))
 
 /* MSR_IA32_VMX_MISC bits */
 #define MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS (1ULL << 29)
+#define MSR_IA32_VMX_MISC_PREEMPTION_TIMER_SCALE   0x1F
 /* AMD-V MSRs */
 
 #define MSR_VM_CR                       0xc0010114
index ed165d6..d278736 100644 (file)
@@ -62,6 +62,7 @@ unsigned disabled_cpus;
 
 /* Processor that is doing the boot up */
 unsigned int boot_cpu_physical_apicid = -1U;
+EXPORT_SYMBOL_GPL(boot_cpu_physical_apicid);
 
 /*
  * The highest APIC ID seen during enumeration.
index 3daece7..bca023b 100644 (file)
@@ -339,7 +339,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c)
 #endif
 
 /*
- * On a AMD dual core setup the lower bits of the APIC id distingush the cores.
+ * On a AMD dual core setup the lower bits of the APIC id distinguish the cores.
  * Assumes number of cores is a power of two.
  */
 static void amd_detect_cmp(struct cpuinfo_x86 *c)
index 1414c90..0641113 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *     Routines to indentify caches on Intel CPU.
+ *     Routines to identify caches on Intel CPU.
  *
  *     Changes:
  *     Venkatesh Pallipadi     : Adding cache identification through cpuid(4)
index f2cc63e..b6f794a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *     Routines to indentify additional cpu features that are scattered in
+ *     Routines to identify additional cpu features that are scattered in
  *     cpuid space.
  */
 #include <linux/cpu.h>
index b3cd3eb..96f958d 100644 (file)
@@ -313,6 +313,16 @@ static size_t __init gen6_stolen_size(int num, int slot, int func)
        return gmch_ctrl << 25; /* 32 MB units */
 }
 
+static inline size_t gen8_stolen_size(int num, int slot, int func)
+{
+       u16 gmch_ctrl;
+
+       gmch_ctrl = read_pci_config_16(num, slot, func, SNB_GMCH_CTRL);
+       gmch_ctrl >>= BDW_GMCH_GMS_SHIFT;
+       gmch_ctrl &= BDW_GMCH_GMS_MASK;
+       return gmch_ctrl << 25; /* 32 MB units */
+}
+
 typedef size_t (*stolen_size_fn)(int num, int slot, int func);
 
 static struct pci_device_id intel_stolen_ids[] __initdata = {
@@ -336,6 +346,8 @@ static struct pci_device_id intel_stolen_ids[] __initdata = {
        INTEL_IVB_D_IDS(gen6_stolen_size),
        INTEL_HSW_D_IDS(gen6_stolen_size),
        INTEL_HSW_M_IDS(gen6_stolen_size),
+       INTEL_BDW_M_IDS(gen8_stolen_size),
+       INTEL_BDW_D_IDS(gen8_stolen_size)
 };
 
 static void __init intel_graphics_stolen(int num, int slot, int func)
index 42a392a..d4bdd25 100644 (file)
@@ -248,6 +248,15 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
        return ret;
 }
 
+static int is_ftrace_caller(unsigned long ip)
+{
+       if (ip == (unsigned long)(&ftrace_call) ||
+               ip == (unsigned long)(&ftrace_regs_call))
+               return 1;
+
+       return 0;
+}
+
 /*
  * A breakpoint was added to the code address we are about to
  * modify, and this is the handle that will just skip over it.
@@ -257,10 +266,13 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
  */
 int ftrace_int3_handler(struct pt_regs *regs)
 {
+       unsigned long ip;
+
        if (WARN_ON_ONCE(!regs))
                return 0;
 
-       if (!ftrace_location(regs->ip - 1))
+       ip = regs->ip - 1;
+       if (!ftrace_location(ip) && !is_ftrace_caller(ip))
                return 0;
 
        regs->ip += MCOUNT_INSN_SIZE - 1;
index 1570e07..e604109 100644 (file)
@@ -139,6 +139,7 @@ bool kvm_check_and_clear_guest_paused(void)
        src = &hv_clock[cpu].pvti;
        if ((src->flags & PVCLOCK_GUEST_STOPPED) != 0) {
                src->flags &= ~PVCLOCK_GUEST_STOPPED;
+               pvclock_touch_watchdogs();
                ret = true;
        }
 
index a16bae3..2f355d2 100644 (file)
@@ -43,6 +43,14 @@ unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src)
        return pv_tsc_khz;
 }
 
+void pvclock_touch_watchdogs(void)
+{
+       touch_softlockup_watchdog_sync();
+       clocksource_touch_watchdog();
+       rcu_cpu_stall_reset();
+       reset_hung_task_detector();
+}
+
 static atomic64_t last_value = ATOMIC64_INIT(0);
 
 void pvclock_resume(void)
@@ -74,6 +82,11 @@ cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
                version = __pvclock_read_cycles(src, &ret, &flags);
        } while ((src->version & 1) || version != src->version);
 
+       if (unlikely((flags & PVCLOCK_GUEST_STOPPED) != 0)) {
+               src->flags &= ~PVCLOCK_GUEST_STOPPED;
+               pvclock_touch_watchdogs();
+       }
+
        if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&
                (flags & PVCLOCK_TSC_STABLE_BIT))
                return ret;
index a47a3e5..b89c5db 100644 (file)
@@ -38,6 +38,7 @@ config KVM
        select PERF_EVENTS
        select HAVE_KVM_MSI
        select HAVE_KVM_CPU_RELAX_INTERCEPT
+       select KVM_VFIO
        ---help---
          Support hosting fully virtualized guest machines using hardware
          virtualization extensions.  You will need a fairly recent
index bf4fb04..25d22b2 100644 (file)
@@ -9,7 +9,7 @@ KVM := ../../../virt/kvm
 
 kvm-y                  += $(KVM)/kvm_main.o $(KVM)/ioapic.o \
                                $(KVM)/coalesced_mmio.o $(KVM)/irq_comm.o \
-                               $(KVM)/eventfd.o $(KVM)/irqchip.o
+                               $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o
 kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT)    += $(KVM)/assigned-dev.o $(KVM)/iommu.o
 kvm-$(CONFIG_KVM_ASYNC_PF)     += $(KVM)/async_pf.o
 
index b110fe6..c697625 100644 (file)
 #include "mmu.h"
 #include "trace.h"
 
+static u32 xstate_required_size(u64 xstate_bv)
+{
+       int feature_bit = 0;
+       u32 ret = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;
+
+       xstate_bv &= ~XSTATE_FPSSE;
+       while (xstate_bv) {
+               if (xstate_bv & 0x1) {
+                       u32 eax, ebx, ecx, edx;
+                       cpuid_count(0xD, feature_bit, &eax, &ebx, &ecx, &edx);
+                       ret = max(ret, eax + ebx);
+               }
+
+               xstate_bv >>= 1;
+               feature_bit++;
+       }
+
+       return ret;
+}
+
 void kvm_update_cpuid(struct kvm_vcpu *vcpu)
 {
        struct kvm_cpuid_entry2 *best;
@@ -46,6 +66,18 @@ void kvm_update_cpuid(struct kvm_vcpu *vcpu)
                        apic->lapic_timer.timer_mode_mask = 1 << 17;
        }
 
+       best = kvm_find_cpuid_entry(vcpu, 0xD, 0);
+       if (!best) {
+               vcpu->arch.guest_supported_xcr0 = 0;
+               vcpu->arch.guest_xstate_size = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;
+       } else {
+               vcpu->arch.guest_supported_xcr0 =
+                       (best->eax | ((u64)best->edx << 32)) &
+                       host_xcr0 & KVM_SUPPORTED_XCR0;
+               vcpu->arch.guest_xstate_size =
+                       xstate_required_size(vcpu->arch.guest_supported_xcr0);
+       }
+
        kvm_pmu_cpuid_update(vcpu);
 }
 
@@ -182,13 +214,35 @@ static bool supported_xcr0_bit(unsigned bit)
 {
        u64 mask = ((u64)1 << bit);
 
-       return mask & (XSTATE_FP | XSTATE_SSE | XSTATE_YMM) & host_xcr0;
+       return mask & KVM_SUPPORTED_XCR0 & host_xcr0;
 }
 
 #define F(x) bit(X86_FEATURE_##x)
 
-static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
-                        u32 index, int *nent, int maxnent)
+static int __do_cpuid_ent_emulated(struct kvm_cpuid_entry2 *entry,
+                                  u32 func, u32 index, int *nent, int maxnent)
+{
+       switch (func) {
+       case 0:
+               entry->eax = 1;         /* only one leaf currently */
+               ++*nent;
+               break;
+       case 1:
+               entry->ecx = F(MOVBE);
+               ++*nent;
+               break;
+       default:
+               break;
+       }
+
+       entry->function = func;
+       entry->index = index;
+
+       return 0;
+}
+
+static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
+                                u32 index, int *nent, int maxnent)
 {
        int r;
        unsigned f_nx = is_efer_nx() ? F(NX) : 0;
@@ -383,6 +437,8 @@ static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
        case 0xd: {
                int idx, i;
 
+               entry->eax &= host_xcr0 & KVM_SUPPORTED_XCR0;
+               entry->edx &= (host_xcr0 & KVM_SUPPORTED_XCR0) >> 32;
                entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
                for (idx = 1, i = 1; idx < 64; ++idx) {
                        if (*nent >= maxnent)
@@ -481,6 +537,15 @@ out:
        return r;
 }
 
+static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 func,
+                       u32 idx, int *nent, int maxnent, unsigned int type)
+{
+       if (type == KVM_GET_EMULATED_CPUID)
+               return __do_cpuid_ent_emulated(entry, func, idx, nent, maxnent);
+
+       return __do_cpuid_ent(entry, func, idx, nent, maxnent);
+}
+
 #undef F
 
 struct kvm_cpuid_param {
@@ -495,8 +560,36 @@ static bool is_centaur_cpu(const struct kvm_cpuid_param *param)
        return boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR;
 }
 
-int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid,
-                                     struct kvm_cpuid_entry2 __user *entries)
+static bool sanity_check_entries(struct kvm_cpuid_entry2 __user *entries,
+                                __u32 num_entries, unsigned int ioctl_type)
+{
+       int i;
+       __u32 pad[3];
+
+       if (ioctl_type != KVM_GET_EMULATED_CPUID)
+               return false;
+
+       /*
+        * We want to make sure that ->padding is being passed clean from
+        * userspace in case we want to use it for something in the future.
+        *
+        * Sadly, this wasn't enforced for KVM_GET_SUPPORTED_CPUID and so we
+        * have to give ourselves satisfied only with the emulated side. /me
+        * sheds a tear.
+        */
+       for (i = 0; i < num_entries; i++) {
+               if (copy_from_user(pad, entries[i].padding, sizeof(pad)))
+                       return true;
+
+               if (pad[0] || pad[1] || pad[2])
+                       return true;
+       }
+       return false;
+}
+
+int kvm_dev_ioctl_get_cpuid(struct kvm_cpuid2 *cpuid,
+                           struct kvm_cpuid_entry2 __user *entries,
+                           unsigned int type)
 {
        struct kvm_cpuid_entry2 *cpuid_entries;
        int limit, nent = 0, r = -E2BIG, i;
@@ -513,8 +606,12 @@ int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid,
                goto out;
        if (cpuid->nent > KVM_MAX_CPUID_ENTRIES)
                cpuid->nent = KVM_MAX_CPUID_ENTRIES;
+
+       if (sanity_check_entries(entries, cpuid->nent, type))
+               return -EINVAL;
+
        r = -ENOMEM;
-       cpuid_entries = vmalloc(sizeof(struct kvm_cpuid_entry2) * cpuid->nent);
+       cpuid_entries = vzalloc(sizeof(struct kvm_cpuid_entry2) * cpuid->nent);
        if (!cpuid_entries)
                goto out;
 
@@ -526,7 +623,7 @@ int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid,
                        continue;
 
                r = do_cpuid_ent(&cpuid_entries[nent], ent->func, ent->idx,
-                               &nent, cpuid->nent);
+                               &nent, cpuid->nent, type);
 
                if (r)
                        goto out_free;
@@ -537,7 +634,7 @@ int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid,
                limit = cpuid_entries[nent - 1].eax;
                for (func = ent->func + 1; func <= limit && nent < cpuid->nent && r == 0; ++func)
                        r = do_cpuid_ent(&cpuid_entries[nent], func, ent->idx,
-                                    &nent, cpuid->nent);
+                                    &nent, cpuid->nent, type);
 
                if (r)
                        goto out_free;
@@ -661,6 +758,7 @@ void kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
                *edx = best->edx;
        } else
                *eax = *ebx = *ecx = *edx = 0;
+       trace_kvm_cpuid(function, *eax, *ebx, *ecx, *edx);
 }
 EXPORT_SYMBOL_GPL(kvm_cpuid);
 
@@ -676,6 +774,5 @@ void kvm_emulate_cpuid(struct kvm_vcpu *vcpu)
        kvm_register_write(vcpu, VCPU_REGS_RCX, ecx);
        kvm_register_write(vcpu, VCPU_REGS_RDX, edx);
        kvm_x86_ops->skip_emulated_instruction(vcpu);
-       trace_kvm_cpuid(function, eax, ebx, ecx, edx);
 }
 EXPORT_SYMBOL_GPL(kvm_emulate_cpuid);
index b7fd079..f1e4895 100644 (file)
@@ -6,8 +6,9 @@
 void kvm_update_cpuid(struct kvm_vcpu *vcpu);
 struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
                                              u32 function, u32 index);
-int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid,
-                                     struct kvm_cpuid_entry2 __user *entries);
+int kvm_dev_ioctl_get_cpuid(struct kvm_cpuid2 *cpuid,
+                           struct kvm_cpuid_entry2 __user *entries,
+                           unsigned int type);
 int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu *vcpu,
                             struct kvm_cpuid *cpuid,
                             struct kvm_cpuid_entry __user *entries);
index ddc3f3d..07ffca0 100644 (file)
 #define Mov         (1<<20)
 /* Misc flags */
 #define Prot        (1<<21) /* instruction generates #UD if not in prot-mode */
-#define VendorSpecific (1<<22) /* Vendor specific instruction */
+#define EmulateOnUD (1<<22) /* Emulate if unsupported by the host */
 #define NoAccess    (1<<23) /* Don't access memory (lea/invlpg/verr etc) */
 #define Op3264      (1<<24) /* Operand is 64b in long mode, 32b otherwise */
 #define Undefined   (1<<25) /* No Such Instruction */
@@ -785,9 +785,10 @@ static int do_insn_fetch(struct x86_emulate_ctxt *ctxt,
  * @highbyte_regs specifies whether to decode AH,CH,DH,BH.
  */
 static void *decode_register(struct x86_emulate_ctxt *ctxt, u8 modrm_reg,
-                            int highbyte_regs)
+                            int byteop)
 {
        void *p;
+       int highbyte_regs = (ctxt->rex_prefix == 0) && byteop;
 
        if (highbyte_regs && modrm_reg >= 4 && modrm_reg < 8)
                p = (unsigned char *)reg_rmw(ctxt, modrm_reg & 3) + 1;
@@ -1024,7 +1025,6 @@ static void decode_register_operand(struct x86_emulate_ctxt *ctxt,
                                    struct operand *op)
 {
        unsigned reg = ctxt->modrm_reg;
-       int highbyte_regs = ctxt->rex_prefix == 0;
 
        if (!(ctxt->d & ModRM))
                reg = (ctxt->b & 7) | ((ctxt->rex_prefix & 1) << 3);
@@ -1045,13 +1045,9 @@ static void decode_register_operand(struct x86_emulate_ctxt *ctxt,
        }
 
        op->type = OP_REG;
-       if (ctxt->d & ByteOp) {
-               op->addr.reg = decode_register(ctxt, reg, highbyte_regs);
-               op->bytes = 1;
-       } else {
-               op->addr.reg = decode_register(ctxt, reg, 0);
-               op->bytes = ctxt->op_bytes;
-       }
+       op->bytes = (ctxt->d & ByteOp) ? 1 : ctxt->op_bytes;
+       op->addr.reg = decode_register(ctxt, reg, ctxt->d & ByteOp);
+
        fetch_register_operand(op);
        op->orig_val = op->val;
 }
@@ -1082,12 +1078,10 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
        ctxt->modrm_seg = VCPU_SREG_DS;
 
        if (ctxt->modrm_mod == 3) {
-               int highbyte_regs = ctxt->rex_prefix == 0;
-
                op->type = OP_REG;
                op->bytes = (ctxt->d & ByteOp) ? 1 : ctxt->op_bytes;
                op->addr.reg = decode_register(ctxt, ctxt->modrm_rm,
-                                              highbyte_regs && (ctxt->d & ByteOp));
+                               ctxt->d & ByteOp);
                if (ctxt->d & Sse) {
                        op->type = OP_XMM;
                        op->bytes = 16;
@@ -2961,6 +2955,46 @@ static int em_mov(struct x86_emulate_ctxt *ctxt)
        return X86EMUL_CONTINUE;
 }
 
+#define FFL(x) bit(X86_FEATURE_##x)
+
+static int em_movbe(struct x86_emulate_ctxt *ctxt)
+{
+       u32 ebx, ecx, edx, eax = 1;
+       u16 tmp;
+
+       /*
+        * Check MOVBE is set in the guest-visible CPUID leaf.
+        */
+       ctxt->ops->get_cpuid(ctxt, &eax, &ebx, &ecx, &edx);
+       if (!(ecx & FFL(MOVBE)))
+               return emulate_ud(ctxt);
+
+       switch (ctxt->op_bytes) {
+       case 2:
+               /*
+                * From MOVBE definition: "...When the operand size is 16 bits,
+                * the upper word of the destination register remains unchanged
+                * ..."
+                *
+                * Both casting ->valptr and ->val to u16 breaks strict aliasing
+                * rules so we have to do the operation almost per hand.
+                */
+               tmp = (u16)ctxt->src.val;
+               ctxt->dst.val &= ~0xffffUL;
+               ctxt->dst.val |= (unsigned long)swab16(tmp);
+               break;
+       case 4:
+               ctxt->dst.val = swab32((u32)ctxt->src.val);
+               break;
+       case 8:
+               ctxt->dst.val = swab64(ctxt->src.val);
+               break;
+       default:
+               return X86EMUL_PROPAGATE_FAULT;
+       }
+       return X86EMUL_CONTINUE;
+}
+
 static int em_cr_write(struct x86_emulate_ctxt *ctxt)
 {
        if (ctxt->ops->set_cr(ctxt, ctxt->modrm_reg, ctxt->src.val))
@@ -3256,6 +3290,18 @@ static int em_cpuid(struct x86_emulate_ctxt *ctxt)
        return X86EMUL_CONTINUE;
 }
 
+static int em_sahf(struct x86_emulate_ctxt *ctxt)
+{
+       u32 flags;
+
+       flags = EFLG_CF | EFLG_PF | EFLG_AF | EFLG_ZF | EFLG_SF;
+       flags &= *reg_rmw(ctxt, VCPU_REGS_RAX) >> 8;
+
+       ctxt->eflags &= ~0xffUL;
+       ctxt->eflags |= flags | X86_EFLAGS_FIXED;
+       return X86EMUL_CONTINUE;
+}
+
 static int em_lahf(struct x86_emulate_ctxt *ctxt)
 {
        *reg_rmw(ctxt, VCPU_REGS_RAX) &= ~0xff00UL;
@@ -3502,7 +3548,7 @@ static const struct opcode group7_rm1[] = {
 
 static const struct opcode group7_rm3[] = {
        DIP(SrcNone | Prot | Priv,              vmrun,          check_svme_pa),
-       II(SrcNone  | Prot | VendorSpecific,    em_vmmcall,     vmmcall),
+       II(SrcNone  | Prot | EmulateOnUD,       em_vmmcall,     vmmcall),
        DIP(SrcNone | Prot | Priv,              vmload,         check_svme_pa),
        DIP(SrcNone | Prot | Priv,              vmsave,         check_svme_pa),
        DIP(SrcNone | Prot | Priv,              stgi,           check_svme),
@@ -3587,7 +3633,7 @@ static const struct group_dual group7 = { {
        II(SrcMem16 | Mov | Priv,               em_lmsw, lmsw),
        II(SrcMem | ByteOp | Priv | NoAccess,   em_invlpg, invlpg),
 }, {
-       I(SrcNone | Priv | VendorSpecific,      em_vmcall),
+       I(SrcNone | Priv | EmulateOnUD, em_vmcall),
        EXT(0, group7_rm1),
        N, EXT(0, group7_rm3),
        II(SrcNone | DstMem | Mov,              em_smsw, smsw), N,
@@ -3750,7 +3796,8 @@ static const struct opcode opcode_table[256] = {
        D(DstAcc | SrcNone), I(ImplicitOps | SrcAcc, em_cwd),
        I(SrcImmFAddr | No64, em_call_far), N,
        II(ImplicitOps | Stack, em_pushf, pushf),
-       II(ImplicitOps | Stack, em_popf, popf), N, I(ImplicitOps, em_lahf),
+       II(ImplicitOps | Stack, em_popf, popf),
+       I(ImplicitOps, em_sahf), I(ImplicitOps, em_lahf),
        /* 0xA0 - 0xA7 */
        I2bv(DstAcc | SrcMem | Mov | MemAbs, em_mov),
        I2bv(DstMem | SrcAcc | Mov | MemAbs | PageTable, em_mov),
@@ -3810,7 +3857,7 @@ static const struct opcode opcode_table[256] = {
 static const struct opcode twobyte_table[256] = {
        /* 0x00 - 0x0F */
        G(0, group6), GD(0, &group7), N, N,
-       N, I(ImplicitOps | VendorSpecific, em_syscall),
+       N, I(ImplicitOps | EmulateOnUD, em_syscall),
        II(ImplicitOps | Priv, em_clts, clts), N,
        DI(ImplicitOps | Priv, invd), DI(ImplicitOps | Priv, wbinvd), N, N,
        N, D(ImplicitOps | ModRM), N, N,
@@ -3830,8 +3877,8 @@ static const struct opcode twobyte_table[256] = {
        IIP(ImplicitOps, em_rdtsc, rdtsc, check_rdtsc),
        II(ImplicitOps | Priv, em_rdmsr, rdmsr),
        IIP(ImplicitOps, em_rdpmc, rdpmc, check_rdpmc),
-       I(ImplicitOps | VendorSpecific, em_sysenter),
-       I(ImplicitOps | Priv | VendorSpecific, em_sysexit),
+       I(ImplicitOps | EmulateOnUD, em_sysenter),
+       I(ImplicitOps | Priv | EmulateOnUD, em_sysexit),
        N, N,
        N, N, N, N, N, N, N, N,
        /* 0x40 - 0x4F */
@@ -3892,6 +3939,30 @@ static const struct opcode twobyte_table[256] = {
        N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N
 };
 
+static const struct gprefix three_byte_0f_38_f0 = {
+       I(DstReg | SrcMem | Mov, em_movbe), N, N, N
+};
+
+static const struct gprefix three_byte_0f_38_f1 = {
+       I(DstMem | SrcReg | Mov, em_movbe), N, N, N
+};
+
+/*
+ * Insns below are selected by the prefix which indexed by the third opcode
+ * byte.
+ */
+static const struct opcode opcode_map_0f_38[256] = {
+       /* 0x00 - 0x7f */
+       X16(N), X16(N), X16(N), X16(N), X16(N), X16(N), X16(N), X16(N),
+       /* 0x80 - 0xef */
+       X16(N), X16(N), X16(N), X16(N), X16(N), X16(N), X16(N),
+       /* 0xf0 - 0xf1 */
+       GP(EmulateOnUD | ModRM | Prefix, &three_byte_0f_38_f0),
+       GP(EmulateOnUD | ModRM | Prefix, &three_byte_0f_38_f1),
+       /* 0xf2 - 0xff */
+       N, N, X4(N), X8(N)
+};
+
 #undef D
 #undef N
 #undef G
@@ -4040,7 +4111,8 @@ static int decode_operand(struct x86_emulate_ctxt *ctxt, struct operand *op,
        case OpMem8:
                ctxt->memop.bytes = 1;
                if (ctxt->memop.type == OP_REG) {
-                       ctxt->memop.addr.reg = decode_register(ctxt, ctxt->modrm_rm, 1);
+                       ctxt->memop.addr.reg = decode_register(ctxt,
+                                       ctxt->modrm_rm, true);
                        fetch_register_operand(&ctxt->memop);
                }
                goto mem_common;
@@ -4126,6 +4198,7 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len)
        ctxt->_eip = ctxt->eip;
        ctxt->fetch.start = ctxt->_eip;
        ctxt->fetch.end = ctxt->fetch.start + insn_len;
+       ctxt->opcode_len = 1;
        if (insn_len > 0)
                memcpy(ctxt->fetch.data, insn, insn_len);
 
@@ -4208,9 +4281,16 @@ done_prefixes:
        opcode = opcode_table[ctxt->b];
        /* Two-byte opcode? */
        if (ctxt->b == 0x0f) {
-               ctxt->twobyte = 1;
+               ctxt->opcode_len = 2;
                ctxt->b = insn_fetch(u8, ctxt);
                opcode = twobyte_table[ctxt->b];
+
+               /* 0F_38 opcode map */
+               if (ctxt->b == 0x38) {
+                       ctxt->opcode_len = 3;
+                       ctxt->b = insn_fetch(u8, ctxt);
+                       opcode = opcode_map_0f_38[ctxt->b];
+               }
        }
        ctxt->d = opcode.flags;
 
@@ -4267,7 +4347,7 @@ done_prefixes:
        if (ctxt->d == 0 || (ctxt->d & NotImpl))
                return EMULATION_FAILED;
 
-       if (!(ctxt->d & VendorSpecific) && ctxt->only_vendor_specific_insn)
+       if (!(ctxt->d & EmulateOnUD) && ctxt->ud)
                return EMULATION_FAILED;
 
        if (mode == X86EMUL_MODE_PROT64 && (ctxt->d & Stack))
@@ -4540,8 +4620,10 @@ special_insn:
                goto writeback;
        }
 
-       if (ctxt->twobyte)
+       if (ctxt->opcode_len == 2)
                goto twobyte_insn;
+       else if (ctxt->opcode_len == 3)
+               goto threebyte_insn;
 
        switch (ctxt->b) {
        case 0x63:              /* movsxd */
@@ -4726,6 +4808,8 @@ twobyte_insn:
                goto cannot_emulate;
        }
 
+threebyte_insn:
+
        if (rc != X86EMUL_CONTINUE)
                goto done;
 
index dce0df8..40772ef 100644 (file)
@@ -2570,11 +2570,6 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
        kvm_release_pfn_clean(pfn);
 }
 
-static void nonpaging_new_cr3(struct kvm_vcpu *vcpu)
-{
-       mmu_free_roots(vcpu);
-}
-
 static pfn_t pte_prefetch_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn,
                                     bool no_dirty_log)
 {
@@ -3424,18 +3419,11 @@ out_unlock:
        return 0;
 }
 
-static void nonpaging_free(struct kvm_vcpu *vcpu)
-{
-       mmu_free_roots(vcpu);
-}
-
-static int nonpaging_init_context(struct kvm_vcpu *vcpu,
-                                 struct kvm_mmu *context)
+static void nonpaging_init_context(struct kvm_vcpu *vcpu,
+                                  struct kvm_mmu *context)
 {
-       context->new_cr3 = nonpaging_new_cr3;
        context->page_fault = nonpaging_page_fault;
        context->gva_to_gpa = nonpaging_gva_to_gpa;
-       context->free = nonpaging_free;
        context->sync_page = nonpaging_sync_page;
        context->invlpg = nonpaging_invlpg;
        context->update_pte = nonpaging_update_pte;
@@ -3444,7 +3432,6 @@ static int nonpaging_init_context(struct kvm_vcpu *vcpu,
        context->root_hpa = INVALID_PAGE;
        context->direct_map = true;
        context->nx = false;
-       return 0;
 }
 
 void kvm_mmu_flush_tlb(struct kvm_vcpu *vcpu)
@@ -3454,9 +3441,8 @@ void kvm_mmu_flush_tlb(struct kvm_vcpu *vcpu)
 }
 EXPORT_SYMBOL_GPL(kvm_mmu_flush_tlb);
 
-static void paging_new_cr3(struct kvm_vcpu *vcpu)
+void kvm_mmu_new_cr3(struct kvm_vcpu *vcpu)
 {
-       pgprintk("%s: cr3 %lx\n", __func__, kvm_read_cr3(vcpu));
        mmu_free_roots(vcpu);
 }
 
@@ -3471,11 +3457,6 @@ static void inject_page_fault(struct kvm_vcpu *vcpu,
        vcpu->arch.mmu.inject_page_fault(vcpu, fault);
 }
 
-static void paging_free(struct kvm_vcpu *vcpu)
-{
-       nonpaging_free(vcpu);
-}
-
 static bool sync_mmio_spte(struct kvm *kvm, u64 *sptep, gfn_t gfn,
                           unsigned access, int *nr_present)
 {
@@ -3665,9 +3646,9 @@ static void update_last_pte_bitmap(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu)
        mmu->last_pte_bitmap = map;
 }
 
-static int paging64_init_context_common(struct kvm_vcpu *vcpu,
-                                       struct kvm_mmu *context,
-                                       int level)
+static void paging64_init_context_common(struct kvm_vcpu *vcpu,
+                                        struct kvm_mmu *context,
+                                        int level)
 {
        context->nx = is_nx(vcpu);
        context->root_level = level;
@@ -3677,27 +3658,24 @@ static int paging64_init_context_common(struct kvm_vcpu *vcpu,
        update_last_pte_bitmap(vcpu, context);
 
        ASSERT(is_pae(vcpu));
-       context->new_cr3 = paging_new_cr3;
        context->page_fault = paging64_page_fault;
        context->gva_to_gpa = paging64_gva_to_gpa;
        context->sync_page = paging64_sync_page;
        context->invlpg = paging64_invlpg;
        context->update_pte = paging64_update_pte;
-       context->free = paging_free;
        context->shadow_root_level = level;
        context->root_hpa = INVALID_PAGE;
        context->direct_map = false;
-       return 0;
 }
 
-static int paging64_init_context(struct kvm_vcpu *vcpu,
-                                struct kvm_mmu *context)
+static void paging64_init_context(struct kvm_vcpu *vcpu,
+                                 struct kvm_mmu *context)
 {
-       return paging64_init_context_common(vcpu, context, PT64_ROOT_LEVEL);
+       paging64_init_context_common(vcpu, context, PT64_ROOT_LEVEL);
 }
 
-static int paging32_init_context(struct kvm_vcpu *vcpu,
-                                struct kvm_mmu *context)
+static void paging32_init_context(struct kvm_vcpu *vcpu,
+                                 struct kvm_mmu *context)
 {
        context->nx = false;
        context->root_level = PT32_ROOT_LEVEL;
@@ -3706,33 +3684,28 @@ static int paging32_init_context(struct kvm_vcpu *vcpu,
        update_permission_bitmask(vcpu, context, false);
        update_last_pte_bitmap(vcpu, context);
 
-       context->new_cr3 = paging_new_cr3;
        context->page_fault = paging32_page_fault;
        context->gva_to_gpa = paging32_gva_to_gpa;
-       context->free = paging_free;
        context->sync_page = paging32_sync_page;
        context->invlpg = paging32_invlpg;
        context->update_pte = paging32_update_pte;
        context->shadow_root_level = PT32E_ROOT_LEVEL;
        context->root_hpa = INVALID_PAGE;
        context->direct_map = false;
-       return 0;
 }
 
-static int paging32E_init_context(struct kvm_vcpu *vcpu,
-                                 struct kvm_mmu *context)
+static void paging32E_init_context(struct kvm_vcpu *vcpu,
+                                  struct kvm_mmu *context)
 {
-       return paging64_init_context_common(vcpu, context, PT32E_ROOT_LEVEL);
+       paging64_init_context_common(vcpu, context, PT32E_ROOT_LEVEL);
 }
 
-static int init_kvm_tdp_mmu(struct kvm_vcpu *vcpu)
+static void init_kvm_tdp_mmu(struct kvm_vcpu *vcpu)
 {
        struct kvm_mmu *context = vcpu->arch.walk_mmu;
 
        context->base_role.word = 0;
-       context->new_cr3 = nonpaging_new_cr3;
        context->page_fault = tdp_page_fault;
-       context->free = nonpaging_free;
        context->sync_page = nonpaging_sync_page;
        context->invlpg = nonpaging_invlpg;
        context->update_pte = nonpaging_update_pte;
@@ -3767,37 +3740,32 @@ static int init_kvm_tdp_mmu(struct kvm_vcpu *vcpu)
 
        update_permission_bitmask(vcpu, context, false);
        update_last_pte_bitmap(vcpu, context);
-
-       return 0;
 }
 
-int kvm_init_shadow_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context)
+void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context)
 {
-       int r;
        bool smep = kvm_read_cr4_bits(vcpu, X86_CR4_SMEP);
        ASSERT(vcpu);
        ASSERT(!VALID_PAGE(vcpu->arch.mmu.root_hpa));
 
        if (!is_paging(vcpu))
-               r = nonpaging_init_context(vcpu, context);
+               nonpaging_init_context(vcpu, context);
        else if (is_long_mode(vcpu))
-               r = paging64_init_context(vcpu, context);
+               paging64_init_context(vcpu, context);
        else if (is_pae(vcpu))
-               r = paging32E_init_context(vcpu, context);
+               paging32E_init_context(vcpu, context);
        else
-               r = paging32_init_context(vcpu, context);
+               paging32_init_context(vcpu, context);
 
        vcpu->arch.mmu.base_role.nxe = is_nx(vcpu);
        vcpu->arch.mmu.base_role.cr4_pae = !!is_pae(vcpu);
        vcpu->arch.mmu.base_role.cr0_wp  = is_write_protection(vcpu);
        vcpu->arch.mmu.base_role.smep_andnot_wp
                = smep && !is_write_protection(vcpu);
-
-       return r;
 }
 EXPORT_SYMBOL_GPL(kvm_init_shadow_mmu);
 
-int kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context,
+void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context,
                bool execonly)
 {
        ASSERT(vcpu);
@@ -3806,37 +3774,30 @@ int kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context,
        context->shadow_root_level = kvm_x86_ops->get_tdp_level();
 
        context->nx = true;
-       context->new_cr3 = paging_new_cr3;
        context->page_fault = ept_page_fault;
        context->gva_to_gpa = ept_gva_to_gpa;
        context->sync_page = ept_sync_page;
        context->invlpg = ept_invlpg;
        context->update_pte = ept_update_pte;
-       context->free = paging_free;
        context->root_level = context->shadow_root_level;
        context->root_hpa = INVALID_PAGE;
        context->direct_map = false;
 
        update_permission_bitmask(vcpu, context, true);
        reset_rsvds_bits_mask_ept(vcpu, context, execonly);
-
-       return 0;
 }
 EXPORT_SYMBOL_GPL(kvm_init_shadow_ept_mmu);
 
-static int init_kvm_softmmu(struct kvm_vcpu *vcpu)
+static void init_kvm_softmmu(struct kvm_vcpu *vcpu)
 {
-       int r = kvm_init_shadow_mmu(vcpu, vcpu->arch.walk_mmu);
-
+       kvm_init_shadow_mmu(vcpu, vcpu->arch.walk_mmu);
        vcpu->arch.walk_mmu->set_cr3           = kvm_x86_ops->set_cr3;
        vcpu->arch.walk_mmu->get_cr3           = get_cr3;
        vcpu->arch.walk_mmu->get_pdptr         = kvm_pdptr_read;
        vcpu->arch.walk_mmu->inject_page_fault = kvm_inject_page_fault;
-
-       return r;
 }
 
-static int init_kvm_nested_mmu(struct kvm_vcpu *vcpu)
+static void init_kvm_nested_mmu(struct kvm_vcpu *vcpu)
 {
        struct kvm_mmu *g_context = &vcpu->arch.nested_mmu;
 
@@ -3873,11 +3834,9 @@ static int init_kvm_nested_mmu(struct kvm_vcpu *vcpu)
 
        update_permission_bitmask(vcpu, g_context, false);
        update_last_pte_bitmap(vcpu, g_context);
-
-       return 0;
 }
 
-static int init_kvm_mmu(struct kvm_vcpu *vcpu)
+static void init_kvm_mmu(struct kvm_vcpu *vcpu)
 {
        if (mmu_is_nested(vcpu))
                return init_kvm_nested_mmu(vcpu);
@@ -3887,18 +3846,12 @@ static int init_kvm_mmu(struct kvm_vcpu *vcpu)
                return init_kvm_softmmu(vcpu);
 }
 
-static void destroy_kvm_mmu(struct kvm_vcpu *vcpu)
+void kvm_mmu_reset_context(struct kvm_vcpu *vcpu)
 {
        ASSERT(vcpu);
-       if (VALID_PAGE(vcpu->arch.mmu.root_hpa))
-               /* mmu.free() should set root_hpa = INVALID_PAGE */
-               vcpu->arch.mmu.free(vcpu);
-}
 
-int kvm_mmu_reset_context(struct kvm_vcpu *vcpu)
-{
-       destroy_kvm_mmu(vcpu);
-       return init_kvm_mmu(vcpu);
+       kvm_mmu_unload(vcpu);
+       init_kvm_mmu(vcpu);
 }
 EXPORT_SYMBOL_GPL(kvm_mmu_reset_context);
 
@@ -3923,6 +3876,7 @@ EXPORT_SYMBOL_GPL(kvm_mmu_load);
 void kvm_mmu_unload(struct kvm_vcpu *vcpu)
 {
        mmu_free_roots(vcpu);
+       WARN_ON(VALID_PAGE(vcpu->arch.mmu.root_hpa));
 }
 EXPORT_SYMBOL_GPL(kvm_mmu_unload);
 
@@ -4281,12 +4235,12 @@ int kvm_mmu_create(struct kvm_vcpu *vcpu)
        return alloc_mmu_pages(vcpu);
 }
 
-int kvm_mmu_setup(struct kvm_vcpu *vcpu)
+void kvm_mmu_setup(struct kvm_vcpu *vcpu)
 {
        ASSERT(vcpu);
        ASSERT(!VALID_PAGE(vcpu->arch.mmu.root_hpa));
 
-       return init_kvm_mmu(vcpu);
+       init_kvm_mmu(vcpu);
 }
 
 void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot)
@@ -4428,7 +4382,7 @@ mmu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
        int nr_to_scan = sc->nr_to_scan;
        unsigned long freed = 0;
 
-       raw_spin_lock(&kvm_lock);
+       spin_lock(&kvm_lock);
 
        list_for_each_entry(kvm, &vm_list, vm_list) {
                int idx;
@@ -4478,9 +4432,8 @@ unlock:
                break;
        }
 
-       raw_spin_unlock(&kvm_lock);
+       spin_unlock(&kvm_lock);
        return freed;
-
 }
 
 static unsigned long
@@ -4574,7 +4527,7 @@ void kvm_mmu_destroy(struct kvm_vcpu *vcpu)
 {
        ASSERT(vcpu);
 
-       destroy_kvm_mmu(vcpu);
+       kvm_mmu_unload(vcpu);
        free_mmu_pages(vcpu);
        mmu_free_memory_caches(vcpu);
 }
index 77e044a..2926152 100644 (file)
@@ -70,8 +70,8 @@ enum {
 };
 
 int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct);
-int kvm_init_shadow_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context);
-int kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context,
+void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context);
+void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context,
                bool execonly);
 
 static inline unsigned int kvm_mmu_available_pages(struct kvm *kvm)
index daff69e..1185fe7 100644 (file)
@@ -296,4 +296,4 @@ static struct kernel_param_ops audit_param_ops = {
        .get = param_get_bool,
 };
 
-module_param_cb(mmu_audit, &audit_param_ops, &mmu_audit, 0644);
+arch_param_cb(mmu_audit, &audit_param_ops, &mmu_audit, 0644);
index c0bc803..c7168a5 100644 (file)
@@ -1959,11 +1959,9 @@ static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu,
        nested_svm_vmexit(svm);
 }
 
-static int nested_svm_init_mmu_context(struct kvm_vcpu *vcpu)
+static void nested_svm_init_mmu_context(struct kvm_vcpu *vcpu)
 {
-       int r;
-
-       r = kvm_init_shadow_mmu(vcpu, &vcpu->arch.mmu);
+       kvm_init_shadow_mmu(vcpu, &vcpu->arch.mmu);
 
        vcpu->arch.mmu.set_cr3           = nested_svm_set_tdp_cr3;
        vcpu->arch.mmu.get_cr3           = nested_svm_get_tdp_cr3;
@@ -1971,8 +1969,6 @@ static int nested_svm_init_mmu_context(struct kvm_vcpu *vcpu)
        vcpu->arch.mmu.inject_page_fault = nested_svm_inject_npf_exit;
        vcpu->arch.mmu.shadow_root_level = get_npt_level();
        vcpu->arch.walk_mmu              = &vcpu->arch.nested_mmu;
-
-       return r;
 }
 
 static void nested_svm_uninit_mmu_context(struct kvm_vcpu *vcpu)
index 2b2fce1..b2fe1c2 100644 (file)
@@ -1498,7 +1498,7 @@ static void add_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr,
                        break;
 
        if (i == NR_AUTOLOAD_MSRS) {
-               printk_once(KERN_WARNING"Not enough mst switch entries. "
+               printk_once(KERN_WARNING "Not enough msr switch entries. "
                                "Can't add msr %x\n", msr);
                return;
        } else if (i == m->nr) {
@@ -1898,16 +1898,12 @@ static void skip_emulated_instruction(struct kvm_vcpu *vcpu)
 /*
  * KVM wants to inject page-faults which it got to the guest. This function
  * checks whether in a nested guest, we need to inject them to L1 or L2.
- * This function assumes it is called with the exit reason in vmcs02 being
- * a #PF exception (this is the only case in which KVM injects a #PF when L2
- * is running).
  */
-static int nested_pf_handled(struct kvm_vcpu *vcpu)
+static int nested_vmx_check_exception(struct kvm_vcpu *vcpu, unsigned nr)
 {
        struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
 
-       /* TODO: also check PFEC_MATCH/MASK, not just EB.PF. */
-       if (!(vmcs12->exception_bitmap & (1u << PF_VECTOR)))
+       if (!(vmcs12->exception_bitmap & (1u << nr)))
                return 0;
 
        nested_vmx_vmexit(vcpu);
@@ -1921,8 +1917,8 @@ static void vmx_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
        struct vcpu_vmx *vmx = to_vmx(vcpu);
        u32 intr_info = nr | INTR_INFO_VALID_MASK;
 
-       if (nr == PF_VECTOR && is_guest_mode(vcpu) &&
-           !vmx->nested.nested_run_pending && nested_pf_handled(vcpu))
+       if (!reinject && is_guest_mode(vcpu) &&
+           nested_vmx_check_exception(vcpu, nr))
                return;
 
        if (has_error_code) {
@@ -2204,9 +2200,15 @@ static __init void nested_vmx_setup_ctls_msrs(void)
 #ifdef CONFIG_X86_64
                VM_EXIT_HOST_ADDR_SPACE_SIZE |
 #endif
-               VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT;
+               VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT |
+               VM_EXIT_SAVE_VMX_PREEMPTION_TIMER;
+       if (!(nested_vmx_pinbased_ctls_high & PIN_BASED_VMX_PREEMPTION_TIMER) ||
+           !(nested_vmx_exit_ctls_high & VM_EXIT_SAVE_VMX_PREEMPTION_TIMER)) {
+               nested_vmx_exit_ctls_high &= ~VM_EXIT_SAVE_VMX_PREEMPTION_TIMER;
+               nested_vmx_pinbased_ctls_high &= ~PIN_BASED_VMX_PREEMPTION_TIMER;
+       }
        nested_vmx_exit_ctls_high |= (VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR |
-                                     VM_EXIT_LOAD_IA32_EFER);
+               VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER);
 
        /* entry controls */
        rdmsr(MSR_IA32_VMX_ENTRY_CTLS,
@@ -2226,7 +2228,8 @@ static __init void nested_vmx_setup_ctls_msrs(void)
                nested_vmx_procbased_ctls_low, nested_vmx_procbased_ctls_high);
        nested_vmx_procbased_ctls_low = 0;
        nested_vmx_procbased_ctls_high &=
-               CPU_BASED_VIRTUAL_INTR_PENDING | CPU_BASED_USE_TSC_OFFSETING |
+               CPU_BASED_VIRTUAL_INTR_PENDING |
+               CPU_BASED_VIRTUAL_NMI_PENDING | CPU_BASED_USE_TSC_OFFSETING |
                CPU_BASED_HLT_EXITING | CPU_BASED_INVLPG_EXITING |
                CPU_BASED_MWAIT_EXITING | CPU_BASED_CR3_LOAD_EXITING |
                CPU_BASED_CR3_STORE_EXITING |
@@ -2252,13 +2255,15 @@ static __init void nested_vmx_setup_ctls_msrs(void)
        nested_vmx_secondary_ctls_low = 0;
        nested_vmx_secondary_ctls_high &=
                SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES |
+               SECONDARY_EXEC_UNRESTRICTED_GUEST |
                SECONDARY_EXEC_WBINVD_EXITING;
 
        if (enable_ept) {
                /* nested EPT: emulate EPT also to L1 */
                nested_vmx_secondary_ctls_high |= SECONDARY_EXEC_ENABLE_EPT;
                nested_vmx_ept_caps = VMX_EPT_PAGE_WALK_4_BIT |
-                        VMX_EPTP_WB_BIT | VMX_EPT_INVEPT_BIT;
+                        VMX_EPTP_WB_BIT | VMX_EPT_2MB_PAGE_BIT |
+                        VMX_EPT_INVEPT_BIT;
                nested_vmx_ept_caps &= vmx_capability.ept;
                /*
                 * Since invept is completely emulated we support both global
@@ -3380,8 +3385,10 @@ static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
        if (enable_ept) {
                eptp = construct_eptp(cr3);
                vmcs_write64(EPT_POINTER, eptp);
-               guest_cr3 = is_paging(vcpu) ? kvm_read_cr3(vcpu) :
-                       vcpu->kvm->arch.ept_identity_map_addr;
+               if (is_paging(vcpu) || is_guest_mode(vcpu))
+                       guest_cr3 = kvm_read_cr3(vcpu);
+               else
+                       guest_cr3 = vcpu->kvm->arch.ept_identity_map_addr;
                ept_load_pdptrs(vcpu);
        }
 
@@ -4879,6 +4886,17 @@ vmx_patch_hypercall(struct kvm_vcpu *vcpu, unsigned char *hypercall)
        hypercall[2] = 0xc1;
 }
 
+static bool nested_cr0_valid(struct vmcs12 *vmcs12, unsigned long val)
+{
+       unsigned long always_on = VMXON_CR0_ALWAYSON;
+
+       if (nested_vmx_secondary_ctls_high &
+               SECONDARY_EXEC_UNRESTRICTED_GUEST &&
+           nested_cpu_has2(vmcs12, SECONDARY_EXEC_UNRESTRICTED_GUEST))
+               always_on &= ~(X86_CR0_PE | X86_CR0_PG);
+       return (val & always_on) == always_on;
+}
+
 /* called to set cr0 as appropriate for a mov-to-cr0 exit. */
 static int handle_set_cr0(struct kvm_vcpu *vcpu, unsigned long val)
 {
@@ -4897,9 +4915,7 @@ static int handle_set_cr0(struct kvm_vcpu *vcpu, unsigned long val)
                val = (val & ~vmcs12->cr0_guest_host_mask) |
                        (vmcs12->guest_cr0 & vmcs12->cr0_guest_host_mask);
 
-               /* TODO: will have to take unrestricted guest mode into
-                * account */
-               if ((val & VMXON_CR0_ALWAYSON) != VMXON_CR0_ALWAYSON)
+               if (!nested_cr0_valid(vmcs12, val))
                        return 1;
 
                if (kvm_set_cr0(vcpu, val))
@@ -6627,6 +6643,9 @@ static bool nested_vmx_exit_handled(struct kvm_vcpu *vcpu)
                        return 0;
                else if (is_page_fault(intr_info))
                        return enable_ept;
+               else if (is_no_device(intr_info) &&
+                        !(nested_read_cr0(vmcs12) & X86_CR0_TS))
+                       return 0;
                return vmcs12->exception_bitmap &
                                (1u << (intr_info & INTR_INFO_VECTOR_MASK));
        case EXIT_REASON_EXTERNAL_INTERRUPT:
@@ -6722,6 +6741,27 @@ static void vmx_get_exit_info(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2)
        *info2 = vmcs_read32(VM_EXIT_INTR_INFO);
 }
 
+static void nested_adjust_preemption_timer(struct kvm_vcpu *vcpu)
+{
+       u64 delta_tsc_l1;
+       u32 preempt_val_l1, preempt_val_l2, preempt_scale;
+
+       if (!(get_vmcs12(vcpu)->pin_based_vm_exec_control &
+                       PIN_BASED_VMX_PREEMPTION_TIMER))
+               return;
+       preempt_scale = native_read_msr(MSR_IA32_VMX_MISC) &
+                       MSR_IA32_VMX_MISC_PREEMPTION_TIMER_SCALE;
+       preempt_val_l2 = vmcs_read32(VMX_PREEMPTION_TIMER_VALUE);
+       delta_tsc_l1 = vmx_read_l1_tsc(vcpu, native_read_tsc())
+               - vcpu->arch.last_guest_tsc;
+       preempt_val_l1 = delta_tsc_l1 >> preempt_scale;
+       if (preempt_val_l2 <= preempt_val_l1)
+               preempt_val_l2 = 0;
+       else
+               preempt_val_l2 -= preempt_val_l1;
+       vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, preempt_val_l2);
+}
+
 /*
  * The guest has exited.  See if we can fix it or if we need userspace
  * assistance.
@@ -6736,20 +6776,6 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu)
        if (vmx->emulation_required)
                return handle_invalid_guest_state(vcpu);
 
-       /*
-        * the KVM_REQ_EVENT optimization bit is only on for one entry, and if
-        * we did not inject a still-pending event to L1 now because of
-        * nested_run_pending, we need to re-enable this bit.
-        */
-       if (vmx->nested.nested_run_pending)
-               kvm_make_request(KVM_REQ_EVENT, vcpu);
-
-       if (!is_guest_mode(vcpu) && (exit_reason == EXIT_REASON_VMLAUNCH ||
-           exit_reason == EXIT_REASON_VMRESUME))
-               vmx->nested.nested_run_pending = 1;
-       else
-               vmx->nested.nested_run_pending = 0;
-
        if (is_guest_mode(vcpu) && nested_vmx_exit_handled(vcpu)) {
                nested_vmx_vmexit(vcpu);
                return 1;
@@ -7061,9 +7087,9 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu,
        case INTR_TYPE_HARD_EXCEPTION:
                if (idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK) {
                        u32 err = vmcs_read32(error_code_field);
-                       kvm_queue_exception_e(vcpu, vector, err);
+                       kvm_requeue_exception_e(vcpu, vector, err);
                } else
-                       kvm_queue_exception(vcpu, vector);
+                       kvm_requeue_exception(vcpu, vector);
                break;
        case INTR_TYPE_SOFT_INTR:
                vcpu->arch.event_exit_inst_len = vmcs_read32(instr_len_field);
@@ -7146,6 +7172,8 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
        atomic_switch_perf_msrs(vmx);
        debugctlmsr = get_debugctlmsr();
 
+       if (is_guest_mode(vcpu) && !vmx->nested.nested_run_pending)
+               nested_adjust_preemption_timer(vcpu);
        vmx->__launched = vmx->loaded_vmcs->launched;
        asm(
                /* Store host registers */
@@ -7284,6 +7312,16 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
        vmx->exit_reason = vmcs_read32(VM_EXIT_REASON);
        trace_kvm_exit(vmx->exit_reason, vcpu, KVM_ISA_VMX);
 
+       /*
+        * the KVM_REQ_EVENT optimization bit is only on for one entry, and if
+        * we did not inject a still-pending event to L1 now because of
+        * nested_run_pending, we need to re-enable this bit.
+        */
+       if (vmx->nested.nested_run_pending)
+               kvm_make_request(KVM_REQ_EVENT, vcpu);
+
+       vmx->nested.nested_run_pending = 0;
+
        vmx_complete_atomic_exit(vmx);
        vmx_recover_nmi_blocking(vmx);
        vmx_complete_interrupts(vmx);
@@ -7410,8 +7448,7 @@ static u64 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
         */
        if (is_mmio)
                ret = MTRR_TYPE_UNCACHABLE << VMX_EPT_MT_EPTE_SHIFT;
-       else if (vcpu->kvm->arch.iommu_domain &&
-               !(vcpu->kvm->arch.iommu_flags & KVM_IOMMU_CACHE_COHERENCY))
+       else if (kvm_arch_has_noncoherent_dma(vcpu->kvm))
                ret = kvm_get_guest_memory_type(vcpu, gfn) <<
                      VMX_EPT_MT_EPTE_SHIFT;
        else
@@ -7501,9 +7538,9 @@ static unsigned long nested_ept_get_cr3(struct kvm_vcpu *vcpu)
        return get_vmcs12(vcpu)->ept_pointer;
 }
 
-static int nested_ept_init_mmu_context(struct kvm_vcpu *vcpu)
+static void nested_ept_init_mmu_context(struct kvm_vcpu *vcpu)
 {
-       int r = kvm_init_shadow_ept_mmu(vcpu, &vcpu->arch.mmu,
+       kvm_init_shadow_ept_mmu(vcpu, &vcpu->arch.mmu,
                        nested_vmx_ept_caps & VMX_EPT_EXECUTE_ONLY_BIT);
 
        vcpu->arch.mmu.set_cr3           = vmx_set_cr3;
@@ -7511,8 +7548,6 @@ static int nested_ept_init_mmu_context(struct kvm_vcpu *vcpu)
        vcpu->arch.mmu.inject_page_fault = nested_ept_inject_page_fault;
 
        vcpu->arch.walk_mmu              = &vcpu->arch.nested_mmu;
-
-       return r;
 }
 
 static void nested_ept_uninit_mmu_context(struct kvm_vcpu *vcpu)
@@ -7520,6 +7555,20 @@ static void nested_ept_uninit_mmu_context(struct kvm_vcpu *vcpu)
        vcpu->arch.walk_mmu = &vcpu->arch.mmu;
 }
 
+static void vmx_inject_page_fault_nested(struct kvm_vcpu *vcpu,
+               struct x86_exception *fault)
+{
+       struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
+
+       WARN_ON(!is_guest_mode(vcpu));
+
+       /* TODO: also check PFEC_MATCH/MASK, not just EB.PF. */
+       if (vmcs12->exception_bitmap & (1u << PF_VECTOR))
+               nested_vmx_vmexit(vcpu);
+       else
+               kvm_inject_page_fault(vcpu, fault);
+}
+
 /*
  * prepare_vmcs02 is called when the L1 guest hypervisor runs its nested
  * L2 guest. L1 has a vmcs for L2 (vmcs12), and this function "merges" it
@@ -7533,6 +7582,7 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
        u32 exec_control;
+       u32 exit_control;
 
        vmcs_write16(GUEST_ES_SELECTOR, vmcs12->guest_es_selector);
        vmcs_write16(GUEST_CS_SELECTOR, vmcs12->guest_cs_selector);
@@ -7706,7 +7756,10 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
         * we should use its exit controls. Note that VM_EXIT_LOAD_IA32_EFER
         * bits are further modified by vmx_set_efer() below.
         */
-       vmcs_write32(VM_EXIT_CONTROLS, vmcs_config.vmexit_ctrl);
+       exit_control = vmcs_config.vmexit_ctrl;
+       if (vmcs12->pin_based_vm_exec_control & PIN_BASED_VMX_PREEMPTION_TIMER)
+               exit_control |= VM_EXIT_SAVE_VMX_PREEMPTION_TIMER;
+       vmcs_write32(VM_EXIT_CONTROLS, exit_control);
 
        /* vmcs12's VM_ENTRY_LOAD_IA32_EFER and VM_ENTRY_IA32E_MODE are
         * emulated by vmx_set_efer(), below.
@@ -7773,6 +7826,9 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
        kvm_set_cr3(vcpu, vmcs12->guest_cr3);
        kvm_mmu_reset_context(vcpu);
 
+       if (!enable_ept)
+               vcpu->arch.walk_mmu->inject_page_fault = vmx_inject_page_fault_nested;
+
        /*
         * L1 may access the L2's PDPTR, so save them to construct vmcs12
         */
@@ -7876,7 +7932,7 @@ static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch)
                return 1;
        }
 
-       if (((vmcs12->guest_cr0 & VMXON_CR0_ALWAYSON) != VMXON_CR0_ALWAYSON) ||
+       if (!nested_cr0_valid(vmcs12, vmcs12->guest_cr0) ||
            ((vmcs12->guest_cr4 & VMXON_CR4_ALWAYSON) != VMXON_CR4_ALWAYSON)) {
                nested_vmx_entry_failure(vcpu, vmcs12,
                        EXIT_REASON_INVALID_STATE, ENTRY_FAIL_DEFAULT);
@@ -7938,6 +7994,8 @@ static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch)
 
        enter_guest_mode(vcpu);
 
+       vmx->nested.nested_run_pending = 1;
+
        vmx->nested.vmcs01_tsc_offset = vmcs_read64(TSC_OFFSET);
 
        cpu = get_cpu();
@@ -8005,7 +8063,7 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu,
        u32 idt_vectoring;
        unsigned int nr;
 
-       if (vcpu->arch.exception.pending) {
+       if (vcpu->arch.exception.pending && vcpu->arch.exception.reinject) {
                nr = vcpu->arch.exception.nr;
                idt_vectoring = nr | VECTORING_INFO_VALID_MASK;
 
@@ -8023,7 +8081,7 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu,
                }
 
                vmcs12->idt_vectoring_info_field = idt_vectoring;
-       } else if (vcpu->arch.nmi_pending) {
+       } else if (vcpu->arch.nmi_injected) {
                vmcs12->idt_vectoring_info_field =
                        INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR;
        } else if (vcpu->arch.interrupt.pending) {
@@ -8105,6 +8163,11 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
        vmcs12->guest_pending_dbg_exceptions =
                vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS);
 
+       if ((vmcs12->pin_based_vm_exec_control & PIN_BASED_VMX_PREEMPTION_TIMER) &&
+           (vmcs12->vm_exit_controls & VM_EXIT_SAVE_VMX_PREEMPTION_TIMER))
+               vmcs12->vmx_preemption_timer_value =
+                       vmcs_read32(VMX_PREEMPTION_TIMER_VALUE);
+
        /*
         * In some cases (usually, nested EPT), L2 is allowed to change its
         * own CR3 without exiting. If it has changed it, we must keep it.
@@ -8130,6 +8193,8 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
        vmcs12->guest_ia32_debugctl = vmcs_read64(GUEST_IA32_DEBUGCTL);
        if (vmcs12->vm_exit_controls & VM_EXIT_SAVE_IA32_PAT)
                vmcs12->guest_ia32_pat = vmcs_read64(GUEST_IA32_PAT);
+       if (vmcs12->vm_exit_controls & VM_EXIT_SAVE_IA32_EFER)
+               vmcs12->guest_ia32_efer = vcpu->arch.efer;
        vmcs12->guest_sysenter_cs = vmcs_read32(GUEST_SYSENTER_CS);
        vmcs12->guest_sysenter_esp = vmcs_readl(GUEST_SYSENTER_ESP);
        vmcs12->guest_sysenter_eip = vmcs_readl(GUEST_SYSENTER_EIP);
@@ -8201,7 +8266,7 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu,
         * fpu_active (which may have changed).
         * Note that vmx_set_cr0 refers to efer set above.
         */
-       kvm_set_cr0(vcpu, vmcs12->host_cr0);
+       vmx_set_cr0(vcpu, vmcs12->host_cr0);
        /*
         * If we did fpu_activate()/fpu_deactivate() during L2's run, we need
         * to apply the same changes to L1's vmcs. We just set cr0 correctly,
@@ -8224,6 +8289,9 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu,
        kvm_set_cr3(vcpu, vmcs12->host_cr3);
        kvm_mmu_reset_context(vcpu);
 
+       if (!enable_ept)
+               vcpu->arch.walk_mmu->inject_page_fault = kvm_inject_page_fault;
+
        if (enable_vpid) {
                /*
                 * Trivially support vpid by letting L2s share their parent
index e5ca72a..21ef1ba 100644 (file)
@@ -577,6 +577,7 @@ static void kvm_put_guest_xcr0(struct kvm_vcpu *vcpu)
 int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr)
 {
        u64 xcr0;
+       u64 valid_bits;
 
        /* Only support XCR_XFEATURE_ENABLED_MASK(xcr0) now  */
        if (index != XCR_XFEATURE_ENABLED_MASK)
@@ -586,8 +587,16 @@ int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr)
                return 1;
        if ((xcr0 & XSTATE_YMM) && !(xcr0 & XSTATE_SSE))
                return 1;
-       if (xcr0 & ~host_xcr0)
+
+       /*
+        * Do not allow the guest to set bits that we do not support
+        * saving.  However, xcr0 bit 0 is always set, even if the
+        * emulated CPU does not support XSAVE (see fx_init).
+        */
+       valid_bits = vcpu->arch.guest_supported_xcr0 | XSTATE_FP;
+       if (xcr0 & ~valid_bits)
                return 1;
+
        kvm_put_guest_xcr0(vcpu);
        vcpu->arch.xcr0 = xcr0;
        return 0;
@@ -684,7 +693,7 @@ int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
 
        vcpu->arch.cr3 = cr3;
        __set_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail);
-       vcpu->arch.mmu.new_cr3(vcpu);
+       kvm_mmu_new_cr3(vcpu);
        return 0;
 }
 EXPORT_SYMBOL_GPL(kvm_set_cr3);
@@ -2564,6 +2573,7 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_MMU_SHADOW_CACHE_CONTROL:
        case KVM_CAP_SET_TSS_ADDR:
        case KVM_CAP_EXT_CPUID:
+       case KVM_CAP_EXT_EMUL_CPUID:
        case KVM_CAP_CLOCKSOURCE:
        case KVM_CAP_PIT:
        case KVM_CAP_NOP_IO_DELAY:
@@ -2673,15 +2683,17 @@ long kvm_arch_dev_ioctl(struct file *filp,
                r = 0;
                break;
        }
-       case KVM_GET_SUPPORTED_CPUID: {
+       case KVM_GET_SUPPORTED_CPUID:
+       case KVM_GET_EMULATED_CPUID: {
                struct kvm_cpuid2 __user *cpuid_arg = argp;
                struct kvm_cpuid2 cpuid;
 
                r = -EFAULT;
                if (copy_from_user(&cpuid, cpuid_arg, sizeof cpuid))
                        goto out;
-               r = kvm_dev_ioctl_get_supported_cpuid(&cpuid,
-                                                     cpuid_arg->entries);
+
+               r = kvm_dev_ioctl_get_cpuid(&cpuid, cpuid_arg->entries,
+                                           ioctl);
                if (r)
                        goto out;
 
@@ -2715,8 +2727,7 @@ static void wbinvd_ipi(void *garbage)
 
 static bool need_emulate_wbinvd(struct kvm_vcpu *vcpu)
 {
-       return vcpu->kvm->arch.iommu_domain &&
-               !(vcpu->kvm->arch.iommu_flags & KVM_IOMMU_CACHE_COHERENCY);
+       return kvm_arch_has_noncoherent_dma(vcpu->kvm);
 }
 
 void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
@@ -2984,11 +2995,13 @@ static int kvm_vcpu_ioctl_x86_set_debugregs(struct kvm_vcpu *vcpu,
 static void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu,
                                         struct kvm_xsave *guest_xsave)
 {
-       if (cpu_has_xsave)
+       if (cpu_has_xsave) {
                memcpy(guest_xsave->region,
                        &vcpu->arch.guest_fpu.state->xsave,
-                       xstate_size);
-       else {
+                       vcpu->arch.guest_xstate_size);
+               *(u64 *)&guest_xsave->region[XSAVE_HDR_OFFSET / sizeof(u32)] &=
+                       vcpu->arch.guest_supported_xcr0 | XSTATE_FPSSE;
+       } else {
                memcpy(guest_xsave->region,
                        &vcpu->arch.guest_fpu.state->fxsave,
                        sizeof(struct i387_fxsave_struct));
@@ -3003,10 +3016,19 @@ static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
        u64 xstate_bv =
                *(u64 *)&guest_xsave->region[XSAVE_HDR_OFFSET / sizeof(u32)];
 
-       if (cpu_has_xsave)
+       if (cpu_has_xsave) {
+               /*
+                * Here we allow setting states that are not present in
+                * CPUID leaf 0xD, index 0, EDX:EAX.  This is for compatibility
+                * with old userspace.
+                */
+               if (xstate_bv & ~KVM_SUPPORTED_XCR0)
+                       return -EINVAL;
+               if (xstate_bv & ~host_xcr0)
+                       return -EINVAL;
                memcpy(&vcpu->arch.guest_fpu.state->xsave,
-                       guest_xsave->region, xstate_size);
-       else {
+                       guest_xsave->region, vcpu->arch.guest_xstate_size);
+       else {
                if (xstate_bv & ~XSTATE_FPSSE)
                        return -EINVAL;
                memcpy(&vcpu->arch.guest_fpu.state->fxsave,
@@ -3042,9 +3064,9 @@ static int kvm_vcpu_ioctl_x86_set_xcrs(struct kvm_vcpu *vcpu,
 
        for (i = 0; i < guest_xcrs->nr_xcrs; i++)
                /* Only support XCR0 currently */
-               if (guest_xcrs->xcrs[0].xcr == XCR_XFEATURE_ENABLED_MASK) {
+               if (guest_xcrs->xcrs[i].xcr == XCR_XFEATURE_ENABLED_MASK) {
                        r = __kvm_set_xcr(vcpu, XCR_XFEATURE_ENABLED_MASK,
-                               guest_xcrs->xcrs[0].value);
+                               guest_xcrs->xcrs[i].value);
                        break;
                }
        if (r)
@@ -4775,8 +4797,8 @@ static void inject_emulated_exception(struct kvm_vcpu *vcpu)
 
 static void init_decode_cache(struct x86_emulate_ctxt *ctxt)
 {
-       memset(&ctxt->twobyte, 0,
-              (void *)&ctxt->_regs - (void *)&ctxt->twobyte);
+       memset(&ctxt->opcode_len, 0,
+              (void *)&ctxt->_regs - (void *)&ctxt->opcode_len);
 
        ctxt->fetch.start = 0;
        ctxt->fetch.end = 0;
@@ -5094,8 +5116,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu,
                ctxt->have_exception = false;
                ctxt->perm_ok = false;
 
-               ctxt->only_vendor_specific_insn
-                       = emulation_type & EMULTYPE_TRAP_UD;
+               ctxt->ud = emulation_type & EMULTYPE_TRAP_UD;
 
                r = x86_decode_insn(ctxt, insn, insn_len);
 
@@ -5263,7 +5284,7 @@ static int kvmclock_cpufreq_notifier(struct notifier_block *nb, unsigned long va
 
        smp_call_function_single(freq->cpu, tsc_khz_changed, freq, 1);
 
-       raw_spin_lock(&kvm_lock);
+       spin_lock(&kvm_lock);
        list_for_each_entry(kvm, &vm_list, vm_list) {
                kvm_for_each_vcpu(i, vcpu, kvm) {
                        if (vcpu->cpu != freq->cpu)
@@ -5273,7 +5294,7 @@ static int kvmclock_cpufreq_notifier(struct notifier_block *nb, unsigned long va
                                send_ipi = 1;
                }
        }
-       raw_spin_unlock(&kvm_lock);
+       spin_unlock(&kvm_lock);
 
        if (freq->old < freq->new && send_ipi) {
                /*
@@ -5426,12 +5447,12 @@ static void pvclock_gtod_update_fn(struct work_struct *work)
        struct kvm_vcpu *vcpu;
        int i;
 
-       raw_spin_lock(&kvm_lock);
+       spin_lock(&kvm_lock);
        list_for_each_entry(kvm, &vm_list, vm_list)
                kvm_for_each_vcpu(i, vcpu, kvm)
                        set_bit(KVM_REQ_MASTERCLOCK_UPDATE, &vcpu->requests);
        atomic_set(&kvm_guest_has_master_clock, 0);
-       raw_spin_unlock(&kvm_lock);
+       spin_unlock(&kvm_lock);
 }
 
 static DECLARE_WORK(pvclock_gtod_work, pvclock_gtod_update_fn);
@@ -5945,10 +5966,12 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
 
        vcpu->mode = IN_GUEST_MODE;
 
+       srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
+
        /* We should set ->mode before check ->requests,
         * see the comment in make_all_cpus_request.
         */
-       smp_mb();
+       smp_mb__after_srcu_read_unlock();
 
        local_irq_disable();
 
@@ -5958,12 +5981,11 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
                smp_wmb();
                local_irq_enable();
                preempt_enable();
+               vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
                r = 1;
                goto cancel_injection;
        }
 
-       srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
-
        if (req_immediate_exit)
                smp_send_reschedule(vcpu->cpu);
 
@@ -6688,7 +6710,7 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
        if (r)
                return r;
        kvm_vcpu_reset(vcpu);
-       r = kvm_mmu_setup(vcpu);
+       kvm_mmu_setup(vcpu);
        vcpu_put(vcpu);
 
        return r;
@@ -6940,6 +6962,10 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 
        vcpu->arch.ia32_tsc_adjust_msr = 0x0;
        vcpu->arch.pv_time_enabled = false;
+
+       vcpu->arch.guest_supported_xcr0 = 0;
+       vcpu->arch.guest_xstate_size = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;
+
        kvm_async_pf_hash_reset(vcpu);
        kvm_pmu_init(vcpu);
 
@@ -6981,6 +7007,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
        INIT_LIST_HEAD(&kvm->arch.active_mmu_pages);
        INIT_LIST_HEAD(&kvm->arch.zapped_obsolete_pages);
        INIT_LIST_HEAD(&kvm->arch.assigned_dev_head);
+       atomic_set(&kvm->arch.noncoherent_dma_count, 0);
 
        /* Reserve bit 0 of irq_sources_bitmap for userspace irq source */
        set_bit(KVM_USERSPACE_IRQ_SOURCE_ID, &kvm->arch.irq_sources_bitmap);
@@ -7065,7 +7092,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
        kfree(rcu_dereference_check(kvm->arch.apic_map, 1));
 }
 
-void kvm_arch_free_memslot(struct kvm_memory_slot *free,
+void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
                           struct kvm_memory_slot *dont)
 {
        int i;
@@ -7086,7 +7113,8 @@ void kvm_arch_free_memslot(struct kvm_memory_slot *free,
        }
 }
 
-int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
+int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
+                           unsigned long npages)
 {
        int i;
 
@@ -7283,7 +7311,7 @@ void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu, struct kvm_async_pf *work)
        int r;
 
        if ((vcpu->arch.mmu.direct_map != work->arch.direct_map) ||
-             is_error_page(work->page))
+             work->wakeup_all)
                return;
 
        r = kvm_mmu_reload(vcpu);
@@ -7393,7 +7421,7 @@ void kvm_arch_async_page_present(struct kvm_vcpu *vcpu,
        struct x86_exception fault;
 
        trace_kvm_async_pf_ready(work->arch.token, work->gva);
-       if (is_error_page(work->page))
+       if (work->wakeup_all)
                work->arch.token = ~0; /* broadcast wakeup */
        else
                kvm_del_async_pf_gfn(vcpu, work->arch.gfn);
@@ -7420,6 +7448,24 @@ bool kvm_arch_can_inject_async_page_present(struct kvm_vcpu *vcpu)
                        kvm_x86_ops->interrupt_allowed(vcpu);
 }
 
+void kvm_arch_register_noncoherent_dma(struct kvm *kvm)
+{
+       atomic_inc(&kvm->arch.noncoherent_dma_count);
+}
+EXPORT_SYMBOL_GPL(kvm_arch_register_noncoherent_dma);
+
+void kvm_arch_unregister_noncoherent_dma(struct kvm *kvm)
+{
+       atomic_dec(&kvm->arch.noncoherent_dma_count);
+}
+EXPORT_SYMBOL_GPL(kvm_arch_unregister_noncoherent_dma);
+
+bool kvm_arch_has_noncoherent_dma(struct kvm *kvm)
+{
+       return atomic_read(&kvm->arch.noncoherent_dma_count);
+}
+EXPORT_SYMBOL_GPL(kvm_arch_has_noncoherent_dma);
+
 EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_exit);
 EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_inj_virq);
 EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_page_fault);
index e224f7a..587fb9e 100644 (file)
@@ -122,6 +122,7 @@ int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt,
        gva_t addr, void *val, unsigned int bytes,
        struct x86_exception *exception);
 
+#define KVM_SUPPORTED_XCR0     (XSTATE_FP | XSTATE_SSE | XSTATE_YMM)
 extern u64 host_xcr0;
 
 extern struct static_key kvm_no_apic_vcpu;
index dfa537a..c96314a 100644 (file)
@@ -25,8 +25,12 @@ pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address)
        struct page *pte;
 
        pte = alloc_pages(__userpte_alloc_gfp, 0);
-       if (pte)
-               pgtable_page_ctor(pte);
+       if (!pte)
+               return NULL;
+       if (!pgtable_page_ctor(pte)) {
+               __free_page(pte);
+               return NULL;
+       }
        return pte;
 }
 
@@ -57,6 +61,7 @@ void ___pte_free_tlb(struct mmu_gather *tlb, struct page *pte)
 #if PAGETABLE_LEVELS > 2
 void ___pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd)
 {
+       struct page *page = virt_to_page(pmd);
        paravirt_release_pmd(__pa(pmd) >> PAGE_SHIFT);
        /*
         * NOTE! For PAE, any changes to the top page-directory-pointer-table
@@ -65,7 +70,8 @@ void ___pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd)
 #ifdef CONFIG_X86_PAE
        tlb->need_flush_all = 1;
 #endif
-       tlb_remove_page(tlb, virt_to_page(pmd));
+       pgtable_pmd_page_dtor(page);
+       tlb_remove_page(tlb, page);
 }
 
 #if PAGETABLE_LEVELS > 3
@@ -189,8 +195,10 @@ static void free_pmds(pmd_t *pmds[])
        int i;
 
        for(i = 0; i < PREALLOCATED_PMDS; i++)
-               if (pmds[i])
+               if (pmds[i]) {
+                       pgtable_pmd_page_dtor(virt_to_page(pmds[i]));
                        free_page((unsigned long)pmds[i]);
+               }
 }
 
 static int preallocate_pmds(pmd_t *pmds[])
@@ -200,8 +208,13 @@ static int preallocate_pmds(pmd_t *pmds[])
 
        for(i = 0; i < PREALLOCATED_PMDS; i++) {
                pmd_t *pmd = (pmd_t *)__get_free_page(PGALLOC_GFP);
-               if (pmd == NULL)
+               if (!pmd)
                        failed = true;
+               if (pmd && !pgtable_pmd_page_ctor(virt_to_page(pmd))) {
+                       free_page((unsigned long)pmd);
+                       pmd = NULL;
+                       failed = true;
+               }
                pmds[i] = pmd;
        }
 
index 7fb24e5..4f25ec0 100644 (file)
@@ -518,7 +518,7 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
        sd = &info->sd;
        sd->domain = domain;
        sd->node = node;
-       sd->acpi = device->handle;
+       sd->companion = device;
        /*
         * Maybe the desired pci bus has been already scanned. In such case
         * it is unnecessary to scan the pci bus with the given domain,busnum.
@@ -589,7 +589,7 @@ int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
 {
        struct pci_sysdata *sd = bridge->bus->sysdata;
 
-       ACPI_HANDLE_SET(&bridge->dev, sd->acpi);
+       ACPI_COMPANION_SET(&bridge->dev, sd->companion);
        return 0;
 }
 
index 14ef8d1..ed56a1c 100644 (file)
@@ -31,6 +31,11 @@ config X86_64
        def_bool 64BIT
        select MODULES_USE_ELF_RELA
 
+config ARCH_DEFCONFIG
+       string
+       default "arch/um/configs/i386_defconfig" if X86_32
+       default "arch/um/configs/x86_64_defconfig" if X86_64
+
 config RWSEM_XCHGADD_ALGORITHM
        def_bool 64BIT
 
index 6c6689e..c112de8 100644 (file)
@@ -33,6 +33,8 @@ struct arch_thread {
        .faultinfo              = { 0, 0, 0 } \
 }
 
+#define STACKSLOTS_PER_LINE 8
+
 static inline void arch_flush_thread(struct arch_thread *thread)
 {
        /* Clear any TLS still hanging */
@@ -53,4 +55,7 @@ static inline void arch_copy_thread(struct arch_thread *from,
 #define current_text_addr() \
        ({ void *pc; __asm__("movl $1f,%0\n1:":"=g" (pc)); pc; })
 
+#define current_sp() ({ void *sp; __asm__("movl %%esp, %0" : "=r" (sp) : ); sp; })
+#define current_bp() ({ unsigned long bp; __asm__("movl %%ebp, %0" : "=r" (bp) : ); bp; })
+
 #endif
index 4b02a84..c3be852 100644 (file)
@@ -19,6 +19,8 @@ struct arch_thread {
                           .fs                  = 0, \
                           .faultinfo           = { 0, 0, 0 } }
 
+#define STACKSLOTS_PER_LINE 4
+
 static inline void arch_flush_thread(struct arch_thread *thread)
 {
 }
@@ -32,4 +34,7 @@ static inline void arch_copy_thread(struct arch_thread *from,
 #define current_text_addr() \
        ({ void *pc; __asm__("movq $1f,%0\n1:":"=g" (pc)); pc; })
 
+#define current_sp() ({ void *sp; __asm__("movq %%rsp, %0" : "=r" (sp) : ); sp; })
+#define current_bp() ({ unsigned long bp; __asm__("movq %%rbp, %0" : "=r" (bp) : ); bp; })
+
 #endif
index c9bee5b..16ee0e4 100644 (file)
@@ -30,70 +30,4 @@ void show_regs(struct pt_regs *regs)
         printk(" DS: %04lx ES: %04lx\n",
               0xffff & PT_REGS_DS(regs), 
               0xffff & PT_REGS_ES(regs));
-
-        show_trace(NULL, (unsigned long *) &regs);
 }
-
-/* Copied from i386. */
-static inline int valid_stack_ptr(struct thread_info *tinfo, void *p)
-{
-       return  p > (void *)tinfo &&
-               p < (void *)tinfo + THREAD_SIZE - 3;
-}
-
-/* Adapted from i386 (we also print the address we read from). */
-static inline unsigned long print_context_stack(struct thread_info *tinfo,
-                               unsigned long *stack, unsigned long ebp)
-{
-       unsigned long addr;
-
-#ifdef CONFIG_FRAME_POINTER
-       while (valid_stack_ptr(tinfo, (void *)ebp)) {
-               addr = *(unsigned long *)(ebp + 4);
-               printk("%08lx:  [<%08lx>]", ebp + 4, addr);
-               print_symbol(" %s", addr);
-               printk("\n");
-               ebp = *(unsigned long *)ebp;
-       }
-#else
-       while (valid_stack_ptr(tinfo, stack)) {
-               addr = *stack;
-               if (__kernel_text_address(addr)) {
-                       printk("%08lx:  [<%08lx>]", (unsigned long) stack, addr);
-                       print_symbol(" %s", addr);
-                       printk("\n");
-               }
-               stack++;
-       }
-#endif
-       return ebp;
-}
-
-void show_trace(struct task_struct* task, unsigned long * stack)
-{
-       unsigned long ebp;
-       struct thread_info *context;
-
-       /* Turn this into BUG_ON if possible. */
-       if (!stack) {
-               stack = (unsigned long*) &stack;
-               printk("show_trace: got NULL stack, implicit assumption task == current");
-               WARN_ON(1);
-       }
-
-       if (!task)
-               task = current;
-
-       if (task != current) {
-               ebp = (unsigned long) KSTK_EBP(task);
-       } else {
-               asm ("movl %%ebp, %0" : "=r" (ebp) : );
-       }
-
-       context = (struct thread_info *)
-               ((unsigned long)stack & (~(THREAD_SIZE - 1)));
-       print_context_stack(context, stack, ebp);
-
-       printk("\n");
-}
-
index a0e7fb1..38b4e4a 100644 (file)
@@ -12,7 +12,7 @@
 #include <asm/ptrace.h>
 #include <asm/sysrq.h>
 
-void __show_regs(struct pt_regs *regs)
+void show_regs(struct pt_regs *regs)
 {
        printk("\n");
        print_modules();
@@ -33,9 +33,3 @@ void __show_regs(struct pt_regs *regs)
        printk(KERN_INFO "R13: %016lx R14: %016lx R15: %016lx\n",
               PT_REGS_R13(regs), PT_REGS_R14(regs), PT_REGS_R15(regs));
 }
-
-void show_regs(struct pt_regs *regs)
-{
-       __show_regs(regs);
-       show_trace(current, (unsigned long *) &regs);
-}
diff --git a/arch/x86/um/vdso/.gitignore b/arch/x86/um/vdso/.gitignore
new file mode 100644 (file)
index 0000000..9cac6d0
--- /dev/null
@@ -0,0 +1,2 @@
+vdso-syms.lds
+vdso.lds
index fdc3ba2..ce563be 100644 (file)
@@ -468,8 +468,8 @@ PV_CALLEE_SAVE_REGS_THUNK(xen_pgd_val);
  * 3        PCD PWT      UC       UC     UC
  * 4    PAT              WB       WC     WB
  * 5    PAT     PWT      WC       WP     WT
- * 6    PAT PCD          UC-      UC     UC-
- * 7    PAT PCD PWT      UC       UC     UC
+ * 6    PAT PCD          UC-      rsv    UC-
+ * 7    PAT PCD PWT      UC       rsv    UC
  */
 
 void xen_set_pat(u64 pat)
@@ -796,8 +796,8 @@ static spinlock_t *xen_pte_lock(struct page *page, struct mm_struct *mm)
 {
        spinlock_t *ptl = NULL;
 
-#if USE_SPLIT_PTLOCKS
-       ptl = __pte_lockptr(page);
+#if USE_SPLIT_PTE_PTLOCKS
+       ptl = ptlock_ptr(page);
        spin_lock_nest_lock(ptl, &mm->page_table_lock);
 #endif
 
@@ -1637,7 +1637,7 @@ static inline void xen_alloc_ptpage(struct mm_struct *mm, unsigned long pfn,
 
                        __set_pfn_prot(pfn, PAGE_KERNEL_RO);
 
-                       if (level == PT_PTE && USE_SPLIT_PTLOCKS)
+                       if (level == PT_PTE && USE_SPLIT_PTE_PTLOCKS)
                                __pin_pagetable_pfn(MMUEXT_PIN_L1_TABLE, pfn);
 
                        xen_mc_issue(PARAVIRT_LAZY_MMU);
@@ -1671,7 +1671,7 @@ static inline void xen_release_ptpage(unsigned long pfn, unsigned level)
                if (!PageHighMem(page)) {
                        xen_mc_batch();
 
-                       if (level == PT_PTE && USE_SPLIT_PTLOCKS)
+                       if (level == PT_PTE && USE_SPLIT_PTE_PTLOCKS)
                                __pin_pagetable_pfn(MMUEXT_UNPIN_TABLE, pfn);
 
                        __set_pfn_prot(pfn, PAGE_KERNEL);
@@ -2328,12 +2328,14 @@ static int xen_exchange_memory(unsigned long extents_in, unsigned int order_in,
        return success;
 }
 
-int xen_create_contiguous_region(unsigned long vstart, unsigned int order,
-                                unsigned int address_bits)
+int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
+                                unsigned int address_bits,
+                                dma_addr_t *dma_handle)
 {
        unsigned long *in_frames = discontig_frames, out_frame;
        unsigned long  flags;
        int            success;
+       unsigned long vstart = (unsigned long)phys_to_virt(pstart);
 
        /*
         * Currently an auto-translated guest will not perform I/O, nor will
@@ -2368,15 +2370,17 @@ int xen_create_contiguous_region(unsigned long vstart, unsigned int order,
 
        spin_unlock_irqrestore(&xen_reservation_lock, flags);
 
+       *dma_handle = virt_to_machine(vstart).maddr;
        return success ? 0 : -ENOMEM;
 }
 EXPORT_SYMBOL_GPL(xen_create_contiguous_region);
 
-void xen_destroy_contiguous_region(unsigned long vstart, unsigned int order)
+void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order)
 {
        unsigned long *out_frames = discontig_frames, in_frame;
        unsigned long  flags;
        int success;
+       unsigned long vstart;
 
        if (xen_feature(XENFEAT_auto_translated_physmap))
                return;
@@ -2384,6 +2388,7 @@ void xen_destroy_contiguous_region(unsigned long vstart, unsigned int order)
        if (unlikely(order > MAX_CONTIG_ORDER))
                return;
 
+       vstart = (unsigned long)phys_to_virt(pstart);
        memset((void *) vstart, 0, PAGE_SIZE << order);
 
        spin_lock_irqsave(&xen_reservation_lock, flags);
index a61c7d5..2ae8699 100644 (file)
@@ -799,10 +799,10 @@ bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn)
 {
        unsigned topidx, mididx, idx;
 
-       if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) {
-               BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY);
+       /* don't track P2M changes in autotranslate guests */
+       if (unlikely(xen_feature(XENFEAT_auto_translated_physmap)))
                return true;
-       }
+
        if (unlikely(pfn >= MAX_P2M_PFN)) {
                BUG_ON(mfn != INVALID_P2M_ENTRY);
                return true;
index 9695704..0e98e5d 100644 (file)
@@ -75,8 +75,10 @@ void __init pci_xen_swiotlb_init(void)
                xen_swiotlb_init(1, true /* early */);
                dma_ops = &xen_swiotlb_dma_ops;
 
+#ifdef CONFIG_PCI
                /* Make sure ACS will be enabled */
                pci_request_acs();
+#endif
        }
 }
 
@@ -92,8 +94,10 @@ int pci_xen_swiotlb_init_late(void)
                return rc;
 
        dma_ops = &xen_swiotlb_dma_ops;
+#ifdef CONFIG_PCI
        /* Make sure ACS will be enabled */
        pci_request_acs();
+#endif
 
        return 0;
 }
index 09f3059..68c054f 100644 (file)
@@ -556,7 +556,7 @@ void xen_enable_syscall(void)
        }
 #endif /* CONFIG_X86_64 */
 }
-void __cpuinit xen_enable_nmi(void)
+void xen_enable_nmi(void)
 {
 #ifdef CONFIG_X86_64
        if (register_callback(CALLBACKTYPE_nmi, nmi))
index 31d0475..c36b325 100644 (file)
@@ -149,7 +149,7 @@ static int xen_smp_intr_init(unsigned int cpu)
        rc = bind_ipi_to_irqhandler(XEN_RESCHEDULE_VECTOR,
                                    cpu,
                                    xen_reschedule_interrupt,
-                                   IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
+                                   IRQF_PERCPU|IRQF_NOBALANCING,
                                    resched_name,
                                    NULL);
        if (rc < 0)
@@ -161,7 +161,7 @@ static int xen_smp_intr_init(unsigned int cpu)
        rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_VECTOR,
                                    cpu,
                                    xen_call_function_interrupt,
-                                   IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
+                                   IRQF_PERCPU|IRQF_NOBALANCING,
                                    callfunc_name,
                                    NULL);
        if (rc < 0)
@@ -171,7 +171,7 @@ static int xen_smp_intr_init(unsigned int cpu)
 
        debug_name = kasprintf(GFP_KERNEL, "debug%d", cpu);
        rc = bind_virq_to_irqhandler(VIRQ_DEBUG, cpu, xen_debug_interrupt,
-                                    IRQF_DISABLED | IRQF_PERCPU | IRQF_NOBALANCING,
+                                    IRQF_PERCPU | IRQF_NOBALANCING,
                                     debug_name, NULL);
        if (rc < 0)
                goto fail;
@@ -182,7 +182,7 @@ static int xen_smp_intr_init(unsigned int cpu)
        rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_SINGLE_VECTOR,
                                    cpu,
                                    xen_call_function_single_interrupt,
-                                   IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
+                                   IRQF_PERCPU|IRQF_NOBALANCING,
                                    callfunc_name,
                                    NULL);
        if (rc < 0)
@@ -201,7 +201,7 @@ static int xen_smp_intr_init(unsigned int cpu)
        rc = bind_ipi_to_irqhandler(XEN_IRQ_WORK_VECTOR,
                                    cpu,
                                    xen_irq_work_interrupt,
-                                   IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
+                                   IRQF_PERCPU|IRQF_NOBALANCING,
                                    callfunc_name,
                                    NULL);
        if (rc < 0)
index be6b860..0e36cde 100644 (file)
@@ -234,7 +234,7 @@ void xen_init_lock_cpu(int cpu)
        irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR,
                                     cpu,
                                     dummy_handler,
-                                    IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
+                                    IRQF_PERCPU|IRQF_NOBALANCING,
                                     name,
                                     NULL);
 
index ee36589..12a1ca7 100644 (file)
@@ -443,8 +443,7 @@ void xen_setup_timer(int cpu)
                name = "<timer kasprintf failed>";
 
        irq = bind_virq_to_irqhandler(VIRQ_TIMER, cpu, xen_timer_interrupt,
-                                     IRQF_DISABLED|IRQF_PERCPU|
-                                     IRQF_NOBALANCING|IRQF_TIMER|
+                                     IRQF_PERCPU|IRQF_NOBALANCING|IRQF_TIMER|
                                      IRQF_FORCE_RESUME,
                                      name, NULL);
 
index cf914c8..d38eb92 100644 (file)
@@ -38,35 +38,46 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
        free_page((unsigned long)pgd);
 }
 
-/* Use a slab cache for the pte pages (see also sparc64 implementation) */
-
-extern struct kmem_cache *pgtable_cache;
-
 static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
                                         unsigned long address)
 {
-       return kmem_cache_alloc(pgtable_cache, GFP_KERNEL|__GFP_REPEAT);
+       pte_t *ptep;
+       int i;
+
+       ptep = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT);
+       if (!ptep)
+               return NULL;
+       for (i = 0; i < 1024; i++)
+               pte_clear(NULL, 0, ptep + i);
+       return ptep;
 }
 
 static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
                                        unsigned long addr)
 {
+       pte_t *pte;
        struct page *page;
 
-       page = virt_to_page(pte_alloc_one_kernel(mm, addr));
-       pgtable_page_ctor(page);
+       pte = pte_alloc_one_kernel(mm, addr);
+       if (!pte)
+               return NULL;
+       page = virt_to_page(pte);
+       if (!pgtable_page_ctor(page)) {
+               __free_page(page);
+               return NULL;
+       }
        return page;
 }
 
 static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 {
-       kmem_cache_free(pgtable_cache, pte);
+       free_page((unsigned long)pte);
 }
 
 static inline void pte_free(struct mm_struct *mm, pgtable_t pte)
 {
        pgtable_page_dtor(pte);
-       kmem_cache_free(pgtable_cache, page_address(pte));
+       __free_page(pte);
 }
 #define pmd_pgtable(pmd) pmd_page(pmd)
 
index 0fdf5d0..2164462 100644 (file)
@@ -220,12 +220,11 @@ extern unsigned long empty_zero_page[1024];
 #ifdef CONFIG_MMU
 extern pgd_t swapper_pg_dir[PAGE_SIZE/sizeof(pgd_t)];
 extern void paging_init(void);
-extern void pgtable_cache_init(void);
 #else
 # define swapper_pg_dir NULL
 static inline void paging_init(void) { }
-static inline void pgtable_cache_init(void) { }
 #endif
+static inline void pgtable_cache_init(void) { }
 
 /*
  * The pmd contains the kernel virtual address of the pte page.
index 9481004..470153e 100644 (file)
@@ -76,8 +76,6 @@ struct thread_info {
 
 #endif
 
-#define PREEMPT_ACTIVE         0x10000000
-
 /*
  * macros/functions for gaining access to the thread information structure
  */
index a107757..c43771c 100644 (file)
@@ -50,23 +50,3 @@ void __init init_mmu(void)
         */
        set_ptevaddr_register(PGTABLE_START);
 }
-
-struct kmem_cache *pgtable_cache __read_mostly;
-
-static void pgd_ctor(void *addr)
-{
-       pte_t *ptep = (pte_t *)addr;
-       int i;
-
-       for (i = 0; i < 1024; i++, ptep++)
-               pte_clear(NULL, 0, ptep);
-
-}
-
-void __init pgtable_cache_init(void)
-{
-       pgtable_cache = kmem_cache_create("pgd",
-                       PAGE_SIZE, PAGE_SIZE,
-                       SLAB_HWCACHE_ALIGN,
-                       pgd_ctor);
-}
index 46cd7bd..242df01 100644 (file)
@@ -6,7 +6,6 @@
 #include <linux/init.h>
 #include <linux/bio.h>
 #include <linux/blkdev.h>
-#include <linux/bootmem.h>     /* for max_pfn/max_low_pfn */
 #include <linux/slab.h>
 
 #include "blk.h"
index f8ea39d..0045ace 100644 (file)
@@ -13,8 +13,8 @@
 static LIST_HEAD(blk_mq_cpu_notify_list);
 static DEFINE_SPINLOCK(blk_mq_cpu_notify_lock);
 
-static int __cpuinit blk_mq_main_cpu_notify(struct notifier_block *self,
-                                           unsigned long action, void *hcpu)
+static int blk_mq_main_cpu_notify(struct notifier_block *self,
+                                 unsigned long action, void *hcpu)
 {
        unsigned int cpu = (unsigned long) hcpu;
        struct blk_mq_cpu_notifier *notify;
@@ -28,8 +28,8 @@ static int __cpuinit blk_mq_main_cpu_notify(struct notifier_block *self,
        return NOTIFY_OK;
 }
 
-static void __cpuinit blk_mq_cpu_notify(void *data, unsigned long action,
-                                       unsigned int cpu)
+static void blk_mq_cpu_notify(void *data, unsigned long action,
+                             unsigned int cpu)
 {
        if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) {
                /*
index 88d4e86..cdc629c 100644 (file)
@@ -171,9 +171,12 @@ bool blk_mq_can_queue(struct blk_mq_hw_ctx *hctx)
 }
 EXPORT_SYMBOL(blk_mq_can_queue);
 
-static void blk_mq_rq_ctx_init(struct blk_mq_ctx *ctx, struct request *rq,
-                              unsigned int rw_flags)
+static void blk_mq_rq_ctx_init(struct request_queue *q, struct blk_mq_ctx *ctx,
+                              struct request *rq, unsigned int rw_flags)
 {
+       if (blk_queue_io_stat(q))
+               rw_flags |= REQ_IO_STAT;
+
        rq->mq_ctx = ctx;
        rq->cmd_flags = rw_flags;
        ctx->rq_dispatched[rw_is_sync(rw_flags)]++;
@@ -197,7 +200,7 @@ static struct request *blk_mq_alloc_request_pinned(struct request_queue *q,
 
                rq = __blk_mq_alloc_request(hctx, gfp & ~__GFP_WAIT, reserved);
                if (rq) {
-                       blk_mq_rq_ctx_init(ctx, rq, rw);
+                       blk_mq_rq_ctx_init(q, ctx, rq, rw);
                        break;
                } else if (!(gfp & __GFP_WAIT))
                        break;
@@ -319,7 +322,7 @@ void __blk_mq_end_io(struct request *rq, int error)
                blk_mq_complete_request(rq, error);
 }
 
-#if defined(CONFIG_SMP) && defined(CONFIG_USE_GENERIC_SMP_HELPERS)
+#if defined(CONFIG_SMP)
 
 /*
  * Called with interrupts disabled.
@@ -361,7 +364,7 @@ static int ipi_remote_cpu(struct blk_mq_ctx *ctx, const int cpu,
 
        return true;
 }
-#else /* CONFIG_SMP && CONFIG_USE_GENERIC_SMP_HELPERS */
+#else /* CONFIG_SMP */
 static int ipi_remote_cpu(struct blk_mq_ctx *ctx, const int cpu,
                          struct request *rq, const int error)
 {
@@ -718,6 +721,8 @@ static void __blk_mq_insert_request(struct blk_mq_hw_ctx *hctx,
 {
        struct blk_mq_ctx *ctx = rq->mq_ctx;
 
+       trace_block_rq_insert(hctx->queue, rq);
+
        list_add_tail(&rq->queuelist, &ctx->rq_list);
        blk_mq_hctx_mark_pending(hctx, ctx);
 
@@ -921,7 +926,7 @@ static void blk_mq_make_request(struct request_queue *q, struct bio *bio)
        trace_block_getrq(q, bio, rw);
        rq = __blk_mq_alloc_request(hctx, GFP_ATOMIC, false);
        if (likely(rq))
-               blk_mq_rq_ctx_init(ctx, rq, rw);
+               blk_mq_rq_ctx_init(q, ctx, rq, rw);
        else {
                blk_mq_put_ctx(ctx);
                trace_block_sleeprq(q, bio, rw);
@@ -1377,6 +1382,7 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_reg *reg,
        q->queue_hw_ctx = hctxs;
 
        q->mq_ops = reg->ops;
+       q->queue_flags |= QUEUE_FLAG_MQ_DEFAULT;
 
        blk_queue_make_request(q, blk_mq_make_request);
        blk_queue_rq_timed_out(q, reg->ops->timeout);
@@ -1444,7 +1450,7 @@ void blk_mq_free_queue(struct request_queue *q)
 EXPORT_SYMBOL(blk_mq_free_queue);
 
 /* Basically redo blk_mq_init_queue with queue frozen */
-static void __cpuinit blk_mq_queue_reinit(struct request_queue *q)
+static void blk_mq_queue_reinit(struct request_queue *q)
 {
        blk_mq_freeze_queue(q);
 
@@ -1461,8 +1467,8 @@ static void __cpuinit blk_mq_queue_reinit(struct request_queue *q)
        blk_mq_unfreeze_queue(q);
 }
 
-static int __cpuinit blk_mq_queue_reinit_notify(struct notifier_block *nb,
-               unsigned long action, void *hcpu)
+static int blk_mq_queue_reinit_notify(struct notifier_block *nb,
+                                     unsigned long action, void *hcpu)
 {
        struct request_queue *q;
 
index ce4b8bf..57790c1 100644 (file)
@@ -36,7 +36,7 @@ static void blk_done_softirq(struct softirq_action *h)
        }
 }
 
-#if defined(CONFIG_SMP) && defined(CONFIG_USE_GENERIC_SMP_HELPERS)
+#ifdef CONFIG_SMP
 static void trigger_softirq(void *data)
 {
        struct request *rq = data;
@@ -71,7 +71,7 @@ static int raise_blk_irq(int cpu, struct request *rq)
 
        return 1;
 }
-#else /* CONFIG_SMP && CONFIG_USE_GENERIC_SMP_HELPERS */
+#else /* CONFIG_SMP */
 static int raise_blk_irq(int cpu, struct request *rq)
 {
        return 1;
index 4f8c4d9..9777952 100644 (file)
@@ -288,7 +288,7 @@ static ssize_t
 queue_rq_affinity_store(struct request_queue *q, const char *page, size_t count)
 {
        ssize_t ret = -EINVAL;
-#if defined(CONFIG_USE_GENERIC_SMP_HELPERS)
+#ifdef CONFIG_SMP
        unsigned long val;
 
        ret = queue_var_store(&val, page, count);
index a8287b4..dc51f46 100644 (file)
@@ -96,6 +96,7 @@
  * - Code works, detects all the partitions.
  *
  ************************************************************/
+#include <linux/kernel.h>
 #include <linux/crc32.h>
 #include <linux/ctype.h>
 #include <linux/math64.h>
@@ -715,8 +716,8 @@ int efi_partition(struct parsed_partitions *state)
                efi_guid_unparse(&ptes[i].unique_partition_guid, info->uuid);
 
                /* Naively convert UTF16-LE to 7 bits. */
-               label_max = min(sizeof(info->volname) - 1,
-                               sizeof(ptes[i].partition_name));
+               label_max = min(ARRAY_SIZE(info->volname) - 1,
+                               ARRAY_SIZE(ptes[i].partition_name));
                info->volname[label_max] = 0;
                while (label_count < label_max) {
                        u8 c = ptes[i].partition_name[label_count] & 0xff;
index 71f337a..4ae5734 100644 (file)
@@ -1402,6 +1402,9 @@ config CRYPTO_USER_API_SKCIPHER
          This option enables the user-spaces interface for symmetric
          key cipher algorithms.
 
+config CRYPTO_HASH_INFO
+       bool
+
 source "drivers/crypto/Kconfig"
 source crypto/asymmetric_keys/Kconfig
 
index 80019ba..b3a7e80 100644 (file)
@@ -104,3 +104,4 @@ obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
 obj-$(CONFIG_XOR_BLOCKS) += xor.o
 obj-$(CONFIG_ASYNC_CORE) += async_tx/
 obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/
+obj-$(CONFIG_CRYPTO_HASH_INFO) += hash_info.o
index ac33d5f..966f893 100644 (file)
@@ -434,7 +434,7 @@ int af_alg_wait_for_completion(int err, struct af_alg_completion *completion)
        case -EINPROGRESS:
        case -EBUSY:
                wait_for_completion(&completion->completion);
-               INIT_COMPLETION(completion->completion);
+               reinit_completion(&completion->completion);
                err = completion->err;
                break;
        };
index 0262210..ef5356c 100644 (file)
@@ -161,8 +161,6 @@ static int hash_recvmsg(struct kiocb *unused, struct socket *sock,
        else if (len < ds)
                msg->msg_flags |= MSG_TRUNC;
 
-       msg->msg_namelen = 0;
-
        lock_sock(sk);
        if (ctx->more) {
                ctx->more = 0;
index a1c4f0a..6a6dfc0 100644 (file)
@@ -432,7 +432,6 @@ static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock,
        long copied = 0;
 
        lock_sock(sk);
-       msg->msg_namelen = 0;
        for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
             iovlen--, iov++) {
                unsigned long seglen = iov->iov_len;
index 6d2c2ea..03a6eb9 100644 (file)
@@ -12,6 +12,8 @@ if ASYMMETRIC_KEY_TYPE
 config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
        tristate "Asymmetric public-key crypto algorithm subtype"
        select MPILIB
+       select PUBLIC_KEY_ALGO_RSA
+       select CRYPTO_HASH_INFO
        help
          This option provides support for asymmetric public key type handling.
          If signature generation and/or verification are to be used,
@@ -20,8 +22,8 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
 
 config PUBLIC_KEY_ALGO_RSA
        tristate "RSA public-key algorithm"
-       depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
        select MPILIB_EXTRA
+       select MPILIB
        help
          This option enables support for the RSA algorithm (PKCS#1, RFC3447).
 
index cf80765..b77eb53 100644 (file)
@@ -209,6 +209,7 @@ struct key_type key_type_asymmetric = {
        .match          = asymmetric_key_match,
        .destroy        = asymmetric_key_destroy,
        .describe       = asymmetric_key_describe,
+       .def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE,
 };
 EXPORT_SYMBOL_GPL(key_type_asymmetric);
 
index cb2e291..97eb001 100644 (file)
 
 MODULE_LICENSE("GPL");
 
-const char *const pkey_algo[PKEY_ALGO__LAST] = {
+const char *const pkey_algo_name[PKEY_ALGO__LAST] = {
        [PKEY_ALGO_DSA]         = "DSA",
        [PKEY_ALGO_RSA]         = "RSA",
 };
-EXPORT_SYMBOL_GPL(pkey_algo);
+EXPORT_SYMBOL_GPL(pkey_algo_name);
 
-const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
-       [PKEY_HASH_MD4]         = "md4",
-       [PKEY_HASH_MD5]         = "md5",
-       [PKEY_HASH_SHA1]        = "sha1",
-       [PKEY_HASH_RIPE_MD_160] = "rmd160",
-       [PKEY_HASH_SHA256]      = "sha256",
-       [PKEY_HASH_SHA384]      = "sha384",
-       [PKEY_HASH_SHA512]      = "sha512",
-       [PKEY_HASH_SHA224]      = "sha224",
+const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST] = {
+#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \
+       defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE)
+       [PKEY_ALGO_RSA]         = &RSA_public_key_algorithm,
+#endif
 };
-EXPORT_SYMBOL_GPL(pkey_hash_algo);
+EXPORT_SYMBOL_GPL(pkey_algo);
 
-const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = {
+const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = {
        [PKEY_ID_PGP]           = "PGP",
        [PKEY_ID_X509]          = "X509",
 };
-EXPORT_SYMBOL_GPL(pkey_id_type);
+EXPORT_SYMBOL_GPL(pkey_id_type_name);
 
 /*
  * Provide a part of a description of the key for /proc/keys.
@@ -56,7 +52,7 @@ static void public_key_describe(const struct key *asymmetric_key,
 
        if (key)
                seq_printf(m, "%s.%s",
-                          pkey_id_type[key->id_type], key->algo->name);
+                          pkey_id_type_name[key->id_type], key->algo->name);
 }
 
 /*
@@ -78,21 +74,45 @@ EXPORT_SYMBOL_GPL(public_key_destroy);
 /*
  * Verify a signature using a public key.
  */
-static int public_key_verify_signature(const struct key *key,
-                                      const struct public_key_signature *sig)
+int public_key_verify_signature(const struct public_key *pk,
+                               const struct public_key_signature *sig)
 {
-       const struct public_key *pk = key->payload.data;
+       const struct public_key_algorithm *algo;
+
+       BUG_ON(!pk);
+       BUG_ON(!pk->mpi[0]);
+       BUG_ON(!pk->mpi[1]);
+       BUG_ON(!sig);
+       BUG_ON(!sig->digest);
+       BUG_ON(!sig->mpi[0]);
+
+       algo = pk->algo;
+       if (!algo) {
+               if (pk->pkey_algo >= PKEY_ALGO__LAST)
+                       return -ENOPKG;
+               algo = pkey_algo[pk->pkey_algo];
+               if (!algo)
+                       return -ENOPKG;
+       }
 
-       if (!pk->algo->verify_signature)
+       if (!algo->verify_signature)
                return -ENOTSUPP;
 
-       if (sig->nr_mpi != pk->algo->n_sig_mpi) {
+       if (sig->nr_mpi != algo->n_sig_mpi) {
                pr_debug("Signature has %u MPI not %u\n",
-                        sig->nr_mpi, pk->algo->n_sig_mpi);
+                        sig->nr_mpi, algo->n_sig_mpi);
                return -EINVAL;
        }
 
-       return pk->algo->verify_signature(pk, sig);
+       return algo->verify_signature(pk, sig);
+}
+EXPORT_SYMBOL_GPL(public_key_verify_signature);
+
+static int public_key_verify_signature_2(const struct key *key,
+                                        const struct public_key_signature *sig)
+{
+       const struct public_key *pk = key->payload.data;
+       return public_key_verify_signature(pk, sig);
 }
 
 /*
@@ -103,6 +123,6 @@ struct asymmetric_key_subtype public_key_subtype = {
        .name                   = "public_key",
        .describe               = public_key_describe,
        .destroy                = public_key_destroy,
-       .verify_signature       = public_key_verify_signature,
+       .verify_signature       = public_key_verify_signature_2,
 };
 EXPORT_SYMBOL_GPL(public_key_subtype);
index 5e5e356..5c37a22 100644 (file)
@@ -28,3 +28,9 @@ struct public_key_algorithm {
 };
 
 extern const struct public_key_algorithm RSA_public_key_algorithm;
+
+/*
+ * public_key.c
+ */
+extern int public_key_verify_signature(const struct public_key *pk,
+                                      const struct public_key_signature *sig);
index 4a6a069..90a17f5 100644 (file)
@@ -73,13 +73,13 @@ static const struct {
        size_t size;
 } RSA_ASN1_templates[PKEY_HASH__LAST] = {
 #define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
-       [PKEY_HASH_MD5]         = _(MD5),
-       [PKEY_HASH_SHA1]        = _(SHA1),
-       [PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160),
-       [PKEY_HASH_SHA256]      = _(SHA256),
-       [PKEY_HASH_SHA384]      = _(SHA384),
-       [PKEY_HASH_SHA512]      = _(SHA512),
-       [PKEY_HASH_SHA224]      = _(SHA224),
+       [HASH_ALGO_MD5]         = _(MD5),
+       [HASH_ALGO_SHA1]        = _(SHA1),
+       [HASH_ALGO_RIPE_MD_160] = _(RIPE_MD_160),
+       [HASH_ALGO_SHA256]      = _(SHA256),
+       [HASH_ALGO_SHA384]      = _(SHA384),
+       [HASH_ALGO_SHA512]      = _(SHA512),
+       [HASH_ALGO_SHA224]      = _(SHA224),
 #undef _
 };
 
index facbf26..2989316 100644 (file)
@@ -47,6 +47,8 @@ void x509_free_certificate(struct x509_certificate *cert)
                kfree(cert->subject);
                kfree(cert->fingerprint);
                kfree(cert->authority);
+               kfree(cert->sig.digest);
+               mpi_free(cert->sig.rsa.s);
                kfree(cert);
        }
 }
@@ -152,33 +154,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
                return -ENOPKG; /* Unsupported combination */
 
        case OID_md4WithRSAEncryption:
-               ctx->cert->sig_hash_algo = PKEY_HASH_MD5;
-               ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+               ctx->cert->sig.pkey_hash_algo = HASH_ALGO_MD5;
+               ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
                break;
 
        case OID_sha1WithRSAEncryption:
-               ctx->cert->sig_hash_algo = PKEY_HASH_SHA1;
-               ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+               ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA1;
+               ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
                break;
 
        case OID_sha256WithRSAEncryption:
-               ctx->cert->sig_hash_algo = PKEY_HASH_SHA256;
-               ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+               ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA256;
+               ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
                break;
 
        case OID_sha384WithRSAEncryption:
-               ctx->cert->sig_hash_algo = PKEY_HASH_SHA384;
-               ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+               ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA384;
+               ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
                break;
 
        case OID_sha512WithRSAEncryption:
-               ctx->cert->sig_hash_algo = PKEY_HASH_SHA512;
-               ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+               ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA512;
+               ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
                break;
 
        case OID_sha224WithRSAEncryption:
-               ctx->cert->sig_hash_algo = PKEY_HASH_SHA224;
-               ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+               ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA224;
+               ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
                break;
        }
 
@@ -203,8 +205,8 @@ int x509_note_signature(void *context, size_t hdrlen,
                return -EINVAL;
        }
 
-       ctx->cert->sig = value;
-       ctx->cert->sig_size = vlen;
+       ctx->cert->raw_sig = value;
+       ctx->cert->raw_sig_size = vlen;
        return 0;
 }
 
@@ -343,8 +345,9 @@ int x509_extract_key_data(void *context, size_t hdrlen,
        if (ctx->last_oid != OID_rsaEncryption)
                return -ENOPKG;
 
-       /* There seems to be an extraneous 0 byte on the front of the data */
-       ctx->cert->pkey_algo = PKEY_ALGO_RSA;
+       ctx->cert->pub->pkey_algo = PKEY_ALGO_RSA;
+
+       /* Discard the BIT STRING metadata */
        ctx->key = value + 1;
        ctx->key_size = vlen - 1;
        return 0;
index f86dc5f..87d9cc2 100644 (file)
@@ -9,6 +9,7 @@
  * 2 of the Licence, or (at your option) any later version.
  */
 
+#include <linux/time.h>
 #include <crypto/public_key.h>
 
 struct x509_certificate {
@@ -20,13 +21,11 @@ struct x509_certificate {
        char            *authority;             /* Authority key fingerprint as hex */
        struct tm       valid_from;
        struct tm       valid_to;
-       enum pkey_algo  pkey_algo : 8;          /* Public key algorithm */
-       enum pkey_algo  sig_pkey_algo : 8;      /* Signature public key algorithm */
-       enum pkey_hash_algo sig_hash_algo : 8;  /* Signature hash algorithm */
        const void      *tbs;                   /* Signed data */
-       size_t          tbs_size;               /* Size of signed data */
-       const void      *sig;                   /* Signature data */
-       size_t          sig_size;               /* Size of sigature */
+       unsigned        tbs_size;               /* Size of signed data */
+       unsigned        raw_sig_size;           /* Size of sigature */
+       const void      *raw_sig;               /* Signature data */
+       struct public_key_signature sig;        /* Signature parameters */
 };
 
 /*
@@ -34,3 +33,10 @@ struct x509_certificate {
  */
 extern void x509_free_certificate(struct x509_certificate *cert);
 extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
+
+/*
+ * x509_public_key.c
+ */
+extern int x509_get_sig_params(struct x509_certificate *cert);
+extern int x509_check_signature(const struct public_key *pub,
+                               struct x509_certificate *cert);
index 06007f0..f83300b 100644 (file)
 #include <linux/asn1_decoder.h>
 #include <keys/asymmetric-subtype.h>
 #include <keys/asymmetric-parser.h>
+#include <keys/system_keyring.h>
 #include <crypto/hash.h>
 #include "asymmetric_keys.h"
 #include "public_key.h"
 #include "x509_parser.h"
 
-static const
-struct public_key_algorithm *x509_public_key_algorithms[PKEY_ALGO__LAST] = {
-       [PKEY_ALGO_DSA]         = NULL,
-#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \
-       defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE)
-       [PKEY_ALGO_RSA]         = &RSA_public_key_algorithm,
-#endif
-};
+/*
+ * Find a key in the given keyring by issuer and authority.
+ */
+static struct key *x509_request_asymmetric_key(
+       struct key *keyring,
+       const char *signer, size_t signer_len,
+       const char *authority, size_t auth_len)
+{
+       key_ref_t key;
+       char *id;
+
+       /* Construct an identifier. */
+       id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL);
+       if (!id)
+               return ERR_PTR(-ENOMEM);
+
+       memcpy(id, signer, signer_len);
+       id[signer_len + 0] = ':';
+       id[signer_len + 1] = ' ';
+       memcpy(id + signer_len + 2, authority, auth_len);
+       id[signer_len + 2 + auth_len] = 0;
+
+       pr_debug("Look up: \"%s\"\n", id);
+
+       key = keyring_search(make_key_ref(keyring, 1),
+                            &key_type_asymmetric, id);
+       if (IS_ERR(key))
+               pr_debug("Request for module key '%s' err %ld\n",
+                        id, PTR_ERR(key));
+       kfree(id);
+
+       if (IS_ERR(key)) {
+               switch (PTR_ERR(key)) {
+                       /* Hide some search errors */
+               case -EACCES:
+               case -ENOTDIR:
+               case -EAGAIN:
+                       return ERR_PTR(-ENOKEY);
+               default:
+                       return ERR_CAST(key);
+               }
+       }
+
+       pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key)));
+       return key_ref_to_ptr(key);
+}
 
 /*
- * Check the signature on a certificate using the provided public key
+ * Set up the signature parameters in an X.509 certificate.  This involves
+ * digesting the signed data and extracting the signature.
  */
-static int x509_check_signature(const struct public_key *pub,
-                               const struct x509_certificate *cert)
+int x509_get_sig_params(struct x509_certificate *cert)
 {
-       struct public_key_signature *sig;
        struct crypto_shash *tfm;
        struct shash_desc *desc;
        size_t digest_size, desc_size;
+       void *digest;
        int ret;
 
        pr_devel("==>%s()\n", __func__);
-       
+
+       if (cert->sig.rsa.s)
+               return 0;
+
+       cert->sig.rsa.s = mpi_read_raw_data(cert->raw_sig, cert->raw_sig_size);
+       if (!cert->sig.rsa.s)
+               return -ENOMEM;
+       cert->sig.nr_mpi = 1;
+
        /* Allocate the hashing algorithm we're going to need and find out how
         * big the hash operational data will be.
         */
-       tfm = crypto_alloc_shash(pkey_hash_algo[cert->sig_hash_algo], 0, 0);
+       tfm = crypto_alloc_shash(hash_algo_name[cert->sig.pkey_hash_algo], 0, 0);
        if (IS_ERR(tfm))
                return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
 
        desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
        digest_size = crypto_shash_digestsize(tfm);
 
-       /* We allocate the hash operational data storage on the end of our
-        * context data.
+       /* We allocate the hash operational data storage on the end of the
+        * digest storage space.
         */
        ret = -ENOMEM;
-       sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL);
-       if (!sig)
-               goto error_no_sig;
+       digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
+       if (!digest)
+               goto error;
 
-       sig->pkey_hash_algo     = cert->sig_hash_algo;
-       sig->digest             = (u8 *)sig + sizeof(*sig) + desc_size;
-       sig->digest_size        = digest_size;
+       cert->sig.digest = digest;
+       cert->sig.digest_size = digest_size;
 
-       desc = (void *)sig + sizeof(*sig);
-       desc->tfm       = tfm;
-       desc->flags     = CRYPTO_TFM_REQ_MAY_SLEEP;
+       desc = digest + digest_size;
+       desc->tfm = tfm;
+       desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
 
        ret = crypto_shash_init(desc);
        if (ret < 0)
                goto error;
+       might_sleep();
+       ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, digest);
+error:
+       crypto_free_shash(tfm);
+       pr_devel("<==%s() = %d\n", __func__, ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(x509_get_sig_params);
 
-       ret = -ENOMEM;
-       sig->rsa.s = mpi_read_raw_data(cert->sig, cert->sig_size);
-       if (!sig->rsa.s)
-               goto error;
+/*
+ * Check the signature on a certificate using the provided public key
+ */
+int x509_check_signature(const struct public_key *pub,
+                        struct x509_certificate *cert)
+{
+       int ret;
 
-       ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
-       if (ret < 0)
-               goto error_mpi;
+       pr_devel("==>%s()\n", __func__);
 
-       ret = pub->algo->verify_signature(pub, sig);
+       ret = x509_get_sig_params(cert);
+       if (ret < 0)
+               return ret;
 
+       ret = public_key_verify_signature(pub, &cert->sig);
        pr_debug("Cert Verification: %d\n", ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(x509_check_signature);
 
-error_mpi:
-       mpi_free(sig->rsa.s);
-error:
-       kfree(sig);
-error_no_sig:
-       crypto_free_shash(tfm);
+/*
+ * Check the new certificate against the ones in the trust keyring.  If one of
+ * those is the signing key and validates the new certificate, then mark the
+ * new certificate as being trusted.
+ *
+ * Return 0 if the new certificate was successfully validated, 1 if we couldn't
+ * find a matching parent certificate in the trusted list and an error if there
+ * is a matching certificate but the signature check fails.
+ */
+static int x509_validate_trust(struct x509_certificate *cert,
+                              struct key *trust_keyring)
+{
+       const struct public_key *pk;
+       struct key *key;
+       int ret = 1;
 
-       pr_devel("<==%s() = %d\n", __func__, ret);
+       key = x509_request_asymmetric_key(trust_keyring,
+                                         cert->issuer, strlen(cert->issuer),
+                                         cert->authority,
+                                         strlen(cert->authority));
+       if (!IS_ERR(key))  {
+               pk = key->payload.data;
+               ret = x509_check_signature(pk, cert);
+       }
        return ret;
 }
 
@@ -106,7 +183,6 @@ error_no_sig:
 static int x509_key_preparse(struct key_preparsed_payload *prep)
 {
        struct x509_certificate *cert;
-       struct tm now;
        size_t srlen, sulen;
        char *desc = NULL;
        int ret;
@@ -117,7 +193,18 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
 
        pr_devel("Cert Issuer: %s\n", cert->issuer);
        pr_devel("Cert Subject: %s\n", cert->subject);
-       pr_devel("Cert Key Algo: %s\n", pkey_algo[cert->pkey_algo]);
+
+       if (cert->pub->pkey_algo >= PKEY_ALGO__LAST ||
+           cert->sig.pkey_algo >= PKEY_ALGO__LAST ||
+           cert->sig.pkey_hash_algo >= PKEY_HASH__LAST ||
+           !pkey_algo[cert->pub->pkey_algo] ||
+           !pkey_algo[cert->sig.pkey_algo] ||
+           !hash_algo_name[cert->sig.pkey_hash_algo]) {
+               ret = -ENOPKG;
+               goto error_free_cert;
+       }
+
+       pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]);
        pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n",
                 cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1,
                 cert->valid_from.tm_mday, cert->valid_from.tm_hour,
@@ -127,61 +214,29 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
                 cert->valid_to.tm_mday, cert->valid_to.tm_hour,
                 cert->valid_to.tm_min,  cert->valid_to.tm_sec);
        pr_devel("Cert Signature: %s + %s\n",
-                pkey_algo[cert->sig_pkey_algo],
-                pkey_hash_algo[cert->sig_hash_algo]);
+                pkey_algo_name[cert->sig.pkey_algo],
+                hash_algo_name[cert->sig.pkey_hash_algo]);
 
-       if (!cert->fingerprint || !cert->authority) {
-               pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n",
+       if (!cert->fingerprint) {
+               pr_warn("Cert for '%s' must have a SubjKeyId extension\n",
                        cert->subject);
                ret = -EKEYREJECTED;
                goto error_free_cert;
        }
 
-       time_to_tm(CURRENT_TIME.tv_sec, 0, &now);
-       pr_devel("Now: %04ld-%02d-%02d %02d:%02d:%02d\n",
-                now.tm_year + 1900, now.tm_mon + 1, now.tm_mday,
-                now.tm_hour, now.tm_min,  now.tm_sec);
-       if (now.tm_year < cert->valid_from.tm_year ||
-           (now.tm_year == cert->valid_from.tm_year &&
-            (now.tm_mon < cert->valid_from.tm_mon ||
-             (now.tm_mon == cert->valid_from.tm_mon &&
-              (now.tm_mday < cert->valid_from.tm_mday ||
-               (now.tm_mday == cert->valid_from.tm_mday &&
-                (now.tm_hour < cert->valid_from.tm_hour ||
-                 (now.tm_hour == cert->valid_from.tm_hour &&
-                  (now.tm_min < cert->valid_from.tm_min ||
-                   (now.tm_min == cert->valid_from.tm_min &&
-                    (now.tm_sec < cert->valid_from.tm_sec
-                     ))))))))))) {
-               pr_warn("Cert %s is not yet valid\n", cert->fingerprint);
-               ret = -EKEYREJECTED;
-               goto error_free_cert;
-       }
-       if (now.tm_year > cert->valid_to.tm_year ||
-           (now.tm_year == cert->valid_to.tm_year &&
-            (now.tm_mon > cert->valid_to.tm_mon ||
-             (now.tm_mon == cert->valid_to.tm_mon &&
-              (now.tm_mday > cert->valid_to.tm_mday ||
-               (now.tm_mday == cert->valid_to.tm_mday &&
-                (now.tm_hour > cert->valid_to.tm_hour ||
-                 (now.tm_hour == cert->valid_to.tm_hour &&
-                  (now.tm_min > cert->valid_to.tm_min ||
-                   (now.tm_min == cert->valid_to.tm_min &&
-                    (now.tm_sec > cert->valid_to.tm_sec
-                     ))))))))))) {
-               pr_warn("Cert %s has expired\n", cert->fingerprint);
-               ret = -EKEYEXPIRED;
-               goto error_free_cert;
-       }
-
-       cert->pub->algo = x509_public_key_algorithms[cert->pkey_algo];
+       cert->pub->algo = pkey_algo[cert->pub->pkey_algo];
        cert->pub->id_type = PKEY_ID_X509;
 
-       /* Check the signature on the key */
-       if (strcmp(cert->fingerprint, cert->authority) == 0) {
-               ret = x509_check_signature(cert->pub, cert);
+       /* Check the signature on the key if it appears to be self-signed */
+       if (!cert->authority ||
+           strcmp(cert->fingerprint, cert->authority) == 0) {
+               ret = x509_check_signature(cert->pub, cert); /* self-signed */
                if (ret < 0)
                        goto error_free_cert;
+       } else {
+               ret = x509_validate_trust(cert, system_trusted_keyring);
+               if (!ret)
+                       prep->trusted = 1;
        }
 
        /* Propose a description */
@@ -237,3 +292,6 @@ static void __exit x509_key_exit(void)
 
 module_init(x509_key_init);
 module_exit(x509_key_exit);
+
+MODULE_DESCRIPTION("X.509 certificate parser");
+MODULE_LICENSE("GPL");
index 9e62fef..f8c0b8d 100644 (file)
@@ -50,33 +50,36 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,
                                                      &dest, 1, &src, 1, len);
        struct dma_device *device = chan ? chan->device : NULL;
        struct dma_async_tx_descriptor *tx = NULL;
+       struct dmaengine_unmap_data *unmap = NULL;
 
-       if (device && is_dma_copy_aligned(device, src_offset, dest_offset, len)) {
-               dma_addr_t dma_dest, dma_src;
+       if (device)
+               unmap = dmaengine_get_unmap_data(device->dev, 2, GFP_NOIO);
+
+       if (unmap && is_dma_copy_aligned(device, src_offset, dest_offset, len)) {
                unsigned long dma_prep_flags = 0;
 
                if (submit->cb_fn)
                        dma_prep_flags |= DMA_PREP_INTERRUPT;
                if (submit->flags & ASYNC_TX_FENCE)
                        dma_prep_flags |= DMA_PREP_FENCE;
-               dma_dest = dma_map_page(device->dev, dest, dest_offset, len,
-                                       DMA_FROM_DEVICE);
-
-               dma_src = dma_map_page(device->dev, src, src_offset, len,
-                                      DMA_TO_DEVICE);
-
-               tx = device->device_prep_dma_memcpy(chan, dma_dest, dma_src,
-                                                   len, dma_prep_flags);
-               if (!tx) {
-                       dma_unmap_page(device->dev, dma_dest, len,
-                                      DMA_FROM_DEVICE);
-                       dma_unmap_page(device->dev, dma_src, len,
-                                      DMA_TO_DEVICE);
-               }
+
+               unmap->to_cnt = 1;
+               unmap->addr[0] = dma_map_page(device->dev, src, src_offset, len,
+                                             DMA_TO_DEVICE);
+               unmap->from_cnt = 1;
+               unmap->addr[1] = dma_map_page(device->dev, dest, dest_offset, len,
+                                             DMA_FROM_DEVICE);
+               unmap->len = len;
+
+               tx = device->device_prep_dma_memcpy(chan, unmap->addr[1],
+                                                   unmap->addr[0], len,
+                                                   dma_prep_flags);
        }
 
        if (tx) {
                pr_debug("%s: (async) len: %zu\n", __func__, len);
+
+               dma_set_unmap(tx, unmap);
                async_tx_submit(chan, tx, submit);
        } else {
                void *dest_buf, *src_buf;
@@ -96,6 +99,8 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,
                async_tx_sync_epilog(submit);
        }
 
+       dmaengine_unmap_put(unmap);
+
        return tx;
 }
 EXPORT_SYMBOL_GPL(async_memcpy);
index 91d5d38..d05327c 100644 (file)
@@ -46,49 +46,24 @@ static struct page *pq_scribble_page;
  * do_async_gen_syndrome - asynchronously calculate P and/or Q
  */
 static __async_inline struct dma_async_tx_descriptor *
-do_async_gen_syndrome(struct dma_chan *chan, struct page **blocks,
-                     const unsigned char *scfs, unsigned int offset, int disks,
-                     size_t len, dma_addr_t *dma_src,
+do_async_gen_syndrome(struct dma_chan *chan,
+                     const unsigned char *scfs, int disks,
+                     struct dmaengine_unmap_data *unmap,
+                     enum dma_ctrl_flags dma_flags,
                      struct async_submit_ctl *submit)
 {
        struct dma_async_tx_descriptor *tx = NULL;
        struct dma_device *dma = chan->device;
-       enum dma_ctrl_flags dma_flags = 0;
        enum async_tx_flags flags_orig = submit->flags;
        dma_async_tx_callback cb_fn_orig = submit->cb_fn;
        dma_async_tx_callback cb_param_orig = submit->cb_param;
        int src_cnt = disks - 2;
-       unsigned char coefs[src_cnt];
        unsigned short pq_src_cnt;
        dma_addr_t dma_dest[2];
        int src_off = 0;
-       int idx;
-       int i;
 
-       /* DMAs use destinations as sources, so use BIDIRECTIONAL mapping */
-       if (P(blocks, disks))
-               dma_dest[0] = dma_map_page(dma->dev, P(blocks, disks), offset,
-                                          len, DMA_BIDIRECTIONAL);
-       else
-               dma_flags |= DMA_PREP_PQ_DISABLE_P;
-       if (Q(blocks, disks))
-               dma_dest[1] = dma_map_page(dma->dev, Q(blocks, disks), offset,
-                                          len, DMA_BIDIRECTIONAL);
-       else
-               dma_flags |= DMA_PREP_PQ_DISABLE_Q;
-
-       /* convert source addresses being careful to collapse 'empty'
-        * sources and update the coefficients accordingly
-        */
-       for (i = 0, idx = 0; i < src_cnt; i++) {
-               if (blocks[i] == NULL)
-                       continue;
-               dma_src[idx] = dma_map_page(dma->dev, blocks[i], offset, len,
-                                           DMA_TO_DEVICE);
-               coefs[idx] = scfs[i];
-               idx++;
-       }
-       src_cnt = idx;
+       if (submit->flags & ASYNC_TX_FENCE)
+               dma_flags |= DMA_PREP_FENCE;
 
        while (src_cnt > 0) {
                submit->flags = flags_orig;
@@ -100,28 +75,25 @@ do_async_gen_syndrome(struct dma_chan *chan, struct page **blocks,
                if (src_cnt > pq_src_cnt) {
                        submit->flags &= ~ASYNC_TX_ACK;
                        submit->flags |= ASYNC_TX_FENCE;
-                       dma_flags |= DMA_COMPL_SKIP_DEST_UNMAP;
                        submit->cb_fn = NULL;
                        submit->cb_param = NULL;
                } else {
-                       dma_flags &= ~DMA_COMPL_SKIP_DEST_UNMAP;
                        submit->cb_fn = cb_fn_orig;
                        submit->cb_param = cb_param_orig;
                        if (cb_fn_orig)
                                dma_flags |= DMA_PREP_INTERRUPT;
                }
-               if (submit->flags & ASYNC_TX_FENCE)
-                       dma_flags |= DMA_PREP_FENCE;
 
-               /* Since we have clobbered the src_list we are committed
-                * to doing this asynchronously.  Drivers force forward
-                * progress in case they can not provide a descriptor
+               /* Drivers force forward progress in case they can not provide
+                * a descriptor
                 */
                for (;;) {
+                       dma_dest[0] = unmap->addr[disks - 2];
+                       dma_dest[1] = unmap->addr[disks - 1];
                        tx = dma->device_prep_dma_pq(chan, dma_dest,
-                                                    &dma_src[src_off],
+                                                    &unmap->addr[src_off],
                                                     pq_src_cnt,
-                                                    &coefs[src_off], len,
+                                                    &scfs[src_off], unmap->len,
                                                     dma_flags);
                        if (likely(tx))
                                break;
@@ -129,6 +101,7 @@ do_async_gen_syndrome(struct dma_chan *chan, struct page **blocks,
                        dma_async_issue_pending(chan);
                }
 
+               dma_set_unmap(tx, unmap);
                async_tx_submit(chan, tx, submit);
                submit->depend_tx = tx;
 
@@ -188,10 +161,6 @@ do_sync_gen_syndrome(struct page **blocks, unsigned int offset, int disks,
  * set to NULL those buffers will be replaced with the raid6_zero_page
  * in the synchronous path and omitted in the hardware-asynchronous
  * path.
- *
- * 'blocks' note: if submit->scribble is NULL then the contents of
- * 'blocks' may be overwritten to perform address conversions
- * (dma_map_page() or page_address()).
  */
 struct dma_async_tx_descriptor *
 async_gen_syndrome(struct page **blocks, unsigned int offset, int disks,
@@ -202,26 +171,69 @@ async_gen_syndrome(struct page **blocks, unsigned int offset, int disks,
                                                      &P(blocks, disks), 2,
                                                      blocks, src_cnt, len);
        struct dma_device *device = chan ? chan->device : NULL;
-       dma_addr_t *dma_src = NULL;
+       struct dmaengine_unmap_data *unmap = NULL;
 
        BUG_ON(disks > 255 || !(P(blocks, disks) || Q(blocks, disks)));
 
-       if (submit->scribble)
-               dma_src = submit->scribble;
-       else if (sizeof(dma_addr_t) <= sizeof(struct page *))
-               dma_src = (dma_addr_t *) blocks;
+       if (device)
+               unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOIO);
 
-       if (dma_src && device &&
+       if (unmap &&
            (src_cnt <= dma_maxpq(device, 0) ||
             dma_maxpq(device, DMA_PREP_CONTINUE) > 0) &&
            is_dma_pq_aligned(device, offset, 0, len)) {
+               struct dma_async_tx_descriptor *tx;
+               enum dma_ctrl_flags dma_flags = 0;
+               unsigned char coefs[src_cnt];
+               int i, j;
+
                /* run the p+q asynchronously */
                pr_debug("%s: (async) disks: %d len: %zu\n",
                         __func__, disks, len);
-               return do_async_gen_syndrome(chan, blocks, raid6_gfexp, offset,
-                                            disks, len, dma_src, submit);
+
+               /* convert source addresses being careful to collapse 'empty'
+                * sources and update the coefficients accordingly
+                */
+               unmap->len = len;
+               for (i = 0, j = 0; i < src_cnt; i++) {
+                       if (blocks[i] == NULL)
+                               continue;
+                       unmap->addr[j] = dma_map_page(device->dev, blocks[i], offset,
+                                                     len, DMA_TO_DEVICE);
+                       coefs[j] = raid6_gfexp[i];
+                       unmap->to_cnt++;
+                       j++;
+               }
+
+               /*
+                * DMAs use destinations as sources,
+                * so use BIDIRECTIONAL mapping
+                */
+               unmap->bidi_cnt++;
+               if (P(blocks, disks))
+                       unmap->addr[j++] = dma_map_page(device->dev, P(blocks, disks),
+                                                       offset, len, DMA_BIDIRECTIONAL);
+               else {
+                       unmap->addr[j++] = 0;
+                       dma_flags |= DMA_PREP_PQ_DISABLE_P;
+               }
+
+               unmap->bidi_cnt++;
+               if (Q(blocks, disks))
+                       unmap->addr[j++] = dma_map_page(device->dev, Q(blocks, disks),
+                                                      offset, len, DMA_BIDIRECTIONAL);
+               else {
+                       unmap->addr[j++] = 0;
+                       dma_flags |= DMA_PREP_PQ_DISABLE_Q;
+               }
+
+               tx = do_async_gen_syndrome(chan, coefs, j, unmap, dma_flags, submit);
+               dmaengine_unmap_put(unmap);
+               return tx;
        }
 
+       dmaengine_unmap_put(unmap);
+
        /* run the pq synchronously */
        pr_debug("%s: (sync) disks: %d len: %zu\n", __func__, disks, len);
 
@@ -277,50 +289,60 @@ async_syndrome_val(struct page **blocks, unsigned int offset, int disks,
        struct dma_async_tx_descriptor *tx;
        unsigned char coefs[disks-2];
        enum dma_ctrl_flags dma_flags = submit->cb_fn ? DMA_PREP_INTERRUPT : 0;
-       dma_addr_t *dma_src = NULL;
-       int src_cnt = 0;
+       struct dmaengine_unmap_data *unmap = NULL;
 
        BUG_ON(disks < 4);
 
-       if (submit->scribble)
-               dma_src = submit->scribble;
-       else if (sizeof(dma_addr_t) <= sizeof(struct page *))
-               dma_src = (dma_addr_t *) blocks;
+       if (device)
+               unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOIO);
 
-       if (dma_src && device && disks <= dma_maxpq(device, 0) &&
+       if (unmap && disks <= dma_maxpq(device, 0) &&
            is_dma_pq_aligned(device, offset, 0, len)) {
                struct device *dev = device->dev;
-               dma_addr_t *pq = &dma_src[disks-2];
-               int i;
+               dma_addr_t pq[2];
+               int i, j = 0, src_cnt = 0;
 
                pr_debug("%s: (async) disks: %d len: %zu\n",
                         __func__, disks, len);
-               if (!P(blocks, disks))
+
+               unmap->len = len;
+               for (i = 0; i < disks-2; i++)
+                       if (likely(blocks[i])) {
+                               unmap->addr[j] = dma_map_page(dev, blocks[i],
+                                                             offset, len,
+                                                             DMA_TO_DEVICE);
+                               coefs[j] = raid6_gfexp[i];
+                               unmap->to_cnt++;
+                               src_cnt++;
+                               j++;
+                       }
+
+               if (!P(blocks, disks)) {
+                       pq[0] = 0;
                        dma_flags |= DMA_PREP_PQ_DISABLE_P;
-               else
+               } else {
                        pq[0] = dma_map_page(dev, P(blocks, disks),
                                             offset, len,
                                             DMA_TO_DEVICE);
-               if (!Q(blocks, disks))
+                       unmap->addr[j++] = pq[0];
+                       unmap->to_cnt++;
+               }
+               if (!Q(blocks, disks)) {
+                       pq[1] = 0;
                        dma_flags |= DMA_PREP_PQ_DISABLE_Q;
-               else
+               } else {
                        pq[1] = dma_map_page(dev, Q(blocks, disks),
                                             offset, len,
                                             DMA_TO_DEVICE);
+                       unmap->addr[j++] = pq[1];
+                       unmap->to_cnt++;
+               }
 
                if (submit->flags & ASYNC_TX_FENCE)
                        dma_flags |= DMA_PREP_FENCE;
-               for (i = 0; i < disks-2; i++)
-                       if (likely(blocks[i])) {
-                               dma_src[src_cnt] = dma_map_page(dev, blocks[i],
-                                                               offset, len,
-                                                               DMA_TO_DEVICE);
-                               coefs[src_cnt] = raid6_gfexp[i];
-                               src_cnt++;
-                       }
-
                for (;;) {
-                       tx = device->device_prep_dma_pq_val(chan, pq, dma_src,
+                       tx = device->device_prep_dma_pq_val(chan, pq,
+                                                           unmap->addr,
                                                            src_cnt,
                                                            coefs,
                                                            len, pqres,
@@ -330,6 +352,8 @@ async_syndrome_val(struct page **blocks, unsigned int offset, int disks,
                        async_tx_quiesce(&submit->depend_tx);
                        dma_async_issue_pending(chan);
                }
+
+               dma_set_unmap(tx, unmap);
                async_tx_submit(chan, tx, submit);
 
                return tx;
index a9f08a6..934a849 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/raid/pq.h>
 #include <linux/async_tx.h>
+#include <linux/dmaengine.h>
 
 static struct dma_async_tx_descriptor *
 async_sum_product(struct page *dest, struct page **srcs, unsigned char *coef,
@@ -34,35 +35,45 @@ async_sum_product(struct page *dest, struct page **srcs, unsigned char *coef,
        struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ,
                                                      &dest, 1, srcs, 2, len);
        struct dma_device *dma = chan ? chan->device : NULL;
+       struct dmaengine_unmap_data *unmap = NULL;
        const u8 *amul, *bmul;
        u8 ax, bx;
        u8 *a, *b, *c;
 
-       if (dma) {
-               dma_addr_t dma_dest[2];
-               dma_addr_t dma_src[2];
+       if (dma)
+               unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOIO);
+
+       if (unmap) {
                struct device *dev = dma->dev;
+               dma_addr_t pq[2];
                struct dma_async_tx_descriptor *tx;
                enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P;
 
                if (submit->flags & ASYNC_TX_FENCE)
                        dma_flags |= DMA_PREP_FENCE;
-               dma_dest[1] = dma_map_page(dev, dest, 0, len, DMA_BIDIRECTIONAL);
-               dma_src[0] = dma_map_page(dev, srcs[0], 0, len, DMA_TO_DEVICE);
-               dma_src[1] = dma_map_page(dev, srcs[1], 0, len, DMA_TO_DEVICE);
-               tx = dma->device_prep_dma_pq(chan, dma_dest, dma_src, 2, coef,
+               unmap->addr[0] = dma_map_page(dev, srcs[0], 0, len, DMA_TO_DEVICE);
+               unmap->addr[1] = dma_map_page(dev, srcs[1], 0, len, DMA_TO_DEVICE);
+               unmap->to_cnt = 2;
+
+               unmap->addr[2] = dma_map_page(dev, dest, 0, len, DMA_BIDIRECTIONAL);
+               unmap->bidi_cnt = 1;
+               /* engine only looks at Q, but expects it to follow P */
+               pq[1] = unmap->addr[2];
+
+               unmap->len = len;
+               tx = dma->device_prep_dma_pq(chan, pq, unmap->addr, 2, coef,
                                             len, dma_flags);
                if (tx) {
+                       dma_set_unmap(tx, unmap);
                        async_tx_submit(chan, tx, submit);
+                       dmaengine_unmap_put(unmap);
                        return tx;
                }
 
                /* could not get a descriptor, unmap and fall through to
                 * the synchronous path
                 */
-               dma_unmap_page(dev, dma_dest[1], len, DMA_BIDIRECTIONAL);
-               dma_unmap_page(dev, dma_src[0], len, DMA_TO_DEVICE);
-               dma_unmap_page(dev, dma_src[1], len, DMA_TO_DEVICE);
+               dmaengine_unmap_put(unmap);
        }
 
        /* run the operation synchronously */
@@ -89,23 +100,38 @@ async_mult(struct page *dest, struct page *src, u8 coef, size_t len,
        struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ,
                                                      &dest, 1, &src, 1, len);
        struct dma_device *dma = chan ? chan->device : NULL;
+       struct dmaengine_unmap_data *unmap = NULL;
        const u8 *qmul; /* Q multiplier table */
        u8 *d, *s;
 
-       if (dma) {
+       if (dma)
+               unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOIO);
+
+       if (unmap) {
                dma_addr_t dma_dest[2];
-               dma_addr_t dma_src[1];
                struct device *dev = dma->dev;
                struct dma_async_tx_descriptor *tx;
                enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P;
 
                if (submit->flags & ASYNC_TX_FENCE)
                        dma_flags |= DMA_PREP_FENCE;
-               dma_dest[1] = dma_map_page(dev, dest, 0, len, DMA_BIDIRECTIONAL);
-               dma_src[0] = dma_map_page(dev, src, 0, len, DMA_TO_DEVICE);
-               tx = dma->device_prep_dma_pq(chan, dma_dest, dma_src, 1, &coef,
-                                            len, dma_flags);
+               unmap->addr[0] = dma_map_page(dev, src, 0, len, DMA_TO_DEVICE);
+               unmap->to_cnt++;
+               unmap->addr[1] = dma_map_page(dev, dest, 0, len, DMA_BIDIRECTIONAL);
+               dma_dest[1] = unmap->addr[1];
+               unmap->bidi_cnt++;
+               unmap->len = len;
+
+               /* this looks funny, but the engine looks for Q at
+                * dma_dest[1] and ignores dma_dest[0] as a dest
+                * due to DMA_PREP_PQ_DISABLE_P
+                */
+               tx = dma->device_prep_dma_pq(chan, dma_dest, unmap->addr,
+                                            1, &coef, len, dma_flags);
+
                if (tx) {
+                       dma_set_unmap(tx, unmap);
+                       dmaengine_unmap_put(unmap);
                        async_tx_submit(chan, tx, submit);
                        return tx;
                }
@@ -113,8 +139,7 @@ async_mult(struct page *dest, struct page *src, u8 coef, size_t len,
                /* could not get a descriptor, unmap and fall through to
                 * the synchronous path
                 */
-               dma_unmap_page(dev, dma_dest[1], len, DMA_BIDIRECTIONAL);
-               dma_unmap_page(dev, dma_src[0], len, DMA_TO_DEVICE);
+               dmaengine_unmap_put(unmap);
        }
 
        /* no channel available, or failed to allocate a descriptor, so
index 7be3424..39ea479 100644 (file)
@@ -128,7 +128,7 @@ async_tx_channel_switch(struct dma_async_tx_descriptor *depend_tx,
                }
                device->device_issue_pending(chan);
        } else {
-               if (dma_wait_for_async_tx(depend_tx) != DMA_SUCCESS)
+               if (dma_wait_for_async_tx(depend_tx) != DMA_COMPLETE)
                        panic("%s: DMA error waiting for depend_tx\n",
                              __func__);
                tx->tx_submit(tx);
@@ -280,7 +280,7 @@ void async_tx_quiesce(struct dma_async_tx_descriptor **tx)
                 * we are referring to the correct operation
                 */
                BUG_ON(async_tx_test_ack(*tx));
-               if (dma_wait_for_async_tx(*tx) != DMA_SUCCESS)
+               if (dma_wait_for_async_tx(*tx) != DMA_COMPLETE)
                        panic("%s: DMA error waiting for transaction\n",
                              __func__);
                async_tx_ack(*tx);
index 8ade0a0..3c562f5 100644 (file)
 
 /* do_async_xor - dma map the pages and perform the xor with an engine */
 static __async_inline struct dma_async_tx_descriptor *
-do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,
-            unsigned int offset, int src_cnt, size_t len, dma_addr_t *dma_src,
+do_async_xor(struct dma_chan *chan, struct dmaengine_unmap_data *unmap,
             struct async_submit_ctl *submit)
 {
        struct dma_device *dma = chan->device;
        struct dma_async_tx_descriptor *tx = NULL;
-       int src_off = 0;
-       int i;
        dma_async_tx_callback cb_fn_orig = submit->cb_fn;
        void *cb_param_orig = submit->cb_param;
        enum async_tx_flags flags_orig = submit->flags;
-       enum dma_ctrl_flags dma_flags;
-       int xor_src_cnt = 0;
-       dma_addr_t dma_dest;
-
-       /* map the dest bidrectional in case it is re-used as a source */
-       dma_dest = dma_map_page(dma->dev, dest, offset, len, DMA_BIDIRECTIONAL);
-       for (i = 0; i < src_cnt; i++) {
-               /* only map the dest once */
-               if (!src_list[i])
-                       continue;
-               if (unlikely(src_list[i] == dest)) {
-                       dma_src[xor_src_cnt++] = dma_dest;
-                       continue;
-               }
-               dma_src[xor_src_cnt++] = dma_map_page(dma->dev, src_list[i], offset,
-                                                     len, DMA_TO_DEVICE);
-       }
-       src_cnt = xor_src_cnt;
+       enum dma_ctrl_flags dma_flags = 0;
+       int src_cnt = unmap->to_cnt;
+       int xor_src_cnt;
+       dma_addr_t dma_dest = unmap->addr[unmap->to_cnt];
+       dma_addr_t *src_list = unmap->addr;
 
        while (src_cnt) {
+               dma_addr_t tmp;
+
                submit->flags = flags_orig;
-               dma_flags = 0;
                xor_src_cnt = min(src_cnt, (int)dma->max_xor);
-               /* if we are submitting additional xors, leave the chain open,
-                * clear the callback parameters, and leave the destination
-                * buffer mapped
+               /* if we are submitting additional xors, leave the chain open
+                * and clear the callback parameters
                 */
                if (src_cnt > xor_src_cnt) {
                        submit->flags &= ~ASYNC_TX_ACK;
                        submit->flags |= ASYNC_TX_FENCE;
-                       dma_flags = DMA_COMPL_SKIP_DEST_UNMAP;
                        submit->cb_fn = NULL;
                        submit->cb_param = NULL;
                } else {
@@ -85,12 +68,18 @@ do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,
                        dma_flags |= DMA_PREP_INTERRUPT;
                if (submit->flags & ASYNC_TX_FENCE)
                        dma_flags |= DMA_PREP_FENCE;
-               /* Since we have clobbered the src_list we are committed
-                * to doing this asynchronously.  Drivers force forward progress
-                * in case they can not provide a descriptor
+
+               /* Drivers force forward progress in case they can not provide a
+                * descriptor
                 */
-               tx = dma->device_prep_dma_xor(chan, dma_dest, &dma_src[src_off],
-                                             xor_src_cnt, len, dma_flags);
+               tmp = src_list[0];
+               if (src_list > unmap->addr)
+                       src_list[0] = dma_dest;
+               tx = dma->device_prep_dma_xor(chan, dma_dest, src_list,
+                                             xor_src_cnt, unmap->len,
+                                             dma_flags);
+               src_list[0] = tmp;
+
 
                if (unlikely(!tx))
                        async_tx_quiesce(&submit->depend_tx);
@@ -99,22 +88,21 @@ do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,
                while (unlikely(!tx)) {
                        dma_async_issue_pending(chan);
                        tx = dma->device_prep_dma_xor(chan, dma_dest,
-                                                     &dma_src[src_off],
-                                                     xor_src_cnt, len,
+                                                     src_list,
+                                                     xor_src_cnt, unmap->len,
                                                      dma_flags);
                }
 
+               dma_set_unmap(tx, unmap);
                async_tx_submit(chan, tx, submit);
                submit->depend_tx = tx;
 
                if (src_cnt > xor_src_cnt) {
                        /* drop completed sources */
                        src_cnt -= xor_src_cnt;
-                       src_off += xor_src_cnt;
-
                        /* use the intermediate result a source */
-                       dma_src[--src_off] = dma_dest;
                        src_cnt++;
+                       src_list += xor_src_cnt - 1;
                } else
                        break;
        }
@@ -189,22 +177,40 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset,
        struct dma_chan *chan = async_tx_find_channel(submit, DMA_XOR,
                                                      &dest, 1, src_list,
                                                      src_cnt, len);
-       dma_addr_t *dma_src = NULL;
+       struct dma_device *device = chan ? chan->device : NULL;
+       struct dmaengine_unmap_data *unmap = NULL;
 
        BUG_ON(src_cnt <= 1);
 
-       if (submit->scribble)
-               dma_src = submit->scribble;
-       else if (sizeof(dma_addr_t) <= sizeof(struct page *))
-               dma_src = (dma_addr_t *) src_list;
+       if (device)
+               unmap = dmaengine_get_unmap_data(device->dev, src_cnt+1, GFP_NOIO);
+
+       if (unmap && is_dma_xor_aligned(device, offset, 0, len)) {
+               struct dma_async_tx_descriptor *tx;
+               int i, j;
 
-       if (dma_src && chan && is_dma_xor_aligned(chan->device, offset, 0, len)) {
                /* run the xor asynchronously */
                pr_debug("%s (async): len: %zu\n", __func__, len);
 
-               return do_async_xor(chan, dest, src_list, offset, src_cnt, len,
-                                   dma_src, submit);
+               unmap->len = len;
+               for (i = 0, j = 0; i < src_cnt; i++) {
+                       if (!src_list[i])
+                               continue;
+                       unmap->to_cnt++;
+                       unmap->addr[j++] = dma_map_page(device->dev, src_list[i],
+                                                       offset, len, DMA_TO_DEVICE);
+               }
+
+               /* map it bidirectional as it may be re-used as a source */
+               unmap->addr[j] = dma_map_page(device->dev, dest, offset, len,
+                                             DMA_BIDIRECTIONAL);
+               unmap->bidi_cnt = 1;
+
+               tx = do_async_xor(chan, unmap, submit);
+               dmaengine_unmap_put(unmap);
+               return tx;
        } else {
+               dmaengine_unmap_put(unmap);
                /* run the xor synchronously */
                pr_debug("%s (sync): len: %zu\n", __func__, len);
                WARN_ONCE(chan, "%s: no space for dma address conversion\n",
@@ -268,16 +274,14 @@ async_xor_val(struct page *dest, struct page **src_list, unsigned int offset,
        struct dma_chan *chan = xor_val_chan(submit, dest, src_list, src_cnt, len);
        struct dma_device *device = chan ? chan->device : NULL;
        struct dma_async_tx_descriptor *tx = NULL;
-       dma_addr_t *dma_src = NULL;
+       struct dmaengine_unmap_data *unmap = NULL;
 
        BUG_ON(src_cnt <= 1);
 
-       if (submit->scribble)
-               dma_src = submit->scribble;
-       else if (sizeof(dma_addr_t) <= sizeof(struct page *))
-               dma_src = (dma_addr_t *) src_list;
+       if (device)
+               unmap = dmaengine_get_unmap_data(device->dev, src_cnt, GFP_NOIO);
 
-       if (dma_src && device && src_cnt <= device->max_xor &&
+       if (unmap && src_cnt <= device->max_xor &&
            is_dma_xor_aligned(device, offset, 0, len)) {
                unsigned long dma_prep_flags = 0;
                int i;
@@ -288,11 +292,15 @@ async_xor_val(struct page *dest, struct page **src_list, unsigned int offset,
                        dma_prep_flags |= DMA_PREP_INTERRUPT;
                if (submit->flags & ASYNC_TX_FENCE)
                        dma_prep_flags |= DMA_PREP_FENCE;
-               for (i = 0; i < src_cnt; i++)
-                       dma_src[i] = dma_map_page(device->dev, src_list[i],
-                                                 offset, len, DMA_TO_DEVICE);
 
-               tx = device->device_prep_dma_xor_val(chan, dma_src, src_cnt,
+               for (i = 0; i < src_cnt; i++) {
+                       unmap->addr[i] = dma_map_page(device->dev, src_list[i],
+                                                     offset, len, DMA_TO_DEVICE);
+                       unmap->to_cnt++;
+               }
+               unmap->len = len;
+
+               tx = device->device_prep_dma_xor_val(chan, unmap->addr, src_cnt,
                                                     len, result,
                                                     dma_prep_flags);
                if (unlikely(!tx)) {
@@ -301,11 +309,11 @@ async_xor_val(struct page *dest, struct page **src_list, unsigned int offset,
                        while (!tx) {
                                dma_async_issue_pending(chan);
                                tx = device->device_prep_dma_xor_val(chan,
-                                       dma_src, src_cnt, len, result,
+                                       unmap->addr, src_cnt, len, result,
                                        dma_prep_flags);
                        }
                }
-
+               dma_set_unmap(tx, unmap);
                async_tx_submit(chan, tx, submit);
        } else {
                enum async_tx_flags flags_orig = submit->flags;
@@ -327,6 +335,7 @@ async_xor_val(struct page *dest, struct page **src_list, unsigned int offset,
                async_tx_sync_epilog(submit);
                submit->flags = flags_orig;
        }
+       dmaengine_unmap_put(unmap);
 
        return tx;
 }
index 4a92bac..dad95f4 100644 (file)
@@ -28,7 +28,7 @@
 #undef pr
 #define pr(fmt, args...) pr_info("raid6test: " fmt, ##args)
 
-#define NDISKS 16 /* Including P and Q */
+#define NDISKS 64 /* Including P and Q */
 
 static struct page *dataptrs[NDISKS];
 static addr_conv_t addr_conv[NDISKS];
@@ -219,6 +219,14 @@ static int raid6_test(void)
                err += test(11, &tests);
                err += test(12, &tests);
        }
+
+       /* the 24 disk case is special for ioatdma as it is the boudary point
+        * at which it needs to switch from 8-source ops to 16-source
+        * ops for continuation (assumes DMA_HAS_PQ_CONTINUE is not set)
+        */
+       if (NDISKS > 24)
+               err += test(24, &tests);
+
        err += test(NDISKS, &tests);
 
        pr("\n");
diff --git a/crypto/hash_info.c b/crypto/hash_info.c
new file mode 100644 (file)
index 0000000..3e7ff46
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Hash Info: Hash algorithms information
+ *
+ * Copyright (c) 2013 Dmitry Kasatkin <d.kasatkin@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/export.h>
+#include <crypto/hash_info.h>
+
+const char *const hash_algo_name[HASH_ALGO__LAST] = {
+       [HASH_ALGO_MD4]         = "md4",
+       [HASH_ALGO_MD5]         = "md5",
+       [HASH_ALGO_SHA1]        = "sha1",
+       [HASH_ALGO_RIPE_MD_160] = "rmd160",
+       [HASH_ALGO_SHA256]      = "sha256",
+       [HASH_ALGO_SHA384]      = "sha384",
+       [HASH_ALGO_SHA512]      = "sha512",
+       [HASH_ALGO_SHA224]      = "sha224",
+       [HASH_ALGO_RIPE_MD_128] = "rmd128",
+       [HASH_ALGO_RIPE_MD_256] = "rmd256",
+       [HASH_ALGO_RIPE_MD_320] = "rmd320",
+       [HASH_ALGO_WP_256]      = "wp256",
+       [HASH_ALGO_WP_384]      = "wp384",
+       [HASH_ALGO_WP_512]      = "wp512",
+       [HASH_ALGO_TGR_128]     = "tgr128",
+       [HASH_ALGO_TGR_160]     = "tgr160",
+       [HASH_ALGO_TGR_192]     = "tgr192",
+};
+EXPORT_SYMBOL_GPL(hash_algo_name);
+
+const int hash_digest_size[HASH_ALGO__LAST] = {
+       [HASH_ALGO_MD4]         = MD5_DIGEST_SIZE,
+       [HASH_ALGO_MD5]         = MD5_DIGEST_SIZE,
+       [HASH_ALGO_SHA1]        = SHA1_DIGEST_SIZE,
+       [HASH_ALGO_RIPE_MD_160] = RMD160_DIGEST_SIZE,
+       [HASH_ALGO_SHA256]      = SHA256_DIGEST_SIZE,
+       [HASH_ALGO_SHA384]      = SHA384_DIGEST_SIZE,
+       [HASH_ALGO_SHA512]      = SHA512_DIGEST_SIZE,
+       [HASH_ALGO_SHA224]      = SHA224_DIGEST_SIZE,
+       [HASH_ALGO_RIPE_MD_128] = RMD128_DIGEST_SIZE,
+       [HASH_ALGO_RIPE_MD_256] = RMD256_DIGEST_SIZE,
+       [HASH_ALGO_RIPE_MD_320] = RMD320_DIGEST_SIZE,
+       [HASH_ALGO_WP_256]      = WP256_DIGEST_SIZE,
+       [HASH_ALGO_WP_384]      = WP384_DIGEST_SIZE,
+       [HASH_ALGO_WP_512]      = WP512_DIGEST_SIZE,
+       [HASH_ALGO_TGR_128]     = TGR128_DIGEST_SIZE,
+       [HASH_ALGO_TGR_160]     = TGR160_DIGEST_SIZE,
+       [HASH_ALGO_TGR_192]     = TGR192_DIGEST_SIZE,
+};
+EXPORT_SYMBOL_GPL(hash_digest_size);
index 25a5934..1ab8258 100644 (file)
@@ -493,7 +493,7 @@ static inline int do_one_ahash_op(struct ahash_request *req, int ret)
                ret = wait_for_completion_interruptible(&tr->completion);
                if (!ret)
                        ret = tr->err;
-               INIT_COMPLETION(tr->completion);
+               reinit_completion(&tr->completion);
        }
        return ret;
 }
@@ -721,7 +721,7 @@ static inline int do_one_acipher_op(struct ablkcipher_request *req, int ret)
                ret = wait_for_completion_interruptible(&tr->completion);
                if (!ret)
                        ret = tr->err;
-               INIT_COMPLETION(tr->completion);
+               reinit_completion(&tr->completion);
        }
 
        return ret;
index e091ef6..432afc0 100644 (file)
@@ -179,7 +179,7 @@ static int do_one_async_hash_op(struct ahash_request *req,
                ret = wait_for_completion_interruptible(&tr->completion);
                if (!ret)
                        ret = tr->err;
-               INIT_COMPLETION(tr->completion);
+               reinit_completion(&tr->completion);
        }
        return ret;
 }
@@ -336,7 +336,7 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template,
                                ret = wait_for_completion_interruptible(
                                        &tresult.completion);
                                if (!ret && !(ret = tresult.err)) {
-                                       INIT_COMPLETION(tresult.completion);
+                                       reinit_completion(&tresult.completion);
                                        break;
                                }
                                /* fall through */
@@ -543,7 +543,7 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
                                ret = wait_for_completion_interruptible(
                                        &result.completion);
                                if (!ret && !(ret = result.err)) {
-                                       INIT_COMPLETION(result.completion);
+                                       reinit_completion(&result.completion);
                                        break;
                                }
                        case -EBADMSG:
@@ -697,7 +697,7 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
                                ret = wait_for_completion_interruptible(
                                        &result.completion);
                                if (!ret && !(ret = result.err)) {
-                                       INIT_COMPLETION(result.completion);
+                                       reinit_completion(&result.completion);
                                        break;
                                }
                        case -EBADMSG:
@@ -983,7 +983,7 @@ static int __test_skcipher(struct crypto_ablkcipher *tfm, int enc,
                                ret = wait_for_completion_interruptible(
                                        &result.completion);
                                if (!ret && !((ret = result.err))) {
-                                       INIT_COMPLETION(result.completion);
+                                       reinit_completion(&result.completion);
                                        break;
                                }
                                /* fall through */
@@ -1086,7 +1086,7 @@ static int __test_skcipher(struct crypto_ablkcipher *tfm, int enc,
                                ret = wait_for_completion_interruptible(
                                        &result.completion);
                                if (!ret && !((ret = result.err))) {
-                                       INIT_COMPLETION(result.completion);
+                                       reinit_completion(&result.completion);
                                        break;
                                }
                                /* fall through */
index c95df0b..5d92485 100644 (file)
@@ -235,17 +235,6 @@ config ACPI_INITRD_TABLE_OVERRIDE
          initrd, therefore it's safe to say Y.
          See Documentation/acpi/initrd_table_override.txt for details
 
-config ACPI_BLACKLIST_YEAR
-       int "Disable ACPI for systems before Jan 1st this year" if X86_32
-       default 0
-       help
-         Enter a 4-digit year, e.g., 2001, to disable ACPI by default
-         on platforms with DMI BIOS date before January 1st that year.
-         "acpi=force" can be used to override this mechanism.
-
-         Enter 0 to disable this mechanism and allow ACPI to
-         run by default no matter what the year.  (default)
-
 config ACPI_DEBUG
        bool "Debug Statements"
        default n
index b9f0d5f..8711e37 100644 (file)
@@ -56,7 +56,6 @@ static int ac_sleep_before_get_state_ms;
 
 struct acpi_ac {
        struct power_supply charger;
-       struct acpi_device *adev;
        struct platform_device *pdev;
        unsigned long long state;
 };
@@ -70,8 +69,9 @@ struct acpi_ac {
 static int acpi_ac_get_state(struct acpi_ac *ac)
 {
        acpi_status status;
+       acpi_handle handle = ACPI_HANDLE(&ac->pdev->dev);
 
-       status = acpi_evaluate_integer(ac->adev->handle, "_PSR", NULL,
+       status = acpi_evaluate_integer(handle, "_PSR", NULL,
                                       &ac->state);
        if (ACPI_FAILURE(status)) {
                ACPI_EXCEPTION((AE_INFO, status,
@@ -119,6 +119,7 @@ static enum power_supply_property ac_props[] = {
 static void acpi_ac_notify_handler(acpi_handle handle, u32 event, void *data)
 {
        struct acpi_ac *ac = data;
+       struct acpi_device *adev;
 
        if (!ac)
                return;
@@ -141,10 +142,11 @@ static void acpi_ac_notify_handler(acpi_handle handle, u32 event, void *data)
                        msleep(ac_sleep_before_get_state_ms);
 
                acpi_ac_get_state(ac);
-               acpi_bus_generate_netlink_event(ac->adev->pnp.device_class,
+               adev = ACPI_COMPANION(&ac->pdev->dev);
+               acpi_bus_generate_netlink_event(adev->pnp.device_class,
                                                dev_name(&ac->pdev->dev),
                                                event, (u32) ac->state);
-               acpi_notifier_call_chain(ac->adev, event, (u32) ac->state);
+               acpi_notifier_call_chain(adev, event, (u32) ac->state);
                kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE);
        }
 
@@ -178,8 +180,8 @@ static int acpi_ac_probe(struct platform_device *pdev)
        if (!pdev)
                return -EINVAL;
 
-       result = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
-       if (result)
+       adev = ACPI_COMPANION(&pdev->dev);
+       if (!adev)
                return -ENODEV;
 
        ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL);
@@ -188,7 +190,6 @@ static int acpi_ac_probe(struct platform_device *pdev)
 
        strcpy(acpi_device_name(adev), ACPI_AC_DEVICE_NAME);
        strcpy(acpi_device_class(adev), ACPI_AC_CLASS);
-       ac->adev = adev;
        ac->pdev = pdev;
        platform_set_drvdata(pdev, ac);
 
index d396101..6745fe1 100644 (file)
@@ -163,6 +163,15 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
        { "80860F41", (unsigned long)&byt_i2c_dev_desc },
        { "INT33B2", },
 
+       { "INT3430", (unsigned long)&lpt_dev_desc },
+       { "INT3431", (unsigned long)&lpt_dev_desc },
+       { "INT3432", (unsigned long)&lpt_dev_desc },
+       { "INT3433", (unsigned long)&lpt_dev_desc },
+       { "INT3434", (unsigned long)&lpt_uart_dev_desc },
+       { "INT3435", (unsigned long)&lpt_uart_dev_desc },
+       { "INT3436", (unsigned long)&lpt_sdio_dev_desc },
+       { "INT3437", },
+
        { }
 };
 
index 8a4cfc7..dbfe49e 100644 (file)
@@ -111,7 +111,7 @@ int acpi_create_platform_device(struct acpi_device *adev,
        pdevinfo.id = -1;
        pdevinfo.res = resources;
        pdevinfo.num_res = count;
-       pdevinfo.acpi_node.handle = adev->handle;
+       pdevinfo.acpi_node.companion = adev;
        pdev = platform_device_register_full(&pdevinfo);
        if (IS_ERR(pdev)) {
                dev_err(&adev->dev, "platform device creation failed: %ld\n",
index 13e0450..517af70 100644 (file)
@@ -356,7 +356,7 @@ u8 acpi_ut_valid_internal_object(void *object)
        default:
 
                ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
-                                 "%p is not not an ACPI operand obj [%s]\n",
+                                 "%p is not an ACPI operand obj [%s]\n",
                                  object, acpi_ut_get_descriptor_name(object)));
                break;
        }
index fb84837..078c4f7 100644 (file)
@@ -75,39 +75,6 @@ static struct acpi_blacklist_item acpi_blacklist[] __initdata = {
        {""}
 };
 
-#if    CONFIG_ACPI_BLACKLIST_YEAR
-
-static int __init blacklist_by_year(void)
-{
-       int year;
-
-       /* Doesn't exist? Likely an old system */
-       if (!dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL)) {
-               printk(KERN_ERR PREFIX "no DMI BIOS year, "
-                       "acpi=force is required to enable ACPI\n" );
-               return 1;
-       }
-       /* 0? Likely a buggy new BIOS */
-       if (year == 0) {
-               printk(KERN_ERR PREFIX "DMI BIOS year==0, "
-                       "assuming ACPI-capable machine\n" );
-               return 0;
-       }
-       if (year < CONFIG_ACPI_BLACKLIST_YEAR) {
-               printk(KERN_ERR PREFIX "BIOS age (%d) fails cutoff (%d), "
-                      "acpi=force is required to enable ACPI\n",
-                      year, CONFIG_ACPI_BLACKLIST_YEAR);
-               return 1;
-       }
-       return 0;
-}
-#else
-static inline int blacklist_by_year(void)
-{
-       return 0;
-}
-#endif
-
 int __init acpi_blacklisted(void)
 {
        int i = 0;
@@ -166,8 +133,6 @@ int __init acpi_blacklisted(void)
                }
        }
 
-       blacklisted += blacklist_by_year();
-
        dmi_check_system(acpi_osi_dmi_table);
 
        return blacklisted;
index d42b2fb..b3480cf 100644 (file)
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
-#include <linux/device.h>
+#include <linux/acpi.h>
 #include <linux/export.h>
 #include <linux/mutex.h>
 #include <linux/pm_qos.h>
 #include <linux/pm_runtime.h>
 
-#include <acpi/acpi.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
-
 #include "internal.h"
 
 #define _COMPONENT     ACPI_POWER_COMPONENT
@@ -548,7 +544,7 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev,
  */
 int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
 {
-       acpi_handle handle = DEVICE_ACPI_HANDLE(dev);
+       acpi_handle handle = ACPI_HANDLE(dev);
        struct acpi_device *adev;
        int ret, d_min, d_max;
 
@@ -656,7 +652,7 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable)
        if (!device_run_wake(phys_dev))
                return -EINVAL;
 
-       handle = DEVICE_ACPI_HANDLE(phys_dev);
+       handle = ACPI_HANDLE(phys_dev);
        if (!handle || acpi_bus_get_device(handle, &adev)) {
                dev_dbg(phys_dev, "ACPI handle without context in %s!\n",
                        __func__);
@@ -700,7 +696,7 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
        if (!device_can_wakeup(dev))
                return -EINVAL;
 
-       handle = DEVICE_ACPI_HANDLE(dev);
+       handle = ACPI_HANDLE(dev);
        if (!handle || acpi_bus_get_device(handle, &adev)) {
                dev_dbg(dev, "ACPI handle without context in %s!\n", __func__);
                return -ENODEV;
@@ -722,7 +718,7 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
  */
 struct acpi_device *acpi_dev_pm_get_node(struct device *dev)
 {
-       acpi_handle handle = DEVICE_ACPI_HANDLE(dev);
+       acpi_handle handle = ACPI_HANDLE(dev);
        struct acpi_device *adev;
 
        return handle && !acpi_bus_get_device(handle, &adev) ? adev : NULL;
index d5309fd..ba5b56d 100644 (file)
@@ -173,9 +173,10 @@ static void start_transaction(struct acpi_ec *ec)
 static void advance_transaction(struct acpi_ec *ec, u8 status)
 {
        unsigned long flags;
-       struct transaction *t = ec->curr;
+       struct transaction *t;
 
        spin_lock_irqsave(&ec->lock, flags);
+       t = ec->curr;
        if (!t)
                goto unlock;
        if (t->wlen > t->wi) {
index fdef416..cae3b38 100644 (file)
@@ -78,15 +78,17 @@ enum {
 #define ACPI_GENL_VERSION              0x01
 #define ACPI_GENL_MCAST_GROUP_NAME     "acpi_mc_group"
 
+static const struct genl_multicast_group acpi_event_mcgrps[] = {
+       { .name = ACPI_GENL_MCAST_GROUP_NAME, },
+};
+
 static struct genl_family acpi_event_genl_family = {
        .id = GENL_ID_GENERATE,
        .name = ACPI_GENL_FAMILY_NAME,
        .version = ACPI_GENL_VERSION,
        .maxattr = ACPI_GENL_ATTR_MAX,
-};
-
-static struct genl_multicast_group acpi_event_mcgrp = {
-       .name = ACPI_GENL_MCAST_GROUP_NAME,
+       .mcgrps = acpi_event_mcgrps,
+       .n_mcgrps = ARRAY_SIZE(acpi_event_mcgrps),
 };
 
 int acpi_bus_generate_netlink_event(const char *device_class,
@@ -141,7 +143,7 @@ int acpi_bus_generate_netlink_event(const char *device_class,
                return result;
        }
 
-       genlmsg_multicast(skb, 0, acpi_event_mcgrp.id, GFP_ATOMIC);
+       genlmsg_multicast(&acpi_event_genl_family, skb, 0, 0, GFP_ATOMIC);
        return 0;
 }
 
@@ -149,18 +151,7 @@ EXPORT_SYMBOL(acpi_bus_generate_netlink_event);
 
 static int acpi_event_genetlink_init(void)
 {
-       int result;
-
-       result = genl_register_family(&acpi_event_genl_family);
-       if (result)
-               return result;
-
-       result = genl_register_mc_group(&acpi_event_genl_family,
-                                       &acpi_event_mcgrp);
-       if (result)
-               genl_unregister_family(&acpi_event_genl_family);
-
-       return result;
+       return genl_register_family(&acpi_event_genl_family);
 }
 
 #else
index 10f0f40..a22a295 100644 (file)
@@ -197,30 +197,28 @@ static void acpi_physnode_link_name(char *buf, unsigned int node_id)
 
 int acpi_bind_one(struct device *dev, acpi_handle handle)
 {
-       struct acpi_device *acpi_dev;
-       acpi_status status;
+       struct acpi_device *acpi_dev = NULL;
        struct acpi_device_physical_node *physical_node, *pn;
        char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
        struct list_head *physnode_list;
        unsigned int node_id;
        int retval = -EINVAL;
 
-       if (ACPI_HANDLE(dev)) {
+       if (ACPI_COMPANION(dev)) {
                if (handle) {
-                       dev_warn(dev, "ACPI handle is already set\n");
+                       dev_warn(dev, "ACPI companion already set\n");
                        return -EINVAL;
                } else {
-                       handle = ACPI_HANDLE(dev);
+                       acpi_dev = ACPI_COMPANION(dev);
                }
+       } else {
+               acpi_bus_get_device(handle, &acpi_dev);
        }
-       if (!handle)
+       if (!acpi_dev)
                return -EINVAL;
 
+       get_device(&acpi_dev->dev);
        get_device(dev);
-       status = acpi_bus_get_device(handle, &acpi_dev);
-       if (ACPI_FAILURE(status))
-               goto err;
-
        physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);
        if (!physical_node) {
                retval = -ENOMEM;
@@ -242,10 +240,11 @@ int acpi_bind_one(struct device *dev, acpi_handle handle)
 
                        dev_warn(dev, "Already associated with ACPI node\n");
                        kfree(physical_node);
-                       if (ACPI_HANDLE(dev) != handle)
+                       if (ACPI_COMPANION(dev) != acpi_dev)
                                goto err;
 
                        put_device(dev);
+                       put_device(&acpi_dev->dev);
                        return 0;
                }
                if (pn->node_id == node_id) {
@@ -259,8 +258,8 @@ int acpi_bind_one(struct device *dev, acpi_handle handle)
        list_add(&physical_node->node, physnode_list);
        acpi_dev->physical_node_count++;
 
-       if (!ACPI_HANDLE(dev))
-               ACPI_HANDLE_SET(dev, acpi_dev->handle);
+       if (!ACPI_COMPANION(dev))
+               ACPI_COMPANION_SET(dev, acpi_dev);
 
        acpi_physnode_link_name(physical_node_name, node_id);
        retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
@@ -283,27 +282,21 @@ int acpi_bind_one(struct device *dev, acpi_handle handle)
        return 0;
 
  err:
-       ACPI_HANDLE_SET(dev, NULL);
+       ACPI_COMPANION_SET(dev, NULL);
        put_device(dev);
+       put_device(&acpi_dev->dev);
        return retval;
 }
 EXPORT_SYMBOL_GPL(acpi_bind_one);
 
 int acpi_unbind_one(struct device *dev)
 {
+       struct acpi_device *acpi_dev = ACPI_COMPANION(dev);
        struct acpi_device_physical_node *entry;
-       struct acpi_device *acpi_dev;
-       acpi_status status;
 
-       if (!ACPI_HANDLE(dev))
+       if (!acpi_dev)
                return 0;
 
-       status = acpi_bus_get_device(ACPI_HANDLE(dev), &acpi_dev);
-       if (ACPI_FAILURE(status)) {
-               dev_err(dev, "Oops, ACPI handle corrupt in %s()\n", __func__);
-               return -EINVAL;
-       }
-
        mutex_lock(&acpi_dev->physical_node_lock);
 
        list_for_each_entry(entry, &acpi_dev->physical_node_list, node)
@@ -316,9 +309,10 @@ int acpi_unbind_one(struct device *dev)
                        acpi_physnode_link_name(physnode_name, entry->node_id);
                        sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name);
                        sysfs_remove_link(&dev->kobj, "firmware_node");
-                       ACPI_HANDLE_SET(dev, NULL);
-                       /* acpi_bind_one() increase refcnt by one. */
+                       ACPI_COMPANION_SET(dev, NULL);
+                       /* Drop references taken by acpi_bind_one(). */
                        put_device(dev);
+                       put_device(&acpi_dev->dev);
                        kfree(entry);
                        break;
                }
@@ -328,6 +322,15 @@ int acpi_unbind_one(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(acpi_unbind_one);
 
+void acpi_preset_companion(struct device *dev, acpi_handle parent, u64 addr)
+{
+       struct acpi_device *adev;
+
+       if (!acpi_bus_get_device(acpi_get_child(parent, addr), &adev))
+               ACPI_COMPANION_SET(dev, adev);
+}
+EXPORT_SYMBOL_GPL(acpi_preset_companion);
+
 static int acpi_platform_notify(struct device *dev)
 {
        struct acpi_bus_type *type = acpi_get_bus_type(dev);
index 56f0586..0703bff 100644 (file)
@@ -575,6 +575,7 @@ static int acpi_pci_root_add(struct acpi_device *device,
                dev_err(&device->dev,
                        "Bus %04x:%02x not present in PCI namespace\n",
                        root->segment, (unsigned int)root->secondary.start);
+               device->driver_data = NULL;
                result = -ENODEV;
                goto end;
        }
index 55f9ded..15daa21 100644 (file)
@@ -289,24 +289,17 @@ void acpi_bus_device_eject(void *data, u32 ost_src)
 {
        struct acpi_device *device = data;
        acpi_handle handle = device->handle;
-       struct acpi_scan_handler *handler;
        u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
        int error;
 
        lock_device_hotplug();
        mutex_lock(&acpi_scan_lock);
 
-       handler = device->handler;
-       if (!handler || !handler->hotplug.enabled) {
-               put_device(&device->dev);
-               goto err_support;
-       }
-
        if (ost_src == ACPI_NOTIFY_EJECT_REQUEST)
                acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
                                          ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
 
-       if (handler->hotplug.mode == AHM_CONTAINER)
+       if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER)
                kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
 
        error = acpi_scan_hot_remove(device);
@@ -411,8 +404,7 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
                break;
        case ACPI_NOTIFY_EJECT_REQUEST:
                acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
-               status = acpi_bus_get_device(handle, &adev);
-               if (ACPI_FAILURE(status))
+               if (acpi_bus_get_device(handle, &adev))
                        goto err_out;
 
                get_device(&adev->dev);
@@ -1997,6 +1989,7 @@ static int acpi_bus_scan_fixed(void)
                if (result)
                        return result;
 
+               device->flags.match_driver = true;
                result = device_attach(&device->dev);
                if (result < 0)
                        return result;
@@ -2013,6 +2006,7 @@ static int acpi_bus_scan_fixed(void)
                if (result)
                        return result;
 
+               device->flags.match_driver = true;
                result = device_attach(&device->dev);
        }
 
index 18dbdff..995e91b 100644 (file)
@@ -82,13 +82,6 @@ static bool allow_duplicates;
 module_param(allow_duplicates, bool, 0644);
 
 /*
- * Some BIOSes claim they use minimum backlight at boot,
- * and this may bring dimming screen after boot
- */
-static bool use_bios_initial_backlight = 1;
-module_param(use_bios_initial_backlight, bool, 0644);
-
-/*
  * For Windows 8 systems: if set ture and the GPU driver has
  * registered a backlight interface, skip registering ACPI video's.
  */
@@ -406,12 +399,6 @@ static int __init video_set_bqc_offset(const struct dmi_system_id *d)
        return 0;
 }
 
-static int video_ignore_initial_backlight(const struct dmi_system_id *d)
-{
-       use_bios_initial_backlight = 0;
-       return 0;
-}
-
 static struct dmi_system_id video_dmi_table[] __initdata = {
        /*
         * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121
@@ -456,54 +443,6 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
                },
        },
-       {
-        .callback = video_ignore_initial_backlight,
-        .ident = "HP Folio 13-2000",
-        .matches = {
-               DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP Folio 13 - 2000 Notebook PC"),
-               },
-       },
-       {
-        .callback = video_ignore_initial_backlight,
-        .ident = "Fujitsu E753",
-        .matches = {
-               DMI_MATCH(DMI_BOARD_VENDOR, "FUJITSU"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E753"),
-               },
-       },
-       {
-        .callback = video_ignore_initial_backlight,
-        .ident = "HP Pavilion dm4",
-        .matches = {
-               DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dm4 Notebook PC"),
-               },
-       },
-       {
-        .callback = video_ignore_initial_backlight,
-        .ident = "HP Pavilion g6 Notebook PC",
-        .matches = {
-                DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
-                DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion g6 Notebook PC"),
-               },
-       },
-       {
-        .callback = video_ignore_initial_backlight,
-        .ident = "HP 1000 Notebook PC",
-        .matches = {
-               DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP 1000 Notebook PC"),
-               },
-       },
-       {
-        .callback = video_ignore_initial_backlight,
-        .ident = "HP Pavilion m4",
-        .matches = {
-               DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion m4 Notebook PC"),
-               },
-       },
        {}
 };
 
@@ -839,20 +778,18 @@ acpi_video_init_brightness(struct acpi_video_device *device)
        if (!device->cap._BQC)
                goto set_level;
 
-       if (use_bios_initial_backlight) {
-               level = acpi_video_bqc_value_to_level(device, level_old);
-               /*
-                * On some buggy laptops, _BQC returns an uninitialized
-                * value when invoked for the first time, i.e.
-                * level_old is invalid (no matter whether it's a level
-                * or an index). Set the backlight to max_level in this case.
-                */
-               for (i = 2; i < br->count; i++)
-                       if (level == br->levels[i])
-                               break;
-               if (i == br->count || !level)
-                       level = max_level;
-       }
+       level = acpi_video_bqc_value_to_level(device, level_old);
+       /*
+        * On some buggy laptops, _BQC returns an uninitialized
+        * value when invoked for the first time, i.e.
+        * level_old is invalid (no matter whether it's a level
+        * or an index). Set the backlight to max_level in this case.
+        */
+       for (i = 2; i < br->count; i++)
+               if (level == br->levels[i])
+                       break;
+       if (i == br->count || !level)
+               level = max_level;
 
 set_level:
        result = acpi_video_device_lcd_set_level(device, level);
index ab714d2..4372cfa 100644 (file)
@@ -185,7 +185,7 @@ void ata_acpi_bind_port(struct ata_port *ap)
        if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_handle)
                return;
 
-       ACPI_HANDLE_SET(&ap->tdev, acpi_get_child(host_handle, ap->port_no));
+       acpi_preset_companion(&ap->tdev, host_handle, ap->port_no);
 
        if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0)
                ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
@@ -222,7 +222,7 @@ void ata_acpi_bind_dev(struct ata_device *dev)
                parent_handle = port_handle;
        }
 
-       ACPI_HANDLE_SET(&dev->tdev, acpi_get_child(parent_handle, adr));
+       acpi_preset_companion(&dev->tdev, parent_handle, adr);
 
        register_hotplug_dock_device(ata_dev_acpi_handle(dev),
                                     &ata_acpi_dev_dock_ops, dev, NULL, NULL);
index 77bbc82..92d7797 100644 (file)
@@ -3017,7 +3017,7 @@ static inline void ata_eh_pull_park_action(struct ata_port *ap)
         * ourselves at the beginning of each pass over the loop.
         *
         * Additionally, all write accesses to &ap->park_req_pending
-        * through INIT_COMPLETION() (see below) or complete_all()
+        * through reinit_completion() (see below) or complete_all()
         * (see ata_scsi_park_store()) are protected by the host lock.
         * As a result we have that park_req_pending.done is zero on
         * exit from this function, i.e. when ATA_EH_PARK actions for
@@ -3031,7 +3031,7 @@ static inline void ata_eh_pull_park_action(struct ata_port *ap)
         */
 
        spin_lock_irqsave(ap->lock, flags);
-       INIT_COMPLETION(ap->park_req_pending);
+       reinit_completion(&ap->park_req_pending);
        ata_for_each_link(link, ap, EDGE) {
                ata_for_each_dev(dev, link, ALL) {
                        struct ata_eh_info *ehi = &link->eh_info;
index 853f610..e88690e 100644 (file)
@@ -396,8 +396,7 @@ dma_xfer(struct arasan_cf_dev *acdev, dma_addr_t src, dma_addr_t dest, u32 len)
        struct dma_async_tx_descriptor *tx;
        struct dma_chan *chan = acdev->dma_chan;
        dma_cookie_t cookie;
-       unsigned long flags = DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP |
-               DMA_COMPL_SKIP_DEST_UNMAP;
+       unsigned long flags = DMA_PREP_INTERRUPT;
        int ret = 0;
 
        tx = chan->device->device_prep_dma_memcpy(chan, dest, src, len, flags);
index 272f009..1bdf104 100644 (file)
@@ -3511,7 +3511,7 @@ static int init_card(struct atm_dev *dev)
        tmp = dev_get_by_name(&init_net, tname);        /* jhs: was "tmp = dev_get(tname);" */
        if (tmp) {
                memcpy(card->atmdev->esi, tmp->dev_addr, 6);
-
+               dev_put(tmp);
                printk("%s: ESI %pM\n", card->name, card->atmdev->esi);
        }
        /*
index 99802d6..165c2c2 100644 (file)
@@ -49,7 +49,7 @@ struct cma *dma_contiguous_default_area;
 
 /*
  * Default global CMA area size can be defined in kernel's .config.
- * This is usefull mainly for distro maintainers to create a kernel
+ * This is useful mainly for distro maintainers to create a kernel
  * that works correctly for most supported systems.
  * The size can be set in bytes or as a percentage of the total memory
  * in the system.
index 47051cd..3a94b79 100644 (file)
@@ -432,7 +432,7 @@ struct platform_device *platform_device_register_full(
                goto err_alloc;
 
        pdev->dev.parent = pdevinfo->parent;
-       ACPI_HANDLE_SET(&pdev->dev, pdevinfo->acpi_node.handle);
+       ACPI_COMPANION_SET(&pdev->dev, pdevinfo->acpi_node.companion);
 
        if (pdevinfo->dma_mask) {
                /*
@@ -463,7 +463,7 @@ struct platform_device *platform_device_register_full(
        ret = platform_device_add(pdev);
        if (ret) {
 err:
-               ACPI_HANDLE_SET(&pdev->dev, NULL);
+               ACPI_COMPANION_SET(&pdev->dev, NULL);
                kfree(pdev->dev.dma_mask);
 
 err_alloc:
index ee039af..1b41fca 100644 (file)
@@ -757,7 +757,7 @@ void dpm_resume(pm_message_t state)
        async_error = 0;
 
        list_for_each_entry(dev, &dpm_suspended_list, power.entry) {
-               INIT_COMPLETION(dev->power.completion);
+               reinit_completion(&dev->power.completion);
                if (is_async(dev)) {
                        get_device(dev);
                        async_schedule(async_resume, dev);
@@ -1237,7 +1237,7 @@ static void async_suspend(void *data, async_cookie_t cookie)
 
 static int device_suspend(struct device *dev)
 {
-       INIT_COMPLETION(dev->power.completion);
+       reinit_completion(&dev->power.completion);
 
        if (pm_async_enabled && dev->power.async_suspend) {
                get_device(dev);
@@ -1350,6 +1350,9 @@ static int device_prepare(struct device *dev, pm_message_t state)
 
        device_unlock(dev);
 
+       if (error)
+               pm_runtime_put(dev);
+
        return error;
 }
 
index 4ff85b8..748dea4 100644 (file)
@@ -343,7 +343,7 @@ static int fd_motor_on(int nr)
                unit[nr].motor = 1;
                fd_select(nr);
 
-               INIT_COMPLETION(motor_on_completion);
+               reinit_completion(&motor_on_completion);
                motor_on_timer.data = nr;
                mod_timer(&motor_on_timer, jiffies + HZ/2);
 
index 0c004ac..b35fc4f 100644 (file)
@@ -2808,7 +2808,7 @@ resend_cmd2:
                /* erase the old error information */
                memset(c->err_info, 0, sizeof(ErrorInfo_struct));
                return_status = IO_OK;
-               INIT_COMPLETION(wait);
+               reinit_completion(&wait);
                goto resend_cmd2;
        }
 
@@ -3669,7 +3669,7 @@ static int add_to_scan_list(struct ctlr_info *h)
                }
        }
        if (!found && !h->busy_scanning) {
-               INIT_COMPLETION(h->scan_wait);
+               reinit_completion(&h->scan_wait);
                list_add_tail(&h->scan_list, &scan_q);
                ret = 1;
        }
index b5d8423..ea192ec 100644 (file)
@@ -223,7 +223,7 @@ static void null_softirq_done_fn(struct request *rq)
        blk_end_request_all(rq, 0);
 }
 
-#if defined(CONFIG_SMP) && defined(CONFIG_USE_GENERIC_SMP_HELPERS)
+#ifdef CONFIG_SMP
 
 static void null_ipi_cmd_end_io(void *data)
 {
@@ -260,7 +260,7 @@ static void null_cmd_end_ipi(struct nullb_cmd *cmd)
        put_cpu();
 }
 
-#endif /* CONFIG_SMP && CONFIG_USE_GENERIC_SMP_HELPERS */
+#endif /* CONFIG_SMP */
 
 static inline void null_handle_cmd(struct nullb_cmd *cmd)
 {
@@ -270,7 +270,7 @@ static inline void null_handle_cmd(struct nullb_cmd *cmd)
                end_cmd(cmd);
                break;
        case NULL_IRQ_SOFTIRQ:
-#if defined(CONFIG_SMP) && defined(CONFIG_USE_GENERIC_SMP_HELPERS)
+#ifdef CONFIG_SMP
                null_cmd_end_ipi(cmd);
 #else
                end_cmd(cmd);
@@ -571,7 +571,7 @@ static int __init null_init(void)
 {
        unsigned int i;
 
-#if !defined(CONFIG_SMP) || !defined(CONFIG_USE_GENERIC_SMP_HELPERS)
+#if !defined(CONFIG_SMP)
        if (irqmode == NULL_IRQ_SOFTIRQ) {
                pr_warn("null_blk: softirq completions not available.\n");
                pr_warn("null_blk: using direct completions.\n");
index 5cdf88b..6a680d4 100644 (file)
 #include <linux/string_helpers.h>
 #include <scsi/scsi_cmnd.h>
 #include <linux/idr.h>
+#include <linux/blk-mq.h>
+#include <linux/numa.h>
 
 #define PART_BITS 4
 
-static bool use_bio;
-module_param(use_bio, bool, S_IRUGO);
-
 static int major;
 static DEFINE_IDA(vd_index_ida);
 
@@ -26,13 +25,11 @@ struct virtio_blk
 {
        struct virtio_device *vdev;
        struct virtqueue *vq;
-       wait_queue_head_t queue_wait;
+       spinlock_t vq_lock;
 
        /* The disk structure for the kernel. */
        struct gendisk *disk;
 
-       mempool_t *pool;
-
        /* Process context for config space updates */
        struct work_struct config_work;
 
@@ -47,31 +44,17 @@ struct virtio_blk
 
        /* Ida index - used to track minor number allocations. */
        int index;
-
-       /* Scatterlist: can be too big for stack. */
-       struct scatterlist sg[/*sg_elems*/];
 };
 
 struct virtblk_req
 {
        struct request *req;
-       struct bio *bio;
        struct virtio_blk_outhdr out_hdr;
        struct virtio_scsi_inhdr in_hdr;
-       struct work_struct work;
-       struct virtio_blk *vblk;
-       int flags;
        u8 status;
        struct scatterlist sg[];
 };
 
-enum {
-       VBLK_IS_FLUSH           = 1,
-       VBLK_REQ_FLUSH          = 2,
-       VBLK_REQ_DATA           = 4,
-       VBLK_REQ_FUA            = 8,
-};
-
 static inline int virtblk_result(struct virtblk_req *vbr)
 {
        switch (vbr->status) {
@@ -84,22 +67,6 @@ static inline int virtblk_result(struct virtblk_req *vbr)
        }
 }
 
-static inline struct virtblk_req *virtblk_alloc_req(struct virtio_blk *vblk,
-                                                   gfp_t gfp_mask)
-{
-       struct virtblk_req *vbr;
-
-       vbr = mempool_alloc(vblk->pool, gfp_mask);
-       if (!vbr)
-               return NULL;
-
-       vbr->vblk = vblk;
-       if (use_bio)
-               sg_init_table(vbr->sg, vblk->sg_elems);
-
-       return vbr;
-}
-
 static int __virtblk_add_req(struct virtqueue *vq,
                             struct virtblk_req *vbr,
                             struct scatterlist *data_sg,
@@ -143,83 +110,8 @@ static int __virtblk_add_req(struct virtqueue *vq,
        return virtqueue_add_sgs(vq, sgs, num_out, num_in, vbr, GFP_ATOMIC);
 }
 
-static void virtblk_add_req(struct virtblk_req *vbr, bool have_data)
-{
-       struct virtio_blk *vblk = vbr->vblk;
-       DEFINE_WAIT(wait);
-       int ret;
-
-       spin_lock_irq(vblk->disk->queue->queue_lock);
-       while (unlikely((ret = __virtblk_add_req(vblk->vq, vbr, vbr->sg,
-                                                have_data)) < 0)) {
-               prepare_to_wait_exclusive(&vblk->queue_wait, &wait,
-                                         TASK_UNINTERRUPTIBLE);
-
-               spin_unlock_irq(vblk->disk->queue->queue_lock);
-               io_schedule();
-               spin_lock_irq(vblk->disk->queue->queue_lock);
-
-               finish_wait(&vblk->queue_wait, &wait);
-       }
-
-       virtqueue_kick(vblk->vq);
-       spin_unlock_irq(vblk->disk->queue->queue_lock);
-}
-
-static void virtblk_bio_send_flush(struct virtblk_req *vbr)
-{
-       vbr->flags |= VBLK_IS_FLUSH;
-       vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
-       vbr->out_hdr.sector = 0;
-       vbr->out_hdr.ioprio = 0;
-
-       virtblk_add_req(vbr, false);
-}
-
-static void virtblk_bio_send_data(struct virtblk_req *vbr)
-{
-       struct virtio_blk *vblk = vbr->vblk;
-       struct bio *bio = vbr->bio;
-       bool have_data;
-
-       vbr->flags &= ~VBLK_IS_FLUSH;
-       vbr->out_hdr.type = 0;
-       vbr->out_hdr.sector = bio->bi_sector;
-       vbr->out_hdr.ioprio = bio_prio(bio);
-
-       if (blk_bio_map_sg(vblk->disk->queue, bio, vbr->sg)) {
-               have_data = true;
-               if (bio->bi_rw & REQ_WRITE)
-                       vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
-               else
-                       vbr->out_hdr.type |= VIRTIO_BLK_T_IN;
-       } else
-               have_data = false;
-
-       virtblk_add_req(vbr, have_data);
-}
-
-static void virtblk_bio_send_data_work(struct work_struct *work)
-{
-       struct virtblk_req *vbr;
-
-       vbr = container_of(work, struct virtblk_req, work);
-
-       virtblk_bio_send_data(vbr);
-}
-
-static void virtblk_bio_send_flush_work(struct work_struct *work)
-{
-       struct virtblk_req *vbr;
-
-       vbr = container_of(work, struct virtblk_req, work);
-
-       virtblk_bio_send_flush(vbr);
-}
-
 static inline void virtblk_request_done(struct virtblk_req *vbr)
 {
-       struct virtio_blk *vblk = vbr->vblk;
        struct request *req = vbr->req;
        int error = virtblk_result(vbr);
 
@@ -231,90 +123,45 @@ static inline void virtblk_request_done(struct virtblk_req *vbr)
                req->errors = (error != 0);
        }
 
-       __blk_end_request_all(req, error);
-       mempool_free(vbr, vblk->pool);
-}
-
-static inline void virtblk_bio_flush_done(struct virtblk_req *vbr)
-{
-       struct virtio_blk *vblk = vbr->vblk;
-
-       if (vbr->flags & VBLK_REQ_DATA) {
-               /* Send out the actual write data */
-               INIT_WORK(&vbr->work, virtblk_bio_send_data_work);
-               queue_work(virtblk_wq, &vbr->work);
-       } else {
-               bio_endio(vbr->bio, virtblk_result(vbr));
-               mempool_free(vbr, vblk->pool);
-       }
-}
-
-static inline void virtblk_bio_data_done(struct virtblk_req *vbr)
-{
-       struct virtio_blk *vblk = vbr->vblk;
-
-       if (unlikely(vbr->flags & VBLK_REQ_FUA)) {
-               /* Send out a flush before end the bio */
-               vbr->flags &= ~VBLK_REQ_DATA;
-               INIT_WORK(&vbr->work, virtblk_bio_send_flush_work);
-               queue_work(virtblk_wq, &vbr->work);
-       } else {
-               bio_endio(vbr->bio, virtblk_result(vbr));
-               mempool_free(vbr, vblk->pool);
-       }
-}
-
-static inline void virtblk_bio_done(struct virtblk_req *vbr)
-{
-       if (unlikely(vbr->flags & VBLK_IS_FLUSH))
-               virtblk_bio_flush_done(vbr);
-       else
-               virtblk_bio_data_done(vbr);
+       blk_mq_end_io(req, error);
 }
 
 static void virtblk_done(struct virtqueue *vq)
 {
        struct virtio_blk *vblk = vq->vdev->priv;
-       bool bio_done = false, req_done = false;
+       bool req_done = false;
        struct virtblk_req *vbr;
        unsigned long flags;
        unsigned int len;
 
-       spin_lock_irqsave(vblk->disk->queue->queue_lock, flags);
+       spin_lock_irqsave(&vblk->vq_lock, flags);
        do {
                virtqueue_disable_cb(vq);
                while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) {
-                       if (vbr->bio) {
-                               virtblk_bio_done(vbr);
-                               bio_done = true;
-                       } else {
-                               virtblk_request_done(vbr);
-                               req_done = true;
-                       }
+                       virtblk_request_done(vbr);
+                       req_done = true;
                }
+               if (unlikely(virtqueue_is_broken(vq)))
+                       break;
        } while (!virtqueue_enable_cb(vq));
+       spin_unlock_irqrestore(&vblk->vq_lock, flags);
+
        /* In case queue is stopped waiting for more buffers. */
        if (req_done)
-               blk_start_queue(vblk->disk->queue);
-       spin_unlock_irqrestore(vblk->disk->queue->queue_lock, flags);
-
-       if (bio_done)
-               wake_up(&vblk->queue_wait);
+               blk_mq_start_stopped_hw_queues(vblk->disk->queue);
 }
 
-static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
-                  struct request *req)
+static int virtio_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *req)
 {
+       struct virtio_blk *vblk = hctx->queue->queuedata;
+       struct virtblk_req *vbr = req->special;
+       unsigned long flags;
        unsigned int num;
-       struct virtblk_req *vbr;
+       const bool last = (req->cmd_flags & REQ_END) != 0;
 
-       vbr = virtblk_alloc_req(vblk, GFP_ATOMIC);
-       if (!vbr)
-               /* When another request finishes we'll try again. */
-               return false;
+       BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);
 
        vbr->req = req;
-       vbr->bio = NULL;
        if (req->cmd_flags & REQ_FLUSH) {
                vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
                vbr->out_hdr.sector = 0;
@@ -342,7 +189,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
                }
        }
 
-       num = blk_rq_map_sg(q, vbr->req, vblk->sg);
+       num = blk_rq_map_sg(hctx->queue, vbr->req, vbr->sg);
        if (num) {
                if (rq_data_dir(vbr->req) == WRITE)
                        vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
@@ -350,63 +197,19 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
                        vbr->out_hdr.type |= VIRTIO_BLK_T_IN;
        }
 
-       if (__virtblk_add_req(vblk->vq, vbr, vblk->sg, num) < 0) {
-               mempool_free(vbr, vblk->pool);
-               return false;
-       }
-
-       return true;
-}
-
-static void virtblk_request(struct request_queue *q)
-{
-       struct virtio_blk *vblk = q->queuedata;
-       struct request *req;
-       unsigned int issued = 0;
-
-       while ((req = blk_peek_request(q)) != NULL) {
-               BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);
-
-               /* If this request fails, stop queue and wait for something to
-                  finish to restart it. */
-               if (!do_req(q, vblk, req)) {
-                       blk_stop_queue(q);
-                       break;
-               }
-               blk_start_request(req);
-               issued++;
+       spin_lock_irqsave(&vblk->vq_lock, flags);
+       if (__virtblk_add_req(vblk->vq, vbr, vbr->sg, num) < 0) {
+               virtqueue_kick(vblk->vq);
+               spin_unlock_irqrestore(&vblk->vq_lock, flags);
+               blk_mq_stop_hw_queue(hctx);
+               return BLK_MQ_RQ_QUEUE_BUSY;
        }
 
-       if (issued)
+       if (last)
                virtqueue_kick(vblk->vq);
-}
 
-static void virtblk_make_request(struct request_queue *q, struct bio *bio)
-{
-       struct virtio_blk *vblk = q->queuedata;
-       struct virtblk_req *vbr;
-
-       BUG_ON(bio->bi_phys_segments + 2 > vblk->sg_elems);
-
-       vbr = virtblk_alloc_req(vblk, GFP_NOIO);
-       if (!vbr) {
-               bio_endio(bio, -ENOMEM);
-               return;
-       }
-
-       vbr->bio = bio;
-       vbr->flags = 0;
-       if (bio->bi_rw & REQ_FLUSH)
-               vbr->flags |= VBLK_REQ_FLUSH;
-       if (bio->bi_rw & REQ_FUA)
-               vbr->flags |= VBLK_REQ_FUA;
-       if (bio->bi_size)
-               vbr->flags |= VBLK_REQ_DATA;
-
-       if (unlikely(vbr->flags & VBLK_REQ_FLUSH))
-               virtblk_bio_send_flush(vbr);
-       else
-               virtblk_bio_send_data(vbr);
+       spin_unlock_irqrestore(&vblk->vq_lock, flags);
+       return BLK_MQ_RQ_QUEUE_OK;
 }
 
 /* return id (s/n) string for *disk to *id_str
@@ -456,18 +259,15 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
 static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
 {
        struct virtio_blk *vblk = bd->bd_disk->private_data;
-       struct virtio_blk_geometry vgeo;
-       int err;
 
        /* see if the host passed in geometry config */
-       err = virtio_config_val(vblk->vdev, VIRTIO_BLK_F_GEOMETRY,
-                               offsetof(struct virtio_blk_config, geometry),
-                               &vgeo);
-
-       if (!err) {
-               geo->heads = vgeo.heads;
-               geo->sectors = vgeo.sectors;
-               geo->cylinders = vgeo.cylinders;
+       if (virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_GEOMETRY)) {
+               virtio_cread(vblk->vdev, struct virtio_blk_config,
+                            geometry.cylinders, &geo->cylinders);
+               virtio_cread(vblk->vdev, struct virtio_blk_config,
+                            geometry.heads, &geo->heads);
+               virtio_cread(vblk->vdev, struct virtio_blk_config,
+                            geometry.sectors, &geo->sectors);
        } else {
                /* some standard values, similar to sd */
                geo->heads = 1 << 6;
@@ -529,8 +329,7 @@ static void virtblk_config_changed_work(struct work_struct *work)
                goto done;
 
        /* Host must always specify the capacity. */
-       vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity),
-                         &capacity, sizeof(capacity));
+       virtio_cread(vdev, struct virtio_blk_config, capacity, &capacity);
 
        /* If capacity is too big, truncate with warning. */
        if ((sector_t)capacity != capacity) {
@@ -608,9 +407,9 @@ 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);
+       err = virtio_cread_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE,
+                                  struct virtio_blk_config, wce,
+                                  &writeback);
        if (err)
                writeback = virtio_has_feature(vdev, VIRTIO_BLK_F_WCE);
 
@@ -642,7 +441,6 @@ virtblk_cache_type_store(struct device *dev, struct device_attribute *attr,
        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; )
@@ -652,11 +450,7 @@ virtblk_cache_type_store(struct device *dev, struct device_attribute *attr,
        if (i < 0)
                return -EINVAL;
 
-       writeback = i;
-       vdev->config->set(vdev,
-                         offsetof(struct virtio_blk_config, wce),
-                         &writeback, sizeof(writeback));
-
+       virtio_cwrite8(vdev, offsetof(struct virtio_blk_config, wce), i);
        virtblk_update_cache_mode(vdev);
        return count;
 }
@@ -680,12 +474,35 @@ 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 struct blk_mq_ops virtio_mq_ops = {
+       .queue_rq       = virtio_queue_rq,
+       .map_queue      = blk_mq_map_queue,
+       .alloc_hctx     = blk_mq_alloc_single_hw_queue,
+       .free_hctx      = blk_mq_free_single_hw_queue,
+};
+
+static struct blk_mq_reg virtio_mq_reg = {
+       .ops            = &virtio_mq_ops,
+       .nr_hw_queues   = 1,
+       .queue_depth    = 64,
+       .numa_node      = NUMA_NO_NODE,
+       .flags          = BLK_MQ_F_SHOULD_MERGE,
+};
+
+static void virtblk_init_vbr(void *data, struct blk_mq_hw_ctx *hctx,
+                            struct request *rq, unsigned int nr)
+{
+       struct virtio_blk *vblk = data;
+       struct virtblk_req *vbr = rq->special;
+
+       sg_init_table(vbr->sg, vblk->sg_elems);
+}
+
 static int virtblk_probe(struct virtio_device *vdev)
 {
        struct virtio_blk *vblk;
        struct request_queue *q;
        int err, index;
-       int pool_size;
 
        u64 cap;
        u32 v, blk_size, sg_elems, opt_io_size;
@@ -699,9 +516,9 @@ static int virtblk_probe(struct virtio_device *vdev)
        index = err;
 
        /* We need to know how many segments before we allocate. */
-       err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
-                               offsetof(struct virtio_blk_config, seg_max),
-                               &sg_elems);
+       err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SEG_MAX,
+                                  struct virtio_blk_config, seg_max,
+                                  &sg_elems);
 
        /* We need at least one SG element, whatever they say. */
        if (err || !sg_elems)
@@ -709,17 +526,14 @@ static int virtblk_probe(struct virtio_device *vdev)
 
        /* We need an extra sg elements at head and tail. */
        sg_elems += 2;
-       vdev->priv = vblk = kmalloc(sizeof(*vblk) +
-                                   sizeof(vblk->sg[0]) * sg_elems, GFP_KERNEL);
+       vdev->priv = vblk = kmalloc(sizeof(*vblk), GFP_KERNEL);
        if (!vblk) {
                err = -ENOMEM;
                goto out_free_index;
        }
 
-       init_waitqueue_head(&vblk->queue_wait);
        vblk->vdev = vdev;
        vblk->sg_elems = sg_elems;
-       sg_init_table(vblk->sg, vblk->sg_elems);
        mutex_init(&vblk->config_lock);
 
        INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
@@ -728,31 +542,27 @@ static int virtblk_probe(struct virtio_device *vdev)
        err = init_vq(vblk);
        if (err)
                goto out_free_vblk;
-
-       pool_size = sizeof(struct virtblk_req);
-       if (use_bio)
-               pool_size += sizeof(struct scatterlist) * sg_elems;
-       vblk->pool = mempool_create_kmalloc_pool(1, pool_size);
-       if (!vblk->pool) {
-               err = -ENOMEM;
-               goto out_free_vq;
-       }
+       spin_lock_init(&vblk->vq_lock);
 
        /* FIXME: How many partitions?  How long is a piece of string? */
        vblk->disk = alloc_disk(1 << PART_BITS);
        if (!vblk->disk) {
                err = -ENOMEM;
-               goto out_mempool;
+               goto out_free_vq;
        }
 
-       q = vblk->disk->queue = blk_init_queue(virtblk_request, NULL);
+       virtio_mq_reg.cmd_size =
+               sizeof(struct virtblk_req) +
+               sizeof(struct scatterlist) * sg_elems;
+
+       q = vblk->disk->queue = blk_mq_init_queue(&virtio_mq_reg, vblk);
        if (!q) {
                err = -ENOMEM;
                goto out_put_disk;
        }
 
-       if (use_bio)
-               blk_queue_make_request(q, virtblk_make_request);
+       blk_mq_init_commands(q, virtblk_init_vbr, vblk);
+
        q->queuedata = vblk;
 
        virtblk_name_format("vd", index, vblk->disk->disk_name, DISK_NAME_LEN);
@@ -772,8 +582,7 @@ static int virtblk_probe(struct virtio_device *vdev)
                set_disk_ro(vblk->disk, 1);
 
        /* Host must always specify the capacity. */
-       vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity),
-                         &cap, sizeof(cap));
+       virtio_cread(vdev, struct virtio_blk_config, capacity, &cap);
 
        /* If capacity is too big, truncate with warning. */
        if ((sector_t)cap != cap) {
@@ -794,46 +603,45 @@ static int virtblk_probe(struct virtio_device *vdev)
 
        /* Host can optionally specify maximum segment size and number of
         * segments. */
-       err = virtio_config_val(vdev, VIRTIO_BLK_F_SIZE_MAX,
-                               offsetof(struct virtio_blk_config, size_max),
-                               &v);
+       err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,
+                                  struct virtio_blk_config, size_max, &v);
        if (!err)
                blk_queue_max_segment_size(q, v);
        else
                blk_queue_max_segment_size(q, -1U);
 
        /* Host can optionally specify the block size of the device */
-       err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE,
-                               offsetof(struct virtio_blk_config, blk_size),
-                               &blk_size);
+       err = virtio_cread_feature(vdev, VIRTIO_BLK_F_BLK_SIZE,
+                                  struct virtio_blk_config, blk_size,
+                                  &blk_size);
        if (!err)
                blk_queue_logical_block_size(q, blk_size);
        else
                blk_size = queue_logical_block_size(q);
 
        /* Use topology information if available */
-       err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY,
-                       offsetof(struct virtio_blk_config, physical_block_exp),
-                       &physical_block_exp);
+       err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
+                                  struct virtio_blk_config, physical_block_exp,
+                                  &physical_block_exp);
        if (!err && physical_block_exp)
                blk_queue_physical_block_size(q,
                                blk_size * (1 << physical_block_exp));
 
-       err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY,
-                       offsetof(struct virtio_blk_config, alignment_offset),
-                       &alignment_offset);
+       err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
+                                  struct virtio_blk_config, alignment_offset,
+                                  &alignment_offset);
        if (!err && alignment_offset)
                blk_queue_alignment_offset(q, blk_size * alignment_offset);
 
-       err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY,
-                       offsetof(struct virtio_blk_config, min_io_size),
-                       &min_io_size);
+       err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
+                                  struct virtio_blk_config, min_io_size,
+                                  &min_io_size);
        if (!err && min_io_size)
                blk_queue_io_min(q, blk_size * min_io_size);
 
-       err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY,
-                       offsetof(struct virtio_blk_config, opt_io_size),
-                       &opt_io_size);
+       err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
+                                  struct virtio_blk_config, opt_io_size,
+                                  &opt_io_size);
        if (!err && opt_io_size)
                blk_queue_io_opt(q, blk_size * opt_io_size);
 
@@ -857,8 +665,6 @@ out_del_disk:
        blk_cleanup_queue(vblk->disk->queue);
 out_put_disk:
        put_disk(vblk->disk);
-out_mempool:
-       mempool_destroy(vblk->pool);
 out_free_vq:
        vdev->config->del_vqs(vdev);
 out_free_vblk:
@@ -890,7 +696,6 @@ static void virtblk_remove(struct virtio_device *vdev)
 
        refc = atomic_read(&disk_to_dev(vblk->disk)->kobj.kref.refcount);
        put_disk(vblk->disk);
-       mempool_destroy(vblk->pool);
        vdev->config->del_vqs(vdev);
        kfree(vblk);
 
@@ -899,7 +704,7 @@ static void virtblk_remove(struct virtio_device *vdev)
                ida_simple_remove(&vd_index_ida, index);
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int virtblk_freeze(struct virtio_device *vdev)
 {
        struct virtio_blk *vblk = vdev->priv;
@@ -914,10 +719,7 @@ static int virtblk_freeze(struct virtio_device *vdev)
 
        flush_work(&vblk->config_work);
 
-       spin_lock_irq(vblk->disk->queue->queue_lock);
-       blk_stop_queue(vblk->disk->queue);
-       spin_unlock_irq(vblk->disk->queue->queue_lock);
-       blk_sync_queue(vblk->disk->queue);
+       blk_mq_stop_hw_queues(vblk->disk->queue);
 
        vdev->config->del_vqs(vdev);
        return 0;
@@ -930,11 +732,9 @@ static int virtblk_restore(struct virtio_device *vdev)
 
        vblk->config_enable = true;
        ret = init_vq(vdev->priv);
-       if (!ret) {
-               spin_lock_irq(vblk->disk->queue->queue_lock);
-               blk_start_queue(vblk->disk->queue);
-               spin_unlock_irq(vblk->disk->queue->queue_lock);
-       }
+       if (!ret)
+               blk_mq_start_stopped_hw_queues(vblk->disk->queue);
+
        return ret;
 }
 #endif
@@ -959,7 +759,7 @@ static struct virtio_driver virtio_blk = {
        .probe                  = virtblk_probe,
        .remove                 = virtblk_remove,
        .config_changed         = virtblk_config_changed,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
        .freeze                 = virtblk_freeze,
        .restore                = virtblk_restore,
 #endif
index d2120ba..73ce739 100644 (file)
@@ -79,7 +79,7 @@ static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data)
        priv->expires = cur + delay;
        priv->present = 0;
 
-       INIT_COMPLETION(priv->completion);
+       reinit_completion(&priv->completion);
        mod_timer(&priv->timer, priv->expires);
 
        return 4;
index ef46a9c..c12398d 100644 (file)
@@ -133,7 +133,7 @@ static void virtrng_remove(struct virtio_device *vdev)
        remove_common(vdev);
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int virtrng_freeze(struct virtio_device *vdev)
 {
        remove_common(vdev);
@@ -157,7 +157,7 @@ static struct virtio_driver virtio_rng_driver = {
        .id_table =     id_table,
        .probe =        virtrng_probe,
        .remove =       virtrng_remove,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
        .freeze =       virtrng_freeze,
        .restore =      virtrng_restore,
 #endif
index 4fe5609..429b75b 100644 (file)
 #include <linux/fips.h>
 #include <linux/ptrace.h>
 #include <linux/kmemcheck.h>
+#include <linux/workqueue.h>
 #include <linux/irq.h>
 
 #include <asm/processor.h>
 /*
  * Configuration information
  */
-#define INPUT_POOL_WORDS 128
-#define OUTPUT_POOL_WORDS 32
-#define SEC_XFER_SIZE 512
-#define EXTRACT_SIZE 10
+#define INPUT_POOL_SHIFT       12
+#define INPUT_POOL_WORDS       (1 << (INPUT_POOL_SHIFT-5))
+#define OUTPUT_POOL_SHIFT      10
+#define OUTPUT_POOL_WORDS      (1 << (OUTPUT_POOL_SHIFT-5))
+#define SEC_XFER_SIZE          512
+#define EXTRACT_SIZE           10
+
+#define DEBUG_RANDOM_BOOT 0
 
 #define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long))
 
 /*
+ * To allow fractional bits to be tracked, the entropy_count field is
+ * denominated in units of 1/8th bits.
+ *
+ * 2*(ENTROPY_SHIFT + log2(poolbits)) must <= 31, or the multiply in
+ * credit_entropy_bits() needs to be 64 bits wide.
+ */
+#define ENTROPY_SHIFT 3
+#define ENTROPY_BITS(r) ((r)->entropy_count >> ENTROPY_SHIFT)
+
+/*
  * The minimum number of bits of entropy before we wake up a read on
  * /dev/random.  Should be enough to do a significant reseed.
  */
@@ -287,108 +302,100 @@ static int random_read_wakeup_thresh = 64;
  * should wake up processes which are selecting or polling on write
  * access to /dev/random.
  */
-static int random_write_wakeup_thresh = 128;
+static int random_write_wakeup_thresh = 28 * OUTPUT_POOL_WORDS;
 
 /*
- * When the input pool goes over trickle_thresh, start dropping most
- * samples to avoid wasting CPU time and reduce lock contention.
+ * The minimum number of seconds between urandom pool resending.  We
+ * do this to limit the amount of entropy that can be drained from the
+ * input pool even if there are heavy demands on /dev/urandom.
  */
-
-static int trickle_thresh __read_mostly = INPUT_POOL_WORDS * 28;
-
-static DEFINE_PER_CPU(int, trickle_count);
+static int random_min_urandom_seed = 60;
 
 /*
- * A pool of size .poolwords is stirred with a primitive polynomial
- * of degree .poolwords over GF(2).  The taps for various sizes are
- * defined below.  They are chosen to be evenly spaced (minimum RMS
- * distance from evenly spaced; the numbers in the comments are a
- * scaled squared error sum) except for the last tap, which is 1 to
- * get the twisting happening as fast as possible.
+ * Originally, we used a primitive polynomial of degree .poolwords
+ * over GF(2).  The taps for various sizes are defined below.  They
+ * were chosen to be evenly spaced except for the last tap, which is 1
+ * to get the twisting happening as fast as possible.
+ *
+ * For the purposes of better mixing, we use the CRC-32 polynomial as
+ * well to make a (modified) twisted Generalized Feedback Shift
+ * Register.  (See M. Matsumoto & Y. Kurita, 1992.  Twisted GFSR
+ * generators.  ACM Transactions on Modeling and Computer Simulation
+ * 2(3):179-194.  Also see M. Matsumoto & Y. Kurita, 1994.  Twisted
+ * GFSR generators II.  ACM Transactions on Mdeling and Computer
+ * Simulation 4:254-266)
+ *
+ * Thanks to Colin Plumb for suggesting this.
+ *
+ * The mixing operation is much less sensitive than the output hash,
+ * where we use SHA-1.  All that we want of mixing operation is that
+ * it be a good non-cryptographic hash; i.e. it not produce collisions
+ * when fed "random" data of the sort we expect to see.  As long as
+ * the pool state differs for different inputs, we have preserved the
+ * input entropy and done a good job.  The fact that an intelligent
+ * attacker can construct inputs that will produce controlled
+ * alterations to the pool's state is not important because we don't
+ * consider such inputs to contribute any randomness.  The only
+ * property we need with respect to them is that the attacker can't
+ * increase his/her knowledge of the pool's state.  Since all
+ * additions are reversible (knowing the final state and the input,
+ * you can reconstruct the initial state), if an attacker has any
+ * uncertainty about the initial state, he/she can only shuffle that
+ * uncertainty about, but never cause any collisions (which would
+ * decrease the uncertainty).
+ *
+ * Our mixing functions were analyzed by Lacharme, Roeck, Strubel, and
+ * Videau in their paper, "The Linux Pseudorandom Number Generator
+ * Revisited" (see: http://eprint.iacr.org/2012/251.pdf).  In their
+ * paper, they point out that we are not using a true Twisted GFSR,
+ * since Matsumoto & Kurita used a trinomial feedback polynomial (that
+ * is, with only three taps, instead of the six that we are using).
+ * As a result, the resulting polynomial is neither primitive nor
+ * irreducible, and hence does not have a maximal period over
+ * GF(2**32).  They suggest a slight change to the generator
+ * polynomial which improves the resulting TGFSR polynomial to be
+ * irreducible, which we have made here.
  */
 static struct poolinfo {
-       int poolwords;
+       int poolbitshift, poolwords, poolbytes, poolbits, poolfracbits;
+#define S(x) ilog2(x)+5, (x), (x)*4, (x)*32, (x) << (ENTROPY_SHIFT+5)
        int tap1, tap2, tap3, tap4, tap5;
 } poolinfo_table[] = {
-       /* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */
-       { 128,  103,    76,     51,     25,     1 },
-       /* x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 -- 15 */
-       { 32,   26,     20,     14,     7,      1 },
+       /* was: x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 */
+       /* x^128 + x^104 + x^76 + x^51 +x^25 + x + 1 */
+       { S(128),       104,    76,     51,     25,     1 },
+       /* was: x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 */
+       /* x^32 + x^26 + x^19 + x^14 + x^7 + x + 1 */
+       { S(32),        26,     19,     14,     7,      1 },
 #if 0
        /* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1  -- 115 */
-       { 2048, 1638,   1231,   819,    411,    1 },
+       { S(2048),      1638,   1231,   819,    411,    1 },
 
        /* x^1024 + x^817 + x^615 + x^412 + x^204 + x + 1 -- 290 */
-       { 1024, 817,    615,    412,    204,    1 },
+       { S(1024),      817,    615,    412,    204,    1 },
 
        /* x^1024 + x^819 + x^616 + x^410 + x^207 + x^2 + 1 -- 115 */
-       { 1024, 819,    616,    410,    207,    2 },
+       { S(1024),      819,    616,    410,    207,    2 },
 
        /* x^512 + x^411 + x^308 + x^208 + x^104 + x + 1 -- 225 */
-       { 512,  411,    308,    208,    104,    1 },
+       { S(512),       411,    308,    208,    104,    1 },
 
        /* x^512 + x^409 + x^307 + x^206 + x^102 + x^2 + 1 -- 95 */
-       { 512,  409,    307,    206,    102,    2 },
+       { S(512),       409,    307,    206,    102,    2 },
        /* x^512 + x^409 + x^309 + x^205 + x^103 + x^2 + 1 -- 95 */
-       { 512,  409,    309,    205,    103,    2 },
+       { S(512),       409,    309,    205,    103,    2 },
 
        /* x^256 + x^205 + x^155 + x^101 + x^52 + x + 1 -- 125 */
-       { 256,  205,    155,    101,    52,     1 },
+       { S(256),       205,    155,    101,    52,     1 },
 
        /* x^128 + x^103 + x^78 + x^51 + x^27 + x^2 + 1 -- 70 */
-       { 128,  103,    78,     51,     27,     2 },
+       { S(128),       103,    78,     51,     27,     2 },
 
        /* x^64 + x^52 + x^39 + x^26 + x^14 + x + 1 -- 15 */
-       { 64,   52,     39,     26,     14,     1 },
+       { S(64),        52,     39,     26,     14,     1 },
 #endif
 };
 
-#define POOLBITS       poolwords*32
-#define POOLBYTES      poolwords*4
-
-/*
- * For the purposes of better mixing, we use the CRC-32 polynomial as
- * well to make a twisted Generalized Feedback Shift Reigster
- *
- * (See M. Matsumoto & Y. Kurita, 1992.  Twisted GFSR generators.  ACM
- * Transactions on Modeling and Computer Simulation 2(3):179-194.
- * Also see M. Matsumoto & Y. Kurita, 1994.  Twisted GFSR generators
- * II.  ACM Transactions on Mdeling and Computer Simulation 4:254-266)
- *
- * Thanks to Colin Plumb for suggesting this.
- *
- * We have not analyzed the resultant polynomial to prove it primitive;
- * in fact it almost certainly isn't.  Nonetheless, the irreducible factors
- * of a random large-degree polynomial over GF(2) are more than large enough
- * that periodicity is not a concern.
- *
- * The input hash is much less sensitive than the output hash.  All
- * that we want of it is that it be a good non-cryptographic hash;
- * i.e. it not produce collisions when fed "random" data of the sort
- * we expect to see.  As long as the pool state differs for different
- * inputs, we have preserved the input entropy and done a good job.
- * The fact that an intelligent attacker can construct inputs that
- * will produce controlled alterations to the pool's state is not
- * important because we don't consider such inputs to contribute any
- * randomness.  The only property we need with respect to them is that
- * the attacker can't increase his/her knowledge of the pool's state.
- * Since all additions are reversible (knowing the final state and the
- * input, you can reconstruct the initial state), if an attacker has
- * any uncertainty about the initial state, he/she can only shuffle
- * that uncertainty about, but never cause any collisions (which would
- * decrease the uncertainty).
- *
- * The chosen system lets the state of the pool be (essentially) the input
- * modulo the generator polymnomial.  Now, for random primitive polynomials,
- * this is a universal class of hash functions, meaning that the chance
- * of a collision is limited by the attacker's knowledge of the generator
- * polynomail, so if it is chosen at random, an attacker can never force
- * a collision.  Here, we use a fixed polynomial, but we *can* assume that
- * ###--> it is unknown to the processes generating the input entropy. <-###
- * Because of this important property, this is a good, collision-resistant
- * hash; hash collisions will occur no more often than chance.
- */
-
 /*
  * Static global variables
  */
@@ -396,17 +403,6 @@ static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
 static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
 static struct fasync_struct *fasync;
 
-static bool debug;
-module_param(debug, bool, 0644);
-#define DEBUG_ENT(fmt, arg...) do { \
-       if (debug) \
-               printk(KERN_DEBUG "random %04d %04d %04d: " \
-               fmt,\
-               input_pool.entropy_count,\
-               blocking_pool.entropy_count,\
-               nonblocking_pool.entropy_count,\
-               ## arg); } while (0)
-
 /**********************************************************************
  *
  * OS independent entropy store.   Here are the functions which handle
@@ -417,23 +413,26 @@ module_param(debug, bool, 0644);
 struct entropy_store;
 struct entropy_store {
        /* read-only data: */
-       struct poolinfo *poolinfo;
+       const struct poolinfo *poolinfo;
        __u32 *pool;
        const char *name;
        struct entropy_store *pull;
-       int limit;
+       struct work_struct push_work;
 
        /* read-write data: */
+       unsigned long last_pulled;
        spinlock_t lock;
-       unsigned add_ptr;
-       unsigned input_rotate;
+       unsigned short add_ptr;
+       unsigned short input_rotate;
        int entropy_count;
        int entropy_total;
        unsigned int initialized:1;
-       bool last_data_init;
+       unsigned int limit:1;
+       unsigned int last_data_init:1;
        __u8 last_data[EXTRACT_SIZE];
 };
 
+static void push_to_pool(struct work_struct *work);
 static __u32 input_pool_data[INPUT_POOL_WORDS];
 static __u32 blocking_pool_data[OUTPUT_POOL_WORDS];
 static __u32 nonblocking_pool_data[OUTPUT_POOL_WORDS];
@@ -452,7 +451,9 @@ static struct entropy_store blocking_pool = {
        .limit = 1,
        .pull = &input_pool,
        .lock = __SPIN_LOCK_UNLOCKED(blocking_pool.lock),
-       .pool = blocking_pool_data
+       .pool = blocking_pool_data,
+       .push_work = __WORK_INITIALIZER(blocking_pool.push_work,
+                                       push_to_pool),
 };
 
 static struct entropy_store nonblocking_pool = {
@@ -460,7 +461,9 @@ static struct entropy_store nonblocking_pool = {
        .name = "nonblocking",
        .pull = &input_pool,
        .lock = __SPIN_LOCK_UNLOCKED(nonblocking_pool.lock),
-       .pool = nonblocking_pool_data
+       .pool = nonblocking_pool_data,
+       .push_work = __WORK_INITIALIZER(nonblocking_pool.push_work,
+                                       push_to_pool),
 };
 
 static __u32 const twist_table[8] = {
@@ -498,7 +501,7 @@ static void _mix_pool_bytes(struct entropy_store *r, const void *in,
 
        /* mix one byte at a time to simplify size handling and churn faster */
        while (nbytes--) {
-               w = rol32(*bytes++, input_rotate & 31);
+               w = rol32(*bytes++, input_rotate);
                i = (i - 1) & wordmask;
 
                /* XOR in the various taps */
@@ -518,7 +521,7 @@ static void _mix_pool_bytes(struct entropy_store *r, const void *in,
                 * rotation, so that successive passes spread the
                 * input bits across the pool evenly.
                 */
-               input_rotate += i ? 7 : 14;
+               input_rotate = (input_rotate + (i ? 7 : 14)) & 31;
        }
 
        ACCESS_ONCE(r->input_rotate) = input_rotate;
@@ -561,65 +564,151 @@ struct fast_pool {
  * 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)
+static void fast_mix(struct fast_pool *f, __u32 input[4])
 {
-       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;
+       w = rol32(input[0], input_rotate) ^ f->pool[0] ^ f->pool[3];
+       f->pool[0] = (w >> 3) ^ twist_table[w & 7];
+       input_rotate = (input_rotate + 14) & 31;
+       w = rol32(input[1], input_rotate) ^ f->pool[1] ^ f->pool[0];
+       f->pool[1] = (w >> 3) ^ twist_table[w & 7];
+       input_rotate = (input_rotate + 7) & 31;
+       w = rol32(input[2], input_rotate) ^ f->pool[2] ^ f->pool[1];
+       f->pool[2] = (w >> 3) ^ twist_table[w & 7];
+       input_rotate = (input_rotate + 7) & 31;
+       w = rol32(input[3], input_rotate) ^ f->pool[3] ^ f->pool[2];
+       f->pool[3] = (w >> 3) ^ twist_table[w & 7];
+       input_rotate = (input_rotate + 7) & 31;
+
        f->rotate = input_rotate;
+       f->count++;
 }
 
 /*
- * Credit (or debit) the entropy store with n bits of entropy
+ * Credit (or debit) the entropy store with n bits of entropy.
+ * Use credit_entropy_bits_safe() if the value comes from userspace
+ * or otherwise should be checked for extreme values.
  */
 static void credit_entropy_bits(struct entropy_store *r, int nbits)
 {
        int entropy_count, orig;
+       const int pool_size = r->poolinfo->poolfracbits;
+       int nfrac = nbits << ENTROPY_SHIFT;
 
        if (!nbits)
                return;
 
-       DEBUG_ENT("added %d entropy credits to %s\n", nbits, r->name);
 retry:
        entropy_count = orig = ACCESS_ONCE(r->entropy_count);
-       entropy_count += nbits;
+       if (nfrac < 0) {
+               /* Debit */
+               entropy_count += nfrac;
+       } else {
+               /*
+                * Credit: we have to account for the possibility of
+                * overwriting already present entropy.  Even in the
+                * ideal case of pure Shannon entropy, new contributions
+                * approach the full value asymptotically:
+                *
+                * entropy <- entropy + (pool_size - entropy) *
+                *      (1 - exp(-add_entropy/pool_size))
+                *
+                * For add_entropy <= pool_size/2 then
+                * (1 - exp(-add_entropy/pool_size)) >=
+                *    (add_entropy/pool_size)*0.7869...
+                * so we can approximate the exponential with
+                * 3/4*add_entropy/pool_size and still be on the
+                * safe side by adding at most pool_size/2 at a time.
+                *
+                * The use of pool_size-2 in the while statement is to
+                * prevent rounding artifacts from making the loop
+                * arbitrarily long; this limits the loop to log2(pool_size)*2
+                * turns no matter how large nbits is.
+                */
+               int pnfrac = nfrac;
+               const int s = r->poolinfo->poolbitshift + ENTROPY_SHIFT + 2;
+               /* The +2 corresponds to the /4 in the denominator */
+
+               do {
+                       unsigned int anfrac = min(pnfrac, pool_size/2);
+                       unsigned int add =
+                               ((pool_size - entropy_count)*anfrac*3) >> s;
+
+                       entropy_count += add;
+                       pnfrac -= anfrac;
+               } while (unlikely(entropy_count < pool_size-2 && pnfrac));
+       }
 
        if (entropy_count < 0) {
-               DEBUG_ENT("negative entropy/overflow\n");
+               pr_warn("random: negative entropy/overflow: pool %s count %d\n",
+                       r->name, entropy_count);
+               WARN_ON(1);
                entropy_count = 0;
-       } else if (entropy_count > r->poolinfo->POOLBITS)
-               entropy_count = r->poolinfo->POOLBITS;
+       } else if (entropy_count > pool_size)
+               entropy_count = pool_size;
        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;
-                       if (r == &nonblocking_pool)
-                               prandom_reseed_late();
+       r->entropy_total += nbits;
+       if (!r->initialized && r->entropy_total > 128) {
+               r->initialized = 1;
+               r->entropy_total = 0;
+               if (r == &nonblocking_pool) {
+                       prandom_reseed_late();
+                       pr_notice("random: %s pool is initialized\n", r->name);
                }
        }
 
-       trace_credit_entropy_bits(r->name, nbits, entropy_count,
+       trace_credit_entropy_bits(r->name, nbits,
+                                 entropy_count >> ENTROPY_SHIFT,
                                  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);
+       if (r == &input_pool) {
+               int entropy_bytes = entropy_count >> ENTROPY_SHIFT;
+
+               /* should we wake readers? */
+               if (entropy_bytes >= random_read_wakeup_thresh) {
+                       wake_up_interruptible(&random_read_wait);
+                       kill_fasync(&fasync, SIGIO, POLL_IN);
+               }
+               /* If the input pool is getting full, send some
+                * entropy to the two output pools, flipping back and
+                * forth between them, until the output pools are 75%
+                * full.
+                */
+               if (entropy_bytes > random_write_wakeup_thresh &&
+                   r->initialized &&
+                   r->entropy_total >= 2*random_read_wakeup_thresh) {
+                       static struct entropy_store *last = &blocking_pool;
+                       struct entropy_store *other = &blocking_pool;
+
+                       if (last == &blocking_pool)
+                               other = &nonblocking_pool;
+                       if (other->entropy_count <=
+                           3 * other->poolinfo->poolfracbits / 4)
+                               last = other;
+                       if (last->entropy_count <=
+                           3 * last->poolinfo->poolfracbits / 4) {
+                               schedule_work(&last->push_work);
+                               r->entropy_total = 0;
+                       }
+               }
        }
 }
 
+static void credit_entropy_bits_safe(struct entropy_store *r, int nbits)
+{
+       const int nbits_max = (int)(~0U >> (ENTROPY_SHIFT + 1));
+
+       /* Cap the value to avoid overflows */
+       nbits = min(nbits,  nbits_max);
+       nbits = max(nbits, -nbits_max);
+
+       credit_entropy_bits(r, nbits);
+}
+
 /*********************************************************************
  *
  * Entropy input management
@@ -633,6 +722,8 @@ struct timer_rand_state {
        unsigned dont_count_entropy:1;
 };
 
+#define INIT_TIMER_RAND_STATE { INITIAL_JIFFIES, };
+
 /*
  * Add device- or boot-specific data to the input and nonblocking
  * pools to help initialize them to unique values.
@@ -644,15 +735,22 @@ struct timer_rand_state {
 void add_device_randomness(const void *buf, unsigned int size)
 {
        unsigned long time = random_get_entropy() ^ jiffies;
+       unsigned long flags;
 
-       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);
+       trace_add_device_randomness(size, _RET_IP_);
+       spin_lock_irqsave(&input_pool.lock, flags);
+       _mix_pool_bytes(&input_pool, buf, size, NULL);
+       _mix_pool_bytes(&input_pool, &time, sizeof(time), NULL);
+       spin_unlock_irqrestore(&input_pool.lock, flags);
+
+       spin_lock_irqsave(&nonblocking_pool.lock, flags);
+       _mix_pool_bytes(&nonblocking_pool, buf, size, NULL);
+       _mix_pool_bytes(&nonblocking_pool, &time, sizeof(time), NULL);
+       spin_unlock_irqrestore(&nonblocking_pool.lock, flags);
 }
 EXPORT_SYMBOL(add_device_randomness);
 
-static struct timer_rand_state input_timer_state;
+static struct timer_rand_state input_timer_state = INIT_TIMER_RAND_STATE;
 
 /*
  * This function adds entropy to the entropy "pool" by using timing
@@ -666,6 +764,7 @@ static struct timer_rand_state input_timer_state;
  */
 static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
 {
+       struct entropy_store    *r;
        struct {
                long jiffies;
                unsigned cycles;
@@ -674,15 +773,12 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
        long delta, delta2, delta3;
 
        preempt_disable();
-       /* if over the trickle threshold, use only 1 in 4096 samples */
-       if (input_pool.entropy_count > trickle_thresh &&
-           ((__this_cpu_inc_return(trickle_count) - 1) & 0xfff))
-               goto out;
 
        sample.jiffies = jiffies;
        sample.cycles = random_get_entropy();
        sample.num = num;
-       mix_pool_bytes(&input_pool, &sample, sizeof(sample), NULL);
+       r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool;
+       mix_pool_bytes(r, &sample, sizeof(sample), NULL);
 
        /*
         * Calculate number of bits of randomness we probably added.
@@ -716,10 +812,8 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
                 * Round down by 1 bit on general principles,
                 * and limit entropy entimate to 12 bits.
                 */
-               credit_entropy_bits(&input_pool,
-                                   min_t(int, fls(delta>>1), 11));
+               credit_entropy_bits(r, min_t(int, fls(delta>>1), 11));
        }
-out:
        preempt_enable();
 }
 
@@ -732,10 +826,10 @@ void add_input_randomness(unsigned int type, unsigned int code,
        if (value == last_value)
                return;
 
-       DEBUG_ENT("input event\n");
        last_value = value;
        add_timer_randomness(&input_timer_state,
                             (type << 4) ^ code ^ (code >> 4) ^ value);
+       trace_add_input_randomness(ENTROPY_BITS(&input_pool));
 }
 EXPORT_SYMBOL_GPL(add_input_randomness);
 
@@ -747,20 +841,21 @@ void add_interrupt_randomness(int irq, int irq_flags)
        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 = random_get_entropy();
-
-       input[0] = cycles ^ jiffies;
-       input[1] = irq;
-       if (regs) {
-               __u64 ip = instruction_pointer(regs);
-               input[2] = ip;
-               input[3] = ip >> 32;
-       }
+       cycles_t                cycles = random_get_entropy();
+       __u32                   input[4], c_high, j_high;
+       __u64                   ip;
 
-       fast_mix(fast_pool, input, sizeof(input));
+       c_high = (sizeof(cycles) > 4) ? cycles >> 32 : 0;
+       j_high = (sizeof(now) > 4) ? now >> 32 : 0;
+       input[0] = cycles ^ j_high ^ irq;
+       input[1] = now ^ c_high;
+       ip = regs ? instruction_pointer(regs) : _RET_IP_;
+       input[2] = ip;
+       input[3] = ip >> 32;
 
-       if ((fast_pool->count & 1023) &&
-           !time_after(now, fast_pool->last + HZ))
+       fast_mix(fast_pool, input);
+
+       if ((fast_pool->count & 63) && !time_after(now, fast_pool->last + HZ))
                return;
 
        fast_pool->last = now;
@@ -789,10 +884,8 @@ void add_disk_randomness(struct gendisk *disk)
        if (!disk || !disk->random)
                return;
        /* first major is 1, so we get >= 0x200 here */
-       DEBUG_ENT("disk event %d:%d\n",
-                 MAJOR(disk_devt(disk)), MINOR(disk_devt(disk)));
-
        add_timer_randomness(disk->random, 0x100 + disk_devt(disk));
+       trace_add_disk_randomness(disk_devt(disk), ENTROPY_BITS(&input_pool));
 }
 #endif
 
@@ -810,30 +903,58 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
  * from the primary pool to the secondary extraction pool. We make
  * sure we pull enough for a 'catastrophic reseed'.
  */
+static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes);
 static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
 {
-       __u32   tmp[OUTPUT_POOL_WORDS];
+       if (r->limit == 0 && random_min_urandom_seed) {
+               unsigned long now = jiffies;
 
-       if (r->pull && r->entropy_count < nbytes * 8 &&
-           r->entropy_count < r->poolinfo->POOLBITS) {
-               /* If we're limited, always leave two wakeup worth's BITS */
-               int rsvd = r->limit ? 0 : random_read_wakeup_thresh/4;
-               int bytes = nbytes;
-
-               /* pull at least as many as BYTES as wakeup BITS */
-               bytes = max_t(int, bytes, random_read_wakeup_thresh / 8);
-               /* but never more than the buffer size */
-               bytes = min_t(int, bytes, sizeof(tmp));
-
-               DEBUG_ENT("going to reseed %s with %d bits "
-                         "(%zu of %d requested)\n",
-                         r->name, bytes * 8, nbytes * 8, r->entropy_count);
-
-               bytes = extract_entropy(r->pull, tmp, bytes,
-                                       random_read_wakeup_thresh / 8, rsvd);
-               mix_pool_bytes(r, tmp, bytes, NULL);
-               credit_entropy_bits(r, bytes*8);
+               if (time_before(now,
+                               r->last_pulled + random_min_urandom_seed * HZ))
+                       return;
+               r->last_pulled = now;
        }
+       if (r->pull &&
+           r->entropy_count < (nbytes << (ENTROPY_SHIFT + 3)) &&
+           r->entropy_count < r->poolinfo->poolfracbits)
+               _xfer_secondary_pool(r, nbytes);
+}
+
+static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
+{
+       __u32   tmp[OUTPUT_POOL_WORDS];
+
+       /* For /dev/random's pool, always leave two wakeup worth's BITS */
+       int rsvd = r->limit ? 0 : random_read_wakeup_thresh/4;
+       int bytes = nbytes;
+
+       /* pull at least as many as BYTES as wakeup BITS */
+       bytes = max_t(int, bytes, random_read_wakeup_thresh / 8);
+       /* but never more than the buffer size */
+       bytes = min_t(int, bytes, sizeof(tmp));
+
+       trace_xfer_secondary_pool(r->name, bytes * 8, nbytes * 8,
+                                 ENTROPY_BITS(r), ENTROPY_BITS(r->pull));
+       bytes = extract_entropy(r->pull, tmp, bytes,
+                               random_read_wakeup_thresh / 8, rsvd);
+       mix_pool_bytes(r, tmp, bytes, NULL);
+       credit_entropy_bits(r, bytes*8);
+}
+
+/*
+ * Used as a workqueue function so that when the input pool is getting
+ * full, we can "spill over" some entropy to the output pools.  That
+ * way the output pools can store some of the excess entropy instead
+ * of letting it go to waste.
+ */
+static void push_to_pool(struct work_struct *work)
+{
+       struct entropy_store *r = container_of(work, struct entropy_store,
+                                             push_work);
+       BUG_ON(!r);
+       _xfer_secondary_pool(r, random_read_wakeup_thresh/8);
+       trace_push_to_pool(r->name, r->entropy_count >> ENTROPY_SHIFT,
+                          r->pull->entropy_count >> ENTROPY_SHIFT);
 }
 
 /*
@@ -853,50 +974,48 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,
 {
        unsigned long flags;
        int wakeup_write = 0;
+       int have_bytes;
+       int entropy_count, orig;
+       size_t ibytes;
 
        /* Hold lock while accounting */
        spin_lock_irqsave(&r->lock, flags);
 
-       BUG_ON(r->entropy_count > r->poolinfo->POOLBITS);
-       DEBUG_ENT("trying to extract %zu bits from %s\n",
-                 nbytes * 8, r->name);
+       BUG_ON(r->entropy_count > r->poolinfo->poolfracbits);
 
        /* Can we pull enough? */
-       if (r->entropy_count / 8 < min + reserved) {
-               nbytes = 0;
-       } else {
-               int entropy_count, orig;
 retry:
-               entropy_count = orig = ACCESS_ONCE(r->entropy_count);
+       entropy_count = orig = ACCESS_ONCE(r->entropy_count);
+       have_bytes = entropy_count >> (ENTROPY_SHIFT + 3);
+       ibytes = nbytes;
+       if (have_bytes < min + reserved) {
+               ibytes = 0;
+       } else {
                /* If limited, never pull more than available */
-               if (r->limit && nbytes + reserved >= entropy_count / 8)
-                       nbytes = entropy_count/8 - reserved;
-
-               if (entropy_count / 8 >= nbytes + reserved) {
-                       entropy_count -= nbytes*8;
-                       if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
-                               goto retry;
-               } else {
-                       entropy_count = reserved;
-                       if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
-                               goto retry;
-               }
+               if (r->limit && ibytes + reserved >= have_bytes)
+                       ibytes = have_bytes - reserved;
 
-               if (entropy_count < random_write_wakeup_thresh)
-                       wakeup_write = 1;
-       }
+               if (have_bytes >= ibytes + reserved)
+                       entropy_count -= ibytes << (ENTROPY_SHIFT + 3);
+               else
+                       entropy_count = reserved << (ENTROPY_SHIFT + 3);
 
-       DEBUG_ENT("debiting %zu entropy credits from %s%s\n",
-                 nbytes * 8, r->name, r->limit ? "" : " (unlimited)");
+               if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
+                       goto retry;
 
+               if ((r->entropy_count >> ENTROPY_SHIFT)
+                   < random_write_wakeup_thresh)
+                       wakeup_write = 1;
+       }
        spin_unlock_irqrestore(&r->lock, flags);
 
+       trace_debit_entropy(r->name, 8 * ibytes);
        if (wakeup_write) {
                wake_up_interruptible(&random_write_wait);
                kill_fasync(&fasync, SIGIO, POLL_OUT);
        }
 
-       return nbytes;
+       return ibytes;
 }
 
 static void extract_buf(struct entropy_store *r, __u8 *out)
@@ -904,7 +1023,7 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
        int i;
        union {
                __u32 w[5];
-               unsigned long l[LONGS(EXTRACT_SIZE)];
+               unsigned long l[LONGS(20)];
        } hash;
        __u32 workspace[SHA_WORKSPACE_WORDS];
        __u8 extract[64];
@@ -917,6 +1036,17 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
                sha_transform(hash.w, (__u8 *)(r->pool + i), workspace);
 
        /*
+        * If we have a architectural hardware random number
+        * generator, mix that in, too.
+        */
+       for (i = 0; i < LONGS(20); i++) {
+               unsigned long v;
+               if (!arch_get_random_long(&v))
+                       break;
+               hash.l[i] ^= v;
+       }
+
+       /*
         * We mix the hash back into the pool to prevent backtracking
         * attacks (where the attacker knows the state of the pool
         * plus the current outputs, and attempts to find previous
@@ -945,17 +1075,6 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
        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));
 }
@@ -971,10 +1090,10 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
        if (fips_enabled) {
                spin_lock_irqsave(&r->lock, flags);
                if (!r->last_data_init) {
-                       r->last_data_init = true;
+                       r->last_data_init = 1;
                        spin_unlock_irqrestore(&r->lock, flags);
                        trace_extract_entropy(r->name, EXTRACT_SIZE,
-                                             r->entropy_count, _RET_IP_);
+                                             ENTROPY_BITS(r), _RET_IP_);
                        xfer_secondary_pool(r, EXTRACT_SIZE);
                        extract_buf(r, tmp);
                        spin_lock_irqsave(&r->lock, flags);
@@ -983,7 +1102,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
                spin_unlock_irqrestore(&r->lock, flags);
        }
 
-       trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_);
+       trace_extract_entropy(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_);
        xfer_secondary_pool(r, nbytes);
        nbytes = account(r, nbytes, min, reserved);
 
@@ -1016,7 +1135,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_);
+       trace_extract_entropy_user(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_);
        xfer_secondary_pool(r, nbytes);
        nbytes = account(r, nbytes, 0, 0);
 
@@ -1056,6 +1175,14 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
  */
 void get_random_bytes(void *buf, int nbytes)
 {
+#if DEBUG_RANDOM_BOOT > 0
+       if (unlikely(nonblocking_pool.initialized == 0))
+               printk(KERN_NOTICE "random: %pF get_random_bytes called "
+                      "with %d bits of entropy available\n",
+                      (void *) _RET_IP_,
+                      nonblocking_pool.entropy_total);
+#endif
+       trace_get_random_bytes(nbytes, _RET_IP_);
        extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0);
 }
 EXPORT_SYMBOL(get_random_bytes);
@@ -1074,7 +1201,7 @@ void get_random_bytes_arch(void *buf, int nbytes)
 {
        char *p = buf;
 
-       trace_get_random_bytes(nbytes, _RET_IP_);
+       trace_get_random_bytes_arch(nbytes, _RET_IP_);
        while (nbytes) {
                unsigned long v;
                int chunk = min(nbytes, (int)sizeof(unsigned long));
@@ -1108,13 +1235,11 @@ static void init_std_data(struct entropy_store *r)
        ktime_t now = ktime_get_real();
        unsigned long rv;
 
-       r->entropy_count = 0;
-       r->entropy_total = 0;
-       r->last_data_init = false;
+       r->last_pulled = jiffies;
        mix_pool_bytes(r, &now, sizeof(now), NULL);
-       for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof(rv)) {
+       for (i = r->poolinfo->poolbytes; i > 0; i -= sizeof(rv)) {
                if (!arch_get_random_long(&rv))
-                       break;
+                       rv = random_get_entropy();
                mix_pool_bytes(r, &rv, sizeof(rv), NULL);
        }
        mix_pool_bytes(r, utsname(), sizeof(*(utsname())), NULL);
@@ -1137,7 +1262,7 @@ static int rand_initialize(void)
        init_std_data(&nonblocking_pool);
        return 0;
 }
-module_init(rand_initialize);
+early_initcall(rand_initialize);
 
 #ifdef CONFIG_BLOCK
 void rand_initialize_disk(struct gendisk *disk)
@@ -1149,8 +1274,10 @@ void rand_initialize_disk(struct gendisk *disk)
         * source.
         */
        state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
-       if (state)
+       if (state) {
+               state->last_time = INITIAL_JIFFIES;
                disk->random = state;
+       }
 }
 #endif
 
@@ -1167,8 +1294,6 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
                if (n > SEC_XFER_SIZE)
                        n = SEC_XFER_SIZE;
 
-               DEBUG_ENT("reading %zu bits\n", n*8);
-
                n = extract_entropy_user(&blocking_pool, buf, n);
 
                if (n < 0) {
@@ -1176,8 +1301,9 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
                        break;
                }
 
-               DEBUG_ENT("read got %zd bits (%zd still needed)\n",
-                         n*8, (nbytes-n)*8);
+               trace_random_read(n*8, (nbytes-n)*8,
+                                 ENTROPY_BITS(&blocking_pool),
+                                 ENTROPY_BITS(&input_pool));
 
                if (n == 0) {
                        if (file->f_flags & O_NONBLOCK) {
@@ -1185,13 +1311,9 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
                                break;
                        }
 
-                       DEBUG_ENT("sleeping?\n");
-
                        wait_event_interruptible(random_read_wait,
-                               input_pool.entropy_count >=
-                                                random_read_wakeup_thresh);
-
-                       DEBUG_ENT("awake\n");
+                               ENTROPY_BITS(&input_pool) >=
+                               random_read_wakeup_thresh);
 
                        if (signal_pending(current)) {
                                retval = -ERESTARTSYS;
@@ -1214,7 +1336,18 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
 static ssize_t
 urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
 {
-       return extract_entropy_user(&nonblocking_pool, buf, nbytes);
+       int ret;
+
+       if (unlikely(nonblocking_pool.initialized == 0))
+               printk_once(KERN_NOTICE "random: %s urandom read "
+                           "with %d bits of entropy available\n",
+                           current->comm, nonblocking_pool.entropy_total);
+
+       ret = extract_entropy_user(&nonblocking_pool, buf, nbytes);
+
+       trace_urandom_read(8 * nbytes, ENTROPY_BITS(&nonblocking_pool),
+                          ENTROPY_BITS(&input_pool));
+       return ret;
 }
 
 static unsigned int
@@ -1225,9 +1358,9 @@ random_poll(struct file *file, poll_table * wait)
        poll_wait(file, &random_read_wait, wait);
        poll_wait(file, &random_write_wait, wait);
        mask = 0;
-       if (input_pool.entropy_count >= random_read_wakeup_thresh)
+       if (ENTROPY_BITS(&input_pool) >= random_read_wakeup_thresh)
                mask |= POLLIN | POLLRDNORM;
-       if (input_pool.entropy_count < random_write_wakeup_thresh)
+       if (ENTROPY_BITS(&input_pool) < random_write_wakeup_thresh)
                mask |= POLLOUT | POLLWRNORM;
        return mask;
 }
@@ -1278,7 +1411,8 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
        switch (cmd) {
        case RNDGETENTCNT:
                /* inherently racy, no point locking */
-               if (put_user(input_pool.entropy_count, p))
+               ent_count = ENTROPY_BITS(&input_pool);
+               if (put_user(ent_count, p))
                        return -EFAULT;
                return 0;
        case RNDADDTOENTCNT:
@@ -1286,7 +1420,7 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
                        return -EPERM;
                if (get_user(ent_count, p))
                        return -EFAULT;
-               credit_entropy_bits(&input_pool, ent_count);
+               credit_entropy_bits_safe(&input_pool, ent_count);
                return 0;
        case RNDADDENTROPY:
                if (!capable(CAP_SYS_ADMIN))
@@ -1301,14 +1435,19 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
                                    size);
                if (retval < 0)
                        return retval;
-               credit_entropy_bits(&input_pool, ent_count);
+               credit_entropy_bits_safe(&input_pool, ent_count);
                return 0;
        case RNDZAPENTCNT:
        case RNDCLEARPOOL:
-               /* Clear the entropy pool counters. */
+               /*
+                * Clear the entropy pool counters. We no longer clear
+                * the entropy pool, as that's silly.
+                */
                if (!capable(CAP_SYS_ADMIN))
                        return -EPERM;
-               rand_initialize();
+               input_pool.entropy_count = 0;
+               nonblocking_pool.entropy_count = 0;
+               blocking_pool.entropy_count = 0;
                return 0;
        default:
                return -EINVAL;
@@ -1408,6 +1547,23 @@ static int proc_do_uuid(struct ctl_table *table, int write,
        return proc_dostring(&fake_table, write, buffer, lenp, ppos);
 }
 
+/*
+ * Return entropy available scaled to integral bits
+ */
+static int proc_do_entropy(ctl_table *table, int write,
+                          void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       ctl_table fake_table;
+       int entropy_count;
+
+       entropy_count = *(int *)table->data >> ENTROPY_SHIFT;
+
+       fake_table.data = &entropy_count;
+       fake_table.maxlen = sizeof(entropy_count);
+
+       return proc_dointvec(&fake_table, write, buffer, lenp, ppos);
+}
+
 static int sysctl_poolsize = INPUT_POOL_WORDS * 32;
 extern struct ctl_table random_table[];
 struct ctl_table random_table[] = {
@@ -1422,7 +1578,7 @@ struct ctl_table random_table[] = {
                .procname       = "entropy_avail",
                .maxlen         = sizeof(int),
                .mode           = 0444,
-               .proc_handler   = proc_dointvec,
+               .proc_handler   = proc_do_entropy,
                .data           = &input_pool.entropy_count,
        },
        {
@@ -1444,6 +1600,13 @@ struct ctl_table random_table[] = {
                .extra2         = &max_write_thresh,
        },
        {
+               .procname       = "urandom_min_reseed_secs",
+               .data           = &random_min_urandom_seed,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
                .procname       = "boot_id",
                .data           = &sysctl_bootid,
                .maxlen         = 16,
index 94c0c74..1a65838 100644 (file)
@@ -33,6 +33,15 @@ config TCG_TIS
          from within Linux.  To compile this driver as a module, choose
          M here; the module will be called tpm_tis.
 
+config TCG_TIS_I2C_ATMEL
+       tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)"
+       depends on I2C
+       ---help---
+         If you have an Atmel I2C TPM security chip say Yes and it will be
+         accessible from within Linux.
+         To compile this driver as a module, choose M here; the module will
+         be called tpm_tis_i2c_atmel.
+
 config TCG_TIS_I2C_INFINEON
        tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)"
        depends on I2C
@@ -42,7 +51,17 @@ config TCG_TIS_I2C_INFINEON
          Specification 0.20 say Yes and it will be accessible from within
          Linux.
          To compile this driver as a module, choose M here; the module
-         will be called tpm_tis_i2c_infineon.
+         will be called tpm_i2c_infineon.
+
+config TCG_TIS_I2C_NUVOTON
+       tristate "TPM Interface Specification 1.2 Interface (I2C - Nuvoton)"
+       depends on I2C
+       ---help---
+         If you have a TPM security chip with an I2C interface from
+         Nuvoton Technology Corp. say Yes and it will be accessible
+         from within Linux.
+         To compile this driver as a module, choose M here; the module
+         will be called tpm_i2c_nuvoton.
 
 config TCG_NSC
        tristate "National Semiconductor TPM Interface"
@@ -82,14 +101,14 @@ config TCG_IBMVTPM
          as a module, choose M here; the module will be called tpm_ibmvtpm.
 
 config TCG_ST33_I2C
-        tristate "STMicroelectronics ST33 I2C TPM"
-        depends on I2C
-        depends on GPIOLIB
-        ---help---
-        If you have a TPM security chip from STMicroelectronics working with
-        an I2C bus say Yes and it will be accessible from within Linux.
-        To compile this driver as a module, choose M here; the module will be
-        called tpm_stm_st33_i2c.
+       tristate "STMicroelectronics ST33 I2C TPM"
+       depends on I2C
+       depends on GPIOLIB
+       ---help---
+         If you have a TPM security chip from STMicroelectronics working with
+         an I2C bus say Yes and it will be accessible from within Linux.
+         To compile this driver as a module, choose M here; the module will be
+         called tpm_stm_st33_i2c.
 
 config TCG_XEN
        tristate "XEN TPM Interface"
index eb41ff9..b80a400 100644 (file)
@@ -2,17 +2,20 @@
 # Makefile for the kernel tpm device drivers.
 #
 obj-$(CONFIG_TCG_TPM) += tpm.o
+tpm-y := tpm-interface.o
+tpm-$(CONFIG_ACPI) += tpm_ppi.o
+
 ifdef CONFIG_ACPI
-       obj-$(CONFIG_TCG_TPM) += tpm_bios.o
-       tpm_bios-objs += tpm_eventlog.o tpm_acpi.o tpm_ppi.o
+       tpm-y += tpm_eventlog.o tpm_acpi.o
 else
 ifdef CONFIG_TCG_IBMVTPM
-       obj-$(CONFIG_TCG_TPM) += tpm_bios.o
-       tpm_bios-objs += tpm_eventlog.o tpm_of.o
+       tpm-y += tpm_eventlog.o tpm_of.o
 endif
 endif
 obj-$(CONFIG_TCG_TIS) += tpm_tis.o
+obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o
 obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
+obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o
 obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
 obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
 obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
similarity index 93%
rename from drivers/char/tpm/tpm.c
rename to drivers/char/tpm/tpm-interface.c
index e3c974a..6ae41d3 100644 (file)
  * Maintained by: <tpmdd-devel@lists.sourceforge.net>
  *
  * Device driver for TCG/TCPA TPM (trusted platform module).
- * Specifications at www.trustedcomputinggroup.org      
+ * Specifications at www.trustedcomputinggroup.org
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
  * published by the Free Software Foundation, version 2 of the
  * License.
- * 
+ *
  * Note, the TPM chip is not interrupt driven (only polling)
  * and can have very long timeouts (minutes!). Hence the unusual
  * calls to msleep.
@@ -371,13 +371,14 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
                return -ENODATA;
        if (count > bufsiz) {
                dev_err(chip->dev,
-                       "invalid count value %x %zx \n", count, bufsiz);
+                       "invalid count value %x %zx\n", count, bufsiz);
                return -E2BIG;
        }
 
        mutex_lock(&chip->tpm_mutex);
 
-       if ((rc = chip->vendor.send(chip, (u8 *) buf, count)) < 0) {
+       rc = chip->vendor.send(chip, (u8 *) buf, count);
+       if (rc < 0) {
                dev_err(chip->dev,
                        "tpm_transmit: tpm_send: error %zd\n", rc);
                goto out;
@@ -444,7 +445,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
 {
        int err;
 
-       len = tpm_transmit(chip,(u8 *) cmd, len);
+       len = tpm_transmit(chip, (u8 *) cmd, len);
        if (len <  0)
                return len;
        else if (len < TPM_HEADER_SIZE)
@@ -658,7 +659,7 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
        return rc;
 }
 
-ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
+ssize_t tpm_show_enabled(struct device *dev, struct device_attribute *attr,
                        char *buf)
 {
        cap_t cap;
@@ -674,7 +675,7 @@ ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
 }
 EXPORT_SYMBOL_GPL(tpm_show_enabled);
 
-ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
+ssize_t tpm_show_active(struct device *dev, struct device_attribute *attr,
                        char *buf)
 {
        cap_t cap;
@@ -690,7 +691,7 @@ ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
 }
 EXPORT_SYMBOL_GPL(tpm_show_active);
 
-ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
+ssize_t tpm_show_owned(struct device *dev, struct device_attribute *attr,
                        char *buf)
 {
        cap_t cap;
@@ -706,8 +707,8 @@ ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
 }
 EXPORT_SYMBOL_GPL(tpm_show_owned);
 
-ssize_t tpm_show_temp_deactivated(struct device * dev,
-                               struct device_attribute * attr, char *buf)
+ssize_t tpm_show_temp_deactivated(struct device *dev,
+                               struct device_attribute *attr, char *buf)
 {
        cap_t cap;
        ssize_t rc;
@@ -769,10 +770,10 @@ static int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
 
 /**
  * tpm_pcr_read - read a pcr value
- * @chip_num:  tpm idx # or ANY
+ * @chip_num:  tpm idx # or ANY
  * @pcr_idx:   pcr idx to retrieve
- * @res_buf:   TPM_PCR value
- *             size of res_buf is 20 bytes (or NULL if you don't care)
+ * @res_buf:   TPM_PCR value
+ *             size of res_buf is 20 bytes (or NULL if you don't care)
  *
  * The TPM driver should be built-in, but for whatever reason it
  * isn't, protect against the chip disappearing, by incrementing
@@ -794,9 +795,9 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read);
 
 /**
  * tpm_pcr_extend - extend pcr value with hash
- * @chip_num:  tpm idx # or AN&
+ * @chip_num:  tpm idx # or AN&
  * @pcr_idx:   pcr idx to extend
- * @hash:      hash value used to extend pcr value
+ * @hash:      hash value used to extend pcr value
  *
  * The TPM driver should be built-in, but for whatever reason it
  * isn't, protect against the chip disappearing, by incrementing
@@ -847,8 +848,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
        unsigned long duration;
        struct tpm_cmd_t cmd;
 
-       duration = tpm_calc_ordinal_duration(chip,
-                                            TPM_ORD_CONTINUE_SELFTEST);
+       duration = tpm_calc_ordinal_duration(chip, TPM_ORD_CONTINUE_SELFTEST);
 
        loops = jiffies_to_msecs(duration) / delay_msec;
 
@@ -965,12 +965,12 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
        if (err)
                goto out;
 
-       /* 
+       /*
           ignore header 10 bytes
           algorithm 32 bits (1 == RSA )
           encscheme 16 bits
           sigscheme 16 bits
-          parameters (RSA 12->bytes: keybit, #primes, expbit)  
+          parameters (RSA 12->bytes: keybit, #primes, expbit)
           keylenbytes 32 bits
           256 byte modulus
           ignore checksum 20 bytes
@@ -1020,43 +1020,33 @@ ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
        str += sprintf(str, "Manufacturer: 0x%x\n",
                       be32_to_cpu(cap.manufacturer_id));
 
-       rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
-                       "attempting to determine the 1.1 version");
-       if (rc)
-               return 0;
-       str += sprintf(str,
-                      "TCG version: %d.%d\nFirmware version: %d.%d\n",
-                      cap.tpm_version.Major, cap.tpm_version.Minor,
-                      cap.tpm_version.revMajor, cap.tpm_version.revMinor);
-       return str - buf;
-}
-EXPORT_SYMBOL_GPL(tpm_show_caps);
-
-ssize_t tpm_show_caps_1_2(struct device * dev,
-                         struct device_attribute * attr, char *buf)
-{
-       cap_t cap;
-       ssize_t rc;
-       char *str = buf;
-
-       rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
-                       "attempting to determine the manufacturer");
-       if (rc)
-               return 0;
-       str += sprintf(str, "Manufacturer: 0x%x\n",
-                      be32_to_cpu(cap.manufacturer_id));
+       /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */
        rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
                         "attempting to determine the 1.2 version");
-       if (rc)
-               return 0;
-       str += sprintf(str,
-                      "TCG version: %d.%d\nFirmware version: %d.%d\n",
-                      cap.tpm_version_1_2.Major, cap.tpm_version_1_2.Minor,
-                      cap.tpm_version_1_2.revMajor,
-                      cap.tpm_version_1_2.revMinor);
+       if (!rc) {
+               str += sprintf(str,
+                              "TCG version: %d.%d\nFirmware version: %d.%d\n",
+                              cap.tpm_version_1_2.Major,
+                              cap.tpm_version_1_2.Minor,
+                              cap.tpm_version_1_2.revMajor,
+                              cap.tpm_version_1_2.revMinor);
+       } else {
+               /* Otherwise just use TPM_STRUCT_VER */
+               rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
+                               "attempting to determine the 1.1 version");
+               if (rc)
+                       return 0;
+               str += sprintf(str,
+                              "TCG version: %d.%d\nFirmware version: %d.%d\n",
+                              cap.tpm_version.Major,
+                              cap.tpm_version.Minor,
+                              cap.tpm_version.revMajor,
+                              cap.tpm_version.revMinor);
+       }
+
        return str - buf;
 }
-EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
+EXPORT_SYMBOL_GPL(tpm_show_caps);
 
 ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr,
                          char *buf)
@@ -1102,8 +1092,8 @@ ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
 }
 EXPORT_SYMBOL_GPL(tpm_store_cancel);
 
-static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, bool check_cancel,
-                                  bool *canceled)
+static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
+                                       bool check_cancel, bool *canceled)
 {
        u8 status = chip->vendor.status(chip);
 
@@ -1170,38 +1160,25 @@ EXPORT_SYMBOL_GPL(wait_for_tpm_stat);
  */
 int tpm_open(struct inode *inode, struct file *file)
 {
-       int minor = iminor(inode);
-       struct tpm_chip *chip = NULL, *pos;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
-               if (pos->vendor.miscdev.minor == minor) {
-                       chip = pos;
-                       get_device(chip->dev);
-                       break;
-               }
-       }
-       rcu_read_unlock();
-
-       if (!chip)
-               return -ENODEV;
+       struct miscdevice *misc = file->private_data;
+       struct tpm_chip *chip = container_of(misc, struct tpm_chip,
+                                            vendor.miscdev);
 
        if (test_and_set_bit(0, &chip->is_open)) {
                dev_dbg(chip->dev, "Another process owns this TPM\n");
-               put_device(chip->dev);
                return -EBUSY;
        }
 
        chip->data_buffer = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
        if (chip->data_buffer == NULL) {
                clear_bit(0, &chip->is_open);
-               put_device(chip->dev);
                return -ENOMEM;
        }
 
        atomic_set(&chip->data_pending, 0);
 
        file->private_data = chip;
+       get_device(chip->dev);
        return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_open);
@@ -1463,7 +1440,6 @@ void tpm_dev_vendor_release(struct tpm_chip *chip)
                chip->vendor.release(chip->dev);
 
        clear_bit(chip->dev_num, dev_mask);
-       kfree(chip->vendor.miscdev.name);
 }
 EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
 
@@ -1487,7 +1463,7 @@ void tpm_dev_release(struct device *dev)
 EXPORT_SYMBOL_GPL(tpm_dev_release);
 
 /*
- * Called from tpm_<specific>.c probe function only for devices 
+ * Called from tpm_<specific>.c probe function only for devices
  * the driver has determined it should claim.  Prior to calling
  * this function the specific probe function has called pci_enable_device
  * upon errant exit from this function specific probe function should call
@@ -1496,17 +1472,13 @@ EXPORT_SYMBOL_GPL(tpm_dev_release);
 struct tpm_chip *tpm_register_hardware(struct device *dev,
                                        const struct tpm_vendor_specific *entry)
 {
-#define DEVNAME_SIZE 7
-
-       char *devname;
        struct tpm_chip *chip;
 
        /* Driver specific per-device data */
        chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-       devname = kmalloc(DEVNAME_SIZE, GFP_KERNEL);
 
-       if (chip == NULL || devname == NULL)
-               goto out_free;
+       if (chip == NULL)
+               return NULL;
 
        mutex_init(&chip->buffer_mutex);
        mutex_init(&chip->tpm_mutex);
@@ -1531,8 +1503,9 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
 
        set_bit(chip->dev_num, dev_mask);
 
-       scnprintf(devname, DEVNAME_SIZE, "%s%d", "tpm", chip->dev_num);
-       chip->vendor.miscdev.name = devname;
+       scnprintf(chip->devname, sizeof(chip->devname), "%s%d", "tpm",
+                 chip->dev_num);
+       chip->vendor.miscdev.name = chip->devname;
 
        chip->vendor.miscdev.parent = dev;
        chip->dev = get_device(dev);
@@ -1558,7 +1531,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
                goto put_device;
        }
 
-       chip->bios_dir = tpm_bios_log_setup(devname);
+       chip->bios_dir = tpm_bios_log_setup(chip->devname);
 
        /* Make chip available */
        spin_lock(&driver_lock);
@@ -1571,7 +1544,6 @@ put_device:
        put_device(chip->dev);
 out_free:
        kfree(chip);
-       kfree(devname);
        return NULL;
 }
 EXPORT_SYMBOL_GPL(tpm_register_hardware);
index a7bfc17..f328478 100644 (file)
@@ -59,8 +59,6 @@ extern ssize_t tpm_show_pcrs(struct device *, struct device_attribute *attr,
                                char *);
 extern ssize_t tpm_show_caps(struct device *, struct device_attribute *attr,
                                char *);
-extern ssize_t tpm_show_caps_1_2(struct device *, struct device_attribute *attr,
-                               char *);
 extern ssize_t tpm_store_cancel(struct device *, struct device_attribute *attr,
                                const char *, size_t);
 extern ssize_t tpm_show_enabled(struct device *, struct device_attribute *attr,
@@ -122,6 +120,7 @@ struct tpm_chip {
        struct device *dev;     /* Device stuff */
 
        int dev_num;            /* /dev/tpm# */
+       char devname[7];
        unsigned long is_open;  /* only one allowed */
        int time_expired;
 
index 99d6820..c9a528d 100644 (file)
@@ -202,7 +202,7 @@ static int __init init_atmel(void)
 
        have_region =
            (atmel_request_region
-            (tpm_atmel.base, region_size, "tpm_atmel0") == NULL) ? 0 : 1;
+            (base, region_size, "tpm_atmel0") == NULL) ? 0 : 1;
 
        pdev = platform_device_register_simple("tpm_atmel", -1, NULL, 0);
        if (IS_ERR(pdev)) {
index 84ddc55..59f7cb2 100644 (file)
@@ -406,7 +406,6 @@ out_tpm:
 out:
        return NULL;
 }
-EXPORT_SYMBOL_GPL(tpm_bios_log_setup);
 
 void tpm_bios_log_teardown(struct dentry **lst)
 {
@@ -415,5 +414,3 @@ void tpm_bios_log_teardown(struct dentry **lst)
        for (i = 0; i < 3; i++)
                securityfs_remove(lst[i]);
 }
-EXPORT_SYMBOL_GPL(tpm_bios_log_teardown);
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
new file mode 100644 (file)
index 0000000..c3cd7fe
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * ATMEL I2C TPM AT97SC3204T
+ *
+ * Copyright (C) 2012 V Lab Technologies
+ *  Teddy Reed <teddy@prosauce.org>
+ * Copyright (C) 2013, Obsidian Research Corp.
+ *  Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
+ * Device driver for ATMEL I2C TPMs.
+ *
+ * Teddy Reed determined the basic I2C command flow, unlike other I2C TPM
+ * devices the raw TCG formatted TPM command data is written via I2C and then
+ * raw TCG formatted TPM command data is returned via I2C.
+ *
+ * TGC status/locality/etc functions seen in the LPC implementation do not
+ * seem to be present.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/>.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include "tpm.h"
+
+#define I2C_DRIVER_NAME "tpm_i2c_atmel"
+
+#define TPM_I2C_SHORT_TIMEOUT  750     /* ms */
+#define TPM_I2C_LONG_TIMEOUT   2000    /* 2 sec */
+
+#define ATMEL_STS_OK 1
+
+struct priv_data {
+       size_t len;
+       /* This is the amount we read on the first try. 25 was chosen to fit a
+        * fair number of read responses in the buffer so a 2nd retry can be
+        * avoided in small message cases. */
+       u8 buffer[sizeof(struct tpm_output_header) + 25];
+};
+
+static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+       struct priv_data *priv = chip->vendor.priv;
+       struct i2c_client *client = to_i2c_client(chip->dev);
+       s32 status;
+
+       priv->len = 0;
+
+       if (len <= 2)
+               return -EIO;
+
+       status = i2c_master_send(client, buf, len);
+
+       dev_dbg(chip->dev,
+               "%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
+               (int)min_t(size_t, 64, len), buf, len, status);
+       return status;
+}
+
+static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       struct priv_data *priv = chip->vendor.priv;
+       struct i2c_client *client = to_i2c_client(chip->dev);
+       struct tpm_output_header *hdr =
+               (struct tpm_output_header *)priv->buffer;
+       u32 expected_len;
+       int rc;
+
+       if (priv->len == 0)
+               return -EIO;
+
+       /* Get the message size from the message header, if we didn't get the
+        * whole message in read_status then we need to re-read the
+        * message. */
+       expected_len = be32_to_cpu(hdr->length);
+       if (expected_len > count)
+               return -ENOMEM;
+
+       if (priv->len >= expected_len) {
+               dev_dbg(chip->dev,
+                       "%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
+                       (int)min_t(size_t, 64, expected_len), buf, count,
+                       expected_len);
+               memcpy(buf, priv->buffer, expected_len);
+               return expected_len;
+       }
+
+       rc = i2c_master_recv(client, buf, expected_len);
+       dev_dbg(chip->dev,
+               "%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
+               (int)min_t(size_t, 64, expected_len), buf, count,
+               expected_len);
+       return rc;
+}
+
+static void i2c_atmel_cancel(struct tpm_chip *chip)
+{
+       dev_err(chip->dev, "TPM operation cancellation was requested, but is not supported");
+}
+
+static u8 i2c_atmel_read_status(struct tpm_chip *chip)
+{
+       struct priv_data *priv = chip->vendor.priv;
+       struct i2c_client *client = to_i2c_client(chip->dev);
+       int rc;
+
+       /* The TPM fails the I2C read until it is ready, so we do the entire
+        * transfer here and buffer it locally. This way the common code can
+        * properly handle the timeouts. */
+       priv->len = 0;
+       memset(priv->buffer, 0, sizeof(priv->buffer));
+
+
+       /* Once the TPM has completed the command the command remains readable
+        * until another command is issued. */
+       rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
+       dev_dbg(chip->dev,
+               "%s: sts=%d", __func__, rc);
+       if (rc <= 0)
+               return 0;
+
+       priv->len = rc;
+
+       return ATMEL_STS_OK;
+}
+
+static const struct file_operations i2c_atmel_ops = {
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .open = tpm_open,
+       .read = tpm_read,
+       .write = tpm_write,
+       .release = tpm_release,
+};
+
+static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
+static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
+static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
+static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
+static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
+static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
+static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
+static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
+static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
+
+static struct attribute *i2c_atmel_attrs[] = {
+       &dev_attr_pubek.attr,
+       &dev_attr_pcrs.attr,
+       &dev_attr_enabled.attr,
+       &dev_attr_active.attr,
+       &dev_attr_owned.attr,
+       &dev_attr_temp_deactivated.attr,
+       &dev_attr_caps.attr,
+       &dev_attr_cancel.attr,
+       &dev_attr_durations.attr,
+       &dev_attr_timeouts.attr,
+       NULL,
+};
+
+static struct attribute_group i2c_atmel_attr_grp = {
+       .attrs = i2c_atmel_attrs
+};
+
+static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status)
+{
+       return 0;
+}
+
+static const struct tpm_vendor_specific i2c_atmel = {
+       .status = i2c_atmel_read_status,
+       .recv = i2c_atmel_recv,
+       .send = i2c_atmel_send,
+       .cancel = i2c_atmel_cancel,
+       .req_complete_mask = ATMEL_STS_OK,
+       .req_complete_val = ATMEL_STS_OK,
+       .req_canceled = i2c_atmel_req_canceled,
+       .attr_group = &i2c_atmel_attr_grp,
+       .miscdev.fops = &i2c_atmel_ops,
+};
+
+static int i2c_atmel_probe(struct i2c_client *client,
+                          const struct i2c_device_id *id)
+{
+       int rc;
+       struct tpm_chip *chip;
+       struct device *dev = &client->dev;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               return -ENODEV;
+
+       chip = tpm_register_hardware(dev, &i2c_atmel);
+       if (!chip) {
+               dev_err(dev, "%s() error in tpm_register_hardware\n", __func__);
+               return -ENODEV;
+       }
+
+       chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
+                                        GFP_KERNEL);
+
+       /* Default timeouts */
+       chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+       chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
+       chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+       chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+       chip->vendor.irq = 0;
+
+       /* There is no known way to probe for this device, and all version
+        * information seems to be read via TPM commands. Thus we rely on the
+        * TPM startup process in the common code to detect the device. */
+       if (tpm_get_timeouts(chip)) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       if (tpm_do_selftest(chip)) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       tpm_dev_vendor_release(chip);
+       tpm_remove_hardware(chip->dev);
+       return rc;
+}
+
+static int i2c_atmel_remove(struct i2c_client *client)
+{
+       struct device *dev = &(client->dev);
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+
+       if (chip)
+               tpm_dev_vendor_release(chip);
+       tpm_remove_hardware(dev);
+       kfree(chip);
+       return 0;
+}
+
+static const struct i2c_device_id i2c_atmel_id[] = {
+       {I2C_DRIVER_NAME, 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, i2c_atmel_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id i2c_atmel_of_match[] = {
+       {.compatible = "atmel,at97sc3204t"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, i2c_atmel_of_match);
+#endif
+
+static SIMPLE_DEV_PM_OPS(i2c_atmel_pm_ops, tpm_pm_suspend, tpm_pm_resume);
+
+static struct i2c_driver i2c_atmel_driver = {
+       .id_table = i2c_atmel_id,
+       .probe = i2c_atmel_probe,
+       .remove = i2c_atmel_remove,
+       .driver = {
+               .name = I2C_DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .pm = &i2c_atmel_pm_ops,
+               .of_match_table = of_match_ptr(i2c_atmel_of_match),
+       },
+};
+
+module_i2c_driver(i2c_atmel_driver);
+
+MODULE_AUTHOR("Jason Gunthorpe <jgunthorpe@obsidianresearch.com>");
+MODULE_DESCRIPTION("Atmel TPM I2C Driver");
+MODULE_LICENSE("GPL");
index b8735de..fefd2aa 100644 (file)
@@ -581,7 +581,7 @@ static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
 static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
 static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
 static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
-static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
 static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
 static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
 static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
@@ -685,7 +685,6 @@ out_vendor:
        chip->dev->release = NULL;
        chip->release = NULL;
        tpm_dev.client = NULL;
-       dev_set_drvdata(chip->dev, chip);
 out_err:
        return rc;
 }
@@ -766,7 +765,6 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
        chip->dev->release = NULL;
        chip->release = NULL;
        tpm_dev.client = NULL;
-       dev_set_drvdata(chip->dev, chip);
 
        return 0;
 }
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
new file mode 100644 (file)
index 0000000..6276fea
--- /dev/null
@@ -0,0 +1,710 @@
+/******************************************************************************
+ * Nuvoton TPM I2C Device Driver Interface for WPCT301/NPCT501,
+ * based on the TCG TPM Interface Spec version 1.2.
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * Copyright (C) 2011, Nuvoton Technology Corporation.
+ *  Dan Morav <dan.morav@nuvoton.com>
+ * Copyright (C) 2013, Obsidian Research Corp.
+ *  Jason Gunthorpe <jgunthorpe@obsidianresearch.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, see http://www.gnu.org/licenses/>.
+ *
+ * Nuvoton contact information: APC.Support@nuvoton.com
+ *****************************************************************************/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include "tpm.h"
+
+/* I2C interface offsets */
+#define TPM_STS                0x00
+#define TPM_BURST_COUNT        0x01
+#define TPM_DATA_FIFO_W        0x20
+#define TPM_DATA_FIFO_R        0x40
+#define TPM_VID_DID_RID        0x60
+/* TPM command header size */
+#define TPM_HEADER_SIZE        10
+#define TPM_RETRY      5
+/*
+ * I2C bus device maximum buffer size w/o counting I2C address or command
+ * i.e. max size required for I2C write is 34 = addr, command, 32 bytes data
+ */
+#define TPM_I2C_MAX_BUF_SIZE           32
+#define TPM_I2C_RETRY_COUNT            32
+#define TPM_I2C_BUS_DELAY              1       /* msec */
+#define TPM_I2C_RETRY_DELAY_SHORT      2       /* msec */
+#define TPM_I2C_RETRY_DELAY_LONG       10      /* msec */
+
+#define I2C_DRIVER_NAME "tpm_i2c_nuvoton"
+
+struct priv_data {
+       unsigned int intrs;
+};
+
+static s32 i2c_nuvoton_read_buf(struct i2c_client *client, u8 offset, u8 size,
+                               u8 *data)
+{
+       s32 status;
+
+       status = i2c_smbus_read_i2c_block_data(client, offset, size, data);
+       dev_dbg(&client->dev,
+               "%s(offset=%u size=%u data=%*ph) -> sts=%d\n", __func__,
+               offset, size, (int)size, data, status);
+       return status;
+}
+
+static s32 i2c_nuvoton_write_buf(struct i2c_client *client, u8 offset, u8 size,
+                                u8 *data)
+{
+       s32 status;
+
+       status = i2c_smbus_write_i2c_block_data(client, offset, size, data);
+       dev_dbg(&client->dev,
+               "%s(offset=%u size=%u data=%*ph) -> sts=%d\n", __func__,
+               offset, size, (int)size, data, status);
+       return status;
+}
+
+#define TPM_STS_VALID          0x80
+#define TPM_STS_COMMAND_READY  0x40
+#define TPM_STS_GO             0x20
+#define TPM_STS_DATA_AVAIL     0x10
+#define TPM_STS_EXPECT         0x08
+#define TPM_STS_RESPONSE_RETRY 0x02
+#define TPM_STS_ERR_VAL        0x07    /* bit2...bit0 reads always 0 */
+
+#define TPM_I2C_SHORT_TIMEOUT  750     /* ms */
+#define TPM_I2C_LONG_TIMEOUT   2000    /* 2 sec */
+
+/* read TPM_STS register */
+static u8 i2c_nuvoton_read_status(struct tpm_chip *chip)
+{
+       struct i2c_client *client = to_i2c_client(chip->dev);
+       s32 status;
+       u8 data;
+
+       status = i2c_nuvoton_read_buf(client, TPM_STS, 1, &data);
+       if (status <= 0) {
+               dev_err(chip->dev, "%s() error return %d\n", __func__,
+                       status);
+               data = TPM_STS_ERR_VAL;
+       }
+
+       return data;
+}
+
+/* write byte to TPM_STS register */
+static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data)
+{
+       s32 status;
+       int i;
+
+       /* this causes the current command to be aborted */
+       for (i = 0, status = -1; i < TPM_I2C_RETRY_COUNT && status < 0; i++) {
+               status = i2c_nuvoton_write_buf(client, TPM_STS, 1, &data);
+               msleep(TPM_I2C_BUS_DELAY);
+       }
+       return status;
+}
+
+/* write commandReady to TPM_STS register */
+static void i2c_nuvoton_ready(struct tpm_chip *chip)
+{
+       struct i2c_client *client = to_i2c_client(chip->dev);
+       s32 status;
+
+       /* this causes the current command to be aborted */
+       status = i2c_nuvoton_write_status(client, TPM_STS_COMMAND_READY);
+       if (status < 0)
+               dev_err(chip->dev,
+                       "%s() fail to write TPM_STS.commandReady\n", __func__);
+}
+
+/* read burstCount field from TPM_STS register
+ * return -1 on fail to read */
+static int i2c_nuvoton_get_burstcount(struct i2c_client *client,
+                                     struct tpm_chip *chip)
+{
+       unsigned long stop = jiffies + chip->vendor.timeout_d;
+       s32 status;
+       int burst_count = -1;
+       u8 data;
+
+       /* wait for burstcount to be non-zero */
+       do {
+               /* in I2C burstCount is 1 byte */
+               status = i2c_nuvoton_read_buf(client, TPM_BURST_COUNT, 1,
+                                             &data);
+               if (status > 0 && data > 0) {
+                       burst_count = min_t(u8, TPM_I2C_MAX_BUF_SIZE, data);
+                       break;
+               }
+               msleep(TPM_I2C_BUS_DELAY);
+       } while (time_before(jiffies, stop));
+
+       return burst_count;
+}
+
+/*
+ * WPCT301/NPCT501 SINT# supports only dataAvail
+ * any call to this function which is not waiting for dataAvail will
+ * set queue to NULL to avoid waiting for interrupt
+ */
+static bool i2c_nuvoton_check_status(struct tpm_chip *chip, u8 mask, u8 value)
+{
+       u8 status = i2c_nuvoton_read_status(chip);
+       return (status != TPM_STS_ERR_VAL) && ((status & mask) == value);
+}
+
+static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
+                                    u32 timeout, wait_queue_head_t *queue)
+{
+       if (chip->vendor.irq && queue) {
+               s32 rc;
+               DEFINE_WAIT(wait);
+               struct priv_data *priv = chip->vendor.priv;
+               unsigned int cur_intrs = priv->intrs;
+
+               enable_irq(chip->vendor.irq);
+               rc = wait_event_interruptible_timeout(*queue,
+                                                     cur_intrs != priv->intrs,
+                                                     timeout);
+               if (rc > 0)
+                       return 0;
+               /* At this point we know that the SINT pin is asserted, so we
+                * do not need to do i2c_nuvoton_check_status */
+       } else {
+               unsigned long ten_msec, stop;
+               bool status_valid;
+
+               /* check current status */
+               status_valid = i2c_nuvoton_check_status(chip, mask, value);
+               if (status_valid)
+                       return 0;
+
+               /* use polling to wait for the event */
+               ten_msec = jiffies + msecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG);
+               stop = jiffies + timeout;
+               do {
+                       if (time_before(jiffies, ten_msec))
+                               msleep(TPM_I2C_RETRY_DELAY_SHORT);
+                       else
+                               msleep(TPM_I2C_RETRY_DELAY_LONG);
+                       status_valid = i2c_nuvoton_check_status(chip, mask,
+                                                               value);
+                       if (status_valid)
+                               return 0;
+               } while (time_before(jiffies, stop));
+       }
+       dev_err(chip->dev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
+               value);
+       return -ETIMEDOUT;
+}
+
+/* wait for dataAvail field to be set in the TPM_STS register */
+static int i2c_nuvoton_wait_for_data_avail(struct tpm_chip *chip, u32 timeout,
+                                          wait_queue_head_t *queue)
+{
+       return i2c_nuvoton_wait_for_stat(chip,
+                                        TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+                                        TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+                                        timeout, queue);
+}
+
+/* Read @count bytes into @buf from TPM_RD_FIFO register */
+static int i2c_nuvoton_recv_data(struct i2c_client *client,
+                                struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       s32 rc;
+       int burst_count, bytes2read, size = 0;
+
+       while (size < count &&
+              i2c_nuvoton_wait_for_data_avail(chip,
+                                              chip->vendor.timeout_c,
+                                              &chip->vendor.read_queue) == 0) {
+               burst_count = i2c_nuvoton_get_burstcount(client, chip);
+               if (burst_count < 0) {
+                       dev_err(chip->dev,
+                               "%s() fail to read burstCount=%d\n", __func__,
+                               burst_count);
+                       return -EIO;
+               }
+               bytes2read = min_t(size_t, burst_count, count - size);
+               rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_R,
+                                         bytes2read, &buf[size]);
+               if (rc < 0) {
+                       dev_err(chip->dev,
+                               "%s() fail on i2c_nuvoton_read_buf()=%d\n",
+                               __func__, rc);
+                       return -EIO;
+               }
+               dev_dbg(chip->dev, "%s(%d):", __func__, bytes2read);
+               size += bytes2read;
+       }
+
+       return size;
+}
+
+/* Read TPM command results */
+static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       struct device *dev = chip->dev;
+       struct i2c_client *client = to_i2c_client(dev);
+       s32 rc;
+       int expected, status, burst_count, retries, size = 0;
+
+       if (count < TPM_HEADER_SIZE) {
+               i2c_nuvoton_ready(chip);    /* return to idle */
+               dev_err(dev, "%s() count < header size\n", __func__);
+               return -EIO;
+       }
+       for (retries = 0; retries < TPM_RETRY; retries++) {
+               if (retries > 0) {
+                       /* if this is not the first trial, set responseRetry */
+                       i2c_nuvoton_write_status(client,
+                                                TPM_STS_RESPONSE_RETRY);
+               }
+               /*
+                * read first available (> 10 bytes), including:
+                * tag, paramsize, and result
+                */
+               status = i2c_nuvoton_wait_for_data_avail(
+                       chip, chip->vendor.timeout_c, &chip->vendor.read_queue);
+               if (status != 0) {
+                       dev_err(dev, "%s() timeout on dataAvail\n", __func__);
+                       size = -ETIMEDOUT;
+                       continue;
+               }
+               burst_count = i2c_nuvoton_get_burstcount(client, chip);
+               if (burst_count < 0) {
+                       dev_err(dev, "%s() fail to get burstCount\n", __func__);
+                       size = -EIO;
+                       continue;
+               }
+               size = i2c_nuvoton_recv_data(client, chip, buf,
+                                            burst_count);
+               if (size < TPM_HEADER_SIZE) {
+                       dev_err(dev, "%s() fail to read header\n", __func__);
+                       size = -EIO;
+                       continue;
+               }
+               /*
+                * convert number of expected bytes field from big endian 32 bit
+                * to machine native
+                */
+               expected = be32_to_cpu(*(__be32 *) (buf + 2));
+               if (expected > count) {
+                       dev_err(dev, "%s() expected > count\n", __func__);
+                       size = -EIO;
+                       continue;
+               }
+               rc = i2c_nuvoton_recv_data(client, chip, &buf[size],
+                                          expected - size);
+               size += rc;
+               if (rc < 0 || size < expected) {
+                       dev_err(dev, "%s() fail to read remainder of result\n",
+                               __func__);
+                       size = -EIO;
+                       continue;
+               }
+               if (i2c_nuvoton_wait_for_stat(
+                           chip, TPM_STS_VALID | TPM_STS_DATA_AVAIL,
+                           TPM_STS_VALID, chip->vendor.timeout_c,
+                           NULL)) {
+                       dev_err(dev, "%s() error left over data\n", __func__);
+                       size = -ETIMEDOUT;
+                       continue;
+               }
+               break;
+       }
+       i2c_nuvoton_ready(chip);
+       dev_dbg(chip->dev, "%s() -> %d\n", __func__, size);
+       return size;
+}
+
+/*
+ * Send TPM command.
+ *
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+       struct device *dev = chip->dev;
+       struct i2c_client *client = to_i2c_client(dev);
+       u32 ordinal;
+       size_t count = 0;
+       int burst_count, bytes2write, retries, rc = -EIO;
+
+       for (retries = 0; retries < TPM_RETRY; retries++) {
+               i2c_nuvoton_ready(chip);
+               if (i2c_nuvoton_wait_for_stat(chip, TPM_STS_COMMAND_READY,
+                                             TPM_STS_COMMAND_READY,
+                                             chip->vendor.timeout_b, NULL)) {
+                       dev_err(dev, "%s() timeout on commandReady\n",
+                               __func__);
+                       rc = -EIO;
+                       continue;
+               }
+               rc = 0;
+               while (count < len - 1) {
+                       burst_count = i2c_nuvoton_get_burstcount(client,
+                                                                chip);
+                       if (burst_count < 0) {
+                               dev_err(dev, "%s() fail get burstCount\n",
+                                       __func__);
+                               rc = -EIO;
+                               break;
+                       }
+                       bytes2write = min_t(size_t, burst_count,
+                                           len - 1 - count);
+                       rc = i2c_nuvoton_write_buf(client, TPM_DATA_FIFO_W,
+                                                  bytes2write, &buf[count]);
+                       if (rc < 0) {
+                               dev_err(dev, "%s() fail i2cWriteBuf\n",
+                                       __func__);
+                               break;
+                       }
+                       dev_dbg(dev, "%s(%d):", __func__, bytes2write);
+                       count += bytes2write;
+                       rc = i2c_nuvoton_wait_for_stat(chip,
+                                                      TPM_STS_VALID |
+                                                      TPM_STS_EXPECT,
+                                                      TPM_STS_VALID |
+                                                      TPM_STS_EXPECT,
+                                                      chip->vendor.timeout_c,
+                                                      NULL);
+                       if (rc < 0) {
+                               dev_err(dev, "%s() timeout on Expect\n",
+                                       __func__);
+                               rc = -ETIMEDOUT;
+                               break;
+                       }
+               }
+               if (rc < 0)
+                       continue;
+
+               /* write last byte */
+               rc = i2c_nuvoton_write_buf(client, TPM_DATA_FIFO_W, 1,
+                                          &buf[count]);
+               if (rc < 0) {
+                       dev_err(dev, "%s() fail to write last byte\n",
+                               __func__);
+                       rc = -EIO;
+                       continue;
+               }
+               dev_dbg(dev, "%s(last): %02x", __func__, buf[count]);
+               rc = i2c_nuvoton_wait_for_stat(chip,
+                                              TPM_STS_VALID | TPM_STS_EXPECT,
+                                              TPM_STS_VALID,
+                                              chip->vendor.timeout_c, NULL);
+               if (rc) {
+                       dev_err(dev, "%s() timeout on Expect to clear\n",
+                               __func__);
+                       rc = -ETIMEDOUT;
+                       continue;
+               }
+               break;
+       }
+       if (rc < 0) {
+               /* retries == TPM_RETRY */
+               i2c_nuvoton_ready(chip);
+               return rc;
+       }
+       /* execute the TPM command */
+       rc = i2c_nuvoton_write_status(client, TPM_STS_GO);
+       if (rc < 0) {
+               dev_err(dev, "%s() fail to write Go\n", __func__);
+               i2c_nuvoton_ready(chip);
+               return rc;
+       }
+       ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+       rc = i2c_nuvoton_wait_for_data_avail(chip,
+                                            tpm_calc_ordinal_duration(chip,
+                                                                      ordinal),
+                                            &chip->vendor.read_queue);
+       if (rc) {
+               dev_err(dev, "%s() timeout command duration\n", __func__);
+               i2c_nuvoton_ready(chip);
+               return rc;
+       }
+
+       dev_dbg(dev, "%s() -> %zd\n", __func__, len);
+       return len;
+}
+
+static bool i2c_nuvoton_req_canceled(struct tpm_chip *chip, u8 status)
+{
+       return (status == TPM_STS_COMMAND_READY);
+}
+
+static const struct file_operations i2c_nuvoton_ops = {
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .open = tpm_open,
+       .read = tpm_read,
+       .write = tpm_write,
+       .release = tpm_release,
+};
+
+static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
+static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
+static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
+static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
+static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
+static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
+static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
+static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
+static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
+
+static struct attribute *i2c_nuvoton_attrs[] = {
+       &dev_attr_pubek.attr,
+       &dev_attr_pcrs.attr,
+       &dev_attr_enabled.attr,
+       &dev_attr_active.attr,
+       &dev_attr_owned.attr,
+       &dev_attr_temp_deactivated.attr,
+       &dev_attr_caps.attr,
+       &dev_attr_cancel.attr,
+       &dev_attr_durations.attr,
+       &dev_attr_timeouts.attr,
+       NULL,
+};
+
+static struct attribute_group i2c_nuvoton_attr_grp = {
+       .attrs = i2c_nuvoton_attrs
+};
+
+static const struct tpm_vendor_specific tpm_i2c = {
+       .status = i2c_nuvoton_read_status,
+       .recv = i2c_nuvoton_recv,
+       .send = i2c_nuvoton_send,
+       .cancel = i2c_nuvoton_ready,
+       .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+       .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+       .req_canceled = i2c_nuvoton_req_canceled,
+       .attr_group = &i2c_nuvoton_attr_grp,
+       .miscdev.fops = &i2c_nuvoton_ops,
+};
+
+/* The only purpose for the handler is to signal to any waiting threads that
+ * the interrupt is currently being asserted. The driver does not do any
+ * processing triggered by interrupts, and the chip provides no way to mask at
+ * the source (plus that would be slow over I2C). Run the IRQ as a one-shot,
+ * this means it cannot be shared. */
+static irqreturn_t i2c_nuvoton_int_handler(int dummy, void *dev_id)
+{
+       struct tpm_chip *chip = dev_id;
+       struct priv_data *priv = chip->vendor.priv;
+
+       priv->intrs++;
+       wake_up(&chip->vendor.read_queue);
+       disable_irq_nosync(chip->vendor.irq);
+       return IRQ_HANDLED;
+}
+
+static int get_vid(struct i2c_client *client, u32 *res)
+{
+       static const u8 vid_did_rid_value[] = { 0x50, 0x10, 0xfe };
+       u32 temp;
+       s32 rc;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+       rc = i2c_nuvoton_read_buf(client, TPM_VID_DID_RID, 4, (u8 *)&temp);
+       if (rc < 0)
+               return rc;
+
+       /* check WPCT301 values - ignore RID */
+       if (memcmp(&temp, vid_did_rid_value, sizeof(vid_did_rid_value))) {
+               /*
+                * f/w rev 2.81 has an issue where the VID_DID_RID is not
+                * reporting the right value. so give it another chance at
+                * offset 0x20 (FIFO_W).
+                */
+               rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_W, 4,
+                                         (u8 *) (&temp));
+               if (rc < 0)
+                       return rc;
+
+               /* check WPCT301 values - ignore RID */
+               if (memcmp(&temp, vid_did_rid_value,
+                          sizeof(vid_did_rid_value)))
+                       return -ENODEV;
+       }
+
+       *res = temp;
+       return 0;
+}
+
+static int i2c_nuvoton_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       int rc;
+       struct tpm_chip *chip;
+       struct device *dev = &client->dev;
+       u32 vid = 0;
+
+       rc = get_vid(client, &vid);
+       if (rc)
+               return rc;
+
+       dev_info(dev, "VID: %04X DID: %02X RID: %02X\n", (u16) vid,
+                (u8) (vid >> 16), (u8) (vid >> 24));
+
+       chip = tpm_register_hardware(dev, &tpm_i2c);
+       if (!chip) {
+               dev_err(dev, "%s() error in tpm_register_hardware\n", __func__);
+               return -ENODEV;
+       }
+
+       chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
+                                        GFP_KERNEL);
+       init_waitqueue_head(&chip->vendor.read_queue);
+       init_waitqueue_head(&chip->vendor.int_queue);
+
+       /* Default timeouts */
+       chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+       chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
+       chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+       chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+
+       /*
+        * I2C intfcaps (interrupt capabilitieis) in the chip are hard coded to:
+        *   TPM_INTF_INT_LEVEL_LOW | TPM_INTF_DATA_AVAIL_INT
+        * The IRQ should be set in the i2c_board_info (which is done
+        * automatically in of_i2c_register_devices, for device tree users */
+       chip->vendor.irq = client->irq;
+
+       if (chip->vendor.irq) {
+               dev_dbg(dev, "%s() chip-vendor.irq\n", __func__);
+               rc = devm_request_irq(dev, chip->vendor.irq,
+                                     i2c_nuvoton_int_handler,
+                                     IRQF_TRIGGER_LOW,
+                                     chip->vendor.miscdev.name,
+                                     chip);
+               if (rc) {
+                       dev_err(dev, "%s() Unable to request irq: %d for use\n",
+                               __func__, chip->vendor.irq);
+                       chip->vendor.irq = 0;
+               } else {
+                       /* Clear any pending interrupt */
+                       i2c_nuvoton_ready(chip);
+                       /* - wait for TPM_STS==0xA0 (stsValid, commandReady) */
+                       rc = i2c_nuvoton_wait_for_stat(chip,
+                                                      TPM_STS_COMMAND_READY,
+                                                      TPM_STS_COMMAND_READY,
+                                                      chip->vendor.timeout_b,
+                                                      NULL);
+                       if (rc == 0) {
+                               /*
+                                * TIS is in ready state
+                                * write dummy byte to enter reception state
+                                * TPM_DATA_FIFO_W <- rc (0)
+                                */
+                               rc = i2c_nuvoton_write_buf(client,
+                                                          TPM_DATA_FIFO_W,
+                                                          1, (u8 *) (&rc));
+                               if (rc < 0)
+                                       goto out_err;
+                               /* TPM_STS <- 0x40 (commandReady) */
+                               i2c_nuvoton_ready(chip);
+                       } else {
+                               /*
+                                * timeout_b reached - command was
+                                * aborted. TIS should now be in idle state -
+                                * only TPM_STS_VALID should be set
+                                */
+                               if (i2c_nuvoton_read_status(chip) !=
+                                   TPM_STS_VALID) {
+                                       rc = -EIO;
+                                       goto out_err;
+                               }
+                       }
+               }
+       }
+
+       if (tpm_get_timeouts(chip)) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       if (tpm_do_selftest(chip)) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       tpm_dev_vendor_release(chip);
+       tpm_remove_hardware(chip->dev);
+       return rc;
+}
+
+static int i2c_nuvoton_remove(struct i2c_client *client)
+{
+       struct device *dev = &(client->dev);
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+
+       if (chip)
+               tpm_dev_vendor_release(chip);
+       tpm_remove_hardware(dev);
+       kfree(chip);
+       return 0;
+}
+
+
+static const struct i2c_device_id i2c_nuvoton_id[] = {
+       {I2C_DRIVER_NAME, 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, i2c_nuvoton_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id i2c_nuvoton_of_match[] = {
+       {.compatible = "nuvoton,npct501"},
+       {.compatible = "winbond,wpct301"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, i2c_nuvoton_of_match);
+#endif
+
+static SIMPLE_DEV_PM_OPS(i2c_nuvoton_pm_ops, tpm_pm_suspend, tpm_pm_resume);
+
+static struct i2c_driver i2c_nuvoton_driver = {
+       .id_table = i2c_nuvoton_id,
+       .probe = i2c_nuvoton_probe,
+       .remove = i2c_nuvoton_remove,
+       .driver = {
+               .name = I2C_DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .pm = &i2c_nuvoton_pm_ops,
+               .of_match_table = of_match_ptr(i2c_nuvoton_of_match),
+       },
+};
+
+module_i2c_driver(i2c_nuvoton_driver);
+
+MODULE_AUTHOR("Dan Morav (dan.morav@nuvoton.com)");
+MODULE_DESCRIPTION("Nuvoton TPM I2C Driver");
+MODULE_LICENSE("GPL");
index 5bb8e2d..a0d6ceb 100644 (file)
@@ -584,7 +584,7 @@ static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
 static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
 static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
 static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
-static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
 static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
 
 static struct attribute *stm_tpm_attrs[] = {
@@ -746,8 +746,6 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
        tpm_get_timeouts(chip);
 
-       i2c_set_clientdata(client, chip);
-
        dev_info(chip->dev, "TPM I2C Initialized\n");
        return 0;
 _irq_set:
@@ -807,24 +805,18 @@ static int tpm_st33_i2c_remove(struct i2c_client *client)
 #ifdef CONFIG_PM_SLEEP
 /*
  * tpm_st33_i2c_pm_suspend suspend the TPM device
- * Added: Work around when suspend and no tpm application is running, suspend
- * may fail because chip->data_buffer is not set (only set in tpm_open in Linux
- * TPM core)
  * @param: client, the i2c_client drescription (TPM I2C description).
  * @param: mesg, the power management message.
  * @return: 0 in case of success.
  */
 static int tpm_st33_i2c_pm_suspend(struct device *dev)
 {
-       struct tpm_chip *chip = dev_get_drvdata(dev);
        struct st33zp24_platform_data *pin_infos = dev->platform_data;
        int ret = 0;
 
        if (power_mgt) {
                gpio_set_value(pin_infos->io_lpcpd, 0);
        } else {
-               if (chip->data_buffer == NULL)
-                       chip->data_buffer = pin_infos->tpm_i2c_buffer[0];
                ret = tpm_pm_suspend(dev);
        }
        return ret;
@@ -849,8 +841,6 @@ static int tpm_st33_i2c_pm_resume(struct device *dev)
                                          TPM_STS_VALID) == TPM_STS_VALID,
                                          chip->vendor.timeout_b);
        } else {
-               if (chip->data_buffer == NULL)
-                       chip->data_buffer = pin_infos->tpm_i2c_buffer[0];
                ret = tpm_pm_resume(dev);
                if (!ret)
                        tpm_do_selftest(chip);
index 56b07c3..2783a42 100644 (file)
@@ -98,7 +98,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 
        if (count < len) {
                dev_err(ibmvtpm->dev,
-                       "Invalid size in recv: count=%ld, crq_size=%d\n",
+                       "Invalid size in recv: count=%zd, crq_size=%d\n",
                        count, len);
                return -EIO;
        }
@@ -136,7 +136,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
 
        if (count > ibmvtpm->rtce_size) {
                dev_err(ibmvtpm->dev,
-                       "Invalid size in send: count=%ld, rtce_size=%d\n",
+                       "Invalid size in send: count=%zd, rtce_size=%d\n",
                        count, ibmvtpm->rtce_size);
                return -EIO;
        }
@@ -419,7 +419,7 @@ static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
 static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
 static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
                   NULL);
-static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
 static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
 static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
 static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
index 2168d15..8e562dc 100644 (file)
@@ -452,12 +452,8 @@ int tpm_add_ppi(struct kobject *parent)
 {
        return sysfs_create_group(parent, &ppi_attr_grp);
 }
-EXPORT_SYMBOL_GPL(tpm_add_ppi);
 
 void tpm_remove_ppi(struct kobject *parent)
 {
        sysfs_remove_group(parent, &ppi_attr_grp);
 }
-EXPORT_SYMBOL_GPL(tpm_remove_ppi);
-
-MODULE_LICENSE("GPL");
index 5796d01..1b74459 100644 (file)
@@ -448,7 +448,7 @@ static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
 static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
 static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
                   NULL);
-static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
 static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
 static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
 static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
index 94c280d..c8ff4df 100644 (file)
@@ -351,8 +351,6 @@ static int tpmfront_probe(struct xenbus_device *dev,
 
        tpm_get_timeouts(priv->chip);
 
-       dev_set_drvdata(&dev->dev, priv->chip);
-
        return rv;
 }
 
index b79cf3e..feea87c 100644 (file)
@@ -577,7 +577,8 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id,
        spin_lock(&portdev->c_ovq_lock);
        if (virtqueue_add_outbuf(vq, sg, 1, &cpkt, GFP_ATOMIC) == 0) {
                virtqueue_kick(vq);
-               while (!virtqueue_get_buf(vq, &len))
+               while (!virtqueue_get_buf(vq, &len)
+                       && !virtqueue_is_broken(vq))
                        cpu_relax();
        }
        spin_unlock(&portdev->c_ovq_lock);
@@ -650,7 +651,8 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
         * we need to kmalloc a GFP_ATOMIC buffer each time the
         * console driver writes something out.
         */
-       while (!virtqueue_get_buf(out_vq, &len))
+       while (!virtqueue_get_buf(out_vq, &len)
+               && !virtqueue_is_broken(out_vq))
                cpu_relax();
 done:
        spin_unlock_irqrestore(&port->outvq_lock, flags);
@@ -1837,12 +1839,8 @@ static void config_intr(struct virtio_device *vdev)
                struct port *port;
                u16 rows, cols;
 
-               vdev->config->get(vdev,
-                                 offsetof(struct virtio_console_config, cols),
-                                 &cols, sizeof(u16));
-               vdev->config->get(vdev,
-                                 offsetof(struct virtio_console_config, rows),
-                                 &rows, sizeof(u16));
+               virtio_cread(vdev, struct virtio_console_config, cols, &cols);
+               virtio_cread(vdev, struct virtio_console_config, rows, &rows);
 
                port = find_port_by_id(portdev, 0);
                set_console_size(port, rows, cols);
@@ -2014,10 +2012,9 @@ static int virtcons_probe(struct virtio_device *vdev)
 
        /* Don't test MULTIPORT at all if we're rproc: not a valid feature! */
        if (!is_rproc_serial(vdev) &&
-           virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
-                                 offsetof(struct virtio_console_config,
-                                          max_nr_ports),
-                                 &portdev->config.max_nr_ports) == 0) {
+           virtio_cread_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
+                                struct virtio_console_config, max_nr_ports,
+                                &portdev->config.max_nr_ports) == 0) {
                multiport = true;
        }
 
@@ -2142,7 +2139,7 @@ static struct virtio_device_id rproc_serial_id_table[] = {
 static unsigned int rproc_serial_features[] = {
 };
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int virtcons_freeze(struct virtio_device *vdev)
 {
        struct ports_device *portdev;
@@ -2220,7 +2217,7 @@ static struct virtio_driver virtio_console = {
        .probe =        virtcons_probe,
        .remove =       virtcons_remove,
        .config_changed = config_intr,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
        .freeze =       virtcons_freeze,
        .restore =      virtcons_restore,
 #endif
index 0e1d89b..d9e3f67 100644 (file)
@@ -117,7 +117,7 @@ void __init of_fixed_factor_clk_setup(struct device_node *node)
        }
 
        if (of_property_read_u32(node, "clock-mult", &mult)) {
-               pr_err("%s Fixed factor clock <%s> must have a clokc-mult property\n",
+               pr_err("%s Fixed factor clock <%s> must have a clock-mult property\n",
                        __func__, node->name);
                return;
        }
index c73fc2b..18c5b9b 100644 (file)
 #include <linux/atomic.h>
 #include <linux/pid_namespace.h>
 
-#include <asm/unaligned.h>
-
 #include <linux/cn_proc.h>
 
-#define CN_PROC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event))
+/*
+ * Size of a cn_msg followed by a proc_event structure.  Since the
+ * sizeof struct cn_msg is a multiple of 4 bytes, but not 8 bytes, we
+ * add one 4-byte word to the size here, and then start the actual
+ * cn_msg structure 4 bytes into the stack buffer.  The result is that
+ * the immediately following proc_event structure is aligned to 8 bytes.
+ */
+#define CN_PROC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event) + 4)
+
+/* See comment above; we test our assumption about sizeof struct cn_msg here. */
+static inline struct cn_msg *buffer_to_cn_msg(__u8 *buffer)
+{
+       BUILD_BUG_ON(sizeof(struct cn_msg) != 20);
+       return (struct cn_msg *)(buffer + 4);
+}
 
 static atomic_t proc_event_num_listeners = ATOMIC_INIT(0);
 static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC };
@@ -56,19 +68,19 @@ void proc_fork_connector(struct task_struct *task)
 {
        struct cn_msg *msg;
        struct proc_event *ev;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
        struct timespec ts;
        struct task_struct *parent;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_FORK;
        rcu_read_lock();
        parent = rcu_dereference(task->real_parent);
@@ -91,17 +103,17 @@ void proc_exec_connector(struct task_struct *task)
        struct cn_msg *msg;
        struct proc_event *ev;
        struct timespec ts;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_EXEC;
        ev->event_data.exec.process_pid = task->pid;
        ev->event_data.exec.process_tgid = task->tgid;
@@ -117,14 +129,14 @@ void proc_id_connector(struct task_struct *task, int which_id)
 {
        struct cn_msg *msg;
        struct proc_event *ev;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
        struct timespec ts;
        const struct cred *cred;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
        ev->what = which_id;
@@ -145,7 +157,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
        rcu_read_unlock();
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
 
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
@@ -159,17 +171,17 @@ void proc_sid_connector(struct task_struct *task)
        struct cn_msg *msg;
        struct proc_event *ev;
        struct timespec ts;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_SID;
        ev->event_data.sid.process_pid = task->pid;
        ev->event_data.sid.process_tgid = task->tgid;
@@ -186,17 +198,17 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
        struct cn_msg *msg;
        struct proc_event *ev;
        struct timespec ts;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_PTRACE;
        ev->event_data.ptrace.process_pid  = task->pid;
        ev->event_data.ptrace.process_tgid = task->tgid;
@@ -221,17 +233,17 @@ void proc_comm_connector(struct task_struct *task)
        struct cn_msg *msg;
        struct proc_event *ev;
        struct timespec ts;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_COMM;
        ev->event_data.comm.process_pid  = task->pid;
        ev->event_data.comm.process_tgid = task->tgid;
@@ -248,18 +260,18 @@ void proc_coredump_connector(struct task_struct *task)
 {
        struct cn_msg *msg;
        struct proc_event *ev;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
        struct timespec ts;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_COREDUMP;
        ev->event_data.coredump.process_pid = task->pid;
        ev->event_data.coredump.process_tgid = task->tgid;
@@ -275,18 +287,18 @@ void proc_exit_connector(struct task_struct *task)
 {
        struct cn_msg *msg;
        struct proc_event *ev;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
        struct timespec ts;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_EXIT;
        ev->event_data.exit.process_pid = task->pid;
        ev->event_data.exit.process_tgid = task->tgid;
@@ -312,18 +324,18 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
 {
        struct cn_msg *msg;
        struct proc_event *ev;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
        struct timespec ts;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
        msg->seq = rcvd_seq;
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->cpu = -1;
        ev->what = PROC_EVENT_NONE;
        ev->event_data.ack.err = err;
index 6897ad8..d369349 100644 (file)
@@ -129,7 +129,7 @@ config X86_AMD_FREQ_SENSITIVITY
        help
          This adds AMD-specific powersave bias function to the ondemand
          governor, which allows it to make more power-conscious frequency
-         change decisions based on feedback from hardware (availble on AMD
+         change decisions based on feedback from hardware (available on AMD
          Family 16h and above).
 
          Hardware feedback tells software how "sensitive" to frequency changes
index 218460f..25a70d0 100644 (file)
@@ -68,6 +68,9 @@ static void cs_check_cpu(int cpu, unsigned int load)
 
                dbs_info->requested_freq += get_freq_target(cs_tuners, policy);
 
+               if (dbs_info->requested_freq > policy->max)
+                       dbs_info->requested_freq = policy->max;
+
                __cpufreq_driver_target(policy, dbs_info->requested_freq,
                        CPUFREQ_RELATION_H);
                return;
index 0806c31..e6be635 100644 (file)
@@ -328,10 +328,6 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
                                             dbs_data->cdata->gov_dbs_timer);
                }
 
-               /*
-                * conservative does not implement micro like ondemand
-                * governor, thus we are bound to jiffes/HZ
-                */
                if (dbs_data->cdata->governor == GOV_CONSERVATIVE) {
                        cs_dbs_info->down_skip = 0;
                        cs_dbs_info->enable = 1;
index 7b6dc06..f3c2287 100644 (file)
@@ -67,7 +67,7 @@ static int exynos_cpufreq_scale(unsigned int target_freq)
        /*
         * The policy max have been changed so that we cannot get proper
         * old_index with cpufreq_frequency_table_target(). Thus, ignore
-        * policy and get the index from the raw freqeuncy table.
+        * policy and get the index from the raw frequency table.
         */
        old_index = exynos_cpufreq_get_index(old_freq);
        if (old_index < 0) {
index be6d143..a0acd0b 100644 (file)
@@ -53,6 +53,7 @@ static unsigned int omap_getspeed(unsigned int cpu)
 
 static int omap_target(struct cpufreq_policy *policy, unsigned int index)
 {
+       int r, ret;
        struct dev_pm_opp *opp;
        unsigned long freq, volt = 0, volt_old = 0, tol = 0;
        unsigned int old_freq, new_freq;
index 2d58da9..fa05e3c 100644 (file)
@@ -268,7 +268,7 @@ static int aes_start_crypt(struct tegra_aes_dev *dd, u32 in_addr, u32 out_addr,
        aes_writel(dd, value, TEGRA_AES_SECURE_INPUT_SELECT);
 
        aes_writel(dd, out_addr, TEGRA_AES_SECURE_DEST_ADDR);
-       INIT_COMPLETION(dd->op_complete);
+       reinit_completion(&dd->op_complete);
 
        for (i = 0; i < AES_HW_MAX_ICQ_LENGTH - 1; i++) {
                do {
index c61a6ec..446687c 100644 (file)
@@ -89,14 +89,15 @@ config AT_HDMAC
          Support the Atmel AHB DMA controller.
 
 config FSL_DMA
-       tristate "Freescale Elo and Elo Plus DMA support"
+       tristate "Freescale Elo series DMA support"
        depends on FSL_SOC
        select DMA_ENGINE
        select ASYNC_TX_ENABLE_CHANNEL_SWITCH
        ---help---
-         Enable support for the Freescale Elo and Elo Plus DMA controllers.
-         The Elo is the DMA controller on some 82xx and 83xx parts, and the
-         Elo Plus is the DMA controller on 85xx and 86xx parts.
+         Enable support for the Freescale Elo series DMA controllers.
+         The Elo is the DMA controller on some mpc82xx and mpc83xx parts, the
+         EloPlus is on mpc85xx and mpc86xx and Pxxx parts, and the Elo3 is on
+         some Txxx and Bxxx parts.
 
 config MPC512X_DMA
        tristate "Freescale MPC512x built-in DMA engine support"
@@ -313,7 +314,7 @@ config MMP_PDMA
        depends on (ARCH_MMP || ARCH_PXA)
        select DMA_ENGINE
        help
-         Support the MMP PDMA engine for PXA and MMP platfrom.
+         Support the MMP PDMA engine for PXA and MMP platform.
 
 config DMA_JZ4740
        tristate "JZ4740 DMA support"
index e51a983..16a2aa2 100644 (file)
@@ -1164,42 +1164,12 @@ static void pl08x_free_txd(struct pl08x_driver_data *pl08x,
        kfree(txd);
 }
 
-static void pl08x_unmap_buffers(struct pl08x_txd *txd)
-{
-       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);
-       }
-}
-
 static void pl08x_desc_free(struct virt_dma_desc *vd)
 {
        struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
        struct pl08x_dma_chan *plchan = to_pl08x_chan(vd->tx.chan);
 
-       if (!plchan->slave)
-               pl08x_unmap_buffers(txd);
-
+       dma_descriptor_unmap(txd);
        if (!txd->done)
                pl08x_release_mux(plchan);
 
@@ -1252,7 +1222,7 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan,
        size_t bytes = 0;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
 
        /*
@@ -1267,7 +1237,7 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan,
 
        spin_lock_irqsave(&plchan->vc.lock, flags);
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret != DMA_SUCCESS) {
+       if (ret != DMA_COMPLETE) {
                vd = vchan_find_desc(&plchan->vc, cookie);
                if (vd) {
                        /* On the issued list, so hasn't been processed yet */
@@ -2138,8 +2108,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
        writel(0x000000FF, pl08x->base + PL080_ERR_CLEAR);
        writel(0x000000FF, pl08x->base + PL080_TC_CLEAR);
 
-       ret = request_irq(adev->irq[0], pl08x_irq, IRQF_DISABLED,
-                         DRIVER_NAME, pl08x);
+       ret = request_irq(adev->irq[0], pl08x_irq, 0, DRIVER_NAME, pl08x);
        if (ret) {
                dev_err(&adev->dev, "%s failed to request interrupt %d\n",
                        __func__, adev->irq[0]);
index c787f38..e2c04dc 100644 (file)
@@ -344,31 +344,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc)
        /* move myself to free_list */
        list_move(&desc->desc_node, &atchan->free_list);
 
-       /* unmap dma addresses (not on slave channels) */
-       if (!atchan->chan_common.private) {
-               struct device *parent = chan2parent(&atchan->chan_common);
-               if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-                       if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE)
-                               dma_unmap_single(parent,
-                                               desc->lli.daddr,
-                                               desc->len, DMA_FROM_DEVICE);
-                       else
-                               dma_unmap_page(parent,
-                                               desc->lli.daddr,
-                                               desc->len, DMA_FROM_DEVICE);
-               }
-               if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-                       if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE)
-                               dma_unmap_single(parent,
-                                               desc->lli.saddr,
-                                               desc->len, DMA_TO_DEVICE);
-                       else
-                               dma_unmap_page(parent,
-                                               desc->lli.saddr,
-                                               desc->len, DMA_TO_DEVICE);
-               }
-       }
-
+       dma_descriptor_unmap(txd);
        /* for cyclic transfers,
         * no need to replay callback function while stopping */
        if (!atc_chan_is_cyclic(atchan)) {
@@ -1102,7 +1078,7 @@ atc_tx_status(struct dma_chan *chan,
        int bytes = 0;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
        /*
         * There's no point calculating the residue if there's
index 31011d2..3c6716e 100644 (file)
@@ -2369,7 +2369,7 @@ coh901318_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
        enum dma_status ret;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
 
        dma_set_residue(txstate, coh901318_get_bytes_left(chan));
@@ -2694,7 +2694,7 @@ static int __init coh901318_probe(struct platform_device *pdev)
        if (irq < 0)
                return irq;
 
-       err = devm_request_irq(&pdev->dev, irq, dma_irq_handler, IRQF_DISABLED,
+       err = devm_request_irq(&pdev->dev, irq, dma_irq_handler, 0,
                               "coh901318", base);
        if (err)
                return err;
index 7c82b92..c29dacf 100644 (file)
@@ -141,6 +141,9 @@ struct cppi41_dd {
        const struct chan_queues *queues_rx;
        const struct chan_queues *queues_tx;
        struct chan_queues td_queue;
+
+       /* context for suspend/resume */
+       unsigned int dma_tdfdq;
 };
 
 #define FIST_COMPLETION_QUEUE  93
@@ -263,6 +266,15 @@ static u32 pd_trans_len(u32 val)
        return val & ((1 << (DESC_LENGTH_BITS_NUM + 1)) - 1);
 }
 
+static u32 cppi41_pop_desc(struct cppi41_dd *cdd, unsigned queue_num)
+{
+       u32 desc;
+
+       desc = cppi_readl(cdd->qmgr_mem + QMGR_QUEUE_D(queue_num));
+       desc &= ~0x1f;
+       return desc;
+}
+
 static irqreturn_t cppi41_irq(int irq, void *data)
 {
        struct cppi41_dd *cdd = data;
@@ -300,8 +312,7 @@ static irqreturn_t cppi41_irq(int irq, void *data)
                        q_num = __fls(val);
                        val &= ~(1 << q_num);
                        q_num += 32 * i;
-                       desc = cppi_readl(cdd->qmgr_mem + QMGR_QUEUE_D(q_num));
-                       desc &= ~0x1f;
+                       desc = cppi41_pop_desc(cdd, q_num);
                        c = desc_to_chan(cdd, desc);
                        if (WARN_ON(!c)) {
                                pr_err("%s() q %d desc %08x\n", __func__,
@@ -353,7 +364,7 @@ static enum dma_status cppi41_dma_tx_status(struct dma_chan *chan,
 
        /* lock */
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (txstate && ret == DMA_SUCCESS)
+       if (txstate && ret == DMA_COMPLETE)
                txstate->residue = c->residue;
        /* unlock */
 
@@ -517,15 +528,6 @@ static void cppi41_compute_td_desc(struct cppi41_desc *d)
        d->pd0 = DESC_TYPE_TEARD << DESC_TYPE;
 }
 
-static u32 cppi41_pop_desc(struct cppi41_dd *cdd, unsigned queue_num)
-{
-       u32 desc;
-
-       desc = cppi_readl(cdd->qmgr_mem + QMGR_QUEUE_D(queue_num));
-       desc &= ~0x1f;
-       return desc;
-}
-
 static int cppi41_tear_down_chan(struct cppi41_channel *c)
 {
        struct cppi41_dd *cdd = c->cdd;
@@ -561,36 +563,26 @@ static int cppi41_tear_down_chan(struct cppi41_channel *c)
                c->td_retry = 100;
        }
 
-       if (!c->td_seen) {
-               unsigned td_comp_queue;
+       if (!c->td_seen || !c->td_desc_seen) {
 
-               if (c->is_tx)
-                       td_comp_queue =  cdd->td_queue.complete;
-               else
-                       td_comp_queue =  c->q_comp_num;
+               desc_phys = cppi41_pop_desc(cdd, cdd->td_queue.complete);
+               if (!desc_phys)
+                       desc_phys = cppi41_pop_desc(cdd, c->q_comp_num);
 
-               desc_phys = cppi41_pop_desc(cdd, td_comp_queue);
-               if (desc_phys) {
-                       __iormb();
+               if (desc_phys == c->desc_phys) {
+                       c->td_desc_seen = 1;
+
+               } else if (desc_phys == td_desc_phys) {
+                       u32 pd0;
 
-                       if (desc_phys == td_desc_phys) {
-                               u32 pd0;
-                               pd0 = td->pd0;
-                               WARN_ON((pd0 >> DESC_TYPE) != DESC_TYPE_TEARD);
-                               WARN_ON(!c->is_tx && !(pd0 & TD_DESC_IS_RX));
-                               WARN_ON((pd0 & 0x1f) != c->port_num);
-                       } else {
-                               WARN_ON_ONCE(1);
-                       }
-                       c->td_seen = 1;
-               }
-       }
-       if (!c->td_desc_seen) {
-               desc_phys = cppi41_pop_desc(cdd, c->q_comp_num);
-               if (desc_phys) {
                        __iormb();
-                       WARN_ON(c->desc_phys != desc_phys);
-                       c->td_desc_seen = 1;
+                       pd0 = td->pd0;
+                       WARN_ON((pd0 >> DESC_TYPE) != DESC_TYPE_TEARD);
+                       WARN_ON(!c->is_tx && !(pd0 & TD_DESC_IS_RX));
+                       WARN_ON((pd0 & 0x1f) != c->port_num);
+                       c->td_seen = 1;
+               } else if (desc_phys) {
+                       WARN_ON_ONCE(1);
                }
        }
        c->td_retry--;
@@ -609,7 +601,7 @@ static int cppi41_tear_down_chan(struct cppi41_channel *c)
 
        WARN_ON(!c->td_retry);
        if (!c->td_desc_seen) {
-               desc_phys = cppi_readl(cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num));
+               desc_phys = cppi41_pop_desc(cdd, c->q_num);
                WARN_ON(!desc_phys);
        }
 
@@ -674,14 +666,14 @@ static void cleanup_chans(struct cppi41_dd *cdd)
        }
 }
 
-static int cppi41_add_chans(struct platform_device *pdev, struct cppi41_dd *cdd)
+static int cppi41_add_chans(struct device *dev, struct cppi41_dd *cdd)
 {
        struct cppi41_channel *cchan;
        int i;
        int ret;
        u32 n_chans;
 
-       ret = of_property_read_u32(pdev->dev.of_node, "#dma-channels",
+       ret = of_property_read_u32(dev->of_node, "#dma-channels",
                        &n_chans);
        if (ret)
                return ret;
@@ -719,7 +711,7 @@ err:
        return -ENOMEM;
 }
 
-static void purge_descs(struct platform_device *pdev, struct cppi41_dd *cdd)
+static void purge_descs(struct device *dev, struct cppi41_dd *cdd)
 {
        unsigned int mem_decs;
        int i;
@@ -731,7 +723,7 @@ static void purge_descs(struct platform_device *pdev, struct cppi41_dd *cdd)
                cppi_writel(0, cdd->qmgr_mem + QMGR_MEMBASE(i));
                cppi_writel(0, cdd->qmgr_mem + QMGR_MEMCTRL(i));
 
-               dma_free_coherent(&pdev->dev, mem_decs, cdd->cd,
+               dma_free_coherent(dev, mem_decs, cdd->cd,
                                cdd->descs_phys);
        }
 }
@@ -741,19 +733,19 @@ static void disable_sched(struct cppi41_dd *cdd)
        cppi_writel(0, cdd->sched_mem + DMA_SCHED_CTRL);
 }
 
-static void deinit_cpii41(struct platform_device *pdev, struct cppi41_dd *cdd)
+static void deinit_cppi41(struct device *dev, struct cppi41_dd *cdd)
 {
        disable_sched(cdd);
 
-       purge_descs(pdev, cdd);
+       purge_descs(dev, cdd);
 
        cppi_writel(0, cdd->qmgr_mem + QMGR_LRAM0_BASE);
        cppi_writel(0, cdd->qmgr_mem + QMGR_LRAM0_BASE);
-       dma_free_coherent(&pdev->dev, QMGR_SCRATCH_SIZE, cdd->qmgr_scratch,
+       dma_free_coherent(dev, QMGR_SCRATCH_SIZE, cdd->qmgr_scratch,
                        cdd->scratch_phys);
 }
 
-static int init_descs(struct platform_device *pdev, struct cppi41_dd *cdd)
+static int init_descs(struct device *dev, struct cppi41_dd *cdd)
 {
        unsigned int desc_size;
        unsigned int mem_decs;
@@ -777,7 +769,7 @@ static int init_descs(struct platform_device *pdev, struct cppi41_dd *cdd)
                reg |= ilog2(ALLOC_DECS_NUM) - 5;
 
                BUILD_BUG_ON(DESCS_AREAS != 1);
-               cdd->cd = dma_alloc_coherent(&pdev->dev, mem_decs,
+               cdd->cd = dma_alloc_coherent(dev, mem_decs,
                                &cdd->descs_phys, GFP_KERNEL);
                if (!cdd->cd)
                        return -ENOMEM;
@@ -813,12 +805,12 @@ static void init_sched(struct cppi41_dd *cdd)
        cppi_writel(reg, cdd->sched_mem + DMA_SCHED_CTRL);
 }
 
-static int init_cppi41(struct platform_device *pdev, struct cppi41_dd *cdd)
+static int init_cppi41(struct device *dev, struct cppi41_dd *cdd)
 {
        int ret;
 
        BUILD_BUG_ON(QMGR_SCRATCH_SIZE > ((1 << 14) - 1));
-       cdd->qmgr_scratch = dma_alloc_coherent(&pdev->dev, QMGR_SCRATCH_SIZE,
+       cdd->qmgr_scratch = dma_alloc_coherent(dev, QMGR_SCRATCH_SIZE,
                        &cdd->scratch_phys, GFP_KERNEL);
        if (!cdd->qmgr_scratch)
                return -ENOMEM;
@@ -827,7 +819,7 @@ static int init_cppi41(struct platform_device *pdev, struct cppi41_dd *cdd)
        cppi_writel(QMGR_SCRATCH_SIZE, cdd->qmgr_mem + QMGR_LRAM_SIZE);
        cppi_writel(0, cdd->qmgr_mem + QMGR_LRAM1_BASE);
 
-       ret = init_descs(pdev, cdd);
+       ret = init_descs(dev, cdd);
        if (ret)
                goto err_td;
 
@@ -835,7 +827,7 @@ static int init_cppi41(struct platform_device *pdev, struct cppi41_dd *cdd)
        init_sched(cdd);
        return 0;
 err_td:
-       deinit_cpii41(pdev, cdd);
+       deinit_cppi41(dev, cdd);
        return ret;
 }
 
@@ -914,11 +906,11 @@ static const struct of_device_id cppi41_dma_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, cppi41_dma_ids);
 
-static const struct cppi_glue_infos *get_glue_info(struct platform_device *pdev)
+static const struct cppi_glue_infos *get_glue_info(struct device *dev)
 {
        const struct of_device_id *of_id;
 
-       of_id = of_match_node(cppi41_dma_ids, pdev->dev.of_node);
+       of_id = of_match_node(cppi41_dma_ids, dev->of_node);
        if (!of_id)
                return NULL;
        return of_id->data;
@@ -927,11 +919,12 @@ static const struct cppi_glue_infos *get_glue_info(struct platform_device *pdev)
 static int cppi41_dma_probe(struct platform_device *pdev)
 {
        struct cppi41_dd *cdd;
+       struct device *dev = &pdev->dev;
        const struct cppi_glue_infos *glue_info;
        int irq;
        int ret;
 
-       glue_info = get_glue_info(pdev);
+       glue_info = get_glue_info(dev);
        if (!glue_info)
                return -EINVAL;
 
@@ -946,14 +939,14 @@ static int cppi41_dma_probe(struct platform_device *pdev)
        cdd->ddev.device_issue_pending = cppi41_dma_issue_pending;
        cdd->ddev.device_prep_slave_sg = cppi41_dma_prep_slave_sg;
        cdd->ddev.device_control = cppi41_dma_control;
-       cdd->ddev.dev = &pdev->dev;
+       cdd->ddev.dev = dev;
        INIT_LIST_HEAD(&cdd->ddev.channels);
        cpp41_dma_info.dma_cap = cdd->ddev.cap_mask;
 
-       cdd->usbss_mem = of_iomap(pdev->dev.of_node, 0);
-       cdd->ctrl_mem = of_iomap(pdev->dev.of_node, 1);
-       cdd->sched_mem = of_iomap(pdev->dev.of_node, 2);
-       cdd->qmgr_mem = of_iomap(pdev->dev.of_node, 3);
+       cdd->usbss_mem = of_iomap(dev->of_node, 0);
+       cdd->ctrl_mem = of_iomap(dev->of_node, 1);
+       cdd->sched_mem = of_iomap(dev->of_node, 2);
+       cdd->qmgr_mem = of_iomap(dev->of_node, 3);
 
        if (!cdd->usbss_mem || !cdd->ctrl_mem || !cdd->sched_mem ||
                        !cdd->qmgr_mem) {
@@ -961,31 +954,31 @@ static int cppi41_dma_probe(struct platform_device *pdev)
                goto err_remap;
        }
 
-       pm_runtime_enable(&pdev->dev);
-       ret = pm_runtime_get_sync(&pdev->dev);
-       if (ret)
+       pm_runtime_enable(dev);
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0)
                goto err_get_sync;
 
        cdd->queues_rx = glue_info->queues_rx;
        cdd->queues_tx = glue_info->queues_tx;
        cdd->td_queue = glue_info->td_queue;
 
-       ret = init_cppi41(pdev, cdd);
+       ret = init_cppi41(dev, cdd);
        if (ret)
                goto err_init_cppi;
 
-       ret = cppi41_add_chans(pdev, cdd);
+       ret = cppi41_add_chans(dev, cdd);
        if (ret)
                goto err_chans;
 
-       irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+       irq = irq_of_parse_and_map(dev->of_node, 0);
        if (!irq)
                goto err_irq;
 
        cppi_writel(USBSS_IRQ_PD_COMP, cdd->usbss_mem + USBSS_IRQ_ENABLER);
 
        ret = request_irq(irq, glue_info->isr, IRQF_SHARED,
-                       dev_name(&pdev->dev), cdd);
+                       dev_name(dev), cdd);
        if (ret)
                goto err_irq;
        cdd->irq = irq;
@@ -994,7 +987,7 @@ static int cppi41_dma_probe(struct platform_device *pdev)
        if (ret)
                goto err_dma_reg;
 
-       ret = of_dma_controller_register(pdev->dev.of_node,
+       ret = of_dma_controller_register(dev->of_node,
                        cppi41_dma_xlate, &cpp41_dma_info);
        if (ret)
                goto err_of;
@@ -1009,11 +1002,11 @@ err_irq:
        cppi_writel(0, cdd->usbss_mem + USBSS_IRQ_CLEARR);
        cleanup_chans(cdd);
 err_chans:
-       deinit_cpii41(pdev, cdd);
+       deinit_cppi41(dev, cdd);
 err_init_cppi:
-       pm_runtime_put(&pdev->dev);
+       pm_runtime_put(dev);
 err_get_sync:
-       pm_runtime_disable(&pdev->dev);
+       pm_runtime_disable(dev);
        iounmap(cdd->usbss_mem);
        iounmap(cdd->ctrl_mem);
        iounmap(cdd->sched_mem);
@@ -1033,7 +1026,7 @@ static int cppi41_dma_remove(struct platform_device *pdev)
        cppi_writel(0, cdd->usbss_mem + USBSS_IRQ_CLEARR);
        free_irq(cdd->irq, cdd);
        cleanup_chans(cdd);
-       deinit_cpii41(pdev, cdd);
+       deinit_cppi41(&pdev->dev, cdd);
        iounmap(cdd->usbss_mem);
        iounmap(cdd->ctrl_mem);
        iounmap(cdd->sched_mem);
@@ -1044,12 +1037,53 @@ static int cppi41_dma_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int cppi41_suspend(struct device *dev)
+{
+       struct cppi41_dd *cdd = dev_get_drvdata(dev);
+
+       cdd->dma_tdfdq = cppi_readl(cdd->ctrl_mem + DMA_TDFDQ);
+       cppi_writel(0, cdd->usbss_mem + USBSS_IRQ_CLEARR);
+       disable_sched(cdd);
+
+       return 0;
+}
+
+static int cppi41_resume(struct device *dev)
+{
+       struct cppi41_dd *cdd = dev_get_drvdata(dev);
+       struct cppi41_channel *c;
+       int i;
+
+       for (i = 0; i < DESCS_AREAS; i++)
+               cppi_writel(cdd->descs_phys, cdd->qmgr_mem + QMGR_MEMBASE(i));
+
+       list_for_each_entry(c, &cdd->ddev.channels, chan.device_node)
+               if (!c->is_tx)
+                       cppi_writel(c->q_num, c->gcr_reg + RXHPCRA0);
+
+       init_sched(cdd);
+
+       cppi_writel(cdd->dma_tdfdq, cdd->ctrl_mem + DMA_TDFDQ);
+       cppi_writel(cdd->scratch_phys, cdd->qmgr_mem + QMGR_LRAM0_BASE);
+       cppi_writel(QMGR_SCRATCH_SIZE, cdd->qmgr_mem + QMGR_LRAM_SIZE);
+       cppi_writel(0, cdd->qmgr_mem + QMGR_LRAM1_BASE);
+
+       cppi_writel(USBSS_IRQ_PD_COMP, cdd->usbss_mem + USBSS_IRQ_ENABLER);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cppi41_pm_ops, cppi41_suspend, cppi41_resume);
+
 static struct platform_driver cpp41_dma_driver = {
        .probe  = cppi41_dma_probe,
        .remove = cppi41_dma_remove,
        .driver = {
                .name = "cppi41-dma-engine",
                .owner = THIS_MODULE,
+               .pm = &cppi41_pm_ops,
                .of_match_table = of_match_ptr(cppi41_dma_ids),
        },
 };
index b0c0c82..94c380f 100644 (file)
@@ -491,7 +491,7 @@ static enum dma_status jz4740_dma_tx_status(struct dma_chan *c,
        unsigned long flags;
 
        status = dma_cookie_status(c, cookie, state);
-       if (status == DMA_SUCCESS || !state)
+       if (status == DMA_COMPLETE || !state)
                return status;
 
        spin_lock_irqsave(&chan->vchan.lock, flags);
index 9162ac8..ea806bd 100644 (file)
@@ -65,6 +65,7 @@
 #include <linux/acpi.h>
 #include <linux/acpi_dma.h>
 #include <linux/of_dma.h>
+#include <linux/mempool.h>
 
 static DEFINE_MUTEX(dma_list_mutex);
 static DEFINE_IDR(dma_idr);
@@ -901,98 +902,132 @@ void dma_async_device_unregister(struct dma_device *device)
 }
 EXPORT_SYMBOL(dma_async_device_unregister);
 
-/**
- * dma_async_memcpy_buf_to_buf - offloaded copy between virtual addresses
- * @chan: DMA channel to offload copy to
- * @dest: destination address (virtual)
- * @src: source address (virtual)
- * @len: length
- *
- * Both @dest and @src must be mappable to a bus address according to the
- * DMA mapping API rules for streaming mappings.
- * Both @dest and @src must stay memory resident (kernel memory or locked
- * user space pages).
- */
-dma_cookie_t
-dma_async_memcpy_buf_to_buf(struct dma_chan *chan, void *dest,
-                       void *src, size_t len)
-{
-       struct dma_device *dev = chan->device;
-       struct dma_async_tx_descriptor *tx;
-       dma_addr_t dma_dest, dma_src;
-       dma_cookie_t cookie;
-       unsigned long flags;
+struct dmaengine_unmap_pool {
+       struct kmem_cache *cache;
+       const char *name;
+       mempool_t *pool;
+       size_t size;
+};
 
-       dma_src = dma_map_single(dev->dev, src, len, DMA_TO_DEVICE);
-       dma_dest = dma_map_single(dev->dev, dest, len, DMA_FROM_DEVICE);
-       flags = DMA_CTRL_ACK |
-               DMA_COMPL_SRC_UNMAP_SINGLE |
-               DMA_COMPL_DEST_UNMAP_SINGLE;
-       tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, flags);
+#define __UNMAP_POOL(x) { .size = x, .name = "dmaengine-unmap-" __stringify(x) }
+static struct dmaengine_unmap_pool unmap_pool[] = {
+       __UNMAP_POOL(2),
+       #if IS_ENABLED(CONFIG_ASYNC_TX_DMA)
+       __UNMAP_POOL(16),
+       __UNMAP_POOL(128),
+       __UNMAP_POOL(256),
+       #endif
+};
 
-       if (!tx) {
-               dma_unmap_single(dev->dev, dma_src, len, DMA_TO_DEVICE);
-               dma_unmap_single(dev->dev, dma_dest, len, DMA_FROM_DEVICE);
-               return -ENOMEM;
+static struct dmaengine_unmap_pool *__get_unmap_pool(int nr)
+{
+       int order = get_count_order(nr);
+
+       switch (order) {
+       case 0 ... 1:
+               return &unmap_pool[0];
+       case 2 ... 4:
+               return &unmap_pool[1];
+       case 5 ... 7:
+               return &unmap_pool[2];
+       case 8:
+               return &unmap_pool[3];
+       default:
+               BUG();
+               return NULL;
        }
+}
 
-       tx->callback = NULL;
-       cookie = tx->tx_submit(tx);
+static void dmaengine_unmap(struct kref *kref)
+{
+       struct dmaengine_unmap_data *unmap = container_of(kref, typeof(*unmap), kref);
+       struct device *dev = unmap->dev;
+       int cnt, i;
+
+       cnt = unmap->to_cnt;
+       for (i = 0; i < cnt; i++)
+               dma_unmap_page(dev, unmap->addr[i], unmap->len,
+                              DMA_TO_DEVICE);
+       cnt += unmap->from_cnt;
+       for (; i < cnt; i++)
+               dma_unmap_page(dev, unmap->addr[i], unmap->len,
+                              DMA_FROM_DEVICE);
+       cnt += unmap->bidi_cnt;
+       for (; i < cnt; i++) {
+               if (unmap->addr[i] == 0)
+                       continue;
+               dma_unmap_page(dev, unmap->addr[i], unmap->len,
+                              DMA_BIDIRECTIONAL);
+       }
+       mempool_free(unmap, __get_unmap_pool(cnt)->pool);
+}
 
-       preempt_disable();
-       __this_cpu_add(chan->local->bytes_transferred, len);
-       __this_cpu_inc(chan->local->memcpy_count);
-       preempt_enable();
+void dmaengine_unmap_put(struct dmaengine_unmap_data *unmap)
+{
+       if (unmap)
+               kref_put(&unmap->kref, dmaengine_unmap);
+}
+EXPORT_SYMBOL_GPL(dmaengine_unmap_put);
 
-       return cookie;
+static void dmaengine_destroy_unmap_pool(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(unmap_pool); i++) {
+               struct dmaengine_unmap_pool *p = &unmap_pool[i];
+
+               if (p->pool)
+                       mempool_destroy(p->pool);
+               p->pool = NULL;
+               if (p->cache)
+                       kmem_cache_destroy(p->cache);
+               p->cache = NULL;
+       }
 }
-EXPORT_SYMBOL(dma_async_memcpy_buf_to_buf);
 
-/**
- * dma_async_memcpy_buf_to_pg - offloaded copy from address to page
- * @chan: DMA channel to offload copy to
- * @page: destination page
- * @offset: offset in page to copy to
- * @kdata: source address (virtual)
- * @len: length
- *
- * Both @page/@offset and @kdata must be mappable to a bus address according
- * to the DMA mapping API rules for streaming mappings.
- * Both @page/@offset and @kdata must stay memory resident (kernel memory or
- * locked user space pages)
- */
-dma_cookie_t
-dma_async_memcpy_buf_to_pg(struct dma_chan *chan, struct page *page,
-                       unsigned int offset, void *kdata, size_t len)
+static int __init dmaengine_init_unmap_pool(void)
 {
-       struct dma_device *dev = chan->device;
-       struct dma_async_tx_descriptor *tx;
-       dma_addr_t dma_dest, dma_src;
-       dma_cookie_t cookie;
-       unsigned long flags;
+       int i;
 
-       dma_src = dma_map_single(dev->dev, kdata, len, DMA_TO_DEVICE);
-       dma_dest = dma_map_page(dev->dev, page, offset, len, DMA_FROM_DEVICE);
-       flags = DMA_CTRL_ACK | DMA_COMPL_SRC_UNMAP_SINGLE;
-       tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, flags);
+       for (i = 0; i < ARRAY_SIZE(unmap_pool); i++) {
+               struct dmaengine_unmap_pool *p = &unmap_pool[i];
+               size_t size;
 
-       if (!tx) {
-               dma_unmap_single(dev->dev, dma_src, len, DMA_TO_DEVICE);
-               dma_unmap_page(dev->dev, dma_dest, len, DMA_FROM_DEVICE);
-               return -ENOMEM;
+               size = sizeof(struct dmaengine_unmap_data) +
+                      sizeof(dma_addr_t) * p->size;
+
+               p->cache = kmem_cache_create(p->name, size, 0,
+                                            SLAB_HWCACHE_ALIGN, NULL);
+               if (!p->cache)
+                       break;
+               p->pool = mempool_create_slab_pool(1, p->cache);
+               if (!p->pool)
+                       break;
        }
 
-       tx->callback = NULL;
-       cookie = tx->tx_submit(tx);
+       if (i == ARRAY_SIZE(unmap_pool))
+               return 0;
 
-       preempt_disable();
-       __this_cpu_add(chan->local->bytes_transferred, len);
-       __this_cpu_inc(chan->local->memcpy_count);
-       preempt_enable();
+       dmaengine_destroy_unmap_pool();
+       return -ENOMEM;
+}
 
-       return cookie;
+struct dmaengine_unmap_data *
+dmaengine_get_unmap_data(struct device *dev, int nr, gfp_t flags)
+{
+       struct dmaengine_unmap_data *unmap;
+
+       unmap = mempool_alloc(__get_unmap_pool(nr)->pool, flags);
+       if (!unmap)
+               return NULL;
+
+       memset(unmap, 0, sizeof(*unmap));
+       kref_init(&unmap->kref);
+       unmap->dev = dev;
+
+       return unmap;
 }
-EXPORT_SYMBOL(dma_async_memcpy_buf_to_pg);
+EXPORT_SYMBOL(dmaengine_get_unmap_data);
 
 /**
  * dma_async_memcpy_pg_to_pg - offloaded copy from page to page
@@ -1015,24 +1050,33 @@ dma_async_memcpy_pg_to_pg(struct dma_chan *chan, struct page *dest_pg,
 {
        struct dma_device *dev = chan->device;
        struct dma_async_tx_descriptor *tx;
-       dma_addr_t dma_dest, dma_src;
+       struct dmaengine_unmap_data *unmap;
        dma_cookie_t cookie;
        unsigned long flags;
 
-       dma_src = dma_map_page(dev->dev, src_pg, src_off, len, DMA_TO_DEVICE);
-       dma_dest = dma_map_page(dev->dev, dest_pg, dest_off, len,
-                               DMA_FROM_DEVICE);
+       unmap = dmaengine_get_unmap_data(dev->dev, 2, GFP_NOIO);
+       if (!unmap)
+               return -ENOMEM;
+
+       unmap->to_cnt = 1;
+       unmap->from_cnt = 1;
+       unmap->addr[0] = dma_map_page(dev->dev, src_pg, src_off, len,
+                                     DMA_TO_DEVICE);
+       unmap->addr[1] = dma_map_page(dev->dev, dest_pg, dest_off, len,
+                                     DMA_FROM_DEVICE);
+       unmap->len = len;
        flags = DMA_CTRL_ACK;
-       tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, flags);
+       tx = dev->device_prep_dma_memcpy(chan, unmap->addr[1], unmap->addr[0],
+                                        len, flags);
 
        if (!tx) {
-               dma_unmap_page(dev->dev, dma_src, len, DMA_TO_DEVICE);
-               dma_unmap_page(dev->dev, dma_dest, len, DMA_FROM_DEVICE);
+               dmaengine_unmap_put(unmap);
                return -ENOMEM;
        }
 
-       tx->callback = NULL;
+       dma_set_unmap(tx, unmap);
        cookie = tx->tx_submit(tx);
+       dmaengine_unmap_put(unmap);
 
        preempt_disable();
        __this_cpu_add(chan->local->bytes_transferred, len);
@@ -1043,6 +1087,52 @@ dma_async_memcpy_pg_to_pg(struct dma_chan *chan, struct page *dest_pg,
 }
 EXPORT_SYMBOL(dma_async_memcpy_pg_to_pg);
 
+/**
+ * dma_async_memcpy_buf_to_buf - offloaded copy between virtual addresses
+ * @chan: DMA channel to offload copy to
+ * @dest: destination address (virtual)
+ * @src: source address (virtual)
+ * @len: length
+ *
+ * Both @dest and @src must be mappable to a bus address according to the
+ * DMA mapping API rules for streaming mappings.
+ * Both @dest and @src must stay memory resident (kernel memory or locked
+ * user space pages).
+ */
+dma_cookie_t
+dma_async_memcpy_buf_to_buf(struct dma_chan *chan, void *dest,
+                           void *src, size_t len)
+{
+       return dma_async_memcpy_pg_to_pg(chan, virt_to_page(dest),
+                                        (unsigned long) dest & ~PAGE_MASK,
+                                        virt_to_page(src),
+                                        (unsigned long) src & ~PAGE_MASK, len);
+}
+EXPORT_SYMBOL(dma_async_memcpy_buf_to_buf);
+
+/**
+ * dma_async_memcpy_buf_to_pg - offloaded copy from address to page
+ * @chan: DMA channel to offload copy to
+ * @page: destination page
+ * @offset: offset in page to copy to
+ * @kdata: source address (virtual)
+ * @len: length
+ *
+ * Both @page/@offset and @kdata must be mappable to a bus address according
+ * to the DMA mapping API rules for streaming mappings.
+ * Both @page/@offset and @kdata must stay memory resident (kernel memory or
+ * locked user space pages)
+ */
+dma_cookie_t
+dma_async_memcpy_buf_to_pg(struct dma_chan *chan, struct page *page,
+                          unsigned int offset, void *kdata, size_t len)
+{
+       return dma_async_memcpy_pg_to_pg(chan, page, offset,
+                                        virt_to_page(kdata),
+                                        (unsigned long) kdata & ~PAGE_MASK, len);
+}
+EXPORT_SYMBOL(dma_async_memcpy_buf_to_pg);
+
 void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx,
        struct dma_chan *chan)
 {
@@ -1062,7 +1152,7 @@ dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
        unsigned long dma_sync_wait_timeout = jiffies + msecs_to_jiffies(5000);
 
        if (!tx)
-               return DMA_SUCCESS;
+               return DMA_COMPLETE;
 
        while (tx->cookie == -EBUSY) {
                if (time_after_eq(jiffies, dma_sync_wait_timeout)) {
@@ -1116,6 +1206,10 @@ EXPORT_SYMBOL_GPL(dma_run_dependencies);
 
 static int __init dma_bus_init(void)
 {
+       int err = dmaengine_init_unmap_pool();
+
+       if (err)
+               return err;
        return class_register(&dma_devclass);
 }
 arch_initcall(dma_bus_init);
index 92f796c..20f9a3a 100644 (file)
@@ -8,6 +8,8 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 #include <linux/random.h>
 #include <linux/slab.h>
 #include <linux/wait.h>
-#include <linux/ctype.h>
-#include <linux/debugfs.h>
-#include <linux/uaccess.h>
-#include <linux/seq_file.h>
 
 static unsigned int test_buf_size = 16384;
 module_param(test_buf_size, uint, S_IRUGO | S_IWUSR);
@@ -68,92 +66,13 @@ module_param(timeout, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
                 "Pass -1 for infinite timeout");
 
-/* Maximum amount of mismatched bytes in buffer to print */
-#define MAX_ERROR_COUNT                32
-
-/*
- * Initialization patterns. All bytes in the source buffer has bit 7
- * set, all bytes in the destination buffer has bit 7 cleared.
- *
- * Bit 6 is set for all bytes which are to be copied by the DMA
- * engine. Bit 5 is set for all bytes which are to be overwritten by
- * the DMA engine.
- *
- * The remaining bits are the inverse of a counter which increments by
- * one for each byte address.
- */
-#define PATTERN_SRC            0x80
-#define PATTERN_DST            0x00
-#define PATTERN_COPY           0x40
-#define PATTERN_OVERWRITE      0x20
-#define PATTERN_COUNT_MASK     0x1f
-
-enum dmatest_error_type {
-       DMATEST_ET_OK,
-       DMATEST_ET_MAP_SRC,
-       DMATEST_ET_MAP_DST,
-       DMATEST_ET_PREP,
-       DMATEST_ET_SUBMIT,
-       DMATEST_ET_TIMEOUT,
-       DMATEST_ET_DMA_ERROR,
-       DMATEST_ET_DMA_IN_PROGRESS,
-       DMATEST_ET_VERIFY,
-       DMATEST_ET_VERIFY_BUF,
-};
-
-struct dmatest_verify_buffer {
-       unsigned int    index;
-       u8              expected;
-       u8              actual;
-};
-
-struct dmatest_verify_result {
-       unsigned int                    error_count;
-       struct dmatest_verify_buffer    data[MAX_ERROR_COUNT];
-       u8                              pattern;
-       bool                            is_srcbuf;
-};
-
-struct dmatest_thread_result {
-       struct list_head        node;
-       unsigned int            n;
-       unsigned int            src_off;
-       unsigned int            dst_off;
-       unsigned int            len;
-       enum dmatest_error_type type;
-       union {
-               unsigned long                   data;
-               dma_cookie_t                    cookie;
-               enum dma_status                 status;
-               int                             error;
-               struct dmatest_verify_result    *vr;
-       };
-};
-
-struct dmatest_result {
-       struct list_head        node;
-       char                    *name;
-       struct list_head        results;
-};
-
-struct dmatest_info;
-
-struct dmatest_thread {
-       struct list_head        node;
-       struct dmatest_info     *info;
-       struct task_struct      *task;
-       struct dma_chan         *chan;
-       u8                      **srcs;
-       u8                      **dsts;
-       enum dma_transaction_type type;
-       bool                    done;
-};
+static bool noverify;
+module_param(noverify, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(noverify, "Disable random data setup and verification");
 
-struct dmatest_chan {
-       struct list_head        node;
-       struct dma_chan         *chan;
-       struct list_head        threads;
-};
+static bool verbose;
+module_param(verbose, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(verbose, "Enable \"success\" result messages (default: off)");
 
 /**
  * struct dmatest_params - test parameters.
@@ -177,6 +96,7 @@ struct dmatest_params {
        unsigned int    xor_sources;
        unsigned int    pq_sources;
        int             timeout;
+       bool            noverify;
 };
 
 /**
@@ -184,7 +104,7 @@ struct dmatest_params {
  * @params:            test parameters
  * @lock:              access protection to the fields of this structure
  */
-struct dmatest_info {
+static struct dmatest_info {
        /* Test parameters */
        struct dmatest_params   params;
 
@@ -192,16 +112,95 @@ struct dmatest_info {
        struct list_head        channels;
        unsigned int            nr_channels;
        struct mutex            lock;
+       bool                    did_init;
+} test_info = {
+       .channels = LIST_HEAD_INIT(test_info.channels),
+       .lock = __MUTEX_INITIALIZER(test_info.lock),
+};
+
+static int dmatest_run_set(const char *val, const struct kernel_param *kp);
+static int dmatest_run_get(char *val, const struct kernel_param *kp);
+static struct kernel_param_ops run_ops = {
+       .set = dmatest_run_set,
+       .get = dmatest_run_get,
+};
+static bool dmatest_run;
+module_param_cb(run, &run_ops, &dmatest_run, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(run, "Run the test (default: false)");
+
+/* Maximum amount of mismatched bytes in buffer to print */
+#define MAX_ERROR_COUNT                32
+
+/*
+ * Initialization patterns. All bytes in the source buffer has bit 7
+ * set, all bytes in the destination buffer has bit 7 cleared.
+ *
+ * Bit 6 is set for all bytes which are to be copied by the DMA
+ * engine. Bit 5 is set for all bytes which are to be overwritten by
+ * the DMA engine.
+ *
+ * The remaining bits are the inverse of a counter which increments by
+ * one for each byte address.
+ */
+#define PATTERN_SRC            0x80
+#define PATTERN_DST            0x00
+#define PATTERN_COPY           0x40
+#define PATTERN_OVERWRITE      0x20
+#define PATTERN_COUNT_MASK     0x1f
 
-       /* debugfs related stuff */
-       struct dentry           *root;
+struct dmatest_thread {
+       struct list_head        node;
+       struct dmatest_info     *info;
+       struct task_struct      *task;
+       struct dma_chan         *chan;
+       u8                      **srcs;
+       u8                      **dsts;
+       enum dma_transaction_type type;
+       bool                    done;
+};
 
-       /* Test results */
-       struct list_head        results;
-       struct mutex            results_lock;
+struct dmatest_chan {
+       struct list_head        node;
+       struct dma_chan         *chan;
+       struct list_head        threads;
 };
 
-static struct dmatest_info test_info;
+static DECLARE_WAIT_QUEUE_HEAD(thread_wait);
+static bool wait;
+
+static bool is_threaded_test_run(struct dmatest_info *info)
+{
+       struct dmatest_chan *dtc;
+
+       list_for_each_entry(dtc, &info->channels, node) {
+               struct dmatest_thread *thread;
+
+               list_for_each_entry(thread, &dtc->threads, node) {
+                       if (!thread->done)
+                               return true;
+               }
+       }
+
+       return false;
+}
+
+static int dmatest_wait_get(char *val, const struct kernel_param *kp)
+{
+       struct dmatest_info *info = &test_info;
+       struct dmatest_params *params = &info->params;
+
+       if (params->iterations)
+               wait_event(thread_wait, !is_threaded_test_run(info));
+       wait = true;
+       return param_get_bool(val, kp);
+}
+
+static struct kernel_param_ops wait_ops = {
+       .get = dmatest_wait_get,
+       .set = param_set_bool,
+};
+module_param_cb(wait, &wait_ops, &wait, S_IRUGO);
+MODULE_PARM_DESC(wait, "Wait for tests to complete (default: false)");
 
 static bool dmatest_match_channel(struct dmatest_params *params,
                struct dma_chan *chan)
@@ -223,7 +222,7 @@ static unsigned long dmatest_random(void)
 {
        unsigned long buf;
 
-       get_random_bytes(&buf, sizeof(buf));
+       prandom_bytes(&buf, sizeof(buf));
        return buf;
 }
 
@@ -262,9 +261,31 @@ static void dmatest_init_dsts(u8 **bufs, unsigned int start, unsigned int len,
        }
 }
 
-static unsigned int dmatest_verify(struct dmatest_verify_result *vr, u8 **bufs,
-               unsigned int start, unsigned int end, unsigned int counter,
-               u8 pattern, bool is_srcbuf)
+static void dmatest_mismatch(u8 actual, u8 pattern, unsigned int index,
+               unsigned int counter, bool is_srcbuf)
+{
+       u8              diff = actual ^ pattern;
+       u8              expected = pattern | (~counter & PATTERN_COUNT_MASK);
+       const char      *thread_name = current->comm;
+
+       if (is_srcbuf)
+               pr_warn("%s: srcbuf[0x%x] overwritten! Expected %02x, got %02x\n",
+                       thread_name, index, expected, actual);
+       else if ((pattern & PATTERN_COPY)
+                       && (diff & (PATTERN_COPY | PATTERN_OVERWRITE)))
+               pr_warn("%s: dstbuf[0x%x] not copied! Expected %02x, got %02x\n",
+                       thread_name, index, expected, actual);
+       else if (diff & PATTERN_SRC)
+               pr_warn("%s: dstbuf[0x%x] was copied! Expected %02x, got %02x\n",
+                       thread_name, index, expected, actual);
+       else
+               pr_warn("%s: dstbuf[0x%x] mismatch! Expected %02x, got %02x\n",
+                       thread_name, index, expected, actual);
+}
+
+static unsigned int dmatest_verify(u8 **bufs, unsigned int start,
+               unsigned int end, unsigned int counter, u8 pattern,
+               bool is_srcbuf)
 {
        unsigned int i;
        unsigned int error_count = 0;
@@ -272,7 +293,6 @@ static unsigned int dmatest_verify(struct dmatest_verify_result *vr, u8 **bufs,
        u8 expected;
        u8 *buf;
        unsigned int counter_orig = counter;
-       struct dmatest_verify_buffer *vb;
 
        for (; (buf = *bufs); bufs++) {
                counter = counter_orig;
@@ -280,12 +300,9 @@ static unsigned int dmatest_verify(struct dmatest_verify_result *vr, u8 **bufs,
                        actual = buf[i];
                        expected = pattern | (~counter & PATTERN_COUNT_MASK);
                        if (actual != expected) {
-                               if (error_count < MAX_ERROR_COUNT && vr) {
-                                       vb = &vr->data[error_count];
-                                       vb->index = i;
-                                       vb->expected = expected;
-                                       vb->actual = actual;
-                               }
+                               if (error_count < MAX_ERROR_COUNT)
+                                       dmatest_mismatch(actual, pattern, i,
+                                                        counter, is_srcbuf);
                                error_count++;
                        }
                        counter++;
@@ -293,7 +310,7 @@ static unsigned int dmatest_verify(struct dmatest_verify_result *vr, u8 **bufs,
        }
 
        if (error_count > MAX_ERROR_COUNT)
-               pr_warning("%s: %u errors suppressed\n",
+               pr_warn("%s: %u errors suppressed\n",
                        current->comm, error_count - MAX_ERROR_COUNT);
 
        return error_count;
@@ -313,20 +330,6 @@ static void dmatest_callback(void *arg)
        wake_up_all(done->wait);
 }
 
-static inline void unmap_src(struct device *dev, dma_addr_t *addr, size_t len,
-                            unsigned int count)
-{
-       while (count--)
-               dma_unmap_single(dev, addr[count], len, DMA_TO_DEVICE);
-}
-
-static inline void unmap_dst(struct device *dev, dma_addr_t *addr, size_t len,
-                            unsigned int count)
-{
-       while (count--)
-               dma_unmap_single(dev, addr[count], len, DMA_BIDIRECTIONAL);
-}
-
 static unsigned int min_odd(unsigned int x, unsigned int y)
 {
        unsigned int val = min(x, y);
@@ -334,172 +337,49 @@ static unsigned int min_odd(unsigned int x, unsigned int y)
        return val % 2 ? val : val - 1;
 }
 
-static char *verify_result_get_one(struct dmatest_verify_result *vr,
-               unsigned int i)
+static void result(const char *err, unsigned int n, unsigned int src_off,
+                  unsigned int dst_off, unsigned int len, unsigned long data)
 {
-       struct dmatest_verify_buffer *vb = &vr->data[i];
-       u8 diff = vb->actual ^ vr->pattern;
-       static char buf[512];
-       char *msg;
-
-       if (vr->is_srcbuf)
-               msg = "srcbuf overwritten!";
-       else if ((vr->pattern & PATTERN_COPY)
-                       && (diff & (PATTERN_COPY | PATTERN_OVERWRITE)))
-               msg = "dstbuf not copied!";
-       else if (diff & PATTERN_SRC)
-               msg = "dstbuf was copied!";
-       else
-               msg = "dstbuf mismatch!";
-
-       snprintf(buf, sizeof(buf) - 1, "%s [0x%x] Expected %02x, got %02x", msg,
-                vb->index, vb->expected, vb->actual);
-
-       return buf;
+       pr_info("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)",
+               current->comm, n, err, src_off, dst_off, len, data);
 }
 
-static char *thread_result_get(const char *name,
-               struct dmatest_thread_result *tr)
+static void dbg_result(const char *err, unsigned int n, unsigned int src_off,
+                      unsigned int dst_off, unsigned int len,
+                      unsigned long data)
 {
-       static const char * const messages[] = {
-               [DMATEST_ET_OK]                 = "No errors",
-               [DMATEST_ET_MAP_SRC]            = "src mapping error",
-               [DMATEST_ET_MAP_DST]            = "dst mapping error",
-               [DMATEST_ET_PREP]               = "prep error",
-               [DMATEST_ET_SUBMIT]             = "submit error",
-               [DMATEST_ET_TIMEOUT]            = "test timed out",
-               [DMATEST_ET_DMA_ERROR]          =
-                       "got completion callback (DMA_ERROR)",
-               [DMATEST_ET_DMA_IN_PROGRESS]    =
-                       "got completion callback (DMA_IN_PROGRESS)",
-               [DMATEST_ET_VERIFY]             = "errors",
-               [DMATEST_ET_VERIFY_BUF]         = "verify errors",
-       };
-       static char buf[512];
-
-       snprintf(buf, sizeof(buf) - 1,
-                "%s: #%u: %s with src_off=0x%x ""dst_off=0x%x len=0x%x (%lu)",
-                name, tr->n, messages[tr->type], tr->src_off, tr->dst_off,
-                tr->len, tr->data);
-
-       return buf;
+       pr_debug("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)",
+                  current->comm, n, err, src_off, dst_off, len, data);
 }
 
-static int thread_result_add(struct dmatest_info *info,
-               struct dmatest_result *r, enum dmatest_error_type type,
-               unsigned int n, unsigned int src_off, unsigned int dst_off,
-               unsigned int len, unsigned long data)
-{
-       struct dmatest_thread_result *tr;
-
-       tr = kzalloc(sizeof(*tr), GFP_KERNEL);
-       if (!tr)
-               return -ENOMEM;
-
-       tr->type = type;
-       tr->n = n;
-       tr->src_off = src_off;
-       tr->dst_off = dst_off;
-       tr->len = len;
-       tr->data = data;
+#define verbose_result(err, n, src_off, dst_off, len, data) ({ \
+       if (verbose) \
+               result(err, n, src_off, dst_off, len, data); \
+       else \
+               dbg_result(err, n, src_off, dst_off, len, data); \
+})
 
-       mutex_lock(&info->results_lock);
-       list_add_tail(&tr->node, &r->results);
-       mutex_unlock(&info->results_lock);
-
-       if (tr->type == DMATEST_ET_OK)
-               pr_debug("%s\n", thread_result_get(r->name, tr));
-       else
-               pr_warn("%s\n", thread_result_get(r->name, tr));
-
-       return 0;
-}
-
-static unsigned int verify_result_add(struct dmatest_info *info,
-               struct dmatest_result *r, unsigned int n,
-               unsigned int src_off, unsigned int dst_off, unsigned int len,
-               u8 **bufs, int whence, unsigned int counter, u8 pattern,
-               bool is_srcbuf)
+static unsigned long long dmatest_persec(s64 runtime, unsigned int val)
 {
-       struct dmatest_verify_result *vr;
-       unsigned int error_count;
-       unsigned int buf_off = is_srcbuf ? src_off : dst_off;
-       unsigned int start, end;
-
-       if (whence < 0) {
-               start = 0;
-               end = buf_off;
-       } else if (whence > 0) {
-               start = buf_off + len;
-               end = info->params.buf_size;
-       } else {
-               start = buf_off;
-               end = buf_off + len;
-       }
+       unsigned long long per_sec = 1000000;
 
-       vr = kmalloc(sizeof(*vr), GFP_KERNEL);
-       if (!vr) {
-               pr_warn("dmatest: No memory to store verify result\n");
-               return dmatest_verify(NULL, bufs, start, end, counter, pattern,
-                                     is_srcbuf);
-       }
-
-       vr->pattern = pattern;
-       vr->is_srcbuf = is_srcbuf;
-
-       error_count = dmatest_verify(vr, bufs, start, end, counter, pattern,
-                                    is_srcbuf);
-       if (error_count) {
-               vr->error_count = error_count;
-               thread_result_add(info, r, DMATEST_ET_VERIFY_BUF, n, src_off,
-                                 dst_off, len, (unsigned long)vr);
-               return error_count;
-       }
-
-       kfree(vr);
-       return 0;
-}
-
-static void result_free(struct dmatest_info *info, const char *name)
-{
-       struct dmatest_result *r, *_r;
-
-       mutex_lock(&info->results_lock);
-       list_for_each_entry_safe(r, _r, &info->results, node) {
-               struct dmatest_thread_result *tr, *_tr;
-
-               if (name && strcmp(r->name, name))
-                       continue;
-
-               list_for_each_entry_safe(tr, _tr, &r->results, node) {
-                       if (tr->type == DMATEST_ET_VERIFY_BUF)
-                               kfree(tr->vr);
-                       list_del(&tr->node);
-                       kfree(tr);
-               }
+       if (runtime <= 0)
+               return 0;
 
-               kfree(r->name);
-               list_del(&r->node);
-               kfree(r);
+       /* drop precision until runtime is 32-bits */
+       while (runtime > UINT_MAX) {
+               runtime >>= 1;
+               per_sec <<= 1;
        }
 
-       mutex_unlock(&info->results_lock);
+       per_sec *= val;
+       do_div(per_sec, runtime);
+       return per_sec;
 }
 
-static struct dmatest_result *result_init(struct dmatest_info *info,
-               const char *name)
+static unsigned long long dmatest_KBs(s64 runtime, unsigned long long len)
 {
-       struct dmatest_result *r;
-
-       r = kzalloc(sizeof(*r), GFP_KERNEL);
-       if (r) {
-               r->name = kstrdup(name, GFP_KERNEL);
-               INIT_LIST_HEAD(&r->results);
-               mutex_lock(&info->results_lock);
-               list_add_tail(&r->node, &info->results);
-               mutex_unlock(&info->results_lock);
-       }
-       return r;
+       return dmatest_persec(runtime, len >> 10);
 }
 
 /*
@@ -525,7 +405,6 @@ static int dmatest_func(void *data)
        struct dmatest_params   *params;
        struct dma_chan         *chan;
        struct dma_device       *dev;
-       const char              *thread_name;
        unsigned int            src_off, dst_off, len;
        unsigned int            error_count;
        unsigned int            failed_tests = 0;
@@ -538,9 +417,10 @@ static int dmatest_func(void *data)
        int                     src_cnt;
        int                     dst_cnt;
        int                     i;
-       struct dmatest_result   *result;
+       ktime_t                 ktime;
+       s64                     runtime = 0;
+       unsigned long long      total_len = 0;
 
-       thread_name = current->comm;
        set_freezable();
 
        ret = -ENOMEM;
@@ -570,10 +450,6 @@ static int dmatest_func(void *data)
        } else
                goto err_thread_type;
 
-       result = result_init(info, thread_name);
-       if (!result)
-               goto err_srcs;
-
        thread->srcs = kcalloc(src_cnt+1, sizeof(u8 *), GFP_KERNEL);
        if (!thread->srcs)
                goto err_srcs;
@@ -597,17 +473,17 @@ static int dmatest_func(void *data)
        set_user_nice(current, 10);
 
        /*
-        * src buffers are freed by the DMAEngine code with dma_unmap_single()
-        * dst buffers are freed by ourselves below
+        * src and dst buffers are freed by ourselves below
         */
-       flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT
-             | DMA_COMPL_SKIP_DEST_UNMAP | DMA_COMPL_SRC_UNMAP_SINGLE;
+       flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
 
+       ktime = ktime_get();
        while (!kthread_should_stop()
               && !(params->iterations && total_tests >= params->iterations)) {
                struct dma_async_tx_descriptor *tx = NULL;
-               dma_addr_t dma_srcs[src_cnt];
-               dma_addr_t dma_dsts[dst_cnt];
+               struct dmaengine_unmap_data *um;
+               dma_addr_t srcs[src_cnt];
+               dma_addr_t *dsts;
                u8 align = 0;
 
                total_tests++;
@@ -626,81 +502,103 @@ static int dmatest_func(void *data)
                        break;
                }
 
-               len = dmatest_random() % params->buf_size + 1;
+               if (params->noverify) {
+                       len = params->buf_size;
+                       src_off = 0;
+                       dst_off = 0;
+               } else {
+                       len = dmatest_random() % params->buf_size + 1;
+                       len = (len >> align) << align;
+                       if (!len)
+                               len = 1 << align;
+                       src_off = dmatest_random() % (params->buf_size - len + 1);
+                       dst_off = dmatest_random() % (params->buf_size - len + 1);
+
+                       src_off = (src_off >> align) << align;
+                       dst_off = (dst_off >> align) << align;
+
+                       dmatest_init_srcs(thread->srcs, src_off, len,
+                                         params->buf_size);
+                       dmatest_init_dsts(thread->dsts, dst_off, len,
+                                         params->buf_size);
+               }
+
                len = (len >> align) << align;
                if (!len)
                        len = 1 << align;
-               src_off = dmatest_random() % (params->buf_size - len + 1);
-               dst_off = dmatest_random() % (params->buf_size - len + 1);
+               total_len += len;
 
-               src_off = (src_off >> align) << align;
-               dst_off = (dst_off >> align) << align;
-
-               dmatest_init_srcs(thread->srcs, src_off, len, params->buf_size);
-               dmatest_init_dsts(thread->dsts, dst_off, len, params->buf_size);
+               um = dmaengine_get_unmap_data(dev->dev, src_cnt+dst_cnt,
+                                             GFP_KERNEL);
+               if (!um) {
+                       failed_tests++;
+                       result("unmap data NULL", total_tests,
+                              src_off, dst_off, len, ret);
+                       continue;
+               }
 
+               um->len = params->buf_size;
                for (i = 0; i < src_cnt; i++) {
-                       u8 *buf = thread->srcs[i] + src_off;
-
-                       dma_srcs[i] = dma_map_single(dev->dev, buf, len,
-                                                    DMA_TO_DEVICE);
-                       ret = dma_mapping_error(dev->dev, dma_srcs[i]);
+                       unsigned long buf = (unsigned long) thread->srcs[i];
+                       struct page *pg = virt_to_page(buf);
+                       unsigned pg_off = buf & ~PAGE_MASK;
+
+                       um->addr[i] = dma_map_page(dev->dev, pg, pg_off,
+                                                  um->len, DMA_TO_DEVICE);
+                       srcs[i] = um->addr[i] + src_off;
+                       ret = dma_mapping_error(dev->dev, um->addr[i]);
                        if (ret) {
-                               unmap_src(dev->dev, dma_srcs, len, i);
-                               thread_result_add(info, result,
-                                                 DMATEST_ET_MAP_SRC,
-                                                 total_tests, src_off, dst_off,
-                                                 len, ret);
+                               dmaengine_unmap_put(um);
+                               result("src mapping error", total_tests,
+                                      src_off, dst_off, len, ret);
                                failed_tests++;
                                continue;
                        }
+                       um->to_cnt++;
                }
                /* map with DMA_BIDIRECTIONAL to force writeback/invalidate */
+               dsts = &um->addr[src_cnt];
                for (i = 0; i < dst_cnt; i++) {
-                       dma_dsts[i] = dma_map_single(dev->dev, thread->dsts[i],
-                                                    params->buf_size,
-                                                    DMA_BIDIRECTIONAL);
-                       ret = dma_mapping_error(dev->dev, dma_dsts[i]);
+                       unsigned long buf = (unsigned long) thread->dsts[i];
+                       struct page *pg = virt_to_page(buf);
+                       unsigned pg_off = buf & ~PAGE_MASK;
+
+                       dsts[i] = dma_map_page(dev->dev, pg, pg_off, um->len,
+                                              DMA_BIDIRECTIONAL);
+                       ret = dma_mapping_error(dev->dev, dsts[i]);
                        if (ret) {
-                               unmap_src(dev->dev, dma_srcs, len, src_cnt);
-                               unmap_dst(dev->dev, dma_dsts, params->buf_size,
-                                         i);
-                               thread_result_add(info, result,
-                                                 DMATEST_ET_MAP_DST,
-                                                 total_tests, src_off, dst_off,
-                                                 len, ret);
+                               dmaengine_unmap_put(um);
+                               result("dst mapping error", total_tests,
+                                      src_off, dst_off, len, ret);
                                failed_tests++;
                                continue;
                        }
+                       um->bidi_cnt++;
                }
 
                if (thread->type == DMA_MEMCPY)
                        tx = dev->device_prep_dma_memcpy(chan,
-                                                        dma_dsts[0] + dst_off,
-                                                        dma_srcs[0], len,
-                                                        flags);
+                                                        dsts[0] + dst_off,
+                                                        srcs[0], len, flags);
                else if (thread->type == DMA_XOR)
                        tx = dev->device_prep_dma_xor(chan,
-                                                     dma_dsts[0] + dst_off,
-                                                     dma_srcs, src_cnt,
+                                                     dsts[0] + dst_off,
+                                                     srcs, src_cnt,
                                                      len, flags);
                else if (thread->type == DMA_PQ) {
                        dma_addr_t dma_pq[dst_cnt];
 
                        for (i = 0; i < dst_cnt; i++)
-                               dma_pq[i] = dma_dsts[i] + dst_off;
-                       tx = dev->device_prep_dma_pq(chan, dma_pq, dma_srcs,
+                               dma_pq[i] = dsts[i] + dst_off;
+                       tx = dev->device_prep_dma_pq(chan, dma_pq, srcs,
                                                     src_cnt, pq_coefs,
                                                     len, flags);
                }
 
                if (!tx) {
-                       unmap_src(dev->dev, dma_srcs, len, src_cnt);
-                       unmap_dst(dev->dev, dma_dsts, params->buf_size,
-                                 dst_cnt);
-                       thread_result_add(info, result, DMATEST_ET_PREP,
-                                         total_tests, src_off, dst_off,
-                                         len, 0);
+                       dmaengine_unmap_put(um);
+                       result("prep error", total_tests, src_off,
+                              dst_off, len, ret);
                        msleep(100);
                        failed_tests++;
                        continue;
@@ -712,9 +610,9 @@ static int dmatest_func(void *data)
                cookie = tx->tx_submit(tx);
 
                if (dma_submit_error(cookie)) {
-                       thread_result_add(info, result, DMATEST_ET_SUBMIT,
-                                         total_tests, src_off, dst_off,
-                                         len, cookie);
+                       dmaengine_unmap_put(um);
+                       result("submit error", total_tests, src_off,
+                              dst_off, len, ret);
                        msleep(100);
                        failed_tests++;
                        continue;
@@ -735,59 +633,59 @@ static int dmatest_func(void *data)
                         * free it this time?" dancing.  For now, just
                         * leave it dangling.
                         */
-                       thread_result_add(info, result, DMATEST_ET_TIMEOUT,
-                                         total_tests, src_off, dst_off,
-                                         len, 0);
+                       dmaengine_unmap_put(um);
+                       result("test timed out", total_tests, src_off, dst_off,
+                              len, 0);
                        failed_tests++;
                        continue;
-               } else if (status != DMA_SUCCESS) {
-                       enum dmatest_error_type type = (status == DMA_ERROR) ?
-                               DMATEST_ET_DMA_ERROR : DMATEST_ET_DMA_IN_PROGRESS;
-                       thread_result_add(info, result, type,
-                                         total_tests, src_off, dst_off,
-                                         len, status);
+               } else if (status != DMA_COMPLETE) {
+                       dmaengine_unmap_put(um);
+                       result(status == DMA_ERROR ?
+                              "completion error status" :
+                              "completion busy status", total_tests, src_off,
+                              dst_off, len, ret);
                        failed_tests++;
                        continue;
                }
 
-               /* Unmap by myself (see DMA_COMPL_SKIP_DEST_UNMAP above) */
-               unmap_dst(dev->dev, dma_dsts, params->buf_size, dst_cnt);
+               dmaengine_unmap_put(um);
 
-               error_count = 0;
+               if (params->noverify) {
+                       verbose_result("test passed", total_tests, src_off,
+                                      dst_off, len, 0);
+                       continue;
+               }
 
-               pr_debug("%s: verifying source buffer...\n", thread_name);
-               error_count += verify_result_add(info, result, total_tests,
-                               src_off, dst_off, len, thread->srcs, -1,
+               pr_debug("%s: verifying source buffer...\n", current->comm);
+               error_count = dmatest_verify(thread->srcs, 0, src_off,
                                0, PATTERN_SRC, true);
-               error_count += verify_result_add(info, result, total_tests,
-                               src_off, dst_off, len, thread->srcs, 0,
-                               src_off, PATTERN_SRC | PATTERN_COPY, true);
-               error_count += verify_result_add(info, result, total_tests,
-                               src_off, dst_off, len, thread->srcs, 1,
-                               src_off + len, PATTERN_SRC, true);
-
-               pr_debug("%s: verifying dest buffer...\n", thread_name);
-               error_count += verify_result_add(info, result, total_tests,
-                               src_off, dst_off, len, thread->dsts, -1,
+               error_count += dmatest_verify(thread->srcs, src_off,
+                               src_off + len, src_off,
+                               PATTERN_SRC | PATTERN_COPY, true);
+               error_count += dmatest_verify(thread->srcs, src_off + len,
+                               params->buf_size, src_off + len,
+                               PATTERN_SRC, true);
+
+               pr_debug("%s: verifying dest buffer...\n", current->comm);
+               error_count += dmatest_verify(thread->dsts, 0, dst_off,
                                0, PATTERN_DST, false);
-               error_count += verify_result_add(info, result, total_tests,
-                               src_off, dst_off, len, thread->dsts, 0,
-                               src_off, PATTERN_SRC | PATTERN_COPY, false);
-               error_count += verify_result_add(info, result, total_tests,
-                               src_off, dst_off, len, thread->dsts, 1,
-                               dst_off + len, PATTERN_DST, false);
+               error_count += dmatest_verify(thread->dsts, dst_off,
+                               dst_off + len, src_off,
+                               PATTERN_SRC | PATTERN_COPY, false);
+               error_count += dmatest_verify(thread->dsts, dst_off + len,
+                               params->buf_size, dst_off + len,
+                               PATTERN_DST, false);
 
                if (error_count) {
-                       thread_result_add(info, result, DMATEST_ET_VERIFY,
-                                         total_tests, src_off, dst_off,
-                                         len, error_count);
+                       result("data error", total_tests, src_off, dst_off,
+                              len, error_count);
                        failed_tests++;
                } else {
-                       thread_result_add(info, result, DMATEST_ET_OK,
-                                         total_tests, src_off, dst_off,
-                                         len, 0);
+                       verbose_result("test passed", total_tests, src_off,
+                                      dst_off, len, 0);
                }
        }
+       runtime = ktime_us_delta(ktime_get(), ktime);
 
        ret = 0;
        for (i = 0; thread->dsts[i]; i++)
@@ -802,20 +700,17 @@ err_srcbuf:
 err_srcs:
        kfree(pq_coefs);
 err_thread_type:
-       pr_notice("%s: terminating after %u tests, %u failures (status %d)\n",
-                       thread_name, total_tests, failed_tests, ret);
+       pr_info("%s: summary %u tests, %u failures %llu iops %llu KB/s (%d)\n",
+               current->comm, total_tests, failed_tests,
+               dmatest_persec(runtime, total_tests),
+               dmatest_KBs(runtime, total_len), ret);
 
        /* terminate all transfers on specified channels */
        if (ret)
                dmaengine_terminate_all(chan);
 
        thread->done = true;
-
-       if (params->iterations > 0)
-               while (!kthread_should_stop()) {
-                       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit);
-                       interruptible_sleep_on(&wait_dmatest_exit);
-               }
+       wake_up(&thread_wait);
 
        return ret;
 }
@@ -828,9 +723,10 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc)
 
        list_for_each_entry_safe(thread, _thread, &dtc->threads, node) {
                ret = kthread_stop(thread->task);
-               pr_debug("dmatest: thread %s exited with status %d\n",
-                               thread->task->comm, ret);
+               pr_debug("thread %s exited with status %d\n",
+                        thread->task->comm, ret);
                list_del(&thread->node);
+               put_task_struct(thread->task);
                kfree(thread);
        }
 
@@ -861,27 +757,27 @@ static int dmatest_add_threads(struct dmatest_info *info,
        for (i = 0; i < params->threads_per_chan; i++) {
                thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL);
                if (!thread) {
-                       pr_warning("dmatest: No memory for %s-%s%u\n",
-                                  dma_chan_name(chan), op, i);
-
+                       pr_warn("No memory for %s-%s%u\n",
+                               dma_chan_name(chan), op, i);
                        break;
                }
                thread->info = info;
                thread->chan = dtc->chan;
                thread->type = type;
                smp_wmb();
-               thread->task = kthread_run(dmatest_func, thread, "%s-%s%u",
+               thread->task = kthread_create(dmatest_func, thread, "%s-%s%u",
                                dma_chan_name(chan), op, i);
                if (IS_ERR(thread->task)) {
-                       pr_warning("dmatest: Failed to run thread %s-%s%u\n",
-                                       dma_chan_name(chan), op, i);
+                       pr_warn("Failed to create thread %s-%s%u\n",
+                               dma_chan_name(chan), op, i);
                        kfree(thread);
                        break;
                }
 
                /* srcbuf and dstbuf are allocated by the thread itself */
-
+               get_task_struct(thread->task);
                list_add_tail(&thread->node, &dtc->threads);
+               wake_up_process(thread->task);
        }
 
        return i;
@@ -897,7 +793,7 @@ static int dmatest_add_channel(struct dmatest_info *info,
 
        dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL);
        if (!dtc) {
-               pr_warning("dmatest: No memory for %s\n", dma_chan_name(chan));
+               pr_warn("No memory for %s\n", dma_chan_name(chan));
                return -ENOMEM;
        }
 
@@ -917,7 +813,7 @@ static int dmatest_add_channel(struct dmatest_info *info,
                thread_count += cnt > 0 ? cnt : 0;
        }
 
-       pr_info("dmatest: Started %u threads using %s\n",
+       pr_info("Started %u threads using %s\n",
                thread_count, dma_chan_name(chan));
 
        list_add_tail(&dtc->node, &info->channels);
@@ -937,20 +833,20 @@ static bool filter(struct dma_chan *chan, void *param)
                return true;
 }
 
-static int __run_threaded_test(struct dmatest_info *info)
+static void request_channels(struct dmatest_info *info,
+                            enum dma_transaction_type type)
 {
        dma_cap_mask_t mask;
-       struct dma_chan *chan;
-       struct dmatest_params *params = &info->params;
-       int err = 0;
 
        dma_cap_zero(mask);
-       dma_cap_set(DMA_MEMCPY, mask);
+       dma_cap_set(type, mask);
        for (;;) {
+               struct dmatest_params *params = &info->params;
+               struct dma_chan *chan;
+
                chan = dma_request_channel(mask, filter, params);
                if (chan) {
-                       err = dmatest_add_channel(info, chan);
-                       if (err) {
+                       if (dmatest_add_channel(info, chan)) {
                                dma_release_channel(chan);
                                break; /* add_channel failed, punt */
                        }
@@ -960,22 +856,30 @@ static int __run_threaded_test(struct dmatest_info *info)
                    info->nr_channels >= params->max_channels)
                        break; /* we have all we need */
        }
-       return err;
 }
 
-#ifndef MODULE
-static int run_threaded_test(struct dmatest_info *info)
+static void run_threaded_test(struct dmatest_info *info)
 {
-       int ret;
+       struct dmatest_params *params = &info->params;
 
-       mutex_lock(&info->lock);
-       ret = __run_threaded_test(info);
-       mutex_unlock(&info->lock);
-       return ret;
+       /* Copy test parameters */
+       params->buf_size = test_buf_size;
+       strlcpy(params->channel, strim(test_channel), sizeof(params->channel));
+       strlcpy(params->device, strim(test_device), sizeof(params->device));
+       params->threads_per_chan = threads_per_chan;
+       params->max_channels = max_channels;
+       params->iterations = iterations;
+       params->xor_sources = xor_sources;
+       params->pq_sources = pq_sources;
+       params->timeout = timeout;
+       params->noverify = noverify;
+
+       request_channels(info, DMA_MEMCPY);
+       request_channels(info, DMA_XOR);
+       request_channels(info, DMA_PQ);
 }
-#endif
 
-static void __stop_threaded_test(struct dmatest_info *info)
+static void stop_threaded_test(struct dmatest_info *info)
 {
        struct dmatest_chan *dtc, *_dtc;
        struct dma_chan *chan;
@@ -984,203 +888,86 @@ static void __stop_threaded_test(struct dmatest_info *info)
                list_del(&dtc->node);
                chan = dtc->chan;
                dmatest_cleanup_channel(dtc);
-               pr_debug("dmatest: dropped channel %s\n", dma_chan_name(chan));
+               pr_debug("dropped channel %s\n", dma_chan_name(chan));
                dma_release_channel(chan);
        }
 
        info->nr_channels = 0;
 }
 
-static void stop_threaded_test(struct dmatest_info *info)
+static void restart_threaded_test(struct dmatest_info *info, bool run)
 {
-       mutex_lock(&info->lock);
-       __stop_threaded_test(info);
-       mutex_unlock(&info->lock);
-}
-
-static int __restart_threaded_test(struct dmatest_info *info, bool run)
-{
-       struct dmatest_params *params = &info->params;
+       /* we might be called early to set run=, defer running until all
+        * parameters have been evaluated
+        */
+       if (!info->did_init)
+               return;
 
        /* Stop any running test first */
-       __stop_threaded_test(info);
-
-       if (run == false)
-               return 0;
-
-       /* Clear results from previous run */
-       result_free(info, NULL);
-
-       /* Copy test parameters */
-       params->buf_size = test_buf_size;
-       strlcpy(params->channel, strim(test_channel), sizeof(params->channel));
-       strlcpy(params->device, strim(test_device), sizeof(params->device));
-       params->threads_per_chan = threads_per_chan;
-       params->max_channels = max_channels;
-       params->iterations = iterations;
-       params->xor_sources = xor_sources;
-       params->pq_sources = pq_sources;
-       params->timeout = timeout;
+       stop_threaded_test(info);
 
        /* Run test with new parameters */
-       return __run_threaded_test(info);
-}
-
-static bool __is_threaded_test_run(struct dmatest_info *info)
-{
-       struct dmatest_chan *dtc;
-
-       list_for_each_entry(dtc, &info->channels, node) {
-               struct dmatest_thread *thread;
-
-               list_for_each_entry(thread, &dtc->threads, node) {
-                       if (!thread->done)
-                               return true;
-               }
-       }
-
-       return false;
+       run_threaded_test(info);
 }
 
-static ssize_t dtf_read_run(struct file *file, char __user *user_buf,
-               size_t count, loff_t *ppos)
+static int dmatest_run_get(char *val, const struct kernel_param *kp)
 {
-       struct dmatest_info *info = file->private_data;
-       char buf[3];
+       struct dmatest_info *info = &test_info;
 
        mutex_lock(&info->lock);
-
-       if (__is_threaded_test_run(info)) {
-               buf[0] = 'Y';
+       if (is_threaded_test_run(info)) {
+               dmatest_run = true;
        } else {
-               __stop_threaded_test(info);
-               buf[0] = 'N';
+               stop_threaded_test(info);
+               dmatest_run = false;
        }
-
        mutex_unlock(&info->lock);
-       buf[1] = '\n';
-       buf[2] = 0x00;
-       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static ssize_t dtf_write_run(struct file *file, const char __user *user_buf,
-               size_t count, loff_t *ppos)
-{
-       struct dmatest_info *info = file->private_data;
-       char buf[16];
-       bool bv;
-       int ret = 0;
 
-       if (copy_from_user(buf, user_buf, min(count, (sizeof(buf) - 1))))
-               return -EFAULT;
-
-       if (strtobool(buf, &bv) == 0) {
-               mutex_lock(&info->lock);
-
-               if (__is_threaded_test_run(info))
-                       ret = -EBUSY;
-               else
-                       ret = __restart_threaded_test(info, bv);
-
-               mutex_unlock(&info->lock);
-       }
-
-       return ret ? ret : count;
+       return param_get_bool(val, kp);
 }
 
-static const struct file_operations dtf_run_fops = {
-       .read   = dtf_read_run,
-       .write  = dtf_write_run,
-       .open   = simple_open,
-       .llseek = default_llseek,
-};
-
-static int dtf_results_show(struct seq_file *sf, void *data)
+static int dmatest_run_set(const char *val, const struct kernel_param *kp)
 {
-       struct dmatest_info *info = sf->private;
-       struct dmatest_result *result;
-       struct dmatest_thread_result *tr;
-       unsigned int i;
+       struct dmatest_info *info = &test_info;
+       int ret;
 
-       mutex_lock(&info->results_lock);
-       list_for_each_entry(result, &info->results, node) {
-               list_for_each_entry(tr, &result->results, node) {
-                       seq_printf(sf, "%s\n",
-                               thread_result_get(result->name, tr));
-                       if (tr->type == DMATEST_ET_VERIFY_BUF) {
-                               for (i = 0; i < tr->vr->error_count; i++) {
-                                       seq_printf(sf, "\t%s\n",
-                                               verify_result_get_one(tr->vr, i));
-                               }
-                       }
-               }
+       mutex_lock(&info->lock);
+       ret = param_set_bool(val, kp);
+       if (ret) {
+               mutex_unlock(&info->lock);
+               return ret;
        }
 
-       mutex_unlock(&info->results_lock);
-       return 0;
-}
-
-static int dtf_results_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, dtf_results_show, inode->i_private);
-}
-
-static const struct file_operations dtf_results_fops = {
-       .open           = dtf_results_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int dmatest_register_dbgfs(struct dmatest_info *info)
-{
-       struct dentry *d;
-
-       d = debugfs_create_dir("dmatest", NULL);
-       if (IS_ERR(d))
-               return PTR_ERR(d);
-       if (!d)
-               goto err_root;
+       if (is_threaded_test_run(info))
+               ret = -EBUSY;
+       else if (dmatest_run)
+               restart_threaded_test(info, dmatest_run);
 
-       info->root = d;
-
-       /* Run or stop threaded test */
-       debugfs_create_file("run", S_IWUSR | S_IRUGO, info->root, info,
-                           &dtf_run_fops);
-
-       /* Results of test in progress */
-       debugfs_create_file("results", S_IRUGO, info->root, info,
-                           &dtf_results_fops);
-
-       return 0;
+       mutex_unlock(&info->lock);
 
-err_root:
-       pr_err("dmatest: Failed to initialize debugfs\n");
-       return -ENOMEM;
+       return ret;
 }
 
 static int __init dmatest_init(void)
 {
        struct dmatest_info *info = &test_info;
-       int ret;
-
-       memset(info, 0, sizeof(*info));
+       struct dmatest_params *params = &info->params;
 
-       mutex_init(&info->lock);
-       INIT_LIST_HEAD(&info->channels);
+       if (dmatest_run) {
+               mutex_lock(&info->lock);
+               run_threaded_test(info);
+               mutex_unlock(&info->lock);
+       }
 
-       mutex_init(&info->results_lock);
-       INIT_LIST_HEAD(&info->results);
+       if (params->iterations && wait)
+               wait_event(thread_wait, !is_threaded_test_run(info));
 
-       ret = dmatest_register_dbgfs(info);
-       if (ret)
-               return ret;
+       /* module parameters are stable, inittime tests are started,
+        * let userspace take over 'run' control
+        */
+       info->did_init = true;
 
-#ifdef MODULE
        return 0;
-#else
-       return run_threaded_test(info);
-#endif
 }
 /* when compiled-in wait for drivers to load first */
 late_initcall(dmatest_init);
@@ -1189,9 +976,9 @@ static void __exit dmatest_exit(void)
 {
        struct dmatest_info *info = &test_info;
 
-       debugfs_remove_recursive(info->root);
+       mutex_lock(&info->lock);
        stop_threaded_test(info);
-       result_free(info, NULL);
+       mutex_unlock(&info->lock);
 }
 module_exit(dmatest_exit);
 
index 89eb89f..7516be4 100644 (file)
@@ -85,10 +85,6 @@ static struct device *chan2dev(struct dma_chan *chan)
 {
        return &chan->dev->device;
 }
-static struct device *chan2parent(struct dma_chan *chan)
-{
-       return chan->dev->device.parent;
-}
 
 static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc)
 {
@@ -311,26 +307,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc,
        list_splice_init(&desc->tx_list, &dwc->free_list);
        list_move(&desc->desc_node, &dwc->free_list);
 
-       if (!is_slave_direction(dwc->direction)) {
-               struct device *parent = chan2parent(&dwc->chan);
-               if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-                       if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE)
-                               dma_unmap_single(parent, desc->lli.dar,
-                                       desc->total_len, DMA_FROM_DEVICE);
-                       else
-                               dma_unmap_page(parent, desc->lli.dar,
-                                       desc->total_len, DMA_FROM_DEVICE);
-               }
-               if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-                       if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE)
-                               dma_unmap_single(parent, desc->lli.sar,
-                                       desc->total_len, DMA_TO_DEVICE);
-                       else
-                               dma_unmap_page(parent, desc->lli.sar,
-                                       desc->total_len, DMA_TO_DEVICE);
-               }
-       }
-
+       dma_descriptor_unmap(txd);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
        if (callback)
@@ -1098,13 +1075,13 @@ dwc_tx_status(struct dma_chan *chan,
        enum dma_status         ret;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
 
        dwc_scan_descriptors(to_dw_dma(chan->device), dwc);
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret != DMA_SUCCESS)
+       if (ret != DMA_COMPLETE)
                dma_set_residue(txstate, dwc_get_residue(dwc));
 
        if (dwc->paused && ret == DMA_IN_PROGRESS)
index bef8a36..2539ea0 100644 (file)
 #define EDMA_CHANS     64
 #endif /* CONFIG_ARCH_DAVINCI_DA8XX */
 
-/* Max of 16 segments per channel to conserve PaRAM slots */
-#define MAX_NR_SG              16
+/*
+ * Max of 20 segments per channel to conserve PaRAM slots
+ * Also note that MAX_NR_SG should be atleast the no.of periods
+ * that are required for ASoC, otherwise DMA prep calls will
+ * fail. Today davinci-pcm is the only user of this driver and
+ * requires atleast 17 slots, so we setup the default to 20.
+ */
+#define MAX_NR_SG              20
 #define EDMA_MAX_SLOTS         MAX_NR_SG
 #define EDMA_DESCRIPTORS       16
 
 struct edma_desc {
        struct virt_dma_desc            vdesc;
        struct list_head                node;
+       int                             cyclic;
        int                             absync;
        int                             pset_nr;
        int                             processed;
@@ -167,8 +174,13 @@ static void edma_execute(struct edma_chan *echan)
         * then setup a link to the dummy slot, this results in all future
         * events being absorbed and that's OK because we're done
         */
-       if (edesc->processed == edesc->pset_nr)
-               edma_link(echan->slot[nslots-1], echan->ecc->dummy_slot);
+       if (edesc->processed == edesc->pset_nr) {
+               if (edesc->cyclic)
+                       edma_link(echan->slot[nslots-1], echan->slot[1]);
+               else
+                       edma_link(echan->slot[nslots-1],
+                                 echan->ecc->dummy_slot);
+       }
 
        edma_resume(echan->ch_num);
 
@@ -250,6 +262,117 @@ static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        return ret;
 }
 
+/*
+ * A PaRAM set configuration abstraction used by other modes
+ * @chan: Channel who's PaRAM set we're configuring
+ * @pset: PaRAM set to initialize and setup.
+ * @src_addr: Source address of the DMA
+ * @dst_addr: Destination address of the DMA
+ * @burst: In units of dev_width, how much to send
+ * @dev_width: How much is the dev_width
+ * @dma_length: Total length of the DMA transfer
+ * @direction: Direction of the transfer
+ */
+static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset,
+       dma_addr_t src_addr, dma_addr_t dst_addr, u32 burst,
+       enum dma_slave_buswidth dev_width, unsigned int dma_length,
+       enum dma_transfer_direction direction)
+{
+       struct edma_chan *echan = to_edma_chan(chan);
+       struct device *dev = chan->device->dev;
+       int acnt, bcnt, ccnt, cidx;
+       int src_bidx, dst_bidx, src_cidx, dst_cidx;
+       int absync;
+
+       acnt = dev_width;
+       /*
+        * If the maxburst is equal to the fifo width, use
+        * A-synced transfers. This allows for large contiguous
+        * buffer transfers using only one PaRAM set.
+        */
+       if (burst == 1) {
+               /*
+                * For the A-sync case, bcnt and ccnt are the remainder
+                * and quotient respectively of the division of:
+                * (dma_length / acnt) by (SZ_64K -1). This is so
+                * that in case bcnt over flows, we have ccnt to use.
+                * Note: In A-sync tranfer only, bcntrld is used, but it
+                * only applies for sg_dma_len(sg) >= SZ_64K.
+                * In this case, the best way adopted is- bccnt for the
+                * first frame will be the remainder below. Then for
+                * every successive frame, bcnt will be SZ_64K-1. This
+                * is assured as bcntrld = 0xffff in end of function.
+                */
+               absync = false;
+               ccnt = dma_length / acnt / (SZ_64K - 1);
+               bcnt = dma_length / acnt - ccnt * (SZ_64K - 1);
+               /*
+                * If bcnt is non-zero, we have a remainder and hence an
+                * extra frame to transfer, so increment ccnt.
+                */
+               if (bcnt)
+                       ccnt++;
+               else
+                       bcnt = SZ_64K - 1;
+               cidx = acnt;
+       } else {
+               /*
+                * If maxburst is greater than the fifo address_width,
+                * use AB-synced transfers where A count is the fifo
+                * address_width and B count is the maxburst. In this
+                * case, we are limited to transfers of C count frames
+                * of (address_width * maxburst) where C count is limited
+                * to SZ_64K-1. This places an upper bound on the length
+                * of an SG segment that can be handled.
+                */
+               absync = true;
+               bcnt = burst;
+               ccnt = dma_length / (acnt * bcnt);
+               if (ccnt > (SZ_64K - 1)) {
+                       dev_err(dev, "Exceeded max SG segment size\n");
+                       return -EINVAL;
+               }
+               cidx = acnt * bcnt;
+       }
+
+       if (direction == DMA_MEM_TO_DEV) {
+               src_bidx = acnt;
+               src_cidx = cidx;
+               dst_bidx = 0;
+               dst_cidx = 0;
+       } else if (direction == DMA_DEV_TO_MEM)  {
+               src_bidx = 0;
+               src_cidx = 0;
+               dst_bidx = acnt;
+               dst_cidx = cidx;
+       } else {
+               dev_err(dev, "%s: direction not implemented yet\n", __func__);
+               return -EINVAL;
+       }
+
+       pset->opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num));
+       /* Configure A or AB synchronized transfers */
+       if (absync)
+               pset->opt |= SYNCDIM;
+
+       pset->src = src_addr;
+       pset->dst = dst_addr;
+
+       pset->src_dst_bidx = (dst_bidx << 16) | src_bidx;
+       pset->src_dst_cidx = (dst_cidx << 16) | src_cidx;
+
+       pset->a_b_cnt = bcnt << 16 | acnt;
+       pset->ccnt = ccnt;
+       /*
+        * Only time when (bcntrld) auto reload is required is for
+        * A-sync case, and in this case, a requirement of reload value
+        * of SZ_64K-1 only is assured. 'link' is initially set to NULL
+        * and then later will be populated by edma_execute.
+        */
+       pset->link_bcntrld = 0xffffffff;
+       return absync;
+}
+
 static struct dma_async_tx_descriptor *edma_prep_slave_sg(
        struct dma_chan *chan, struct scatterlist *sgl,
        unsigned int sg_len, enum dma_transfer_direction direction,
@@ -258,23 +381,21 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
        struct edma_chan *echan = to_edma_chan(chan);
        struct device *dev = chan->device->dev;
        struct edma_desc *edesc;
-       dma_addr_t dev_addr;
+       dma_addr_t src_addr = 0, dst_addr = 0;
        enum dma_slave_buswidth dev_width;
        u32 burst;
        struct scatterlist *sg;
-       int acnt, bcnt, ccnt, src, dst, cidx;
-       int src_bidx, dst_bidx, src_cidx, dst_cidx;
-       int i, nslots;
+       int i, nslots, ret;
 
        if (unlikely(!echan || !sgl || !sg_len))
                return NULL;
 
        if (direction == DMA_DEV_TO_MEM) {
-               dev_addr = echan->cfg.src_addr;
+               src_addr = echan->cfg.src_addr;
                dev_width = echan->cfg.src_addr_width;
                burst = echan->cfg.src_maxburst;
        } else if (direction == DMA_MEM_TO_DEV) {
-               dev_addr = echan->cfg.dst_addr;
+               dst_addr = echan->cfg.dst_addr;
                dev_width = echan->cfg.dst_addr_width;
                burst = echan->cfg.dst_maxburst;
        } else {
@@ -307,7 +428,6 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
                        if (echan->slot[i] < 0) {
                                kfree(edesc);
                                dev_err(dev, "Failed to allocate slot\n");
-                               kfree(edesc);
                                return NULL;
                        }
                }
@@ -315,64 +435,21 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
 
        /* Configure PaRAM sets for each SG */
        for_each_sg(sgl, sg, sg_len, i) {
-
-               acnt = dev_width;
-
-               /*
-                * If the maxburst is equal to the fifo width, use
-                * A-synced transfers. This allows for large contiguous
-                * buffer transfers using only one PaRAM set.
-                */
-               if (burst == 1) {
-                       edesc->absync = false;
-                       ccnt = sg_dma_len(sg) / acnt / (SZ_64K - 1);
-                       bcnt = sg_dma_len(sg) / acnt - ccnt * (SZ_64K - 1);
-                       if (bcnt)
-                               ccnt++;
-                       else
-                               bcnt = SZ_64K - 1;
-                       cidx = acnt;
-               /*
-                * If maxburst is greater than the fifo address_width,
-                * use AB-synced transfers where A count is the fifo
-                * address_width and B count is the maxburst. In this
-                * case, we are limited to transfers of C count frames
-                * of (address_width * maxburst) where C count is limited
-                * to SZ_64K-1. This places an upper bound on the length
-                * of an SG segment that can be handled.
-                */
-               } else {
-                       edesc->absync = true;
-                       bcnt = burst;
-                       ccnt = sg_dma_len(sg) / (acnt * bcnt);
-                       if (ccnt > (SZ_64K - 1)) {
-                               dev_err(dev, "Exceeded max SG segment size\n");
-                               kfree(edesc);
-                               return NULL;
-                       }
-                       cidx = acnt * bcnt;
+               /* Get address for each SG */
+               if (direction == DMA_DEV_TO_MEM)
+                       dst_addr = sg_dma_address(sg);
+               else
+                       src_addr = sg_dma_address(sg);
+
+               ret = edma_config_pset(chan, &edesc->pset[i], src_addr,
+                                      dst_addr, burst, dev_width,
+                                      sg_dma_len(sg), direction);
+               if (ret < 0) {
+                       kfree(edesc);
+                       return NULL;
                }
 
-               if (direction == DMA_MEM_TO_DEV) {
-                       src = sg_dma_address(sg);
-                       dst = dev_addr;
-                       src_bidx = acnt;
-                       src_cidx = cidx;
-                       dst_bidx = 0;
-                       dst_cidx = 0;
-               } else {
-                       src = dev_addr;
-                       dst = sg_dma_address(sg);
-                       src_bidx = 0;
-                       src_cidx = 0;
-                       dst_bidx = acnt;
-                       dst_cidx = cidx;
-               }
-
-               edesc->pset[i].opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num));
-               /* Configure A or AB synchronized transfers */
-               if (edesc->absync)
-                       edesc->pset[i].opt |= SYNCDIM;
+               edesc->absync = ret;
 
                /* If this is the last in a current SG set of transactions,
                   enable interrupts so that next set is processed */
@@ -382,17 +459,138 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
                /* If this is the last set, enable completion interrupt flag */
                if (i == sg_len - 1)
                        edesc->pset[i].opt |= TCINTEN;
+       }
 
-               edesc->pset[i].src = src;
-               edesc->pset[i].dst = dst;
+       return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags);
+}
 
-               edesc->pset[i].src_dst_bidx = (dst_bidx << 16) | src_bidx;
-               edesc->pset[i].src_dst_cidx = (dst_cidx << 16) | src_cidx;
+static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
+       struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
+       size_t period_len, enum dma_transfer_direction direction,
+       unsigned long tx_flags, void *context)
+{
+       struct edma_chan *echan = to_edma_chan(chan);
+       struct device *dev = chan->device->dev;
+       struct edma_desc *edesc;
+       dma_addr_t src_addr, dst_addr;
+       enum dma_slave_buswidth dev_width;
+       u32 burst;
+       int i, ret, nslots;
+
+       if (unlikely(!echan || !buf_len || !period_len))
+               return NULL;
+
+       if (direction == DMA_DEV_TO_MEM) {
+               src_addr = echan->cfg.src_addr;
+               dst_addr = buf_addr;
+               dev_width = echan->cfg.src_addr_width;
+               burst = echan->cfg.src_maxburst;
+       } else if (direction == DMA_MEM_TO_DEV) {
+               src_addr = buf_addr;
+               dst_addr = echan->cfg.dst_addr;
+               dev_width = echan->cfg.dst_addr_width;
+               burst = echan->cfg.dst_maxburst;
+       } else {
+               dev_err(dev, "%s: bad direction?\n", __func__);
+               return NULL;
+       }
+
+       if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) {
+               dev_err(dev, "Undefined slave buswidth\n");
+               return NULL;
+       }
+
+       if (unlikely(buf_len % period_len)) {
+               dev_err(dev, "Period should be multiple of Buffer length\n");
+               return NULL;
+       }
+
+       nslots = (buf_len / period_len) + 1;
+
+       /*
+        * Cyclic DMA users such as audio cannot tolerate delays introduced
+        * by cases where the number of periods is more than the maximum
+        * number of SGs the EDMA driver can handle at a time. For DMA types
+        * such as Slave SGs, such delays are tolerable and synchronized,
+        * but the synchronization is difficult to achieve with Cyclic and
+        * cannot be guaranteed, so we error out early.
+        */
+       if (nslots > MAX_NR_SG)
+               return NULL;
+
+       edesc = kzalloc(sizeof(*edesc) + nslots *
+               sizeof(edesc->pset[0]), GFP_ATOMIC);
+       if (!edesc) {
+               dev_dbg(dev, "Failed to allocate a descriptor\n");
+               return NULL;
+       }
+
+       edesc->cyclic = 1;
+       edesc->pset_nr = nslots;
+
+       dev_dbg(dev, "%s: nslots=%d\n", __func__, nslots);
+       dev_dbg(dev, "%s: period_len=%d\n", __func__, period_len);
+       dev_dbg(dev, "%s: buf_len=%d\n", __func__, buf_len);
+
+       for (i = 0; i < nslots; i++) {
+               /* Allocate a PaRAM slot, if needed */
+               if (echan->slot[i] < 0) {
+                       echan->slot[i] =
+                               edma_alloc_slot(EDMA_CTLR(echan->ch_num),
+                                               EDMA_SLOT_ANY);
+                       if (echan->slot[i] < 0) {
+                               dev_err(dev, "Failed to allocate slot\n");
+                               return NULL;
+                       }
+               }
+
+               if (i == nslots - 1) {
+                       memcpy(&edesc->pset[i], &edesc->pset[0],
+                              sizeof(edesc->pset[0]));
+                       break;
+               }
+
+               ret = edma_config_pset(chan, &edesc->pset[i], src_addr,
+                                      dst_addr, burst, dev_width, period_len,
+                                      direction);
+               if (ret < 0)
+                       return NULL;
 
-               edesc->pset[i].a_b_cnt = bcnt << 16 | acnt;
-               edesc->pset[i].ccnt = ccnt;
-               edesc->pset[i].link_bcntrld = 0xffffffff;
+               if (direction == DMA_DEV_TO_MEM)
+                       dst_addr += period_len;
+               else
+                       src_addr += period_len;
 
+               dev_dbg(dev, "%s: Configure period %d of buf:\n", __func__, i);
+               dev_dbg(dev,
+                       "\n pset[%d]:\n"
+                       "  chnum\t%d\n"
+                       "  slot\t%d\n"
+                       "  opt\t%08x\n"
+                       "  src\t%08x\n"
+                       "  dst\t%08x\n"
+                       "  abcnt\t%08x\n"
+                       "  ccnt\t%08x\n"
+                       "  bidx\t%08x\n"
+                       "  cidx\t%08x\n"
+                       "  lkrld\t%08x\n",
+                       i, echan->ch_num, echan->slot[i],
+                       edesc->pset[i].opt,
+                       edesc->pset[i].src,
+                       edesc->pset[i].dst,
+                       edesc->pset[i].a_b_cnt,
+                       edesc->pset[i].ccnt,
+                       edesc->pset[i].src_dst_bidx,
+                       edesc->pset[i].src_dst_cidx,
+                       edesc->pset[i].link_bcntrld);
+
+               edesc->absync = ret;
+
+               /*
+                * Enable interrupts for every period because callback
+                * has to be called for every period.
+                */
+               edesc->pset[i].opt |= TCINTEN;
        }
 
        return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags);
@@ -406,30 +604,34 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data)
        unsigned long flags;
        struct edmacc_param p;
 
-       /* Pause the channel */
-       edma_pause(echan->ch_num);
+       edesc = echan->edesc;
+
+       /* Pause the channel for non-cyclic */
+       if (!edesc || (edesc && !edesc->cyclic))
+               edma_pause(echan->ch_num);
 
        switch (ch_status) {
-       case DMA_COMPLETE:
+       case EDMA_DMA_COMPLETE:
                spin_lock_irqsave(&echan->vchan.lock, flags);
 
-               edesc = echan->edesc;
                if (edesc) {
-                       if (edesc->processed == edesc->pset_nr) {
+                       if (edesc->cyclic) {
+                               vchan_cyclic_callback(&edesc->vdesc);
+                       } else if (edesc->processed == edesc->pset_nr) {
                                dev_dbg(dev, "Transfer complete, stopping channel %d\n", ch_num);
                                edma_stop(echan->ch_num);
                                vchan_cookie_complete(&edesc->vdesc);
+                               edma_execute(echan);
                        } else {
                                dev_dbg(dev, "Intermediate transfer complete on channel %d\n", ch_num);
+                               edma_execute(echan);
                        }
-
-                       edma_execute(echan);
                }
 
                spin_unlock_irqrestore(&echan->vchan.lock, flags);
 
                break;
-       case DMA_CC_ERROR:
+       case EDMA_DMA_CC_ERROR:
                spin_lock_irqsave(&echan->vchan.lock, flags);
 
                edma_read_slot(EDMA_CHAN_SLOT(echan->slot[0]), &p);
@@ -579,7 +781,7 @@ static enum dma_status edma_tx_status(struct dma_chan *chan,
        unsigned long flags;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret == DMA_SUCCESS || !txstate)
+       if (ret == DMA_COMPLETE || !txstate)
                return ret;
 
        spin_lock_irqsave(&echan->vchan.lock, flags);
@@ -619,6 +821,7 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma,
                          struct device *dev)
 {
        dma->device_prep_slave_sg = edma_prep_slave_sg;
+       dma->device_prep_dma_cyclic = edma_prep_dma_cyclic;
        dma->device_alloc_chan_resources = edma_alloc_chan_resources;
        dma->device_free_chan_resources = edma_free_chan_resources;
        dma->device_issue_pending = edma_issue_pending;
index 591cd8c..cb4bf68 100644 (file)
@@ -733,28 +733,6 @@ static void ep93xx_dma_advance_work(struct ep93xx_dma_chan *edmac)
        spin_unlock_irqrestore(&edmac->lock, flags);
 }
 
-static void ep93xx_dma_unmap_buffers(struct ep93xx_dma_desc *desc)
-{
-       struct device *dev = desc->txd.chan->device->dev;
-
-       if (!(desc->txd.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-               if (desc->txd.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
-                       dma_unmap_single(dev, desc->src_addr, desc->size,
-                                        DMA_TO_DEVICE);
-               else
-                       dma_unmap_page(dev, desc->src_addr, desc->size,
-                                      DMA_TO_DEVICE);
-       }
-       if (!(desc->txd.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-               if (desc->txd.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
-                       dma_unmap_single(dev, desc->dst_addr, desc->size,
-                                        DMA_FROM_DEVICE);
-               else
-                       dma_unmap_page(dev, desc->dst_addr, desc->size,
-                                      DMA_FROM_DEVICE);
-       }
-}
-
 static void ep93xx_dma_tasklet(unsigned long data)
 {
        struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data;
@@ -787,13 +765,7 @@ static void ep93xx_dma_tasklet(unsigned long data)
 
        /* Now we can release all the chained descriptors */
        list_for_each_entry_safe(desc, d, &list, node) {
-               /*
-                * For the memcpy channels the API requires us to unmap the
-                * buffers unless requested otherwise.
-                */
-               if (!edmac->chan.private)
-                       ep93xx_dma_unmap_buffers(desc);
-
+               dma_descriptor_unmap(&desc->txd);
                ep93xx_dma_desc_put(edmac, desc);
        }
 
index 61517dd..7086a16 100644 (file)
@@ -870,22 +870,7 @@ static void fsldma_cleanup_descriptor(struct fsldma_chan *chan,
        /* Run any dependencies */
        dma_run_dependencies(txd);
 
-       /* Unmap the dst buffer, if requested */
-       if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-               if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE)
-                       dma_unmap_single(dev, dst, len, DMA_FROM_DEVICE);
-               else
-                       dma_unmap_page(dev, dst, len, DMA_FROM_DEVICE);
-       }
-
-       /* Unmap the src buffer, if requested */
-       if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-               if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE)
-                       dma_unmap_single(dev, src, len, DMA_TO_DEVICE);
-               else
-                       dma_unmap_page(dev, src, len, DMA_TO_DEVICE);
-       }
-
+       dma_descriptor_unmap(txd);
 #ifdef FSL_DMA_LD_DEBUG
        chan_dbg(chan, "LD %p free\n", desc);
 #endif
@@ -1255,7 +1240,9 @@ static int fsl_dma_chan_probe(struct fsldma_device *fdev,
        WARN_ON(fdev->feature != chan->feature);
 
        chan->dev = fdev->dev;
-       chan->id = ((res.start - 0x100) & 0xfff) >> 7;
+       chan->id = (res.start & 0xfff) < 0x300 ?
+                  ((res.start - 0x100) & 0xfff) >> 7 :
+                  ((res.start - 0x200) & 0xfff) >> 7;
        if (chan->id >= FSL_DMA_MAX_CHANS_PER_DEVICE) {
                dev_err(fdev->dev, "too many channels for device\n");
                err = -EINVAL;
@@ -1428,6 +1415,7 @@ static int fsldma_of_remove(struct platform_device *op)
 }
 
 static const struct of_device_id fsldma_of_ids[] = {
+       { .compatible = "fsl,elo3-dma", },
        { .compatible = "fsl,eloplus-dma", },
        { .compatible = "fsl,elo-dma", },
        {}
@@ -1449,7 +1437,7 @@ static struct platform_driver fsldma_of_driver = {
 
 static __init int fsldma_init(void)
 {
-       pr_info("Freescale Elo / Elo Plus DMA driver\n");
+       pr_info("Freescale Elo series DMA driver\n");
        return platform_driver_register(&fsldma_of_driver);
 }
 
@@ -1461,5 +1449,5 @@ static void __exit fsldma_exit(void)
 subsys_initcall(fsldma_init);
 module_exit(fsldma_exit);
 
-MODULE_DESCRIPTION("Freescale Elo / Elo Plus DMA driver");
+MODULE_DESCRIPTION("Freescale Elo series DMA driver");
 MODULE_LICENSE("GPL");
index f5c3879..1ffc244 100644 (file)
@@ -112,7 +112,7 @@ struct fsldma_chan_regs {
 };
 
 struct fsldma_chan;
-#define FSL_DMA_MAX_CHANS_PER_DEVICE 4
+#define FSL_DMA_MAX_CHANS_PER_DEVICE 8
 
 struct fsldma_device {
        void __iomem *regs;     /* DGSR register base */
index 55852c0..6f9ac20 100644 (file)
@@ -572,9 +572,11 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
 
                imx_dmav1_writel(imxdma, d->len, DMA_CNTR(imxdmac->channel));
 
-               dev_dbg(imxdma->dev, "%s channel: %d dest=0x%08x src=0x%08x "
-                       "dma_length=%d\n", __func__, imxdmac->channel,
-                       d->dest, d->src, d->len);
+               dev_dbg(imxdma->dev,
+                       "%s channel: %d dest=0x%08llx src=0x%08llx dma_length=%zu\n",
+                       __func__, imxdmac->channel,
+                       (unsigned long long)d->dest,
+                       (unsigned long long)d->src, d->len);
 
                break;
        /* Cyclic transfer is the same as slave_sg with special sg configuration. */
@@ -586,20 +588,22 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
                        imx_dmav1_writel(imxdma, imxdmac->ccr_from_device,
                                         DMA_CCR(imxdmac->channel));
 
-                       dev_dbg(imxdma->dev, "%s channel: %d sg=%p sgcount=%d "
-                               "total length=%d dev_addr=0x%08x (dev2mem)\n",
-                               __func__, imxdmac->channel, d->sg, d->sgcount,
-                               d->len, imxdmac->per_address);
+                       dev_dbg(imxdma->dev,
+                               "%s channel: %d sg=%p sgcount=%d total length=%zu dev_addr=0x%08llx (dev2mem)\n",
+                               __func__, imxdmac->channel,
+                               d->sg, d->sgcount, d->len,
+                               (unsigned long long)imxdmac->per_address);
                } else if (d->direction == DMA_MEM_TO_DEV) {
                        imx_dmav1_writel(imxdma, imxdmac->per_address,
                                         DMA_DAR(imxdmac->channel));
                        imx_dmav1_writel(imxdma, imxdmac->ccr_to_device,
                                         DMA_CCR(imxdmac->channel));
 
-                       dev_dbg(imxdma->dev, "%s channel: %d sg=%p sgcount=%d "
-                               "total length=%d dev_addr=0x%08x (mem2dev)\n",
-                               __func__, imxdmac->channel, d->sg, d->sgcount,
-                               d->len, imxdmac->per_address);
+                       dev_dbg(imxdma->dev,
+                               "%s channel: %d sg=%p sgcount=%d total length=%zu dev_addr=0x%08llx (mem2dev)\n",
+                               __func__, imxdmac->channel,
+                               d->sg, d->sgcount, d->len,
+                               (unsigned long long)imxdmac->per_address);
                } else {
                        dev_err(imxdma->dev, "%s channel: %d bad dma mode\n",
                                __func__, imxdmac->channel);
@@ -771,7 +775,7 @@ static int imxdma_alloc_chan_resources(struct dma_chan *chan)
                desc->desc.tx_submit = imxdma_tx_submit;
                /* txd.flags will be overwritten in prep funcs */
                desc->desc.flags = DMA_CTRL_ACK;
-               desc->status = DMA_SUCCESS;
+               desc->status = DMA_COMPLETE;
 
                list_add_tail(&desc->node, &imxdmac->ld_free);
                imxdmac->descs_allocated++;
@@ -870,7 +874,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
        int i;
        unsigned int periods = buf_len / period_len;
 
-       dev_dbg(imxdma->dev, "%s channel: %d buf_len=%d period_len=%d\n",
+       dev_dbg(imxdma->dev, "%s channel: %d buf_len=%zu period_len=%zu\n",
                        __func__, imxdmac->channel, buf_len, period_len);
 
        if (list_empty(&imxdmac->ld_free) ||
@@ -926,8 +930,9 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy(
        struct imxdma_engine *imxdma = imxdmac->imxdma;
        struct imxdma_desc *desc;
 
-       dev_dbg(imxdma->dev, "%s channel: %d src=0x%x dst=0x%x len=%d\n",
-                       __func__, imxdmac->channel, src, dest, len);
+       dev_dbg(imxdma->dev, "%s channel: %d src=0x%llx dst=0x%llx len=%zu\n",
+               __func__, imxdmac->channel, (unsigned long long)src,
+               (unsigned long long)dest, len);
 
        if (list_empty(&imxdmac->ld_free) ||
            imxdma_chan_is_doing_cyclic(imxdmac))
@@ -956,9 +961,10 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_interleaved(
        struct imxdma_engine *imxdma = imxdmac->imxdma;
        struct imxdma_desc *desc;
 
-       dev_dbg(imxdma->dev, "%s channel: %d src_start=0x%x dst_start=0x%x\n"
-               "   src_sgl=%s dst_sgl=%s numf=%d frame_size=%d\n", __func__,
-               imxdmac->channel, xt->src_start, xt->dst_start,
+       dev_dbg(imxdma->dev, "%s channel: %d src_start=0x%llx dst_start=0x%llx\n"
+               "   src_sgl=%s dst_sgl=%s numf=%zu frame_size=%zu\n", __func__,
+               imxdmac->channel, (unsigned long long)xt->src_start,
+               (unsigned long long) xt->dst_start,
                xt->src_sgl ? "true" : "false", xt->dst_sgl ? "true" : "false",
                xt->numf, xt->frame_size);
 
index c1fd504..c75679d 100644 (file)
@@ -638,7 +638,7 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac)
        if (error)
                sdmac->status = DMA_ERROR;
        else
-               sdmac->status = DMA_SUCCESS;
+               sdmac->status = DMA_COMPLETE;
 
        dma_cookie_complete(&sdmac->desc);
        if (sdmac->desc.callback)
@@ -1089,8 +1089,8 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg(
                        param &= ~BD_CONT;
                }
 
-               dev_dbg(sdma->dev, "entry %d: count: %d dma: 0x%08x %s%s\n",
-                               i, count, sg->dma_address,
+               dev_dbg(sdma->dev, "entry %d: count: %d dma: %#llx %s%s\n",
+                               i, count, (u64)sg->dma_address,
                                param & BD_WRAP ? "wrap" : "",
                                param & BD_INTR ? " intr" : "");
 
@@ -1163,8 +1163,8 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic(
                if (i + 1 == num_periods)
                        param |= BD_WRAP;
 
-               dev_dbg(sdma->dev, "entry %d: count: %d dma: 0x%08x %s%s\n",
-                               i, period_len, dma_addr,
+               dev_dbg(sdma->dev, "entry %d: count: %d dma: %#llx %s%s\n",
+                               i, period_len, (u64)dma_addr,
                                param & BD_WRAP ? "wrap" : "",
                                param & BD_INTR ? " intr" : "");
 
index a975ebe..1aab813 100644 (file)
@@ -309,7 +309,7 @@ static void midc_descriptor_complete(struct intel_mid_dma_chan *midc,
                callback_txd(param_txd);
        }
        if (midc->raw_tfr) {
-               desc->status = DMA_SUCCESS;
+               desc->status = DMA_COMPLETE;
                if (desc->lli != NULL) {
                        pci_pool_free(desc->lli_pool, desc->lli,
                                                desc->lli_phys);
@@ -481,7 +481,7 @@ static enum dma_status intel_mid_dma_tx_status(struct dma_chan *chan,
        enum dma_status ret;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret != DMA_SUCCESS) {
+       if (ret != DMA_COMPLETE) {
                spin_lock_bh(&midc->lock);
                midc_scan_descriptors(to_middma_device(chan->device), midc);
                spin_unlock_bh(&midc->lock);
index 5ff6fc1..1a49c77 100644 (file)
@@ -531,21 +531,6 @@ static void ioat1_cleanup_event(unsigned long data)
        writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET);
 }
 
-void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags,
-                   size_t len, struct ioat_dma_descriptor *hw)
-{
-       struct pci_dev *pdev = chan->device->pdev;
-       size_t offset = len - hw->size;
-
-       if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP))
-               ioat_unmap(pdev, hw->dst_addr - offset, len,
-                          PCI_DMA_FROMDEVICE, flags, 1);
-
-       if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP))
-               ioat_unmap(pdev, hw->src_addr - offset, len,
-                          PCI_DMA_TODEVICE, flags, 0);
-}
-
 dma_addr_t ioat_get_current_completion(struct ioat_chan_common *chan)
 {
        dma_addr_t phys_complete;
@@ -602,7 +587,7 @@ static void __cleanup(struct ioat_dma_chan *ioat, dma_addr_t phys_complete)
                dump_desc_dbg(ioat, desc);
                if (tx->cookie) {
                        dma_cookie_complete(tx);
-                       ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw);
+                       dma_descriptor_unmap(tx);
                        ioat->active -= desc->hw->tx_cnt;
                        if (tx->callback) {
                                tx->callback(tx->callback_param);
@@ -733,7 +718,7 @@ ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie,
        enum dma_status ret;
 
        ret = dma_cookie_status(c, cookie, txstate);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
 
        device->cleanup_fn((unsigned long) c);
@@ -833,8 +818,7 @@ int ioat_dma_self_test(struct ioatdma_device *device)
 
        dma_src = dma_map_single(dev, src, IOAT_TEST_SIZE, DMA_TO_DEVICE);
        dma_dest = dma_map_single(dev, dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE);
-       flags = DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP |
-               DMA_PREP_INTERRUPT;
+       flags = DMA_PREP_INTERRUPT;
        tx = device->common.device_prep_dma_memcpy(dma_chan, dma_dest, dma_src,
                                                   IOAT_TEST_SIZE, flags);
        if (!tx) {
@@ -859,7 +843,7 @@ int ioat_dma_self_test(struct ioatdma_device *device)
 
        if (tmo == 0 ||
            dma->device_tx_status(dma_chan, cookie, NULL)
-                                       != DMA_SUCCESS) {
+                                       != DMA_COMPLETE) {
                dev_err(dev, "Self-test copy timed out, disabling\n");
                err = -ENODEV;
                goto unmap_dma;
@@ -885,8 +869,7 @@ static char ioat_interrupt_style[32] = "msix";
 module_param_string(ioat_interrupt_style, ioat_interrupt_style,
                    sizeof(ioat_interrupt_style), 0644);
 MODULE_PARM_DESC(ioat_interrupt_style,
-                "set ioat interrupt style: msix (default), "
-                "msix-single-vector, msi, intx)");
+                "set ioat interrupt style: msix (default), msi, intx");
 
 /**
  * ioat_dma_setup_interrupts - setup interrupt handler
@@ -904,8 +887,6 @@ int ioat_dma_setup_interrupts(struct ioatdma_device *device)
 
        if (!strcmp(ioat_interrupt_style, "msix"))
                goto msix;
-       if (!strcmp(ioat_interrupt_style, "msix-single-vector"))
-               goto msix_single_vector;
        if (!strcmp(ioat_interrupt_style, "msi"))
                goto msi;
        if (!strcmp(ioat_interrupt_style, "intx"))
@@ -920,10 +901,8 @@ msix:
                device->msix_entries[i].entry = i;
 
        err = pci_enable_msix(pdev, device->msix_entries, msixcnt);
-       if (err < 0)
+       if (err)
                goto msi;
-       if (err > 0)
-               goto msix_single_vector;
 
        for (i = 0; i < msixcnt; i++) {
                msix = &device->msix_entries[i];
@@ -937,29 +916,13 @@ msix:
                                chan = ioat_chan_by_index(device, j);
                                devm_free_irq(dev, msix->vector, chan);
                        }
-                       goto msix_single_vector;
+                       goto msi;
                }
        }
        intrctrl |= IOAT_INTRCTRL_MSIX_VECTOR_CONTROL;
        device->irq_mode = IOAT_MSIX;
        goto done;
 
-msix_single_vector:
-       msix = &device->msix_entries[0];
-       msix->entry = 0;
-       err = pci_enable_msix(pdev, device->msix_entries, 1);
-       if (err)
-               goto msi;
-
-       err = devm_request_irq(dev, msix->vector, ioat_dma_do_interrupt, 0,
-                              "ioat-msix", device);
-       if (err) {
-               pci_disable_msix(pdev);
-               goto msi;
-       }
-       device->irq_mode = IOAT_MSIX_SINGLE;
-       goto done;
-
 msi:
        err = pci_enable_msi(pdev);
        if (err)
@@ -971,7 +934,7 @@ msi:
                pci_disable_msi(pdev);
                goto intx;
        }
-       device->irq_mode = IOAT_MSIX;
+       device->irq_mode = IOAT_MSI;
        goto done;
 
 intx:
index 54fb7b9..11fb877 100644 (file)
@@ -52,7 +52,6 @@
 enum ioat_irq_mode {
        IOAT_NOIRQ = 0,
        IOAT_MSIX,
-       IOAT_MSIX_SINGLE,
        IOAT_MSI,
        IOAT_INTX
 };
@@ -83,7 +82,6 @@ struct ioatdma_device {
        struct pci_pool *completion_pool;
 #define MAX_SED_POOLS  5
        struct dma_pool *sed_hw_pool[MAX_SED_POOLS];
-       struct kmem_cache *sed_pool;
        struct dma_device common;
        u8 version;
        struct msix_entry msix_entries[4];
@@ -342,16 +340,6 @@ static inline bool is_ioat_bug(unsigned long err)
        return !!err;
 }
 
-static inline void ioat_unmap(struct pci_dev *pdev, dma_addr_t addr, size_t len,
-                             int direction, enum dma_ctrl_flags flags, bool dst)
-{
-       if ((dst && (flags & DMA_COMPL_DEST_UNMAP_SINGLE)) ||
-           (!dst && (flags & DMA_COMPL_SRC_UNMAP_SINGLE)))
-               pci_unmap_single(pdev, addr, len, direction);
-       else
-               pci_unmap_page(pdev, addr, len, direction);
-}
-
 int ioat_probe(struct ioatdma_device *device);
 int ioat_register(struct ioatdma_device *device);
 int ioat1_dma_probe(struct ioatdma_device *dev, int dca);
@@ -363,8 +351,6 @@ void ioat_init_channel(struct ioatdma_device *device,
                       struct ioat_chan_common *chan, int idx);
 enum dma_status ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie,
                                   struct dma_tx_state *txstate);
-void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags,
-                   size_t len, struct ioat_dma_descriptor *hw);
 bool ioat_cleanup_preamble(struct ioat_chan_common *chan,
                           dma_addr_t *phys_complete);
 void ioat_kobject_add(struct ioatdma_device *device, struct kobj_type *type);
index b925e1b..5d3affe 100644 (file)
@@ -148,7 +148,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, dma_addr_t phys_complete)
                tx = &desc->txd;
                dump_desc_dbg(ioat, desc);
                if (tx->cookie) {
-                       ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw);
+                       dma_descriptor_unmap(tx);
                        dma_cookie_complete(tx);
                        if (tx->callback) {
                                tx->callback(tx->callback_param);
index 212d584..4702927 100644 (file)
@@ -157,7 +157,6 @@ static inline void ioat2_set_chainaddr(struct ioat2_dma_chan *ioat, u64 addr)
 
 int ioat2_dma_probe(struct ioatdma_device *dev, int dca);
 int ioat3_dma_probe(struct ioatdma_device *dev, int dca);
-void ioat3_dma_remove(struct ioatdma_device *dev);
 struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase);
 struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase);
 int ioat2_check_space_lock(struct ioat2_dma_chan *ioat, int num_descs);
index d8ececa..820817e 100644 (file)
@@ -67,6 +67,8 @@
 #include "dma.h"
 #include "dma_v2.h"
 
+extern struct kmem_cache *ioat3_sed_cache;
+
 /* ioat hardware assumes at least two sources for raid operations */
 #define src_cnt_to_sw(x) ((x) + 2)
 #define src_cnt_to_hw(x) ((x) - 2)
@@ -87,22 +89,8 @@ static const u8 pq_idx_to_field[] = { 1, 4, 5, 0, 1, 2, 4, 5 };
 static const u8 pq16_idx_to_field[] = { 1, 4, 1, 2, 3, 4, 5, 6, 7,
                                        0, 1, 2, 3, 4, 5, 6 };
 
-/*
- * technically sources 1 and 2 do not require SED, but the op will have
- * at least 9 descriptors so that's irrelevant.
- */
-static const u8 pq16_idx_to_sed[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                                     1, 1, 1, 1, 1, 1, 1 };
-
 static void ioat3_eh(struct ioat2_dma_chan *ioat);
 
-static dma_addr_t xor_get_src(struct ioat_raw_descriptor *descs[2], int idx)
-{
-       struct ioat_raw_descriptor *raw = descs[xor_idx_to_desc >> idx & 1];
-
-       return raw->field[xor_idx_to_field[idx]];
-}
-
 static void xor_set_src(struct ioat_raw_descriptor *descs[2],
                        dma_addr_t addr, u32 offset, int idx)
 {
@@ -135,12 +123,6 @@ static void pq_set_src(struct ioat_raw_descriptor *descs[2],
        pq->coef[idx] = coef;
 }
 
-static int sed_get_pq16_pool_idx(int src_cnt)
-{
-
-       return pq16_idx_to_sed[src_cnt];
-}
-
 static bool is_jf_ioat(struct pci_dev *pdev)
 {
        switch (pdev->device) {
@@ -272,7 +254,7 @@ ioat3_alloc_sed(struct ioatdma_device *device, unsigned int hw_pool)
        struct ioat_sed_ent *sed;
        gfp_t flags = __GFP_ZERO | GFP_ATOMIC;
 
-       sed = kmem_cache_alloc(device->sed_pool, flags);
+       sed = kmem_cache_alloc(ioat3_sed_cache, flags);
        if (!sed)
                return NULL;
 
@@ -280,7 +262,7 @@ ioat3_alloc_sed(struct ioatdma_device *device, unsigned int hw_pool)
        sed->hw = dma_pool_alloc(device->sed_hw_pool[hw_pool],
                                 flags, &sed->dma);
        if (!sed->hw) {
-               kmem_cache_free(device->sed_pool, sed);
+               kmem_cache_free(ioat3_sed_cache, sed);
                return NULL;
        }
 
@@ -293,165 +275,7 @@ static void ioat3_free_sed(struct ioatdma_device *device, struct ioat_sed_ent *s
                return;
 
        dma_pool_free(device->sed_hw_pool[sed->hw_pool], sed->hw, sed->dma);
-       kmem_cache_free(device->sed_pool, sed);
-}
-
-static void ioat3_dma_unmap(struct ioat2_dma_chan *ioat,
-                           struct ioat_ring_ent *desc, int idx)
-{
-       struct ioat_chan_common *chan = &ioat->base;
-       struct pci_dev *pdev = chan->device->pdev;
-       size_t len = desc->len;
-       size_t offset = len - desc->hw->size;
-       struct dma_async_tx_descriptor *tx = &desc->txd;
-       enum dma_ctrl_flags flags = tx->flags;
-
-       switch (desc->hw->ctl_f.op) {
-       case IOAT_OP_COPY:
-               if (!desc->hw->ctl_f.null) /* skip 'interrupt' ops */
-                       ioat_dma_unmap(chan, flags, len, desc->hw);
-               break;
-       case IOAT_OP_XOR_VAL:
-       case IOAT_OP_XOR: {
-               struct ioat_xor_descriptor *xor = desc->xor;
-               struct ioat_ring_ent *ext;
-               struct ioat_xor_ext_descriptor *xor_ex = NULL;
-               int src_cnt = src_cnt_to_sw(xor->ctl_f.src_cnt);
-               struct ioat_raw_descriptor *descs[2];
-               int i;
-
-               if (src_cnt > 5) {
-                       ext = ioat2_get_ring_ent(ioat, idx + 1);
-                       xor_ex = ext->xor_ex;
-               }
-
-               if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-                       descs[0] = (struct ioat_raw_descriptor *) xor;
-                       descs[1] = (struct ioat_raw_descriptor *) xor_ex;
-                       for (i = 0; i < src_cnt; i++) {
-                               dma_addr_t src = xor_get_src(descs, i);
-
-                               ioat_unmap(pdev, src - offset, len,
-                                          PCI_DMA_TODEVICE, flags, 0);
-                       }
-
-                       /* dest is a source in xor validate operations */
-                       if (xor->ctl_f.op == IOAT_OP_XOR_VAL) {
-                               ioat_unmap(pdev, xor->dst_addr - offset, len,
-                                          PCI_DMA_TODEVICE, flags, 1);
-                               break;
-                       }
-               }
-
-               if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP))
-                       ioat_unmap(pdev, xor->dst_addr - offset, len,
-                                  PCI_DMA_FROMDEVICE, flags, 1);
-               break;
-       }
-       case IOAT_OP_PQ_VAL:
-       case IOAT_OP_PQ: {
-               struct ioat_pq_descriptor *pq = desc->pq;
-               struct ioat_ring_ent *ext;
-               struct ioat_pq_ext_descriptor *pq_ex = NULL;
-               int src_cnt = src_cnt_to_sw(pq->ctl_f.src_cnt);
-               struct ioat_raw_descriptor *descs[2];
-               int i;
-
-               if (src_cnt > 3) {
-                       ext = ioat2_get_ring_ent(ioat, idx + 1);
-                       pq_ex = ext->pq_ex;
-               }
-
-               /* in the 'continue' case don't unmap the dests as sources */
-               if (dmaf_p_disabled_continue(flags))
-                       src_cnt--;
-               else if (dmaf_continue(flags))
-                       src_cnt -= 3;
-
-               if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-                       descs[0] = (struct ioat_raw_descriptor *) pq;
-                       descs[1] = (struct ioat_raw_descriptor *) pq_ex;
-                       for (i = 0; i < src_cnt; i++) {
-                               dma_addr_t src = pq_get_src(descs, i);
-
-                               ioat_unmap(pdev, src - offset, len,
-                                          PCI_DMA_TODEVICE, flags, 0);
-                       }
-
-                       /* the dests are sources in pq validate operations */
-                       if (pq->ctl_f.op == IOAT_OP_XOR_VAL) {
-                               if (!(flags & DMA_PREP_PQ_DISABLE_P))
-                                       ioat_unmap(pdev, pq->p_addr - offset,
-                                                  len, PCI_DMA_TODEVICE, flags, 0);
-                               if (!(flags & DMA_PREP_PQ_DISABLE_Q))
-                                       ioat_unmap(pdev, pq->q_addr - offset,
-                                                  len, PCI_DMA_TODEVICE, flags, 0);
-                               break;
-                       }
-               }
-
-               if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-                       if (!(flags & DMA_PREP_PQ_DISABLE_P))
-                               ioat_unmap(pdev, pq->p_addr - offset, len,
-                                          PCI_DMA_BIDIRECTIONAL, flags, 1);
-                       if (!(flags & DMA_PREP_PQ_DISABLE_Q))
-                               ioat_unmap(pdev, pq->q_addr - offset, len,
-                                          PCI_DMA_BIDIRECTIONAL, flags, 1);
-               }
-               break;
-       }
-       case IOAT_OP_PQ_16S:
-       case IOAT_OP_PQ_VAL_16S: {
-               struct ioat_pq_descriptor *pq = desc->pq;
-               int src_cnt = src16_cnt_to_sw(pq->ctl_f.src_cnt);
-               struct ioat_raw_descriptor *descs[4];
-               int i;
-
-               /* in the 'continue' case don't unmap the dests as sources */
-               if (dmaf_p_disabled_continue(flags))
-                       src_cnt--;
-               else if (dmaf_continue(flags))
-                       src_cnt -= 3;
-
-               if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-                       descs[0] = (struct ioat_raw_descriptor *)pq;
-                       descs[1] = (struct ioat_raw_descriptor *)(desc->sed->hw);
-                       descs[2] = (struct ioat_raw_descriptor *)(&desc->sed->hw->b[0]);
-                       for (i = 0; i < src_cnt; i++) {
-                               dma_addr_t src = pq16_get_src(descs, i);
-
-                               ioat_unmap(pdev, src - offset, len,
-                                          PCI_DMA_TODEVICE, flags, 0);
-                       }
-
-                       /* the dests are sources in pq validate operations */
-                       if (pq->ctl_f.op == IOAT_OP_XOR_VAL) {
-                               if (!(flags & DMA_PREP_PQ_DISABLE_P))
-                                       ioat_unmap(pdev, pq->p_addr - offset,
-                                                  len, PCI_DMA_TODEVICE,
-                                                  flags, 0);
-                               if (!(flags & DMA_PREP_PQ_DISABLE_Q))
-                                       ioat_unmap(pdev, pq->q_addr - offset,
-                                                  len, PCI_DMA_TODEVICE,
-                                                  flags, 0);
-                               break;
-                       }
-               }
-
-               if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-                       if (!(flags & DMA_PREP_PQ_DISABLE_P))
-                               ioat_unmap(pdev, pq->p_addr - offset, len,
-                                          PCI_DMA_BIDIRECTIONAL, flags, 1);
-                       if (!(flags & DMA_PREP_PQ_DISABLE_Q))
-                               ioat_unmap(pdev, pq->q_addr - offset, len,
-                                          PCI_DMA_BIDIRECTIONAL, flags, 1);
-               }
-               break;
-       }
-       default:
-               dev_err(&pdev->dev, "%s: unknown op type: %#x\n",
-                       __func__, desc->hw->ctl_f.op);
-       }
+       kmem_cache_free(ioat3_sed_cache, sed);
 }
 
 static bool desc_has_ext(struct ioat_ring_ent *desc)
@@ -577,7 +401,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, dma_addr_t phys_complete)
                tx = &desc->txd;
                if (tx->cookie) {
                        dma_cookie_complete(tx);
-                       ioat3_dma_unmap(ioat, desc, idx + i);
+                       dma_descriptor_unmap(tx);
                        if (tx->callback) {
                                tx->callback(tx->callback_param);
                                tx->callback = NULL;
@@ -807,7 +631,7 @@ ioat3_tx_status(struct dma_chan *c, dma_cookie_t cookie,
        enum dma_status ret;
 
        ret = dma_cookie_status(c, cookie, txstate);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
 
        ioat3_cleanup(ioat);
@@ -1129,9 +953,6 @@ __ioat3_prep_pq16_lock(struct dma_chan *c, enum sum_check_flags *result,
        u8 op;
        int i, s, idx, num_descs;
 
-       /* this function only handles src_cnt 9 - 16 */
-       BUG_ON(src_cnt < 9);
-
        /* this function is only called with 9-16 sources */
        op = result ? IOAT_OP_PQ_VAL_16S : IOAT_OP_PQ_16S;
 
@@ -1159,8 +980,7 @@ __ioat3_prep_pq16_lock(struct dma_chan *c, enum sum_check_flags *result,
 
                descs[0] = (struct ioat_raw_descriptor *) pq;
 
-               desc->sed = ioat3_alloc_sed(device,
-                                           sed_get_pq16_pool_idx(src_cnt));
+               desc->sed = ioat3_alloc_sed(device, (src_cnt-2) >> 3);
                if (!desc->sed) {
                        dev_err(to_dev(chan),
                                "%s: no free sed entries\n", __func__);
@@ -1218,13 +1038,21 @@ __ioat3_prep_pq16_lock(struct dma_chan *c, enum sum_check_flags *result,
        return &desc->txd;
 }
 
+static int src_cnt_flags(unsigned int src_cnt, unsigned long flags)
+{
+       if (dmaf_p_disabled_continue(flags))
+               return src_cnt + 1;
+       else if (dmaf_continue(flags))
+               return src_cnt + 3;
+       else
+               return src_cnt;
+}
+
 static struct dma_async_tx_descriptor *
 ioat3_prep_pq(struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src,
              unsigned int src_cnt, const unsigned char *scf, size_t len,
              unsigned long flags)
 {
-       struct dma_device *dma = chan->device;
-
        /* specify valid address for disabled result */
        if (flags & DMA_PREP_PQ_DISABLE_P)
                dst[0] = dst[1];
@@ -1244,7 +1072,7 @@ ioat3_prep_pq(struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src,
                single_source_coef[0] = scf[0];
                single_source_coef[1] = 0;
 
-               return (src_cnt > 8) && (dma->max_pq > 8) ?
+               return src_cnt_flags(src_cnt, flags) > 8 ?
                        __ioat3_prep_pq16_lock(chan, NULL, dst, single_source,
                                               2, single_source_coef, len,
                                               flags) :
@@ -1252,7 +1080,7 @@ ioat3_prep_pq(struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src,
                                             single_source_coef, len, flags);
 
        } else {
-               return (src_cnt > 8) && (dma->max_pq > 8) ?
+               return src_cnt_flags(src_cnt, flags) > 8 ?
                        __ioat3_prep_pq16_lock(chan, NULL, dst, src, src_cnt,
                                               scf, len, flags) :
                        __ioat3_prep_pq_lock(chan, NULL, dst, src, src_cnt,
@@ -1265,8 +1093,6 @@ ioat3_prep_pq_val(struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src,
                  unsigned int src_cnt, const unsigned char *scf, size_t len,
                  enum sum_check_flags *pqres, unsigned long flags)
 {
-       struct dma_device *dma = chan->device;
-
        /* specify valid address for disabled result */
        if (flags & DMA_PREP_PQ_DISABLE_P)
                pq[0] = pq[1];
@@ -1278,7 +1104,7 @@ ioat3_prep_pq_val(struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src,
         */
        *pqres = 0;
 
-       return (src_cnt > 8) && (dma->max_pq > 8) ?
+       return src_cnt_flags(src_cnt, flags) > 8 ?
                __ioat3_prep_pq16_lock(chan, pqres, pq, src, src_cnt, scf, len,
                                       flags) :
                __ioat3_prep_pq_lock(chan, pqres, pq, src, src_cnt, scf, len,
@@ -1289,7 +1115,6 @@ static struct dma_async_tx_descriptor *
 ioat3_prep_pqxor(struct dma_chan *chan, dma_addr_t dst, dma_addr_t *src,
                 unsigned int src_cnt, size_t len, unsigned long flags)
 {
-       struct dma_device *dma = chan->device;
        unsigned char scf[src_cnt];
        dma_addr_t pq[2];
 
@@ -1298,7 +1123,7 @@ ioat3_prep_pqxor(struct dma_chan *chan, dma_addr_t dst, dma_addr_t *src,
        flags |= DMA_PREP_PQ_DISABLE_Q;
        pq[1] = dst; /* specify valid address for disabled result */
 
-       return (src_cnt > 8) && (dma->max_pq > 8) ?
+       return src_cnt_flags(src_cnt, flags) > 8 ?
                __ioat3_prep_pq16_lock(chan, NULL, pq, src, src_cnt, scf, len,
                                       flags) :
                __ioat3_prep_pq_lock(chan, NULL, pq, src, src_cnt, scf, len,
@@ -1310,7 +1135,6 @@ ioat3_prep_pqxor_val(struct dma_chan *chan, dma_addr_t *src,
                     unsigned int src_cnt, size_t len,
                     enum sum_check_flags *result, unsigned long flags)
 {
-       struct dma_device *dma = chan->device;
        unsigned char scf[src_cnt];
        dma_addr_t pq[2];
 
@@ -1324,8 +1148,7 @@ ioat3_prep_pqxor_val(struct dma_chan *chan, dma_addr_t *src,
        flags |= DMA_PREP_PQ_DISABLE_Q;
        pq[1] = pq[0]; /* specify valid address for disabled result */
 
-
-       return (src_cnt > 8) && (dma->max_pq > 8) ?
+       return src_cnt_flags(src_cnt, flags) > 8 ?
                __ioat3_prep_pq16_lock(chan, result, pq, &src[1], src_cnt - 1,
                                       scf, len, flags) :
                __ioat3_prep_pq_lock(chan, result, pq, &src[1], src_cnt - 1,
@@ -1444,9 +1267,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
                                           DMA_TO_DEVICE);
        tx = dma->device_prep_dma_xor(dma_chan, dest_dma, dma_srcs,
                                      IOAT_NUM_SRC_TEST, PAGE_SIZE,
-                                     DMA_PREP_INTERRUPT |
-                                     DMA_COMPL_SKIP_SRC_UNMAP |
-                                     DMA_COMPL_SKIP_DEST_UNMAP);
+                                     DMA_PREP_INTERRUPT);
 
        if (!tx) {
                dev_err(dev, "Self-test xor prep failed\n");
@@ -1468,7 +1289,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
 
        tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000));
 
-       if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
+       if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) {
                dev_err(dev, "Self-test xor timed out\n");
                err = -ENODEV;
                goto dma_unmap;
@@ -1507,9 +1328,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
                                           DMA_TO_DEVICE);
        tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs,
                                          IOAT_NUM_SRC_TEST + 1, PAGE_SIZE,
-                                         &xor_val_result, DMA_PREP_INTERRUPT |
-                                         DMA_COMPL_SKIP_SRC_UNMAP |
-                                         DMA_COMPL_SKIP_DEST_UNMAP);
+                                         &xor_val_result, DMA_PREP_INTERRUPT);
        if (!tx) {
                dev_err(dev, "Self-test zero prep failed\n");
                err = -ENODEV;
@@ -1530,7 +1349,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
 
        tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000));
 
-       if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
+       if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) {
                dev_err(dev, "Self-test validate timed out\n");
                err = -ENODEV;
                goto dma_unmap;
@@ -1545,6 +1364,8 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
                goto free_resources;
        }
 
+       memset(page_address(dest), 0, PAGE_SIZE);
+
        /* test for non-zero parity sum */
        op = IOAT_OP_XOR_VAL;
 
@@ -1554,9 +1375,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
                                           DMA_TO_DEVICE);
        tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs,
                                          IOAT_NUM_SRC_TEST + 1, PAGE_SIZE,
-                                         &xor_val_result, DMA_PREP_INTERRUPT |
-                                         DMA_COMPL_SKIP_SRC_UNMAP |
-                                         DMA_COMPL_SKIP_DEST_UNMAP);
+                                         &xor_val_result, DMA_PREP_INTERRUPT);
        if (!tx) {
                dev_err(dev, "Self-test 2nd zero prep failed\n");
                err = -ENODEV;
@@ -1577,7 +1396,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
 
        tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000));
 
-       if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
+       if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) {
                dev_err(dev, "Self-test 2nd validate timed out\n");
                err = -ENODEV;
                goto dma_unmap;
@@ -1630,52 +1449,36 @@ static int ioat3_dma_self_test(struct ioatdma_device *device)
 
 static int ioat3_irq_reinit(struct ioatdma_device *device)
 {
-       int msixcnt = device->common.chancnt;
        struct pci_dev *pdev = device->pdev;
-       int i;
-       struct msix_entry *msix;
-       struct ioat_chan_common *chan;
-       int err = 0;
+       int irq = pdev->irq, i;
+
+       if (!is_bwd_ioat(pdev))
+               return 0;
 
        switch (device->irq_mode) {
        case IOAT_MSIX:
+               for (i = 0; i < device->common.chancnt; i++) {
+                       struct msix_entry *msix = &device->msix_entries[i];
+                       struct ioat_chan_common *chan;
 
-               for (i = 0; i < msixcnt; i++) {
-                       msix = &device->msix_entries[i];
                        chan = ioat_chan_by_index(device, i);
                        devm_free_irq(&pdev->dev, msix->vector, chan);
                }
 
                pci_disable_msix(pdev);
                break;
-
-       case IOAT_MSIX_SINGLE:
-               msix = &device->msix_entries[0];
-               chan = ioat_chan_by_index(device, 0);
-               devm_free_irq(&pdev->dev, msix->vector, chan);
-               pci_disable_msix(pdev);
-               break;
-
        case IOAT_MSI:
-               chan = ioat_chan_by_index(device, 0);
-               devm_free_irq(&pdev->dev, pdev->irq, chan);
                pci_disable_msi(pdev);
-               break;
-
+               /* fall through */
        case IOAT_INTX:
-               chan = ioat_chan_by_index(device, 0);
-               devm_free_irq(&pdev->dev, pdev->irq, chan);
+               devm_free_irq(&pdev->dev, irq, device);
                break;
-
        default:
                return 0;
        }
-
        device->irq_mode = IOAT_NOIRQ;
 
-       err = ioat_dma_setup_interrupts(device);
-
-       return err;
+       return ioat_dma_setup_interrupts(device);
 }
 
 static int ioat3_reset_hw(struct ioat_chan_common *chan)
@@ -1718,14 +1521,12 @@ static int ioat3_reset_hw(struct ioat_chan_common *chan)
        }
 
        err = ioat2_reset_sync(chan, msecs_to_jiffies(200));
-       if (err) {
-               dev_err(&pdev->dev, "Failed to reset!\n");
-               return err;
-       }
-
-       if (device->irq_mode != IOAT_NOIRQ && is_bwd_ioat(pdev))
+       if (!err)
                err = ioat3_irq_reinit(device);
 
+       if (err)
+               dev_err(&pdev->dev, "Failed to reset: %d\n", err);
+
        return err;
 }
 
@@ -1835,21 +1636,15 @@ int ioat3_dma_probe(struct ioatdma_device *device, int dca)
                char pool_name[14];
                int i;
 
-               /* allocate sw descriptor pool for SED */
-               device->sed_pool = kmem_cache_create("ioat_sed",
-                               sizeof(struct ioat_sed_ent), 0, 0, NULL);
-               if (!device->sed_pool)
-                       return -ENOMEM;
-
                for (i = 0; i < MAX_SED_POOLS; i++) {
                        snprintf(pool_name, 14, "ioat_hw%d_sed", i);
 
                        /* allocate SED DMA pool */
-                       device->sed_hw_pool[i] = dma_pool_create(pool_name,
+                       device->sed_hw_pool[i] = dmam_pool_create(pool_name,
                                        &pdev->dev,
                                        SED_SIZE * (i + 1), 64, 0);
                        if (!device->sed_hw_pool[i])
-                               goto sed_pool_cleanup;
+                               return -ENOMEM;
 
                }
        }
@@ -1875,28 +1670,4 @@ int ioat3_dma_probe(struct ioatdma_device *device, int dca)
                device->dca = ioat3_dca_init(pdev, device->reg_base);
 
        return 0;
-
-sed_pool_cleanup:
-       if (device->sed_pool) {
-               int i;
-               kmem_cache_destroy(device->sed_pool);
-
-               for (i = 0; i < MAX_SED_POOLS; i++)
-                       if (device->sed_hw_pool[i])
-                               dma_pool_destroy(device->sed_hw_pool[i]);
-       }
-
-       return -ENOMEM;
-}
-
-void ioat3_dma_remove(struct ioatdma_device *device)
-{
-       if (device->sed_pool) {
-               int i;
-               kmem_cache_destroy(device->sed_pool);
-
-               for (i = 0; i < MAX_SED_POOLS; i++)
-                       if (device->sed_hw_pool[i])
-                               dma_pool_destroy(device->sed_hw_pool[i]);
-       }
 }
index 2c8d560..1d051cd 100644 (file)
@@ -123,6 +123,7 @@ module_param(ioat_dca_enabled, int, 0644);
 MODULE_PARM_DESC(ioat_dca_enabled, "control support of dca service (default: 1)");
 
 struct kmem_cache *ioat2_cache;
+struct kmem_cache *ioat3_sed_cache;
 
 #define DRV_NAME "ioatdma"
 
@@ -207,9 +208,6 @@ static void ioat_remove(struct pci_dev *pdev)
        if (!device)
                return;
 
-       if (device->version >= IOAT_VER_3_0)
-               ioat3_dma_remove(device);
-
        dev_err(&pdev->dev, "Removing dma and dca services\n");
        if (device->dca) {
                unregister_dca_provider(device->dca, &pdev->dev);
@@ -221,7 +219,7 @@ static void ioat_remove(struct pci_dev *pdev)
 
 static int __init ioat_init_module(void)
 {
-       int err;
+       int err = -ENOMEM;
 
        pr_info("%s: Intel(R) QuickData Technology Driver %s\n",
                DRV_NAME, IOAT_DMA_VERSION);
@@ -231,9 +229,21 @@ static int __init ioat_init_module(void)
        if (!ioat2_cache)
                return -ENOMEM;
 
+       ioat3_sed_cache = KMEM_CACHE(ioat_sed_ent, 0);
+       if (!ioat3_sed_cache)
+               goto err_ioat2_cache;
+
        err = pci_register_driver(&ioat_pci_driver);
        if (err)
-               kmem_cache_destroy(ioat2_cache);
+               goto err_ioat3_cache;
+
+       return 0;
+
+ err_ioat3_cache:
+       kmem_cache_destroy(ioat3_sed_cache);
+
+ err_ioat2_cache:
+       kmem_cache_destroy(ioat2_cache);
 
        return err;
 }
index dd8b44a..c56137b 100644 (file)
@@ -61,80 +61,6 @@ static void iop_adma_free_slots(struct iop_adma_desc_slot *slot)
        }
 }
 
-static void
-iop_desc_unmap(struct iop_adma_chan *iop_chan, struct iop_adma_desc_slot *desc)
-{
-       struct dma_async_tx_descriptor *tx = &desc->async_tx;
-       struct iop_adma_desc_slot *unmap = desc->group_head;
-       struct device *dev = &iop_chan->device->pdev->dev;
-       u32 len = unmap->unmap_len;
-       enum dma_ctrl_flags flags = tx->flags;
-       u32 src_cnt;
-       dma_addr_t addr;
-       dma_addr_t dest;
-
-       src_cnt = unmap->unmap_src_cnt;
-       dest = iop_desc_get_dest_addr(unmap, iop_chan);
-       if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-               enum dma_data_direction dir;
-
-               if (src_cnt > 1) /* is xor? */
-                       dir = DMA_BIDIRECTIONAL;
-               else
-                       dir = DMA_FROM_DEVICE;
-
-               dma_unmap_page(dev, dest, len, dir);
-       }
-
-       if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-               while (src_cnt--) {
-                       addr = iop_desc_get_src_addr(unmap, iop_chan, src_cnt);
-                       if (addr == dest)
-                               continue;
-                       dma_unmap_page(dev, addr, len, DMA_TO_DEVICE);
-               }
-       }
-       desc->group_head = NULL;
-}
-
-static void
-iop_desc_unmap_pq(struct iop_adma_chan *iop_chan, struct iop_adma_desc_slot *desc)
-{
-       struct dma_async_tx_descriptor *tx = &desc->async_tx;
-       struct iop_adma_desc_slot *unmap = desc->group_head;
-       struct device *dev = &iop_chan->device->pdev->dev;
-       u32 len = unmap->unmap_len;
-       enum dma_ctrl_flags flags = tx->flags;
-       u32 src_cnt = unmap->unmap_src_cnt;
-       dma_addr_t pdest = iop_desc_get_dest_addr(unmap, iop_chan);
-       dma_addr_t qdest = iop_desc_get_qdest_addr(unmap, iop_chan);
-       int i;
-
-       if (tx->flags & DMA_PREP_CONTINUE)
-               src_cnt -= 3;
-
-       if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP) && !desc->pq_check_result) {
-               dma_unmap_page(dev, pdest, len, DMA_BIDIRECTIONAL);
-               dma_unmap_page(dev, qdest, len, DMA_BIDIRECTIONAL);
-       }
-
-       if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-               dma_addr_t addr;
-
-               for (i = 0; i < src_cnt; i++) {
-                       addr = iop_desc_get_src_addr(unmap, iop_chan, i);
-                       dma_unmap_page(dev, addr, len, DMA_TO_DEVICE);
-               }
-               if (desc->pq_check_result) {
-                       dma_unmap_page(dev, pdest, len, DMA_TO_DEVICE);
-                       dma_unmap_page(dev, qdest, len, DMA_TO_DEVICE);
-               }
-       }
-
-       desc->group_head = NULL;
-}
-
-
 static dma_cookie_t
 iop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc,
        struct iop_adma_chan *iop_chan, dma_cookie_t cookie)
@@ -152,15 +78,9 @@ iop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc,
                if (tx->callback)
                        tx->callback(tx->callback_param);
 
-               /* unmap dma addresses
-                * (unmap_single vs unmap_page?)
-                */
-               if (desc->group_head && desc->unmap_len) {
-                       if (iop_desc_is_pq(desc))
-                               iop_desc_unmap_pq(iop_chan, desc);
-                       else
-                               iop_desc_unmap(iop_chan, desc);
-               }
+               dma_descriptor_unmap(tx);
+               if (desc->group_head)
+                       desc->group_head = NULL;
        }
 
        /* run dependent operations */
@@ -591,7 +511,6 @@ iop_adma_prep_dma_interrupt(struct dma_chan *chan, unsigned long flags)
        if (sw_desc) {
                grp_start = sw_desc->group_head;
                iop_desc_init_interrupt(grp_start, iop_chan);
-               grp_start->unmap_len = 0;
                sw_desc->async_tx.flags = flags;
        }
        spin_unlock_bh(&iop_chan->lock);
@@ -623,8 +542,6 @@ iop_adma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dma_dest,
                iop_desc_set_byte_count(grp_start, iop_chan, len);
                iop_desc_set_dest_addr(grp_start, iop_chan, dma_dest);
                iop_desc_set_memcpy_src_addr(grp_start, dma_src);
-               sw_desc->unmap_src_cnt = 1;
-               sw_desc->unmap_len = len;
                sw_desc->async_tx.flags = flags;
        }
        spin_unlock_bh(&iop_chan->lock);
@@ -657,8 +574,6 @@ iop_adma_prep_dma_xor(struct dma_chan *chan, dma_addr_t dma_dest,
                iop_desc_init_xor(grp_start, src_cnt, flags);
                iop_desc_set_byte_count(grp_start, iop_chan, len);
                iop_desc_set_dest_addr(grp_start, iop_chan, dma_dest);
-               sw_desc->unmap_src_cnt = src_cnt;
-               sw_desc->unmap_len = len;
                sw_desc->async_tx.flags = flags;
                while (src_cnt--)
                        iop_desc_set_xor_src_addr(grp_start, src_cnt,
@@ -694,8 +609,6 @@ iop_adma_prep_dma_xor_val(struct dma_chan *chan, dma_addr_t *dma_src,
                grp_start->xor_check_result = result;
                pr_debug("\t%s: grp_start->xor_check_result: %p\n",
                        __func__, grp_start->xor_check_result);
-               sw_desc->unmap_src_cnt = src_cnt;
-               sw_desc->unmap_len = len;
                sw_desc->async_tx.flags = flags;
                while (src_cnt--)
                        iop_desc_set_zero_sum_src_addr(grp_start, src_cnt,
@@ -748,8 +661,6 @@ iop_adma_prep_dma_pq(struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src,
                        dst[0] = dst[1] & 0x7;
 
                iop_desc_set_pq_addr(g, dst);
-               sw_desc->unmap_src_cnt = src_cnt;
-               sw_desc->unmap_len = len;
                sw_desc->async_tx.flags = flags;
                for (i = 0; i < src_cnt; i++)
                        iop_desc_set_pq_src_addr(g, i, src[i], scf[i]);
@@ -804,8 +715,6 @@ iop_adma_prep_dma_pq_val(struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src,
                g->pq_check_result = pqres;
                pr_debug("\t%s: g->pq_check_result: %p\n",
                        __func__, g->pq_check_result);
-               sw_desc->unmap_src_cnt = src_cnt+2;
-               sw_desc->unmap_len = len;
                sw_desc->async_tx.flags = flags;
                while (src_cnt--)
                        iop_desc_set_pq_zero_sum_src_addr(g, src_cnt,
@@ -864,7 +773,7 @@ static enum dma_status iop_adma_status(struct dma_chan *chan,
        int ret;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
 
        iop_adma_slot_cleanup(iop_chan);
@@ -983,7 +892,7 @@ static int iop_adma_memcpy_self_test(struct iop_adma_device *device)
        msleep(1);
 
        if (iop_adma_status(dma_chan, cookie, NULL) !=
-                       DMA_SUCCESS) {
+                       DMA_COMPLETE) {
                dev_err(dma_chan->device->dev,
                        "Self-test copy timed out, disabling\n");
                err = -ENODEV;
@@ -1083,7 +992,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device)
        msleep(8);
 
        if (iop_adma_status(dma_chan, cookie, NULL) !=
-               DMA_SUCCESS) {
+               DMA_COMPLETE) {
                dev_err(dma_chan->device->dev,
                        "Self-test xor timed out, disabling\n");
                err = -ENODEV;
@@ -1129,7 +1038,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device)
        iop_adma_issue_pending(dma_chan);
        msleep(8);
 
-       if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
+       if (iop_adma_status(dma_chan, cookie, NULL) != DMA_COMPLETE) {
                dev_err(dma_chan->device->dev,
                        "Self-test zero sum timed out, disabling\n");
                err = -ENODEV;
@@ -1158,7 +1067,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device)
        iop_adma_issue_pending(dma_chan);
        msleep(8);
 
-       if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
+       if (iop_adma_status(dma_chan, cookie, NULL) != DMA_COMPLETE) {
                dev_err(dma_chan->device->dev,
                        "Self-test non-zero sum timed out, disabling\n");
                err = -ENODEV;
@@ -1254,7 +1163,7 @@ iop_adma_pq_zero_sum_self_test(struct iop_adma_device *device)
        msleep(8);
 
        if (iop_adma_status(dma_chan, cookie, NULL) !=
-               DMA_SUCCESS) {
+               DMA_COMPLETE) {
                dev_err(dev, "Self-test pq timed out, disabling\n");
                err = -ENODEV;
                goto free_resources;
@@ -1291,7 +1200,7 @@ iop_adma_pq_zero_sum_self_test(struct iop_adma_device *device)
        msleep(8);
 
        if (iop_adma_status(dma_chan, cookie, NULL) !=
-               DMA_SUCCESS) {
+               DMA_COMPLETE) {
                dev_err(dev, "Self-test pq-zero-sum timed out, disabling\n");
                err = -ENODEV;
                goto free_resources;
@@ -1323,7 +1232,7 @@ iop_adma_pq_zero_sum_self_test(struct iop_adma_device *device)
        msleep(8);
 
        if (iop_adma_status(dma_chan, cookie, NULL) !=
-               DMA_SUCCESS) {
+               DMA_COMPLETE) {
                dev_err(dev, "Self-test !pq-zero-sum timed out, disabling\n");
                err = -ENODEV;
                goto free_resources;
index cb9c0bc..128ca14 100644 (file)
@@ -1232,8 +1232,10 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
        desc = list_entry(ichan->queue.next, struct idmac_tx_desc, list);
        descnew = desc;
 
-       dev_dbg(dev, "IDMAC irq %d, dma 0x%08x, next dma 0x%08x, current %d, curbuf 0x%08x\n",
-               irq, sg_dma_address(*sg), sgnext ? sg_dma_address(sgnext) : 0, ichan->active_buffer, curbuf);
+       dev_dbg(dev, "IDMAC irq %d, dma %#llx, next dma %#llx, current %d, curbuf %#x\n",
+               irq, (u64)sg_dma_address(*sg),
+               sgnext ? (u64)sg_dma_address(sgnext) : 0,
+               ichan->active_buffer, curbuf);
 
        /* Find the descriptor of sgnext */
        sgnew = idmac_sg_next(ichan, &descnew, *sg);
index a2c330f..e260754 100644 (file)
@@ -344,7 +344,7 @@ static enum dma_status k3_dma_tx_status(struct dma_chan *chan,
        size_t bytes = 0;
 
        ret = dma_cookie_status(&c->vc.chan, cookie, state);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
 
        spin_lock_irqsave(&c->vc.lock, flags);
@@ -693,7 +693,7 @@ static int k3_dma_probe(struct platform_device *op)
 
        irq = platform_get_irq(op, 0);
        ret = devm_request_irq(&op->dev, irq,
-                       k3_dma_int_handler, IRQF_DISABLED, DRIVER_NAME, d);
+                       k3_dma_int_handler, 0, DRIVER_NAME, d);
        if (ret)
                return ret;
 
index ff8d782..dcb1e05 100644 (file)
@@ -798,8 +798,7 @@ static void dma_do_tasklet(unsigned long data)
                 * move the descriptors to a temporary list so we can drop
                 * the lock during the entire cleanup operation
                 */
-               list_del(&desc->node);
-               list_add(&desc->node, &chain_cleanup);
+               list_move(&desc->node, &chain_cleanup);
 
                /*
                 * Look for the first list entry which has the ENDIRQEN flag
@@ -863,7 +862,7 @@ static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev,
 
        if (irq) {
                ret = devm_request_irq(pdev->dev, irq,
-                       mmp_pdma_chan_handler, IRQF_DISABLED, "pdma", phy);
+                       mmp_pdma_chan_handler, 0, "pdma", phy);
                if (ret) {
                        dev_err(pdev->dev, "channel request irq fail!\n");
                        return ret;
@@ -970,7 +969,7 @@ static int mmp_pdma_probe(struct platform_device *op)
                /* all chan share one irq, demux inside */
                irq = platform_get_irq(op, 0);
                ret = devm_request_irq(pdev->dev, irq,
-                       mmp_pdma_int_handler, IRQF_DISABLED, "pdma", pdev);
+                       mmp_pdma_int_handler, 0, "pdma", pdev);
                if (ret)
                        return ret;
        }
index d3b6358..3ddacc1 100644 (file)
 #define TDCR_BURSTSZ_16B       (0x3 << 6)
 #define TDCR_BURSTSZ_32B       (0x6 << 6)
 #define TDCR_BURSTSZ_64B       (0x7 << 6)
+#define TDCR_BURSTSZ_SQU_1B            (0x5 << 6)
+#define TDCR_BURSTSZ_SQU_2B            (0x6 << 6)
+#define TDCR_BURSTSZ_SQU_4B            (0x0 << 6)
+#define TDCR_BURSTSZ_SQU_8B            (0x1 << 6)
+#define TDCR_BURSTSZ_SQU_16B   (0x3 << 6)
 #define TDCR_BURSTSZ_SQU_32B   (0x7 << 6)
 #define TDCR_BURSTSZ_128B      (0x5 << 6)
 #define TDCR_DSTDIR_MSK                (0x3 << 4)      /* Dst Direction */
@@ -158,7 +163,7 @@ static void mmp_tdma_disable_chan(struct mmp_tdma_chan *tdmac)
        /* disable irq */
        writel(0, tdmac->reg_base + TDIMR);
 
-       tdmac->status = DMA_SUCCESS;
+       tdmac->status = DMA_COMPLETE;
 }
 
 static void mmp_tdma_resume_chan(struct mmp_tdma_chan *tdmac)
@@ -228,8 +233,31 @@ static int mmp_tdma_config_chan(struct mmp_tdma_chan *tdmac)
                        return -EINVAL;
                }
        } else if (tdmac->type == PXA910_SQU) {
-               tdcr |= TDCR_BURSTSZ_SQU_32B;
                tdcr |= TDCR_SSPMOD;
+
+               switch (tdmac->burst_sz) {
+               case 1:
+                       tdcr |= TDCR_BURSTSZ_SQU_1B;
+                       break;
+               case 2:
+                       tdcr |= TDCR_BURSTSZ_SQU_2B;
+                       break;
+               case 4:
+                       tdcr |= TDCR_BURSTSZ_SQU_4B;
+                       break;
+               case 8:
+                       tdcr |= TDCR_BURSTSZ_SQU_8B;
+                       break;
+               case 16:
+                       tdcr |= TDCR_BURSTSZ_SQU_16B;
+                       break;
+               case 32:
+                       tdcr |= TDCR_BURSTSZ_SQU_32B;
+                       break;
+               default:
+                       dev_err(tdmac->dev, "mmp_tdma: unknown burst size.\n");
+                       return -EINVAL;
+               }
        }
 
        writel(tdcr, tdmac->reg_base + TDCR);
@@ -324,7 +352,7 @@ static int mmp_tdma_alloc_chan_resources(struct dma_chan *chan)
 
        if (tdmac->irq) {
                ret = devm_request_irq(tdmac->dev, tdmac->irq,
-                       mmp_tdma_chan_handler, IRQF_DISABLED, "tdma", tdmac);
+                       mmp_tdma_chan_handler, 0, "tdma", tdmac);
                if (ret)
                        return ret;
        }
@@ -365,7 +393,7 @@ static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic(
        int num_periods = buf_len / period_len;
        int i = 0, buf = 0;
 
-       if (tdmac->status != DMA_SUCCESS)
+       if (tdmac->status != DMA_COMPLETE)
                return NULL;
 
        if (period_len > TDMA_MAX_XFER_BYTES) {
@@ -499,7 +527,7 @@ static int mmp_tdma_chan_init(struct mmp_tdma_device *tdev,
        tdmac->idx         = idx;
        tdmac->type        = type;
        tdmac->reg_base    = (unsigned long)tdev->base + idx * 4;
-       tdmac->status = DMA_SUCCESS;
+       tdmac->status = DMA_COMPLETE;
        tdev->tdmac[tdmac->idx] = tdmac;
        tasklet_init(&tdmac->tasklet, dma_do_tasklet, (unsigned long)tdmac);
 
@@ -554,7 +582,7 @@ static int mmp_tdma_probe(struct platform_device *pdev)
        if (irq_num != chan_num) {
                irq = platform_get_irq(pdev, 0);
                ret = devm_request_irq(&pdev->dev, irq,
-                       mmp_tdma_int_handler, IRQF_DISABLED, "tdma", tdev);
+                       mmp_tdma_int_handler, 0, "tdma", tdev);
                if (ret)
                        return ret;
        }
index 536dcb8..7807f0e 100644 (file)
@@ -60,14 +60,6 @@ static u32 mv_desc_get_dest_addr(struct mv_xor_desc_slot *desc)
        return hw_desc->phy_dest_addr;
 }
 
-static u32 mv_desc_get_src_addr(struct mv_xor_desc_slot *desc,
-                               int src_idx)
-{
-       struct mv_xor_desc *hw_desc = desc->hw_desc;
-       return hw_desc->phy_src_addr[mv_phy_src_idx(src_idx)];
-}
-
-
 static void mv_desc_set_byte_count(struct mv_xor_desc_slot *desc,
                                   u32 byte_count)
 {
@@ -278,42 +270,9 @@ mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc,
                        desc->async_tx.callback(
                                desc->async_tx.callback_param);
 
-               /* unmap dma addresses
-                * (unmap_single vs unmap_page?)
-                */
-               if (desc->group_head && desc->unmap_len) {
-                       struct mv_xor_desc_slot *unmap = desc->group_head;
-                       struct device *dev = mv_chan_to_devp(mv_chan);
-                       u32 len = unmap->unmap_len;
-                       enum dma_ctrl_flags flags = desc->async_tx.flags;
-                       u32 src_cnt;
-                       dma_addr_t addr;
-                       dma_addr_t dest;
-
-                       src_cnt = unmap->unmap_src_cnt;
-                       dest = mv_desc_get_dest_addr(unmap);
-                       if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-                               enum dma_data_direction dir;
-
-                               if (src_cnt > 1) /* is xor ? */
-                                       dir = DMA_BIDIRECTIONAL;
-                               else
-                                       dir = DMA_FROM_DEVICE;
-                               dma_unmap_page(dev, dest, len, dir);
-                       }
-
-                       if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-                               while (src_cnt--) {
-                                       addr = mv_desc_get_src_addr(unmap,
-                                                                   src_cnt);
-                                       if (addr == dest)
-                                               continue;
-                                       dma_unmap_page(dev, addr, len,
-                                                      DMA_TO_DEVICE);
-                               }
-                       }
+               dma_descriptor_unmap(&desc->async_tx);
+               if (desc->group_head)
                        desc->group_head = NULL;
-               }
        }
 
        /* run dependent operations */
@@ -749,7 +708,7 @@ static enum dma_status mv_xor_status(struct dma_chan *chan,
        enum dma_status ret;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret == DMA_SUCCESS) {
+       if (ret == DMA_COMPLETE) {
                mv_xor_clean_completed_slots(mv_chan);
                return ret;
        }
@@ -874,7 +833,7 @@ static int mv_xor_memcpy_self_test(struct mv_xor_chan *mv_chan)
        msleep(1);
 
        if (mv_xor_status(dma_chan, cookie, NULL) !=
-           DMA_SUCCESS) {
+           DMA_COMPLETE) {
                dev_err(dma_chan->device->dev,
                        "Self-test copy timed out, disabling\n");
                err = -ENODEV;
@@ -968,7 +927,7 @@ mv_xor_xor_self_test(struct mv_xor_chan *mv_chan)
        msleep(8);
 
        if (mv_xor_status(dma_chan, cookie, NULL) !=
-           DMA_SUCCESS) {
+           DMA_COMPLETE) {
                dev_err(dma_chan->device->dev,
                        "Self-test xor timed out, disabling\n");
                err = -ENODEV;
@@ -1076,10 +1035,7 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
        }
 
        mv_chan->mmr_base = xordev->xor_base;
-       if (!mv_chan->mmr_base) {
-               ret = -ENOMEM;
-               goto err_free_dma;
-       }
+       mv_chan->mmr_high_base = xordev->xor_high_base;
        tasklet_init(&mv_chan->irq_tasklet, mv_xor_tasklet, (unsigned long)
                     mv_chan);
 
@@ -1138,7 +1094,7 @@ static void
 mv_xor_conf_mbus_windows(struct mv_xor_device *xordev,
                         const struct mbus_dram_target_info *dram)
 {
-       void __iomem *base = xordev->xor_base;
+       void __iomem *base = xordev->xor_high_base;
        u32 win_enable = 0;
        int i;
 
index 06b067f..d074922 100644 (file)
 #define XOR_OPERATION_MODE_MEMCPY      2
 #define XOR_DESCRIPTOR_SWAP            BIT(14)
 
-#define XOR_CURR_DESC(chan)    (chan->mmr_base + 0x210 + (chan->idx * 4))
-#define XOR_NEXT_DESC(chan)    (chan->mmr_base + 0x200 + (chan->idx * 4))
-#define XOR_BYTE_COUNT(chan)   (chan->mmr_base + 0x220 + (chan->idx * 4))
-#define XOR_DEST_POINTER(chan) (chan->mmr_base + 0x2B0 + (chan->idx * 4))
-#define XOR_BLOCK_SIZE(chan)   (chan->mmr_base + 0x2C0 + (chan->idx * 4))
-#define XOR_INIT_VALUE_LOW(chan)       (chan->mmr_base + 0x2E0)
-#define XOR_INIT_VALUE_HIGH(chan)      (chan->mmr_base + 0x2E4)
+#define XOR_CURR_DESC(chan)    (chan->mmr_high_base + 0x10 + (chan->idx * 4))
+#define XOR_NEXT_DESC(chan)    (chan->mmr_high_base + 0x00 + (chan->idx * 4))
+#define XOR_BYTE_COUNT(chan)   (chan->mmr_high_base + 0x20 + (chan->idx * 4))
+#define XOR_DEST_POINTER(chan) (chan->mmr_high_base + 0xB0 + (chan->idx * 4))
+#define XOR_BLOCK_SIZE(chan)   (chan->mmr_high_base + 0xC0 + (chan->idx * 4))
+#define XOR_INIT_VALUE_LOW(chan)       (chan->mmr_high_base + 0xE0)
+#define XOR_INIT_VALUE_HIGH(chan)      (chan->mmr_high_base + 0xE4)
 
 #define XOR_CONFIG(chan)       (chan->mmr_base + 0x10 + (chan->idx * 4))
 #define XOR_ACTIVATION(chan)   (chan->mmr_base + 0x20 + (chan->idx * 4))
 #define XOR_ERROR_ADDR(chan)   (chan->mmr_base + 0x60)
 #define XOR_INTR_MASK_VALUE    0x3F5
 
-#define WINDOW_BASE(w)         (0x250 + ((w) << 2))
-#define WINDOW_SIZE(w)         (0x270 + ((w) << 2))
-#define WINDOW_REMAP_HIGH(w)   (0x290 + ((w) << 2))
-#define WINDOW_BAR_ENABLE(chan)        (0x240 + ((chan) << 2))
-#define WINDOW_OVERRIDE_CTRL(chan)     (0x2A0 + ((chan) << 2))
+#define WINDOW_BASE(w)         (0x50 + ((w) << 2))
+#define WINDOW_SIZE(w)         (0x70 + ((w) << 2))
+#define WINDOW_REMAP_HIGH(w)   (0x90 + ((w) << 2))
+#define WINDOW_BAR_ENABLE(chan)        (0x40 + ((chan) << 2))
+#define WINDOW_OVERRIDE_CTRL(chan)     (0xA0 + ((chan) << 2))
 
 struct mv_xor_device {
        void __iomem         *xor_base;
@@ -82,6 +82,7 @@ struct mv_xor_chan {
        int                     pending;
        spinlock_t              lock; /* protects the descriptor slot pool */
        void __iomem            *mmr_base;
+       void __iomem            *mmr_high_base;
        unsigned int            idx;
        int                     irq;
        enum dma_transaction_type       current_type;
index ccd13df..ead4913 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_dma.h>
+#include <linux/list.h>
 
 #include <asm/irq.h>
 
@@ -57,6 +58,9 @@
        (((dma_is_apbh(d) && apbh_is_old(d)) ? 0x050 : 0x110) + (n) * 0x70)
 #define HW_APBHX_CHn_SEMA(d, n) \
        (((dma_is_apbh(d) && apbh_is_old(d)) ? 0x080 : 0x140) + (n) * 0x70)
+#define HW_APBHX_CHn_BAR(d, n) \
+       (((dma_is_apbh(d) && apbh_is_old(d)) ? 0x070 : 0x130) + (n) * 0x70)
+#define HW_APBX_CHn_DEBUG1(d, n) (0x150 + (n) * 0x70)
 
 /*
  * ccw bits definitions
@@ -115,7 +119,9 @@ struct mxs_dma_chan {
        int                             desc_count;
        enum dma_status                 status;
        unsigned int                    flags;
+       bool                            reset;
 #define MXS_DMA_SG_LOOP                        (1 << 0)
+#define MXS_DMA_USE_SEMAPHORE          (1 << 1)
 };
 
 #define MXS_DMA_CHANNELS               16
@@ -201,12 +207,47 @@ static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan)
        struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
        int chan_id = mxs_chan->chan.chan_id;
 
-       if (dma_is_apbh(mxs_dma) && apbh_is_old(mxs_dma))
+       /*
+        * mxs dma channel resets can cause a channel stall. To recover from a
+        * channel stall, we have to reset the whole DMA engine. To avoid this,
+        * we use cyclic DMA with semaphores, that are enhanced in
+        * mxs_dma_int_handler. To reset the channel, we can simply stop writing
+        * into the semaphore counter.
+        */
+       if (mxs_chan->flags & MXS_DMA_USE_SEMAPHORE &&
+                       mxs_chan->flags & MXS_DMA_SG_LOOP) {
+               mxs_chan->reset = true;
+       } else if (dma_is_apbh(mxs_dma) && apbh_is_old(mxs_dma)) {
                writel(1 << (chan_id + BP_APBH_CTRL0_RESET_CHANNEL),
                        mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET);
-       else
+       } else {
+               unsigned long elapsed = 0;
+               const unsigned long max_wait = 50000; /* 50ms */
+               void __iomem *reg_dbg1 = mxs_dma->base +
+                               HW_APBX_CHn_DEBUG1(mxs_dma, chan_id);
+
+               /*
+                * On i.MX28 APBX, the DMA channel can stop working if we reset
+                * the channel while it is in READ_FLUSH (0x08) state.
+                * We wait here until we leave the state. Then we trigger the
+                * reset. Waiting a maximum of 50ms, the kernel shouldn't crash
+                * because of this.
+                */
+               while ((readl(reg_dbg1) & 0xf) == 0x8 && elapsed < max_wait) {
+                       udelay(100);
+                       elapsed += 100;
+               }
+
+               if (elapsed >= max_wait)
+                       dev_err(&mxs_chan->mxs_dma->pdev->dev,
+                                       "Failed waiting for the DMA channel %d to leave state READ_FLUSH, trying to reset channel in READ_FLUSH state now\n",
+                                       chan_id);
+
                writel(1 << (chan_id + BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL),
                        mxs_dma->base + HW_APBHX_CHANNEL_CTRL + STMP_OFFSET_REG_SET);
+       }
+
+       mxs_chan->status = DMA_COMPLETE;
 }
 
 static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan)
@@ -219,12 +260,21 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan)
                mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(mxs_dma, chan_id));
 
        /* write 1 to SEMA to kick off the channel */
-       writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(mxs_dma, chan_id));
+       if (mxs_chan->flags & MXS_DMA_USE_SEMAPHORE &&
+                       mxs_chan->flags & MXS_DMA_SG_LOOP) {
+               /* A cyclic DMA consists of at least 2 segments, so initialize
+                * the semaphore with 2 so we have enough time to add 1 to the
+                * semaphore if we need to */
+               writel(2, mxs_dma->base + HW_APBHX_CHn_SEMA(mxs_dma, chan_id));
+       } else {
+               writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(mxs_dma, chan_id));
+       }
+       mxs_chan->reset = false;
 }
 
 static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan)
 {
-       mxs_chan->status = DMA_SUCCESS;
+       mxs_chan->status = DMA_COMPLETE;
 }
 
 static void mxs_dma_pause_chan(struct mxs_dma_chan *mxs_chan)
@@ -272,58 +322,88 @@ static void mxs_dma_tasklet(unsigned long data)
                mxs_chan->desc.callback(mxs_chan->desc.callback_param);
 }
 
+static int mxs_dma_irq_to_chan(struct mxs_dma_engine *mxs_dma, int irq)
+{
+       int i;
+
+       for (i = 0; i != mxs_dma->nr_channels; ++i)
+               if (mxs_dma->mxs_chans[i].chan_irq == irq)
+                       return i;
+
+       return -EINVAL;
+}
+
 static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id)
 {
        struct mxs_dma_engine *mxs_dma = dev_id;
-       u32 stat1, stat2;
+       struct mxs_dma_chan *mxs_chan;
+       u32 completed;
+       u32 err;
+       int chan = mxs_dma_irq_to_chan(mxs_dma, irq);
+
+       if (chan < 0)
+               return IRQ_NONE;
 
        /* completion status */
-       stat1 = readl(mxs_dma->base + HW_APBHX_CTRL1);
-       stat1 &= MXS_DMA_CHANNELS_MASK;
-       writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
+       completed = readl(mxs_dma->base + HW_APBHX_CTRL1);
+       completed = (completed >> chan) & 0x1;
+
+       /* Clear interrupt */
+       writel((1 << chan),
+                       mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
 
        /* error status */
-       stat2 = readl(mxs_dma->base + HW_APBHX_CTRL2);
-       writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR);
+       err = readl(mxs_dma->base + HW_APBHX_CTRL2);
+       err &= (1 << (MXS_DMA_CHANNELS + chan)) | (1 << chan);
+
+       /*
+        * error status bit is in the upper 16 bits, error irq bit in the lower
+        * 16 bits. We transform it into a simpler error code:
+        * err: 0x00 = no error, 0x01 = TERMINATION, 0x02 = BUS_ERROR
+        */
+       err = (err >> (MXS_DMA_CHANNELS + chan)) + (err >> chan);
+
+       /* Clear error irq */
+       writel((1 << chan),
+                       mxs_dma->base + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR);
 
        /*
         * When both completion and error of termination bits set at the
         * same time, we do not take it as an error.  IOW, it only becomes
-        * an error we need to handle here in case of either it's (1) a bus
-        * error or (2) a termination error with no completion.
+        * an error we need to handle here in case of either it's a bus
+        * error or a termination error with no completion. 0x01 is termination
+        * error, so we can subtract err & completed to get the real error case.
         */
-       stat2 = ((stat2 >> MXS_DMA_CHANNELS) & stat2) | /* (1) */
-               (~(stat2 >> MXS_DMA_CHANNELS) & stat2 & ~stat1); /* (2) */
-
-       /* combine error and completion status for checking */
-       stat1 = (stat2 << MXS_DMA_CHANNELS) | stat1;
-       while (stat1) {
-               int channel = fls(stat1) - 1;
-               struct mxs_dma_chan *mxs_chan =
-                       &mxs_dma->mxs_chans[channel % MXS_DMA_CHANNELS];
-
-               if (channel >= MXS_DMA_CHANNELS) {
-                       dev_dbg(mxs_dma->dma_device.dev,
-                               "%s: error in channel %d\n", __func__,
-                               channel - MXS_DMA_CHANNELS);
-                       mxs_chan->status = DMA_ERROR;
-                       mxs_dma_reset_chan(mxs_chan);
-               } else {
-                       if (mxs_chan->flags & MXS_DMA_SG_LOOP)
-                               mxs_chan->status = DMA_IN_PROGRESS;
-                       else
-                               mxs_chan->status = DMA_SUCCESS;
-               }
+       err -= err & completed;
 
-               stat1 &= ~(1 << channel);
+       mxs_chan = &mxs_dma->mxs_chans[chan];
 
-               if (mxs_chan->status == DMA_SUCCESS)
-                       dma_cookie_complete(&mxs_chan->desc);
+       if (err) {
+               dev_dbg(mxs_dma->dma_device.dev,
+                       "%s: error in channel %d\n", __func__,
+                       chan);
+               mxs_chan->status = DMA_ERROR;
+               mxs_dma_reset_chan(mxs_chan);
+       } else if (mxs_chan->status != DMA_COMPLETE) {
+               if (mxs_chan->flags & MXS_DMA_SG_LOOP) {
+                       mxs_chan->status = DMA_IN_PROGRESS;
+                       if (mxs_chan->flags & MXS_DMA_USE_SEMAPHORE)
+                               writel(1, mxs_dma->base +
+                                       HW_APBHX_CHn_SEMA(mxs_dma, chan));
+               } else {
+                       mxs_chan->status = DMA_COMPLETE;
+               }
+       }
 
-               /* schedule tasklet on this channel */
-               tasklet_schedule(&mxs_chan->tasklet);
+       if (mxs_chan->status == DMA_COMPLETE) {
+               if (mxs_chan->reset)
+                       return IRQ_HANDLED;
+               dma_cookie_complete(&mxs_chan->desc);
        }
 
+       /* schedule tasklet on this channel */
+       tasklet_schedule(&mxs_chan->tasklet);
+
        return IRQ_HANDLED;
 }
 
@@ -523,6 +603,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic(
 
        mxs_chan->status = DMA_IN_PROGRESS;
        mxs_chan->flags |= MXS_DMA_SG_LOOP;
+       mxs_chan->flags |= MXS_DMA_USE_SEMAPHORE;
 
        if (num_periods > NUM_CCW) {
                dev_err(mxs_dma->dma_device.dev,
@@ -554,6 +635,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic(
                ccw->bits |= CCW_IRQ;
                ccw->bits |= CCW_HALT_ON_TERM;
                ccw->bits |= CCW_TERM_FLUSH;
+               ccw->bits |= CCW_DEC_SEM;
                ccw->bits |= BF_CCW(direction == DMA_DEV_TO_MEM ?
                                MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ, COMMAND);
 
@@ -599,8 +681,24 @@ static enum dma_status mxs_dma_tx_status(struct dma_chan *chan,
                        dma_cookie_t cookie, struct dma_tx_state *txstate)
 {
        struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
+       struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
+       u32 residue = 0;
+
+       if (mxs_chan->status == DMA_IN_PROGRESS &&
+                       mxs_chan->flags & MXS_DMA_SG_LOOP) {
+               struct mxs_dma_ccw *last_ccw;
+               u32 bar;
+
+               last_ccw = &mxs_chan->ccw[mxs_chan->desc_count - 1];
+               residue = last_ccw->xfer_bytes + last_ccw->bufaddr;
+
+               bar = readl(mxs_dma->base +
+                               HW_APBHX_CHn_BAR(mxs_dma, chan->chan_id));
+               residue -= bar;
+       }
 
-       dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, 0);
+       dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie,
+                       residue);
 
        return mxs_chan->status;
 }
index ec3fc4f..2f66cf4 100644 (file)
@@ -248,7 +248,7 @@ static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
        unsigned long flags;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret == DMA_SUCCESS || !txstate)
+       if (ret == DMA_COMPLETE || !txstate)
                return ret;
 
        spin_lock_irqsave(&c->vc.lock, flags);
index df8b10f..cdf0483 100644 (file)
@@ -2268,6 +2268,8 @@ static void pl330_tasklet(unsigned long data)
                        list_move_tail(&desc->node, &pch->dmac->desc_pool);
                }
 
+               dma_descriptor_unmap(&desc->txd);
+
                if (callback) {
                        spin_unlock_irqrestore(&pch->lock, flags);
                        callback(callback_param);
@@ -2314,7 +2316,7 @@ bool pl330_filter(struct dma_chan *chan, void *param)
                return false;
 
        peri_id = chan->private;
-       return *peri_id == (unsigned)param;
+       return *peri_id == (unsigned long)param;
 }
 EXPORT_SYMBOL(pl330_filter);
 
@@ -2926,16 +2928,23 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
 
        amba_set_drvdata(adev, pdmac);
 
-       irq = adev->irq[0];
-       ret = request_irq(irq, pl330_irq_handler, 0,
-                       dev_name(&adev->dev), pi);
-       if (ret)
-               return ret;
+       for (i = 0; i < AMBA_NR_IRQS; i++) {
+               irq = adev->irq[i];
+               if (irq) {
+                       ret = devm_request_irq(&adev->dev, irq,
+                                              pl330_irq_handler, 0,
+                                              dev_name(&adev->dev), pi);
+                       if (ret)
+                               return ret;
+               } else {
+                       break;
+               }
+       }
 
        pi->pcfg.periph_id = adev->periphid;
        ret = pl330_add(pi);
        if (ret)
-               goto probe_err1;
+               return ret;
 
        INIT_LIST_HEAD(&pdmac->desc_pool);
        spin_lock_init(&pdmac->pool_lock);
@@ -3033,8 +3042,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
 
        return 0;
 probe_err3:
-       amba_set_drvdata(adev, NULL);
-
        /* Idle the DMAC */
        list_for_each_entry_safe(pch, _p, &pdmac->ddma.channels,
                        chan.device_node) {
@@ -3048,8 +3055,6 @@ probe_err3:
        }
 probe_err2:
        pl330_del(pi);
-probe_err1:
-       free_irq(irq, pi);
 
        return ret;
 }
@@ -3059,7 +3064,6 @@ static int pl330_remove(struct amba_device *adev)
        struct dma_pl330_dmac *pdmac = amba_get_drvdata(adev);
        struct dma_pl330_chan *pch, *_p;
        struct pl330_info *pi;
-       int irq;
 
        if (!pdmac)
                return 0;
@@ -3068,7 +3072,6 @@ static int pl330_remove(struct amba_device *adev)
                of_dma_controller_free(adev->dev.of_node);
 
        dma_async_device_unregister(&pdmac->ddma);
-       amba_set_drvdata(adev, NULL);
 
        /* Idle the DMAC */
        list_for_each_entry_safe(pch, _p, &pdmac->ddma.channels,
@@ -3086,9 +3089,6 @@ static int pl330_remove(struct amba_device *adev)
 
        pl330_del(pi);
 
-       irq = adev->irq[0];
-       free_irq(irq, pi);
-
        return 0;
 }
 
index e24b5ef..8da48c6 100644 (file)
@@ -804,218 +804,6 @@ static void ppc440spe_desc_set_link(struct ppc440spe_adma_chan *chan,
 }
 
 /**
- * ppc440spe_desc_get_src_addr - extract the source address from the descriptor
- */
-static u32 ppc440spe_desc_get_src_addr(struct ppc440spe_adma_desc_slot *desc,
-                               struct ppc440spe_adma_chan *chan, int src_idx)
-{
-       struct dma_cdb *dma_hw_desc;
-       struct xor_cb *xor_hw_desc;
-
-       switch (chan->device->id) {
-       case PPC440SPE_DMA0_ID:
-       case PPC440SPE_DMA1_ID:
-               dma_hw_desc = desc->hw_desc;
-               /* May have 0, 1, 2, or 3 sources */
-               switch (dma_hw_desc->opc) {
-               case DMA_CDB_OPC_NO_OP:
-               case DMA_CDB_OPC_DFILL128:
-                       return 0;
-               case DMA_CDB_OPC_DCHECK128:
-                       if (unlikely(src_idx)) {
-                               printk(KERN_ERR "%s: try to get %d source for"
-                                   " DCHECK128\n", __func__, src_idx);
-                               BUG();
-                       }
-                       return le32_to_cpu(dma_hw_desc->sg1l);
-               case DMA_CDB_OPC_MULTICAST:
-               case DMA_CDB_OPC_MV_SG1_SG2:
-                       if (unlikely(src_idx > 2)) {
-                               printk(KERN_ERR "%s: try to get %d source from"
-                                   " DMA descr\n", __func__, src_idx);
-                               BUG();
-                       }
-                       if (src_idx) {
-                               if (le32_to_cpu(dma_hw_desc->sg1u) &
-                                   DMA_CUED_XOR_WIN_MSK) {
-                                       u8 region;
-
-                                       if (src_idx == 1)
-                                               return le32_to_cpu(
-                                                   dma_hw_desc->sg1l) +
-                                                       desc->unmap_len;
-
-                                       region = (le32_to_cpu(
-                                           dma_hw_desc->sg1u)) >>
-                                               DMA_CUED_REGION_OFF;
-
-                                       region &= DMA_CUED_REGION_MSK;
-                                       switch (region) {
-                                       case DMA_RXOR123:
-                                               return le32_to_cpu(
-                                                   dma_hw_desc->sg1l) +
-                                                       (desc->unmap_len << 1);
-                                       case DMA_RXOR124:
-                                               return le32_to_cpu(
-                                                   dma_hw_desc->sg1l) +
-                                                       (desc->unmap_len * 3);
-                                       case DMA_RXOR125:
-                                               return le32_to_cpu(
-                                                   dma_hw_desc->sg1l) +
-                                                       (desc->unmap_len << 2);
-                                       default:
-                                               printk(KERN_ERR
-                                                   "%s: try to"
-                                                   " get src3 for region %02x"
-                                                   "PPC440SPE_DESC_RXOR12?\n",
-                                                   __func__, region);
-                                               BUG();
-                                       }
-                               } else {
-                                       printk(KERN_ERR
-                                               "%s: try to get %d"
-                                               " source for non-cued descr\n",
-                                               __func__, src_idx);
-                                       BUG();
-                               }
-                       }
-                       return le32_to_cpu(dma_hw_desc->sg1l);
-               default:
-                       printk(KERN_ERR "%s: unknown OPC 0x%02x\n",
-                               __func__, dma_hw_desc->opc);
-                       BUG();
-               }
-               return le32_to_cpu(dma_hw_desc->sg1l);
-       case PPC440SPE_XOR_ID:
-               /* May have up to 16 sources */
-               xor_hw_desc = desc->hw_desc;
-               return xor_hw_desc->ops[src_idx].l;
-       }
-       return 0;
-}
-
-/**
- * ppc440spe_desc_get_dest_addr - extract the destination address from the
- * descriptor
- */
-static u32 ppc440spe_desc_get_dest_addr(struct ppc440spe_adma_desc_slot *desc,
-                               struct ppc440spe_adma_chan *chan, int idx)
-{
-       struct dma_cdb *dma_hw_desc;
-       struct xor_cb *xor_hw_desc;
-
-       switch (chan->device->id) {
-       case PPC440SPE_DMA0_ID:
-       case PPC440SPE_DMA1_ID:
-               dma_hw_desc = desc->hw_desc;
-
-               if (likely(!idx))
-                       return le32_to_cpu(dma_hw_desc->sg2l);
-               return le32_to_cpu(dma_hw_desc->sg3l);
-       case PPC440SPE_XOR_ID:
-               xor_hw_desc = desc->hw_desc;
-               return xor_hw_desc->cbtal;
-       }
-       return 0;
-}
-
-/**
- * ppc440spe_desc_get_src_num - extract the number of source addresses from
- * the descriptor
- */
-static u32 ppc440spe_desc_get_src_num(struct ppc440spe_adma_desc_slot *desc,
-                               struct ppc440spe_adma_chan *chan)
-{
-       struct dma_cdb *dma_hw_desc;
-       struct xor_cb *xor_hw_desc;
-
-       switch (chan->device->id) {
-       case PPC440SPE_DMA0_ID:
-       case PPC440SPE_DMA1_ID:
-               dma_hw_desc = desc->hw_desc;
-
-               switch (dma_hw_desc->opc) {
-               case DMA_CDB_OPC_NO_OP:
-               case DMA_CDB_OPC_DFILL128:
-                       return 0;
-               case DMA_CDB_OPC_DCHECK128:
-                       return 1;
-               case DMA_CDB_OPC_MV_SG1_SG2:
-               case DMA_CDB_OPC_MULTICAST:
-                       /*
-                        * Only for RXOR operations we have more than
-                        * one source
-                        */
-                       if (le32_to_cpu(dma_hw_desc->sg1u) &
-                           DMA_CUED_XOR_WIN_MSK) {
-                               /* RXOR op, there are 2 or 3 sources */
-                               if (((le32_to_cpu(dma_hw_desc->sg1u) >>
-                                   DMA_CUED_REGION_OFF) &
-                                     DMA_CUED_REGION_MSK) == DMA_RXOR12) {
-                                       /* RXOR 1-2 */
-                                       return 2;
-                               } else {
-                                       /* RXOR 1-2-3/1-2-4/1-2-5 */
-                                       return 3;
-                               }
-                       }
-                       return 1;
-               default:
-                       printk(KERN_ERR "%s: unknown OPC 0x%02x\n",
-                               __func__, dma_hw_desc->opc);
-                       BUG();
-               }
-       case PPC440SPE_XOR_ID:
-               /* up to 16 sources */
-               xor_hw_desc = desc->hw_desc;
-               return xor_hw_desc->cbc & XOR_CDCR_OAC_MSK;
-       default:
-               BUG();
-       }
-       return 0;
-}
-
-/**
- * ppc440spe_desc_get_dst_num - get the number of destination addresses in
- * this descriptor
- */
-static u32 ppc440spe_desc_get_dst_num(struct ppc440spe_adma_desc_slot *desc,
-                               struct ppc440spe_adma_chan *chan)
-{
-       struct dma_cdb *dma_hw_desc;
-
-       switch (chan->device->id) {
-       case PPC440SPE_DMA0_ID:
-       case PPC440SPE_DMA1_ID:
-               /* May be 1 or 2 destinations */
-               dma_hw_desc = desc->hw_desc;
-               switch (dma_hw_desc->opc) {
-               case DMA_CDB_OPC_NO_OP:
-               case DMA_CDB_OPC_DCHECK128:
-                       return 0;
-               case DMA_CDB_OPC_MV_SG1_SG2:
-               case DMA_CDB_OPC_DFILL128:
-                       return 1;
-               case DMA_CDB_OPC_MULTICAST:
-                       if (desc->dst_cnt == 2)
-                               return 2;
-                       else
-                               return 1;
-               default:
-                       printk(KERN_ERR "%s: unknown OPC 0x%02x\n",
-                               __func__, dma_hw_desc->opc);
-                       BUG();
-               }
-       case PPC440SPE_XOR_ID:
-               /* Always only 1 destination */
-               return 1;
-       default:
-               BUG();
-       }
-       return 0;
-}
-
-/**
  * ppc440spe_desc_get_link - get the address of the descriptor that
  * follows this one
  */
@@ -1707,43 +1495,6 @@ static void ppc440spe_adma_free_slots(struct ppc440spe_adma_desc_slot *slot,
        }
 }
 
-static void ppc440spe_adma_unmap(struct ppc440spe_adma_chan *chan,
-                                struct ppc440spe_adma_desc_slot *desc)
-{
-       u32 src_cnt, dst_cnt;
-       dma_addr_t addr;
-
-       /*
-        * get the number of sources & destination
-        * included in this descriptor and unmap
-        * them all
-        */
-       src_cnt = ppc440spe_desc_get_src_num(desc, chan);
-       dst_cnt = ppc440spe_desc_get_dst_num(desc, chan);
-
-       /* unmap destinations */
-       if (!(desc->async_tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-               while (dst_cnt--) {
-                       addr = ppc440spe_desc_get_dest_addr(
-                               desc, chan, dst_cnt);
-                       dma_unmap_page(chan->device->dev,
-                                       addr, desc->unmap_len,
-                                       DMA_FROM_DEVICE);
-               }
-       }
-
-       /* unmap sources */
-       if (!(desc->async_tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-               while (src_cnt--) {
-                       addr = ppc440spe_desc_get_src_addr(
-                               desc, chan, src_cnt);
-                       dma_unmap_page(chan->device->dev,
-                                       addr, desc->unmap_len,
-                                       DMA_TO_DEVICE);
-               }
-       }
-}
-
 /**
  * ppc440spe_adma_run_tx_complete_actions - call functions to be called
  * upon completion
@@ -1767,26 +1518,7 @@ static dma_cookie_t ppc440spe_adma_run_tx_complete_actions(
                        desc->async_tx.callback(
                                desc->async_tx.callback_param);
 
-               /* unmap dma addresses
-                * (unmap_single vs unmap_page?)
-                *
-                * actually, ppc's dma_unmap_page() functions are empty, so
-                * the following code is just for the sake of completeness
-                */
-               if (chan && chan->needs_unmap && desc->group_head &&
-                    desc->unmap_len) {
-                       struct ppc440spe_adma_desc_slot *unmap =
-                                                       desc->group_head;
-                       /* assume 1 slot per op always */
-                       u32 slot_count = unmap->slot_cnt;
-
-                       /* Run through the group list and unmap addresses */
-                       for (i = 0; i < slot_count; i++) {
-                               BUG_ON(!unmap);
-                               ppc440spe_adma_unmap(chan, unmap);
-                               unmap = unmap->hw_next;
-                       }
-               }
+               dma_descriptor_unmap(&desc->async_tx);
        }
 
        /* run dependent operations */
@@ -3893,7 +3625,7 @@ static enum dma_status ppc440spe_adma_tx_status(struct dma_chan *chan,
 
        ppc440spe_chan = to_ppc440spe_adma_chan(chan);
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
 
        ppc440spe_adma_slot_cleanup(ppc440spe_chan);
index 461a91a..ab26d46 100644 (file)
@@ -436,7 +436,7 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
        enum dma_status ret;
 
        ret = dma_cookie_status(&c->vc.chan, cookie, state);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
 
        if (!state)
index d94ab59..2e7b394 100644 (file)
@@ -724,7 +724,7 @@ static enum dma_status shdma_tx_status(struct dma_chan *chan,
         * If we don't find cookie on the queue, it has been aborted and we have
         * to report error
         */
-       if (status != DMA_SUCCESS) {
+       if (status != DMA_COMPLETE) {
                struct shdma_desc *sdesc;
                status = DMA_ERROR;
                list_for_each_entry(sdesc, &schan->ld_queue, node)
index 1069e88..0d765c0 100644 (file)
@@ -685,7 +685,7 @@ MODULE_DEVICE_TABLE(of, sh_dmae_of_match);
 static int sh_dmae_probe(struct platform_device *pdev)
 {
        const struct sh_dmae_pdata *pdata;
-       unsigned long irqflags = IRQF_DISABLED,
+       unsigned long irqflags = 0,
                chan_flag[SH_DMAE_MAX_CHANNELS] = {};
        int errirq, chan_irq[SH_DMAE_MAX_CHANNELS];
        int err, i, irq_cnt = 0, irqres = 0, irq_cap = 0;
@@ -838,7 +838,7 @@ static int sh_dmae_probe(struct platform_device *pdev)
                                    IORESOURCE_IRQ_SHAREABLE)
                                        chan_flag[irq_cnt] = IRQF_SHARED;
                                else
-                                       chan_flag[irq_cnt] = IRQF_DISABLED;
+                                       chan_flag[irq_cnt] = 0;
                                dev_dbg(&pdev->dev,
                                        "Found IRQ %d for channel %d\n",
                                        i, irq_cnt);
index 82d2b97..b8c031b 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/log2.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/err.h>
@@ -2626,7 +2627,7 @@ static enum dma_status d40_tx_status(struct dma_chan *chan,
        }
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret != DMA_SUCCESS)
+       if (ret != DMA_COMPLETE)
                dma_set_residue(txstate, stedma40_residue(chan));
 
        if (d40_is_paused(d40c))
@@ -2796,8 +2797,8 @@ static int d40_set_runtime_config(struct dma_chan *chan,
            src_addr_width >  DMA_SLAVE_BUSWIDTH_8_BYTES   ||
            dst_addr_width <= DMA_SLAVE_BUSWIDTH_UNDEFINED ||
            dst_addr_width >  DMA_SLAVE_BUSWIDTH_8_BYTES   ||
-           ((src_addr_width > 1) && (src_addr_width & 1)) ||
-           ((dst_addr_width > 1) && (dst_addr_width & 1)))
+           !is_power_of_2(src_addr_width) ||
+           !is_power_of_2(dst_addr_width))
                return -EINVAL;
 
        cfg->src_info.data_width = src_addr_width;
index 5d4986e..73654e3 100644 (file)
@@ -570,7 +570,7 @@ static void handle_once_dma_done(struct tegra_dma_channel *tdc,
 
        list_del(&sgreq->node);
        if (sgreq->last_sg) {
-               dma_desc->dma_status = DMA_SUCCESS;
+               dma_desc->dma_status = DMA_COMPLETE;
                dma_cookie_complete(&dma_desc->txd);
                if (!dma_desc->cb_count)
                        list_add_tail(&dma_desc->cb_node, &tdc->cb_desc);
@@ -768,7 +768,7 @@ static enum dma_status tegra_dma_tx_status(struct dma_chan *dc,
        unsigned int residual;
 
        ret = dma_cookie_status(dc, cookie, txstate);
-       if (ret == DMA_SUCCESS)
+       if (ret == DMA_COMPLETE)
                return ret;
 
        spin_lock_irqsave(&tdc->lock, flags);
@@ -1018,7 +1018,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg(
        return &dma_desc->txd;
 }
 
-struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
+static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
        struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_len,
        size_t period_len, enum dma_transfer_direction direction,
        unsigned long flags, void *context)
index 28af214..4506a7b 100644 (file)
@@ -154,38 +154,6 @@ static bool __td_dma_done_ack(struct timb_dma_chan *td_chan)
        return done;
 }
 
-static void __td_unmap_desc(struct timb_dma_chan *td_chan, const u8 *dma_desc,
-       bool single)
-{
-       dma_addr_t addr;
-       int len;
-
-       addr = (dma_desc[7] << 24) | (dma_desc[6] << 16) | (dma_desc[5] << 8) |
-               dma_desc[4];
-
-       len = (dma_desc[3] << 8) | dma_desc[2];
-
-       if (single)
-               dma_unmap_single(chan2dev(&td_chan->chan), addr, len,
-                       DMA_TO_DEVICE);
-       else
-               dma_unmap_page(chan2dev(&td_chan->chan), addr, len,
-                       DMA_TO_DEVICE);
-}
-
-static void __td_unmap_descs(struct timb_dma_desc *td_desc, bool single)
-{
-       struct timb_dma_chan *td_chan = container_of(td_desc->txd.chan,
-               struct timb_dma_chan, chan);
-       u8 *descs;
-
-       for (descs = td_desc->desc_list; ; descs += TIMB_DMA_DESC_SIZE) {
-               __td_unmap_desc(td_chan, descs, single);
-               if (descs[0] & 0x02)
-                       break;
-       }
-}
-
 static int td_fill_desc(struct timb_dma_chan *td_chan, u8 *dma_desc,
        struct scatterlist *sg, bool last)
 {
@@ -293,10 +261,7 @@ static void __td_finish(struct timb_dma_chan *td_chan)
 
        list_move(&td_desc->desc_node, &td_chan->free_list);
 
-       if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP))
-               __td_unmap_descs(td_desc,
-                       txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE);
-
+       dma_descriptor_unmap(txd);
        /*
         * The API requires that no submissions are done from a
         * callback, so we don't need to drop the lock here
index 71e8e77..bae6c29 100644 (file)
@@ -419,30 +419,7 @@ txx9dmac_descriptor_complete(struct txx9dmac_chan *dc,
        list_splice_init(&desc->tx_list, &dc->free_list);
        list_move(&desc->desc_node, &dc->free_list);
 
-       if (!ds) {
-               dma_addr_t dmaaddr;
-               if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-                       dmaaddr = is_dmac64(dc) ?
-                               desc->hwdesc.DAR : desc->hwdesc32.DAR;
-                       if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE)
-                               dma_unmap_single(chan2parent(&dc->chan),
-                                       dmaaddr, desc->len, DMA_FROM_DEVICE);
-                       else
-                               dma_unmap_page(chan2parent(&dc->chan),
-                                       dmaaddr, desc->len, DMA_FROM_DEVICE);
-               }
-               if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-                       dmaaddr = is_dmac64(dc) ?
-                               desc->hwdesc.SAR : desc->hwdesc32.SAR;
-                       if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE)
-                               dma_unmap_single(chan2parent(&dc->chan),
-                                       dmaaddr, desc->len, DMA_TO_DEVICE);
-                       else
-                               dma_unmap_page(chan2parent(&dc->chan),
-                                       dmaaddr, desc->len, DMA_TO_DEVICE);
-               }
-       }
-
+       dma_descriptor_unmap(txd);
        /*
         * The API requires that no submissions are done from a
         * callback, so we don't need to drop the lock here
@@ -962,8 +939,8 @@ txx9dmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
        enum dma_status ret;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       if (ret == DMA_SUCCESS)
-               return DMA_SUCCESS;
+       if (ret == DMA_COMPLETE)
+               return DMA_COMPLETE;
 
        spin_lock_bh(&dc->lock);
        txx9dmac_scan_descriptors(dc);
index 9ee1c76..374b57f 100644 (file)
@@ -163,6 +163,7 @@ static void cell_edac_init_csrows(struct mem_ctl_info *mci)
                        csrow->first_page, nr_pages);
                break;
        }
+       of_node_put(np);
 }
 
 static int cell_edac_probe(struct platform_device *pdev)
index 211021d..1026743 100644 (file)
@@ -530,12 +530,9 @@ int edac_device_add_device(struct edac_device_ctl_info *edac_dev)
 
        /* Report action taken */
        edac_device_printk(edac_dev, KERN_INFO,
-                               "Giving out device to module '%s' controller "
-                               "'%s': DEV '%s' (%s)\n",
-                               edac_dev->mod_name,
-                               edac_dev->ctl_name,
-                               edac_dev_name(edac_dev),
-                               edac_op_state_to_string(edac_dev->op_state));
+               "Giving out device to module %s controller %s: DEV %s (%s)\n",
+               edac_dev->mod_name, edac_dev->ctl_name, edac_dev->dev_name,
+               edac_op_state_to_string(edac_dev->op_state));
 
        mutex_unlock(&device_ctls_mutex);
        return 0;
index 89e1090..e8c9ef0 100644 (file)
@@ -788,8 +788,10 @@ int edac_mc_add_mc(struct mem_ctl_info *mci)
        }
 
        /* Report action taken */
-       edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':"
-               " DEV %s\n", mci->mod_name, mci->ctl_name, edac_dev_name(mci));
+       edac_mc_printk(mci, KERN_INFO,
+               "Giving out device to module %s controller %s: DEV %s (%s)\n",
+               mci->mod_name, mci->ctl_name, mci->dev_name,
+               edac_op_state_to_string(mci->op_state));
 
        edac_mc_owner = mci->mod_name;
 
index dd370f9..2cf44b4 100644 (file)
@@ -358,11 +358,9 @@ int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx)
        }
 
        edac_pci_printk(pci, KERN_INFO,
-                       "Giving out device to module '%s' controller '%s':"
-                       " DEV '%s' (%s)\n",
-                       pci->mod_name,
-                       pci->ctl_name,
-                       edac_dev_name(pci), edac_op_state_to_string(pci->op_state));
+               "Giving out device to module %s controller %s: DEV %s (%s)\n",
+               pci->mod_name, pci->ctl_name, pci->dev_name,
+               edac_op_state_to_string(pci->op_state));
 
        mutex_unlock(&edac_pci_ctls_mutex);
        return 0;
index c2bd8c6..2f19366 100644 (file)
@@ -50,8 +50,15 @@ static irqreturn_t highbank_l2_err_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+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 int highbank_l2_err_probe(struct platform_device *pdev)
 {
+       const struct of_device_id *id;
        struct edac_device_ctl_info *dci;
        struct hb_l2_drvdata *drvdata;
        struct resource *r;
@@ -90,28 +97,32 @@ static int highbank_l2_err_probe(struct platform_device *pdev)
                goto err;
        }
 
+       id = of_match_device(hb_l2_err_of_match, &pdev->dev);
+       dci->mod_name = pdev->dev.driver->name;
+       dci->ctl_name = id ? id->compatible : "unknown";
+       dci->dev_name = dev_name(&pdev->dev);
+
+       if (edac_device_add_device(dci))
+               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;
+               goto err2;
 
        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;
+               goto err2;
 
        devres_close_group(&pdev->dev, NULL);
        return 0;
+err2:
+       edac_device_del_device(&pdev->dev);
 err:
        devres_release_group(&pdev->dev, NULL);
        edac_device_free_ctl_info(dci);
@@ -127,12 +138,6 @@ static int highbank_l2_err_remove(struct platform_device *pdev)
        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,
index 4695dd2..f784de1 100644 (file)
 #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_ERR_BASE            0x128
+#define MW_DDR_ECC_ERR_BASE            0x1b4
+
+#define HB_DDR_ECC_OPT                 0x00
+#define HB_DDR_ECC_U_ERR_ADDR          0x08
+#define HB_DDR_ECC_U_ERR_STAT          0x0c
+#define HB_DDR_ECC_U_ERR_DATAL         0x10
+#define HB_DDR_ECC_U_ERR_DATAH         0x14
+#define HB_DDR_ECC_C_ERR_ADDR          0x18
+#define HB_DDR_ECC_C_ERR_STAT          0x1c
+#define HB_DDR_ECC_C_ERR_DATAL         0x20
+#define HB_DDR_ECC_C_ERR_DATAH         0x24
 
 #define HB_DDR_ECC_OPT_MODE_MASK       0x3
 #define HB_DDR_ECC_OPT_FWC             0x100
 #define HB_DDR_ECC_OPT_XOR_SHIFT       16
 
+/* DDR Ctrlr Interrupt Registers */
+
+#define HB_DDR_ECC_INT_BASE            0x180
+#define MW_DDR_ECC_INT_BASE            0x218
+
+#define HB_DDR_ECC_INT_STATUS          0x00
+#define HB_DDR_ECC_INT_ACK             0x04
+
+#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
+
 struct hb_mc_drvdata {
-       void __iomem *mc_vbase;
+       void __iomem *mc_err_base;
+       void __iomem *mc_int_base;
 };
 
 static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id)
@@ -60,10 +69,10 @@ static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id)
        u32 status, err_addr;
 
        /* Read the interrupt status register */
-       status = readl(drvdata->mc_vbase + HB_DDR_ECC_INT_STATUS);
+       status = readl(drvdata->mc_int_base + 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);
+               err_addr = readl(drvdata->mc_err_base + 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,
@@ -71,9 +80,9 @@ static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id)
                                     mci->ctl_name, "");
        }
        if (status & HB_DDR_ECC_INT_STAT_CE) {
-               u32 syndrome = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_STAT);
+               u32 syndrome = readl(drvdata->mc_err_base + HB_DDR_ECC_C_ERR_STAT);
                syndrome = (syndrome >> 8) & 0xff;
-               err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_ADDR);
+               err_addr = readl(drvdata->mc_err_base + 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,
@@ -82,66 +91,79 @@ static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id)
        }
 
        /* clear the error, clears the interrupt */
-       writel(status, drvdata->mc_vbase + HB_DDR_ECC_INT_ACK);
+       writel(status, drvdata->mc_int_base + 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)
+static void highbank_mc_err_inject(struct mem_ctl_info *mci, u8 synd)
 {
-       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;
+
+       reg = readl(pdata->mc_err_base + 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_err_base + HB_DDR_ECC_OPT);
+}
+
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
+
+static ssize_t highbank_mc_inject_ctrl(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct mem_ctl_info *mci = to_mci(dev);
        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))
+               return -EINVAL;
 
-       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);
-       }
+       highbank_mc_err_inject(mci, synd);
 
        return count;
 }
 
-static const struct file_operations highbank_mc_debug_inject_fops = {
-       .open = simple_open,
-       .write = highbank_mc_err_inject_write,
-       .llseek = generic_file_llseek,
+static DEVICE_ATTR(inject_ctrl, S_IWUSR, NULL, highbank_mc_inject_ctrl);
+
+struct hb_mc_settings {
+       int     err_offset;
+       int     int_offset;
 };
 
-static void 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 highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
-{}
-#endif
+static struct hb_mc_settings hb_settings = {
+       .err_offset = HB_DDR_ECC_ERR_BASE,
+       .int_offset = HB_DDR_ECC_INT_BASE,
+};
+
+static struct hb_mc_settings mw_settings = {
+       .err_offset = MW_DDR_ECC_ERR_BASE,
+       .int_offset = MW_DDR_ECC_INT_BASE,
+};
+
+static struct of_device_id hb_ddr_ctrl_of_match[] = {
+       { .compatible = "calxeda,hb-ddr-ctrl",          .data = &hb_settings },
+       { .compatible = "calxeda,ecx-2000-ddr-ctrl",    .data = &mw_settings },
+       {},
+};
+MODULE_DEVICE_TABLE(of, hb_ddr_ctrl_of_match);
 
 static int highbank_mc_probe(struct platform_device *pdev)
 {
+       const struct of_device_id *id;
+       const struct hb_mc_settings *settings;
        struct edac_mc_layer layers[2];
        struct mem_ctl_info *mci;
        struct hb_mc_drvdata *drvdata;
        struct dimm_info *dimm;
        struct resource *r;
+       void __iomem *base;
        u32 control;
        int irq;
        int res = 0;
 
+       id = of_match_device(hb_ddr_ctrl_of_match, &pdev->dev);
+       if (!id)
+               return -ENODEV;
+
        layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
        layers[0].size = 1;
        layers[0].is_virt_csrow = true;
@@ -174,35 +196,31 @@ static int highbank_mc_probe(struct platform_device *pdev)
                goto err;
        }
 
-       drvdata->mc_vbase = devm_ioremap(&pdev->dev,
-                                         r->start, resource_size(r));
-       if (!drvdata->mc_vbase) {
+       base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+       if (!base) {
                dev_err(&pdev->dev, "Unable to map regs\n");
                res = -ENOMEM;
                goto err;
        }
 
-       control = readl(drvdata->mc_vbase + HB_DDR_ECC_OPT) & 0x3;
+       settings = id->data;
+       drvdata->mc_err_base = base + settings->err_offset;
+       drvdata->mc_int_base = base + settings->int_offset;
+
+       control = readl(drvdata->mc_err_base + 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_name = pdev->dev.driver->name;
        mci->mod_ver = "1";
-       mci->ctl_name = dev_name(&pdev->dev);
+       mci->ctl_name = id->compatible;
+       mci->dev_name = dev_name(&pdev->dev);
        mci->scrub_mode = SCRUB_SW_SRC;
 
        /* Only a single 4GB DIMM is supported */
@@ -217,10 +235,20 @@ static int highbank_mc_probe(struct platform_device *pdev)
        if (res < 0)
                goto err;
 
-       highbank_mc_create_debugfs_nodes(mci);
+       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 err2;
+       }
+
+       device_create_file(&mci->dev, &dev_attr_inject_ctrl);
 
        devres_close_group(&pdev->dev, NULL);
        return 0;
+err2:
+       edac_mc_del_mc(&pdev->dev);
 err:
        devres_release_group(&pdev->dev, NULL);
        edac_mc_free(mci);
@@ -231,17 +259,12 @@ static int highbank_mc_remove(struct platform_device *pdev)
 {
        struct mem_ctl_info *mci = platform_get_drvdata(pdev);
 
+       device_remove_file(&mci->dev, &dev_attr_inject_ctrl);
        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,
index 3eb32f6..fd46b0b 100644 (file)
@@ -327,28 +327,6 @@ err:
 }
 EXPORT_SYMBOL(mpc85xx_pci_err_probe);
 
-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;
-
-       edac_dbg(0, "\n");
-
-       out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR,
-                orig_pci_err_cap_dr);
-
-       out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, orig_pci_err_en);
-
-       edac_pci_del_device(pci->dev);
-
-       if (edac_op_state == EDAC_OPSTATE_INT)
-               irq_dispose_mapping(pdata->irq);
-
-       edac_pci_free_ctl_info(pci);
-
-       return 0;
-}
-
 #endif                         /* CONFIG_PCI */
 
 /**************************** L2 Err device ***************************/
index 88f60c5..8472405 100644 (file)
@@ -34,7 +34,7 @@ static int probed;
 /*
  * Alter this version for the module when modifications are made
  */
-#define SBRIDGE_REVISION    " Ver: 1.0.0 "
+#define SBRIDGE_REVISION    " Ver: 1.1.0 "
 #define EDAC_MOD_STR      "sbridge_edac"
 
 /*
@@ -83,11 +83,17 @@ static int probed;
 #define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR3   0x3c77  /* 16.7 */
 
 /* Devices 12 Function 6, Offsets 0x80 to 0xcc */
-static const u32 dram_rule[] = {
+static const u32 sbridge_dram_rule[] = {
        0x80, 0x88, 0x90, 0x98, 0xa0,
        0xa8, 0xb0, 0xb8, 0xc0, 0xc8,
 };
-#define MAX_SAD                ARRAY_SIZE(dram_rule)
+
+static const u32 ibridge_dram_rule[] = {
+       0x60, 0x68, 0x70, 0x78, 0x80,
+       0x88, 0x90, 0x98, 0xa0, 0xa8,
+       0xb0, 0xb8, 0xc0, 0xc8, 0xd0,
+       0xd8, 0xe0, 0xe8, 0xf0, 0xf8,
+};
 
 #define SAD_LIMIT(reg)         ((GET_BITFIELD(reg, 6, 25) << 26) | 0x3ffffff)
 #define DRAM_ATTR(reg)         GET_BITFIELD(reg, 2,  3)
@@ -108,43 +114,50 @@ static char *get_dram_attr(u32 reg)
        }
 }
 
-static const u32 interleave_list[] = {
+static const u32 sbridge_interleave_list[] = {
        0x84, 0x8c, 0x94, 0x9c, 0xa4,
        0xac, 0xb4, 0xbc, 0xc4, 0xcc,
 };
-#define MAX_INTERLEAVE ARRAY_SIZE(interleave_list)
-
-#define SAD_PKG0(reg)          GET_BITFIELD(reg, 0, 2)
-#define SAD_PKG1(reg)          GET_BITFIELD(reg, 3, 5)
-#define SAD_PKG2(reg)          GET_BITFIELD(reg, 8, 10)
-#define SAD_PKG3(reg)          GET_BITFIELD(reg, 11, 13)
-#define SAD_PKG4(reg)          GET_BITFIELD(reg, 16, 18)
-#define SAD_PKG5(reg)          GET_BITFIELD(reg, 19, 21)
-#define SAD_PKG6(reg)          GET_BITFIELD(reg, 24, 26)
-#define SAD_PKG7(reg)          GET_BITFIELD(reg, 27, 29)
-
-static inline int sad_pkg(u32 reg, int interleave)
+
+static const u32 ibridge_interleave_list[] = {
+       0x64, 0x6c, 0x74, 0x7c, 0x84,
+       0x8c, 0x94, 0x9c, 0xa4, 0xac,
+       0xb4, 0xbc, 0xc4, 0xcc, 0xd4,
+       0xdc, 0xe4, 0xec, 0xf4, 0xfc,
+};
+
+struct interleave_pkg {
+       unsigned char start;
+       unsigned char end;
+};
+
+static const struct interleave_pkg sbridge_interleave_pkg[] = {
+       { 0, 2 },
+       { 3, 5 },
+       { 8, 10 },
+       { 11, 13 },
+       { 16, 18 },
+       { 19, 21 },
+       { 24, 26 },
+       { 27, 29 },
+};
+
+static const struct interleave_pkg ibridge_interleave_pkg[] = {
+       { 0, 3 },
+       { 4, 7 },
+       { 8, 11 },
+       { 12, 15 },
+       { 16, 19 },
+       { 20, 23 },
+       { 24, 27 },
+       { 28, 31 },
+};
+
+static inline int sad_pkg(const struct interleave_pkg *table, u32 reg,
+                         int interleave)
 {
-       switch (interleave) {
-       case 0:
-               return SAD_PKG0(reg);
-       case 1:
-               return SAD_PKG1(reg);
-       case 2:
-               return SAD_PKG2(reg);
-       case 3:
-               return SAD_PKG3(reg);
-       case 4:
-               return SAD_PKG4(reg);
-       case 5:
-               return SAD_PKG5(reg);
-       case 6:
-               return SAD_PKG6(reg);
-       case 7:
-               return SAD_PKG7(reg);
-       default:
-               return -EINVAL;
-       }
+       return GET_BITFIELD(reg, table[interleave].start,
+                           table[interleave].end);
 }
 
 /* Devices 12 Function 7 */
@@ -262,7 +275,9 @@ static const u32 correrrthrsld[] = {
 
 /* Device 17, function 0 */
 
-#define RANK_CFG_A             0x0328
+#define SB_RANK_CFG_A          0x0328
+
+#define IB_RANK_CFG_A          0x0320
 
 #define IS_RDIMM_ENABLED(reg)          GET_BITFIELD(reg, 11, 11)
 
@@ -273,8 +288,23 @@ static const u32 correrrthrsld[] = {
 #define NUM_CHANNELS   4
 #define MAX_DIMMS      3               /* Max DIMMS per channel */
 
+enum type {
+       SANDY_BRIDGE,
+       IVY_BRIDGE,
+};
+
+struct sbridge_pvt;
 struct sbridge_info {
-       u32     mcmtr;
+       enum type       type;
+       u32             mcmtr;
+       u32             rankcfgr;
+       u64             (*get_tolm)(struct sbridge_pvt *pvt);
+       u64             (*get_tohm)(struct sbridge_pvt *pvt);
+       const u32       *dram_rule;
+       const u32       *interleave_list;
+       const struct interleave_pkg *interleave_pkg;
+       u8              max_sad;
+       u8              max_interleave;
 };
 
 struct sbridge_channel {
@@ -305,8 +335,9 @@ struct sbridge_dev {
 
 struct sbridge_pvt {
        struct pci_dev          *pci_ta, *pci_ddrio, *pci_ras;
-       struct pci_dev          *pci_sad0, *pci_sad1, *pci_ha0;
-       struct pci_dev          *pci_br;
+       struct pci_dev          *pci_sad0, *pci_sad1;
+       struct pci_dev          *pci_ha0, *pci_ha1;
+       struct pci_dev          *pci_br0, *pci_br1;
        struct pci_dev          *pci_tad[NUM_CHANNELS];
 
        struct sbridge_dev      *sbridge_dev;
@@ -364,11 +395,75 @@ static const struct pci_id_table pci_dev_descr_sbridge_table[] = {
        {0,}                    /* 0 terminated list. */
 };
 
+/* This changes depending if 1HA or 2HA:
+ * 1HA:
+ *     0x0eb8 (17.0) is DDRIO0
+ * 2HA:
+ *     0x0ebc (17.4) is DDRIO0
+ */
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_1HA_DDRIO0     0x0eb8
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_2HA_DDRIO0     0x0ebc
+
+/* pci ids */
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0            0x0ea0
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA         0x0ea8
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_RAS                0x0e71
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD0       0x0eaa
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD1       0x0eab
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD2       0x0eac
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD3       0x0ead
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_SAD                        0x0ec8
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_BR0                        0x0ec9
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_BR1                        0x0eca
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1            0x0e60
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TA         0x0e68
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_RAS                0x0e79
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0       0x0e6a
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD1       0x0e6b
+
+static const struct pci_id_descr pci_dev_descr_ibridge[] = {
+               /* Processor Home Agent */
+       { PCI_DESCR(14, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0, 0)      },
+
+               /* Memory controller */
+       { PCI_DESCR(15, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA, 0)   },
+       { PCI_DESCR(15, 1, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_RAS, 0)  },
+       { PCI_DESCR(15, 2, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD0, 0) },
+       { PCI_DESCR(15, 3, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD1, 0) },
+       { PCI_DESCR(15, 4, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD2, 0) },
+       { PCI_DESCR(15, 5, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD3, 0) },
+
+               /* System Address Decoder */
+       { PCI_DESCR(22, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_SAD, 0)          },
+
+               /* Broadcast Registers */
+       { PCI_DESCR(22, 1, PCI_DEVICE_ID_INTEL_IBRIDGE_BR0, 1)          },
+       { PCI_DESCR(22, 2, PCI_DEVICE_ID_INTEL_IBRIDGE_BR1, 0)          },
+
+               /* Optional, mode 2HA */
+       { PCI_DESCR(28, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1, 1)      },
+#if 0
+       { PCI_DESCR(29, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TA, 1)   },
+       { PCI_DESCR(29, 1, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_RAS, 1)  },
+#endif
+       { PCI_DESCR(29, 2, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0, 1) },
+       { PCI_DESCR(29, 3, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD1, 1) },
+
+       { PCI_DESCR(17, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_1HA_DDRIO0, 1) },
+       { PCI_DESCR(17, 4, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_2HA_DDRIO0, 1) },
+};
+
+static const struct pci_id_table pci_dev_descr_ibridge_table[] = {
+       PCI_ID_TABLE_ENTRY(pci_dev_descr_ibridge),
+       {0,}                    /* 0 terminated list. */
+};
+
 /*
  *     pci_device_id   table for which devices we are looking for
  */
 static DEFINE_PCI_DEVICE_TABLE(sbridge_pci_tbl) = {
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA)},
        {0,}                    /* 0 terminated list. */
 };
 
@@ -458,6 +553,52 @@ static void free_sbridge_dev(struct sbridge_dev *sbridge_dev)
        kfree(sbridge_dev);
 }
 
+static u64 sbridge_get_tolm(struct sbridge_pvt *pvt)
+{
+       u32 reg;
+
+       /* Address range is 32:28 */
+       pci_read_config_dword(pvt->pci_sad1, TOLM, &reg);
+       return GET_TOLM(reg);
+}
+
+static u64 sbridge_get_tohm(struct sbridge_pvt *pvt)
+{
+       u32 reg;
+
+       pci_read_config_dword(pvt->pci_sad1, TOHM, &reg);
+       return GET_TOHM(reg);
+}
+
+static u64 ibridge_get_tolm(struct sbridge_pvt *pvt)
+{
+       u32 reg;
+
+       pci_read_config_dword(pvt->pci_br1, TOLM, &reg);
+
+       return GET_TOLM(reg);
+}
+
+static u64 ibridge_get_tohm(struct sbridge_pvt *pvt)
+{
+       u32 reg;
+
+       pci_read_config_dword(pvt->pci_br1, TOHM, &reg);
+
+       return GET_TOHM(reg);
+}
+
+static inline u8 sad_pkg_socket(u8 pkg)
+{
+       /* on Ivy Bridge, nodeID is SASS, where A is HA and S is node id */
+       return (pkg >> 3) | (pkg & 0x3);
+}
+
+static inline u8 sad_pkg_ha(u8 pkg)
+{
+       return (pkg >> 2) & 0x1;
+}
+
 /****************************************************************************
                        Memory check routines
  ****************************************************************************/
@@ -520,10 +661,10 @@ static int get_dimm_config(struct mem_ctl_info *mci)
        enum edac_type mode;
        enum mem_type mtype;
 
-       pci_read_config_dword(pvt->pci_br, SAD_TARGET, &reg);
+       pci_read_config_dword(pvt->pci_br0, SAD_TARGET, &reg);
        pvt->sbridge_dev->source_id = SOURCE_ID(reg);
 
-       pci_read_config_dword(pvt->pci_br, SAD_CONTROL, &reg);
+       pci_read_config_dword(pvt->pci_br0, SAD_CONTROL, &reg);
        pvt->sbridge_dev->node_id = NODE_ID(reg);
        edac_dbg(0, "mc#%d: Node ID: %d, source ID: %d\n",
                 pvt->sbridge_dev->mc,
@@ -558,7 +699,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
        }
 
        if (pvt->pci_ddrio) {
-               pci_read_config_dword(pvt->pci_ddrio, RANK_CFG_A, &reg);
+               pci_read_config_dword(pvt->pci_ddrio, pvt->info.rankcfgr,
+                                     &reg);
                if (IS_RDIMM_ENABLED(reg)) {
                        /* FIXME: Can also be LRDIMM */
                        edac_dbg(0, "Memory is registered\n");
@@ -629,19 +771,14 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
         * Step 1) Get TOLM/TOHM ranges
         */
 
-       /* Address range is 32:28 */
-       pci_read_config_dword(pvt->pci_sad1, TOLM,
-                             &reg);
-       pvt->tolm = GET_TOLM(reg);
+       pvt->tolm = pvt->info.get_tolm(pvt);
        tmp_mb = (1 + pvt->tolm) >> 20;
 
        mb = div_u64_rem(tmp_mb, 1000, &kb);
        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,
-                             &reg);
-       pvt->tohm = GET_TOHM(reg);
+       pvt->tohm = pvt->info.get_tohm(pvt);
        tmp_mb = (1 + pvt->tohm) >> 20;
 
        mb = div_u64_rem(tmp_mb, 1000, &kb);
@@ -654,9 +791,9 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
         * algorithm bellow.
         */
        prv = 0;
-       for (n_sads = 0; n_sads < MAX_SAD; n_sads++) {
+       for (n_sads = 0; n_sads < pvt->info.max_sad; n_sads++) {
                /* SAD_LIMIT Address range is 45:26 */
-               pci_read_config_dword(pvt->pci_sad0, dram_rule[n_sads],
+               pci_read_config_dword(pvt->pci_sad0, pvt->info.dram_rule[n_sads],
                                      &reg);
                limit = SAD_LIMIT(reg);
 
@@ -677,15 +814,16 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
                         reg);
                prv = limit;
 
-               pci_read_config_dword(pvt->pci_sad0, interleave_list[n_sads],
+               pci_read_config_dword(pvt->pci_sad0, pvt->info.interleave_list[n_sads],
                                      &reg);
-               sad_interl = sad_pkg(reg, 0);
+               sad_interl = sad_pkg(pvt->info.interleave_pkg, reg, 0);
                for (j = 0; j < 8; j++) {
-                       if (j > 0 && sad_interl == sad_pkg(reg, j))
+                       u32 pkg = sad_pkg(pvt->info.interleave_pkg, reg, j);
+                       if (j > 0 && sad_interl == pkg)
                                break;
 
                        edac_dbg(0, "SAD#%d, interleave #%d: %d\n",
-                                n_sads, j, sad_pkg(reg, j));
+                                n_sads, j, pkg);
                }
        }
 
@@ -797,12 +935,13 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
 {
        struct mem_ctl_info     *new_mci;
        struct sbridge_pvt *pvt = mci->pvt_info;
+       struct pci_dev          *pci_ha;
        int                     n_rir, n_sads, n_tads, sad_way, sck_xch;
        int                     sad_interl, idx, base_ch;
        int                     interleave_mode;
-       unsigned                sad_interleave[MAX_INTERLEAVE];
+       unsigned                sad_interleave[pvt->info.max_interleave];
        u32                     reg;
-       u8                      ch_way,sck_way;
+       u8                      ch_way, sck_way, pkg, sad_ha = 0;
        u32                     tad_offset;
        u32                     rir_way;
        u32                     mb, kb;
@@ -828,8 +967,8 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
        /*
         * Step 1) Get socket
         */
-       for (n_sads = 0; n_sads < MAX_SAD; n_sads++) {
-               pci_read_config_dword(pvt->pci_sad0, dram_rule[n_sads],
+       for (n_sads = 0; n_sads < pvt->info.max_sad; n_sads++) {
+               pci_read_config_dword(pvt->pci_sad0, pvt->info.dram_rule[n_sads],
                                      &reg);
 
                if (!DRAM_RULE_ENABLE(reg))
@@ -844,53 +983,65 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                        break;
                prv = limit;
        }
-       if (n_sads == MAX_SAD) {
+       if (n_sads == pvt->info.max_sad) {
                sprintf(msg, "Can't discover the memory socket");
                return -EINVAL;
        }
        *area_type = get_dram_attr(reg);
        interleave_mode = INTERLEAVE_MODE(reg);
 
-       pci_read_config_dword(pvt->pci_sad0, interleave_list[n_sads],
+       pci_read_config_dword(pvt->pci_sad0, pvt->info.interleave_list[n_sads],
                              &reg);
-       sad_interl = sad_pkg(reg, 0);
-       for (sad_way = 0; sad_way < 8; sad_way++) {
-               if (sad_way > 0 && sad_interl == sad_pkg(reg, sad_way))
+
+       if (pvt->info.type == SANDY_BRIDGE) {
+               sad_interl = sad_pkg(pvt->info.interleave_pkg, reg, 0);
+               for (sad_way = 0; sad_way < 8; sad_way++) {
+                       u32 pkg = sad_pkg(pvt->info.interleave_pkg, reg, sad_way);
+                       if (sad_way > 0 && sad_interl == pkg)
+                               break;
+                       sad_interleave[sad_way] = pkg;
+                       edac_dbg(0, "SAD interleave #%d: %d\n",
+                                sad_way, sad_interleave[sad_way]);
+               }
+               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
+                       idx = (addr >> 6) & 7;
+               switch (sad_way) {
+               case 1:
+                       idx = 0;
                        break;
-               sad_interleave[sad_way] = sad_pkg(reg, sad_way);
-               edac_dbg(0, "SAD interleave #%d: %d\n",
-                        sad_way, sad_interleave[sad_way]);
-       }
-       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
+               case 2:
+                       idx = idx & 1;
+                       break;
+               case 4:
+                       idx = idx & 3;
+                       break;
+               case 8:
+                       break;
+               default:
+                       sprintf(msg, "Can't discover socket interleave");
+                       return -EINVAL;
+               }
+               *socket = sad_interleave[idx];
+               edac_dbg(0, "SAD interleave index: %d (wayness %d) = CPU socket %d\n",
+                        idx, sad_way, *socket);
+       } else {
+               /* Ivy Bridge's SAD mode doesn't support XOR interleave mode */
                idx = (addr >> 6) & 7;
-       switch (sad_way) {
-       case 1:
-               idx = 0;
-               break;
-       case 2:
-               idx = idx & 1;
-               break;
-       case 4:
-               idx = idx & 3;
-               break;
-       case 8:
-               break;
-       default:
-               sprintf(msg, "Can't discover socket interleave");
-               return -EINVAL;
+               pkg = sad_pkg(pvt->info.interleave_pkg, reg, idx);
+               *socket = sad_pkg_socket(pkg);
+               sad_ha = sad_pkg_ha(pkg);
+               edac_dbg(0, "SAD interleave package: %d = CPU socket %d, HA %d\n",
+                        idx, *socket, sad_ha);
        }
-       *socket = sad_interleave[idx];
-       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
@@ -909,9 +1060,16 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
         * Step 2) Get memory channel
         */
        prv = 0;
+       if (pvt->info.type == SANDY_BRIDGE)
+               pci_ha = pvt->pci_ha0;
+       else {
+               if (sad_ha)
+                       pci_ha = pvt->pci_ha1;
+               else
+                       pci_ha = pvt->pci_ha0;
+       }
        for (n_tads = 0; n_tads < MAX_TAD; n_tads++) {
-               pci_read_config_dword(pvt->pci_ha0, tad_dram_rule[n_tads],
-                                     &reg);
+               pci_read_config_dword(pci_ha, tad_dram_rule[n_tads], &reg);
                limit = TAD_LIMIT(reg);
                if (limit <= prv) {
                        sprintf(msg, "Can't discover the memory channel");
@@ -921,14 +1079,13 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                        break;
                prv = limit;
        }
+       if (n_tads == MAX_TAD) {
+               sprintf(msg, "Can't discover the memory channel");
+               return -EINVAL;
+       }
+
        ch_way = TAD_CH(reg) + 1;
        sck_way = TAD_SOCK(reg) + 1;
-       /*
-        * FIXME: Is it right to always use channel 0 for offsets?
-        */
-       pci_read_config_dword(pvt->pci_tad[0],
-                               tad_ch_nilv_offset[n_tads],
-                               &tad_offset);
 
        if (ch_way == 3)
                idx = addr >> 6;
@@ -958,6 +1115,10 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
        }
        *channel_mask = 1 << base_ch;
 
+       pci_read_config_dword(pvt->pci_tad[base_ch],
+                               tad_ch_nilv_offset[n_tads],
+                               &tad_offset);
+
        if (pvt->is_mirrored) {
                *channel_mask |= 1 << ((base_ch + 2) % 4);
                switch(ch_way) {
@@ -1091,12 +1252,6 @@ static void sbridge_put_all_devices(void)
        }
 }
 
-/*
- *     sbridge_get_all_devices Find and perform 'get' operation on the MCH's
- *                     device/functions we want to reference for this driver
- *
- *                     Need to 'get' device 16 func 1 and func 2
- */
 static int sbridge_get_onedevice(struct pci_dev **prev,
                                 u8 *num_mc,
                                 const struct pci_id_table *table,
@@ -1198,11 +1353,21 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
        return 0;
 }
 
-static int sbridge_get_all_devices(u8 *num_mc)
+/*
+ * sbridge_get_all_devices - Find and perform 'get' operation on the MCH's
+ *                          device/functions we want to reference for this driver.
+ *                          Need to 'get' device 16 func 1 and func 2.
+ * @num_mc: pointer to the memory controllers count, to be incremented in case
+ *         of success.
+ * @table: model specific table
+ *
+ * returns 0 in case of success or error code
+ */
+static int sbridge_get_all_devices(u8 *num_mc,
+                                  const struct pci_id_table *table)
 {
        int i, rc;
        struct pci_dev *pdev = NULL;
-       const struct pci_id_table *table = pci_dev_descr_sbridge_table;
 
        while (table && table->descr) {
                for (i = 0; i < table->n_devs; i++) {
@@ -1226,8 +1391,8 @@ static int sbridge_get_all_devices(u8 *num_mc)
        return 0;
 }
 
-static int mci_bind_devs(struct mem_ctl_info *mci,
-                        struct sbridge_dev *sbridge_dev)
+static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
+                                struct sbridge_dev *sbridge_dev)
 {
        struct sbridge_pvt *pvt = mci->pvt_info;
        struct pci_dev *pdev;
@@ -1255,7 +1420,7 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
                case 13:
                        switch (func) {
                        case 6:
-                               pvt->pci_br = pdev;
+                               pvt->pci_br0 = pdev;
                                break;
                        default:
                                goto error;
@@ -1329,6 +1494,131 @@ error:
        return -EINVAL;
 }
 
+static int ibridge_mci_bind_devs(struct mem_ctl_info *mci,
+                                struct sbridge_dev *sbridge_dev)
+{
+       struct sbridge_pvt *pvt = mci->pvt_info;
+       struct pci_dev *pdev, *tmp;
+       int i, func, slot;
+       bool mode_2ha = false;
+
+       tmp = pci_get_device(PCI_VENDOR_ID_INTEL,
+                            PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1, NULL);
+       if (tmp) {
+               mode_2ha = true;
+               pci_dev_put(tmp);
+       }
+
+       for (i = 0; i < sbridge_dev->n_devs; i++) {
+               pdev = sbridge_dev->pdev[i];
+               if (!pdev)
+                       continue;
+               slot = PCI_SLOT(pdev->devfn);
+               func = PCI_FUNC(pdev->devfn);
+
+               switch (slot) {
+               case 14:
+                       if (func == 0) {
+                               pvt->pci_ha0 = pdev;
+                               break;
+                       }
+                       goto error;
+               case 15:
+                       switch (func) {
+                       case 0:
+                               pvt->pci_ta = pdev;
+                               break;
+                       case 1:
+                               pvt->pci_ras = pdev;
+                               break;
+                       case 4:
+                       case 5:
+                               /* if we have 2 HAs active, channels 2 and 3
+                                * are in other device */
+                               if (mode_2ha)
+                                       break;
+                               /* fall through */
+                       case 2:
+                       case 3:
+                               pvt->pci_tad[func - 2] = pdev;
+                               break;
+                       default:
+                               goto error;
+                       }
+                       break;
+               case 17:
+                       if (func == 4) {
+                               pvt->pci_ddrio = pdev;
+                               break;
+                       } else if (func == 0) {
+                               if (!mode_2ha)
+                                       pvt->pci_ddrio = pdev;
+                               break;
+                       }
+                       goto error;
+               case 22:
+                       switch (func) {
+                       case 0:
+                               pvt->pci_sad0 = pdev;
+                               break;
+                       case 1:
+                               pvt->pci_br0 = pdev;
+                               break;
+                       case 2:
+                               pvt->pci_br1 = pdev;
+                               break;
+                       default:
+                               goto error;
+                       }
+                       break;
+               case 28:
+                       if (func == 0) {
+                               pvt->pci_ha1 = pdev;
+                               break;
+                       }
+                       goto error;
+               case 29:
+                       /* we shouldn't have this device if we have just one
+                        * HA present */
+                       WARN_ON(!mode_2ha);
+                       if (func == 2 || func == 3) {
+                               pvt->pci_tad[func] = pdev;
+                               break;
+                       }
+                       goto error;
+               default:
+                       goto error;
+               }
+
+               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 */
+       if (!pvt->pci_sad0 || !pvt->pci_ha0 || !pvt->pci_br0 ||
+           !pvt->pci_br1 || !pvt->pci_tad || !pvt->pci_ras  ||
+           !pvt->pci_ta)
+               goto enodev;
+
+       for (i = 0; i < NUM_CHANNELS; i++) {
+               if (!pvt->pci_tad[i])
+                       goto enodev;
+       }
+       return 0;
+
+enodev:
+       sbridge_printk(KERN_ERR, "Some needed devices are missing\n");
+       return -ENODEV;
+
+error:
+       sbridge_printk(KERN_ERR,
+                      "Device %d, function %d is out of the expected range\n",
+                      slot, func);
+       return -EINVAL;
+}
+
 /****************************************************************************
                        Error check routines
  ****************************************************************************/
@@ -1349,7 +1639,7 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
        bool ripv = GET_BITFIELD(m->mcgstatus, 0, 0);
        bool overflow = GET_BITFIELD(m->status, 62, 62);
        bool uncorrected_error = GET_BITFIELD(m->status, 61, 61);
-       bool recoverable = GET_BITFIELD(m->status, 56, 56);
+       bool recoverable;
        u32 core_err_cnt = GET_BITFIELD(m->status, 38, 52);
        u32 mscod = GET_BITFIELD(m->status, 16, 31);
        u32 errcode = GET_BITFIELD(m->status, 0, 15);
@@ -1360,6 +1650,11 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
        int rc, dimm;
        char *area_type = NULL;
 
+       if (pvt->info.type == IVY_BRIDGE)
+               recoverable = true;
+       else
+               recoverable = GET_BITFIELD(m->status, 56, 56);
+
        if (uncorrected_error) {
                if (ripv) {
                        type = "FATAL";
@@ -1409,6 +1704,10 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
                }
        }
 
+       /* Only decode errors with an valid address (ADDRV) */
+       if (!GET_BITFIELD(m->status, 58, 58))
+               return;
+
        rc = get_memory_error_data(mci, m->addr, &socket,
                                   &channel_mask, &rank, &area_type, msg);
        if (rc < 0)
@@ -1614,11 +1913,12 @@ static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev)
        sbridge_dev->mci = NULL;
 }
 
-static int sbridge_register_mci(struct sbridge_dev *sbridge_dev)
+static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
 {
        struct mem_ctl_info *mci;
        struct edac_mc_layer layers[2];
        struct sbridge_pvt *pvt;
+       struct pci_dev *pdev = sbridge_dev->pdev[0];
        int rc;
 
        /* Check the number of active and not disabled channels */
@@ -1640,7 +1940,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev)
                return -ENOMEM;
 
        edac_dbg(0, "MC: mci = %p, dev = %p\n",
-                mci, &sbridge_dev->pdev[0]->dev);
+                mci, &pdev->dev);
 
        pvt = mci->pvt_info;
        memset(pvt, 0, sizeof(*pvt));
@@ -1654,24 +1954,52 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev)
        mci->edac_cap = EDAC_FLAG_NONE;
        mci->mod_name = "sbridge_edac.c";
        mci->mod_ver = SBRIDGE_REVISION;
-       mci->ctl_name = kasprintf(GFP_KERNEL, "Sandy Bridge Socket#%d", mci->mc_idx);
-       mci->dev_name = pci_name(sbridge_dev->pdev[0]);
+       mci->dev_name = pci_name(pdev);
        mci->ctl_page_to_phys = NULL;
 
        /* Set the function pointer to an actual operation function */
        mci->edac_check = sbridge_check_error;
 
-       /* Store pci devices at mci for faster access */
-       rc = mci_bind_devs(mci, sbridge_dev);
-       if (unlikely(rc < 0))
-               goto fail0;
+       pvt->info.type = type;
+       if (type == IVY_BRIDGE) {
+               pvt->info.rankcfgr = IB_RANK_CFG_A;
+               pvt->info.get_tolm = ibridge_get_tolm;
+               pvt->info.get_tohm = ibridge_get_tohm;
+               pvt->info.dram_rule = ibridge_dram_rule;
+               pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
+               pvt->info.interleave_list = ibridge_interleave_list;
+               pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
+               pvt->info.interleave_pkg = ibridge_interleave_pkg;
+               mci->ctl_name = kasprintf(GFP_KERNEL, "Ivy Bridge Socket#%d", mci->mc_idx);
+
+               /* Store pci devices at mci for faster access */
+               rc = ibridge_mci_bind_devs(mci, sbridge_dev);
+               if (unlikely(rc < 0))
+                       goto fail0;
+       } else {
+               pvt->info.rankcfgr = SB_RANK_CFG_A;
+               pvt->info.get_tolm = sbridge_get_tolm;
+               pvt->info.get_tohm = sbridge_get_tohm;
+               pvt->info.dram_rule = sbridge_dram_rule;
+               pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule);
+               pvt->info.interleave_list = sbridge_interleave_list;
+               pvt->info.max_interleave = ARRAY_SIZE(sbridge_interleave_list);
+               pvt->info.interleave_pkg = sbridge_interleave_pkg;
+               mci->ctl_name = kasprintf(GFP_KERNEL, "Sandy Bridge Socket#%d", mci->mc_idx);
+
+               /* Store pci devices at mci for faster access */
+               rc = sbridge_mci_bind_devs(mci, sbridge_dev);
+               if (unlikely(rc < 0))
+                       goto fail0;
+       }
+
 
        /* Get dimm basic config and the memory layout */
        get_dimm_config(mci);
        get_memory_layout(mci);
 
        /* record ptr to the generic device */
-       mci->pdev = &sbridge_dev->pdev[0]->dev;
+       mci->pdev = &pdev->dev;
 
        /* add this new MC control structure to EDAC's list of MCs */
        if (unlikely(edac_mc_add_mc(mci))) {
@@ -1702,6 +2030,7 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        int rc;
        u8 mc, num_mc = 0;
        struct sbridge_dev *sbridge_dev;
+       enum type type;
 
        /* get the pci devices we want to reserve for our use */
        mutex_lock(&sbridge_edac_lock);
@@ -1715,7 +2044,13 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        }
        probed++;
 
-       rc = sbridge_get_all_devices(&num_mc);
+       if (pdev->device == PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA) {
+               rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_ibridge_table);
+               type = IVY_BRIDGE;
+       } else {
+               rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_sbridge_table);
+               type = SANDY_BRIDGE;
+       }
        if (unlikely(rc < 0))
                goto fail0;
        mc = 0;
@@ -1724,7 +2059,7 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                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);
+               rc = sbridge_register_mci(sbridge_dev, type);
                if (unlikely(rc < 0))
                        goto fail1;
        }
@@ -1839,5 +2174,5 @@ MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
 MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
-MODULE_DESCRIPTION("MC Driver for Intel Sandy Bridge memory controllers - "
+MODULE_DESCRIPTION("MC Driver for Intel Sandy Bridge and Ivy Bridge memory controllers - "
                   SBRIDGE_REVISION);
index e5af0e3..0e79951 100644 (file)
@@ -477,7 +477,7 @@ void fw_send_phy_config(struct fw_card *card,
        phy_config_packet.header[1] = data;
        phy_config_packet.header[2] = ~data;
        phy_config_packet.generation = generation;
-       INIT_COMPLETION(phy_config_done);
+       reinit_completion(&phy_config_done);
 
        card->driver->send_request(card, &phy_config_packet);
        wait_for_completion_timeout(&phy_config_done, timeout);
index c01cf45..3a75f42 100644 (file)
@@ -46,6 +46,6 @@ config FMC_CHARDEV
          This driver matches every mezzanine device and allows user
          space to read and write registers using a char device. It
          can be used to write user-space drivers, or just get
-         aquainted with a mezzanine before writing its specific driver.
+         acquainted with a mezzanine before writing its specific driver.
 
 endif # FMC
index 7dd4461..4e10b10 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/acpi_gpio.h>
 #include <linux/idr.h>
 #include <linux/slab.h>
+#include <linux/acpi.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/gpio.h>
index 955555d..f864275 100644 (file)
@@ -29,11 +29,17 @@ config DRM_USB
 config DRM_KMS_HELPER
        tristate
        depends on DRM
+       help
+         CRTC helpers for KMS drivers.
+
+config DRM_KMS_FB_HELPER
+       bool
+       depends on DRM_KMS_HELPER
        select FB
        select FRAMEBUFFER_CONSOLE if !EXPERT
        select FRAMEBUFFER_CONSOLE_DETECT_PRIMARY if FRAMEBUFFER_CONSOLE
        help
-         FB and CRTC helpers for KMS drivers.
+         FBDEV helpers for KMS drivers.
 
 config DRM_LOAD_EDID_FIRMWARE
        bool "Allow to specify an EDID data set instead of probing for it"
@@ -64,6 +70,7 @@ config DRM_GEM_CMA_HELPER
 config DRM_KMS_CMA_HELPER
        bool
        select DRM_GEM_CMA_HELPER
+       select DRM_KMS_FB_HELPER
        select FB_SYS_FILLRECT
        select FB_SYS_COPYAREA
        select FB_SYS_IMAGEBLIT
@@ -96,6 +103,7 @@ config DRM_RADEON
        select FB_CFB_IMAGEBLIT
        select FW_LOADER
         select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
         select DRM_TTM
        select POWER_SUPPLY
        select HWMON
@@ -120,64 +128,7 @@ config DRM_I810
          selected, the module will be called i810.  AGP support is required
          for this driver to work.
 
-config DRM_I915
-       tristate "Intel 8xx/9xx/G3x/G4x/HD Graphics"
-       depends on DRM
-       depends on AGP
-       depends on AGP_INTEL
-       # we need shmfs for the swappable backing store, and in particular
-       # the shmem_readpage() which depends upon tmpfs
-       select SHMEM
-       select TMPFS
-       select DRM_KMS_HELPER
-       select FB_CFB_FILLRECT
-       select FB_CFB_COPYAREA
-       select FB_CFB_IMAGEBLIT
-       # i915 depends on ACPI_VIDEO when ACPI is enabled
-       # but for select to work, need to select ACPI_VIDEO's dependencies, ick
-       select BACKLIGHT_LCD_SUPPORT if ACPI
-       select BACKLIGHT_CLASS_DEVICE if ACPI
-       select VIDEO_OUTPUT_CONTROL if ACPI
-       select INPUT if ACPI
-       select THERMAL if ACPI
-       select ACPI_VIDEO if ACPI
-       select ACPI_BUTTON if ACPI
-       help
-         Choose this option if you have a system that has "Intel Graphics
-         Media Accelerator" or "HD Graphics" integrated graphics,
-         including 830M, 845G, 852GM, 855GM, 865G, 915G, 945G, 965G,
-         G35, G41, G43, G45 chipsets and Celeron, Pentium, Core i3,
-         Core i5, Core i7 as well as Atom CPUs with integrated graphics.
-         If M is selected, the module will be called i915.  AGP support
-         is required for this driver to work. This driver is used by
-         the Intel driver in X.org 6.8 and XFree86 4.4 and above. It
-         replaces the older i830 module that supported a subset of the
-         hardware in older X.org releases.
-
-         Note that the older i810/i815 chipsets require the use of the
-         i810 driver instead, and the Atom z5xx series has an entirely
-         different implementation.
-
-config DRM_I915_KMS
-       bool "Enable modesetting on intel by default"
-       depends on DRM_I915
-       help
-         Choose this option if you want kernel modesetting enabled by default,
-         and you have a new enough userspace to support this. Running old
-         userspaces with this enabled will cause pain.  Note that this causes
-         the driver to bind to PCI devices, which precludes loading things
-         like intelfb.
-
-config DRM_I915_PRELIMINARY_HW_SUPPORT
-       bool "Enable preliminary support for prerelease Intel hardware by default"
-       depends on DRM_I915
-       help
-         Choose this option if you have prerelease Intel hardware and want the
-         i915 driver to support it by default.  You can enable such support at
-         runtime with the module option i915.preliminary_hw_support=1; this
-         option changes the default for that module option.
-
-         If in doubt, say "N".
+source "drivers/gpu/drm/i915/Kconfig"
 
 config DRM_MGA
        tristate "Matrox g200/g400"
@@ -225,6 +176,8 @@ source "drivers/gpu/drm/mgag200/Kconfig"
 
 source "drivers/gpu/drm/cirrus/Kconfig"
 
+source "drivers/gpu/drm/armada/Kconfig"
+
 source "drivers/gpu/drm/rcar-du/Kconfig"
 
 source "drivers/gpu/drm/shmobile/Kconfig"
@@ -236,3 +189,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
 source "drivers/gpu/drm/qxl/Kconfig"
 
 source "drivers/gpu/drm/msm/Kconfig"
+
+source "drivers/gpu/drm/tegra/Kconfig"
index f089adf..cc08b84 100644 (file)
@@ -21,8 +21,9 @@ drm-$(CONFIG_PCI) += ati_pcigart.o
 
 drm-usb-y   := drm_usb.o
 
-drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_helper.o
+drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o
 drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
+drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o
 drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
 
 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
@@ -49,10 +50,12 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
+obj-$(CONFIG_DRM_ARMADA) += armada/
 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_OMAP) += omapdrm/
 obj-$(CONFIG_DRM_TILCDC)       += tilcdc/
 obj-$(CONFIG_DRM_QXL) += qxl/
 obj-$(CONFIG_DRM_MSM) += msm/
+obj-$(CONFIG_DRM_TEGRA) += tegra/
 obj-y                  += i2c/
diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
new file mode 100644 (file)
index 0000000..40d3715
--- /dev/null
@@ -0,0 +1,24 @@
+config DRM_ARMADA
+       tristate "DRM support for Marvell Armada SoCs"
+       depends on DRM && HAVE_CLK && ARM
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       select DRM_KMS_HELPER
+       help
+         Support the "LCD" controllers found on the Marvell Armada 510
+         devices.  There are two controllers on the device, each controller
+         supports graphics and video overlays.
+
+         This driver provides no built-in acceleration; acceleration is
+         performed by other IP found on the SoC.  This driver provides
+         kernel mode setting and buffer management to userspace.
+
+config DRM_ARMADA_TDA1998X
+       bool "Support TDA1998X HDMI output"
+       depends on DRM_ARMADA != n
+       depends on I2C && DRM_I2C_NXP_TDA998X = y
+       default y
+       help
+         Support the TDA1998x HDMI output device found on the Solid-Run
+         CuBox.
diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile
new file mode 100644 (file)
index 0000000..d6f43e0
--- /dev/null
@@ -0,0 +1,7 @@
+armada-y       := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
+                  armada_gem.o armada_output.o armada_overlay.o \
+                  armada_slave.o
+armada-y       += armada_510.o
+armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
+
+obj-$(CONFIG_DRM_ARMADA) := armada.o
diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c
new file mode 100644 (file)
index 0000000..59948ef
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ *
+ * Armada 510 (aka Dove) variant support
+ */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_hw.h"
+
+static int armada510_init(struct armada_private *priv, struct device *dev)
+{
+       priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1");
+
+       if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
+               priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
+
+       return PTR_RET(priv->extclk[0]);
+}
+
+static int armada510_crtc_init(struct armada_crtc *dcrtc)
+{
+       /* Lower the watermark so to eliminate jitter at higher bandwidths */
+       armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
+       return 0;
+}
+
+/*
+ * Armada510 specific SCLK register selection.
+ * This gets called with sclk = NULL to test whether the mode is
+ * supportable, and again with sclk != NULL to set the clocks up for
+ * that.  The former can return an error, but the latter is expected
+ * not to.
+ *
+ * We currently are pretty rudimentary here, always selecting
+ * EXT_REF_CLK_1 for LCD0 and erroring LCD1.  This needs improvement!
+ */
+static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
+       const struct drm_display_mode *mode, uint32_t *sclk)
+{
+       struct armada_private *priv = dcrtc->crtc.dev->dev_private;
+       struct clk *clk = priv->extclk[0];
+       int ret;
+
+       if (dcrtc->num == 1)
+               return -EINVAL;
+
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       if (dcrtc->clk != clk) {
+               ret = clk_prepare_enable(clk);
+               if (ret)
+                       return ret;
+               dcrtc->clk = clk;
+       }
+
+       if (sclk) {
+               uint32_t rate, ref, div;
+
+               rate = mode->clock * 1000;
+               ref = clk_round_rate(clk, rate);
+               div = DIV_ROUND_UP(ref, rate);
+               if (div < 1)
+                       div = 1;
+
+               clk_set_rate(clk, ref);
+               *sclk = div | SCLK_510_EXTCLK1;
+       }
+
+       return 0;
+}
+
+const struct armada_variant armada510_ops = {
+       .has_spu_adv_reg = true,
+       .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
+       .init = armada510_init,
+       .crtc_init = armada510_crtc_init,
+       .crtc_compute_clock = armada510_crtc_compute_clock,
+};
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
new file mode 100644 (file)
index 0000000..d8e3982
--- /dev/null
@@ -0,0 +1,1098 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Rewritten from the dovefb driver, and Armada510 manuals.
+ *
+ * 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/clk.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+
+struct armada_frame_work {
+       struct drm_pending_vblank_event *event;
+       struct armada_regs regs[4];
+       struct drm_framebuffer *old_fb;
+};
+
+enum csc_mode {
+       CSC_AUTO = 0,
+       CSC_YUV_CCIR601 = 1,
+       CSC_YUV_CCIR709 = 2,
+       CSC_RGB_COMPUTER = 1,
+       CSC_RGB_STUDIO = 2,
+};
+
+/*
+ * A note about interlacing.  Let's consider HDMI 1920x1080i.
+ * The timing parameters we have from X are:
+ *  Hact HsyA HsyI Htot  Vact VsyA VsyI Vtot
+ *  1920 2448 2492 2640  1080 1084 1094 1125
+ * Which get translated to:
+ *  Hact HsyA HsyI Htot  Vact VsyA VsyI Vtot
+ *  1920 2448 2492 2640   540  542  547  562
+ *
+ * This is how it is defined by CEA-861-D - line and pixel numbers are
+ * referenced to the rising edge of VSYNC and HSYNC.  Total clocks per
+ * line: 2640.  The odd frame, the first active line is at line 21, and
+ * the even frame, the first active line is 584.
+ *
+ * LN:    560     561     562     563             567     568    569
+ * DE:    ~~~|____________________________//__________________________
+ * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____
+ * VSYNC: _________________________|~~~~~~//~~~~~~~~~~~~~~~|__________
+ *  22 blanking lines.  VSYNC at 1320 (referenced to the HSYNC rising edge).
+ *
+ * LN:    1123   1124    1125      1               5       6      7
+ * DE:    ~~~|____________________________//__________________________
+ * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____
+ * VSYNC: ____________________|~~~~~~~~~~~//~~~~~~~~~~|_______________
+ *  23 blanking lines
+ *
+ * The Armada LCD Controller line and pixel numbers are, like X timings,
+ * referenced to the top left of the active frame.
+ *
+ * So, translating these to our LCD controller:
+ *  Odd frame, 563 total lines, VSYNC at line 543-548, pixel 1128.
+ *  Even frame, 562 total lines, VSYNC at line 542-547, pixel 2448.
+ * Note: Vsync front porch remains constant!
+ *
+ * if (odd_frame) {
+ *   vtotal = mode->crtc_vtotal + 1;
+ *   vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay + 1;
+ *   vhorizpos = mode->crtc_hsync_start - mode->crtc_htotal / 2
+ * } else {
+ *   vtotal = mode->crtc_vtotal;
+ *   vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay;
+ *   vhorizpos = mode->crtc_hsync_start;
+ * }
+ * vfrontporch = mode->crtc_vtotal - mode->crtc_vsync_end;
+ *
+ * So, we need to reprogram these registers on each vsync event:
+ *  LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL
+ *
+ * Note: we do not use the frame done interrupts because these appear
+ * to happen too early, and lead to jitter on the display (presumably
+ * they occur at the end of the last active line, before the vsync back
+ * porch, which we're reprogramming.)
+ */
+
+void
+armada_drm_crtc_update_regs(struct armada_crtc *dcrtc, struct armada_regs *regs)
+{
+       while (regs->offset != ~0) {
+               void __iomem *reg = dcrtc->base + regs->offset;
+               uint32_t val;
+
+               val = regs->mask;
+               if (val != 0)
+                       val &= readl_relaxed(reg);
+               writel_relaxed(val | regs->val, reg);
+               ++regs;
+       }
+}
+
+#define dpms_blanked(dpms)     ((dpms) != DRM_MODE_DPMS_ON)
+
+static void armada_drm_crtc_update(struct armada_crtc *dcrtc)
+{
+       uint32_t dumb_ctrl;
+
+       dumb_ctrl = dcrtc->cfg_dumb_ctrl;
+
+       if (!dpms_blanked(dcrtc->dpms))
+               dumb_ctrl |= CFG_DUMB_ENA;
+
+       /*
+        * When the dumb interface isn't in DUMB24_RGB888_0 mode, it might
+        * be using SPI or GPIO.  If we set this to DUMB_BLANK, we will
+        * force LCD_D[23:0] to output blank color, overriding the GPIO or
+        * SPI usage.  So leave it as-is unless in DUMB24_RGB888_0 mode.
+        */
+       if (dpms_blanked(dcrtc->dpms) &&
+           (dumb_ctrl & DUMB_MASK) == DUMB24_RGB888_0) {
+               dumb_ctrl &= ~DUMB_MASK;
+               dumb_ctrl |= DUMB_BLANK;
+       }
+
+       /*
+        * The documentation doesn't indicate what the normal state of
+        * the sync signals are.  Sebastian Hesselbart kindly probed
+        * these signals on his board to determine their state.
+        *
+        * The non-inverted state of the sync signals is active high.
+        * Setting these bits makes the appropriate signal active low.
+        */
+       if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NCSYNC)
+               dumb_ctrl |= CFG_INV_CSYNC;
+       if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NHSYNC)
+               dumb_ctrl |= CFG_INV_HSYNC;
+       if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NVSYNC)
+               dumb_ctrl |= CFG_INV_VSYNC;
+
+       if (dcrtc->dumb_ctrl != dumb_ctrl) {
+               dcrtc->dumb_ctrl = dumb_ctrl;
+               writel_relaxed(dumb_ctrl, dcrtc->base + LCD_SPU_DUMB_CTRL);
+       }
+}
+
+static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
+       int x, int y, struct armada_regs *regs, bool interlaced)
+{
+       struct armada_gem_object *obj = drm_fb_obj(fb);
+       unsigned pitch = fb->pitches[0];
+       unsigned offset = y * pitch + x * fb->bits_per_pixel / 8;
+       uint32_t addr_odd, addr_even;
+       unsigned i = 0;
+
+       DRM_DEBUG_DRIVER("pitch %u x %d y %d bpp %d\n",
+               pitch, x, y, fb->bits_per_pixel);
+
+       addr_odd = addr_even = obj->dev_addr + offset;
+
+       if (interlaced) {
+               addr_even += pitch;
+               pitch *= 2;
+       }
+
+       /* write offset, base, and pitch */
+       armada_reg_queue_set(regs, i, addr_odd, LCD_CFG_GRA_START_ADDR0);
+       armada_reg_queue_set(regs, i, addr_even, LCD_CFG_GRA_START_ADDR1);
+       armada_reg_queue_mod(regs, i, pitch, 0xffff, LCD_CFG_GRA_PITCH);
+
+       return i;
+}
+
+static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
+       struct armada_frame_work *work)
+{
+       struct drm_device *dev = dcrtc->crtc.dev;
+       unsigned long flags;
+       int ret;
+
+       ret = drm_vblank_get(dev, dcrtc->num);
+       if (ret) {
+               DRM_ERROR("failed to acquire vblank counter\n");
+               return ret;
+       }
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (!dcrtc->frame_work)
+               dcrtc->frame_work = work;
+       else
+               ret = -EBUSY;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       if (ret)
+               drm_vblank_put(dev, dcrtc->num);
+
+       return ret;
+}
+
+static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc)
+{
+       struct drm_device *dev = dcrtc->crtc.dev;
+       struct armada_frame_work *work = dcrtc->frame_work;
+
+       dcrtc->frame_work = NULL;
+
+       armada_drm_crtc_update_regs(dcrtc, work->regs);
+
+       if (work->event)
+               drm_send_vblank_event(dev, dcrtc->num, work->event);
+
+       drm_vblank_put(dev, dcrtc->num);
+
+       /* Finally, queue the process-half of the cleanup. */
+       __armada_drm_queue_unref_work(dcrtc->crtc.dev, work->old_fb);
+       kfree(work);
+}
+
+static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
+       struct drm_framebuffer *fb, bool force)
+{
+       struct armada_frame_work *work;
+
+       if (!fb)
+               return;
+
+       if (force) {
+               /* Display is disabled, so just drop the old fb */
+               drm_framebuffer_unreference(fb);
+               return;
+       }
+
+       work = kmalloc(sizeof(*work), GFP_KERNEL);
+       if (work) {
+               int i = 0;
+               work->event = NULL;
+               work->old_fb = fb;
+               armada_reg_queue_end(work->regs, i);
+
+               if (armada_drm_crtc_queue_frame_work(dcrtc, work) == 0)
+                       return;
+
+               kfree(work);
+       }
+
+       /*
+        * Oops - just drop the reference immediately and hope for
+        * the best.  The worst that will happen is the buffer gets
+        * reused before it has finished being displayed.
+        */
+       drm_framebuffer_unreference(fb);
+}
+
+static void armada_drm_vblank_off(struct armada_crtc *dcrtc)
+{
+       struct drm_device *dev = dcrtc->crtc.dev;
+
+       /*
+        * Tell the DRM core that vblank IRQs aren't going to happen for
+        * a while.  This cleans up any pending vblank events for us.
+        */
+       drm_vblank_off(dev, dcrtc->num);
+
+       /* Handle any pending flip event. */
+       spin_lock_irq(&dev->event_lock);
+       if (dcrtc->frame_work)
+               armada_drm_crtc_complete_frame_work(dcrtc);
+       spin_unlock_irq(&dev->event_lock);
+}
+
+void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b,
+       int idx)
+{
+}
+
+void armada_drm_crtc_gamma_get(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
+       int idx)
+{
+}
+
+/* The mode_config.mutex will be held for this call */
+static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms)
+{
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+
+       if (dcrtc->dpms != dpms) {
+               dcrtc->dpms = dpms;
+               armada_drm_crtc_update(dcrtc);
+               if (dpms_blanked(dpms))
+                       armada_drm_vblank_off(dcrtc);
+       }
+}
+
+/*
+ * Prepare for a mode set.  Turn off overlay to ensure that we don't end
+ * up with the overlay size being bigger than the active screen size.
+ * We rely upon X refreshing this state after the mode set has completed.
+ *
+ * The mode_config.mutex will be held for this call
+ */
+static void armada_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       struct drm_plane *plane;
+
+       /*
+        * If we have an overlay plane associated with this CRTC, disable
+        * it before the modeset to avoid its coordinates being outside
+        * the new mode parameters.  DRM doesn't provide help with this.
+        */
+       plane = dcrtc->plane;
+       if (plane) {
+               struct drm_framebuffer *fb = plane->fb;
+
+               plane->funcs->disable_plane(plane);
+               plane->fb = NULL;
+               plane->crtc = NULL;
+               drm_framebuffer_unreference(fb);
+       }
+}
+
+/* The mode_config.mutex will be held for this call */
+static void armada_drm_crtc_commit(struct drm_crtc *crtc)
+{
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+
+       if (dcrtc->dpms != DRM_MODE_DPMS_ON) {
+               dcrtc->dpms = DRM_MODE_DPMS_ON;
+               armada_drm_crtc_update(dcrtc);
+       }
+}
+
+/* The mode_config.mutex will be held for this call */
+static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+       const struct drm_display_mode *mode, struct drm_display_mode *adj)
+{
+       struct armada_private *priv = crtc->dev->dev_private;
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       int ret;
+
+       /* We can't do interlaced modes if we don't have the SPU_ADV_REG */
+       if (!priv->variant->has_spu_adv_reg &&
+           adj->flags & DRM_MODE_FLAG_INTERLACE)
+               return false;
+
+       /* Check whether the display mode is possible */
+       ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL);
+       if (ret)
+               return false;
+
+       return true;
+}
+
+void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
+{
+       struct armada_vbl_event *e, *n;
+       void __iomem *base = dcrtc->base;
+
+       if (stat & DMA_FF_UNDERFLOW)
+               DRM_ERROR("video underflow on crtc %u\n", dcrtc->num);
+       if (stat & GRA_FF_UNDERFLOW)
+               DRM_ERROR("graphics underflow on crtc %u\n", dcrtc->num);
+
+       if (stat & VSYNC_IRQ)
+               drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num);
+
+       spin_lock(&dcrtc->irq_lock);
+
+       list_for_each_entry_safe(e, n, &dcrtc->vbl_list, node) {
+               list_del_init(&e->node);
+               drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+               e->fn(dcrtc, e->data);
+       }
+
+       if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
+               int i = stat & GRA_FRAME_IRQ0 ? 0 : 1;
+               uint32_t val;
+
+               writel_relaxed(dcrtc->v[i].spu_v_porch, base + LCD_SPU_V_PORCH);
+               writel_relaxed(dcrtc->v[i].spu_v_h_total,
+                              base + LCD_SPUT_V_H_TOTAL);
+
+               val = readl_relaxed(base + LCD_SPU_ADV_REG);
+               val &= ~(ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN);
+               val |= dcrtc->v[i].spu_adv_reg;
+               writel_relaxed(val, base + LCD_SPU_ADV_REG);
+       }
+
+       if (stat & DUMB_FRAMEDONE && dcrtc->cursor_update) {
+               writel_relaxed(dcrtc->cursor_hw_pos,
+                              base + LCD_SPU_HWC_OVSA_HPXL_VLN);
+               writel_relaxed(dcrtc->cursor_hw_sz,
+                              base + LCD_SPU_HWC_HPXL_VLN);
+               armada_updatel(CFG_HWC_ENA,
+                              CFG_HWC_ENA | CFG_HWC_1BITMOD | CFG_HWC_1BITENA,
+                              base + LCD_SPU_DMA_CTRL0);
+               dcrtc->cursor_update = false;
+               armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+       }
+
+       spin_unlock(&dcrtc->irq_lock);
+
+       if (stat & GRA_FRAME_IRQ) {
+               struct drm_device *dev = dcrtc->crtc.dev;
+
+               spin_lock(&dev->event_lock);
+               if (dcrtc->frame_work)
+                       armada_drm_crtc_complete_frame_work(dcrtc);
+               spin_unlock(&dev->event_lock);
+
+               wake_up(&dcrtc->frame_wait);
+       }
+}
+
+/* These are locked by dev->vbl_lock */
+void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
+{
+       if (dcrtc->irq_ena & mask) {
+               dcrtc->irq_ena &= ~mask;
+               writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+       }
+}
+
+void armada_drm_crtc_enable_irq(struct armada_crtc *dcrtc, u32 mask)
+{
+       if ((dcrtc->irq_ena & mask) != mask) {
+               dcrtc->irq_ena |= mask;
+               writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+               if (readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR) & mask)
+                       writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+       }
+}
+
+static uint32_t armada_drm_crtc_calculate_csc(struct armada_crtc *dcrtc)
+{
+       struct drm_display_mode *adj = &dcrtc->crtc.mode;
+       uint32_t val = 0;
+
+       if (dcrtc->csc_yuv_mode == CSC_YUV_CCIR709)
+               val |= CFG_CSC_YUV_CCIR709;
+       if (dcrtc->csc_rgb_mode == CSC_RGB_STUDIO)
+               val |= CFG_CSC_RGB_STUDIO;
+
+       /*
+        * In auto mode, set the colorimetry, based upon the HDMI spec.
+        * 1280x720p, 1920x1080p and 1920x1080i use ITU709, others use
+        * ITU601.  It may be more appropriate to set this depending on
+        * the source - but what if the graphic frame is YUV and the
+        * video frame is RGB?
+        */
+       if ((adj->hdisplay == 1280 && adj->vdisplay == 720 &&
+            !(adj->flags & DRM_MODE_FLAG_INTERLACE)) ||
+           (adj->hdisplay == 1920 && adj->vdisplay == 1080)) {
+               if (dcrtc->csc_yuv_mode == CSC_AUTO)
+                       val |= CFG_CSC_YUV_CCIR709;
+       }
+
+       /*
+        * We assume we're connected to a TV-like device, so the YUV->RGB
+        * conversion should produce a limited range.  We should set this
+        * depending on the connectors attached to this CRTC, and what
+        * kind of device they report being connected.
+        */
+       if (dcrtc->csc_rgb_mode == CSC_AUTO)
+               val |= CFG_CSC_RGB_STUDIO;
+
+       return val;
+}
+
+/* The mode_config.mutex will be held for this call */
+static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
+       struct drm_display_mode *mode, struct drm_display_mode *adj,
+       int x, int y, struct drm_framebuffer *old_fb)
+{
+       struct armada_private *priv = crtc->dev->dev_private;
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       struct armada_regs regs[17];
+       uint32_t lm, rm, tm, bm, val, sclk;
+       unsigned long flags;
+       unsigned i;
+       bool interlaced;
+
+       drm_framebuffer_reference(crtc->fb);
+
+       interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE);
+
+       i = armada_drm_crtc_calc_fb(dcrtc->crtc.fb, x, y, regs, interlaced);
+
+       rm = adj->crtc_hsync_start - adj->crtc_hdisplay;
+       lm = adj->crtc_htotal - adj->crtc_hsync_end;
+       bm = adj->crtc_vsync_start - adj->crtc_vdisplay;
+       tm = adj->crtc_vtotal - adj->crtc_vsync_end;
+
+       DRM_DEBUG_DRIVER("H: %d %d %d %d lm %d rm %d\n",
+               adj->crtc_hdisplay,
+               adj->crtc_hsync_start,
+               adj->crtc_hsync_end,
+               adj->crtc_htotal, lm, rm);
+       DRM_DEBUG_DRIVER("V: %d %d %d %d tm %d bm %d\n",
+               adj->crtc_vdisplay,
+               adj->crtc_vsync_start,
+               adj->crtc_vsync_end,
+               adj->crtc_vtotal, tm, bm);
+
+       /* Wait for pending flips to complete */
+       wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
+
+       drm_vblank_pre_modeset(crtc->dev, dcrtc->num);
+
+       crtc->mode = *adj;
+
+       val = dcrtc->dumb_ctrl & ~CFG_DUMB_ENA;
+       if (val != dcrtc->dumb_ctrl) {
+               dcrtc->dumb_ctrl = val;
+               writel_relaxed(val, dcrtc->base + LCD_SPU_DUMB_CTRL);
+       }
+
+       /* Now compute the divider for real */
+       priv->variant->crtc_compute_clock(dcrtc, adj, &sclk);
+
+       /* Ensure graphic fifo is enabled */
+       armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
+       armada_reg_queue_set(regs, i, sclk, LCD_CFG_SCLK_DIV);
+
+       if (interlaced ^ dcrtc->interlaced) {
+               if (adj->flags & DRM_MODE_FLAG_INTERLACE)
+                       drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
+               else
+                       drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+               dcrtc->interlaced = interlaced;
+       }
+
+       spin_lock_irqsave(&dcrtc->irq_lock, flags);
+
+       /* Even interlaced/progressive frame */
+       dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 |
+                                   adj->crtc_htotal;
+       dcrtc->v[1].spu_v_porch = tm << 16 | bm;
+       val = adj->crtc_hsync_start;
+       dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
+               priv->variant->spu_adv_reg;
+
+       if (interlaced) {
+               /* Odd interlaced frame */
+               dcrtc->v[0].spu_v_h_total = dcrtc->v[1].spu_v_h_total +
+                                               (1 << 16);
+               dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1;
+               val = adj->crtc_hsync_start - adj->crtc_htotal / 2;
+               dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
+                       priv->variant->spu_adv_reg;
+       } else {
+               dcrtc->v[0] = dcrtc->v[1];
+       }
+
+       val = adj->crtc_vdisplay << 16 | adj->crtc_hdisplay;
+
+       armada_reg_queue_set(regs, i, val, LCD_SPU_V_H_ACTIVE);
+       armada_reg_queue_set(regs, i, val, LCD_SPU_GRA_HPXL_VLN);
+       armada_reg_queue_set(regs, i, val, LCD_SPU_GZM_HPXL_VLN);
+       armada_reg_queue_set(regs, i, (lm << 16) | rm, LCD_SPU_H_PORCH);
+       armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_porch, LCD_SPU_V_PORCH);
+       armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
+                          LCD_SPUT_V_H_TOTAL);
+
+       if (priv->variant->has_spu_adv_reg) {
+               armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
+                                    ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
+                                    ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
+       }
+
+       val = CFG_GRA_ENA | CFG_GRA_HSMOOTH;
+       val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt);
+       val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.fb)->mod);
+
+       if (drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt > CFG_420)
+               val |= CFG_PALETTE_ENA;
+
+       if (interlaced)
+               val |= CFG_GRA_FTOGGLE;
+
+       armada_reg_queue_mod(regs, i, val, CFG_GRAFORMAT |
+                            CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
+                                        CFG_SWAPYU | CFG_YUV2RGB) |
+                            CFG_PALETTE_ENA | CFG_GRA_FTOGGLE,
+                            LCD_SPU_DMA_CTRL0);
+
+       val = adj->flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0;
+       armada_reg_queue_mod(regs, i, val, CFG_VSYNC_INV, LCD_SPU_DMA_CTRL1);
+
+       val = dcrtc->spu_iopad_ctrl | armada_drm_crtc_calculate_csc(dcrtc);
+       armada_reg_queue_set(regs, i, val, LCD_SPU_IOPAD_CONTROL);
+       armada_reg_queue_end(regs, i);
+
+       armada_drm_crtc_update_regs(dcrtc, regs);
+       spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+
+       armada_drm_crtc_update(dcrtc);
+
+       drm_vblank_post_modeset(crtc->dev, dcrtc->num);
+       armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
+
+       return 0;
+}
+
+/* The mode_config.mutex will be held for this call */
+static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+       struct drm_framebuffer *old_fb)
+{
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       struct armada_regs regs[4];
+       unsigned i;
+
+       i = armada_drm_crtc_calc_fb(crtc->fb, crtc->x, crtc->y, regs,
+                                   dcrtc->interlaced);
+       armada_reg_queue_end(regs, i);
+
+       /* Wait for pending flips to complete */
+       wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
+
+       /* Take a reference to the new fb as we're using it */
+       drm_framebuffer_reference(crtc->fb);
+
+       /* Update the base in the CRTC */
+       armada_drm_crtc_update_regs(dcrtc, regs);
+
+       /* Drop our previously held reference */
+       armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
+
+       return 0;
+}
+
+static void armada_drm_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+/* The mode_config.mutex will be held for this call */
+static void armada_drm_crtc_disable(struct drm_crtc *crtc)
+{
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+
+       armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       armada_drm_crtc_finish_fb(dcrtc, crtc->fb, true);
+
+       /* Power down most RAMs and FIFOs */
+       writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
+                      CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
+                      CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
+}
+
+static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
+       .dpms           = armada_drm_crtc_dpms,
+       .prepare        = armada_drm_crtc_prepare,
+       .commit         = armada_drm_crtc_commit,
+       .mode_fixup     = armada_drm_crtc_mode_fixup,
+       .mode_set       = armada_drm_crtc_mode_set,
+       .mode_set_base  = armada_drm_crtc_mode_set_base,
+       .load_lut       = armada_drm_crtc_load_lut,
+       .disable        = armada_drm_crtc_disable,
+};
+
+static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix,
+       unsigned stride, unsigned width, unsigned height)
+{
+       uint32_t addr;
+       unsigned y;
+
+       addr = SRAM_HWC32_RAM1;
+       for (y = 0; y < height; y++) {
+               uint32_t *p = &pix[y * stride];
+               unsigned x;
+
+               for (x = 0; x < width; x++, p++) {
+                       uint32_t val = *p;
+
+                       val = (val & 0xff00ff00) |
+                             (val & 0x000000ff) << 16 |
+                             (val & 0x00ff0000) >> 16;
+
+                       writel_relaxed(val,
+                                      base + LCD_SPU_SRAM_WRDAT);
+                       writel_relaxed(addr | SRAM_WRITE,
+                                      base + LCD_SPU_SRAM_CTRL);
+                       addr += 1;
+                       if ((addr & 0x00ff) == 0)
+                               addr += 0xf00;
+                       if ((addr & 0x30ff) == 0)
+                               addr = SRAM_HWC32_RAM2;
+               }
+       }
+}
+
+static void armada_drm_crtc_cursor_tran(void __iomem *base)
+{
+       unsigned addr;
+
+       for (addr = 0; addr < 256; addr++) {
+               /* write the default value */
+               writel_relaxed(0x55555555, base + LCD_SPU_SRAM_WRDAT);
+               writel_relaxed(addr | SRAM_WRITE | SRAM_HWC32_TRAN,
+                              base + LCD_SPU_SRAM_CTRL);
+       }
+}
+
+static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload)
+{
+       uint32_t xoff, xscr, w = dcrtc->cursor_w, s;
+       uint32_t yoff, yscr, h = dcrtc->cursor_h;
+       uint32_t para1;
+
+       /*
+        * Calculate the visible width and height of the cursor,
+        * screen position, and the position in the cursor bitmap.
+        */
+       if (dcrtc->cursor_x < 0) {
+               xoff = -dcrtc->cursor_x;
+               xscr = 0;
+               w -= min(xoff, w);
+       } else if (dcrtc->cursor_x + w > dcrtc->crtc.mode.hdisplay) {
+               xoff = 0;
+               xscr = dcrtc->cursor_x;
+               w = max_t(int, dcrtc->crtc.mode.hdisplay - dcrtc->cursor_x, 0);
+       } else {
+               xoff = 0;
+               xscr = dcrtc->cursor_x;
+       }
+
+       if (dcrtc->cursor_y < 0) {
+               yoff = -dcrtc->cursor_y;
+               yscr = 0;
+               h -= min(yoff, h);
+       } else if (dcrtc->cursor_y + h > dcrtc->crtc.mode.vdisplay) {
+               yoff = 0;
+               yscr = dcrtc->cursor_y;
+               h = max_t(int, dcrtc->crtc.mode.vdisplay - dcrtc->cursor_y, 0);
+       } else {
+               yoff = 0;
+               yscr = dcrtc->cursor_y;
+       }
+
+       /* On interlaced modes, the vertical cursor size must be halved */
+       s = dcrtc->cursor_w;
+       if (dcrtc->interlaced) {
+               s *= 2;
+               yscr /= 2;
+               h /= 2;
+       }
+
+       if (!dcrtc->cursor_obj || !h || !w) {
+               spin_lock_irq(&dcrtc->irq_lock);
+               armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+               dcrtc->cursor_update = false;
+               armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+               spin_unlock_irq(&dcrtc->irq_lock);
+               return 0;
+       }
+
+       para1 = readl_relaxed(dcrtc->base + LCD_SPU_SRAM_PARA1);
+       armada_updatel(CFG_CSB_256x32, CFG_CSB_256x32 | CFG_PDWN256x32,
+                      dcrtc->base + LCD_SPU_SRAM_PARA1);
+
+       /*
+        * Initialize the transparency if the SRAM was powered down.
+        * We must also reload the cursor data as well.
+        */
+       if (!(para1 & CFG_CSB_256x32)) {
+               armada_drm_crtc_cursor_tran(dcrtc->base);
+               reload = true;
+       }
+
+       if (dcrtc->cursor_hw_sz != (h << 16 | w)) {
+               spin_lock_irq(&dcrtc->irq_lock);
+               armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+               dcrtc->cursor_update = false;
+               armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+               spin_unlock_irq(&dcrtc->irq_lock);
+               reload = true;
+       }
+       if (reload) {
+               struct armada_gem_object *obj = dcrtc->cursor_obj;
+               uint32_t *pix;
+               /* Set the top-left corner of the cursor image */
+               pix = obj->addr;
+               pix += yoff * s + xoff;
+               armada_load_cursor_argb(dcrtc->base, pix, s, w, h);
+       }
+
+       /* Reload the cursor position, size and enable in the IRQ handler */
+       spin_lock_irq(&dcrtc->irq_lock);
+       dcrtc->cursor_hw_pos = yscr << 16 | xscr;
+       dcrtc->cursor_hw_sz = h << 16 | w;
+       dcrtc->cursor_update = true;
+       armada_drm_crtc_enable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+       spin_unlock_irq(&dcrtc->irq_lock);
+
+       return 0;
+}
+
+static void cursor_update(void *data)
+{
+       armada_drm_crtc_cursor_update(data, true);
+}
+
+static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc,
+       struct drm_file *file, uint32_t handle, uint32_t w, uint32_t h)
+{
+       struct drm_device *dev = crtc->dev;
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       struct armada_private *priv = crtc->dev->dev_private;
+       struct armada_gem_object *obj = NULL;
+       int ret;
+
+       /* If no cursor support, replicate drm's return value */
+       if (!priv->variant->has_spu_adv_reg)
+               return -ENXIO;
+
+       if (handle && w > 0 && h > 0) {
+               /* maximum size is 64x32 or 32x64 */
+               if (w > 64 || h > 64 || (w > 32 && h > 32))
+                       return -ENOMEM;
+
+               obj = armada_gem_object_lookup(dev, file, handle);
+               if (!obj)
+                       return -ENOENT;
+
+               /* Must be a kernel-mapped object */
+               if (!obj->addr) {
+                       drm_gem_object_unreference_unlocked(&obj->obj);
+                       return -EINVAL;
+               }
+
+               if (obj->obj.size < w * h * 4) {
+                       DRM_ERROR("buffer is too small\n");
+                       drm_gem_object_unreference_unlocked(&obj->obj);
+                       return -ENOMEM;
+               }
+       }
+
+       mutex_lock(&dev->struct_mutex);
+       if (dcrtc->cursor_obj) {
+               dcrtc->cursor_obj->update = NULL;
+               dcrtc->cursor_obj->update_data = NULL;
+               drm_gem_object_unreference(&dcrtc->cursor_obj->obj);
+       }
+       dcrtc->cursor_obj = obj;
+       dcrtc->cursor_w = w;
+       dcrtc->cursor_h = h;
+       ret = armada_drm_crtc_cursor_update(dcrtc, true);
+       if (obj) {
+               obj->update_data = dcrtc;
+               obj->update = cursor_update;
+       }
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
+
+static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+       struct drm_device *dev = crtc->dev;
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       struct armada_private *priv = crtc->dev->dev_private;
+       int ret;
+
+       /* If no cursor support, replicate drm's return value */
+       if (!priv->variant->has_spu_adv_reg)
+               return -EFAULT;
+
+       mutex_lock(&dev->struct_mutex);
+       dcrtc->cursor_x = x;
+       dcrtc->cursor_y = y;
+       ret = armada_drm_crtc_cursor_update(dcrtc, false);
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
+
+static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       struct armada_private *priv = crtc->dev->dev_private;
+
+       if (dcrtc->cursor_obj)
+               drm_gem_object_unreference(&dcrtc->cursor_obj->obj);
+
+       priv->dcrtc[dcrtc->num] = NULL;
+       drm_crtc_cleanup(&dcrtc->crtc);
+
+       if (!IS_ERR(dcrtc->clk))
+               clk_disable_unprepare(dcrtc->clk);
+
+       kfree(dcrtc);
+}
+
+/*
+ * The mode_config lock is held here, to prevent races between this
+ * and a mode_set.
+ */
+static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
+       struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags)
+{
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       struct armada_frame_work *work;
+       struct drm_device *dev = crtc->dev;
+       unsigned long flags;
+       unsigned i;
+       int ret;
+
+       /* We don't support changing the pixel format */
+       if (fb->pixel_format != crtc->fb->pixel_format)
+               return -EINVAL;
+
+       work = kmalloc(sizeof(*work), GFP_KERNEL);
+       if (!work)
+               return -ENOMEM;
+
+       work->event = event;
+       work->old_fb = dcrtc->crtc.fb;
+
+       i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs,
+                                   dcrtc->interlaced);
+       armada_reg_queue_end(work->regs, i);
+
+       /*
+        * Hold the old framebuffer for the work - DRM appears to drop our
+        * reference to the old framebuffer in drm_mode_page_flip_ioctl().
+        */
+       drm_framebuffer_reference(work->old_fb);
+
+       ret = armada_drm_crtc_queue_frame_work(dcrtc, work);
+       if (ret) {
+               /*
+                * Undo our reference above; DRM does not drop the reference
+                * to this object on error, so that's okay.
+                */
+               drm_framebuffer_unreference(work->old_fb);
+               kfree(work);
+               return ret;
+       }
+
+       /*
+        * Don't take a reference on the new framebuffer;
+        * drm_mode_page_flip_ioctl() has already grabbed a reference and
+        * will _not_ drop that reference on successful return from this
+        * function.  Simply mark this new framebuffer as the current one.
+        */
+       dcrtc->crtc.fb = fb;
+
+       /*
+        * Finally, if the display is blanked, we won't receive an
+        * interrupt, so complete it now.
+        */
+       if (dpms_blanked(dcrtc->dpms)) {
+               spin_lock_irqsave(&dev->event_lock, flags);
+               if (dcrtc->frame_work)
+                       armada_drm_crtc_complete_frame_work(dcrtc);
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
+
+       return 0;
+}
+
+static int
+armada_drm_crtc_set_property(struct drm_crtc *crtc,
+       struct drm_property *property, uint64_t val)
+{
+       struct armada_private *priv = crtc->dev->dev_private;
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       bool update_csc = false;
+
+       if (property == priv->csc_yuv_prop) {
+               dcrtc->csc_yuv_mode = val;
+               update_csc = true;
+       } else if (property == priv->csc_rgb_prop) {
+               dcrtc->csc_rgb_mode = val;
+               update_csc = true;
+       }
+
+       if (update_csc) {
+               uint32_t val;
+
+               val = dcrtc->spu_iopad_ctrl |
+                     armada_drm_crtc_calculate_csc(dcrtc);
+               writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
+       }
+
+       return 0;
+}
+
+static struct drm_crtc_funcs armada_crtc_funcs = {
+       .cursor_set     = armada_drm_crtc_cursor_set,
+       .cursor_move    = armada_drm_crtc_cursor_move,
+       .destroy        = armada_drm_crtc_destroy,
+       .set_config     = drm_crtc_helper_set_config,
+       .page_flip      = armada_drm_crtc_page_flip,
+       .set_property   = armada_drm_crtc_set_property,
+};
+
+static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = {
+       { CSC_AUTO,        "Auto" },
+       { CSC_YUV_CCIR601, "CCIR601" },
+       { CSC_YUV_CCIR709, "CCIR709" },
+};
+
+static struct drm_prop_enum_list armada_drm_csc_rgb_enum_list[] = {
+       { CSC_AUTO,         "Auto" },
+       { CSC_RGB_COMPUTER, "Computer system" },
+       { CSC_RGB_STUDIO,   "Studio" },
+};
+
+static int armada_drm_crtc_create_properties(struct drm_device *dev)
+{
+       struct armada_private *priv = dev->dev_private;
+
+       if (priv->csc_yuv_prop)
+               return 0;
+
+       priv->csc_yuv_prop = drm_property_create_enum(dev, 0,
+                               "CSC_YUV", armada_drm_csc_yuv_enum_list,
+                               ARRAY_SIZE(armada_drm_csc_yuv_enum_list));
+       priv->csc_rgb_prop = drm_property_create_enum(dev, 0,
+                               "CSC_RGB", armada_drm_csc_rgb_enum_list,
+                               ARRAY_SIZE(armada_drm_csc_rgb_enum_list));
+
+       if (!priv->csc_yuv_prop || !priv->csc_rgb_prop)
+               return -ENOMEM;
+
+       return 0;
+}
+
+int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
+       struct resource *res)
+{
+       struct armada_private *priv = dev->dev_private;
+       struct armada_crtc *dcrtc;
+       void __iomem *base;
+       int ret;
+
+       ret = armada_drm_crtc_create_properties(dev);
+       if (ret)
+               return ret;
+
+       base = devm_request_and_ioremap(dev->dev, res);
+       if (!base) {
+               DRM_ERROR("failed to ioremap register\n");
+               return -ENOMEM;
+       }
+
+       dcrtc = kzalloc(sizeof(*dcrtc), GFP_KERNEL);
+       if (!dcrtc) {
+               DRM_ERROR("failed to allocate Armada crtc\n");
+               return -ENOMEM;
+       }
+
+       dcrtc->base = base;
+       dcrtc->num = num;
+       dcrtc->clk = ERR_PTR(-EINVAL);
+       dcrtc->csc_yuv_mode = CSC_AUTO;
+       dcrtc->csc_rgb_mode = CSC_AUTO;
+       dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
+       dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
+       spin_lock_init(&dcrtc->irq_lock);
+       dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR;
+       INIT_LIST_HEAD(&dcrtc->vbl_list);
+       init_waitqueue_head(&dcrtc->frame_wait);
+
+       /* Initialize some registers which we don't otherwise set */
+       writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV);
+       writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_BLANKCOLOR);
+       writel_relaxed(dcrtc->spu_iopad_ctrl,
+                      dcrtc->base + LCD_SPU_IOPAD_CONTROL);
+       writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_SRAM_PARA0);
+       writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
+                      CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
+                      CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
+       writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1);
+       writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN);
+
+       if (priv->variant->crtc_init) {
+               ret = priv->variant->crtc_init(dcrtc);
+               if (ret) {
+                       kfree(dcrtc);
+                       return ret;
+               }
+       }
+
+       /* Ensure AXI pipeline is enabled */
+       armada_updatel(CFG_ARBFAST_ENA, 0, dcrtc->base + LCD_SPU_DMA_CTRL0);
+
+       priv->dcrtc[dcrtc->num] = dcrtc;
+
+       drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs);
+       drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
+
+       drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
+                                  dcrtc->csc_yuv_mode);
+       drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop,
+                                  dcrtc->csc_rgb_mode);
+
+       return armada_overlay_plane_create(dev, 1 << dcrtc->num);
+}
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
new file mode 100644 (file)
index 0000000..9c10a07
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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 ARMADA_CRTC_H
+#define ARMADA_CRTC_H
+
+struct armada_gem_object;
+
+struct armada_regs {
+       uint32_t offset;
+       uint32_t mask;
+       uint32_t val;
+};
+
+#define armada_reg_queue_mod(_r, _i, _v, _m, _o)       \
+       do {                                    \
+               struct armada_regs *__reg = _r; \
+               __reg[_i].offset = _o;          \
+               __reg[_i].mask = ~(_m);         \
+               __reg[_i].val = _v;             \
+               _i++;                           \
+       } while (0)
+
+#define armada_reg_queue_set(_r, _i, _v, _o)   \
+       armada_reg_queue_mod(_r, _i, _v, ~0, _o)
+
+#define armada_reg_queue_end(_r, _i)           \
+       armada_reg_queue_mod(_r, _i, 0, 0, ~0)
+
+struct armada_frame_work;
+
+struct armada_crtc {
+       struct drm_crtc         crtc;
+       unsigned                num;
+       void __iomem            *base;
+       struct clk              *clk;
+       struct {
+               uint32_t        spu_v_h_total;
+               uint32_t        spu_v_porch;
+               uint32_t        spu_adv_reg;
+       } v[2];
+       bool                    interlaced;
+       bool                    cursor_update;
+       uint8_t                 csc_yuv_mode;
+       uint8_t                 csc_rgb_mode;
+
+       struct drm_plane        *plane;
+
+       struct armada_gem_object        *cursor_obj;
+       int                     cursor_x;
+       int                     cursor_y;
+       uint32_t                cursor_hw_pos;
+       uint32_t                cursor_hw_sz;
+       uint32_t                cursor_w;
+       uint32_t                cursor_h;
+
+       int                     dpms;
+       uint32_t                cfg_dumb_ctrl;
+       uint32_t                dumb_ctrl;
+       uint32_t                spu_iopad_ctrl;
+
+       wait_queue_head_t       frame_wait;
+       struct armada_frame_work *frame_work;
+
+       spinlock_t              irq_lock;
+       uint32_t                irq_ena;
+       struct list_head        vbl_list;
+};
+#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
+
+int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *);
+void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
+void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
+void armada_drm_crtc_irq(struct armada_crtc *, u32);
+void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
+void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
+void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_debugfs.c b/drivers/gpu/drm/armada/armada_debugfs.c
new file mode 100644 (file)
index 0000000..471e456
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Rewritten from the dovefb driver, and Armada510 manuals.
+ *
+ * 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/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <drm/drmP.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+
+static int armada_debugfs_gem_linear_show(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct armada_private *priv = dev->dev_private;
+       int ret;
+
+       mutex_lock(&dev->struct_mutex);
+       ret = drm_mm_dump_table(m, &priv->linear);
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
+
+static int armada_debugfs_reg_show(struct seq_file *m, void *data)
+{
+       struct drm_device *dev = m->private;
+       struct armada_private *priv = dev->dev_private;
+       int n, i;
+
+       if (priv) {
+               for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
+                       struct armada_crtc *dcrtc = priv->dcrtc[n];
+                       if (!dcrtc)
+                               continue;
+
+                       for (i = 0x84; i <= 0x1c4; i += 4) {
+                               uint32_t v = readl_relaxed(dcrtc->base + i);
+                               seq_printf(m, "%u: 0x%04x: 0x%08x\n", n, i, v);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int armada_debugfs_reg_r_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, armada_debugfs_reg_show, inode->i_private);
+}
+
+static const struct file_operations fops_reg_r = {
+       .owner = THIS_MODULE,
+       .open = armada_debugfs_reg_r_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static int armada_debugfs_write(struct file *file, const char __user *ptr,
+       size_t len, loff_t *off)
+{
+       struct drm_device *dev = file->private_data;
+       struct armada_private *priv = dev->dev_private;
+       struct armada_crtc *dcrtc = priv->dcrtc[0];
+       char buf[32], *p;
+       uint32_t reg, val;
+       int ret;
+
+       if (*off != 0)
+               return 0;
+
+       if (len > sizeof(buf) - 1)
+               len = sizeof(buf) - 1;
+
+       ret = strncpy_from_user(buf, ptr, len);
+       if (ret < 0)
+               return ret;
+       buf[len] = '\0';
+
+       reg = simple_strtoul(buf, &p, 16);
+       if (!isspace(*p))
+               return -EINVAL;
+       val = simple_strtoul(p + 1, NULL, 16);
+
+       if (reg >= 0x84 && reg <= 0x1c4)
+               writel(val, dcrtc->base + reg);
+
+       return len;
+}
+
+static const struct file_operations fops_reg_w = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .write = armada_debugfs_write,
+       .llseek = noop_llseek,
+};
+
+static struct drm_info_list armada_debugfs_list[] = {
+       { "gem_linear", armada_debugfs_gem_linear_show, 0 },
+};
+#define ARMADA_DEBUGFS_ENTRIES ARRAY_SIZE(armada_debugfs_list)
+
+static int drm_add_fake_info_node(struct drm_minor *minor, struct dentry *ent,
+       const void *key)
+{
+       struct drm_info_node *node;
+
+       node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
+       if (node == NULL) {
+               debugfs_remove(ent);
+               return -ENOMEM;
+       }
+
+       node->minor = minor;
+       node->dent = ent;
+       node->info_ent = (void *) key;
+
+       mutex_lock(&minor->debugfs_lock);
+       list_add(&node->list, &minor->debugfs_list);
+       mutex_unlock(&minor->debugfs_lock);
+
+       return 0;
+}
+
+static int armada_debugfs_create(struct dentry *root, struct drm_minor *minor,
+       const char *name, umode_t mode, const struct file_operations *fops)
+{
+       struct dentry *de;
+
+       de = debugfs_create_file(name, mode, root, minor->dev, fops);
+
+       return drm_add_fake_info_node(minor, de, fops);
+}
+
+int armada_drm_debugfs_init(struct drm_minor *minor)
+{
+       int ret;
+
+       ret = drm_debugfs_create_files(armada_debugfs_list,
+                                      ARMADA_DEBUGFS_ENTRIES,
+                                      minor->debugfs_root, minor);
+       if (ret)
+               return ret;
+
+       ret = armada_debugfs_create(minor->debugfs_root, minor,
+                                  "reg", S_IFREG | S_IRUSR, &fops_reg_r);
+       if (ret)
+               goto err_1;
+
+       ret = armada_debugfs_create(minor->debugfs_root, minor,
+                               "reg_wr", S_IFREG | S_IWUSR, &fops_reg_w);
+       if (ret)
+               goto err_2;
+       return ret;
+
+ err_2:
+       drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_r, 1, minor);
+ err_1:
+       drm_debugfs_remove_files(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
+                                minor);
+       return ret;
+}
+
+void armada_drm_debugfs_cleanup(struct drm_minor *minor)
+{
+       drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_w, 1, minor);
+       drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_r, 1, minor);
+       drm_debugfs_remove_files(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
+                                minor);
+}
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
new file mode 100644 (file)
index 0000000..eef09ec
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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 ARMADA_DRM_H
+#define ARMADA_DRM_H
+
+#include <linux/kfifo.h>
+#include <linux/io.h>
+#include <linux/workqueue.h>
+#include <drm/drmP.h>
+
+struct armada_crtc;
+struct armada_gem_object;
+struct clk;
+struct drm_fb_helper;
+
+static inline void
+armada_updatel(uint32_t val, uint32_t mask, void __iomem *ptr)
+{
+       uint32_t ov, v;
+
+       ov = v = readl_relaxed(ptr);
+       v = (v & ~mask) | val;
+       if (ov != v)
+               writel_relaxed(v, ptr);
+}
+
+static inline uint32_t armada_pitch(uint32_t width, uint32_t bpp)
+{
+       uint32_t pitch = bpp != 4 ? width * ((bpp + 7) / 8) : width / 2;
+
+       /* 88AP510 spec recommends pitch be a multiple of 128 */
+       return ALIGN(pitch, 128);
+}
+
+struct armada_vbl_event {
+       struct list_head        node;
+       void                    *data;
+       void                    (*fn)(struct armada_crtc *, void *);
+};
+void armada_drm_vbl_event_add(struct armada_crtc *,
+       struct armada_vbl_event *);
+void armada_drm_vbl_event_remove(struct armada_crtc *,
+       struct armada_vbl_event *);
+void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *,
+       struct armada_vbl_event *);
+#define armada_drm_vbl_event_init(_e, _f, _d) do {     \
+       struct armada_vbl_event *__e = _e;              \
+       INIT_LIST_HEAD(&__e->node);                     \
+       __e->data = _d;                                 \
+       __e->fn = _f;                                   \
+} while (0)
+
+
+struct armada_private;
+
+struct armada_variant {
+       bool    has_spu_adv_reg;
+       uint32_t spu_adv_reg;
+       int (*init)(struct armada_private *, struct device *);
+       int (*crtc_init)(struct armada_crtc *);
+       int (*crtc_compute_clock)(struct armada_crtc *,
+                                 const struct drm_display_mode *,
+                                 uint32_t *);
+};
+
+/* Variant ops */
+extern const struct armada_variant armada510_ops;
+
+struct armada_private {
+       const struct armada_variant *variant;
+       struct work_struct      fb_unref_work;
+       DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8);
+       struct drm_fb_helper    *fbdev;
+       struct armada_crtc      *dcrtc[2];
+       struct drm_mm           linear;
+       struct clk              *extclk[2];
+       struct drm_property     *csc_yuv_prop;
+       struct drm_property     *csc_rgb_prop;
+       struct drm_property     *colorkey_prop;
+       struct drm_property     *colorkey_min_prop;
+       struct drm_property     *colorkey_max_prop;
+       struct drm_property     *colorkey_val_prop;
+       struct drm_property     *colorkey_alpha_prop;
+       struct drm_property     *colorkey_mode_prop;
+       struct drm_property     *brightness_prop;
+       struct drm_property     *contrast_prop;
+       struct drm_property     *saturation_prop;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry           *de;
+#endif
+};
+
+void __armada_drm_queue_unref_work(struct drm_device *,
+       struct drm_framebuffer *);
+void armada_drm_queue_unref_work(struct drm_device *,
+       struct drm_framebuffer *);
+
+extern const struct drm_mode_config_funcs armada_drm_mode_config_funcs;
+
+int armada_fbdev_init(struct drm_device *);
+void armada_fbdev_fini(struct drm_device *);
+
+int armada_overlay_plane_create(struct drm_device *, unsigned long);
+
+int armada_drm_debugfs_init(struct drm_minor *);
+void armada_drm_debugfs_cleanup(struct drm_minor *);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
new file mode 100644 (file)
index 0000000..4f2b283
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * 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/clk.h>
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+#include <drm/armada_drm.h>
+#include "armada_ioctlP.h"
+
+#ifdef CONFIG_DRM_ARMADA_TDA1998X
+#include <drm/i2c/tda998x.h>
+#include "armada_slave.h"
+
+static struct tda998x_encoder_params params = {
+       /* With 0x24, there is no translation between vp_out and int_vp
+       FB      LCD out Pins    VIP     Int Vp
+       R:23:16 R:7:0   VPC7:0  7:0     7:0[R]
+       G:15:8  G:15:8  VPB7:0  23:16   23:16[G]
+       B:7:0   B:23:16 VPA7:0  15:8    15:8[B]
+       */
+       .swap_a = 2,
+       .swap_b = 3,
+       .swap_c = 4,
+       .swap_d = 5,
+       .swap_e = 0,
+       .swap_f = 1,
+       .audio_cfg = BIT(2),
+       .audio_frame[1] = 1,
+       .audio_format = AFMT_SPDIF,
+       .audio_sample_rate = 44100,
+};
+
+static const struct armada_drm_slave_config tda19988_config = {
+       .i2c_adapter_id = 0,
+       .crtcs = 1 << 0, /* Only LCD0 at the moment */
+       .polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT,
+       .interlace_allowed = true,
+       .info = {
+               .type = "tda998x",
+               .addr = 0x70,
+               .platform_data = &params,
+       },
+};
+#endif
+
+static void armada_drm_unref_work(struct work_struct *work)
+{
+       struct armada_private *priv =
+               container_of(work, struct armada_private, fb_unref_work);
+       struct drm_framebuffer *fb;
+
+       while (kfifo_get(&priv->fb_unref, &fb))
+               drm_framebuffer_unreference(fb);
+}
+
+/* Must be called with dev->event_lock held */
+void __armada_drm_queue_unref_work(struct drm_device *dev,
+       struct drm_framebuffer *fb)
+{
+       struct armada_private *priv = dev->dev_private;
+
+       /*
+        * Yes, we really must jump through these hoops just to store a
+        * _pointer_ to something into the kfifo.  This is utterly insane
+        * and idiotic, because it kfifo requires the _data_ pointed to by
+        * the pointer const, not the pointer itself.  Not only that, but
+        * you have to pass a pointer _to_ the pointer you want stored.
+        */
+       const struct drm_framebuffer *silly_api_alert = fb;
+       WARN_ON(!kfifo_put(&priv->fb_unref, &silly_api_alert));
+       schedule_work(&priv->fb_unref_work);
+}
+
+void armada_drm_queue_unref_work(struct drm_device *dev,
+       struct drm_framebuffer *fb)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       __armada_drm_queue_unref_work(dev, fb);
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int armada_drm_load(struct drm_device *dev, unsigned long flags)
+{
+       const struct platform_device_id *id;
+       struct armada_private *priv;
+       struct resource *res[ARRAY_SIZE(priv->dcrtc)];
+       struct resource *mem = NULL;
+       int ret, n, i;
+
+       memset(res, 0, sizeof(res));
+
+       for (n = i = 0; ; n++) {
+               struct resource *r = platform_get_resource(dev->platformdev,
+                                                          IORESOURCE_MEM, n);
+               if (!r)
+                       break;
+
+               /* Resources above 64K are graphics memory */
+               if (resource_size(r) > SZ_64K)
+                       mem = r;
+               else if (i < ARRAY_SIZE(priv->dcrtc))
+                       res[i++] = r;
+               else
+                       return -EINVAL;
+       }
+
+       if (!res[0] || !mem)
+               return -ENXIO;
+
+       if (!devm_request_mem_region(dev->dev, mem->start,
+                       resource_size(mem), "armada-drm"))
+               return -EBUSY;
+
+       priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               DRM_ERROR("failed to allocate private\n");
+               return -ENOMEM;
+       }
+
+       dev->dev_private = priv;
+
+       /* Get the implementation specific driver data. */
+       id = platform_get_device_id(dev->platformdev);
+       if (!id)
+               return -ENXIO;
+
+       priv->variant = (struct armada_variant *)id->driver_data;
+
+       ret = priv->variant->init(priv, dev->dev);
+       if (ret)
+               return ret;
+
+       INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
+       INIT_KFIFO(priv->fb_unref);
+
+       /* Mode setting support */
+       drm_mode_config_init(dev);
+       dev->mode_config.min_width = 320;
+       dev->mode_config.min_height = 200;
+
+       /*
+        * With vscale enabled, the maximum width is 1920 due to the
+        * 1920 by 3 lines RAM
+        */
+       dev->mode_config.max_width = 1920;
+       dev->mode_config.max_height = 2048;
+
+       dev->mode_config.preferred_depth = 24;
+       dev->mode_config.funcs = &armada_drm_mode_config_funcs;
+       drm_mm_init(&priv->linear, mem->start, resource_size(mem));
+
+       /* Create all LCD controllers */
+       for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
+               if (!res[n])
+                       break;
+
+               ret = armada_drm_crtc_create(dev, n, res[n]);
+               if (ret)
+                       goto err_kms;
+       }
+
+#ifdef CONFIG_DRM_ARMADA_TDA1998X
+       ret = armada_drm_connector_slave_create(dev, &tda19988_config);
+       if (ret)
+               goto err_kms;
+#endif
+
+       ret = drm_vblank_init(dev, n);
+       if (ret)
+               goto err_kms;
+
+       ret = drm_irq_install(dev);
+       if (ret)
+               goto err_kms;
+
+       dev->vblank_disable_allowed = 1;
+
+       ret = armada_fbdev_init(dev);
+       if (ret)
+               goto err_irq;
+
+       drm_kms_helper_poll_init(dev);
+
+       return 0;
+
+ err_irq:
+       drm_irq_uninstall(dev);
+ err_kms:
+       drm_mode_config_cleanup(dev);
+       drm_mm_takedown(&priv->linear);
+       flush_work(&priv->fb_unref_work);
+
+       return ret;
+}
+
+static int armada_drm_unload(struct drm_device *dev)
+{
+       struct armada_private *priv = dev->dev_private;
+
+       drm_kms_helper_poll_fini(dev);
+       armada_fbdev_fini(dev);
+       drm_irq_uninstall(dev);
+       drm_mode_config_cleanup(dev);
+       drm_mm_takedown(&priv->linear);
+       flush_work(&priv->fb_unref_work);
+       dev->dev_private = NULL;
+
+       return 0;
+}
+
+void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
+       struct armada_vbl_event *evt)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dcrtc->irq_lock, flags);
+       if (list_empty(&evt->node)) {
+               list_add_tail(&evt->node, &dcrtc->vbl_list);
+
+               drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
+       }
+       spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+}
+
+void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
+       struct armada_vbl_event *evt)
+{
+       if (!list_empty(&evt->node)) {
+               list_del_init(&evt->node);
+               drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+       }
+}
+
+void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *dcrtc,
+       struct armada_vbl_event *evt)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dcrtc->irq_lock, flags);
+       armada_drm_vbl_event_remove(dcrtc, evt);
+       spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+}
+
+/* These are called under the vbl_lock. */
+static int armada_drm_enable_vblank(struct drm_device *dev, int crtc)
+{
+       struct armada_private *priv = dev->dev_private;
+       armada_drm_crtc_enable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
+       return 0;
+}
+
+static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
+{
+       struct armada_private *priv = dev->dev_private;
+       armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
+}
+
+static irqreturn_t armada_drm_irq_handler(int irq, void *arg)
+{
+       struct drm_device *dev = arg;
+       struct armada_private *priv = dev->dev_private;
+       struct armada_crtc *dcrtc = priv->dcrtc[0];
+       uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
+       irqreturn_t handled = IRQ_NONE;
+
+       /*
+        * This is rediculous - rather than writing bits to clear, we
+        * have to set the actual status register value.  This is racy.
+        */
+       writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+
+       /* Mask out those interrupts we haven't enabled */
+       v = stat & dcrtc->irq_ena;
+
+       if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
+               armada_drm_crtc_irq(dcrtc, stat);
+               handled = IRQ_HANDLED;
+       }
+
+       return handled;
+}
+
+static int armada_drm_irq_postinstall(struct drm_device *dev)
+{
+       struct armada_private *priv = dev->dev_private;
+       struct armada_crtc *dcrtc = priv->dcrtc[0];
+
+       spin_lock_irq(&dev->vbl_lock);
+       writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+       writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+       spin_unlock_irq(&dev->vbl_lock);
+
+       return 0;
+}
+
+static void armada_drm_irq_uninstall(struct drm_device *dev)
+{
+       struct armada_private *priv = dev->dev_private;
+       struct armada_crtc *dcrtc = priv->dcrtc[0];
+
+       writel(0, dcrtc->base + LCD_SPU_IRQ_ENA);
+}
+
+static struct drm_ioctl_desc armada_ioctls[] = {
+       DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,
+               DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(ARMADA_GEM_MMAP, armada_gem_mmap_ioctl,
+               DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(ARMADA_GEM_PWRITE, armada_gem_pwrite_ioctl,
+               DRM_UNLOCKED),
+};
+
+static const struct file_operations armada_drm_fops = {
+       .owner                  = THIS_MODULE,
+       .llseek                 = no_llseek,
+       .read                   = drm_read,
+       .poll                   = drm_poll,
+       .unlocked_ioctl         = drm_ioctl,
+       .mmap                   = drm_gem_mmap,
+       .open                   = drm_open,
+       .release                = drm_release,
+};
+
+static struct drm_driver armada_drm_driver = {
+       .load                   = armada_drm_load,
+       .open                   = NULL,
+       .preclose               = NULL,
+       .postclose              = NULL,
+       .lastclose              = NULL,
+       .unload                 = armada_drm_unload,
+       .get_vblank_counter     = drm_vblank_count,
+       .enable_vblank          = armada_drm_enable_vblank,
+       .disable_vblank         = armada_drm_disable_vblank,
+       .irq_handler            = armada_drm_irq_handler,
+       .irq_postinstall        = armada_drm_irq_postinstall,
+       .irq_uninstall          = armada_drm_irq_uninstall,
+#ifdef CONFIG_DEBUG_FS
+       .debugfs_init           = armada_drm_debugfs_init,
+       .debugfs_cleanup        = armada_drm_debugfs_cleanup,
+#endif
+       .gem_free_object        = armada_gem_free_object,
+       .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
+       .gem_prime_export       = armada_gem_prime_export,
+       .gem_prime_import       = armada_gem_prime_import,
+       .dumb_create            = armada_gem_dumb_create,
+       .dumb_map_offset        = armada_gem_dumb_map_offset,
+       .dumb_destroy           = armada_gem_dumb_destroy,
+       .gem_vm_ops             = &armada_gem_vm_ops,
+       .major                  = 1,
+       .minor                  = 0,
+       .name                   = "armada-drm",
+       .desc                   = "Armada SoC DRM",
+       .date                   = "20120730",
+       .driver_features        = DRIVER_GEM | DRIVER_MODESET |
+                                 DRIVER_HAVE_IRQ | DRIVER_PRIME,
+       .ioctls                 = armada_ioctls,
+       .fops                   = &armada_drm_fops,
+};
+
+static int armada_drm_probe(struct platform_device *pdev)
+{
+       return drm_platform_init(&armada_drm_driver, pdev);
+}
+
+static int armada_drm_remove(struct platform_device *pdev)
+{
+       drm_platform_exit(&armada_drm_driver, pdev);
+       return 0;
+}
+
+static const struct platform_device_id armada_drm_platform_ids[] = {
+       {
+               .name           = "armada-drm",
+               .driver_data    = (unsigned long)&armada510_ops,
+       }, {
+               .name           = "armada-510-drm",
+               .driver_data    = (unsigned long)&armada510_ops,
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids);
+
+static struct platform_driver armada_drm_platform_driver = {
+       .probe  = armada_drm_probe,
+       .remove = armada_drm_remove,
+       .driver = {
+               .name   = "armada-drm",
+               .owner  = THIS_MODULE,
+       },
+       .id_table = armada_drm_platform_ids,
+};
+
+static int __init armada_drm_init(void)
+{
+       armada_drm_driver.num_ioctls = DRM_ARRAY_SIZE(armada_ioctls);
+       return platform_driver_register(&armada_drm_platform_driver);
+}
+module_init(armada_drm_init);
+
+static void __exit armada_drm_exit(void)
+{
+       platform_driver_unregister(&armada_drm_platform_driver);
+}
+module_exit(armada_drm_exit);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Armada DRM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:armada-drm");
diff --git a/drivers/gpu/drm/armada/armada_fb.c b/drivers/gpu/drm/armada/armada_fb.c
new file mode 100644 (file)
index 0000000..1c90969
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+
+static void armada_fb_destroy(struct drm_framebuffer *fb)
+{
+       struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb);
+
+       drm_framebuffer_cleanup(&dfb->fb);
+       drm_gem_object_unreference_unlocked(&dfb->obj->obj);
+       kfree(dfb);
+}
+
+static int armada_fb_create_handle(struct drm_framebuffer *fb,
+       struct drm_file *dfile, unsigned int *handle)
+{
+       struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb);
+       return drm_gem_handle_create(dfile, &dfb->obj->obj, handle);
+}
+
+static const struct drm_framebuffer_funcs armada_fb_funcs = {
+       .destroy        = armada_fb_destroy,
+       .create_handle  = armada_fb_create_handle,
+};
+
+struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev,
+       struct drm_mode_fb_cmd2 *mode, struct armada_gem_object *obj)
+{
+       struct armada_framebuffer *dfb;
+       uint8_t format, config;
+       int ret;
+
+       switch (mode->pixel_format) {
+#define FMT(drm, fmt, mod)             \
+       case DRM_FORMAT_##drm:          \
+               format = CFG_##fmt;     \
+               config = mod;           \
+               break
+       FMT(RGB565,     565,            CFG_SWAPRB);
+       FMT(BGR565,     565,            0);
+       FMT(ARGB1555,   1555,           CFG_SWAPRB);
+       FMT(ABGR1555,   1555,           0);
+       FMT(RGB888,     888PACK,        CFG_SWAPRB);
+       FMT(BGR888,     888PACK,        0);
+       FMT(XRGB8888,   X888,           CFG_SWAPRB);
+       FMT(XBGR8888,   X888,           0);
+       FMT(ARGB8888,   8888,           CFG_SWAPRB);
+       FMT(ABGR8888,   8888,           0);
+       FMT(YUYV,       422PACK,        CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV);
+       FMT(UYVY,       422PACK,        CFG_YUV2RGB);
+       FMT(VYUY,       422PACK,        CFG_YUV2RGB | CFG_SWAPUV);
+       FMT(YVYU,       422PACK,        CFG_YUV2RGB | CFG_SWAPYU);
+       FMT(YUV422,     422,            CFG_YUV2RGB);
+       FMT(YVU422,     422,            CFG_YUV2RGB | CFG_SWAPUV);
+       FMT(YUV420,     420,            CFG_YUV2RGB);
+       FMT(YVU420,     420,            CFG_YUV2RGB | CFG_SWAPUV);
+       FMT(C8,         PSEUDO8,        0);
+#undef FMT
+       default:
+               return ERR_PTR(-EINVAL);
+       }
+
+       dfb = kzalloc(sizeof(*dfb), GFP_KERNEL);
+       if (!dfb) {
+               DRM_ERROR("failed to allocate Armada fb object\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dfb->fmt = format;
+       dfb->mod = config;
+       dfb->obj = obj;
+
+       drm_helper_mode_fill_fb_struct(&dfb->fb, mode);
+
+       ret = drm_framebuffer_init(dev, &dfb->fb, &armada_fb_funcs);
+       if (ret) {
+               kfree(dfb);
+               return ERR_PTR(ret);
+       }
+
+       /*
+        * Take a reference on our object as we're successful - the
+        * caller already holds a reference, which keeps us safe for
+        * the above call, but the caller will drop their reference
+        * to it.  Hence we need to take our own reference.
+        */
+       drm_gem_object_reference(&obj->obj);
+
+       return dfb;
+}
+
+static struct drm_framebuffer *armada_fb_create(struct drm_device *dev,
+       struct drm_file *dfile, struct drm_mode_fb_cmd2 *mode)
+{
+       struct armada_gem_object *obj;
+       struct armada_framebuffer *dfb;
+       int ret;
+
+       DRM_DEBUG_DRIVER("w%u h%u pf%08x f%u p%u,%u,%u\n",
+               mode->width, mode->height, mode->pixel_format,
+               mode->flags, mode->pitches[0], mode->pitches[1],
+               mode->pitches[2]);
+
+       /* We can only handle a single plane at the moment */
+       if (drm_format_num_planes(mode->pixel_format) > 1 &&
+           (mode->handles[0] != mode->handles[1] ||
+            mode->handles[0] != mode->handles[2])) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       obj = armada_gem_object_lookup(dev, dfile, mode->handles[0]);
+       if (!obj) {
+               ret = -ENOENT;
+               goto err;
+       }
+
+       if (obj->obj.import_attach && !obj->sgt) {
+               ret = armada_gem_map_import(obj);
+               if (ret)
+                       goto err_unref;
+       }
+
+       /* Framebuffer objects must have a valid device address for scanout */
+       if (obj->dev_addr == DMA_ERROR_CODE) {
+               ret = -EINVAL;
+               goto err_unref;
+       }
+
+       dfb = armada_framebuffer_create(dev, mode, obj);
+       if (IS_ERR(dfb)) {
+               ret = PTR_ERR(dfb);
+               goto err;
+       }
+
+       drm_gem_object_unreference_unlocked(&obj->obj);
+
+       return &dfb->fb;
+
+ err_unref:
+       drm_gem_object_unreference_unlocked(&obj->obj);
+ err:
+       DRM_ERROR("failed to initialize framebuffer: %d\n", ret);
+       return ERR_PTR(ret);
+}
+
+static void armada_output_poll_changed(struct drm_device *dev)
+{
+       struct armada_private *priv = dev->dev_private;
+       struct drm_fb_helper *fbh = priv->fbdev;
+
+       if (fbh)
+               drm_fb_helper_hotplug_event(fbh);
+}
+
+const struct drm_mode_config_funcs armada_drm_mode_config_funcs = {
+       .fb_create              = armada_fb_create,
+       .output_poll_changed    = armada_output_poll_changed,
+};
diff --git a/drivers/gpu/drm/armada/armada_fb.h b/drivers/gpu/drm/armada/armada_fb.h
new file mode 100644 (file)
index 0000000..ce3f12e
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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 ARMADA_FB_H
+#define ARMADA_FB_H
+
+struct armada_framebuffer {
+       struct drm_framebuffer  fb;
+       struct armada_gem_object *obj;
+       uint8_t                 fmt;
+       uint8_t                 mod;
+};
+#define drm_fb_to_armada_fb(dfb) \
+       container_of(dfb, struct armada_framebuffer, fb)
+#define drm_fb_obj(fb) drm_fb_to_armada_fb(fb)->obj
+
+struct armada_framebuffer *armada_framebuffer_create(struct drm_device *,
+       struct drm_mode_fb_cmd2 *, struct armada_gem_object *);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
new file mode 100644 (file)
index 0000000..dd5ea77
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Written from the i915 driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+
+static /*const*/ struct fb_ops armada_fb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_check_var   = drm_fb_helper_check_var,
+       .fb_set_par     = drm_fb_helper_set_par,
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+       .fb_pan_display = drm_fb_helper_pan_display,
+       .fb_blank       = drm_fb_helper_blank,
+       .fb_setcmap     = drm_fb_helper_setcmap,
+       .fb_debug_enter = drm_fb_helper_debug_enter,
+       .fb_debug_leave = drm_fb_helper_debug_leave,
+};
+
+static int armada_fb_create(struct drm_fb_helper *fbh,
+       struct drm_fb_helper_surface_size *sizes)
+{
+       struct drm_device *dev = fbh->dev;
+       struct drm_mode_fb_cmd2 mode;
+       struct armada_framebuffer *dfb;
+       struct armada_gem_object *obj;
+       struct fb_info *info;
+       int size, ret;
+       void *ptr;
+
+       memset(&mode, 0, sizeof(mode));
+       mode.width = sizes->surface_width;
+       mode.height = sizes->surface_height;
+       mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp);
+       mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+                                       sizes->surface_depth);
+
+       size = mode.pitches[0] * mode.height;
+       obj = armada_gem_alloc_private_object(dev, size);
+       if (!obj) {
+               DRM_ERROR("failed to allocate fb memory\n");
+               return -ENOMEM;
+       }
+
+       ret = armada_gem_linear_back(dev, obj);
+       if (ret) {
+               drm_gem_object_unreference_unlocked(&obj->obj);
+               return ret;
+       }
+
+       ptr = armada_gem_map_object(dev, obj);
+       if (!ptr) {
+               drm_gem_object_unreference_unlocked(&obj->obj);
+               return -ENOMEM;
+       }
+
+       dfb = armada_framebuffer_create(dev, &mode, obj);
+
+       /*
+        * A reference is now held by the framebuffer object if
+        * successful, otherwise this drops the ref for the error path.
+        */
+       drm_gem_object_unreference_unlocked(&obj->obj);
+
+       if (IS_ERR(dfb))
+               return PTR_ERR(dfb);
+
+       info = framebuffer_alloc(0, dev->dev);
+       if (!info) {
+               ret = -ENOMEM;
+               goto err_fballoc;
+       }
+
+       ret = fb_alloc_cmap(&info->cmap, 256, 0);
+       if (ret) {
+               ret = -ENOMEM;
+               goto err_fbcmap;
+       }
+
+       strlcpy(info->fix.id, "armada-drmfb", sizeof(info->fix.id));
+       info->par = fbh;
+       info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+       info->fbops = &armada_fb_ops;
+       info->fix.smem_start = obj->phys_addr;
+       info->fix.smem_len = obj->obj.size;
+       info->screen_size = obj->obj.size;
+       info->screen_base = ptr;
+       fbh->fb = &dfb->fb;
+       fbh->fbdev = info;
+       drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], dfb->fb.depth);
+       drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height);
+
+       DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08x\n",
+               dfb->fb.width, dfb->fb.height,
+               dfb->fb.bits_per_pixel, obj->phys_addr);
+
+       return 0;
+
+ err_fbcmap:
+       framebuffer_release(info);
+ err_fballoc:
+       dfb->fb.funcs->destroy(&dfb->fb);
+       return ret;
+}
+
+static int armada_fb_probe(struct drm_fb_helper *fbh,
+       struct drm_fb_helper_surface_size *sizes)
+{
+       int ret = 0;
+
+       if (!fbh->fb) {
+               ret = armada_fb_create(fbh, sizes);
+               if (ret == 0)
+                       ret = 1;
+       }
+       return ret;
+}
+
+static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
+       .gamma_set      = armada_drm_crtc_gamma_set,
+       .gamma_get      = armada_drm_crtc_gamma_get,
+       .fb_probe       = armada_fb_probe,
+};
+
+int armada_fbdev_init(struct drm_device *dev)
+{
+       struct armada_private *priv = dev->dev_private;
+       struct drm_fb_helper *fbh;
+       int ret;
+
+       fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL);
+       if (!fbh)
+               return -ENOMEM;
+
+       priv->fbdev = fbh;
+
+       fbh->funcs = &armada_fb_helper_funcs;
+
+       ret = drm_fb_helper_init(dev, fbh, 1, 1);
+       if (ret) {
+               DRM_ERROR("failed to initialize drm fb helper\n");
+               goto err_fb_helper;
+       }
+
+       ret = drm_fb_helper_single_add_all_connectors(fbh);
+       if (ret) {
+               DRM_ERROR("failed to add fb connectors\n");
+               goto err_fb_setup;
+       }
+
+       ret = drm_fb_helper_initial_config(fbh, 32);
+       if (ret) {
+               DRM_ERROR("failed to set initial config\n");
+               goto err_fb_setup;
+       }
+
+       return 0;
+ err_fb_setup:
+       drm_fb_helper_fini(fbh);
+ err_fb_helper:
+       priv->fbdev = NULL;
+       return ret;
+}
+
+void armada_fbdev_fini(struct drm_device *dev)
+{
+       struct armada_private *priv = dev->dev_private;
+       struct drm_fb_helper *fbh = priv->fbdev;
+
+       if (fbh) {
+               struct fb_info *info = fbh->fbdev;
+
+               if (info) {
+                       unregister_framebuffer(info);
+                       if (info->cmap.len)
+                               fb_dealloc_cmap(&info->cmap);
+                       framebuffer_release(info);
+               }
+
+               if (fbh->fb)
+                       fbh->fb->funcs->destroy(fbh->fb);
+
+               drm_fb_helper_fini(fbh);
+
+               priv->fbdev = NULL;
+       }
+}
diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
new file mode 100644 (file)
index 0000000..9f2356b
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ * 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/dma-buf.h>
+#include <linux/dma-mapping.h>
+#include <linux/shmem_fs.h>
+#include <drm/drmP.h>
+#include "armada_drm.h"
+#include "armada_gem.h"
+#include <drm/armada_drm.h>
+#include "armada_ioctlP.h"
+
+static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+       struct armada_gem_object *obj = drm_to_armada_gem(vma->vm_private_data);
+       unsigned long addr = (unsigned long)vmf->virtual_address;
+       unsigned long pfn = obj->phys_addr >> PAGE_SHIFT;
+       int ret;
+
+       pfn += (addr - vma->vm_start) >> PAGE_SHIFT;
+       ret = vm_insert_pfn(vma, addr, pfn);
+
+       switch (ret) {
+       case 0:
+       case -EBUSY:
+               return VM_FAULT_NOPAGE;
+       case -ENOMEM:
+               return VM_FAULT_OOM;
+       default:
+               return VM_FAULT_SIGBUS;
+       }
+}
+
+const struct vm_operations_struct armada_gem_vm_ops = {
+       .fault  = armada_gem_vm_fault,
+       .open   = drm_gem_vm_open,
+       .close  = drm_gem_vm_close,
+};
+
+static size_t roundup_gem_size(size_t size)
+{
+       return roundup(size, PAGE_SIZE);
+}
+
+/* dev->struct_mutex is held here */
+void armada_gem_free_object(struct drm_gem_object *obj)
+{
+       struct armada_gem_object *dobj = drm_to_armada_gem(obj);
+
+       DRM_DEBUG_DRIVER("release obj %p\n", dobj);
+
+       drm_gem_free_mmap_offset(&dobj->obj);
+
+       if (dobj->page) {
+               /* page backed memory */
+               unsigned int order = get_order(dobj->obj.size);
+               __free_pages(dobj->page, order);
+       } else if (dobj->linear) {
+               /* linear backed memory */
+               drm_mm_remove_node(dobj->linear);
+               kfree(dobj->linear);
+               if (dobj->addr)
+                       iounmap(dobj->addr);
+       }
+
+       if (dobj->obj.import_attach) {
+               /* We only ever display imported data */
+               dma_buf_unmap_attachment(dobj->obj.import_attach, dobj->sgt,
+                                        DMA_TO_DEVICE);
+               drm_prime_gem_destroy(&dobj->obj, NULL);
+       }
+
+       drm_gem_object_release(&dobj->obj);
+
+       kfree(dobj);
+}
+
+int
+armada_gem_linear_back(struct drm_device *dev, struct armada_gem_object *obj)
+{
+       struct armada_private *priv = dev->dev_private;
+       size_t size = obj->obj.size;
+
+       if (obj->page || obj->linear)
+               return 0;
+
+       /*
+        * If it is a small allocation (typically cursor, which will
+        * be 32x64 or 64x32 ARGB pixels) try to get it from the system.
+        * Framebuffers will never be this small (our minimum size for
+        * framebuffers is larger than this anyway.)  Such objects are
+        * only accessed by the CPU so we don't need any special handing
+        * here.
+        */
+       if (size <= 8192) {
+               unsigned int order = get_order(size);
+               struct page *p = alloc_pages(GFP_KERNEL, order);
+
+               if (p) {
+                       obj->addr = page_address(p);
+                       obj->phys_addr = page_to_phys(p);
+                       obj->page = p;
+
+                       memset(obj->addr, 0, PAGE_ALIGN(size));
+               }
+       }
+
+       /*
+        * We could grab something from CMA if it's enabled, but that
+        * involves building in a problem:
+        *
+        * CMA's interface uses dma_alloc_coherent(), which provides us
+        * with an CPU virtual address and a device address.
+        *
+        * The CPU virtual address may be either an address in the kernel
+        * direct mapped region (for example, as it would be on x86) or
+        * it may be remapped into another part of kernel memory space
+        * (eg, as it would be on ARM.)  This means virt_to_phys() on the
+        * returned virtual address is invalid depending on the architecture
+        * implementation.
+        *
+        * The device address may also not be a physical address; it may
+        * be that there is some kind of remapping between the device and
+        * system RAM, which makes the use of the device address also
+        * unsafe to re-use as a physical address.
+        *
+        * This makes DRM usage of dma_alloc_coherent() in a generic way
+        * at best very questionable and unsafe.
+        */
+
+       /* Otherwise, grab it from our linear allocation */
+       if (!obj->page) {
+               struct drm_mm_node *node;
+               unsigned align = min_t(unsigned, size, SZ_2M);
+               void __iomem *ptr;
+               int ret;
+
+               node = kzalloc(sizeof(*node), GFP_KERNEL);
+               if (!node)
+                       return -ENOSPC;
+
+               mutex_lock(&dev->struct_mutex);
+               ret = drm_mm_insert_node(&priv->linear, node, size, align,
+                                        DRM_MM_SEARCH_DEFAULT);
+               mutex_unlock(&dev->struct_mutex);
+               if (ret) {
+                       kfree(node);
+                       return ret;
+               }
+
+               obj->linear = node;
+
+               /* Ensure that the memory we're returning is cleared. */
+               ptr = ioremap_wc(obj->linear->start, size);
+               if (!ptr) {
+                       mutex_lock(&dev->struct_mutex);
+                       drm_mm_remove_node(obj->linear);
+                       mutex_unlock(&dev->struct_mutex);
+                       kfree(obj->linear);
+                       obj->linear = NULL;
+                       return -ENOMEM;
+               }
+
+               memset_io(ptr, 0, size);
+               iounmap(ptr);
+
+               obj->phys_addr = obj->linear->start;
+               obj->dev_addr = obj->linear->start;
+       }
+
+       DRM_DEBUG_DRIVER("obj %p phys %#x dev %#x\n",
+                        obj, obj->phys_addr, obj->dev_addr);
+
+       return 0;
+}
+
+void *
+armada_gem_map_object(struct drm_device *dev, struct armada_gem_object *dobj)
+{
+       /* only linear objects need to be ioremap'd */
+       if (!dobj->addr && dobj->linear)
+               dobj->addr = ioremap_wc(dobj->phys_addr, dobj->obj.size);
+       return dobj->addr;
+}
+
+struct armada_gem_object *
+armada_gem_alloc_private_object(struct drm_device *dev, size_t size)
+{
+       struct armada_gem_object *obj;
+
+       size = roundup_gem_size(size);
+
+       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+       if (!obj)
+               return NULL;
+
+       drm_gem_private_object_init(dev, &obj->obj, size);
+       obj->dev_addr = DMA_ERROR_CODE;
+
+       DRM_DEBUG_DRIVER("alloc private obj %p size %zu\n", obj, size);
+
+       return obj;
+}
+
+struct armada_gem_object *armada_gem_alloc_object(struct drm_device *dev,
+       size_t size)
+{
+       struct armada_gem_object *obj;
+       struct address_space *mapping;
+
+       size = roundup_gem_size(size);
+
+       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+       if (!obj)
+               return NULL;
+
+       if (drm_gem_object_init(dev, &obj->obj, size)) {
+               kfree(obj);
+               return NULL;
+       }
+
+       obj->dev_addr = DMA_ERROR_CODE;
+
+       mapping = obj->obj.filp->f_path.dentry->d_inode->i_mapping;
+       mapping_set_gfp_mask(mapping, GFP_HIGHUSER | __GFP_RECLAIMABLE);
+
+       DRM_DEBUG_DRIVER("alloc obj %p size %zu\n", obj, size);
+
+       return obj;
+}
+
+/* Dumb alloc support */
+int armada_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+       struct drm_mode_create_dumb *args)
+{
+       struct armada_gem_object *dobj;
+       u32 handle;
+       size_t size;
+       int ret;
+
+       args->pitch = armada_pitch(args->width, args->bpp);
+       args->size = size = args->pitch * args->height;
+
+       dobj = armada_gem_alloc_private_object(dev, size);
+       if (dobj == NULL)
+               return -ENOMEM;
+
+       ret = armada_gem_linear_back(dev, dobj);
+       if (ret)
+               goto err;
+
+       ret = drm_gem_handle_create(file, &dobj->obj, &handle);
+       if (ret)
+               goto err;
+
+       args->handle = handle;
+
+       /* drop reference from allocate - handle holds it now */
+       DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle);
+ err:
+       drm_gem_object_unreference_unlocked(&dobj->obj);
+       return ret;
+}
+
+int armada_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
+       uint32_t handle, uint64_t *offset)
+{
+       struct armada_gem_object *obj;
+       int ret = 0;
+
+       mutex_lock(&dev->struct_mutex);
+       obj = armada_gem_object_lookup(dev, file, handle);
+       if (!obj) {
+               DRM_ERROR("failed to lookup gem object\n");
+               ret = -EINVAL;
+               goto err_unlock;
+       }
+
+       /* Don't allow imported objects to be mapped */
+       if (obj->obj.import_attach) {
+               ret = -EINVAL;
+               goto err_unlock;
+       }
+
+       ret = drm_gem_create_mmap_offset(&obj->obj);
+       if (ret == 0) {
+               *offset = drm_vma_node_offset_addr(&obj->obj.vma_node);
+               DRM_DEBUG_DRIVER("handle %#x offset %llx\n", handle, *offset);
+       }
+
+       drm_gem_object_unreference(&obj->obj);
+ err_unlock:
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
+
+int armada_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
+       uint32_t handle)
+{
+       return drm_gem_handle_delete(file, handle);
+}
+
+/* Private driver gem ioctls */
+int armada_gem_create_ioctl(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct drm_armada_gem_create *args = data;
+       struct armada_gem_object *dobj;
+       size_t size;
+       u32 handle;
+       int ret;
+
+       if (args->size == 0)
+               return -ENOMEM;
+
+       size = args->size;
+
+       dobj = armada_gem_alloc_object(dev, size);
+       if (dobj == NULL)
+               return -ENOMEM;
+
+       ret = drm_gem_handle_create(file, &dobj->obj, &handle);
+       if (ret)
+               goto err;
+
+       args->handle = handle;
+
+       /* drop reference from allocate - handle holds it now */
+       DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle);
+ err:
+       drm_gem_object_unreference_unlocked(&dobj->obj);
+       return ret;
+}
+
+/* Map a shmem-backed object into process memory space */
+int armada_gem_mmap_ioctl(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct drm_armada_gem_mmap *args = data;
+       struct armada_gem_object *dobj;
+       unsigned long addr;
+
+       dobj = armada_gem_object_lookup(dev, file, args->handle);
+       if (dobj == NULL)
+               return -ENOENT;
+
+       if (!dobj->obj.filp) {
+               drm_gem_object_unreference(&dobj->obj);
+               return -EINVAL;
+       }
+
+       addr = vm_mmap(dobj->obj.filp, 0, args->size, PROT_READ | PROT_WRITE,
+                      MAP_SHARED, args->offset);
+       drm_gem_object_unreference(&dobj->obj);
+       if (IS_ERR_VALUE(addr))
+               return addr;
+
+       args->addr = addr;
+
+       return 0;
+}
+
+int armada_gem_pwrite_ioctl(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct drm_armada_gem_pwrite *args = data;
+       struct armada_gem_object *dobj;
+       char __user *ptr;
+       int ret;
+
+       DRM_DEBUG_DRIVER("handle %u off %u size %u ptr 0x%llx\n",
+               args->handle, args->offset, args->size, args->ptr);
+
+       if (args->size == 0)
+               return 0;
+
+       ptr = (char __user *)(uintptr_t)args->ptr;
+
+       if (!access_ok(VERIFY_READ, ptr, args->size))
+               return -EFAULT;
+
+       ret = fault_in_multipages_readable(ptr, args->size);
+       if (ret)
+               return ret;
+
+       dobj = armada_gem_object_lookup(dev, file, args->handle);
+       if (dobj == NULL)
+               return -ENOENT;
+
+       /* Must be a kernel-mapped object */
+       if (!dobj->addr)
+               return -EINVAL;
+
+       if (args->offset > dobj->obj.size ||
+           args->size > dobj->obj.size - args->offset) {
+               DRM_ERROR("invalid size: object size %u\n", dobj->obj.size);
+               ret = -EINVAL;
+               goto unref;
+       }
+
+       if (copy_from_user(dobj->addr + args->offset, ptr, args->size)) {
+               ret = -EFAULT;
+       } else if (dobj->update) {
+               dobj->update(dobj->update_data);
+               ret = 0;
+       }
+
+ unref:
+       drm_gem_object_unreference_unlocked(&dobj->obj);
+       return ret;
+}
+
+/* Prime support */
+struct sg_table *
+armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
+       enum dma_data_direction dir)
+{
+       struct drm_gem_object *obj = attach->dmabuf->priv;
+       struct armada_gem_object *dobj = drm_to_armada_gem(obj);
+       struct scatterlist *sg;
+       struct sg_table *sgt;
+       int i, num;
+
+       sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+       if (!sgt)
+               return NULL;
+
+       if (dobj->obj.filp) {
+               struct address_space *mapping;
+               gfp_t gfp;
+               int count;
+
+               count = dobj->obj.size / PAGE_SIZE;
+               if (sg_alloc_table(sgt, count, GFP_KERNEL))
+                       goto free_sgt;
+
+               mapping = file_inode(dobj->obj.filp)->i_mapping;
+               gfp = mapping_gfp_mask(mapping);
+
+               for_each_sg(sgt->sgl, sg, count, i) {
+                       struct page *page;
+
+                       page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+                       if (IS_ERR(page)) {
+                               num = i;
+                               goto release;
+                       }
+
+                       sg_set_page(sg, page, PAGE_SIZE, 0);
+               }
+
+               if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0) {
+                       num = sgt->nents;
+                       goto release;
+               }
+       } else if (dobj->page) {
+               /* Single contiguous page */
+               if (sg_alloc_table(sgt, 1, GFP_KERNEL))
+                       goto free_sgt;
+
+               sg_set_page(sgt->sgl, dobj->page, dobj->obj.size, 0);
+
+               if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
+                       goto free_table;
+       } else if (dobj->linear) {
+               /* Single contiguous physical region - no struct page */
+               if (sg_alloc_table(sgt, 1, GFP_KERNEL))
+                       goto free_sgt;
+               sg_dma_address(sgt->sgl) = dobj->dev_addr;
+               sg_dma_len(sgt->sgl) = dobj->obj.size;
+       } else {
+               goto free_sgt;
+       }
+       return sgt;
+
+ release:
+       for_each_sg(sgt->sgl, sg, num, i)
+               page_cache_release(sg_page(sg));
+ free_table:
+       sg_free_table(sgt);
+ free_sgt:
+       kfree(sgt);
+       return NULL;
+}
+
+static void armada_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach,
+       struct sg_table *sgt, enum dma_data_direction dir)
+{
+       struct drm_gem_object *obj = attach->dmabuf->priv;
+       struct armada_gem_object *dobj = drm_to_armada_gem(obj);
+       int i;
+
+       if (!dobj->linear)
+               dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
+
+       if (dobj->obj.filp) {
+               struct scatterlist *sg;
+               for_each_sg(sgt->sgl, sg, sgt->nents, i)
+                       page_cache_release(sg_page(sg));
+       }
+
+       sg_free_table(sgt);
+       kfree(sgt);
+}
+
+static void *armada_gem_dmabuf_no_kmap(struct dma_buf *buf, unsigned long n)
+{
+       return NULL;
+}
+
+static void
+armada_gem_dmabuf_no_kunmap(struct dma_buf *buf, unsigned long n, void *addr)
+{
+}
+
+static int
+armada_gem_dmabuf_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
+{
+       return -EINVAL;
+}
+
+static const struct dma_buf_ops armada_gem_prime_dmabuf_ops = {
+       .map_dma_buf    = armada_gem_prime_map_dma_buf,
+       .unmap_dma_buf  = armada_gem_prime_unmap_dma_buf,
+       .release        = drm_gem_dmabuf_release,
+       .kmap_atomic    = armada_gem_dmabuf_no_kmap,
+       .kunmap_atomic  = armada_gem_dmabuf_no_kunmap,
+       .kmap           = armada_gem_dmabuf_no_kmap,
+       .kunmap         = armada_gem_dmabuf_no_kunmap,
+       .mmap           = armada_gem_dmabuf_mmap,
+};
+
+struct dma_buf *
+armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj,
+       int flags)
+{
+       return dma_buf_export(obj, &armada_gem_prime_dmabuf_ops, obj->size,
+                             O_RDWR);
+}
+
+struct drm_gem_object *
+armada_gem_prime_import(struct drm_device *dev, struct dma_buf *buf)
+{
+       struct dma_buf_attachment *attach;
+       struct armada_gem_object *dobj;
+
+       if (buf->ops == &armada_gem_prime_dmabuf_ops) {
+               struct drm_gem_object *obj = buf->priv;
+               if (obj->dev == dev) {
+                       /*
+                        * Importing our own dmabuf(s) increases the
+                        * refcount on the gem object itself.
+                        */
+                       drm_gem_object_reference(obj);
+                       dma_buf_put(buf);
+                       return obj;
+               }
+       }
+
+       attach = dma_buf_attach(buf, dev->dev);
+       if (IS_ERR(attach))
+               return ERR_CAST(attach);
+
+       dobj = armada_gem_alloc_private_object(dev, buf->size);
+       if (!dobj) {
+               dma_buf_detach(buf, attach);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dobj->obj.import_attach = attach;
+
+       /*
+        * Don't call dma_buf_map_attachment() here - it maps the
+        * scatterlist immediately for DMA, and this is not always
+        * an appropriate thing to do.
+        */
+       return &dobj->obj;
+}
+
+int armada_gem_map_import(struct armada_gem_object *dobj)
+{
+       int ret;
+
+       dobj->sgt = dma_buf_map_attachment(dobj->obj.import_attach,
+                                         DMA_TO_DEVICE);
+       if (!dobj->sgt) {
+               DRM_ERROR("dma_buf_map_attachment() returned NULL\n");
+               return -EINVAL;
+       }
+       if (IS_ERR(dobj->sgt)) {
+               ret = PTR_ERR(dobj->sgt);
+               dobj->sgt = NULL;
+               DRM_ERROR("dma_buf_map_attachment() error: %d\n", ret);
+               return ret;
+       }
+       if (dobj->sgt->nents > 1) {
+               DRM_ERROR("dma_buf_map_attachment() returned an (unsupported) scattered list\n");
+               return -EINVAL;
+       }
+       if (sg_dma_len(dobj->sgt->sgl) < dobj->obj.size) {
+               DRM_ERROR("dma_buf_map_attachment() returned a small buffer\n");
+               return -EINVAL;
+       }
+       dobj->dev_addr = sg_dma_address(dobj->sgt->sgl);
+       return 0;
+}
diff --git a/drivers/gpu/drm/armada/armada_gem.h b/drivers/gpu/drm/armada/armada_gem.h
new file mode 100644 (file)
index 0000000..00b6cd4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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 ARMADA_GEM_H
+#define ARMADA_GEM_H
+
+/* GEM */
+struct armada_gem_object {
+       struct drm_gem_object   obj;
+       void                    *addr;
+       phys_addr_t             phys_addr;
+       resource_size_t         dev_addr;
+       struct drm_mm_node      *linear;        /* for linear backed */
+       struct page             *page;          /* for page backed */
+       struct sg_table         *sgt;           /* for imported */
+       void                    (*update)(void *);
+       void                    *update_data;
+};
+
+extern const struct vm_operations_struct armada_gem_vm_ops;
+
+#define drm_to_armada_gem(o) container_of(o, struct armada_gem_object, obj)
+
+void armada_gem_free_object(struct drm_gem_object *);
+int armada_gem_linear_back(struct drm_device *, struct armada_gem_object *);
+void *armada_gem_map_object(struct drm_device *, struct armada_gem_object *);
+struct armada_gem_object *armada_gem_alloc_private_object(struct drm_device *,
+       size_t);
+int armada_gem_dumb_create(struct drm_file *, struct drm_device *,
+       struct drm_mode_create_dumb *);
+int armada_gem_dumb_map_offset(struct drm_file *, struct drm_device *,
+       uint32_t, uint64_t *);
+int armada_gem_dumb_destroy(struct drm_file *, struct drm_device *,
+       uint32_t);
+struct dma_buf *armada_gem_prime_export(struct drm_device *dev,
+       struct drm_gem_object *obj, int flags);
+struct drm_gem_object *armada_gem_prime_import(struct drm_device *,
+       struct dma_buf *);
+int armada_gem_map_import(struct armada_gem_object *);
+
+static inline struct armada_gem_object *armada_gem_object_lookup(
+       struct drm_device *dev, struct drm_file *dfile, unsigned handle)
+{
+       struct drm_gem_object *obj = drm_gem_object_lookup(dev, dfile, handle);
+
+       return obj ? drm_to_armada_gem(obj) : NULL;
+}
+#endif
diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
new file mode 100644 (file)
index 0000000..27319a8
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Rewritten from the dovefb driver, and Armada510 manuals.
+ *
+ * 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 ARMADA_HW_H
+#define ARMADA_HW_H
+
+/*
+ * Note: the following registers are written from IRQ context:
+ *  LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL
+ *  LCD_SPU_DMA_START_ADDR_[YUV][01], LCD_SPU_DMA_PITCH_YC,
+ *  LCD_SPU_DMA_PITCH_UV, LCD_SPU_DMA_OVSA_HPXL_VLN,
+ *  LCD_SPU_DMA_HPXL_VLN, LCD_SPU_DZM_HPXL_VLN, LCD_SPU_DMA_CTRL0
+ */
+enum {
+       LCD_SPU_ADV_REG                 = 0x0084,       /* Armada 510 */
+       LCD_SPU_DMA_START_ADDR_Y0       = 0x00c0,
+       LCD_SPU_DMA_START_ADDR_U0       = 0x00c4,
+       LCD_SPU_DMA_START_ADDR_V0       = 0x00c8,
+       LCD_CFG_DMA_START_ADDR_0        = 0x00cc,
+       LCD_SPU_DMA_START_ADDR_Y1       = 0x00d0,
+       LCD_SPU_DMA_START_ADDR_U1       = 0x00d4,
+       LCD_SPU_DMA_START_ADDR_V1       = 0x00d8,
+       LCD_CFG_DMA_START_ADDR_1        = 0x00dc,
+       LCD_SPU_DMA_PITCH_YC            = 0x00e0,
+       LCD_SPU_DMA_PITCH_UV            = 0x00e4,
+       LCD_SPU_DMA_OVSA_HPXL_VLN       = 0x00e8,
+       LCD_SPU_DMA_HPXL_VLN            = 0x00ec,
+       LCD_SPU_DZM_HPXL_VLN            = 0x00f0,
+       LCD_CFG_GRA_START_ADDR0         = 0x00f4,
+       LCD_CFG_GRA_START_ADDR1         = 0x00f8,
+       LCD_CFG_GRA_PITCH               = 0x00fc,
+       LCD_SPU_GRA_OVSA_HPXL_VLN       = 0x0100,
+       LCD_SPU_GRA_HPXL_VLN            = 0x0104,
+       LCD_SPU_GZM_HPXL_VLN            = 0x0108,
+       LCD_SPU_HWC_OVSA_HPXL_VLN       = 0x010c,
+       LCD_SPU_HWC_HPXL_VLN            = 0x0110,
+       LCD_SPUT_V_H_TOTAL              = 0x0114,
+       LCD_SPU_V_H_ACTIVE              = 0x0118,
+       LCD_SPU_H_PORCH                 = 0x011c,
+       LCD_SPU_V_PORCH                 = 0x0120,
+       LCD_SPU_BLANKCOLOR              = 0x0124,
+       LCD_SPU_ALPHA_COLOR1            = 0x0128,
+       LCD_SPU_ALPHA_COLOR2            = 0x012c,
+       LCD_SPU_COLORKEY_Y              = 0x0130,
+       LCD_SPU_COLORKEY_U              = 0x0134,
+       LCD_SPU_COLORKEY_V              = 0x0138,
+       LCD_CFG_RDREG4F                 = 0x013c,       /* Armada 510 */
+       LCD_SPU_SPI_RXDATA              = 0x0140,
+       LCD_SPU_ISA_RXDATA              = 0x0144,
+       LCD_SPU_HWC_RDDAT               = 0x0158,
+       LCD_SPU_GAMMA_RDDAT             = 0x015c,
+       LCD_SPU_PALETTE_RDDAT           = 0x0160,
+       LCD_SPU_IOPAD_IN                = 0x0178,
+       LCD_CFG_RDREG5F                 = 0x017c,
+       LCD_SPU_SPI_CTRL                = 0x0180,
+       LCD_SPU_SPI_TXDATA              = 0x0184,
+       LCD_SPU_SMPN_CTRL               = 0x0188,
+       LCD_SPU_DMA_CTRL0               = 0x0190,
+       LCD_SPU_DMA_CTRL1               = 0x0194,
+       LCD_SPU_SRAM_CTRL               = 0x0198,
+       LCD_SPU_SRAM_WRDAT              = 0x019c,
+       LCD_SPU_SRAM_PARA0              = 0x01a0,       /* Armada 510 */
+       LCD_SPU_SRAM_PARA1              = 0x01a4,
+       LCD_CFG_SCLK_DIV                = 0x01a8,
+       LCD_SPU_CONTRAST                = 0x01ac,
+       LCD_SPU_SATURATION              = 0x01b0,
+       LCD_SPU_CBSH_HUE                = 0x01b4,
+       LCD_SPU_DUMB_CTRL               = 0x01b8,
+       LCD_SPU_IOPAD_CONTROL           = 0x01bc,
+       LCD_SPU_IRQ_ENA                 = 0x01c0,
+       LCD_SPU_IRQ_ISR                 = 0x01c4,
+};
+
+/* For LCD_SPU_ADV_REG */
+enum {
+       ADV_VSYNC_L_OFF = 0xfff << 20,
+       ADV_GRACOLORKEY = 1 << 19,
+       ADV_VIDCOLORKEY = 1 << 18,
+       ADV_HWC32BLEND  = 1 << 15,
+       ADV_HWC32ARGB   = 1 << 14,
+       ADV_HWC32ENABLE = 1 << 13,
+       ADV_VSYNCOFFEN  = 1 << 12,
+       ADV_VSYNC_H_OFF = 0xfff << 0,
+};
+
+enum {
+       CFG_565         = 0,
+       CFG_1555        = 1,
+       CFG_888PACK     = 2,
+       CFG_X888        = 3,
+       CFG_8888        = 4,
+       CFG_422PACK     = 5,
+       CFG_422         = 6,
+       CFG_420         = 7,
+       CFG_PSEUDO4     = 9,
+       CFG_PSEUDO8     = 10,
+       CFG_SWAPRB      = 1 << 4,
+       CFG_SWAPUV      = 1 << 3,
+       CFG_SWAPYU      = 1 << 2,
+       CFG_YUV2RGB     = 1 << 1,
+};
+
+/* For LCD_SPU_DMA_CTRL0 */
+enum {
+       CFG_NOBLENDING  = 1 << 31,
+       CFG_GAMMA_ENA   = 1 << 30,
+       CFG_CBSH_ENA    = 1 << 29,
+       CFG_PALETTE_ENA = 1 << 28,
+       CFG_ARBFAST_ENA = 1 << 27,
+       CFG_HWC_1BITMOD = 1 << 26,
+       CFG_HWC_1BITENA = 1 << 25,
+       CFG_HWC_ENA     = 1 << 24,
+       CFG_DMAFORMAT   = 0xf << 20,
+#define        CFG_DMA_FMT(x)  ((x) << 20)
+       CFG_GRAFORMAT   = 0xf << 16,
+#define        CFG_GRA_FMT(x)  ((x) << 16)
+#define CFG_GRA_MOD(x) ((x) << 8)
+       CFG_GRA_FTOGGLE = 1 << 15,
+       CFG_GRA_HSMOOTH = 1 << 14,
+       CFG_GRA_TSTMODE = 1 << 13,
+       CFG_GRA_ENA     = 1 << 8,
+#define CFG_DMA_MOD(x) ((x) << 0)
+       CFG_DMA_FTOGGLE = 1 << 7,
+       CFG_DMA_HSMOOTH = 1 << 6,
+       CFG_DMA_TSTMODE = 1 << 5,
+       CFG_DMA_ENA     = 1 << 0,
+};
+
+enum {
+       CKMODE_DISABLE  = 0,
+       CKMODE_Y        = 1,
+       CKMODE_U        = 2,
+       CKMODE_RGB      = 3,
+       CKMODE_V        = 4,
+       CKMODE_R        = 5,
+       CKMODE_G        = 6,
+       CKMODE_B        = 7,
+};
+
+/* For LCD_SPU_DMA_CTRL1 */
+enum {
+       CFG_FRAME_TRIG          = 1 << 31,
+       CFG_VSYNC_INV           = 1 << 27,
+       CFG_CKMODE_MASK         = 0x7 << 24,
+#define CFG_CKMODE(x)          ((x) << 24)
+       CFG_CARRY               = 1 << 23,
+       CFG_GATED_CLK           = 1 << 21,
+       CFG_PWRDN_ENA           = 1 << 20,
+       CFG_DSCALE_MASK         = 0x3 << 18,
+       CFG_DSCALE_NONE         = 0x0 << 18,
+       CFG_DSCALE_HALF         = 0x1 << 18,
+       CFG_DSCALE_QUAR         = 0x2 << 18,
+       CFG_ALPHAM_MASK         = 0x3 << 16,
+       CFG_ALPHAM_VIDEO        = 0x0 << 16,
+       CFG_ALPHAM_GRA          = 0x1 << 16,
+       CFG_ALPHAM_CFG          = 0x2 << 16,
+       CFG_ALPHA_MASK          = 0xff << 8,
+       CFG_PIXCMD_MASK         = 0xff,
+};
+
+/* For LCD_SPU_SRAM_CTRL */
+enum {
+       SRAM_READ       = 0 << 14,
+       SRAM_WRITE      = 2 << 14,
+       SRAM_INIT       = 3 << 14,
+       SRAM_HWC32_RAM1 = 0xc << 8,
+       SRAM_HWC32_RAM2 = 0xd << 8,
+       SRAM_HWC32_RAMR = SRAM_HWC32_RAM1,
+       SRAM_HWC32_RAMG = SRAM_HWC32_RAM2,
+       SRAM_HWC32_RAMB = 0xe << 8,
+       SRAM_HWC32_TRAN = 0xf << 8,
+       SRAM_HWC        = 0xf << 8,
+};
+
+/* For LCD_SPU_SRAM_PARA1 */
+enum {
+       CFG_CSB_256x32  = 1 << 15,      /* cursor */
+       CFG_CSB_256x24  = 1 << 14,      /* palette */
+       CFG_CSB_256x8   = 1 << 13,      /* gamma */
+       CFG_PDWN1920x32 = 1 << 8,       /* Armada 510: power down vscale ram */
+       CFG_PDWN256x32  = 1 << 7,       /* power down cursor */
+       CFG_PDWN256x24  = 1 << 6,       /* power down palette */
+       CFG_PDWN256x8   = 1 << 5,       /* power down gamma */
+       CFG_PDWNHWC     = 1 << 4,       /* Armada 510: power down all hwc ram */
+       CFG_PDWN32x32   = 1 << 3,       /* power down slave->smart ram */
+       CFG_PDWN16x66   = 1 << 2,       /* power down UV fifo */
+       CFG_PDWN32x66   = 1 << 1,       /* power down Y fifo */
+       CFG_PDWN64x66   = 1 << 0,       /* power down graphic fifo */
+};
+
+/* For LCD_CFG_SCLK_DIV */
+enum {
+       /* Armada 510 */
+       SCLK_510_AXI            = 0x0 << 30,
+       SCLK_510_EXTCLK0        = 0x1 << 30,
+       SCLK_510_PLL            = 0x2 << 30,
+       SCLK_510_EXTCLK1        = 0x3 << 30,
+       SCLK_510_DIV_CHANGE     = 1 << 29,
+       SCLK_510_FRAC_DIV_MASK  = 0xfff << 16,
+       SCLK_510_INT_DIV_MASK   = 0xffff << 0,
+
+       /* Armada 16x */
+       SCLK_16X_AHB            = 0x0 << 28,
+       SCLK_16X_PCLK           = 0x1 << 28,
+       SCLK_16X_AXI            = 0x4 << 28,
+       SCLK_16X_PLL            = 0x8 << 28,
+       SCLK_16X_FRAC_DIV_MASK  = 0xfff << 16,
+       SCLK_16X_INT_DIV_MASK   = 0xffff << 0,
+};
+
+/* For LCD_SPU_DUMB_CTRL */
+enum {
+       DUMB16_RGB565_0 = 0x0 << 28,
+       DUMB16_RGB565_1 = 0x1 << 28,
+       DUMB18_RGB666_0 = 0x2 << 28,
+       DUMB18_RGB666_1 = 0x3 << 28,
+       DUMB12_RGB444_0 = 0x4 << 28,
+       DUMB12_RGB444_1 = 0x5 << 28,
+       DUMB24_RGB888_0 = 0x6 << 28,
+       DUMB_BLANK      = 0x7 << 28,
+       DUMB_MASK       = 0xf << 28,
+       CFG_BIAS_OUT    = 1 << 8,
+       CFG_REV_RGB     = 1 << 7,
+       CFG_INV_CBLANK  = 1 << 6,
+       CFG_INV_CSYNC   = 1 << 5,       /* Normally active high */
+       CFG_INV_HENA    = 1 << 4,
+       CFG_INV_VSYNC   = 1 << 3,       /* Normally active high */
+       CFG_INV_HSYNC   = 1 << 2,       /* Normally active high */
+       CFG_INV_PCLK    = 1 << 1,
+       CFG_DUMB_ENA    = 1 << 0,
+};
+
+/* For LCD_SPU_IOPAD_CONTROL */
+enum {
+       CFG_VSCALE_LN_EN        = 3 << 18,
+       CFG_GRA_VM_ENA          = 1 << 15,
+       CFG_DMA_VM_ENA          = 1 << 13,
+       CFG_CMD_VM_ENA          = 1 << 11,
+       CFG_CSC_MASK            = 3 << 8,
+       CFG_CSC_YUV_CCIR709     = 1 << 9,
+       CFG_CSC_YUV_CCIR601     = 0 << 9,
+       CFG_CSC_RGB_STUDIO      = 1 << 8,
+       CFG_CSC_RGB_COMPUTER    = 0 << 8,
+       CFG_IOPAD_MASK          = 0xf << 0,
+       CFG_IOPAD_DUMB24        = 0x0 << 0,
+       CFG_IOPAD_DUMB18SPI     = 0x1 << 0,
+       CFG_IOPAD_DUMB18GPIO    = 0x2 << 0,
+       CFG_IOPAD_DUMB16SPI     = 0x3 << 0,
+       CFG_IOPAD_DUMB16GPIO    = 0x4 << 0,
+       CFG_IOPAD_DUMB12GPIO    = 0x5 << 0,
+       CFG_IOPAD_SMART18       = 0x6 << 0,
+       CFG_IOPAD_SMART16       = 0x7 << 0,
+       CFG_IOPAD_SMART8        = 0x8 << 0,
+};
+
+#define IOPAD_DUMB24                0x0
+
+/* For LCD_SPU_IRQ_ENA */
+enum {
+       DMA_FRAME_IRQ0_ENA      = 1 << 31,
+       DMA_FRAME_IRQ1_ENA      = 1 << 30,
+       DMA_FRAME_IRQ_ENA       = DMA_FRAME_IRQ0_ENA | DMA_FRAME_IRQ1_ENA,
+       DMA_FF_UNDERFLOW_ENA    = 1 << 29,
+       GRA_FRAME_IRQ0_ENA      = 1 << 27,
+       GRA_FRAME_IRQ1_ENA      = 1 << 26,
+       GRA_FRAME_IRQ_ENA       = GRA_FRAME_IRQ0_ENA | GRA_FRAME_IRQ1_ENA,
+       GRA_FF_UNDERFLOW_ENA    = 1 << 25,
+       VSYNC_IRQ_ENA           = 1 << 23,
+       DUMB_FRAMEDONE_ENA      = 1 << 22,
+       TWC_FRAMEDONE_ENA       = 1 << 21,
+       HWC_FRAMEDONE_ENA       = 1 << 20,
+       SLV_IRQ_ENA             = 1 << 19,
+       SPI_IRQ_ENA             = 1 << 18,
+       PWRDN_IRQ_ENA           = 1 << 17,
+       ERR_IRQ_ENA             = 1 << 16,
+       CLEAN_SPU_IRQ_ISR       = 0xffff,
+};
+
+/* For LCD_SPU_IRQ_ISR */
+enum {
+       DMA_FRAME_IRQ0          = 1 << 31,
+       DMA_FRAME_IRQ1          = 1 << 30,
+       DMA_FRAME_IRQ           = DMA_FRAME_IRQ0 | DMA_FRAME_IRQ1,
+       DMA_FF_UNDERFLOW        = 1 << 29,
+       GRA_FRAME_IRQ0          = 1 << 27,
+       GRA_FRAME_IRQ1          = 1 << 26,
+       GRA_FRAME_IRQ           = GRA_FRAME_IRQ0 | GRA_FRAME_IRQ1,
+       GRA_FF_UNDERFLOW        = 1 << 25,
+       VSYNC_IRQ               = 1 << 23,
+       DUMB_FRAMEDONE          = 1 << 22,
+       TWC_FRAMEDONE           = 1 << 21,
+       HWC_FRAMEDONE           = 1 << 20,
+       SLV_IRQ                 = 1 << 19,
+       SPI_IRQ                 = 1 << 18,
+       PWRDN_IRQ               = 1 << 17,
+       ERR_IRQ                 = 1 << 16,
+       DMA_FRAME_IRQ0_LEVEL    = 1 << 15,
+       DMA_FRAME_IRQ1_LEVEL    = 1 << 14,
+       DMA_FRAME_CNT_ISR       = 3 << 12,
+       GRA_FRAME_IRQ0_LEVEL    = 1 << 11,
+       GRA_FRAME_IRQ1_LEVEL    = 1 << 10,
+       GRA_FRAME_CNT_ISR       = 3 << 8,
+       VSYNC_IRQ_LEVEL         = 1 << 7,
+       DUMB_FRAMEDONE_LEVEL    = 1 << 6,
+       TWC_FRAMEDONE_LEVEL     = 1 << 5,
+       HWC_FRAMEDONE_LEVEL     = 1 << 4,
+       SLV_FF_EMPTY            = 1 << 3,
+       DMA_FF_ALLEMPTY         = 1 << 2,
+       GRA_FF_ALLEMPTY         = 1 << 1,
+       PWRDN_IRQ_LEVEL         = 1 << 0,
+};
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_ioctlP.h b/drivers/gpu/drm/armada/armada_ioctlP.h
new file mode 100644 (file)
index 0000000..bd8c456
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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 ARMADA_IOCTLP_H
+#define ARMADA_IOCTLP_H
+
+#define ARMADA_IOCTL_PROTO(name)\
+extern int armada_##name##_ioctl(struct drm_device *, void *, struct drm_file *)
+
+ARMADA_IOCTL_PROTO(gem_create);
+ARMADA_IOCTL_PROTO(gem_mmap);
+ARMADA_IOCTL_PROTO(gem_pwrite);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c
new file mode 100644 (file)
index 0000000..d685a54
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+#include "armada_output.h"
+#include "armada_drm.h"
+
+struct armada_connector {
+       struct drm_connector conn;
+       const struct armada_output_type *type;
+};
+
+#define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn)
+
+struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn)
+{
+       struct drm_encoder *enc = conn->encoder;
+
+       return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]);
+}
+
+static enum drm_connector_status armada_drm_connector_detect(
+       struct drm_connector *conn, bool force)
+{
+       struct armada_connector *dconn = drm_to_armada_conn(conn);
+       enum drm_connector_status status = connector_status_disconnected;
+
+       if (dconn->type->detect) {
+               status = dconn->type->detect(conn, force);
+       } else {
+               struct drm_encoder *enc = armada_drm_connector_encoder(conn);
+
+               if (enc)
+                       status = encoder_helper_funcs(enc)->detect(enc, conn);
+       }
+
+       return status;
+}
+
+static void armada_drm_connector_destroy(struct drm_connector *conn)
+{
+       struct armada_connector *dconn = drm_to_armada_conn(conn);
+
+       drm_sysfs_connector_remove(conn);
+       drm_connector_cleanup(conn);
+       kfree(dconn);
+}
+
+static int armada_drm_connector_set_property(struct drm_connector *conn,
+       struct drm_property *property, uint64_t value)
+{
+       struct armada_connector *dconn = drm_to_armada_conn(conn);
+
+       if (!dconn->type->set_property)
+               return -EINVAL;
+
+       return dconn->type->set_property(conn, property, value);
+}
+
+static const struct drm_connector_funcs armada_drm_conn_funcs = {
+       .dpms           = drm_helper_connector_dpms,
+       .fill_modes     = drm_helper_probe_single_connector_modes,
+       .detect         = armada_drm_connector_detect,
+       .destroy        = armada_drm_connector_destroy,
+       .set_property   = armada_drm_connector_set_property,
+};
+
+void armada_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+       encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+void armada_drm_encoder_commit(struct drm_encoder *encoder)
+{
+       encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+       const struct drm_display_mode *mode, struct drm_display_mode *adjusted)
+{
+       return true;
+}
+
+/* Shouldn't this be a generic helper function? */
+int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
+       struct drm_display_mode *mode)
+{
+       struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
+       int valid = MODE_BAD;
+
+       if (encoder) {
+               struct drm_encoder_slave *slave = to_encoder_slave(encoder);
+
+               valid = slave->slave_funcs->mode_valid(encoder, mode);
+       }
+       return valid;
+}
+
+int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
+       struct drm_property *property, uint64_t value)
+{
+       struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
+       int rc = -EINVAL;
+
+       if (encoder) {
+               struct drm_encoder_slave *slave = to_encoder_slave(encoder);
+
+               rc = slave->slave_funcs->set_property(encoder, conn, property,
+                                                     value);
+       }
+       return rc;
+}
+
+int armada_output_create(struct drm_device *dev,
+       const struct armada_output_type *type, const void *data)
+{
+       struct armada_connector *dconn;
+       int ret;
+
+       dconn = kzalloc(sizeof(*dconn), GFP_KERNEL);
+       if (!dconn)
+               return -ENOMEM;
+
+       dconn->type = type;
+
+       ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs,
+                                type->connector_type);
+       if (ret) {
+               DRM_ERROR("unable to init connector\n");
+               goto err_destroy_dconn;
+       }
+
+       ret = type->create(&dconn->conn, data);
+       if (ret)
+               goto err_conn;
+
+       ret = drm_sysfs_connector_add(&dconn->conn);
+       if (ret)
+               goto err_sysfs;
+
+       return 0;
+
+ err_sysfs:
+       if (dconn->conn.encoder)
+               dconn->conn.encoder->funcs->destroy(dconn->conn.encoder);
+ err_conn:
+       drm_connector_cleanup(&dconn->conn);
+ err_destroy_dconn:
+       kfree(dconn);
+       return ret;
+}
diff --git a/drivers/gpu/drm/armada/armada_output.h b/drivers/gpu/drm/armada/armada_output.h
new file mode 100644 (file)
index 0000000..4126d43
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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 ARMADA_CONNETOR_H
+#define ARMADA_CONNETOR_H
+
+#define encoder_helper_funcs(encoder) \
+       ((struct drm_encoder_helper_funcs *)encoder->helper_private)
+
+struct armada_output_type {
+       int connector_type;
+       enum drm_connector_status (*detect)(struct drm_connector *, bool);
+       int (*create)(struct drm_connector *, const void *);
+       int (*set_property)(struct drm_connector *, struct drm_property *,
+                           uint64_t);
+};
+
+struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn);
+
+void armada_drm_encoder_prepare(struct drm_encoder *encoder);
+void armada_drm_encoder_commit(struct drm_encoder *encoder);
+
+bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+       const struct drm_display_mode *mode, struct drm_display_mode *adj);
+
+int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
+       struct drm_display_mode *mode);
+
+int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
+       struct drm_property *property, uint64_t value);
+
+int armada_output_create(struct drm_device *dev,
+       const struct armada_output_type *type, const void *data);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
new file mode 100644 (file)
index 0000000..c5b06fd
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Rewritten from the dovefb driver, and Armada510 manuals.
+ *
+ * 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 <drm/drmP.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+#include <drm/armada_drm.h>
+#include "armada_ioctlP.h"
+
+struct armada_plane_properties {
+       uint32_t colorkey_yr;
+       uint32_t colorkey_ug;
+       uint32_t colorkey_vb;
+#define K2R(val) (((val) >> 0) & 0xff)
+#define K2G(val) (((val) >> 8) & 0xff)
+#define K2B(val) (((val) >> 16) & 0xff)
+       int16_t  brightness;
+       uint16_t contrast;
+       uint16_t saturation;
+       uint32_t colorkey_mode;
+};
+
+struct armada_plane {
+       struct drm_plane base;
+       spinlock_t lock;
+       struct drm_framebuffer *old_fb;
+       uint32_t src_hw;
+       uint32_t dst_hw;
+       uint32_t dst_yx;
+       uint32_t ctrl0;
+       struct {
+               struct armada_vbl_event update;
+               struct armada_regs regs[13];
+               wait_queue_head_t wait;
+       } vbl;
+       struct armada_plane_properties prop;
+};
+#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
+
+
+static void
+armada_ovl_update_attr(struct armada_plane_properties *prop,
+       struct armada_crtc *dcrtc)
+{
+       writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
+       writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U);
+       writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V);
+
+       writel_relaxed(prop->brightness << 16 | prop->contrast,
+                      dcrtc->base + LCD_SPU_CONTRAST);
+       /* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */
+       writel_relaxed(prop->saturation << 16,
+                      dcrtc->base + LCD_SPU_SATURATION);
+       writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE);
+
+       spin_lock_irq(&dcrtc->irq_lock);
+       armada_updatel(prop->colorkey_mode | CFG_ALPHAM_GRA,
+                    CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK,
+                    dcrtc->base + LCD_SPU_DMA_CTRL1);
+
+       armada_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG);
+       spin_unlock_irq(&dcrtc->irq_lock);
+}
+
+/* === Plane support === */
+static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)
+{
+       struct armada_plane *dplane = data;
+       struct drm_framebuffer *fb;
+
+       armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
+
+       spin_lock(&dplane->lock);
+       fb = dplane->old_fb;
+       dplane->old_fb = NULL;
+       spin_unlock(&dplane->lock);
+
+       if (fb)
+               armada_drm_queue_unref_work(dcrtc->crtc.dev, fb);
+}
+
+static unsigned armada_limit(int start, unsigned size, unsigned max)
+{
+       int end = start + size;
+       if (end < 0)
+               return 0;
+       if (start < 0)
+               start = 0;
+       return (unsigned)end > max ? max - start : end - start;
+}
+
+static int
+armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
+       struct drm_framebuffer *fb,
+       int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
+       uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+       struct armada_plane *dplane = drm_to_armada_plane(plane);
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       uint32_t val, ctrl0;
+       unsigned idx = 0;
+       int ret;
+
+       crtc_w = armada_limit(crtc_x, crtc_w, dcrtc->crtc.mode.hdisplay);
+       crtc_h = armada_limit(crtc_y, crtc_h, dcrtc->crtc.mode.vdisplay);
+       ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
+               CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
+               CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA;
+
+       /* Does the position/size result in nothing to display? */
+       if (crtc_w == 0 || crtc_h == 0) {
+               ctrl0 &= ~CFG_DMA_ENA;
+       }
+
+       /*
+        * FIXME: if the starting point is off screen, we need to
+        * adjust src_x, src_y, src_w, src_h appropriately, and
+        * according to the scale.
+        */
+
+       if (!dcrtc->plane) {
+               dcrtc->plane = plane;
+               armada_ovl_update_attr(&dplane->prop, dcrtc);
+       }
+
+       /* FIXME: overlay on an interlaced display */
+       /* Just updating the position/size? */
+       if (plane->fb == fb && dplane->ctrl0 == ctrl0) {
+               val = (src_h & 0xffff0000) | src_w >> 16;
+               dplane->src_hw = val;
+               writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
+               val = crtc_h << 16 | crtc_w;
+               dplane->dst_hw = val;
+               writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
+               val = crtc_y << 16 | crtc_x;
+               dplane->dst_yx = val;
+               writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);
+               return 0;
+       } else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) {
+               /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
+               armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66,
+                              dcrtc->base + LCD_SPU_SRAM_PARA1);
+       }
+
+       ret = wait_event_timeout(dplane->vbl.wait,
+                                list_empty(&dplane->vbl.update.node),
+                                HZ/25);
+       if (ret < 0)
+               return ret;
+
+       if (plane->fb != fb) {
+               struct armada_gem_object *obj = drm_fb_obj(fb);
+               uint32_t sy, su, sv;
+
+               /*
+                * Take a reference on the new framebuffer - we want to
+                * hold on to it while the hardware is displaying it.
+                */
+               drm_framebuffer_reference(fb);
+
+               if (plane->fb) {
+                       struct drm_framebuffer *older_fb;
+
+                       spin_lock_irq(&dplane->lock);
+                       older_fb = dplane->old_fb;
+                       dplane->old_fb = plane->fb;
+                       spin_unlock_irq(&dplane->lock);
+                       if (older_fb)
+                               armada_drm_queue_unref_work(dcrtc->crtc.dev,
+                                                           older_fb);
+               }
+
+               src_y >>= 16;
+               src_x >>= 16;
+               sy = obj->dev_addr + fb->offsets[0] + src_y * fb->pitches[0] +
+                       src_x * fb->bits_per_pixel / 8;
+               su = obj->dev_addr + fb->offsets[1] + src_y * fb->pitches[1] +
+                       src_x;
+               sv = obj->dev_addr + fb->offsets[2] + src_y * fb->pitches[2] +
+                       src_x;
+
+               armada_reg_queue_set(dplane->vbl.regs, idx, sy,
+                                    LCD_SPU_DMA_START_ADDR_Y0);
+               armada_reg_queue_set(dplane->vbl.regs, idx, su,
+                                    LCD_SPU_DMA_START_ADDR_U0);
+               armada_reg_queue_set(dplane->vbl.regs, idx, sv,
+                                    LCD_SPU_DMA_START_ADDR_V0);
+               armada_reg_queue_set(dplane->vbl.regs, idx, sy,
+                                    LCD_SPU_DMA_START_ADDR_Y1);
+               armada_reg_queue_set(dplane->vbl.regs, idx, su,
+                                    LCD_SPU_DMA_START_ADDR_U1);
+               armada_reg_queue_set(dplane->vbl.regs, idx, sv,
+                                    LCD_SPU_DMA_START_ADDR_V1);
+
+               val = fb->pitches[0] << 16 | fb->pitches[0];
+               armada_reg_queue_set(dplane->vbl.regs, idx, val,
+                                    LCD_SPU_DMA_PITCH_YC);
+               val = fb->pitches[1] << 16 | fb->pitches[2];
+               armada_reg_queue_set(dplane->vbl.regs, idx, val,
+                                    LCD_SPU_DMA_PITCH_UV);
+       }
+
+       val = (src_h & 0xffff0000) | src_w >> 16;
+       if (dplane->src_hw != val) {
+               dplane->src_hw = val;
+               armada_reg_queue_set(dplane->vbl.regs, idx, val,
+                                    LCD_SPU_DMA_HPXL_VLN);
+       }
+       val = crtc_h << 16 | crtc_w;
+       if (dplane->dst_hw != val) {
+               dplane->dst_hw = val;
+               armada_reg_queue_set(dplane->vbl.regs, idx, val,
+                                    LCD_SPU_DZM_HPXL_VLN);
+       }
+       val = crtc_y << 16 | crtc_x;
+       if (dplane->dst_yx != val) {
+               dplane->dst_yx = val;
+               armada_reg_queue_set(dplane->vbl.regs, idx, val,
+                                    LCD_SPU_DMA_OVSA_HPXL_VLN);
+       }
+       if (dplane->ctrl0 != ctrl0) {
+               dplane->ctrl0 = ctrl0;
+               armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0,
+                       CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE |
+                       CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE |
+                       CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU |
+                       CFG_YUV2RGB) | CFG_DMA_ENA,
+                       LCD_SPU_DMA_CTRL0);
+       }
+       if (idx) {
+               armada_reg_queue_end(dplane->vbl.regs, idx);
+               armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update);
+       }
+       return 0;
+}
+
+static int armada_plane_disable(struct drm_plane *plane)
+{
+       struct armada_plane *dplane = drm_to_armada_plane(plane);
+       struct drm_framebuffer *fb;
+       struct armada_crtc *dcrtc;
+
+       if (!dplane->base.crtc)
+               return 0;
+
+       dcrtc = drm_to_armada_crtc(dplane->base.crtc);
+       dcrtc->plane = NULL;
+
+       spin_lock_irq(&dcrtc->irq_lock);
+       armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update);
+       armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+       dplane->ctrl0 = 0;
+       spin_unlock_irq(&dcrtc->irq_lock);
+
+       /* Power down the Y/U/V FIFOs */
+       armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0,
+                      dcrtc->base + LCD_SPU_SRAM_PARA1);
+
+       if (plane->fb)
+               drm_framebuffer_unreference(plane->fb);
+
+       spin_lock_irq(&dplane->lock);
+       fb = dplane->old_fb;
+       dplane->old_fb = NULL;
+       spin_unlock_irq(&dplane->lock);
+       if (fb)
+               drm_framebuffer_unreference(fb);
+
+       return 0;
+}
+
+static void armada_plane_destroy(struct drm_plane *plane)
+{
+       kfree(plane);
+}
+
+static int armada_plane_set_property(struct drm_plane *plane,
+       struct drm_property *property, uint64_t val)
+{
+       struct armada_private *priv = plane->dev->dev_private;
+       struct armada_plane *dplane = drm_to_armada_plane(plane);
+       bool update_attr = false;
+
+       if (property == priv->colorkey_prop) {
+#define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
+               dplane->prop.colorkey_yr = CCC(K2R(val));
+               dplane->prop.colorkey_ug = CCC(K2G(val));
+               dplane->prop.colorkey_vb = CCC(K2B(val));
+#undef CCC
+               update_attr = true;
+       } else if (property == priv->colorkey_min_prop) {
+               dplane->prop.colorkey_yr &= ~0x00ff0000;
+               dplane->prop.colorkey_yr |= K2R(val) << 16;
+               dplane->prop.colorkey_ug &= ~0x00ff0000;
+               dplane->prop.colorkey_ug |= K2G(val) << 16;
+               dplane->prop.colorkey_vb &= ~0x00ff0000;
+               dplane->prop.colorkey_vb |= K2B(val) << 16;
+               update_attr = true;
+       } else if (property == priv->colorkey_max_prop) {
+               dplane->prop.colorkey_yr &= ~0xff000000;
+               dplane->prop.colorkey_yr |= K2R(val) << 24;
+               dplane->prop.colorkey_ug &= ~0xff000000;
+               dplane->prop.colorkey_ug |= K2G(val) << 24;
+               dplane->prop.colorkey_vb &= ~0xff000000;
+               dplane->prop.colorkey_vb |= K2B(val) << 24;
+               update_attr = true;
+       } else if (property == priv->colorkey_val_prop) {
+               dplane->prop.colorkey_yr &= ~0x0000ff00;
+               dplane->prop.colorkey_yr |= K2R(val) << 8;
+               dplane->prop.colorkey_ug &= ~0x0000ff00;
+               dplane->prop.colorkey_ug |= K2G(val) << 8;
+               dplane->prop.colorkey_vb &= ~0x0000ff00;
+               dplane->prop.colorkey_vb |= K2B(val) << 8;
+               update_attr = true;
+       } else if (property == priv->colorkey_alpha_prop) {
+               dplane->prop.colorkey_yr &= ~0x000000ff;
+               dplane->prop.colorkey_yr |= K2R(val);
+               dplane->prop.colorkey_ug &= ~0x000000ff;
+               dplane->prop.colorkey_ug |= K2G(val);
+               dplane->prop.colorkey_vb &= ~0x000000ff;
+               dplane->prop.colorkey_vb |= K2B(val);
+               update_attr = true;
+       } else if (property == priv->colorkey_mode_prop) {
+               dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK;
+               dplane->prop.colorkey_mode |= CFG_CKMODE(val);
+               update_attr = true;
+       } else if (property == priv->brightness_prop) {
+               dplane->prop.brightness = val - 256;
+               update_attr = true;
+       } else if (property == priv->contrast_prop) {
+               dplane->prop.contrast = val;
+               update_attr = true;
+       } else if (property == priv->saturation_prop) {
+               dplane->prop.saturation = val;
+               update_attr = true;
+       }
+
+       if (update_attr && dplane->base.crtc)
+               armada_ovl_update_attr(&dplane->prop,
+                                      drm_to_armada_crtc(dplane->base.crtc));
+
+       return 0;
+}
+
+static const struct drm_plane_funcs armada_plane_funcs = {
+       .update_plane   = armada_plane_update,
+       .disable_plane  = armada_plane_disable,
+       .destroy        = armada_plane_destroy,
+       .set_property   = armada_plane_set_property,
+};
+
+static const uint32_t armada_formats[] = {
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YUV420,
+       DRM_FORMAT_YVU420,
+       DRM_FORMAT_YUV422,
+       DRM_FORMAT_YVU422,
+       DRM_FORMAT_VYUY,
+       DRM_FORMAT_YVYU,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_BGR888,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_ABGR1555,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_BGR565,
+};
+
+static struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = {
+       { CKMODE_DISABLE, "disabled" },
+       { CKMODE_Y,       "Y component" },
+       { CKMODE_U,       "U component" },
+       { CKMODE_V,       "V component" },
+       { CKMODE_RGB,     "RGB" },
+       { CKMODE_R,       "R component" },
+       { CKMODE_G,       "G component" },
+       { CKMODE_B,       "B component" },
+};
+
+static int armada_overlay_create_properties(struct drm_device *dev)
+{
+       struct armada_private *priv = dev->dev_private;
+
+       if (priv->colorkey_prop)
+               return 0;
+
+       priv->colorkey_prop = drm_property_create_range(dev, 0,
+                               "colorkey", 0, 0xffffff);
+       priv->colorkey_min_prop = drm_property_create_range(dev, 0,
+                               "colorkey_min", 0, 0xffffff);
+       priv->colorkey_max_prop = drm_property_create_range(dev, 0,
+                               "colorkey_max", 0, 0xffffff);
+       priv->colorkey_val_prop = drm_property_create_range(dev, 0,
+                               "colorkey_val", 0, 0xffffff);
+       priv->colorkey_alpha_prop = drm_property_create_range(dev, 0,
+                               "colorkey_alpha", 0, 0xffffff);
+       priv->colorkey_mode_prop = drm_property_create_enum(dev, 0,
+                               "colorkey_mode",
+                               armada_drm_colorkey_enum_list,
+                               ARRAY_SIZE(armada_drm_colorkey_enum_list));
+       priv->brightness_prop = drm_property_create_range(dev, 0,
+                               "brightness", 0, 256 + 255);
+       priv->contrast_prop = drm_property_create_range(dev, 0,
+                               "contrast", 0, 0x7fff);
+       priv->saturation_prop = drm_property_create_range(dev, 0,
+                               "saturation", 0, 0x7fff);
+
+       if (!priv->colorkey_prop)
+               return -ENOMEM;
+
+       return 0;
+}
+
+int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
+{
+       struct armada_private *priv = dev->dev_private;
+       struct drm_mode_object *mobj;
+       struct armada_plane *dplane;
+       int ret;
+
+       ret = armada_overlay_create_properties(dev);
+       if (ret)
+               return ret;
+
+       dplane = kzalloc(sizeof(*dplane), GFP_KERNEL);
+       if (!dplane)
+               return -ENOMEM;
+
+       spin_lock_init(&dplane->lock);
+       init_waitqueue_head(&dplane->vbl.wait);
+       armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl,
+                                 dplane);
+
+       drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs,
+                      armada_formats, ARRAY_SIZE(armada_formats), false);
+
+       dplane->prop.colorkey_yr = 0xfefefe00;
+       dplane->prop.colorkey_ug = 0x01010100;
+       dplane->prop.colorkey_vb = 0x01010100;
+       dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB);
+       dplane->prop.brightness = 0;
+       dplane->prop.contrast = 0x4000;
+       dplane->prop.saturation = 0x4000;
+
+       mobj = &dplane->base.base;
+       drm_object_attach_property(mobj, priv->colorkey_prop,
+                                  0x0101fe);
+       drm_object_attach_property(mobj, priv->colorkey_min_prop,
+                                  0x0101fe);
+       drm_object_attach_property(mobj, priv->colorkey_max_prop,
+                                  0x0101fe);
+       drm_object_attach_property(mobj, priv->colorkey_val_prop,
+                                  0x0101fe);
+       drm_object_attach_property(mobj, priv->colorkey_alpha_prop,
+                                  0x000000);
+       drm_object_attach_property(mobj, priv->colorkey_mode_prop,
+                                  CKMODE_RGB);
+       drm_object_attach_property(mobj, priv->brightness_prop, 256);
+       drm_object_attach_property(mobj, priv->contrast_prop,
+                                  dplane->prop.contrast);
+       drm_object_attach_property(mobj, priv->saturation_prop,
+                                  dplane->prop.saturation);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/armada/armada_slave.c b/drivers/gpu/drm/armada/armada_slave.c
new file mode 100644 (file)
index 0000000..00d0fac
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Rewritten from the dovefb driver, and Armada510 manuals.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+#include "armada_drm.h"
+#include "armada_output.h"
+#include "armada_slave.h"
+
+static int armada_drm_slave_get_modes(struct drm_connector *conn)
+{
+       struct drm_encoder *enc = armada_drm_connector_encoder(conn);
+       int count = 0;
+
+       if (enc) {
+               struct drm_encoder_slave *slave = to_encoder_slave(enc);
+
+               count = slave->slave_funcs->get_modes(enc, conn);
+       }
+
+       return count;
+}
+
+static void armada_drm_slave_destroy(struct drm_encoder *enc)
+{
+       struct drm_encoder_slave *slave = to_encoder_slave(enc);
+       struct i2c_client *client = drm_i2c_encoder_get_client(enc);
+
+       if (slave->slave_funcs)
+               slave->slave_funcs->destroy(enc);
+       if (client)
+               i2c_put_adapter(client->adapter);
+
+       drm_encoder_cleanup(&slave->base);
+       kfree(slave);
+}
+
+static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = {
+       .destroy        = armada_drm_slave_destroy,
+};
+
+static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = {
+       .get_modes      = armada_drm_slave_get_modes,
+       .mode_valid     = armada_drm_slave_encoder_mode_valid,
+       .best_encoder   = armada_drm_connector_encoder,
+};
+
+static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = {
+       .dpms = drm_i2c_encoder_dpms,
+       .save = drm_i2c_encoder_save,
+       .restore = drm_i2c_encoder_restore,
+       .mode_fixup = drm_i2c_encoder_mode_fixup,
+       .prepare = drm_i2c_encoder_prepare,
+       .commit = drm_i2c_encoder_commit,
+       .mode_set = drm_i2c_encoder_mode_set,
+       .detect = drm_i2c_encoder_detect,
+};
+
+static int
+armada_drm_conn_slave_create(struct drm_connector *conn, const void *data)
+{
+       const struct armada_drm_slave_config *config = data;
+       struct drm_encoder_slave *slave;
+       struct i2c_adapter *adap;
+       int ret;
+
+       conn->interlace_allowed = config->interlace_allowed;
+       conn->doublescan_allowed = config->doublescan_allowed;
+       conn->polled = config->polled;
+
+       drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs);
+
+       slave = kzalloc(sizeof(*slave), GFP_KERNEL);
+       if (!slave)
+               return -ENOMEM;
+
+       slave->base.possible_crtcs = config->crtcs;
+
+       adap = i2c_get_adapter(config->i2c_adapter_id);
+       if (!adap) {
+               kfree(slave);
+               return -EPROBE_DEFER;
+       }
+
+       ret = drm_encoder_init(conn->dev, &slave->base,
+                              &armada_drm_slave_encoder_funcs,
+                              DRM_MODE_ENCODER_TMDS);
+       if (ret) {
+               DRM_ERROR("unable to init encoder\n");
+               i2c_put_adapter(adap);
+               kfree(slave);
+               return ret;
+       }
+
+       ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info);
+       i2c_put_adapter(adap);
+       if (ret) {
+               DRM_ERROR("unable to init encoder slave\n");
+               armada_drm_slave_destroy(&slave->base);
+               return ret;
+       }
+
+       drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers);
+
+       ret = slave->slave_funcs->create_resources(&slave->base, conn);
+       if (ret) {
+               armada_drm_slave_destroy(&slave->base);
+               return ret;
+       }
+
+       ret = drm_mode_connector_attach_encoder(conn, &slave->base);
+       if (ret) {
+               armada_drm_slave_destroy(&slave->base);
+               return ret;
+       }
+
+       conn->encoder = &slave->base;
+
+       return ret;
+}
+
+static const struct armada_output_type armada_drm_conn_slave = {
+       .connector_type = DRM_MODE_CONNECTOR_HDMIA,
+       .create         = armada_drm_conn_slave_create,
+       .set_property   = armada_drm_slave_encoder_set_property,
+};
+
+int armada_drm_connector_slave_create(struct drm_device *dev,
+       const struct armada_drm_slave_config *config)
+{
+       return armada_output_create(dev, &armada_drm_conn_slave, config);
+}
diff --git a/drivers/gpu/drm/armada/armada_slave.h b/drivers/gpu/drm/armada/armada_slave.h
new file mode 100644 (file)
index 0000000..bf2374c
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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 ARMADA_SLAVE_H
+#define ARMADA_SLAVE_H
+
+#include <linux/i2c.h>
+#include <drm/drmP.h>
+
+struct armada_drm_slave_config {
+       int i2c_adapter_id;
+       uint32_t crtcs;
+       uint8_t polled;
+       bool interlace_allowed;
+       bool doublescan_allowed;
+       struct i2c_board_info info;
+};
+
+int armada_drm_connector_slave_create(struct drm_device *dev,
+       const struct armada_drm_slave_config *);
+
+#endif
index da4a51e..8a784c4 100644 (file)
@@ -6,6 +6,7 @@ config DRM_AST
        select FB_SYS_FILLRECT
        select FB_SYS_IMAGEBLIT
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select DRM_TTM
        help
         Say yes for experimental AST GPU driver. Do not enable
index 32e270d..5137f15 100644 (file)
@@ -211,7 +211,6 @@ static struct drm_driver driver = {
        .minor = DRIVER_MINOR,
        .patchlevel = DRIVER_PATCHLEVEL,
 
-       .gem_init_object = ast_gem_init_object,
        .gem_free_object = ast_gem_free_object,
        .dumb_create = ast_dumb_create,
        .dumb_map_offset = ast_dumb_mmap_offset,
index 8492b68..9833a1b 100644 (file)
@@ -323,7 +323,6 @@ extern int ast_dumb_create(struct drm_file *file,
                           struct drm_device *dev,
                           struct drm_mode_create_dumb *args);
 
-extern int ast_gem_init_object(struct drm_gem_object *obj);
 extern void ast_gem_free_object(struct drm_gem_object *obj);
 extern int ast_dumb_mmap_offset(struct drm_file *file,
                                struct drm_device *dev,
index 7f6152d..af0b868 100644 (file)
@@ -449,12 +449,6 @@ int ast_dumb_create(struct drm_file *file,
        return 0;
 }
 
-int ast_gem_init_object(struct drm_gem_object *obj)
-{
-       BUG();
-       return 0;
-}
-
 void ast_bo_unref(struct ast_bo **bo)
 {
        struct ttm_buffer_object *tbo;
index bf67b22..9864559 100644 (file)
@@ -5,6 +5,7 @@ config DRM_CIRRUS_QEMU
        select FB_SYS_COPYAREA
        select FB_SYS_IMAGEBLIT
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select DRM_TTM
        help
         This is a KMS driver for emulated cirrus device in qemu.
index 138364d..953fc8a 100644 (file)
@@ -97,7 +97,6 @@ static struct drm_driver driver = {
        .major = DRIVER_MAJOR,
        .minor = DRIVER_MINOR,
        .patchlevel = DRIVER_PATCHLEVEL,
-       .gem_init_object = cirrus_gem_init_object,
        .gem_free_object = cirrus_gem_free_object,
        .dumb_create = cirrus_dumb_create,
        .dumb_map_offset = cirrus_dumb_mmap_offset,
index 9b0bb91..b6aded7 100644 (file)
@@ -191,7 +191,6 @@ int cirrus_device_init(struct cirrus_device *cdev,
                      struct pci_dev *pdev,
                      uint32_t flags);
 void cirrus_device_fini(struct cirrus_device *cdev);
-int cirrus_gem_init_object(struct drm_gem_object *obj);
 void cirrus_gem_free_object(struct drm_gem_object *obj);
 int cirrus_dumb_mmap_offset(struct drm_file *file,
                            struct drm_device *dev,
index f130a53..78e76f2 100644 (file)
@@ -255,12 +255,6 @@ int cirrus_dumb_create(struct drm_file *file,
        return 0;
 }
 
-int cirrus_gem_init_object(struct drm_gem_object *obj)
-{
-       BUG();
-       return 0;
-}
-
 void cirrus_bo_unref(struct cirrus_bo **bo)
 {
        struct ttm_buffer_object *tbo;
index 60685b2..adabc3d 100644 (file)
@@ -494,13 +494,12 @@ static struct drm_encoder *cirrus_encoder_init(struct drm_device *dev)
 
 int cirrus_vga_get_modes(struct drm_connector *connector)
 {
-       /* Just add a static list of modes */
-       drm_add_modes_noedid(connector, 640, 480);
-       drm_add_modes_noedid(connector, 800, 600);
-       drm_add_modes_noedid(connector, 1024, 768);
-       drm_add_modes_noedid(connector, 1280, 1024);
+       int count;
 
-       return 4;
+       /* Just add a static list of modes */
+       count = drm_add_modes_noedid(connector, 1280, 1024);
+       drm_set_preferred_mode(connector, 1024, 768);
+       return count;
 }
 
 static int cirrus_vga_mode_valid(struct drm_connector *connector,
index 224ff96..a4b017b 100644 (file)
@@ -334,7 +334,6 @@ int drm_addctx(struct drm_device *dev, void *data,
 
        mutex_lock(&dev->ctxlist_mutex);
        list_add(&ctx_entry->head, &dev->ctxlist);
-       ++dev->ctx_count;
        mutex_unlock(&dev->ctxlist_mutex);
 
        return 0;
@@ -432,7 +431,6 @@ int drm_rmctx(struct drm_device *dev, void *data,
                        if (pos->handle == ctx->handle) {
                                list_del(&pos->head);
                                kfree(pos);
-                               --dev->ctx_count;
                        }
                }
        }
index bff2fa9..d6cf77c 100644 (file)
@@ -202,6 +202,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] =
        { DRM_MODE_CONNECTOR_TV, "TV" },
        { DRM_MODE_CONNECTOR_eDP, "eDP" },
        { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
+       { DRM_MODE_CONNECTOR_DSI, "DSI" },
 };
 
 static const struct drm_prop_enum_list drm_encoder_enum_list[] =
@@ -211,6 +212,7 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] =
        { DRM_MODE_ENCODER_LVDS, "LVDS" },
        { DRM_MODE_ENCODER_TVDAC, "TV" },
        { DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
+       { DRM_MODE_ENCODER_DSI, "DSI" },
 };
 
 void drm_connector_ida_init(void)
@@ -1301,7 +1303,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
 }
 
 /**
- * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode
+ * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode
  * @out: drm_display_mode to return to the user
  * @in: drm_mode_modeinfo to use
  *
@@ -1317,6 +1319,9 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out,
        if (in->clock > INT_MAX || in->vrefresh > INT_MAX)
                return -ERANGE;
 
+       if ((in->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX)
+               return -EINVAL;
+
        out->clock = in->clock;
        out->hdisplay = in->hdisplay;
        out->hsync_start = in->hsync_start;
@@ -1552,7 +1557,7 @@ int drm_mode_getcrtc(struct drm_device *dev,
        obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
                                   DRM_MODE_OBJECT_CRTC);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out;
        }
        crtc = obj_to_crtc(obj);
@@ -1579,6 +1584,19 @@ out:
        return ret;
 }
 
+static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
+                                        const struct drm_file *file_priv)
+{
+       /*
+        * If user-space hasn't configured the driver to expose the stereo 3D
+        * modes, don't expose them.
+        */
+       if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode))
+               return false;
+
+       return true;
+}
+
 /**
  * drm_mode_getconnector - get connector configuration
  * @dev: drm device for the ioctl
@@ -1623,7 +1641,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
        obj = drm_mode_object_find(dev, out_resp->connector_id,
                                   DRM_MODE_OBJECT_CONNECTOR);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out;
        }
        connector = obj_to_connector(obj);
@@ -1644,7 +1662,8 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
        /* delayed so we get modes regardless of pre-fill_modes state */
        list_for_each_entry(mode, &connector->modes, head)
-               mode_count++;
+               if (drm_mode_expose_to_userspace(mode, file_priv))
+                       mode_count++;
 
        out_resp->connector_id = connector->base.id;
        out_resp->connector_type = connector->connector_type;
@@ -1666,6 +1685,9 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
                copied = 0;
                mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
                list_for_each_entry(mode, &connector->modes, head) {
+                       if (!drm_mode_expose_to_userspace(mode, file_priv))
+                               continue;
+
                        drm_crtc_convert_to_umode(&u_mode, mode);
                        if (copy_to_user(mode_ptr + copied,
                                         &u_mode, sizeof(u_mode))) {
@@ -1735,7 +1757,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
        obj = drm_mode_object_find(dev, enc_resp->encoder_id,
                                   DRM_MODE_OBJECT_ENCODER);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out;
        }
        encoder = obj_to_encoder(obj);
@@ -2040,6 +2062,45 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
 }
 EXPORT_SYMBOL(drm_mode_set_config_internal);
 
+/*
+ * Checks that the framebuffer is big enough for the CRTC viewport
+ * (x, y, hdisplay, vdisplay)
+ */
+static int drm_crtc_check_viewport(const struct drm_crtc *crtc,
+                                  int x, int y,
+                                  const struct drm_display_mode *mode,
+                                  const struct drm_framebuffer *fb)
+
+{
+       int hdisplay, vdisplay;
+
+       hdisplay = mode->hdisplay;
+       vdisplay = mode->vdisplay;
+
+       if (drm_mode_is_stereo(mode)) {
+               struct drm_display_mode adjusted = *mode;
+
+               drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE);
+               hdisplay = adjusted.crtc_hdisplay;
+               vdisplay = adjusted.crtc_vdisplay;
+       }
+
+       if (crtc->invert_dimensions)
+               swap(hdisplay, vdisplay);
+
+       if (hdisplay > fb->width ||
+           vdisplay > fb->height ||
+           x > fb->width - hdisplay ||
+           y > fb->height - vdisplay) {
+               DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
+                             fb->width, fb->height, hdisplay, vdisplay, x, y,
+                             crtc->invert_dimensions ? " (inverted)" : "");
+               return -ENOSPC;
+       }
+
+       return 0;
+}
+
 /**
  * drm_mode_setcrtc - set CRTC configuration
  * @dev: drm device for the ioctl
@@ -2080,14 +2141,13 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                                   DRM_MODE_OBJECT_CRTC);
        if (!obj) {
                DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out;
        }
        crtc = obj_to_crtc(obj);
        DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
 
        if (crtc_req->mode_valid) {
-               int hdisplay, vdisplay;
                /* If we have a mode we need a framebuffer. */
                /* If we pass -1, set the mode with the currently bound fb */
                if (crtc_req->fb_id == -1) {
@@ -2104,7 +2164,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                        if (!fb) {
                                DRM_DEBUG_KMS("Unknown FB ID%d\n",
                                                crtc_req->fb_id);
-                               ret = -EINVAL;
+                               ret = -ENOENT;
                                goto out;
                        }
                }
@@ -2123,23 +2183,11 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 
                drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
 
-               hdisplay = mode->hdisplay;
-               vdisplay = mode->vdisplay;
-
-               if (crtc->invert_dimensions)
-                       swap(hdisplay, vdisplay);
-
-               if (hdisplay > fb->width ||
-                   vdisplay > fb->height ||
-                   crtc_req->x > fb->width - hdisplay ||
-                   crtc_req->y > fb->height - vdisplay) {
-                       DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
-                                     fb->width, fb->height,
-                                     hdisplay, vdisplay, crtc_req->x, crtc_req->y,
-                                     crtc->invert_dimensions ? " (inverted)" : "");
-                       ret = -ENOSPC;
+               ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
+                                             mode, fb);
+               if (ret)
                        goto out;
-               }
+
        }
 
        if (crtc_req->count_connectors == 0 && mode) {
@@ -2184,7 +2232,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                        if (!obj) {
                                DRM_DEBUG_KMS("Connector id %d unknown\n",
                                                out_id);
-                               ret = -EINVAL;
+                               ret = -ENOENT;
                                goto out;
                        }
                        connector = obj_to_connector(obj);
@@ -2232,7 +2280,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
        obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj) {
                DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
-               return -EINVAL;
+               return -ENOENT;
        }
        crtc = obj_to_crtc(obj);
 
@@ -2441,6 +2489,8 @@ static int format_check(const struct drm_mode_fb_cmd2 *r)
        case DRM_FORMAT_YVU444:
                return 0;
        default:
+               DRM_DEBUG_KMS("invalid pixel format %s\n",
+                             drm_get_format_name(r->pixel_format));
                return -EINVAL;
        }
 }
@@ -2606,7 +2656,7 @@ fail_lookup:
        mutex_unlock(&dev->mode_config.fb_lock);
        mutex_unlock(&file_priv->fbs_lock);
 
-       return -EINVAL;
+       return -ENOENT;
 }
 
 /**
@@ -2634,7 +2684,7 @@ int drm_mode_getfb(struct drm_device *dev,
 
        fb = drm_framebuffer_lookup(dev, r->fb_id);
        if (!fb)
-               return -EINVAL;
+               return -ENOENT;
 
        r->height = fb->height;
        r->width = fb->width;
@@ -2679,7 +2729,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
 
        fb = drm_framebuffer_lookup(dev, r->fb_id);
        if (!fb)
-               return -EINVAL;
+               return -ENOENT;
 
        num_clips = r->num_clips;
        clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
@@ -3011,7 +3061,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
        drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto done;
        }
        property = obj_to_property(obj);
@@ -3140,7 +3190,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
        drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto done;
        }
        blob = obj_to_blob(obj);
@@ -3301,7 +3351,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
 
        obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out;
        }
        if (!obj->properties) {
@@ -3354,8 +3404,10 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
        drm_modeset_lock_all(dev);
 
        arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
-       if (!arg_obj)
+       if (!arg_obj) {
+               ret = -ENOENT;
                goto out;
+       }
        if (!arg_obj->properties)
                goto out;
 
@@ -3368,8 +3420,10 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
 
        prop_obj = drm_mode_object_find(dev, arg->prop_id,
                                        DRM_MODE_OBJECT_PROPERTY);
-       if (!prop_obj)
+       if (!prop_obj) {
+               ret = -ENOENT;
                goto out;
+       }
        property = obj_to_property(prop_obj);
 
        if (!drm_property_change_is_valid(property, arg->value))
@@ -3454,7 +3508,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
        drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out;
        }
        crtc = obj_to_crtc(obj);
@@ -3513,7 +3567,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
        drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out;
        }
        crtc = obj_to_crtc(obj);
@@ -3556,7 +3610,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        struct drm_framebuffer *fb = NULL, *old_fb = NULL;
        struct drm_pending_vblank_event *e = NULL;
        unsigned long flags;
-       int hdisplay, vdisplay;
        int ret = -EINVAL;
 
        if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
@@ -3568,7 +3621,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 
        obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj)
-               return -EINVAL;
+               return -ENOENT;
        crtc = obj_to_crtc(obj);
 
        mutex_lock(&crtc->mutex);
@@ -3585,25 +3638,14 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                goto out;
 
        fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
-       if (!fb)
+       if (!fb) {
+               ret = -ENOENT;
                goto out;
+       }
 
-       hdisplay = crtc->mode.hdisplay;
-       vdisplay = crtc->mode.vdisplay;
-
-       if (crtc->invert_dimensions)
-               swap(hdisplay, vdisplay);
-
-       if (hdisplay > fb->width ||
-           vdisplay > fb->height ||
-           crtc->x > fb->width - hdisplay ||
-           crtc->y > fb->height - vdisplay) {
-               DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
-                             fb->width, fb->height, hdisplay, vdisplay, crtc->x, crtc->y,
-                             crtc->invert_dimensions ? " (inverted)" : "");
-               ret = -ENOSPC;
+       ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
+       if (ret)
                goto out;
-       }
 
        if (crtc->fb->pixel_format != fb->pixel_format) {
                DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
@@ -3788,7 +3830,8 @@ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
                *bpp = 32;
                break;
        default:
-               DRM_DEBUG_KMS("unsupported pixel format\n");
+               DRM_DEBUG_KMS("unsupported pixel format %s\n",
+                             drm_get_format_name(format));
                *depth = 0;
                *bpp = 0;
                break;
index c722c3b..01361ab 100644 (file)
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_edid.h>
 
+MODULE_AUTHOR("David Airlie, Jesse Barnes");
+MODULE_DESCRIPTION("DRM KMS helper");
+MODULE_LICENSE("GPL and additional rights");
+
 /**
  * drm_helper_move_panel_connectors_to_head() - move panels to the front in the
  *                                             connector list
@@ -76,7 +80,8 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
 {
        struct drm_display_mode *mode;
 
-       if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE))
+       if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE |
+                     DRM_MODE_FLAG_3D_MASK))
                return;
 
        list_for_each_entry(mode, &connector->modes, head) {
@@ -86,6 +91,9 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
                if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) &&
                                !(flags & DRM_MODE_FLAG_DBLSCAN))
                        mode->status = MODE_NO_DBLESCAN;
+               if ((mode->flags & DRM_MODE_FLAG_3D_MASK) &&
+                               !(flags & DRM_MODE_FLAG_3D_MASK))
+                       mode->status = MODE_NO_STEREO;
        }
 
        return;
@@ -105,9 +113,9 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
  * then culled (based on validity and the @maxX, @maxY parameters) and put into
  * the normal modes list.
  *
- * Intended to be use as a generic implementation of the ->probe() @connector
- * callback for drivers that use the crtc helpers for output mode filtering and
- * detection.
+ * Intended to be use as a generic implementation of the ->fill_modes()
+ * @connector vfunc for drivers that use the crtc helpers for output mode
+ * filtering and detection.
  *
  * RETURNS:
  * Number of modes found on @connector.
@@ -175,6 +183,8 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
                mode_flags |= DRM_MODE_FLAG_INTERLACE;
        if (connector->doublescan_allowed)
                mode_flags |= DRM_MODE_FLAG_DBLSCAN;
+       if (connector->stereo_allowed)
+               mode_flags |= DRM_MODE_FLAG_3D_MASK;
        drm_mode_validate_flag(connector, mode_flags);
 
        list_for_each_entry(mode, &connector->modes, head) {
@@ -395,22 +405,25 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                              struct drm_framebuffer *old_fb)
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode;
+       struct drm_display_mode *adjusted_mode, saved_mode;
        struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
        struct drm_encoder_helper_funcs *encoder_funcs;
        int saved_x, saved_y;
+       bool saved_enabled;
        struct drm_encoder *encoder;
        bool ret = true;
 
+       saved_enabled = crtc->enabled;
        crtc->enabled = drm_helper_crtc_in_use(crtc);
        if (!crtc->enabled)
                return true;
 
        adjusted_mode = drm_mode_duplicate(dev, mode);
-       if (!adjusted_mode)
+       if (!adjusted_mode) {
+               crtc->enabled = saved_enabled;
                return false;
+       }
 
-       saved_hwmode = crtc->hwmode;
        saved_mode = crtc->mode;
        saved_x = crtc->x;
        saved_y = crtc->y;
@@ -529,7 +542,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
 done:
        drm_mode_destroy(dev, adjusted_mode);
        if (!ret) {
-               crtc->hwmode = saved_hwmode;
+               crtc->enabled = saved_enabled;
                crtc->mode = saved_mode;
                crtc->x = saved_x;
                crtc->y = saved_y;
@@ -557,6 +570,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
                                continue;
 
                        connector->encoder = NULL;
+
+                       /*
+                        * drm_helper_disable_unused_functions() ought to be
+                        * doing this, but since we've decoupled the encoder
+                        * from the connector above, the required connection
+                        * between them is henceforth no longer available.
+                        */
+                       connector->dpms = DRM_MODE_DPMS_OFF;
                }
        }
 
@@ -583,9 +604,8 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
 int drm_crtc_helper_set_config(struct drm_mode_set *set)
 {
        struct drm_device *dev;
-       struct drm_crtc *save_crtcs, *new_crtc, *crtc;
+       struct drm_crtc *new_crtc;
        struct drm_encoder *save_encoders, *new_encoder, *encoder;
-       struct drm_framebuffer *old_fb = NULL;
        bool mode_changed = false; /* if true do a full mode set */
        bool fb_changed = false; /* if true and !mode_changed just do a flip */
        struct drm_connector *save_connectors, *connector;
@@ -621,38 +641,28 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
 
        dev = set->crtc->dev;
 
-       /* Allocate space for the backup of all (non-pointer) crtc, encoder and
-        * connector data. */
-       save_crtcs = kzalloc(dev->mode_config.num_crtc *
-                            sizeof(struct drm_crtc), GFP_KERNEL);
-       if (!save_crtcs)
-               return -ENOMEM;
-
+       /*
+        * Allocate space for the backup of all (non-pointer) encoder and
+        * connector data.
+        */
        save_encoders = kzalloc(dev->mode_config.num_encoder *
                                sizeof(struct drm_encoder), GFP_KERNEL);
-       if (!save_encoders) {
-               kfree(save_crtcs);
+       if (!save_encoders)
                return -ENOMEM;
-       }
 
        save_connectors = kzalloc(dev->mode_config.num_connector *
                                sizeof(struct drm_connector), GFP_KERNEL);
        if (!save_connectors) {
-               kfree(save_crtcs);
                kfree(save_encoders);
                return -ENOMEM;
        }
 
-       /* Copy data. Note that driver private data is not affected.
+       /*
+        * Copy data. Note that driver private data is not affected.
         * Should anything bad happen only the expected state is
         * restored, not the drivers personal bookkeeping.
         */
        count = 0;
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               save_crtcs[count++] = *crtc;
-       }
-
-       count = 0;
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                save_encoders[count++] = *encoder;
        }
@@ -775,19 +785,17 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                mode_changed = true;
 
        if (mode_changed) {
-               set->crtc->enabled = drm_helper_crtc_in_use(set->crtc);
-               if (set->crtc->enabled) {
+               if (drm_helper_crtc_in_use(set->crtc)) {
                        DRM_DEBUG_KMS("attempting to set mode from"
                                        " userspace\n");
                        drm_mode_debug_printmodeline(set->mode);
-                       old_fb = set->crtc->fb;
                        set->crtc->fb = set->fb;
                        if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
                                                      set->x, set->y,
-                                                     old_fb)) {
+                                                     save_set.fb)) {
                                DRM_ERROR("failed to set mode on [CRTC:%d]\n",
                                          set->crtc->base.id);
-                               set->crtc->fb = old_fb;
+                               set->crtc->fb = save_set.fb;
                                ret = -EINVAL;
                                goto fail;
                        }
@@ -802,31 +810,24 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
        } else if (fb_changed) {
                set->crtc->x = set->x;
                set->crtc->y = set->y;
-
-               old_fb = set->crtc->fb;
-               if (set->crtc->fb != set->fb)
-                       set->crtc->fb = set->fb;
+               set->crtc->fb = set->fb;
                ret = crtc_funcs->mode_set_base(set->crtc,
-                                               set->x, set->y, old_fb);
+                                               set->x, set->y, save_set.fb);
                if (ret != 0) {
-                       set->crtc->fb = old_fb;
+                       set->crtc->x = save_set.x;
+                       set->crtc->y = save_set.y;
+                       set->crtc->fb = save_set.fb;
                        goto fail;
                }
        }
 
        kfree(save_connectors);
        kfree(save_encoders);
-       kfree(save_crtcs);
        return 0;
 
 fail:
        /* Restore all previous data. */
        count = 0;
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               *crtc = save_crtcs[count++];
-       }
-
-       count = 0;
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                *encoder = save_encoders[count++];
        }
@@ -844,7 +845,6 @@ fail:
 
        kfree(save_connectors);
        kfree(save_encoders);
-       kfree(save_crtcs);
        return ret;
 }
 EXPORT_SYMBOL(drm_crtc_helper_set_config);
@@ -1125,14 +1125,14 @@ void drm_kms_helper_poll_fini(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_kms_helper_poll_fini);
 
-void drm_helper_hpd_irq_event(struct drm_device *dev)
+bool drm_helper_hpd_irq_event(struct drm_device *dev)
 {
        struct drm_connector *connector;
        enum drm_connector_status old_status;
        bool changed = false;
 
        if (!dev->mode_config.poll_enabled)
-               return;
+               return false;
 
        mutex_lock(&dev->mode_config.mutex);
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
@@ -1157,5 +1157,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev)
 
        if (changed)
                drm_kms_helper_hotplug_event(dev);
+
+       return changed;
 }
 EXPORT_SYMBOL(drm_helper_hpd_irq_event);
index a05087c..b4b51d4 100644 (file)
@@ -42,7 +42,7 @@
  * Initialization, etc.
  **************************************************/
 
-static struct drm_info_list drm_debugfs_list[] = {
+static const struct drm_info_list drm_debugfs_list[] = {
        {"name", drm_name_info, 0},
        {"vm", drm_vm_info, 0},
        {"clients", drm_clients_info, 0},
@@ -84,7 +84,7 @@ static const struct file_operations drm_debugfs_fops = {
  * Create a given set of debugfs files represented by an array of
  * gdm_debugfs_lists in the given root directory.
  */
-int drm_debugfs_create_files(struct drm_info_list *files, int count,
+int drm_debugfs_create_files(const struct drm_info_list *files, int count,
                             struct dentry *root, struct drm_minor *minor)
 {
        struct drm_device *dev = minor->dev;
@@ -188,7 +188,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id,
  *
  * Remove all debugfs entries created by debugfs_init().
  */
-int drm_debugfs_remove_files(struct drm_info_list *files, int count,
+int drm_debugfs_remove_files(const struct drm_info_list *files, int count,
                             struct drm_minor *minor)
 {
        struct list_head *pos, *q;
index 89e1966..9e978aa 100644 (file)
@@ -228,12 +228,12 @@ i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
 EXPORT_SYMBOL(i2c_dp_aux_add_bus);
 
 /* Helpers for DP link training */
-static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r)
+static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
 {
        return link_status[r - DP_LANE0_1_STATUS];
 }
 
-static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE],
+static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
                             int lane)
 {
        int i = DP_LANE0_1_STATUS + (lane >> 1);
@@ -242,7 +242,7 @@ static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE],
        return (l >> s) & 0xf;
 }
 
-bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE],
+bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
                          int lane_count)
 {
        u8 lane_align;
@@ -262,7 +262,7 @@ bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE],
 }
 EXPORT_SYMBOL(drm_dp_channel_eq_ok);
 
-bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE],
+bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
                              int lane_count)
 {
        int lane;
@@ -277,7 +277,7 @@ bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE],
 }
 EXPORT_SYMBOL(drm_dp_clock_recovery_ok);
 
-u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE],
+u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
                                     int lane)
 {
        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
@@ -290,7 +290,7 @@ u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE],
 }
 EXPORT_SYMBOL(drm_dp_get_adjust_request_voltage);
 
-u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
+u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
                                          int lane)
 {
        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
@@ -303,7 +303,7 @@ u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
 }
 EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis);
 
-void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
+void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
                udelay(100);
        else
@@ -311,7 +311,7 @@ void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
 }
 EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);
 
-void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
+void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
                udelay(400);
        else
index fe58d08..d9137e4 100644 (file)
@@ -69,6 +69,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0),
        DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER),
 
        DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
@@ -170,76 +171,6 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
 
 #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
 
-/**
- * drm_legacy_dev_reinit
- *
- * Reinitializes a legacy/ums drm device in it's lastclose function.
- */
-static void drm_legacy_dev_reinit(struct drm_device *dev)
-{
-       int i;
-
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return;
-
-       atomic_set(&dev->ioctl_count, 0);
-       atomic_set(&dev->vma_count, 0);
-
-       for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
-               atomic_set(&dev->counts[i], 0);
-
-       dev->sigdata.lock = NULL;
-
-       dev->context_flag = 0;
-       dev->last_context = 0;
-       dev->if_version = 0;
-}
-
-/**
- * Take down the DRM device.
- *
- * \param dev DRM device structure.
- *
- * Frees every resource in \p dev.
- *
- * \sa drm_device
- */
-int drm_lastclose(struct drm_device * dev)
-{
-       struct drm_vma_entry *vma, *vma_temp;
-
-       DRM_DEBUG("\n");
-
-       if (dev->driver->lastclose)
-               dev->driver->lastclose(dev);
-       DRM_DEBUG("driver lastclose completed\n");
-
-       if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET))
-               drm_irq_uninstall(dev);
-
-       mutex_lock(&dev->struct_mutex);
-
-       drm_agp_clear(dev);
-
-       drm_legacy_sg_cleanup(dev);
-
-       /* Clear vma list (only built for debugging) */
-       list_for_each_entry_safe(vma, vma_temp, &dev->vmalist, head) {
-               list_del(&vma->head);
-               kfree(vma);
-       }
-
-       drm_legacy_dma_takedown(dev);
-
-       dev->dev_mapping = NULL;
-       mutex_unlock(&dev->struct_mutex);
-
-       drm_legacy_dev_reinit(dev);
-
-       DRM_DEBUG("lastclose completed\n");
-       return 0;
-}
-
 /** File operations structure */
 static const struct file_operations drm_stub_fops = {
        .owner = THIS_MODULE,
@@ -385,7 +316,6 @@ long drm_ioctl(struct file *filp,
                return -ENODEV;
 
        atomic_inc(&dev->ioctl_count);
-       atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
        ++file_priv->ioctl_count;
 
        if ((nr >= DRM_CORE_IOCTL_COUNT) &&
@@ -473,7 +403,7 @@ long drm_ioctl(struct file *filp,
 
       err_i1:
        if (!ioctl)
-               DRM_DEBUG("invalid iotcl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
+               DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
                          task_pid_nr(current),
                          (long)old_encode_dev(file_priv->minor->device),
                          file_priv->authenticated, cmd, nr);
index 830f750..fb7cf0e 100644 (file)
@@ -458,6 +458,15 @@ static const struct drm_display_mode drm_dmt_modes[] = {
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
 };
 
+/*
+ * These more or less come from the DMT spec.  The 720x400 modes are
+ * inferred from historical 80x25 practice.  The 640x480@67 and 832x624@75
+ * modes are old-school Mac modes.  The EDID spec says the 1152x864@75 mode
+ * should be 1152x870, again for the Mac, but instead we use the x864 DMT
+ * mode.
+ *
+ * The DMT modes have been fact-checked; the rest are mild guesses.
+ */
 static const struct drm_display_mode edid_est_modes[] = {
        { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
                   968, 1056, 0, 600, 601, 605, 628, 0,
@@ -560,7 +569,7 @@ static const struct minimode est3_modes[] = {
        { 1600, 1200, 75, 0 },
        { 1600, 1200, 85, 0 },
        { 1792, 1344, 60, 0 },
-       { 1792, 1344, 85, 0 },
+       { 1792, 1344, 75, 0 },
        { 1856, 1392, 60, 0 },
        { 1856, 1392, 75, 0 },
        { 1920, 1200, 60, 1 },
@@ -1264,6 +1273,18 @@ struct edid *drm_get_edid(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_get_edid);
 
+/**
+ * drm_edid_duplicate - duplicate an EDID and the extensions
+ * @edid: EDID to duplicate
+ *
+ * Return duplicate edid or NULL on allocation failure.
+ */
+struct edid *drm_edid_duplicate(const struct edid *edid)
+{
+       return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL);
+}
+EXPORT_SYMBOL(drm_edid_duplicate);
+
 /*** EDID parsing ***/
 
 /**
@@ -1308,7 +1329,7 @@ static u32 edid_get_quirks(struct edid *edid)
 }
 
 #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
-#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh))
+#define MODE_REFRESH_DIFF(c,t) (abs((c) - (t)))
 
 /**
  * edid_fixup_preferred - set preferred modes based on quirk list
@@ -1323,6 +1344,7 @@ static void edid_fixup_preferred(struct drm_connector *connector,
 {
        struct drm_display_mode *t, *cur_mode, *preferred_mode;
        int target_refresh = 0;
+       int cur_vrefresh, preferred_vrefresh;
 
        if (list_empty(&connector->probed_modes))
                return;
@@ -1345,10 +1367,14 @@ static void edid_fixup_preferred(struct drm_connector *connector,
                if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))
                        preferred_mode = cur_mode;
 
+               cur_vrefresh = cur_mode->vrefresh ?
+                       cur_mode->vrefresh : drm_mode_vrefresh(cur_mode);
+               preferred_vrefresh = preferred_mode->vrefresh ?
+                       preferred_mode->vrefresh : drm_mode_vrefresh(preferred_mode);
                /* At a given size, try to get closest to target refresh */
                if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) &&
-                   MODE_REFRESH_DIFF(cur_mode, target_refresh) <
-                   MODE_REFRESH_DIFF(preferred_mode, target_refresh)) {
+                   MODE_REFRESH_DIFF(cur_vrefresh, target_refresh) <
+                   MODE_REFRESH_DIFF(preferred_vrefresh, target_refresh)) {
                        preferred_mode = cur_mode;
                }
        }
@@ -2068,7 +2094,7 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
        u8 *est = ((u8 *)timing) + 5;
 
        for (i = 0; i < 6; i++) {
-               for (j = 7; j > 0; j--) {
+               for (j = 7; j >= 0; j--) {
                        m = (i * 8) + (7 - j);
                        if (m >= ARRAY_SIZE(est3_modes))
                                break;
@@ -2404,7 +2430,7 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
 
                if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||
                     KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) &&
-                   drm_mode_equal_no_clocks(to_match, cea_mode))
+                   drm_mode_equal_no_clocks_no_stereo(to_match, cea_mode))
                        return mode + 1;
        }
        return 0;
@@ -2453,7 +2479,7 @@ static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
 
                if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||
                     KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) &&
-                   drm_mode_equal_no_clocks(to_match, hdmi_mode))
+                   drm_mode_equal_no_clocks_no_stereo(to_match, hdmi_mode))
                        return mode + 1;
        }
        return 0;
@@ -2507,6 +2533,9 @@ add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)
                if (!newmode)
                        continue;
 
+               /* Carry over the stereo flags */
+               newmode->flags |= mode->flags & DRM_MODE_FLAG_3D_MASK;
+
                /*
                 * The current mode could be either variant. Make
                 * sure to pick the "other" clock for the new mode.
@@ -2553,20 +2582,151 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len)
        return modes;
 }
 
+struct stereo_mandatory_mode {
+       int width, height, vrefresh;
+       unsigned int flags;
+};
+
+static const struct stereo_mandatory_mode stereo_mandatory_modes[] = {
+       { 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
+       { 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING },
+       { 1920, 1080, 50,
+         DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF },
+       { 1920, 1080, 60,
+         DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF },
+       { 1280, 720,  50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
+       { 1280, 720,  50, DRM_MODE_FLAG_3D_FRAME_PACKING },
+       { 1280, 720,  60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
+       { 1280, 720,  60, DRM_MODE_FLAG_3D_FRAME_PACKING }
+};
+
+static bool
+stereo_match_mandatory(const struct drm_display_mode *mode,
+                      const struct stereo_mandatory_mode *stereo_mode)
+{
+       unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
+
+       return mode->hdisplay == stereo_mode->width &&
+              mode->vdisplay == stereo_mode->height &&
+              interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) &&
+              drm_mode_vrefresh(mode) == stereo_mode->vrefresh;
+}
+
+static int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       const struct drm_display_mode *mode;
+       struct list_head stereo_modes;
+       int modes = 0, i;
+
+       INIT_LIST_HEAD(&stereo_modes);
+
+       list_for_each_entry(mode, &connector->probed_modes, head) {
+               for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) {
+                       const struct stereo_mandatory_mode *mandatory;
+                       struct drm_display_mode *new_mode;
+
+                       if (!stereo_match_mandatory(mode,
+                                                   &stereo_mandatory_modes[i]))
+                               continue;
+
+                       mandatory = &stereo_mandatory_modes[i];
+                       new_mode = drm_mode_duplicate(dev, mode);
+                       if (!new_mode)
+                               continue;
+
+                       new_mode->flags |= mandatory->flags;
+                       list_add_tail(&new_mode->head, &stereo_modes);
+                       modes++;
+               }
+       }
+
+       list_splice_tail(&stereo_modes, &connector->probed_modes);
+
+       return modes;
+}
+
+static int add_hdmi_mode(struct drm_connector *connector, u8 vic)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_display_mode *newmode;
+
+       vic--; /* VICs start at 1 */
+       if (vic >= ARRAY_SIZE(edid_4k_modes)) {
+               DRM_ERROR("Unknown HDMI VIC: %d\n", vic);
+               return 0;
+       }
+
+       newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]);
+       if (!newmode)
+               return 0;
+
+       drm_mode_probed_add(connector, newmode);
+
+       return 1;
+}
+
+static int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
+                              const u8 *video_db, u8 video_len, u8 video_index)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_display_mode *newmode;
+       int modes = 0;
+       u8 cea_mode;
+
+       if (video_db == NULL || video_index > video_len)
+               return 0;
+
+       /* CEA modes are numbered 1..127 */
+       cea_mode = (video_db[video_index] & 127) - 1;
+       if (cea_mode >= ARRAY_SIZE(edid_cea_modes))
+               return 0;
+
+       if (structure & (1 << 0)) {
+               newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]);
+               if (newmode) {
+                       newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING;
+                       drm_mode_probed_add(connector, newmode);
+                       modes++;
+               }
+       }
+       if (structure & (1 << 6)) {
+               newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]);
+               if (newmode) {
+                       newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM;
+                       drm_mode_probed_add(connector, newmode);
+                       modes++;
+               }
+       }
+       if (structure & (1 << 8)) {
+               newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]);
+               if (newmode) {
+                       newmode->flags = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF;
+                       drm_mode_probed_add(connector, newmode);
+                       modes++;
+               }
+       }
+
+       return modes;
+}
+
 /*
  * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block
  * @connector: connector corresponding to the HDMI sink
  * @db: start of the CEA vendor specific block
  * @len: length of the CEA block payload, ie. one can access up to db[len]
  *
- * Parses the HDMI VSDB looking for modes to add to @connector.
+ * Parses the HDMI VSDB looking for modes to add to @connector. This function
+ * also adds the stereo 3d modes when applicable.
  */
 static int
-do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len)
+do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
+                  const u8 *video_db, u8 video_len)
 {
-       struct drm_device *dev = connector->dev;
-       int modes = 0, offset = 0, i;
-       u8 vic_len;
+       int modes = 0, offset = 0, i, multi_present = 0;
+       u8 vic_len, hdmi_3d_len = 0;
+       u16 mask;
+       u16 structure_all;
 
        if (len < 8)
                goto out;
@@ -2585,30 +2745,56 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len)
 
        /* the declared length is not long enough for the 2 first bytes
         * of additional video format capabilities */
-       offset += 2;
-       if (len < (8 + offset))
+       if (len < (8 + offset + 2))
                goto out;
 
+       /* 3D_Present */
+       offset++;
+       if (db[8 + offset] & (1 << 7)) {
+               modes += add_hdmi_mandatory_stereo_modes(connector);
+
+               /* 3D_Multi_present */
+               multi_present = (db[8 + offset] & 0x60) >> 5;
+       }
+
+       offset++;
        vic_len = db[8 + offset] >> 5;
+       hdmi_3d_len = db[8 + offset] & 0x1f;
 
        for (i = 0; i < vic_len && len >= (9 + offset + i); i++) {
-               struct drm_display_mode *newmode;
                u8 vic;
 
                vic = db[9 + offset + i];
+               modes += add_hdmi_mode(connector, vic);
+       }
+       offset += 1 + vic_len;
 
-               vic--; /* VICs start at 1 */
-               if (vic >= ARRAY_SIZE(edid_4k_modes)) {
-                       DRM_ERROR("Unknown HDMI VIC: %d\n", vic);
-                       continue;
-               }
+       if (!(multi_present == 1 || multi_present == 2))
+               goto out;
 
-               newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]);
-               if (!newmode)
-                       continue;
+       if ((multi_present == 1 && len < (9 + offset)) ||
+           (multi_present == 2 && len < (11 + offset)))
+               goto out;
 
-               drm_mode_probed_add(connector, newmode);
-               modes++;
+       if ((multi_present == 1 && hdmi_3d_len < 2) ||
+           (multi_present == 2 && hdmi_3d_len < 4))
+               goto out;
+
+       /* 3D_Structure_ALL */
+       structure_all = (db[8 + offset] << 8) | db[9 + offset];
+
+       /* check if 3D_MASK is present */
+       if (multi_present == 2)
+               mask = (db[10 + offset] << 8) | db[11 + offset];
+       else
+               mask = 0xffff;
+
+       for (i = 0; i < 16; i++) {
+               if (mask & (1 << i))
+                       modes += add_3d_struct_modes(connector,
+                                                    structure_all,
+                                                    video_db,
+                                                    video_len, i);
        }
 
 out:
@@ -2668,8 +2854,8 @@ static int
 add_cea_modes(struct drm_connector *connector, struct edid *edid)
 {
        const u8 *cea = drm_find_cea_extension(edid);
-       const u8 *db;
-       u8 dbl;
+       const u8 *db, *hdmi = NULL, *video = NULL;
+       u8 dbl, hdmi_len, video_len = 0;
        int modes = 0;
 
        if (cea && cea_revision(cea) >= 3) {
@@ -2682,13 +2868,26 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
                        db = &cea[i];
                        dbl = cea_db_payload_len(db);
 
-                       if (cea_db_tag(db) == VIDEO_BLOCK)
-                               modes += do_cea_modes(connector, db + 1, dbl);
-                       else if (cea_db_is_hdmi_vsdb(db))
-                               modes += do_hdmi_vsdb_modes(connector, db, dbl);
+                       if (cea_db_tag(db) == VIDEO_BLOCK) {
+                               video = db + 1;
+                               video_len = dbl;
+                               modes += do_cea_modes(connector, video, dbl);
+                       }
+                       else if (cea_db_is_hdmi_vsdb(db)) {
+                               hdmi = db;
+                               hdmi_len = dbl;
+                       }
                }
        }
 
+       /*
+        * We parse the HDMI VSDB after having added the cea modes as we will
+        * be patching their flags when the sink supports stereo 3D.
+        */
+       if (hdmi)
+               modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video,
+                                           video_len);
+
        return modes;
 }
 
@@ -3288,6 +3487,19 @@ int drm_add_modes_noedid(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_add_modes_noedid);
 
+void drm_set_preferred_mode(struct drm_connector *connector,
+                          int hpref, int vpref)
+{
+       struct drm_display_mode *mode;
+
+       list_for_each_entry(mode, &connector->probed_modes, head) {
+               if (drm_mode_width(mode)  == hpref &&
+                   drm_mode_height(mode) == vpref)
+                       mode->type |= DRM_MODE_TYPE_PREFERRED;
+       }
+}
+EXPORT_SYMBOL(drm_set_preferred_mode);
+
 /**
  * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with
  *                                              data from a DRM display mode
@@ -3321,6 +3533,33 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
 }
 EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode);
 
+static enum hdmi_3d_structure
+s3d_structure_from_display_mode(const struct drm_display_mode *mode)
+{
+       u32 layout = mode->flags & DRM_MODE_FLAG_3D_MASK;
+
+       switch (layout) {
+       case DRM_MODE_FLAG_3D_FRAME_PACKING:
+               return HDMI_3D_STRUCTURE_FRAME_PACKING;
+       case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE:
+               return HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE;
+       case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE:
+               return HDMI_3D_STRUCTURE_LINE_ALTERNATIVE;
+       case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL:
+               return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL;
+       case DRM_MODE_FLAG_3D_L_DEPTH:
+               return HDMI_3D_STRUCTURE_L_DEPTH;
+       case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH:
+               return HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH;
+       case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
+               return HDMI_3D_STRUCTURE_TOP_AND_BOTTOM;
+       case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
+               return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF;
+       default:
+               return HDMI_3D_STRUCTURE_INVALID;
+       }
+}
+
 /**
  * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with
  * data from a DRM display mode
@@ -3338,20 +3577,29 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
                                            const struct drm_display_mode *mode)
 {
        int err;
+       u32 s3d_flags;
        u8 vic;
 
        if (!frame || !mode)
                return -EINVAL;
 
        vic = drm_match_hdmi_mode(mode);
-       if (!vic)
+       s3d_flags = mode->flags & DRM_MODE_FLAG_3D_MASK;
+
+       if (!vic && !s3d_flags)
+               return -EINVAL;
+
+       if (vic && s3d_flags)
                return -EINVAL;
 
        err = hdmi_vendor_infoframe_init(frame);
        if (err < 0)
                return err;
 
-       frame->vic = vic;
+       if (vic)
+               frame->vic = vic;
+       else
+               frame->s3d_struct = s3d_structure_from_display_mode(mode);
 
        return 0;
 }
index 271b42b..9081172 100644 (file)
@@ -32,7 +32,7 @@ MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
        "from built-in data or /lib/firmware instead. ");
 
 #define GENERIC_EDIDS 5
-static char *generic_edid_name[GENERIC_EDIDS] = {
+static const char *generic_edid_name[GENERIC_EDIDS] = {
        "edid/1024x768.bin",
        "edid/1280x1024.bin",
        "edid/1600x1200.bin",
@@ -40,7 +40,7 @@ static char *generic_edid_name[GENERIC_EDIDS] = {
        "edid/1920x1080.bin",
 };
 
-static u8 generic_edid[GENERIC_EDIDS][128] = {
+static const u8 generic_edid[GENERIC_EDIDS][128] = {
        {
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
        0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -133,63 +133,68 @@ static u8 generic_edid[GENERIC_EDIDS][128] = {
        },
 };
 
+static int edid_size(const u8 *edid, int data_size)
+{
+       if (data_size < EDID_LENGTH)
+               return 0;
+
+       return (edid[0x7e] + 1) * EDID_LENGTH;
+}
+
 static u8 *edid_load(struct drm_connector *connector, const char *name,
                        const char *connector_name)
 {
-       const struct firmware *fw;
-       struct platform_device *pdev;
-       u8 *fwdata = NULL, *edid, *new_edid;
-       int fwsize, expected;
-       int builtin = 0, err = 0;
+       const struct firmware *fw = NULL;
+       const u8 *fwdata;
+       u8 *edid;
+       int fwsize, builtin;
        int i, valid_extensions = 0;
        bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
 
-       pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
-       if (IS_ERR(pdev)) {
-               DRM_ERROR("Failed to register EDID firmware platform device "
-                   "for connector \"%s\"\n", connector_name);
-               err = -EINVAL;
-               goto out;
-       }
-
-       err = request_firmware(&fw, name, &pdev->dev);
-       platform_device_unregister(pdev);
-
-       if (err) {
-               i = 0;
-               while (i < GENERIC_EDIDS && strcmp(name, generic_edid_name[i]))
-                       i++;
-               if (i < GENERIC_EDIDS) {
-                       err = 0;
-                       builtin = 1;
+       builtin = 0;
+       for (i = 0; i < GENERIC_EDIDS; i++) {
+               if (strcmp(name, generic_edid_name[i]) == 0) {
                        fwdata = generic_edid[i];
                        fwsize = sizeof(generic_edid[i]);
+                       builtin = 1;
+                       break;
                }
        }
+       if (!builtin) {
+               struct platform_device *pdev;
+               int err;
 
-       if (err) {
-               DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
-                   name, err);
-               goto out;
-       }
+               pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
+               if (IS_ERR(pdev)) {
+                       DRM_ERROR("Failed to register EDID firmware platform device "
+                                 "for connector \"%s\"\n", connector_name);
+                       return ERR_CAST(pdev);
+               }
+
+               err = request_firmware(&fw, name, &pdev->dev);
+               platform_device_unregister(pdev);
+               if (err) {
+                       DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
+                                 name, err);
+                       return ERR_PTR(err);
+               }
 
-       if (fwdata == NULL) {
-               fwdata = (u8 *) fw->data;
+               fwdata = fw->data;
                fwsize = fw->size;
        }
 
-       expected = (fwdata[0x7e] + 1) * EDID_LENGTH;
-       if (expected != fwsize) {
+       if (edid_size(fwdata, fwsize) != fwsize) {
                DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
-                   "(expected %d, got %d)\n", name, expected, (int) fwsize);
-               err = -EINVAL;
-               goto relfw_out;
+                         "(expected %d, got %d\n", name,
+                         edid_size(fwdata, fwsize), (int)fwsize);
+               edid = ERR_PTR(-EINVAL);
+               goto out;
        }
 
        edid = kmemdup(fwdata, fwsize, GFP_KERNEL);
        if (edid == NULL) {
-               err = -ENOMEM;
-               goto relfw_out;
+               edid = ERR_PTR(-ENOMEM);
+               goto out;
        }
 
        if (!drm_edid_block_valid(edid, 0, print_bad_edid)) {
@@ -197,8 +202,8 @@ static u8 *edid_load(struct drm_connector *connector, const char *name,
                DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
                    name);
                kfree(edid);
-               err = -EINVAL;
-               goto relfw_out;
+               edid = ERR_PTR(-EINVAL);
+               goto out;
        }
 
        for (i = 1; i <= edid[0x7e]; i++) {
@@ -210,19 +215,18 @@ static u8 *edid_load(struct drm_connector *connector, const char *name,
        }
 
        if (valid_extensions != edid[0x7e]) {
+               u8 *new_edid;
+
                edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
                DRM_INFO("Found %d valid extensions instead of %d in EDID data "
                    "\"%s\" for connector \"%s\"\n", valid_extensions,
                    edid[0x7e], name, connector_name);
                edid[0x7e] = valid_extensions;
+
                new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
-                   GFP_KERNEL);
-               if (new_edid == NULL) {
-                       err = -ENOMEM;
-                       kfree(edid);
-                       goto relfw_out;
-               }
-               edid = new_edid;
+                                   GFP_KERNEL);
+               if (new_edid)
+                       edid = new_edid;
        }
 
        DRM_INFO("Got %s EDID base block and %d extension%s from "
@@ -230,13 +234,9 @@ static u8 *edid_load(struct drm_connector *connector, const char *name,
            "external", valid_extensions, valid_extensions == 1 ? "" : "s",
            name, connector_name);
 
-relfw_out:
-       release_firmware(fw);
-
 out:
-       if (err)
-               return ERR_PTR(err);
-
+       if (fw)
+               release_firmware(fw);
        return edid;
 }
 
index 0cfb60f..d18b88b 100644 (file)
@@ -67,12 +67,12 @@ int drm_i2c_encoder_init(struct drm_device *dev,
                goto fail;
        }
 
-       if (!client->driver) {
+       if (!client->dev.driver) {
                err = -ENODEV;
                goto fail_unregister;
        }
 
-       module = client->driver->driver.owner;
+       module = client->dev.driver->owner;
        if (!try_module_get(module)) {
                err = -ENODEV;
                goto fail_unregister;
@@ -80,7 +80,7 @@ int drm_i2c_encoder_init(struct drm_device *dev,
 
        encoder->bus_priv = client;
 
-       encoder_drv = to_drm_i2c_encoder_driver(client->driver);
+       encoder_drv = to_drm_i2c_encoder_driver(to_i2c_driver(client->dev.driver));
 
        err = encoder_drv->encoder_init(client, dev, encoder);
        if (err)
@@ -111,7 +111,7 @@ void drm_i2c_encoder_destroy(struct drm_encoder *drm_encoder)
 {
        struct drm_encoder_slave *encoder = to_encoder_slave(drm_encoder);
        struct i2c_client *client = drm_i2c_encoder_get_client(drm_encoder);
-       struct module *module = client->driver->driver.owner;
+       struct module *module = client->dev.driver->owner;
 
        i2c_unregister_device(client);
        encoder->bus_priv = NULL;
index 3d13ca6..0a19401 100644 (file)
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
 
-MODULE_AUTHOR("David Airlie, Jesse Barnes");
-MODULE_DESCRIPTION("DRM KMS helper");
-MODULE_LICENSE("GPL and additional rights");
-
 static LIST_HEAD(kernel_fb_helper_list);
 
 /**
@@ -844,7 +840,6 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
        struct drm_fb_helper *fb_helper = info->par;
        struct drm_device *dev = fb_helper->dev;
        struct drm_mode_set *modeset;
-       struct drm_crtc *crtc;
        int ret = 0;
        int i;
 
@@ -855,8 +850,6 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
        }
 
        for (i = 0; i < fb_helper->crtc_count; i++) {
-               crtc = fb_helper->crtc_info[i].mode_set.crtc;
-
                modeset = &fb_helper->crtc_info[i].mode_set;
 
                modeset->x = var->xoffset;
@@ -1352,7 +1345,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
        struct drm_connector *connector;
        struct drm_connector_helper_funcs *connector_funcs;
        struct drm_encoder *encoder;
-       struct drm_fb_helper_crtc *best_crtc;
        int my_score, best_score, score;
        struct drm_fb_helper_crtc **crtcs, *crtc;
        struct drm_fb_helper_connector *fb_helper_conn;
@@ -1364,7 +1356,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
        connector = fb_helper_conn->connector;
 
        best_crtcs[n] = NULL;
-       best_crtc = NULL;
        best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
        if (modes[n] == NULL)
                return best_score;
@@ -1413,7 +1404,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
                score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
                                                  width, height);
                if (score > best_score) {
-                       best_crtc = crtc;
                        best_score = score;
                        memcpy(best_crtcs, crtcs,
                               dev->mode_config.num_connector *
@@ -1580,8 +1570,7 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
 int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
 {
        struct drm_device *dev = fb_helper->dev;
-       int count = 0;
-       u32 max_width, max_height, bpp_sel;
+       u32 max_width, max_height;
 
        if (!fb_helper->fb)
                return 0;
@@ -1596,10 +1585,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
 
        max_width = fb_helper->fb->width;
        max_height = fb_helper->fb->height;
-       bpp_sel = fb_helper->fb->bits_per_pixel;
 
-       count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
-                                                   max_height);
+       drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height);
        mutex_unlock(&fb_helper->dev->mode_config.mutex);
 
        drm_modeset_lock_all(dev);
index e788882..f9c7fa3 100644 (file)
@@ -34,7 +34,7 @@
  */
 void drm_flip_work_queue(struct drm_flip_work *work, void *val)
 {
-       if (kfifo_put(&work->fifo, (const void **)&val)) {
+       if (kfifo_put(&work->fifo, val)) {
                atomic_inc(&work->pending);
        } else {
                DRM_ERROR("%s fifo full!\n", work->name);
index 22d14ec..c5b929c 100644 (file)
@@ -113,7 +113,6 @@ int drm_open(struct inode *inode, struct file *filp)
        retcode = drm_open_helper(inode, filp, dev);
        if (retcode)
                goto err_undo;
-       atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
        if (need_setup) {
                retcode = drm_setup(dev);
                if (retcode)
@@ -235,7 +234,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
 
        priv->ioctl_count = 0;
        /* for compatibility root is always authenticated */
-       priv->authenticated = capable(CAP_SYS_ADMIN);
+       priv->always_authenticated = capable(CAP_SYS_ADMIN);
+       priv->authenticated = priv->always_authenticated;
        priv->lock_count = 0;
 
        INIT_LIST_HEAD(&priv->lhead);
@@ -374,13 +374,80 @@ static void drm_events_release(struct drm_file *file_priv)
                }
 
        /* Remove unconsumed events */
-       list_for_each_entry_safe(e, et, &file_priv->event_list, link)
+       list_for_each_entry_safe(e, et, &file_priv->event_list, link) {
+               list_del(&e->link);
                e->destroy(e);
+       }
 
        spin_unlock_irqrestore(&dev->event_lock, flags);
 }
 
 /**
+ * drm_legacy_dev_reinit
+ *
+ * Reinitializes a legacy/ums drm device in it's lastclose function.
+ */
+static void drm_legacy_dev_reinit(struct drm_device *dev)
+{
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               return;
+
+       atomic_set(&dev->ioctl_count, 0);
+       atomic_set(&dev->vma_count, 0);
+
+       dev->sigdata.lock = NULL;
+
+       dev->context_flag = 0;
+       dev->last_context = 0;
+       dev->if_version = 0;
+}
+
+/**
+ * Take down the DRM device.
+ *
+ * \param dev DRM device structure.
+ *
+ * Frees every resource in \p dev.
+ *
+ * \sa drm_device
+ */
+int drm_lastclose(struct drm_device * dev)
+{
+       struct drm_vma_entry *vma, *vma_temp;
+
+       DRM_DEBUG("\n");
+
+       if (dev->driver->lastclose)
+               dev->driver->lastclose(dev);
+       DRM_DEBUG("driver lastclose completed\n");
+
+       if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET))
+               drm_irq_uninstall(dev);
+
+       mutex_lock(&dev->struct_mutex);
+
+       drm_agp_clear(dev);
+
+       drm_legacy_sg_cleanup(dev);
+
+       /* Clear vma list (only built for debugging) */
+       list_for_each_entry_safe(vma, vma_temp, &dev->vmalist, head) {
+               list_del(&vma->head);
+               kfree(vma);
+       }
+
+       drm_legacy_dma_takedown(dev);
+
+       dev->dev_mapping = NULL;
+       mutex_unlock(&dev->struct_mutex);
+
+       drm_legacy_dev_reinit(dev);
+
+       DRM_DEBUG("lastclose completed\n");
+       return 0;
+}
+
+/**
  * Release file.
  *
  * \param inode device inode
@@ -449,7 +516,6 @@ int drm_release(struct inode *inode, struct file *filp)
 
                                list_del(&pos->head);
                                kfree(pos);
-                               --dev->ctx_count;
                        }
                }
        }
@@ -463,7 +529,7 @@ int drm_release(struct inode *inode, struct file *filp)
                list_for_each_entry(temp, &dev->filelist, lhead) {
                        if ((temp->master == file_priv->master) &&
                            (temp != file_priv))
-                               temp->authenticated = 0;
+                               temp->authenticated = temp->always_authenticated;
                }
 
                /**
@@ -511,7 +577,6 @@ int drm_release(struct inode *inode, struct file *filp)
         * End inline drm_release
         */
 
-       atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
        if (!--dev->open_count) {
                if (atomic_read(&dev->ioctl_count)) {
                        DRM_ERROR("Device busy: %d\n",
index 49293bd..4761ade 100644 (file)
@@ -160,35 +160,6 @@ void drm_gem_private_object_init(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_gem_private_object_init);
 
-/**
- * Allocate a GEM object of the specified size with shmfs backing store
- */
-struct drm_gem_object *
-drm_gem_object_alloc(struct drm_device *dev, size_t size)
-{
-       struct drm_gem_object *obj;
-
-       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
-       if (!obj)
-               goto free;
-
-       if (drm_gem_object_init(dev, obj, size) != 0)
-               goto free;
-
-       if (dev->driver->gem_init_object != NULL &&
-           dev->driver->gem_init_object(obj) != 0) {
-               goto fput;
-       }
-       return obj;
-fput:
-       /* Object_init mangles the global counters - readjust them. */
-       fput(obj->filp);
-free:
-       kfree(obj);
-       return NULL;
-}
-EXPORT_SYMBOL(drm_gem_object_alloc);
-
 static void
 drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp)
 {
index f731116..3d2e91c 100644 (file)
@@ -67,7 +67,6 @@ int drm_global_item_ref(struct drm_global_reference *ref)
 {
        int ret;
        struct drm_global_item *item = &glob[ref->global_type];
-       void *object;
 
        mutex_lock(&item->mutex);
        if (item->refcount == 0) {
@@ -85,7 +84,6 @@ int drm_global_item_ref(struct drm_global_reference *ref)
        }
        ++item->refcount;
        ref->object = item->object;
-       object = item->object;
        mutex_unlock(&item->mutex);
        return 0;
 out_err:
index 5329832..7d5a152 100644 (file)
@@ -163,13 +163,13 @@ int drm_vblank_info(struct seq_file *m, void *data)
        mutex_lock(&dev->struct_mutex);
        for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
                seq_printf(m, "CRTC %d enable:     %d\n",
-                          crtc, atomic_read(&dev->vblank_refcount[crtc]));
+                          crtc, atomic_read(&dev->vblank[crtc].refcount));
                seq_printf(m, "CRTC %d counter:    %d\n",
                           crtc, drm_vblank_count(dev, crtc));
                seq_printf(m, "CRTC %d last wait:  %d\n",
-                          crtc, dev->last_vblank_wait[crtc]);
+                          crtc, dev->vblank[crtc].last_wait);
                seq_printf(m, "CRTC %d in modeset: %d\n",
-                          crtc, dev->vblank_inmodeset[crtc]);
+                          crtc, dev->vblank[crtc].inmodeset);
        }
        mutex_unlock(&dev->struct_mutex);
        return 0;
index 07247e2..dffc836 100644 (file)
@@ -303,6 +303,27 @@ int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
 }
 
 /**
+ * Set device/driver capabilities
+ */
+int
+drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+       struct drm_set_client_cap *req = data;
+
+       switch (req->capability) {
+       case DRM_CLIENT_CAP_STEREO_3D:
+               if (req->value > 1)
+                       return -EINVAL;
+               file_priv->stereo_allowed = req->value;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
  * Setversion ioctl.
  *
  * \param inode device inode.
index f92da0a..64c34d5 100644 (file)
@@ -43,9 +43,8 @@
 #include <linux/export.h>
 
 /* Access macro for slots in vblank timestamp ringbuffer. */
-#define vblanktimestamp(dev, crtc, count) ( \
-       (dev)->_vblank_time[(crtc) * DRM_VBLANKTIME_RBSIZE + \
-       ((count) % DRM_VBLANKTIME_RBSIZE)])
+#define vblanktimestamp(dev, crtc, count) \
+       ((dev)->vblank[crtc].time[(count) % DRM_VBLANKTIME_RBSIZE])
 
 /* Retry timestamp calculation up to 3 times to satisfy
  * drm_timestamp_precision before giving up.
@@ -89,8 +88,7 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
  */
 static void clear_vblank_timestamps(struct drm_device *dev, int crtc)
 {
-       memset(&dev->_vblank_time[crtc * DRM_VBLANKTIME_RBSIZE], 0,
-               DRM_VBLANKTIME_RBSIZE * sizeof(struct timeval));
+       memset(dev->vblank[crtc].time, 0, sizeof(dev->vblank[crtc].time));
 }
 
 /*
@@ -115,7 +113,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
        spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
 
        dev->driver->disable_vblank(dev, crtc);
-       dev->vblank_enabled[crtc] = 0;
+       dev->vblank[crtc].enabled = false;
 
        /* No further vblank irq's will be processed after
         * this point. Get current hardware vblank count and
@@ -130,9 +128,9 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
         * delayed gpu counter increment.
         */
        do {
-               dev->last_vblank[crtc] = dev->driver->get_vblank_counter(dev, crtc);
+               dev->vblank[crtc].last = dev->driver->get_vblank_counter(dev, crtc);
                vblrc = drm_get_last_vbltimestamp(dev, crtc, &tvblank, 0);
-       } while (dev->last_vblank[crtc] != dev->driver->get_vblank_counter(dev, crtc) && (--count) && vblrc);
+       } while (dev->vblank[crtc].last != dev->driver->get_vblank_counter(dev, crtc) && (--count) && vblrc);
 
        if (!count)
                vblrc = 0;
@@ -140,7 +138,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
        /* Compute time difference to stored timestamp of last vblank
         * as updated by last invocation of drm_handle_vblank() in vblank irq.
         */
-       vblcount = atomic_read(&dev->_vblank_count[crtc]);
+       vblcount = atomic_read(&dev->vblank[crtc].count);
        diff_ns = timeval_to_ns(&tvblank) -
                  timeval_to_ns(&vblanktimestamp(dev, crtc, vblcount));
 
@@ -157,7 +155,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
         * hope for the best.
         */
        if ((vblrc > 0) && (abs64(diff_ns) > 1000000)) {
-               atomic_inc(&dev->_vblank_count[crtc]);
+               atomic_inc(&dev->vblank[crtc].count);
                smp_mb__after_atomic_inc();
        }
 
@@ -178,8 +176,8 @@ static void vblank_disable_fn(unsigned long arg)
 
        for (i = 0; i < dev->num_crtcs; i++) {
                spin_lock_irqsave(&dev->vbl_lock, irqflags);
-               if (atomic_read(&dev->vblank_refcount[i]) == 0 &&
-                   dev->vblank_enabled[i]) {
+               if (atomic_read(&dev->vblank[i].refcount) == 0 &&
+                   dev->vblank[i].enabled) {
                        DRM_DEBUG("disabling vblank on crtc %d\n", i);
                        vblank_disable_and_save(dev, i);
                }
@@ -197,14 +195,7 @@ void drm_vblank_cleanup(struct drm_device *dev)
 
        vblank_disable_fn((unsigned long)dev);
 
-       kfree(dev->vbl_queue);
-       kfree(dev->_vblank_count);
-       kfree(dev->vblank_refcount);
-       kfree(dev->vblank_enabled);
-       kfree(dev->last_vblank);
-       kfree(dev->last_vblank_wait);
-       kfree(dev->vblank_inmodeset);
-       kfree(dev->_vblank_time);
+       kfree(dev->vblank);
 
        dev->num_crtcs = 0;
 }
@@ -221,42 +212,14 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
 
        dev->num_crtcs = num_crtcs;
 
-       dev->vbl_queue = kmalloc(sizeof(wait_queue_head_t) * num_crtcs,
-                                GFP_KERNEL);
-       if (!dev->vbl_queue)
+       dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL);
+       if (!dev->vblank)
                goto err;
 
-       dev->_vblank_count = kmalloc(sizeof(atomic_t) * num_crtcs, GFP_KERNEL);
-       if (!dev->_vblank_count)
-               goto err;
-
-       dev->vblank_refcount = kmalloc(sizeof(atomic_t) * num_crtcs,
-                                      GFP_KERNEL);
-       if (!dev->vblank_refcount)
-               goto err;
-
-       dev->vblank_enabled = kcalloc(num_crtcs, sizeof(int), GFP_KERNEL);
-       if (!dev->vblank_enabled)
-               goto err;
+       for (i = 0; i < num_crtcs; i++)
+               init_waitqueue_head(&dev->vblank[i].queue);
 
-       dev->last_vblank = kcalloc(num_crtcs, sizeof(u32), GFP_KERNEL);
-       if (!dev->last_vblank)
-               goto err;
-
-       dev->last_vblank_wait = kcalloc(num_crtcs, sizeof(u32), GFP_KERNEL);
-       if (!dev->last_vblank_wait)
-               goto err;
-
-       dev->vblank_inmodeset = kcalloc(num_crtcs, sizeof(int), GFP_KERNEL);
-       if (!dev->vblank_inmodeset)
-               goto err;
-
-       dev->_vblank_time = kcalloc(num_crtcs * DRM_VBLANKTIME_RBSIZE,
-                                   sizeof(struct timeval), GFP_KERNEL);
-       if (!dev->_vblank_time)
-               goto err;
-
-       DRM_INFO("Supports vblank timestamp caching Rev 1 (10.10.2010).\n");
+       DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
 
        /* Driver specific high-precision vblank timestamping supported? */
        if (dev->driver->get_vblank_timestamp)
@@ -264,14 +227,8 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
        else
                DRM_INFO("No driver support for vblank timestamp query.\n");
 
-       /* Zero per-crtc vblank stuff */
-       for (i = 0; i < num_crtcs; i++) {
-               init_waitqueue_head(&dev->vbl_queue[i]);
-               atomic_set(&dev->_vblank_count[i], 0);
-               atomic_set(&dev->vblank_refcount[i], 0);
-       }
+       dev->vblank_disable_allowed = false;
 
-       dev->vblank_disable_allowed = 0;
        return 0;
 
 err:
@@ -336,7 +293,7 @@ int drm_irq_install(struct drm_device *dev)
                mutex_unlock(&dev->struct_mutex);
                return -EBUSY;
        }
-       dev->irq_enabled = 1;
+       dev->irq_enabled = true;
        mutex_unlock(&dev->struct_mutex);
 
        DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev));
@@ -359,7 +316,7 @@ int drm_irq_install(struct drm_device *dev)
 
        if (ret < 0) {
                mutex_lock(&dev->struct_mutex);
-               dev->irq_enabled = 0;
+               dev->irq_enabled = false;
                mutex_unlock(&dev->struct_mutex);
                return ret;
        }
@@ -373,7 +330,7 @@ int drm_irq_install(struct drm_device *dev)
 
        if (ret < 0) {
                mutex_lock(&dev->struct_mutex);
-               dev->irq_enabled = 0;
+               dev->irq_enabled = false;
                mutex_unlock(&dev->struct_mutex);
                if (!drm_core_check_feature(dev, DRIVER_MODESET))
                        vga_client_register(dev->pdev, NULL, NULL, NULL);
@@ -394,14 +351,15 @@ EXPORT_SYMBOL(drm_irq_install);
 int drm_irq_uninstall(struct drm_device *dev)
 {
        unsigned long irqflags;
-       int irq_enabled, i;
+       bool irq_enabled;
+       int i;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
                return -EINVAL;
 
        mutex_lock(&dev->struct_mutex);
        irq_enabled = dev->irq_enabled;
-       dev->irq_enabled = 0;
+       dev->irq_enabled = false;
        mutex_unlock(&dev->struct_mutex);
 
        /*
@@ -410,9 +368,9 @@ int drm_irq_uninstall(struct drm_device *dev)
        if (dev->num_crtcs) {
                spin_lock_irqsave(&dev->vbl_lock, irqflags);
                for (i = 0; i < dev->num_crtcs; i++) {
-                       DRM_WAKEUP(&dev->vbl_queue[i]);
-                       dev->vblank_enabled[i] = 0;
-                       dev->last_vblank[i] =
+                       DRM_WAKEUP(&dev->vblank[i].queue);
+                       dev->vblank[i].enabled = false;
+                       dev->vblank[i].last =
                                dev->driver->get_vblank_counter(dev, i);
                }
                spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
@@ -497,8 +455,8 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc)
        /* Dot clock in Hz: */
        dotclock = (u64) crtc->hwmode.clock * 1000;
 
-       /* Fields of interlaced scanout modes are only halve a frame duration.
-        * Double the dotclock to get halve the frame-/line-/pixelduration.
+       /* Fields of interlaced scanout modes are only half a frame duration.
+        * Double the dotclock to get half the frame-/line-/pixelduration.
         */
        if (crtc->hwmode.flags & DRM_MODE_FLAG_INTERLACE)
                dotclock *= 2;
@@ -628,24 +586,20 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
         * code gets preempted or delayed for some reason.
         */
        for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
-               /* Disable preemption to make it very likely to
-                * succeed in the first iteration even on PREEMPT_RT kernel.
+               /*
+                * Get vertical and horizontal scanout position vpos, hpos,
+                * and bounding timestamps stime, etime, pre/post query.
                 */
-               preempt_disable();
+               vbl_status = dev->driver->get_scanout_position(dev, crtc, &vpos,
+                                                              &hpos, &stime, &etime);
 
-               /* Get system timestamp before query. */
-               stime = ktime_get();
-
-               /* Get vertical and horizontal scanout pos. vpos, hpos. */
-               vbl_status = dev->driver->get_scanout_position(dev, crtc, &vpos, &hpos);
-
-               /* Get system timestamp after query. */
-               etime = ktime_get();
+               /*
+                * Get correction for CLOCK_MONOTONIC -> CLOCK_REALTIME if
+                * CLOCK_REALTIME is requested.
+                */
                if (!drm_timestamp_monotonic)
                        mono_time_offset = ktime_get_monotonic_offset();
 
-               preempt_enable();
-
                /* Return as no-op if scanout query unsupported or failed. */
                if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
                        DRM_DEBUG("crtc %d : scanoutpos query failed [%d].\n",
@@ -653,6 +607,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
                        return -EIO;
                }
 
+               /* Compute uncertainty in timestamp of scanout position query. */
                duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
 
                /* Accept result with <  max_error nsecs timing uncertainty. */
@@ -795,7 +750,7 @@ EXPORT_SYMBOL(drm_get_last_vbltimestamp);
  */
 u32 drm_vblank_count(struct drm_device *dev, int crtc)
 {
-       return atomic_read(&dev->_vblank_count[crtc]);
+       return atomic_read(&dev->vblank[crtc].count);
 }
 EXPORT_SYMBOL(drm_vblank_count);
 
@@ -824,10 +779,10 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc,
         * a seqlock.
         */
        do {
-               cur_vblank = atomic_read(&dev->_vblank_count[crtc]);
+               cur_vblank = atomic_read(&dev->vblank[crtc].count);
                *vblanktime = vblanktimestamp(dev, crtc, cur_vblank);
                smp_rmb();
-       } while (cur_vblank != atomic_read(&dev->_vblank_count[crtc]));
+       } while (cur_vblank != atomic_read(&dev->vblank[crtc].count));
 
        return cur_vblank;
 }
@@ -914,12 +869,12 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
        } while (cur_vblank != dev->driver->get_vblank_counter(dev, crtc));
 
        /* Deal with counter wrap */
-       diff = cur_vblank - dev->last_vblank[crtc];
-       if (cur_vblank < dev->last_vblank[crtc]) {
+       diff = cur_vblank - dev->vblank[crtc].last;
+       if (cur_vblank < dev->vblank[crtc].last) {
                diff += dev->max_vblank_count;
 
                DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n",
-                         crtc, dev->last_vblank[crtc], cur_vblank, diff);
+                         crtc, dev->vblank[crtc].last, cur_vblank, diff);
        }
 
        DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n",
@@ -930,12 +885,12 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
         * reinitialize delayed at next vblank interrupt in that case.
         */
        if (rc) {
-               tslot = atomic_read(&dev->_vblank_count[crtc]) + diff;
+               tslot = atomic_read(&dev->vblank[crtc].count) + diff;
                vblanktimestamp(dev, crtc, tslot) = t_vblank;
        }
 
        smp_mb__before_atomic_inc();
-       atomic_add(diff, &dev->_vblank_count[crtc]);
+       atomic_add(diff, &dev->vblank[crtc].count);
        smp_mb__after_atomic_inc();
 }
 
@@ -957,9 +912,9 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
 
        spin_lock_irqsave(&dev->vbl_lock, irqflags);
        /* Going from 0->1 means we have to enable interrupts again */
-       if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) {
+       if (atomic_add_return(1, &dev->vblank[crtc].refcount) == 1) {
                spin_lock_irqsave(&dev->vblank_time_lock, irqflags2);
-               if (!dev->vblank_enabled[crtc]) {
+               if (!dev->vblank[crtc].enabled) {
                        /* Enable vblank irqs under vblank_time_lock protection.
                         * All vblank count & timestamp updates are held off
                         * until we are done reinitializing master counter and
@@ -970,16 +925,16 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
                        DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n",
                                  crtc, ret);
                        if (ret)
-                               atomic_dec(&dev->vblank_refcount[crtc]);
+                               atomic_dec(&dev->vblank[crtc].refcount);
                        else {
-                               dev->vblank_enabled[crtc] = 1;
+                               dev->vblank[crtc].enabled = true;
                                drm_update_vblank_count(dev, crtc);
                        }
                }
                spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags2);
        } else {
-               if (!dev->vblank_enabled[crtc]) {
-                       atomic_dec(&dev->vblank_refcount[crtc]);
+               if (!dev->vblank[crtc].enabled) {
+                       atomic_dec(&dev->vblank[crtc].refcount);
                        ret = -EINVAL;
                }
        }
@@ -999,10 +954,10 @@ EXPORT_SYMBOL(drm_vblank_get);
  */
 void drm_vblank_put(struct drm_device *dev, int crtc)
 {
-       BUG_ON(atomic_read(&dev->vblank_refcount[crtc]) == 0);
+       BUG_ON(atomic_read(&dev->vblank[crtc].refcount) == 0);
 
        /* Last user schedules interrupt disable */
-       if (atomic_dec_and_test(&dev->vblank_refcount[crtc]) &&
+       if (atomic_dec_and_test(&dev->vblank[crtc].refcount) &&
            (drm_vblank_offdelay > 0))
                mod_timer(&dev->vblank_disable_timer,
                          jiffies + ((drm_vblank_offdelay * DRM_HZ)/1000));
@@ -1025,7 +980,7 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
 
        spin_lock_irqsave(&dev->vbl_lock, irqflags);
        vblank_disable_and_save(dev, crtc);
-       DRM_WAKEUP(&dev->vbl_queue[crtc]);
+       DRM_WAKEUP(&dev->vblank[crtc].queue);
 
        /* Send any queued vblank events, lest the natives grow disquiet */
        seq = drm_vblank_count_and_time(dev, crtc, &now);
@@ -1067,10 +1022,10 @@ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
         * to avoid corrupting the count if multiple, mismatch calls occur),
         * so that interrupts remain enabled in the interim.
         */
-       if (!dev->vblank_inmodeset[crtc]) {
-               dev->vblank_inmodeset[crtc] = 0x1;
+       if (!dev->vblank[crtc].inmodeset) {
+               dev->vblank[crtc].inmodeset = 0x1;
                if (drm_vblank_get(dev, crtc) == 0)
-                       dev->vblank_inmodeset[crtc] |= 0x2;
+                       dev->vblank[crtc].inmodeset |= 0x2;
        }
 }
 EXPORT_SYMBOL(drm_vblank_pre_modeset);
@@ -1083,15 +1038,15 @@ void drm_vblank_post_modeset(struct drm_device *dev, int crtc)
        if (!dev->num_crtcs)
                return;
 
-       if (dev->vblank_inmodeset[crtc]) {
+       if (dev->vblank[crtc].inmodeset) {
                spin_lock_irqsave(&dev->vbl_lock, irqflags);
-               dev->vblank_disable_allowed = 1;
+               dev->vblank_disable_allowed = true;
                spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
 
-               if (dev->vblank_inmodeset[crtc] & 0x2)
+               if (dev->vblank[crtc].inmodeset & 0x2)
                        drm_vblank_put(dev, crtc);
 
-               dev->vblank_inmodeset[crtc] = 0;
+               dev->vblank[crtc].inmodeset = 0;
        }
 }
 EXPORT_SYMBOL(drm_vblank_post_modeset);
@@ -1288,8 +1243,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
 
        DRM_DEBUG("waiting on vblank count %d, crtc %d\n",
                  vblwait->request.sequence, crtc);
-       dev->last_vblank_wait[crtc] = vblwait->request.sequence;
-       DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ,
+       dev->vblank[crtc].last_wait = vblwait->request.sequence;
+       DRM_WAIT_ON(ret, dev->vblank[crtc].queue, 3 * DRM_HZ,
                    (((drm_vblank_count(dev, crtc) -
                       vblwait->request.sequence) <= (1 << 23)) ||
                     !dev->irq_enabled));
@@ -1367,7 +1322,7 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
        spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
 
        /* Vblank irq handling disabled. Nothing to do. */
-       if (!dev->vblank_enabled[crtc]) {
+       if (!dev->vblank[crtc].enabled) {
                spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
                return false;
        }
@@ -1377,7 +1332,7 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
         */
 
        /* Get current timestamp and count. */
-       vblcount = atomic_read(&dev->_vblank_count[crtc]);
+       vblcount = atomic_read(&dev->vblank[crtc].count);
        drm_get_last_vbltimestamp(dev, crtc, &tvblank, DRM_CALLED_FROM_VBLIRQ);
 
        /* Compute time difference to timestamp of last vblank */
@@ -1401,14 +1356,14 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
                 * the timestamp computed above.
                 */
                smp_mb__before_atomic_inc();
-               atomic_inc(&dev->_vblank_count[crtc]);
+               atomic_inc(&dev->vblank[crtc].count);
                smp_mb__after_atomic_inc();
        } else {
                DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n",
                          crtc, (int) diff_ns);
        }
 
-       DRM_WAKEUP(&dev->vbl_queue[crtc]);
+       DRM_WAKEUP(&dev->vblank[crtc].queue);
        drm_handle_vblank_events(dev, crtc);
 
        spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
index d752c96..f645268 100644 (file)
@@ -86,7 +86,6 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
                if (drm_lock_take(&master->lock, lock->context)) {
                        master->lock.file_priv = file_priv;
                        master->lock.lock_time = jiffies;
-                       atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
                        break;  /* Got lock */
                }
 
@@ -157,8 +156,6 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
                return -EINVAL;
        }
 
-       atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]);
-
        if (drm_lock_free(&master->lock, lock->context)) {
                /* FIXME: Should really bail out here. */
        }
index fc2adb6..85071a1 100644 (file)
@@ -707,18 +707,25 @@ EXPORT_SYMBOL(drm_mode_vrefresh);
 /**
  * drm_mode_set_crtcinfo - set CRTC modesetting parameters
  * @p: mode
- * @adjust_flags: unused? (FIXME)
+ * @adjust_flags: a combination of adjustment flags
  *
  * LOCKING:
  * None.
  *
  * Setup the CRTC modesetting parameters for @p, adjusting if necessary.
+ *
+ * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of
+ *   interlaced modes.
+ * - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for
+ *   buffers containing two eyes (only adjust the timings when needed, eg. for
+ *   "frame packing" or "side by side full").
  */
 void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
 {
        if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
                return;
 
+       p->crtc_clock = p->clock;
        p->crtc_hdisplay = p->hdisplay;
        p->crtc_hsync_start = p->hsync_start;
        p->crtc_hsync_end = p->hsync_end;
@@ -752,6 +759,20 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
                p->crtc_vtotal *= p->vscan;
        }
 
+       if (adjust_flags & CRTC_STEREO_DOUBLE) {
+               unsigned int layout = p->flags & DRM_MODE_FLAG_3D_MASK;
+
+               switch (layout) {
+               case DRM_MODE_FLAG_3D_FRAME_PACKING:
+                       p->crtc_clock *= 2;
+                       p->crtc_vdisplay += p->crtc_vtotal;
+                       p->crtc_vsync_start += p->crtc_vtotal;
+                       p->crtc_vsync_end += p->crtc_vtotal;
+                       p->crtc_vtotal += p->crtc_vtotal;
+                       break;
+               }
+       }
+
        p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
        p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
        p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
@@ -830,12 +851,16 @@ bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_displ
        } else if (mode1->clock != mode2->clock)
                return false;
 
-       return drm_mode_equal_no_clocks(mode1, mode2);
+       if ((mode1->flags & DRM_MODE_FLAG_3D_MASK) !=
+           (mode2->flags & DRM_MODE_FLAG_3D_MASK))
+               return false;
+
+       return drm_mode_equal_no_clocks_no_stereo(mode1, mode2);
 }
 EXPORT_SYMBOL(drm_mode_equal);
 
 /**
- * drm_mode_equal_no_clocks - test modes for equality
+ * drm_mode_equal_no_clocks_no_stereo - test modes for equality
  * @mode1: first mode
  * @mode2: second mode
  *
@@ -843,12 +868,13 @@ EXPORT_SYMBOL(drm_mode_equal);
  * None.
  *
  * Check to see if @mode1 and @mode2 are equivalent, but
- * don't check the pixel clocks.
+ * don't check the pixel clocks nor the stereo layout.
  *
  * RETURNS:
  * True if the modes are equal, false otherwise.
  */
-bool drm_mode_equal_no_clocks(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2)
+bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1,
+                                       const struct drm_display_mode *mode2)
 {
        if (mode1->hdisplay == mode2->hdisplay &&
            mode1->hsync_start == mode2->hsync_start &&
@@ -860,12 +886,13 @@ bool drm_mode_equal_no_clocks(const struct drm_display_mode *mode1, const struct
            mode1->vsync_end == mode2->vsync_end &&
            mode1->vtotal == mode2->vtotal &&
            mode1->vscan == mode2->vscan &&
-           mode1->flags == mode2->flags)
+           (mode1->flags & ~DRM_MODE_FLAG_3D_MASK) ==
+            (mode2->flags & ~DRM_MODE_FLAG_3D_MASK))
                return true;
 
        return false;
 }
-EXPORT_SYMBOL(drm_mode_equal_no_clocks);
+EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo);
 
 /**
  * drm_mode_validate_size - make sure modes adhere to size constraints
@@ -1014,7 +1041,7 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
                                /* if equal delete the probed mode */
                                mode->status = pmode->status;
                                /* Merge type bits together */
-                               mode->type |= pmode->type;
+                               mode->type = pmode->type;
                                list_del(&pmode->head);
                                drm_mode_destroy(connector->dev, pmode);
                                break;
index 1f96cee..0267979 100644 (file)
@@ -80,7 +80,7 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t ali
        /* Reserve */
        for (addr = (unsigned long)dmah->vaddr, sz = size;
             sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
-               SetPageReserved(virt_to_page(addr));
+               SetPageReserved(virt_to_page((void *)addr));
        }
 
        return dmah;
@@ -103,7 +103,7 @@ void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
                /* Unreserve */
                for (addr = (unsigned long)dmah->vaddr, sz = dmah->size;
                     sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
-                       ClearPageReserved(virt_to_page(addr));
+                       ClearPageReserved(virt_to_page((void *)addr));
                }
                dma_free_coherent(&dev->pdev->dev, dmah->size, dmah->vaddr,
                                  dmah->busaddr);
@@ -322,83 +322,36 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
 
        DRM_DEBUG("\n");
 
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       dev = drm_dev_alloc(driver, &pdev->dev);
        if (!dev)
                return -ENOMEM;
 
        ret = pci_enable_device(pdev);
        if (ret)
-               goto err_g1;
+               goto err_free;
 
        dev->pdev = pdev;
-       dev->dev = &pdev->dev;
-
-       dev->pci_device = pdev->device;
-       dev->pci_vendor = pdev->vendor;
-
 #ifdef __alpha__
        dev->hose = pdev->sysdata;
 #endif
 
-       mutex_lock(&drm_global_mutex);
-
-       if ((ret = drm_fill_in_dev(dev, ent, driver))) {
-               printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
-               goto err_g2;
-       }
-
-       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
                pci_set_drvdata(pdev, dev);
-               ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
-               if (ret)
-                       goto err_g2;
-       }
-
-       if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
-               ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER);
-               if (ret)
-                       goto err_g21;
-       }
-
-       if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
-               goto err_g3;
-
-       if (dev->driver->load) {
-               ret = dev->driver->load(dev, ent->driver_data);
-               if (ret)
-                       goto err_g4;
-       }
 
-       /* setup the grouping for the legacy output */
-       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-               ret = drm_mode_group_init_legacy_group(dev,
-                                               &dev->primary->mode_group);
-               if (ret)
-                       goto err_g4;
-       }
-
-       list_add_tail(&dev->driver_item, &driver->device_list);
+       ret = drm_dev_register(dev, ent->driver_data);
+       if (ret)
+               goto err_pci;
 
        DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
                 driver->name, driver->major, driver->minor, driver->patchlevel,
                 driver->date, pci_name(pdev), dev->primary->index);
 
-       mutex_unlock(&drm_global_mutex);
        return 0;
 
-err_g4:
-       drm_put_minor(&dev->primary);
-err_g3:
-       if (dev->render)
-               drm_put_minor(&dev->render);
-err_g21:
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               drm_put_minor(&dev->control);
-err_g2:
+err_pci:
        pci_disable_device(pdev);
-err_g1:
-       kfree(dev);
-       mutex_unlock(&drm_global_mutex);
+err_free:
+       drm_dev_free(dev);
        return ret;
 }
 EXPORT_SYMBOL(drm_get_pci_dev);
index f7a18c6..fc24fee 100644 (file)
@@ -47,55 +47,15 @@ static int drm_get_platform_dev(struct platform_device *platdev,
 
        DRM_DEBUG("\n");
 
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       dev = drm_dev_alloc(driver, &platdev->dev);
        if (!dev)
                return -ENOMEM;
 
        dev->platformdev = platdev;
-       dev->dev = &platdev->dev;
 
-       mutex_lock(&drm_global_mutex);
-
-       ret = drm_fill_in_dev(dev, NULL, driver);
-
-       if (ret) {
-               printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
-               goto err_g1;
-       }
-
-       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-               ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
-               if (ret)
-                       goto err_g1;
-       }
-
-       if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
-               ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER);
-               if (ret)
-                       goto err_g11;
-       }
-
-       ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY);
+       ret = drm_dev_register(dev, 0);
        if (ret)
-               goto err_g2;
-
-       if (dev->driver->load) {
-               ret = dev->driver->load(dev, 0);
-               if (ret)
-                       goto err_g3;
-       }
-
-       /* setup the grouping for the legacy output */
-       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-               ret = drm_mode_group_init_legacy_group(dev,
-                               &dev->primary->mode_group);
-               if (ret)
-                       goto err_g3;
-       }
-
-       list_add_tail(&dev->driver_item, &driver->device_list);
-
-       mutex_unlock(&drm_global_mutex);
+               goto err_free;
 
        DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
                 driver->name, driver->major, driver->minor, driver->patchlevel,
@@ -103,17 +63,8 @@ static int drm_get_platform_dev(struct platform_device *platdev,
 
        return 0;
 
-err_g3:
-       drm_put_minor(&dev->primary);
-err_g2:
-       if (dev->render)
-               drm_put_minor(&dev->render);
-err_g11:
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               drm_put_minor(&dev->control);
-err_g1:
-       kfree(dev);
-       mutex_unlock(&drm_global_mutex);
+err_free:
+       drm_dev_free(dev);
        return ret;
 }
 
index 276d470..56805c3 100644 (file)
@@ -637,14 +637,13 @@ int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages,
        unsigned count;
        struct scatterlist *sg;
        struct page *page;
-       u32 len, offset;
+       u32 len;
        int pg_index;
        dma_addr_t addr;
 
        pg_index = 0;
        for_each_sg(sgt->sgl, sg, sgt->nents, count) {
                len = sg->length;
-               offset = sg->offset;
                page = sg_page(sg);
                addr = sg_dma_address(sg);
 
index 39d8645..f53d524 100644 (file)
@@ -254,81 +254,21 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
        return 0;
 }
 
-int drm_fill_in_dev(struct drm_device *dev,
-                          const struct pci_device_id *ent,
-                          struct drm_driver *driver)
-{
-       int retcode;
-
-       INIT_LIST_HEAD(&dev->filelist);
-       INIT_LIST_HEAD(&dev->ctxlist);
-       INIT_LIST_HEAD(&dev->vmalist);
-       INIT_LIST_HEAD(&dev->maplist);
-       INIT_LIST_HEAD(&dev->vblank_event_list);
-
-       spin_lock_init(&dev->count_lock);
-       spin_lock_init(&dev->event_lock);
-       mutex_init(&dev->struct_mutex);
-       mutex_init(&dev->ctxlist_mutex);
-
-       if (drm_ht_create(&dev->map_hash, 12)) {
-               return -ENOMEM;
-       }
-
-       /* the DRM has 6 basic counters */
-       dev->counters = 6;
-       dev->types[0] = _DRM_STAT_LOCK;
-       dev->types[1] = _DRM_STAT_OPENS;
-       dev->types[2] = _DRM_STAT_CLOSES;
-       dev->types[3] = _DRM_STAT_IOCTLS;
-       dev->types[4] = _DRM_STAT_LOCKS;
-       dev->types[5] = _DRM_STAT_UNLOCKS;
-
-       dev->driver = driver;
-
-       if (dev->driver->bus->agp_init) {
-               retcode = dev->driver->bus->agp_init(dev);
-               if (retcode)
-                       goto error_out_unreg;
-       }
-
-
-
-       retcode = drm_ctxbitmap_init(dev);
-       if (retcode) {
-               DRM_ERROR("Cannot allocate memory for context bitmap.\n");
-               goto error_out_unreg;
-       }
-
-       if (driver->driver_features & DRIVER_GEM) {
-               retcode = drm_gem_init(dev);
-               if (retcode) {
-                       DRM_ERROR("Cannot initialize graphics execution "
-                                 "manager (GEM)\n");
-                       goto error_out_unreg;
-               }
-       }
-
-       return 0;
-
-      error_out_unreg:
-       drm_lastclose(dev);
-       return retcode;
-}
-EXPORT_SYMBOL(drm_fill_in_dev);
-
-
 /**
- * Get a secondary minor number.
+ * drm_get_minor - Allocate and register new DRM minor
+ * @dev: DRM device
+ * @minor: Pointer to where new minor is stored
+ * @type: Type of minor
  *
- * \param dev device data structure
- * \param sec-minor structure to hold the assigned minor
- * \return negative number on failure.
+ * Allocate a new minor of the given type and register it. A pointer to the new
+ * minor is returned in @minor.
+ * Caller must hold the global DRM mutex.
  *
- * Search an empty entry and initialize it to the given parameters. This
- * routines assigns minor numbers to secondary heads of multi-headed cards
+ * RETURNS:
+ * 0 on success, negative error code on failure.
  */
-int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type)
+static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor,
+                        int type)
 {
        struct drm_minor *new_minor;
        int ret;
@@ -385,37 +325,48 @@ err_idr:
        *minor = NULL;
        return ret;
 }
-EXPORT_SYMBOL(drm_get_minor);
 
 /**
- * Put a secondary minor number.
+ * drm_unplug_minor - Unplug DRM minor
+ * @minor: Minor to unplug
  *
- * \param sec_minor - structure to be released
- * \return always zero
+ * Unplugs the given DRM minor but keeps the object. So after this returns,
+ * minor->dev is still valid so existing open-files can still access it to get
+ * device information from their drm_file ojects.
+ * If the minor is already unplugged or if @minor is NULL, nothing is done.
+ * The global DRM mutex must be held by the caller.
  */
-int drm_put_minor(struct drm_minor **minor_p)
+static void drm_unplug_minor(struct drm_minor *minor)
 {
-       struct drm_minor *minor = *minor_p;
-
-       DRM_DEBUG("release secondary minor %d\n", minor->index);
+       if (!minor || !minor->kdev)
+               return;
 
 #if defined(CONFIG_DEBUG_FS)
        drm_debugfs_cleanup(minor);
 #endif
 
        drm_sysfs_device_remove(minor);
-
        idr_remove(&drm_minors_idr, minor->index);
-
-       kfree(minor);
-       *minor_p = NULL;
-       return 0;
 }
-EXPORT_SYMBOL(drm_put_minor);
 
-static void drm_unplug_minor(struct drm_minor *minor)
+/**
+ * drm_put_minor - Destroy DRM minor
+ * @minor: Minor to destroy
+ *
+ * This calls drm_unplug_minor() on the given minor and then frees it. Nothing
+ * is done if @minor is NULL. It is fine to call this on already unplugged
+ * minors.
+ * The global DRM mutex must be held by the caller.
+ */
+static void drm_put_minor(struct drm_minor *minor)
 {
-       drm_sysfs_device_remove(minor);
+       if (!minor)
+               return;
+
+       DRM_DEBUG("release secondary minor %d\n", minor->index);
+
+       drm_unplug_minor(minor);
+       kfree(minor);
 }
 
 /**
@@ -427,66 +378,237 @@ static void drm_unplug_minor(struct drm_minor *minor)
  */
 void drm_put_dev(struct drm_device *dev)
 {
-       struct drm_driver *driver;
-       struct drm_map_list *r_list, *list_temp;
-
        DRM_DEBUG("\n");
 
        if (!dev) {
                DRM_ERROR("cleanup called no dev\n");
                return;
        }
-       driver = dev->driver;
 
-       drm_lastclose(dev);
+       drm_dev_unregister(dev);
+       drm_dev_free(dev);
+}
+EXPORT_SYMBOL(drm_put_dev);
 
-       if (dev->driver->unload)
-               dev->driver->unload(dev);
+void drm_unplug_dev(struct drm_device *dev)
+{
+       /* for a USB device */
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               drm_unplug_minor(dev->control);
+       if (dev->render)
+               drm_unplug_minor(dev->render);
+       drm_unplug_minor(dev->primary);
 
-       if (dev->driver->bus->agp_destroy)
-               dev->driver->bus->agp_destroy(dev);
+       mutex_lock(&drm_global_mutex);
 
-       drm_vblank_cleanup(dev);
+       drm_device_set_unplugged(dev);
 
-       list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
-               drm_rmmap(dev, r_list->map);
-       drm_ht_remove(&dev->map_hash);
+       if (dev->open_count == 0) {
+               drm_put_dev(dev);
+       }
+       mutex_unlock(&drm_global_mutex);
+}
+EXPORT_SYMBOL(drm_unplug_dev);
 
-       drm_ctxbitmap_cleanup(dev);
+/**
+ * drm_dev_alloc - Allocate new drm device
+ * @driver: DRM driver to allocate device for
+ * @parent: Parent device object
+ *
+ * Allocate and initialize a new DRM device. No device registration is done.
+ * Call drm_dev_register() to advertice the device to user space and register it
+ * with other core subsystems.
+ *
+ * RETURNS:
+ * Pointer to new DRM device, or NULL if out of memory.
+ */
+struct drm_device *drm_dev_alloc(struct drm_driver *driver,
+                                struct device *parent)
+{
+       struct drm_device *dev;
+       int ret;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               drm_put_minor(&dev->control);
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return NULL;
 
-       if (dev->render)
-               drm_put_minor(&dev->render);
+       dev->dev = parent;
+       dev->driver = driver;
 
-       if (driver->driver_features & DRIVER_GEM)
+       INIT_LIST_HEAD(&dev->filelist);
+       INIT_LIST_HEAD(&dev->ctxlist);
+       INIT_LIST_HEAD(&dev->vmalist);
+       INIT_LIST_HEAD(&dev->maplist);
+       INIT_LIST_HEAD(&dev->vblank_event_list);
+
+       spin_lock_init(&dev->count_lock);
+       spin_lock_init(&dev->event_lock);
+       mutex_init(&dev->struct_mutex);
+       mutex_init(&dev->ctxlist_mutex);
+
+       if (drm_ht_create(&dev->map_hash, 12))
+               goto err_free;
+
+       ret = drm_ctxbitmap_init(dev);
+       if (ret) {
+               DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+               goto err_ht;
+       }
+
+       if (driver->driver_features & DRIVER_GEM) {
+               ret = drm_gem_init(dev);
+               if (ret) {
+                       DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
+                       goto err_ctxbitmap;
+               }
+       }
+
+       return dev;
+
+err_ctxbitmap:
+       drm_ctxbitmap_cleanup(dev);
+err_ht:
+       drm_ht_remove(&dev->map_hash);
+err_free:
+       kfree(dev);
+       return NULL;
+}
+EXPORT_SYMBOL(drm_dev_alloc);
+
+/**
+ * drm_dev_free - Free DRM device
+ * @dev: DRM device to free
+ *
+ * Free a DRM device that has previously been allocated via drm_dev_alloc().
+ * You must not use kfree() instead or you will leak memory.
+ *
+ * This must not be called once the device got registered. Use drm_put_dev()
+ * instead, which then calls drm_dev_free().
+ */
+void drm_dev_free(struct drm_device *dev)
+{
+       drm_put_minor(dev->control);
+       drm_put_minor(dev->render);
+       drm_put_minor(dev->primary);
+
+       if (dev->driver->driver_features & DRIVER_GEM)
                drm_gem_destroy(dev);
 
-       drm_put_minor(&dev->primary);
+       drm_ctxbitmap_cleanup(dev);
+       drm_ht_remove(&dev->map_hash);
 
-       list_del(&dev->driver_item);
        kfree(dev->devname);
        kfree(dev);
 }
-EXPORT_SYMBOL(drm_put_dev);
+EXPORT_SYMBOL(drm_dev_free);
 
-void drm_unplug_dev(struct drm_device *dev)
+/**
+ * drm_dev_register - Register DRM device
+ * @dev: Device to register
+ *
+ * Register the DRM device @dev with the system, advertise device to user-space
+ * and start normal device operation. @dev must be allocated via drm_dev_alloc()
+ * previously.
+ *
+ * Never call this twice on any device!
+ *
+ * RETURNS:
+ * 0 on success, negative error code on failure.
+ */
+int drm_dev_register(struct drm_device *dev, unsigned long flags)
 {
-       /* for a USB device */
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               drm_unplug_minor(dev->control);
-       if (dev->render)
-               drm_unplug_minor(dev->render);
-       drm_unplug_minor(dev->primary);
+       int ret;
 
        mutex_lock(&drm_global_mutex);
 
-       drm_device_set_unplugged(dev);
+       if (dev->driver->bus->agp_init) {
+               ret = dev->driver->bus->agp_init(dev);
+               if (ret)
+                       goto out_unlock;
+       }
 
-       if (dev->open_count == 0) {
-               drm_put_dev(dev);
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
+               if (ret)
+                       goto err_agp;
+       }
+
+       if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
+               ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER);
+               if (ret)
+                       goto err_control_node;
+       }
+
+       ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY);
+       if (ret)
+               goto err_render_node;
+
+       if (dev->driver->load) {
+               ret = dev->driver->load(dev, flags);
+               if (ret)
+                       goto err_primary_node;
        }
+
+       /* setup grouping for legacy outputs */
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               ret = drm_mode_group_init_legacy_group(dev,
+                               &dev->primary->mode_group);
+               if (ret)
+                       goto err_unload;
+       }
+
+       list_add_tail(&dev->driver_item, &dev->driver->device_list);
+
+       ret = 0;
+       goto out_unlock;
+
+err_unload:
+       if (dev->driver->unload)
+               dev->driver->unload(dev);
+err_primary_node:
+       drm_put_minor(dev->primary);
+err_render_node:
+       drm_put_minor(dev->render);
+err_control_node:
+       drm_put_minor(dev->control);
+err_agp:
+       if (dev->driver->bus->agp_destroy)
+               dev->driver->bus->agp_destroy(dev);
+out_unlock:
        mutex_unlock(&drm_global_mutex);
+       return ret;
 }
-EXPORT_SYMBOL(drm_unplug_dev);
+EXPORT_SYMBOL(drm_dev_register);
+
+/**
+ * drm_dev_unregister - Unregister DRM device
+ * @dev: Device to unregister
+ *
+ * Unregister the DRM device from the system. This does the reverse of
+ * drm_dev_register() but does not deallocate the device. The caller must call
+ * drm_dev_free() to free all resources.
+ */
+void drm_dev_unregister(struct drm_device *dev)
+{
+       struct drm_map_list *r_list, *list_temp;
+
+       drm_lastclose(dev);
+
+       if (dev->driver->unload)
+               dev->driver->unload(dev);
+
+       if (dev->driver->bus->agp_destroy)
+               dev->driver->bus->agp_destroy(dev);
+
+       drm_vblank_cleanup(dev);
+
+       list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
+               drm_rmmap(dev, r_list->map);
+
+       drm_unplug_minor(dev->control);
+       drm_unplug_minor(dev->render);
+       drm_unplug_minor(dev->primary);
+
+       list_del(&dev->driver_item);
+}
+EXPORT_SYMBOL(drm_dev_unregister);
index 2290b3b..1a35ea5 100644 (file)
@@ -22,8 +22,8 @@
 #include <drm/drm_core.h>
 #include <drm/drmP.h>
 
-#define to_drm_minor(d) container_of(d, struct drm_minor, kdev)
-#define to_drm_connector(d) container_of(d, struct drm_connector, kdev)
+#define to_drm_minor(d) dev_get_drvdata(d)
+#define to_drm_connector(d) dev_get_drvdata(d)
 
 static struct device_type drm_sysfs_device_minor = {
        .name = "drm_minor"
@@ -162,20 +162,6 @@ void drm_sysfs_destroy(void)
        drm_class = NULL;
 }
 
-/**
- * drm_sysfs_device_release - do nothing
- * @dev: Linux device
- *
- * Normally, this would free the DRM device associated with @dev, along
- * with cleaning up any other stuff.  But we do that in the DRM core, so
- * this function can just return and hope that the core does its job.
- */
-static void drm_sysfs_device_release(struct device *dev)
-{
-       memset(dev, 0, sizeof(struct device));
-       return;
-}
-
 /*
  * Connector properties
  */
@@ -380,11 +366,6 @@ static struct bin_attribute edid_attr = {
  * properties (so far, connection status, dpms, mode list & edid) and
  * generate a hotplug event so userspace knows there's a new connector
  * available.
- *
- * Note:
- * This routine should only be called *once* for each registered connector.
- * A second call for an already registered connector will trigger the BUG_ON
- * below.
  */
 int drm_sysfs_connector_add(struct drm_connector *connector)
 {
@@ -394,29 +375,25 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
        int i;
        int ret;
 
-       /* We shouldn't get called more than once for the same connector */
-       BUG_ON(device_is_registered(&connector->kdev));
-
-       connector->kdev.parent = &dev->primary->kdev;
-       connector->kdev.class = drm_class;
-       connector->kdev.release = drm_sysfs_device_release;
+       if (connector->kdev)
+               return 0;
 
+       connector->kdev = device_create(drm_class, dev->primary->kdev,
+                                       0, connector, "card%d-%s",
+                                       dev->primary->index, drm_get_connector_name(connector));
        DRM_DEBUG("adding \"%s\" to sysfs\n",
                  drm_get_connector_name(connector));
 
-       dev_set_name(&connector->kdev, "card%d-%s",
-                    dev->primary->index, drm_get_connector_name(connector));
-       ret = device_register(&connector->kdev);
-
-       if (ret) {
-               DRM_ERROR("failed to register connector device: %d\n", ret);
+       if (IS_ERR(connector->kdev)) {
+               DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev));
+               ret = PTR_ERR(connector->kdev);
                goto out;
        }
 
        /* Standard attributes */
 
        for (attr_cnt = 0; attr_cnt < ARRAY_SIZE(connector_attrs); attr_cnt++) {
-               ret = device_create_file(&connector->kdev, &connector_attrs[attr_cnt]);
+               ret = device_create_file(connector->kdev, &connector_attrs[attr_cnt]);
                if (ret)
                        goto err_out_files;
        }
@@ -433,7 +410,7 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
                case DRM_MODE_CONNECTOR_Component:
                case DRM_MODE_CONNECTOR_TV:
                        for (opt_cnt = 0; opt_cnt < ARRAY_SIZE(connector_attrs_opt1); opt_cnt++) {
-                               ret = device_create_file(&connector->kdev, &connector_attrs_opt1[opt_cnt]);
+                               ret = device_create_file(connector->kdev, &connector_attrs_opt1[opt_cnt]);
                                if (ret)
                                        goto err_out_files;
                        }
@@ -442,7 +419,7 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
                        break;
        }
 
-       ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr);
+       ret = sysfs_create_bin_file(&connector->kdev->kobj, &edid_attr);
        if (ret)
                goto err_out_files;
 
@@ -453,10 +430,10 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
 
 err_out_files:
        for (i = 0; i < opt_cnt; i++)
-               device_remove_file(&connector->kdev, &connector_attrs_opt1[i]);
+               device_remove_file(connector->kdev, &connector_attrs_opt1[i]);
        for (i = 0; i < attr_cnt; i++)
-               device_remove_file(&connector->kdev, &connector_attrs[i]);
-       device_unregister(&connector->kdev);
+               device_remove_file(connector->kdev, &connector_attrs[i]);
+       device_unregister(connector->kdev);
 
 out:
        return ret;
@@ -480,16 +457,16 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
 {
        int i;
 
-       if (!connector->kdev.parent)
+       if (!connector->kdev)
                return;
        DRM_DEBUG("removing \"%s\" from sysfs\n",
                  drm_get_connector_name(connector));
 
        for (i = 0; i < ARRAY_SIZE(connector_attrs); i++)
-               device_remove_file(&connector->kdev, &connector_attrs[i]);
-       sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr);
-       device_unregister(&connector->kdev);
-       connector->kdev.parent = NULL;
+               device_remove_file(connector->kdev, &connector_attrs[i]);
+       sysfs_remove_bin_file(&connector->kdev->kobj, &edid_attr);
+       device_unregister(connector->kdev);
+       connector->kdev = NULL;
 }
 EXPORT_SYMBOL(drm_sysfs_connector_remove);
 
@@ -508,7 +485,7 @@ void drm_sysfs_hotplug_event(struct drm_device *dev)
 
        DRM_DEBUG("generating hotplug event\n");
 
-       kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
+       kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
 }
 EXPORT_SYMBOL(drm_sysfs_hotplug_event);
 
@@ -523,15 +500,8 @@ EXPORT_SYMBOL(drm_sysfs_hotplug_event);
  */
 int drm_sysfs_device_add(struct drm_minor *minor)
 {
-       int err;
        char *minor_str;
 
-       minor->kdev.parent = minor->dev->dev;
-
-       minor->kdev.class = drm_class;
-       minor->kdev.release = drm_sysfs_device_release;
-       minor->kdev.devt = minor->device;
-       minor->kdev.type = &drm_sysfs_device_minor;
        if (minor->type == DRM_MINOR_CONTROL)
                minor_str = "controlD%d";
         else if (minor->type == DRM_MINOR_RENDER)
@@ -539,18 +509,14 @@ int drm_sysfs_device_add(struct drm_minor *minor)
         else
                 minor_str = "card%d";
 
-       dev_set_name(&minor->kdev, minor_str, minor->index);
-
-       err = device_register(&minor->kdev);
-       if (err) {
-               DRM_ERROR("device add failed: %d\n", err);
-               goto err_out;
+       minor->kdev = device_create(drm_class, minor->dev->dev,
+                                   MKDEV(DRM_MAJOR, minor->index),
+                                   minor, minor_str, minor->index);
+       if (IS_ERR(minor->kdev)) {
+               DRM_ERROR("device create failed %ld\n", PTR_ERR(minor->kdev));
+               return PTR_ERR(minor->kdev);
        }
-
        return 0;
-
-err_out:
-       return err;
 }
 
 /**
@@ -562,9 +528,9 @@ err_out:
  */
 void drm_sysfs_device_remove(struct drm_minor *minor)
 {
-       if (minor->kdev.parent)
-               device_unregister(&minor->kdev);
-       minor->kdev.parent = NULL;
+       if (minor->kdev)
+               device_destroy(drm_class, MKDEV(DRM_MAJOR, minor->index));
+       minor->kdev = NULL;
 }
 
 
index 8766472..b179b70 100644 (file)
@@ -7,57 +7,20 @@ int drm_get_usb_dev(struct usb_interface *interface,
                    struct drm_driver *driver)
 {
        struct drm_device *dev;
-       struct usb_device *usbdev;
        int ret;
 
        DRM_DEBUG("\n");
 
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       dev = drm_dev_alloc(driver, &interface->dev);
        if (!dev)
                return -ENOMEM;
 
-       usbdev = interface_to_usbdev(interface);
-       dev->usbdev = usbdev;
-       dev->dev = &interface->dev;
-
-       mutex_lock(&drm_global_mutex);
-
-       ret = drm_fill_in_dev(dev, NULL, driver);
-       if (ret) {
-               printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
-               goto err_g1;
-       }
-
+       dev->usbdev = interface_to_usbdev(interface);
        usb_set_intfdata(interface, dev);
-       ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
-       if (ret)
-               goto err_g1;
-
-       if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
-               ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER);
-               if (ret)
-                       goto err_g11;
-       }
 
-       ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY);
+       ret = drm_dev_register(dev, 0);
        if (ret)
-               goto err_g2;
-
-       if (dev->driver->load) {
-               ret = dev->driver->load(dev, 0);
-               if (ret)
-                       goto err_g3;
-       }
-
-       /* setup the grouping for the legacy output */
-       ret = drm_mode_group_init_legacy_group(dev,
-                                              &dev->primary->mode_group);
-       if (ret)
-               goto err_g3;
-
-       list_add_tail(&dev->driver_item, &driver->device_list);
-
-       mutex_unlock(&drm_global_mutex);
+               goto err_free;
 
        DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
                 driver->name, driver->major, driver->minor, driver->patchlevel,
@@ -65,16 +28,8 @@ int drm_get_usb_dev(struct usb_interface *interface,
 
        return 0;
 
-err_g3:
-       drm_put_minor(&dev->primary);
-err_g2:
-       if (dev->render)
-               drm_put_minor(&dev->render);
-err_g11:
-       drm_put_minor(&dev->control);
-err_g1:
-       kfree(dev);
-       mutex_unlock(&drm_global_mutex);
+err_free:
+       drm_dev_free(dev);
        return ret;
 
 }
index b5c5af7..93e95d7 100644 (file)
@@ -301,7 +301,7 @@ static int drm_do_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 
        offset = (unsigned long)vmf->virtual_address - vma->vm_start;   /* vm_[pg]off[set] should be 0 */
        page_nr = offset >> PAGE_SHIFT; /* page_nr could just be vmf->pgoff */
-       page = virt_to_page((dma->pagelist[page_nr] + (offset & (~PAGE_MASK))));
+       page = virt_to_page((void *)dma->pagelist[page_nr]);
 
        get_page(page);
        vmf->page = page;
index 45b6ef5..f227f54 100644 (file)
@@ -2,6 +2,7 @@ config DRM_EXYNOS
        tristate "DRM Support for Samsung SoC EXYNOS Series"
        depends on OF && DRM && (PLAT_SAMSUNG || ARCH_MULTIPLATFORM)
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
index 81192d0..b676006 100644 (file)
@@ -264,7 +264,6 @@ static struct drm_driver exynos_drm_driver = {
        .get_vblank_counter     = drm_vblank_count,
        .enable_vblank          = exynos_drm_crtc_enable_vblank,
        .disable_vblank         = exynos_drm_crtc_disable_vblank,
-       .gem_init_object        = exynos_drm_gem_init_object,
        .gem_free_object        = exynos_drm_gem_free_object,
        .gem_vm_ops             = &exynos_drm_gem_vm_ops,
        .dumb_create            = exynos_drm_gem_dumb_create,
index 868a14d..23da72b 100644 (file)
@@ -716,20 +716,20 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
 {
        /*
         * enable drm irq mode.
-        * - with irq_enabled = 1, we can use the vblank feature.
+        * - with irq_enabled = true, we can use the vblank feature.
         *
         * P.S. note that we wouldn't use drm irq handler but
         *      just specific driver own one instead because
         *      drm framework supports only one irq handler.
         */
-       drm_dev->irq_enabled = 1;
+       drm_dev->irq_enabled = true;
 
        /*
-        * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+        * with vblank_disable_allowed = true, vblank interrupt will be disabled
         * by drm timer once a current process gives up ownership of
         * vblank event.(after drm_vblank_put function is called)
         */
-       drm_dev->vblank_disable_allowed = 1;
+       drm_dev->vblank_disable_allowed = true;
 
        /* attach this sub driver to iommu mapping if supported. */
        if (is_drm_iommu_supported(drm_dev))
index 49f9cd2..1ade191 100644 (file)
@@ -630,11 +630,6 @@ void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
        dma_unmap_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
 }
 
-int exynos_drm_gem_init_object(struct drm_gem_object *obj)
-{
-       return 0;
-}
-
 void exynos_drm_gem_free_object(struct drm_gem_object *obj)
 {
        struct exynos_drm_gem_obj *exynos_gem_obj;
index 09555af..702ec3a 100644 (file)
@@ -135,9 +135,6 @@ unsigned long exynos_drm_gem_get_size(struct drm_device *dev,
                                                unsigned int gem_handle,
                                                struct drm_file *file_priv);
 
-/* initialize gem object. */
-int exynos_drm_gem_init_object(struct drm_gem_object *obj);
-
 /* free gem object. */
 void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj);
 
index 4400330..ddaaedd 100644 (file)
@@ -101,7 +101,6 @@ static struct edid *vidi_get_edid(struct device *dev,
 {
        struct vidi_context *ctx = get_vidi_context(dev);
        struct edid *edid;
-       int edid_len;
 
        /*
         * the edid data comes from user side and it would be set
@@ -112,8 +111,7 @@ static struct edid *vidi_get_edid(struct device *dev,
                return ERR_PTR(-EFAULT);
        }
 
-       edid_len = (1 + ctx->raw_edid->extensions) * EDID_LENGTH;
-       edid = kmemdup(ctx->raw_edid, edid_len, GFP_KERNEL);
+       edid = drm_edid_duplicate(ctx->raw_edid);
        if (!edid) {
                DRM_DEBUG_KMS("failed to allocate edid\n");
                return ERR_PTR(-ENOMEM);
@@ -385,20 +383,20 @@ static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
 {
        /*
         * enable drm irq mode.
-        * - with irq_enabled = 1, we can use the vblank feature.
+        * - with irq_enabled = true, we can use the vblank feature.
         *
         * P.S. note that we wouldn't use drm irq handler but
         *      just specific driver own one instead because
         *      drm framework supports only one irq handler.
         */
-       drm_dev->irq_enabled = 1;
+       drm_dev->irq_enabled = true;
 
        /*
-        * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+        * with vblank_disable_allowed = true, vblank interrupt will be disabled
         * by drm timer once a current process gives up ownership of
         * vblank event.(after drm_vblank_put function is called)
         */
-       drm_dev->vblank_disable_allowed = 1;
+       drm_dev->vblank_disable_allowed = true;
 
        return 0;
 }
@@ -485,7 +483,6 @@ 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;
-       int edid_len;
 
        if (!vidi) {
                DRM_DEBUG_KMS("user data for vidi is null.\n");
@@ -524,8 +521,7 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
                        DRM_DEBUG_KMS("edid data is invalid.\n");
                        return -EINVAL;
                }
-               edid_len = (1 + raw_edid->extensions) * EDID_LENGTH;
-               ctx->raw_edid = kmemdup(raw_edid, edid_len, GFP_KERNEL);
+               ctx->raw_edid = drm_edid_duplicate(raw_edid);
                if (!ctx->raw_edid) {
                        DRM_DEBUG_KMS("failed to allocate raw_edid.\n");
                        return -ENOMEM;
index 1f6e2df..508cf99 100644 (file)
@@ -5,6 +5,7 @@ config DRM_GMA500
        select FB_CFB_FILLRECT
        select FB_CFB_IMAGEBLIT
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select DRM_TTM
        # GMA500 depends on ACPI_VIDEO when ACPI is enabled, just like i915
        select ACPI_VIDEO if ACPI
index 162f686..5a9a6a3 100644 (file)
@@ -634,6 +634,7 @@ const struct psb_ops cdv_chip_ops = {
        .crtcs = 2,
        .hdmi_mask = (1 << 0) | (1 << 1),
        .lvds_mask = (1 << 1),
+       .sdvo_mask = (1 << 0),
        .cursor_needs_phys = 0,
        .sgx_offset = MRST_SGX_OFFSET,
        .chip_setup = cdv_chip_setup,
index f4eb435..f88a181 100644 (file)
@@ -666,7 +666,7 @@ cdv_intel_dp_i2c_init(struct gma_connector *connector,
        strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
        intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0';
        intel_dp->adapter.algo_data = &intel_dp->algo;
-       intel_dp->adapter.dev.parent = &connector->base.kdev;
+       intel_dp->adapter.dev.parent = connector->base.kdev;
 
        if (is_edp(encoder))
                cdv_intel_edp_panel_vdd_on(encoder);
index 01dd7d2..94b3fec 100644 (file)
@@ -714,7 +714,7 @@ static void psb_setup_outputs(struct drm_device *dev)
                        clone_mask = (1 << INTEL_OUTPUT_ANALOG);
                        break;
                case INTEL_OUTPUT_SDVO:
-                       crtc_mask = ((1 << 0) | (1 << 1));
+                       crtc_mask = dev_priv->ops->sdvo_mask;
                        clone_mask = (1 << INTEL_OUTPUT_SDVO);
                        break;
                case INTEL_OUTPUT_LVDS:
index 10ae8c5..e2db48a 100644 (file)
 #include <drm/drm_vma_manager.h>
 #include "psb_drv.h"
 
-int psb_gem_init_object(struct drm_gem_object *obj)
-{
-       return -EINVAL;
-}
-
 void psb_gem_free_object(struct drm_gem_object *obj)
 {
        struct gtt_range *gtt = container_of(obj, struct gtt_range, gem);
index 62cd42e..566d330 100644 (file)
@@ -51,6 +51,9 @@
 #define wait_for(COND, MS) _wait_for(COND, MS, 1)
 #define wait_for_atomic(COND, MS) _wait_for(COND, MS, 0)
 
+#define GMBUS_REG_READ(reg) ioread32(dev_priv->gmbus_reg + (reg))
+#define GMBUS_REG_WRITE(reg, val) iowrite32((val), dev_priv->gmbus_reg + (reg))
+
 /* Intel GPIO access functions */
 
 #define I2C_RISEFALL_TIME 20
@@ -71,7 +74,8 @@ struct intel_gpio {
 void
 gma_intel_i2c_reset(struct drm_device *dev)
 {
-       REG_WRITE(GMBUS0, 0);
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       GMBUS_REG_WRITE(GMBUS0, 0);
 }
 
 static void intel_i2c_quirk_set(struct drm_psb_private *dev_priv, bool enable)
@@ -98,11 +102,10 @@ static void intel_i2c_quirk_set(struct drm_psb_private *dev_priv, bool enable)
 static u32 get_reserved(struct intel_gpio *gpio)
 {
        struct drm_psb_private *dev_priv = gpio->dev_priv;
-       struct drm_device *dev = dev_priv->dev;
        u32 reserved = 0;
 
        /* On most chips, these bits must be preserved in software. */
-       reserved = REG_READ(gpio->reg) &
+       reserved = GMBUS_REG_READ(gpio->reg) &
                                     (GPIO_DATA_PULLUP_DISABLE |
                                      GPIO_CLOCK_PULLUP_DISABLE);
 
@@ -113,29 +116,26 @@ static int get_clock(void *data)
 {
        struct intel_gpio *gpio = data;
        struct drm_psb_private *dev_priv = gpio->dev_priv;
-       struct drm_device *dev = dev_priv->dev;
        u32 reserved = get_reserved(gpio);
-       REG_WRITE(gpio->reg, reserved | GPIO_CLOCK_DIR_MASK);
-       REG_WRITE(gpio->reg, reserved);
-       return (REG_READ(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0;
+       GMBUS_REG_WRITE(gpio->reg, reserved | GPIO_CLOCK_DIR_MASK);
+       GMBUS_REG_WRITE(gpio->reg, reserved);
+       return (GMBUS_REG_READ(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0;
 }
 
 static int get_data(void *data)
 {
        struct intel_gpio *gpio = data;
        struct drm_psb_private *dev_priv = gpio->dev_priv;
-       struct drm_device *dev = dev_priv->dev;
        u32 reserved = get_reserved(gpio);
-       REG_WRITE(gpio->reg, reserved | GPIO_DATA_DIR_MASK);
-       REG_WRITE(gpio->reg, reserved);
-       return (REG_READ(gpio->reg) & GPIO_DATA_VAL_IN) != 0;
+       GMBUS_REG_WRITE(gpio->reg, reserved | GPIO_DATA_DIR_MASK);
+       GMBUS_REG_WRITE(gpio->reg, reserved);
+       return (GMBUS_REG_READ(gpio->reg) & GPIO_DATA_VAL_IN) != 0;
 }
 
 static void set_clock(void *data, int state_high)
 {
        struct intel_gpio *gpio = data;
        struct drm_psb_private *dev_priv = gpio->dev_priv;
-       struct drm_device *dev = dev_priv->dev;
        u32 reserved = get_reserved(gpio);
        u32 clock_bits;
 
@@ -145,15 +145,14 @@ static void set_clock(void *data, int state_high)
                clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
                        GPIO_CLOCK_VAL_MASK;
 
-       REG_WRITE(gpio->reg, reserved | clock_bits);
-       REG_READ(gpio->reg); /* Posting */
+       GMBUS_REG_WRITE(gpio->reg, reserved | clock_bits);
+       GMBUS_REG_READ(gpio->reg); /* Posting */
 }
 
 static void set_data(void *data, int state_high)
 {
        struct intel_gpio *gpio = data;
        struct drm_psb_private *dev_priv = gpio->dev_priv;
-       struct drm_device *dev = dev_priv->dev;
        u32 reserved = get_reserved(gpio);
        u32 data_bits;
 
@@ -163,8 +162,8 @@ static void set_data(void *data, int state_high)
                data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
                        GPIO_DATA_VAL_MASK;
 
-       REG_WRITE(gpio->reg, reserved | data_bits);
-       REG_READ(gpio->reg);
+       GMBUS_REG_WRITE(gpio->reg, reserved | data_bits);
+       GMBUS_REG_READ(gpio->reg);
 }
 
 static struct i2c_adapter *
@@ -251,7 +250,6 @@ gmbus_xfer(struct i2c_adapter *adapter,
                                               struct intel_gmbus,
                                               adapter);
        struct drm_psb_private *dev_priv = adapter->algo_data;
-       struct drm_device *dev = dev_priv->dev;
        int i, reg_offset;
 
        if (bus->force_bit)
@@ -260,28 +258,30 @@ gmbus_xfer(struct i2c_adapter *adapter,
 
        reg_offset = 0;
 
-       REG_WRITE(GMBUS0 + reg_offset, bus->reg0);
+       GMBUS_REG_WRITE(GMBUS0 + reg_offset, bus->reg0);
 
        for (i = 0; i < num; i++) {
                u16 len = msgs[i].len;
                u8 *buf = msgs[i].buf;
 
                if (msgs[i].flags & I2C_M_RD) {
-                       REG_WRITE(GMBUS1 + reg_offset,
-                                  GMBUS_CYCLE_WAIT | (i + 1 == num ? GMBUS_CYCLE_STOP : 0) |
-                                  (len << GMBUS_BYTE_COUNT_SHIFT) |
-                                  (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) |
-                                  GMBUS_SLAVE_READ | GMBUS_SW_RDY);
-                       REG_READ(GMBUS2+reg_offset);
+                       GMBUS_REG_WRITE(GMBUS1 + reg_offset,
+                                       GMBUS_CYCLE_WAIT |
+                                       (i + 1 == num ? GMBUS_CYCLE_STOP : 0) |
+                                       (len << GMBUS_BYTE_COUNT_SHIFT) |
+                                       (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) |
+                                       GMBUS_SLAVE_READ | GMBUS_SW_RDY);
+                       GMBUS_REG_READ(GMBUS2+reg_offset);
                        do {
                                u32 val, loop = 0;
 
-                               if (wait_for(REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50))
+                               if (wait_for(GMBUS_REG_READ(GMBUS2 + reg_offset) &
+                                            (GMBUS_SATOER | GMBUS_HW_RDY), 50))
                                        goto timeout;
-                               if (REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
+                               if (GMBUS_REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
                                        goto clear_err;
 
-                               val = REG_READ(GMBUS3 + reg_offset);
+                               val = GMBUS_REG_READ(GMBUS3 + reg_offset);
                                do {
                                        *buf++ = val & 0xff;
                                        val >>= 8;
@@ -295,18 +295,20 @@ gmbus_xfer(struct i2c_adapter *adapter,
                                val |= *buf++ << (8 * loop);
                        } while (--len && ++loop < 4);
 
-                       REG_WRITE(GMBUS3 + reg_offset, val);
-                       REG_WRITE(GMBUS1 + reg_offset,
+                       GMBUS_REG_WRITE(GMBUS3 + reg_offset, val);
+                       GMBUS_REG_WRITE(GMBUS1 + reg_offset,
                                   (i + 1 == num ? GMBUS_CYCLE_STOP : GMBUS_CYCLE_WAIT) |
                                   (msgs[i].len << GMBUS_BYTE_COUNT_SHIFT) |
                                   (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) |
                                   GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
-                       REG_READ(GMBUS2+reg_offset);
+                       GMBUS_REG_READ(GMBUS2+reg_offset);
 
                        while (len) {
-                               if (wait_for(REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50))
+                               if (wait_for(GMBUS_REG_READ(GMBUS2 + reg_offset) &
+                                            (GMBUS_SATOER | GMBUS_HW_RDY), 50))
                                        goto timeout;
-                               if (REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
+                               if (GMBUS_REG_READ(GMBUS2 + reg_offset) &
+                                   GMBUS_SATOER)
                                        goto clear_err;
 
                                val = loop = 0;
@@ -314,14 +316,14 @@ gmbus_xfer(struct i2c_adapter *adapter,
                                        val |= *buf++ << (8 * loop);
                                } while (--len && ++loop < 4);
 
-                               REG_WRITE(GMBUS3 + reg_offset, val);
-                               REG_READ(GMBUS2+reg_offset);
+                               GMBUS_REG_WRITE(GMBUS3 + reg_offset, val);
+                               GMBUS_REG_READ(GMBUS2+reg_offset);
                        }
                }
 
-               if (i + 1 < num && wait_for(REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), 50))
+               if (i + 1 < num && wait_for(GMBUS_REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), 50))
                        goto timeout;
-               if (REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
+               if (GMBUS_REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
                        goto clear_err;
        }
 
@@ -332,20 +334,20 @@ clear_err:
         * of resetting the GMBUS controller and so clearing the
         * BUS_ERROR raised by the slave's NAK.
         */
-       REG_WRITE(GMBUS1 + reg_offset, GMBUS_SW_CLR_INT);
-       REG_WRITE(GMBUS1 + reg_offset, 0);
+       GMBUS_REG_WRITE(GMBUS1 + reg_offset, GMBUS_SW_CLR_INT);
+       GMBUS_REG_WRITE(GMBUS1 + reg_offset, 0);
 
 done:
        /* Mark the GMBUS interface as disabled. We will re-enable it at the
         * start of the next xfer, till then let it sleep.
         */
-       REG_WRITE(GMBUS0 + reg_offset, 0);
+       GMBUS_REG_WRITE(GMBUS0 + reg_offset, 0);
        return i;
 
 timeout:
        DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n",
                 bus->reg0 & 0xff, bus->adapter.name);
-       REG_WRITE(GMBUS0 + reg_offset, 0);
+       GMBUS_REG_WRITE(GMBUS0 + reg_offset, 0);
 
        /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */
        bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff);
@@ -399,6 +401,11 @@ int gma_intel_setup_gmbus(struct drm_device *dev)
        if (dev_priv->gmbus == NULL)
                return -ENOMEM;
 
+       if (IS_MRST(dev))
+               dev_priv->gmbus_reg = dev_priv->aux_reg;
+       else
+               dev_priv->gmbus_reg = dev_priv->vdc_reg;
+
        for (i = 0; i < GMBUS_NUM_PORTS; i++) {
                struct intel_gmbus *bus = &dev_priv->gmbus[i];
 
@@ -487,6 +494,7 @@ void gma_intel_teardown_gmbus(struct drm_device *dev)
                i2c_del_adapter(&bus->adapter);
        }
 
+       dev_priv->gmbus_reg = NULL; /* iounmap is done in driver_unload */
        kfree(dev_priv->gmbus);
        dev_priv->gmbus = NULL;
 }
index 54c9896..8195e85 100644 (file)
 #include "gma_display.h"
 #include "power.h"
 
-struct psb_intel_range_t {
-       int min, max;
-};
-
-struct oaktrail_limit_t {
-       struct psb_intel_range_t dot, m, p1;
-};
-
-struct oaktrail_clock_t {
-       /* derived values */
-       int dot;
-       int m;
-       int p1;
-};
-
-#define MRST_LIMIT_LVDS_100L       0
-#define MRST_LIMIT_LVDS_83         1
-#define MRST_LIMIT_LVDS_100        2
+#define MRST_LIMIT_LVDS_100L   0
+#define MRST_LIMIT_LVDS_83     1
+#define MRST_LIMIT_LVDS_100    2
+#define MRST_LIMIT_SDVO                3
 
 #define MRST_DOT_MIN             19750
 #define MRST_DOT_MAX             120000
@@ -57,21 +43,40 @@ struct oaktrail_clock_t {
 #define MRST_P1_MAX_0              7
 #define MRST_P1_MAX_1              8
 
-static const struct oaktrail_limit_t oaktrail_limits[] = {
+static bool mrst_lvds_find_best_pll(const struct gma_limit_t *limit,
+                                   struct drm_crtc *crtc, int target,
+                                   int refclk, struct gma_clock_t *best_clock);
+
+static bool mrst_sdvo_find_best_pll(const struct gma_limit_t *limit,
+                                   struct drm_crtc *crtc, int target,
+                                   int refclk, struct gma_clock_t *best_clock);
+
+static const struct gma_limit_t mrst_limits[] = {
        {                       /* MRST_LIMIT_LVDS_100L */
         .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX},
         .m = {.min = MRST_M_MIN_100L, .max = MRST_M_MAX_100L},
         .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1},
+        .find_pll = mrst_lvds_find_best_pll,
         },
        {                       /* MRST_LIMIT_LVDS_83L */
         .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX},
         .m = {.min = MRST_M_MIN_83, .max = MRST_M_MAX_83},
         .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_0},
+        .find_pll = mrst_lvds_find_best_pll,
         },
        {                       /* MRST_LIMIT_LVDS_100 */
         .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX},
         .m = {.min = MRST_M_MIN_100, .max = MRST_M_MAX_100},
         .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1},
+        .find_pll = mrst_lvds_find_best_pll,
+        },
+       {                       /* MRST_LIMIT_SDVO */
+        .vco = {.min = 1400000, .max = 2800000},
+        .n = {.min = 3, .max = 7},
+        .m = {.min = 80, .max = 137},
+        .p1 = {.min = 1, .max = 2},
+        .p2 = {.dot_limit = 200000, .p2_slow = 10, .p2_fast = 10},
+        .find_pll = mrst_sdvo_find_best_pll,
         },
 };
 
@@ -82,9 +87,10 @@ static const u32 oaktrail_m_converts[] = {
        0x12, 0x09, 0x24, 0x32, 0x39, 0x1c,
 };
 
-static const struct oaktrail_limit_t *oaktrail_limit(struct drm_crtc *crtc)
+static const struct gma_limit_t *mrst_limit(struct drm_crtc *crtc,
+                                           int refclk)
 {
-       const struct oaktrail_limit_t *limit = NULL;
+       const struct gma_limit_t *limit = NULL;
        struct drm_device *dev = crtc->dev;
        struct drm_psb_private *dev_priv = dev->dev_private;
 
@@ -92,45 +98,100 @@ static const struct oaktrail_limit_t *oaktrail_limit(struct drm_crtc *crtc)
            || gma_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) {
                switch (dev_priv->core_freq) {
                case 100:
-                       limit = &oaktrail_limits[MRST_LIMIT_LVDS_100L];
+                       limit = &mrst_limits[MRST_LIMIT_LVDS_100L];
                        break;
                case 166:
-                       limit = &oaktrail_limits[MRST_LIMIT_LVDS_83];
+                       limit = &mrst_limits[MRST_LIMIT_LVDS_83];
                        break;
                case 200:
-                       limit = &oaktrail_limits[MRST_LIMIT_LVDS_100];
+                       limit = &mrst_limits[MRST_LIMIT_LVDS_100];
                        break;
                }
+       } else if (gma_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
+               limit = &mrst_limits[MRST_LIMIT_SDVO];
        } else {
                limit = NULL;
-               dev_err(dev->dev, "oaktrail_limit Wrong display type.\n");
+               dev_err(dev->dev, "mrst_limit Wrong display type.\n");
        }
 
        return limit;
 }
 
 /** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
-static void oaktrail_clock(int refclk, struct oaktrail_clock_t *clock)
+static void mrst_lvds_clock(int refclk, struct gma_clock_t *clock)
 {
        clock->dot = (refclk * clock->m) / (14 * clock->p1);
 }
 
-static void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock)
+static void mrst_print_pll(struct gma_clock_t *clock)
 {
-       pr_debug("%s: dotclock = %d,  m = %d, p1 = %d.\n",
-            prefix, clock->dot, clock->m, clock->p1);
+       DRM_DEBUG_DRIVER("dotclock=%d,  m=%d, m1=%d, m2=%d, n=%d, p1=%d, p2=%d\n",
+                        clock->dot, clock->m, clock->m1, clock->m2, clock->n,
+                        clock->p1, clock->p2);
+}
+
+static bool mrst_sdvo_find_best_pll(const struct gma_limit_t *limit,
+                                   struct drm_crtc *crtc, int target,
+                                   int refclk, struct gma_clock_t *best_clock)
+{
+       struct gma_clock_t clock;
+       u32 target_vco, actual_freq;
+       s32 freq_error, min_error = 100000;
+
+       memset(best_clock, 0, sizeof(*best_clock));
+
+       for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
+               for (clock.n = limit->n.min; clock.n <= limit->n.max;
+                    clock.n++) {
+                       for (clock.p1 = limit->p1.min;
+                            clock.p1 <= limit->p1.max; clock.p1++) {
+                               /* p2 value always stored in p2_slow on SDVO */
+                               clock.p = clock.p1 * limit->p2.p2_slow;
+                               target_vco = target * clock.p;
+
+                               /* VCO will increase at this point so break */
+                               if (target_vco > limit->vco.max)
+                                       break;
+
+                               if (target_vco < limit->vco.min)
+                                       continue;
+
+                               actual_freq = (refclk * clock.m) /
+                                             (clock.n * clock.p);
+                               freq_error = 10000 -
+                                            ((target * 10000) / actual_freq);
+
+                               if (freq_error < -min_error) {
+                                       /* freq_error will start to decrease at
+                                          this point so break */
+                                       break;
+                               }
+
+                               if (freq_error < 0)
+                                       freq_error = -freq_error;
+
+                               if (freq_error < min_error) {
+                                       min_error = freq_error;
+                                       *best_clock = clock;
+                               }
+                       }
+               }
+               if (min_error == 0)
+                       break;
+       }
+
+       return min_error == 0;
 }
 
 /**
  * Returns a set of divisors for the desired target clock with the given refclk,
  * or FALSE.  Divisor values are the actual divisors for
  */
-static bool
-mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk,
-               struct oaktrail_clock_t *best_clock)
+static bool mrst_lvds_find_best_pll(const struct gma_limit_t *limit,
+                                   struct drm_crtc *crtc, int target,
+                                   int refclk, struct gma_clock_t *best_clock)
 {
-       struct oaktrail_clock_t clock;
-       const struct oaktrail_limit_t *limit = oaktrail_limit(crtc);
+       struct gma_clock_t clock;
        int err = target;
 
        memset(best_clock, 0, sizeof(*best_clock));
@@ -140,7 +201,7 @@ mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk,
                     clock.p1++) {
                        int this_err;
 
-                       oaktrail_clock(refclk, &clock);
+                       mrst_lvds_clock(refclk, &clock);
 
                        this_err = abs(clock.dot - target);
                        if (this_err < err) {
@@ -149,7 +210,6 @@ mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk,
                        }
                }
        }
-       dev_dbg(crtc->dev->dev, "mrstFindBestPLL err = %d.\n", err);
        return err != target;
 }
 
@@ -167,8 +227,10 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode)
        int pipe = gma_crtc->pipe;
        const struct psb_offset *map = &dev_priv->regmap[pipe];
        u32 temp;
+       int i;
+       int need_aux = gma_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) ? 1 : 0;
 
-       if (pipe == 1) {
+       if (gma_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) {
                oaktrail_crtc_hdmi_dpms(crtc, mode);
                return;
        }
@@ -183,35 +245,45 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode)
        case DRM_MODE_DPMS_ON:
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
-               /* Enable the DPLL */
-               temp = REG_READ(map->dpll);
-               if ((temp & DPLL_VCO_ENABLE) == 0) {
-                       REG_WRITE(map->dpll, temp);
-                       REG_READ(map->dpll);
-                       /* Wait for the clocks to stabilize. */
-                       udelay(150);
-                       REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE);
-                       REG_READ(map->dpll);
-                       /* Wait for the clocks to stabilize. */
-                       udelay(150);
-                       REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE);
-                       REG_READ(map->dpll);
-                       /* Wait for the clocks to stabilize. */
-                       udelay(150);
-               }
-               /* Enable the pipe */
-               temp = REG_READ(map->conf);
-               if ((temp & PIPEACONF_ENABLE) == 0)
-                       REG_WRITE(map->conf, temp | PIPEACONF_ENABLE);
-               /* Enable the plane */
-               temp = REG_READ(map->cntr);
-               if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
-                       REG_WRITE(map->cntr,
-                                 temp | DISPLAY_PLANE_ENABLE);
-                       /* Flush the plane changes */
-                       REG_WRITE(map->base, REG_READ(map->base));
-               }
+               for (i = 0; i <= need_aux; i++) {
+                       /* Enable the DPLL */
+                       temp = REG_READ_WITH_AUX(map->dpll, i);
+                       if ((temp & DPLL_VCO_ENABLE) == 0) {
+                               REG_WRITE_WITH_AUX(map->dpll, temp, i);
+                               REG_READ_WITH_AUX(map->dpll, i);
+                               /* Wait for the clocks to stabilize. */
+                               udelay(150);
+                               REG_WRITE_WITH_AUX(map->dpll,
+                                                  temp | DPLL_VCO_ENABLE, i);
+                               REG_READ_WITH_AUX(map->dpll, i);
+                               /* Wait for the clocks to stabilize. */
+                               udelay(150);
+                               REG_WRITE_WITH_AUX(map->dpll,
+                                                  temp | DPLL_VCO_ENABLE, i);
+                               REG_READ_WITH_AUX(map->dpll, i);
+                               /* Wait for the clocks to stabilize. */
+                               udelay(150);
+                       }
+
+                       /* Enable the pipe */
+                       temp = REG_READ_WITH_AUX(map->conf, i);
+                       if ((temp & PIPEACONF_ENABLE) == 0) {
+                               REG_WRITE_WITH_AUX(map->conf,
+                                                  temp | PIPEACONF_ENABLE, i);
+                       }
+
+                       /* Enable the plane */
+                       temp = REG_READ_WITH_AUX(map->cntr, i);
+                       if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+                               REG_WRITE_WITH_AUX(map->cntr,
+                                                  temp | DISPLAY_PLANE_ENABLE,
+                                                  i);
+                               /* Flush the plane changes */
+                               REG_WRITE_WITH_AUX(map->base,
+                                       REG_READ_WITH_AUX(map->base, i), i);
+                       }
 
+               }
                gma_crtc_load_lut(crtc);
 
                /* Give the overlay scaler a chance to enable
@@ -223,48 +295,52 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode)
                 * if it's on this pipe */
                /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */
 
-               /* Disable the VGA plane that we never use */
-               REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
-               /* Disable display plane */
-               temp = REG_READ(map->cntr);
-               if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
-                       REG_WRITE(map->cntr,
-                                 temp & ~DISPLAY_PLANE_ENABLE);
-                       /* Flush the plane changes */
-                       REG_WRITE(map->base, REG_READ(map->base));
-                       REG_READ(map->base);
-               }
+               for (i = 0; i <= need_aux; i++) {
+                       /* Disable the VGA plane that we never use */
+                       REG_WRITE_WITH_AUX(VGACNTRL, VGA_DISP_DISABLE, i);
+                       /* Disable display plane */
+                       temp = REG_READ_WITH_AUX(map->cntr, i);
+                       if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+                               REG_WRITE_WITH_AUX(map->cntr,
+                                       temp & ~DISPLAY_PLANE_ENABLE, i);
+                               /* Flush the plane changes */
+                               REG_WRITE_WITH_AUX(map->base,
+                                                  REG_READ(map->base), i);
+                               REG_READ_WITH_AUX(map->base, i);
+                       }
 
-               /* Next, disable display pipes */
-               temp = REG_READ(map->conf);
-               if ((temp & PIPEACONF_ENABLE) != 0) {
-                       REG_WRITE(map->conf, temp & ~PIPEACONF_ENABLE);
-                       REG_READ(map->conf);
-               }
-               /* Wait for for the pipe disable to take effect. */
-               gma_wait_for_vblank(dev);
+                       /* Next, disable display pipes */
+                       temp = REG_READ_WITH_AUX(map->conf, i);
+                       if ((temp & PIPEACONF_ENABLE) != 0) {
+                               REG_WRITE_WITH_AUX(map->conf,
+                                                  temp & ~PIPEACONF_ENABLE, i);
+                               REG_READ_WITH_AUX(map->conf, i);
+                       }
+                       /* Wait for for the pipe disable to take effect. */
+                       gma_wait_for_vblank(dev);
+
+                       temp = REG_READ_WITH_AUX(map->dpll, i);
+                       if ((temp & DPLL_VCO_ENABLE) != 0) {
+                               REG_WRITE_WITH_AUX(map->dpll,
+                                                  temp & ~DPLL_VCO_ENABLE, i);
+                               REG_READ_WITH_AUX(map->dpll, i);
+                       }
 
-               temp = REG_READ(map->dpll);
-               if ((temp & DPLL_VCO_ENABLE) != 0) {
-                       REG_WRITE(map->dpll, temp & ~DPLL_VCO_ENABLE);
-                       REG_READ(map->dpll);
+                       /* Wait for the clocks to turn off. */
+                       udelay(150);
                }
-
-               /* Wait for the clocks to turn off. */
-               udelay(150);
                break;
        }
 
-       /*Set FIFO Watermarks*/
-       REG_WRITE(DSPARB, 0x3FFF);
-       REG_WRITE(DSPFW1, 0x3F88080A);
-       REG_WRITE(DSPFW2, 0x0b060808);
+       /* Set FIFO Watermarks (values taken from EMGD) */
+       REG_WRITE(DSPARB, 0x3f80);
+       REG_WRITE(DSPFW1, 0x3f8f0404);
+       REG_WRITE(DSPFW2, 0x04040f04);
        REG_WRITE(DSPFW3, 0x0);
-       REG_WRITE(DSPFW4, 0x08030404);
+       REG_WRITE(DSPFW4, 0x04040404);
        REG_WRITE(DSPFW5, 0x04040404);
        REG_WRITE(DSPFW6, 0x78);
-       REG_WRITE(0x70400, REG_READ(0x70400) | 0x4000);
-       /* Must write Bit 14 of the Chicken Bit Register */
+       REG_WRITE(DSPCHICKENBIT, REG_READ(DSPCHICKENBIT) | 0xc040);
 
        gma_power_end(dev);
 }
@@ -297,7 +373,8 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
        int pipe = gma_crtc->pipe;
        const struct psb_offset *map = &dev_priv->regmap[pipe];
        int refclk = 0;
-       struct oaktrail_clock_t clock;
+       struct gma_clock_t clock;
+       const struct gma_limit_t *limit;
        u32 dpll = 0, fp = 0, dspcntr, pipeconf;
        bool ok, is_sdvo = false;
        bool is_lvds = false;
@@ -306,8 +383,10 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
        struct gma_encoder *gma_encoder = NULL;
        uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN;
        struct drm_connector *connector;
+       int i;
+       int need_aux = gma_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) ? 1 : 0;
 
-       if (pipe == 1)
+       if (gma_pipe_has_type(crtc, INTEL_OUTPUT_HDMI))
                return oaktrail_crtc_hdmi_mode_set(crtc, mode, adjusted_mode, x, y, old_fb);
 
        if (!gma_power_begin(dev, true))
@@ -340,15 +419,17 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
        }
 
        /* Disable the VGA plane that we never use */
-       REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+       for (i = 0; i <= need_aux; i++)
+               REG_WRITE_WITH_AUX(VGACNTRL, VGA_DISP_DISABLE, i);
 
        /* Disable the panel fitter if it was on our pipe */
        if (oaktrail_panel_fitter_pipe(dev) == pipe)
                REG_WRITE(PFIT_CONTROL, 0);
 
-       REG_WRITE(map->src,
-                 ((mode->crtc_hdisplay - 1) << 16) |
-                 (mode->crtc_vdisplay - 1));
+       for (i = 0; i <= need_aux; i++) {
+               REG_WRITE_WITH_AUX(map->src, ((mode->crtc_hdisplay - 1) << 16) |
+                                            (mode->crtc_vdisplay - 1), i);
+       }
 
        if (gma_encoder)
                drm_object_property_get_value(&connector->base,
@@ -365,35 +446,39 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
                offsetY = (adjusted_mode->crtc_vdisplay -
                           mode->crtc_vdisplay) / 2;
 
-               REG_WRITE(map->htotal, (mode->crtc_hdisplay - 1) |
-                       ((adjusted_mode->crtc_htotal - 1) << 16));
-               REG_WRITE(map->vtotal, (mode->crtc_vdisplay - 1) |
-                       ((adjusted_mode->crtc_vtotal - 1) << 16));
-               REG_WRITE(map->hblank,
-                       (adjusted_mode->crtc_hblank_start - offsetX - 1) |
-                       ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16));
-               REG_WRITE(map->hsync,
-                       (adjusted_mode->crtc_hsync_start - offsetX - 1) |
-                       ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16));
-               REG_WRITE(map->vblank,
-                       (adjusted_mode->crtc_vblank_start - offsetY - 1) |
-                       ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16));
-               REG_WRITE(map->vsync,
-                       (adjusted_mode->crtc_vsync_start - offsetY - 1) |
-                       ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16));
+               for (i = 0; i <= need_aux; i++) {
+                       REG_WRITE_WITH_AUX(map->htotal, (mode->crtc_hdisplay - 1) |
+                               ((adjusted_mode->crtc_htotal - 1) << 16), i);
+                       REG_WRITE_WITH_AUX(map->vtotal, (mode->crtc_vdisplay - 1) |
+                               ((adjusted_mode->crtc_vtotal - 1) << 16), i);
+                       REG_WRITE_WITH_AUX(map->hblank,
+                               (adjusted_mode->crtc_hblank_start - offsetX - 1) |
+                               ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16), i);
+                       REG_WRITE_WITH_AUX(map->hsync,
+                               (adjusted_mode->crtc_hsync_start - offsetX - 1) |
+                               ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16), i);
+                       REG_WRITE_WITH_AUX(map->vblank,
+                               (adjusted_mode->crtc_vblank_start - offsetY - 1) |
+                               ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16), i);
+                       REG_WRITE_WITH_AUX(map->vsync,
+                               (adjusted_mode->crtc_vsync_start - offsetY - 1) |
+                               ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16), i);
+               }
        } else {
-               REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) |
-                       ((adjusted_mode->crtc_htotal - 1) << 16));
-               REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) |
-                       ((adjusted_mode->crtc_vtotal - 1) << 16));
-               REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) |
-                       ((adjusted_mode->crtc_hblank_end - 1) << 16));
-               REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) |
-                       ((adjusted_mode->crtc_hsync_end - 1) << 16));
-               REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) |
-                       ((adjusted_mode->crtc_vblank_end - 1) << 16));
-               REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - 1) |
-                       ((adjusted_mode->crtc_vsync_end - 1) << 16));
+               for (i = 0; i <= need_aux; i++) {
+                       REG_WRITE_WITH_AUX(map->htotal, (adjusted_mode->crtc_hdisplay - 1) |
+                               ((adjusted_mode->crtc_htotal - 1) << 16), i);
+                       REG_WRITE_WITH_AUX(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) |
+                               ((adjusted_mode->crtc_vtotal - 1) << 16), i);
+                       REG_WRITE_WITH_AUX(map->hblank, (adjusted_mode->crtc_hblank_start - 1) |
+                               ((adjusted_mode->crtc_hblank_end - 1) << 16), i);
+                       REG_WRITE_WITH_AUX(map->hsync, (adjusted_mode->crtc_hsync_start - 1) |
+                               ((adjusted_mode->crtc_hsync_end - 1) << 16), i);
+                       REG_WRITE_WITH_AUX(map->vblank, (adjusted_mode->crtc_vblank_start - 1) |
+                               ((adjusted_mode->crtc_vblank_end - 1) << 16), i);
+                       REG_WRITE_WITH_AUX(map->vsync, (adjusted_mode->crtc_vsync_start - 1) |
+                               ((adjusted_mode->crtc_vsync_end - 1) << 16), i);
+               }
        }
 
        /* Flush the plane changes */
@@ -418,21 +503,30 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
        if (is_mipi)
                goto oaktrail_crtc_mode_set_exit;
 
-       refclk = dev_priv->core_freq * 1000;
 
        dpll = 0;               /*BIT16 = 0 for 100MHz reference */
 
-       ok = mrstFindBestPLL(crtc, adjusted_mode->clock, refclk, &clock);
+       refclk = is_sdvo ? 96000 : dev_priv->core_freq * 1000;
+       limit = mrst_limit(crtc, refclk);
+       ok = limit->find_pll(limit, crtc, adjusted_mode->clock,
+                            refclk, &clock);
 
-       if (!ok) {
-               dev_dbg(dev->dev, "mrstFindBestPLL fail in oaktrail_crtc_mode_set.\n");
-       } else {
-               dev_dbg(dev->dev, "oaktrail_crtc_mode_set pixel clock = %d,"
-                        "m = %x, p1 = %x.\n", clock.dot, clock.m,
-                        clock.p1);
+       if (is_sdvo) {
+               /* Convert calculated values to register values */
+               clock.p1 = (1L << (clock.p1 - 1));
+               clock.m -= 2;
+               clock.n = (1L << (clock.n - 1));
        }
 
-       fp = oaktrail_m_converts[(clock.m - MRST_M_MIN)] << 8;
+       if (!ok)
+               DRM_ERROR("Failed to find proper PLL settings");
+
+       mrst_print_pll(&clock);
+
+       if (is_sdvo)
+               fp = clock.n << 16 | clock.m;
+       else
+               fp = oaktrail_m_converts[(clock.m - MRST_M_MIN)] << 8;
 
        dpll |= DPLL_VGA_MODE_DIS;
 
@@ -456,38 +550,43 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
 
 
        /* compute bitmask from p1 value */
-       dpll |= (1 << (clock.p1 - 2)) << 17;
+       if (is_sdvo)
+               dpll |= clock.p1 << 16; // dpll |= (1 << (clock.p1 - 1)) << 16;
+       else
+               dpll |= (1 << (clock.p1 - 2)) << 17;
 
        dpll |= DPLL_VCO_ENABLE;
 
-       mrstPrintPll("chosen", &clock);
-
        if (dpll & DPLL_VCO_ENABLE) {
-               REG_WRITE(map->fp0, fp);
-               REG_WRITE(map->dpll, dpll & ~DPLL_VCO_ENABLE);
-               REG_READ(map->dpll);
-               /* Check the DPLLA lock bit PIPEACONF[29] */
-               udelay(150);
+               for (i = 0; i <= need_aux; i++) {
+                       REG_WRITE_WITH_AUX(map->fp0, fp, i);
+                       REG_WRITE_WITH_AUX(map->dpll, dpll & ~DPLL_VCO_ENABLE, i);
+                       REG_READ_WITH_AUX(map->dpll, i);
+                       /* Check the DPLLA lock bit PIPEACONF[29] */
+                       udelay(150);
+               }
        }
 
-       REG_WRITE(map->fp0, fp);
-       REG_WRITE(map->dpll, dpll);
-       REG_READ(map->dpll);
-       /* Wait for the clocks to stabilize. */
-       udelay(150);
+       for (i = 0; i <= need_aux; i++) {
+               REG_WRITE_WITH_AUX(map->fp0, fp, i);
+               REG_WRITE_WITH_AUX(map->dpll, dpll, i);
+               REG_READ_WITH_AUX(map->dpll, i);
+               /* Wait for the clocks to stabilize. */
+               udelay(150);
 
-       /* write it again -- the BIOS does, after all */
-       REG_WRITE(map->dpll, dpll);
-       REG_READ(map->dpll);
-       /* Wait for the clocks to stabilize. */
-       udelay(150);
+               /* write it again -- the BIOS does, after all */
+               REG_WRITE_WITH_AUX(map->dpll, dpll, i);
+               REG_READ_WITH_AUX(map->dpll, i);
+               /* Wait for the clocks to stabilize. */
+               udelay(150);
 
-       REG_WRITE(map->conf, pipeconf);
-       REG_READ(map->conf);
-       gma_wait_for_vblank(dev);
+               REG_WRITE_WITH_AUX(map->conf, pipeconf, i);
+               REG_READ_WITH_AUX(map->conf, i);
+               gma_wait_for_vblank(dev);
 
-       REG_WRITE(map->cntr, dspcntr);
-       gma_wait_for_vblank(dev);
+               REG_WRITE_WITH_AUX(map->cntr, dspcntr, i);
+               gma_wait_for_vblank(dev);
+       }
 
 oaktrail_crtc_mode_set_exit:
        gma_power_end(dev);
@@ -565,3 +664,9 @@ const struct drm_crtc_helper_funcs oaktrail_helper_funcs = {
        .commit = gma_crtc_commit,
 };
 
+/* Not used yet */
+const struct gma_clock_funcs mrst_clock_funcs = {
+       .clock = mrst_lvds_clock,
+       .limit = mrst_limit,
+       .pll_is_valid = gma_pll_is_valid,
+};
index 7a9ce00..368a03a 100644 (file)
@@ -40,6 +40,9 @@ static int oaktrail_output_init(struct drm_device *dev)
                dev_err(dev->dev, "DSI is not supported\n");
        if (dev_priv->hdmi_priv)
                oaktrail_hdmi_init(dev, &dev_priv->mode_dev);
+
+       psb_intel_sdvo_init(dev, SDVOB);
+
        return 0;
 }
 
@@ -526,6 +529,7 @@ static int oaktrail_chip_setup(struct drm_device *dev)
                psb_intel_opregion_init(dev);
                psb_intel_init_bios(dev);
        }
+       gma_intel_setup_gmbus(dev);
        oaktrail_hdmi_setup(dev);
        return 0;
 }
@@ -534,6 +538,7 @@ static void oaktrail_teardown(struct drm_device *dev)
 {
        struct drm_psb_private *dev_priv = dev->dev_private;
 
+       gma_intel_teardown_gmbus(dev);
        oaktrail_hdmi_teardown(dev);
        if (!dev_priv->has_gct)
                psb_intel_destroy_bios(dev);
@@ -546,6 +551,7 @@ const struct psb_ops oaktrail_chip_ops = {
        .crtcs = 2,
        .hdmi_mask = (1 << 1),
        .lvds_mask = (1 << 0),
+       .sdvo_mask = (1 << 1),
        .cursor_needs_phys = 0,
        .sgx_offset = MRST_SGX_OFFSET,
 
index 1eb86c7..e281070 100644 (file)
@@ -99,7 +99,7 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg)
        i2c_dev->status = I2C_STAT_INIT;
        i2c_dev->msg = pmsg;
        i2c_dev->buf_offset = 0;
-       INIT_COMPLETION(i2c_dev->complete);
+       reinit_completion(&i2c_dev->complete);
 
        /* Enable I2C transaction */
        temp = ((pmsg->len) << 20) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION;
index 3ece553..5e06978 100644 (file)
@@ -218,30 +218,6 @@ static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = {
        .commit = oaktrail_lvds_commit,
 };
 
-static struct drm_display_mode lvds_configuration_modes[] = {
-       /* hard coded fixed mode for TPO LTPS LPJ040K001A */
-       { DRM_MODE("800x480",  DRM_MODE_TYPE_DRIVER, 33264, 800, 836,
-                  846, 1056, 0, 480, 489, 491, 525, 0, 0) },
-       /* hard coded fixed mode for LVDS 800x480 */
-       { DRM_MODE("800x480",  DRM_MODE_TYPE_DRIVER, 30994, 800, 801,
-                  802, 1024, 0, 480, 481, 482, 525, 0, 0) },
-       /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */
-       { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072,
-                  1104, 1184, 0, 600, 603, 604, 608, 0, 0) },
-       /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */
-       { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104,
-                  1136, 1184, 0, 600, 603, 604, 608, 0, 0) },
-       /* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */
-       { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124,
-                  1204, 1312, 0, 600, 607, 610, 621, 0, 0) },
-       /* hard coded fixed mode for LVDS 1024x768 */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
-                  1184, 1344, 0, 768, 771, 777, 806, 0, 0) },
-       /* hard coded fixed mode for LVDS 1366x768 */
-       { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430,
-                  1558, 1664, 0, 768, 769, 770, 776, 0, 0) },
-};
-
 /* Returns the panel fixed mode from configuration. */
 
 static void oaktrail_lvds_get_configuration_mode(struct drm_device *dev,
@@ -303,10 +279,10 @@ static void oaktrail_lvds_get_configuration_mode(struct drm_device *dev,
                        mode_dev->panel_fixed_mode =
                                drm_mode_duplicate(dev,
                                        dev_priv->lfp_lvds_vbt_mode);
-       /* Then guess */
+
+       /* If we still got no mode then bail */
        if (mode_dev->panel_fixed_mode == NULL)
-               mode_dev->panel_fixed_mode
-                       = drm_mode_duplicate(dev, &lvds_configuration_modes[2]);
+               return;
 
        drm_mode_set_name(mode_dev->panel_fixed_mode);
        drm_mode_set_crtcinfo(mode_dev->panel_fixed_mode, 0);
index 6976786..23fb33f 100644 (file)
@@ -373,6 +373,7 @@ const struct psb_ops psb_chip_ops = {
        .crtcs = 2,
        .hdmi_mask = (1 << 0),
        .lvds_mask = (1 << 1),
+       .sdvo_mask = (1 << 0),
        .cursor_needs_phys = 1,
        .sgx_offset = PSB_SGX_OFFSET,
        .chip_setup = psb_chip_setup,
index fcb4e9f..1199180 100644 (file)
@@ -251,6 +251,12 @@ static int psb_driver_unload(struct drm_device *dev)
                        iounmap(dev_priv->sgx_reg);
                        dev_priv->sgx_reg = NULL;
                }
+               if (dev_priv->aux_reg) {
+                       iounmap(dev_priv->aux_reg);
+                       dev_priv->aux_reg = NULL;
+               }
+               if (dev_priv->aux_pdev)
+                       pci_dev_put(dev_priv->aux_pdev);
 
                /* Destroy VBT data */
                psb_intel_destroy_bios(dev);
@@ -266,7 +272,7 @@ static int psb_driver_unload(struct drm_device *dev)
 static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
 {
        struct drm_psb_private *dev_priv;
-       unsigned long resource_start;
+       unsigned long resource_start, resource_len;
        unsigned long irqflags;
        int ret = -ENOMEM;
        struct drm_connector *connector;
@@ -296,6 +302,30 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
        if (!dev_priv->sgx_reg)
                goto out_err;
 
+       if (IS_MRST(dev)) {
+               dev_priv->aux_pdev = pci_get_bus_and_slot(0, PCI_DEVFN(3, 0));
+
+               if (dev_priv->aux_pdev) {
+                       resource_start = pci_resource_start(dev_priv->aux_pdev,
+                                                           PSB_AUX_RESOURCE);
+                       resource_len = pci_resource_len(dev_priv->aux_pdev,
+                                                       PSB_AUX_RESOURCE);
+                       dev_priv->aux_reg = ioremap_nocache(resource_start,
+                                                           resource_len);
+                       if (!dev_priv->aux_reg)
+                               goto out_err;
+
+                       DRM_DEBUG_KMS("Found aux vdc");
+               } else {
+                       /* Couldn't find the aux vdc so map to primary vdc */
+                       dev_priv->aux_reg = dev_priv->vdc_reg;
+                       DRM_DEBUG_KMS("Couldn't find aux pci device");
+               }
+               dev_priv->gmbus_reg = dev_priv->aux_reg;
+       } else {
+               dev_priv->gmbus_reg = dev_priv->vdc_reg;
+       }
+
        psb_intel_opregion_setup(dev);
 
        ret = dev_priv->ops->chip_setup(dev);
@@ -359,7 +389,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
 
        drm_irq_install(dev);
 
-       dev->vblank_disable_allowed = 1;
+       dev->vblank_disable_allowed = true;
 
        dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
 
@@ -449,7 +479,7 @@ static int psb_gamma_ioctl(struct drm_device *dev, void *data,
        obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR);
        if (!obj) {
                dev_dbg(dev->dev, "Invalid Connector object.\n");
-               return -EINVAL;
+               return -ENOENT;
        }
 
        connector = obj_to_connector(obj);
@@ -491,7 +521,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data,
                obj = drm_mode_object_find(dev, obj_id,
                                        DRM_MODE_OBJECT_CONNECTOR);
                if (!obj) {
-                       ret = -EINVAL;
+                       ret = -ENOENT;
                        goto mode_op_out;
                }
 
@@ -646,7 +676,6 @@ static struct drm_driver driver = {
        .preclose = psb_driver_preclose,
        .postclose = psb_driver_close,
 
-       .gem_init_object = psb_gem_init_object,
        .gem_free_object = psb_gem_free_object,
        .gem_vm_ops = &psb_gem_vm_ops,
        .dumb_create = psb_gem_dumb_create,
index 4535ac7..b59e658 100644 (file)
@@ -44,10 +44,10 @@ enum {
        CHIP_MFLD_0130 = 3,             /* Medfield */
 };
 
-#define IS_PSB(dev) (((dev)->pci_device & 0xfffe) == 0x8108)
-#define IS_MRST(dev) (((dev)->pci_device & 0xfffc) == 0x4100)
-#define IS_MFLD(dev) (((dev)->pci_device & 0xfff8) == 0x0130)
-#define IS_CDV(dev) (((dev)->pci_device & 0xfff0) == 0x0be0)
+#define IS_PSB(dev) (((dev)->pdev->device & 0xfffe) == 0x8108)
+#define IS_MRST(dev) (((dev)->pdev->device & 0xfff0) == 0x4100)
+#define IS_MFLD(dev) (((dev)->pdev->device & 0xfff8) == 0x0130)
+#define IS_CDV(dev) (((dev)->pdev->device & 0xfff0) == 0x0be0)
 
 /*
  * Driver definitions
@@ -75,6 +75,7 @@ enum {
  *     PCI resource identifiers
  */
 #define PSB_MMIO_RESOURCE       0
+#define PSB_AUX_RESOURCE        0
 #define PSB_GATT_RESOURCE       2
 #define PSB_GTT_RESOURCE        3
 /*
@@ -455,6 +456,7 @@ struct psb_ops;
 
 struct drm_psb_private {
        struct drm_device *dev;
+       struct pci_dev *aux_pdev; /* Currently only used by mrst */
        const struct psb_ops *ops;
        const struct psb_offset *regmap;
        
@@ -486,6 +488,7 @@ struct drm_psb_private {
 
        uint8_t __iomem *sgx_reg;
        uint8_t __iomem *vdc_reg;
+       uint8_t __iomem *aux_reg; /* Auxillary vdc pipe regs */
        uint32_t gatt_free_offset;
 
        /*
@@ -532,6 +535,7 @@ struct drm_psb_private {
 
        /* gmbus */
        struct intel_gmbus *gmbus;
+       uint8_t __iomem *gmbus_reg;
 
        /* Used by SDVO */
        int crt_ddc_pin;
@@ -672,6 +676,7 @@ struct psb_ops {
        int sgx_offset;         /* Base offset of SGX device */
        int hdmi_mask;          /* Mask of HDMI CRTCs */
        int lvds_mask;          /* Mask of LVDS CRTCs */
+       int sdvo_mask;          /* Mask of SDVO CRTCs */
        int cursor_needs_phys;  /* If cursor base reg need physical address */
 
        /* Sub functions */
@@ -837,7 +842,6 @@ extern const struct drm_connector_helper_funcs
 extern const struct drm_connector_funcs psb_intel_lvds_connector_funcs;
 
 /* gem.c */
-extern int psb_gem_init_object(struct drm_gem_object *obj);
 extern void psb_gem_free_object(struct drm_gem_object *obj);
 extern int psb_gem_get_aperture(struct drm_device *dev, void *data,
                        struct drm_file *file);
@@ -928,16 +932,58 @@ static inline uint32_t REGISTER_READ(struct drm_device *dev, uint32_t reg)
        return ioread32(dev_priv->vdc_reg + reg);
 }
 
+static inline uint32_t REGISTER_READ_AUX(struct drm_device *dev, uint32_t reg)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       return ioread32(dev_priv->aux_reg + reg);
+}
+
 #define REG_READ(reg)         REGISTER_READ(dev, (reg))
+#define REG_READ_AUX(reg)      REGISTER_READ_AUX(dev, (reg))
+
+/* Useful for post reads */
+static inline uint32_t REGISTER_READ_WITH_AUX(struct drm_device *dev,
+                                             uint32_t reg, int aux)
+{
+       uint32_t val;
+
+       if (aux)
+               val = REG_READ_AUX(reg);
+       else
+               val = REG_READ(reg);
+
+       return val;
+}
+
+#define REG_READ_WITH_AUX(reg, aux) REGISTER_READ_WITH_AUX(dev, (reg), (aux))
 
 static inline void REGISTER_WRITE(struct drm_device *dev, uint32_t reg,
-                                     uint32_t val)
+                                 uint32_t val)
 {
        struct drm_psb_private *dev_priv = dev->dev_private;
        iowrite32((val), dev_priv->vdc_reg + (reg));
 }
 
+static inline void REGISTER_WRITE_AUX(struct drm_device *dev, uint32_t reg,
+                                     uint32_t val)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       iowrite32((val), dev_priv->aux_reg + (reg));
+}
+
 #define REG_WRITE(reg, val)    REGISTER_WRITE(dev, (reg), (val))
+#define REG_WRITE_AUX(reg, val)        REGISTER_WRITE_AUX(dev, (reg), (val))
+
+static inline void REGISTER_WRITE_WITH_AUX(struct drm_device *dev, uint32_t reg,
+                                     uint32_t val, int aux)
+{
+       if (aux)
+               REG_WRITE_AUX(reg, val);
+       else
+               REG_WRITE(reg, val);
+}
+
+#define REG_WRITE_WITH_AUX(reg, val, aux) REGISTER_WRITE_WITH_AUX(dev, (reg), (val), (aux))
 
 static inline void REGISTER_WRITE16(struct drm_device *dev,
                                        uint32_t reg, uint32_t val)
index 97f8a03..c8841ac 100644 (file)
@@ -572,7 +572,7 @@ int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
 
        if (!drmmode_obj) {
                dev_err(dev->dev, "no such CRTC id\n");
-               return -EINVAL;
+               return -ENOENT;
        }
 
        crtc = to_gma_crtc(obj_to_crtc(drmmode_obj));
index 6f01cdf..07d3a9e 100644 (file)
@@ -228,24 +228,26 @@ static void psb_intel_sdvo_write_sdvox(struct psb_intel_sdvo *psb_intel_sdvo, u3
 {
        struct drm_device *dev = psb_intel_sdvo->base.base.dev;
        u32 bval = val, cval = val;
-       int i;
+       int i, j;
+       int need_aux = IS_MRST(dev) ? 1 : 0;
 
-       if (psb_intel_sdvo->sdvo_reg == SDVOB) {
-               cval = REG_READ(SDVOC);
-       } else {
-               bval = REG_READ(SDVOB);
-       }
-       /*
-        * Write the registers twice for luck. Sometimes,
-        * writing them only once doesn't appear to 'stick'.
-        * The BIOS does this too. Yay, magic
-        */
-       for (i = 0; i < 2; i++)
-       {
-               REG_WRITE(SDVOB, bval);
-               REG_READ(SDVOB);
-               REG_WRITE(SDVOC, cval);
-               REG_READ(SDVOC);
+       for (j = 0; j <= need_aux; j++) {
+               if (psb_intel_sdvo->sdvo_reg == SDVOB)
+                       cval = REG_READ_WITH_AUX(SDVOC, j);
+               else
+                       bval = REG_READ_WITH_AUX(SDVOB, j);
+
+               /*
+               * Write the registers twice for luck. Sometimes,
+               * writing them only once doesn't appear to 'stick'.
+               * The BIOS does this too. Yay, magic
+               */
+               for (i = 0; i < 2; i++) {
+                       REG_WRITE_WITH_AUX(SDVOB, bval, j);
+                       REG_READ_WITH_AUX(SDVOB, j);
+                       REG_WRITE_WITH_AUX(SDVOC, cval, j);
+                       REG_READ_WITH_AUX(SDVOC, j);
+               }
        }
 }
 
@@ -995,6 +997,7 @@ static void psb_intel_sdvo_mode_set(struct drm_encoder *encoder,
        struct psb_intel_sdvo_dtd input_dtd;
        int pixel_multiplier = psb_intel_mode_get_pixel_multiplier(adjusted_mode);
        int rate;
+       int need_aux = IS_MRST(dev) ? 1 : 0;
 
        if (!mode)
                return;
@@ -1060,7 +1063,11 @@ static void psb_intel_sdvo_mode_set(struct drm_encoder *encoder,
                return;
 
        /* Set the SDVO control regs. */
-       sdvox = REG_READ(psb_intel_sdvo->sdvo_reg);
+       if (need_aux)
+               sdvox = REG_READ_AUX(psb_intel_sdvo->sdvo_reg);
+       else
+               sdvox = REG_READ(psb_intel_sdvo->sdvo_reg);
+
        switch (psb_intel_sdvo->sdvo_reg) {
        case SDVOB:
                sdvox &= SDVOB_PRESERVE_MASK;
@@ -1090,6 +1097,8 @@ static void psb_intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
        struct drm_device *dev = encoder->dev;
        struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder);
        u32 temp;
+       int i;
+       int need_aux = IS_MRST(dev) ? 1 : 0;
 
        switch (mode) {
        case DRM_MODE_DPMS_ON:
@@ -1108,19 +1117,27 @@ static void psb_intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
                        psb_intel_sdvo_set_encoder_power_state(psb_intel_sdvo, mode);
 
                if (mode == DRM_MODE_DPMS_OFF) {
-                       temp = REG_READ(psb_intel_sdvo->sdvo_reg);
+                       if (need_aux)
+                               temp = REG_READ_AUX(psb_intel_sdvo->sdvo_reg);
+                       else
+                               temp = REG_READ(psb_intel_sdvo->sdvo_reg);
+
                        if ((temp & SDVO_ENABLE) != 0) {
                                psb_intel_sdvo_write_sdvox(psb_intel_sdvo, temp & ~SDVO_ENABLE);
                        }
                }
        } else {
                bool input1, input2;
-               int i;
                u8 status;
 
-               temp = REG_READ(psb_intel_sdvo->sdvo_reg);
+               if (need_aux)
+                       temp = REG_READ_AUX(psb_intel_sdvo->sdvo_reg);
+               else
+                       temp = REG_READ(psb_intel_sdvo->sdvo_reg);
+
                if ((temp & SDVO_ENABLE) == 0)
                        psb_intel_sdvo_write_sdvox(psb_intel_sdvo, temp | SDVO_ENABLE);
+
                for (i = 0; i < 2; i++)
                        gma_wait_for_vblank(dev);
 
index 029eccf..ba48303 100644 (file)
@@ -271,15 +271,15 @@ void psb_irq_preinstall(struct drm_device *dev)
 
        if (gma_power_is_on(dev))
                PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
-       if (dev->vblank_enabled[0])
+       if (dev->vblank[0].enabled)
                dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG;
-       if (dev->vblank_enabled[1])
+       if (dev->vblank[1].enabled)
                dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG;
 
        /* FIXME: Handle Medfield irq mask
-       if (dev->vblank_enabled[1])
+       if (dev->vblank[1].enabled)
                dev_priv->vdc_irq_mask |= _MDFLD_PIPEB_EVENT_FLAG;
-       if (dev->vblank_enabled[2])
+       if (dev->vblank[2].enabled)
                dev_priv->vdc_irq_mask |= _MDFLD_PIPEC_EVENT_FLAG;
        */
 
@@ -305,17 +305,17 @@ int psb_irq_postinstall(struct drm_device *dev)
        PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
        PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
 
-       if (dev->vblank_enabled[0])
+       if (dev->vblank[0].enabled)
                psb_enable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE);
        else
                psb_disable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE);
 
-       if (dev->vblank_enabled[1])
+       if (dev->vblank[1].enabled)
                psb_enable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE);
        else
                psb_disable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE);
 
-       if (dev->vblank_enabled[2])
+       if (dev->vblank[2].enabled)
                psb_enable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE);
        else
                psb_disable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE);
@@ -339,13 +339,13 @@ void psb_irq_uninstall(struct drm_device *dev)
 
        PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
 
-       if (dev->vblank_enabled[0])
+       if (dev->vblank[0].enabled)
                psb_disable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE);
 
-       if (dev->vblank_enabled[1])
+       if (dev->vblank[1].enabled)
                psb_disable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE);
 
-       if (dev->vblank_enabled[2])
+       if (dev->vblank[2].enabled)
                psb_disable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE);
 
        dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG |
@@ -456,7 +456,7 @@ static int psb_vblank_do_wait(struct drm_device *dev,
 {
        unsigned int cur_vblank;
        int ret = 0;
-       DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
+       DRM_WAIT_ON(ret, dev->vblank.queue, 3 * DRM_HZ,
                    (((cur_vblank = atomic_read(counter))
                      - *sequence) <= (1 << 23)));
        *sequence = cur_vblank;
index 60e8404..400b0c4 100644 (file)
@@ -17,6 +17,7 @@
 
 
 
+#include <linux/hdmi.h>
 #include <linux/module.h>
 
 #include <drm/drmP.h>
@@ -549,6 +550,8 @@ tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
        buf[HB(0)] = 0x82;
        buf[HB(1)] = 0x02;
        buf[HB(2)] = 13;
+       buf[PB(1)] = HDMI_SCAN_MODE_UNDERSCAN;
+       buf[PB(3)] = HDMI_QUANTIZATION_RANGE_FULL << 2;
        buf[PB(4)] = drm_match_cea_mode(mode);
 
        tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
index ab1892e..249fdff 100644 (file)
@@ -944,8 +944,6 @@ static int i810_dma_vertex(struct drm_device *dev, void *data,
                                 dma->buflist[vertex->idx],
                                 vertex->discard, vertex->used);
 
-       atomic_add(vertex->used, &dev->counts[_DRM_STAT_SECONDARY]);
-       atomic_inc(&dev->counts[_DRM_STAT_DMA]);
        sarea_priv->last_enqueue = dev_priv->counter - 1;
        sarea_priv->last_dispatch = (int)hw_status[5];
 
@@ -1105,8 +1103,6 @@ static int i810_dma_mc(struct drm_device *dev, void *data,
        i810_dma_dispatch_mc(dev, dma->buflist[mc->idx], mc->used,
                             mc->last_render);
 
-       atomic_add(mc->used, &dev->counts[_DRM_STAT_SECONDARY]);
-       atomic_inc(&dev->counts[_DRM_STAT_DMA]);
        sarea_priv->last_enqueue = dev_priv->counter - 1;
        sarea_priv->last_dispatch = (int)hw_status[5];
 
@@ -1197,13 +1193,6 @@ static int i810_flip_bufs(struct drm_device *dev, void *data,
 
 int i810_driver_load(struct drm_device *dev, unsigned long flags)
 {
-       /* i810 has 4 more counters */
-       dev->counters += 4;
-       dev->types[6] = _DRM_STAT_IRQ;
-       dev->types[7] = _DRM_STAT_PRIMARY;
-       dev->types[8] = _DRM_STAT_SECONDARY;
-       dev->types[9] = _DRM_STAT_DMA;
-
        pci_set_master(dev->pdev);
 
        return 0;
diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
new file mode 100644 (file)
index 0000000..6199d0b
--- /dev/null
@@ -0,0 +1,67 @@
+config DRM_I915
+       tristate "Intel 8xx/9xx/G3x/G4x/HD Graphics"
+       depends on DRM
+       depends on AGP
+       depends on AGP_INTEL
+       # we need shmfs for the swappable backing store, and in particular
+       # the shmem_readpage() which depends upon tmpfs
+       select SHMEM
+       select TMPFS
+       select DRM_KMS_HELPER
+       # i915 depends on ACPI_VIDEO when ACPI is enabled
+       # but for select to work, need to select ACPI_VIDEO's dependencies, ick
+       select BACKLIGHT_LCD_SUPPORT if ACPI
+       select BACKLIGHT_CLASS_DEVICE if ACPI
+       select VIDEO_OUTPUT_CONTROL if ACPI
+       select INPUT if ACPI
+       select ACPI_VIDEO if ACPI
+       select ACPI_BUTTON if ACPI
+       help
+         Choose this option if you have a system that has "Intel Graphics
+         Media Accelerator" or "HD Graphics" integrated graphics,
+         including 830M, 845G, 852GM, 855GM, 865G, 915G, 945G, 965G,
+         G35, G41, G43, G45 chipsets and Celeron, Pentium, Core i3,
+         Core i5, Core i7 as well as Atom CPUs with integrated graphics.
+         If M is selected, the module will be called i915.  AGP support
+         is required for this driver to work. This driver is used by
+         the Intel driver in X.org 6.8 and XFree86 4.4 and above. It
+         replaces the older i830 module that supported a subset of the
+         hardware in older X.org releases.
+
+         Note that the older i810/i815 chipsets require the use of the
+         i810 driver instead, and the Atom z5xx series has an entirely
+         different implementation.
+
+config DRM_I915_KMS
+       bool "Enable modesetting on intel by default"
+       depends on DRM_I915
+       help
+         Choose this option if you want kernel modesetting enabled by default,
+         and you have a new enough userspace to support this. Running old
+         userspaces with this enabled will cause pain.  Note that this causes
+         the driver to bind to PCI devices, which precludes loading things
+         like intelfb.
+
+config DRM_I915_FBDEV
+       bool "Enable legacy fbdev support for the modesettting intel driver"
+       depends on DRM_I915
+       select DRM_KMS_FB_HELPER
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       default y
+       help
+         Choose this option if you have a need for the legacy fbdev
+         support. Note that this support also provide the linux console
+         support on top of the intel modesetting driver.
+
+config DRM_I915_PRELIMINARY_HW_SUPPORT
+       bool "Enable preliminary support for prerelease Intel hardware by default"
+       depends on DRM_I915
+       help
+         Choose this option if you have prerelease Intel hardware and want the
+         i915 driver to support it by default.  You can enable such support at
+         runtime with the module option i915.preliminary_hw_support=1; this
+         option changes the default for that module option.
+
+         If in doubt, say "N".
index b8449a8..41838ea 100644 (file)
@@ -21,6 +21,9 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
          intel_display.o \
          intel_crt.o \
          intel_lvds.o \
+         intel_dsi.o \
+         intel_dsi_cmd.o \
+         intel_dsi_pll.o \
          intel_bios.o \
          intel_ddi.o \
          intel_dp.o \
@@ -30,7 +33,6 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
          intel_panel.o \
          intel_pm.o \
          intel_i2c.o \
-         intel_fb.o \
          intel_tv.o \
          intel_dvo.o \
          intel_ringbuffer.o \
@@ -51,6 +53,8 @@ i915-$(CONFIG_COMPAT)   += i915_ioc32.o
 
 i915-$(CONFIG_ACPI)    += intel_acpi.o
 
+i915-$(CONFIG_DRM_I915_FBDEV) += intel_fbdev.o
+
 obj-$(CONFIG_DRM_I915)  += i915.o
 
 CFLAGS_i915_trace_points.o := -I$(src)
index 33a62ad..3121633 100644 (file)
@@ -77,17 +77,6 @@ struct intel_dvo_dev_ops {
                          struct drm_display_mode *mode);
 
        /*
-        * Callback to adjust the mode to be set in the CRTC.
-        *
-        * This allows an output to adjust the clock or even the entire set of
-        * timings, which is used for panels with fixed timings or for
-        * buses with clock limitations.
-        */
-       bool (*mode_fixup)(struct intel_dvo_device *dvo,
-                          const struct drm_display_mode *mode,
-                          struct drm_display_mode *adjusted_mode);
-
-       /*
         * Callback for preparing mode changes on an output
         */
        void (*prepare)(struct intel_dvo_device *dvo);
index a6f4cb5..6ed45a9 100644 (file)
@@ -27,6 +27,8 @@
  */
 
 #include <linux/seq_file.h>
+#include <linux/circ_buf.h>
+#include <linux/ctype.h>
 #include <linux/debugfs.h>
 #include <linux/slab.h>
 #include <linux/export.h>
@@ -38,9 +40,6 @@
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 
-#define DRM_I915_RING_DEBUG 1
-
-
 #if defined(CONFIG_DEBUG_FS)
 
 enum {
@@ -54,6 +53,32 @@ static const char *yesno(int v)
        return v ? "yes" : "no";
 }
 
+/* As the drm_debugfs_init() routines are called before dev->dev_private is
+ * allocated we need to hook into the minor for release. */
+static int
+drm_add_fake_info_node(struct drm_minor *minor,
+                      struct dentry *ent,
+                      const void *key)
+{
+       struct drm_info_node *node;
+
+       node = kmalloc(sizeof(*node), GFP_KERNEL);
+       if (node == NULL) {
+               debugfs_remove(ent);
+               return -ENOMEM;
+       }
+
+       node->minor = minor;
+       node->dent = ent;
+       node->info_ent = (void *) key;
+
+       mutex_lock(&minor->debugfs_lock);
+       list_add(&node->list, &minor->debugfs_list);
+       mutex_unlock(&minor->debugfs_lock);
+
+       return 0;
+}
+
 static int i915_capabilities(struct seq_file *m, void *data)
 {
        struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -145,6 +170,13 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
                seq_printf(m, " (%s)", obj->ring->name);
 }
 
+static void describe_ctx(struct seq_file *m, struct i915_hw_context *ctx)
+{
+       seq_putc(m, ctx->is_initialized ? 'I' : 'i');
+       seq_putc(m, ctx->remap_slice ? 'R' : 'r');
+       seq_putc(m, ' ');
+}
+
 static int i915_gem_object_list_info(struct seq_file *m, void *data)
 {
        struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -554,7 +586,53 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
        if (ret)
                return ret;
 
-       if (IS_VALLEYVIEW(dev)) {
+       if (INTEL_INFO(dev)->gen >= 8) {
+               int i;
+               seq_printf(m, "Master Interrupt Control:\t%08x\n",
+                          I915_READ(GEN8_MASTER_IRQ));
+
+               for (i = 0; i < 4; i++) {
+                       seq_printf(m, "GT Interrupt IMR %d:\t%08x\n",
+                                  i, I915_READ(GEN8_GT_IMR(i)));
+                       seq_printf(m, "GT Interrupt IIR %d:\t%08x\n",
+                                  i, I915_READ(GEN8_GT_IIR(i)));
+                       seq_printf(m, "GT Interrupt IER %d:\t%08x\n",
+                                  i, I915_READ(GEN8_GT_IER(i)));
+               }
+
+               for_each_pipe(i) {
+                       seq_printf(m, "Pipe %c IMR:\t%08x\n",
+                                  pipe_name(i),
+                                  I915_READ(GEN8_DE_PIPE_IMR(i)));
+                       seq_printf(m, "Pipe %c IIR:\t%08x\n",
+                                  pipe_name(i),
+                                  I915_READ(GEN8_DE_PIPE_IIR(i)));
+                       seq_printf(m, "Pipe %c IER:\t%08x\n",
+                                  pipe_name(i),
+                                  I915_READ(GEN8_DE_PIPE_IER(i)));
+               }
+
+               seq_printf(m, "Display Engine port interrupt mask:\t%08x\n",
+                          I915_READ(GEN8_DE_PORT_IMR));
+               seq_printf(m, "Display Engine port interrupt identity:\t%08x\n",
+                          I915_READ(GEN8_DE_PORT_IIR));
+               seq_printf(m, "Display Engine port interrupt enable:\t%08x\n",
+                          I915_READ(GEN8_DE_PORT_IER));
+
+               seq_printf(m, "Display Engine misc interrupt mask:\t%08x\n",
+                          I915_READ(GEN8_DE_MISC_IMR));
+               seq_printf(m, "Display Engine misc interrupt identity:\t%08x\n",
+                          I915_READ(GEN8_DE_MISC_IIR));
+               seq_printf(m, "Display Engine misc interrupt enable:\t%08x\n",
+                          I915_READ(GEN8_DE_MISC_IER));
+
+               seq_printf(m, "PCU interrupt mask:\t%08x\n",
+                          I915_READ(GEN8_PCU_IMR));
+               seq_printf(m, "PCU interrupt identity:\t%08x\n",
+                          I915_READ(GEN8_PCU_IIR));
+               seq_printf(m, "PCU interrupt enable:\t%08x\n",
+                          I915_READ(GEN8_PCU_IER));
+       } else if (IS_VALLEYVIEW(dev)) {
                seq_printf(m, "Display IER:\t%08x\n",
                           I915_READ(VLV_IER));
                seq_printf(m, "Display IIR:\t%08x\n",
@@ -626,7 +704,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
        seq_printf(m, "Interrupts received: %d\n",
                   atomic_read(&dev_priv->irq_received));
        for_each_ring(ring, dev_priv, i) {
-               if (IS_GEN6(dev) || IS_GEN7(dev)) {
+               if (INTEL_INFO(dev)->gen >= 6) {
                        seq_printf(m,
                                   "Graphics Interrupt mask (%s):       %08x\n",
                                   ring->name, I915_READ_IMR(ring));
@@ -843,6 +921,8 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
        drm_i915_private_t *dev_priv = dev->dev_private;
        int ret;
 
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
        if (IS_GEN5(dev)) {
                u16 rgvswctl = I915_READ16(MEMSWCTL);
                u16 rgvstat = I915_READ16(MEMSTAT_ILK);
@@ -1321,6 +1401,8 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
                return 0;
        }
 
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
        ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
        if (ret)
                return ret;
@@ -1395,12 +1477,12 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
 {
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        struct drm_device *dev = node->minor->dev;
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       struct intel_fbdev *ifbdev;
+       struct intel_fbdev *ifbdev = NULL;
        struct intel_framebuffer *fb;
-       int ret;
 
-       ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+#ifdef CONFIG_DRM_I915_FBDEV
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret = mutex_lock_interruptible(&dev->mode_config.mutex);
        if (ret)
                return ret;
 
@@ -1416,10 +1498,11 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
        describe_obj(m, fb->obj);
        seq_putc(m, '\n');
        mutex_unlock(&dev->mode_config.mutex);
+#endif
 
        mutex_lock(&dev->mode_config.fb_lock);
        list_for_each_entry(fb, &dev->mode_config.fb_list, base.head) {
-               if (&fb->base == ifbdev->helper.fb)
+               if (ifbdev && &fb->base == ifbdev->helper.fb)
                        continue;
 
                seq_printf(m, "user size: %d x %d, depth %d, %d bpp, refcount %d, obj ",
@@ -1442,6 +1525,7 @@ static int i915_context_status(struct seq_file *m, void *unused)
        struct drm_device *dev = node->minor->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct intel_ring_buffer *ring;
+       struct i915_hw_context *ctx;
        int ret, i;
 
        ret = mutex_lock_interruptible(&dev->mode_config.mutex);
@@ -1460,12 +1544,15 @@ static int i915_context_status(struct seq_file *m, void *unused)
                seq_putc(m, '\n');
        }
 
-       for_each_ring(ring, dev_priv, i) {
-               if (ring->default_context) {
-                       seq_printf(m, "HW default context %s ring ", ring->name);
-                       describe_obj(m, ring->default_context->obj);
-                       seq_putc(m, '\n');
-               }
+       list_for_each_entry(ctx, &dev_priv->context_list, link) {
+               seq_puts(m, "HW context ");
+               describe_ctx(m, ctx);
+               for_each_ring(ring, dev_priv, i)
+                       if (ring->default_context == ctx)
+                               seq_printf(m, "(default context %s) ", ring->name);
+
+               describe_obj(m, ctx->obj);
+               seq_putc(m, '\n');
        }
 
        mutex_unlock(&dev->mode_config.mutex);
@@ -1536,7 +1623,7 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
                           I915_READ16(C0DRB3));
                seq_printf(m, "C1DRB3 = 0x%04x\n",
                           I915_READ16(C1DRB3));
-       } else if (IS_GEN6(dev) || IS_GEN7(dev)) {
+       } else if (INTEL_INFO(dev)->gen >= 6) {
                seq_printf(m, "MAD_DIMM_C0 = 0x%08x\n",
                           I915_READ(MAD_DIMM_C0));
                seq_printf(m, "MAD_DIMM_C1 = 0x%08x\n",
@@ -1545,8 +1632,12 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
                           I915_READ(MAD_DIMM_C2));
                seq_printf(m, "TILECTL = 0x%08x\n",
                           I915_READ(TILECTL));
-               seq_printf(m, "ARB_MODE = 0x%08x\n",
-                          I915_READ(ARB_MODE));
+               if (IS_GEN8(dev))
+                       seq_printf(m, "GAMTARBMODE = 0x%08x\n",
+                                  I915_READ(GAMTARBMODE));
+               else
+                       seq_printf(m, "ARB_MODE = 0x%08x\n",
+                                  I915_READ(ARB_MODE));
                seq_printf(m, "DISP_ARB_CTL = 0x%08x\n",
                           I915_READ(DISP_ARB_CTL));
        }
@@ -1555,18 +1646,37 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
        return 0;
 }
 
-static int i915_ppgtt_info(struct seq_file *m, void *data)
+static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
-       struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_ring_buffer *ring;
-       int i, ret;
+       struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
+       int unused, i;
 
+       if (!ppgtt)
+               return;
+
+       seq_printf(m, "Page directories: %d\n", ppgtt->num_pd_pages);
+       seq_printf(m, "Page tables: %d\n", ppgtt->num_pt_pages);
+       for_each_ring(ring, dev_priv, unused) {
+               seq_printf(m, "%s\n", ring->name);
+               for (i = 0; i < 4; i++) {
+                       u32 offset = 0x270 + i * 8;
+                       u64 pdp = I915_READ(ring->mmio_base + offset + 4);
+                       pdp <<= 32;
+                       pdp |= I915_READ(ring->mmio_base + offset);
+                       for (i = 0; i < 4; i++)
+                               seq_printf(m, "\tPDP%d 0x%016llx\n", i, pdp);
+               }
+       }
+}
+
+static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring;
+       int i;
 
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
-       if (ret)
-               return ret;
        if (INTEL_INFO(dev)->gen == 6)
                seq_printf(m, "GFX_MODE: 0x%08x\n", I915_READ(GFX_MODE));
 
@@ -1585,6 +1695,22 @@ static int i915_ppgtt_info(struct seq_file *m, void *data)
                seq_printf(m, "pd gtt offset: 0x%08x\n", ppgtt->pd_offset);
        }
        seq_printf(m, "ECOCHK: 0x%08x\n", I915_READ(GAM_ECOCHK));
+}
+
+static int i915_ppgtt_info(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+
+       int ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       if (INTEL_INFO(dev)->gen >= 8)
+               gen8_ppgtt_info(m, dev);
+       else if (INTEL_INFO(dev)->gen >= 6)
+               gen6_ppgtt_info(m, dev);
+
        mutex_unlock(&dev->struct_mutex);
 
        return 0;
@@ -1610,27 +1736,27 @@ static int i915_dpio_info(struct seq_file *m, void *data)
        seq_printf(m, "DPIO_CTL: 0x%08x\n", I915_READ(DPIO_CTL));
 
        seq_printf(m, "DPIO_DIV_A: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, _DPIO_DIV_A));
+                  vlv_dpio_read(dev_priv, PIPE_A, _DPIO_DIV_A));
        seq_printf(m, "DPIO_DIV_B: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, _DPIO_DIV_B));
+                  vlv_dpio_read(dev_priv, PIPE_A, _DPIO_DIV_B));
 
        seq_printf(m, "DPIO_REFSFR_A: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, _DPIO_REFSFR_A));
+                  vlv_dpio_read(dev_priv, PIPE_A, _DPIO_REFSFR_A));
        seq_printf(m, "DPIO_REFSFR_B: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, _DPIO_REFSFR_B));
+                  vlv_dpio_read(dev_priv, PIPE_A, _DPIO_REFSFR_B));
 
        seq_printf(m, "DPIO_CORE_CLK_A: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, _DPIO_CORE_CLK_A));
+                  vlv_dpio_read(dev_priv, PIPE_A, _DPIO_CORE_CLK_A));
        seq_printf(m, "DPIO_CORE_CLK_B: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, _DPIO_CORE_CLK_B));
+                  vlv_dpio_read(dev_priv, PIPE_A, _DPIO_CORE_CLK_B));
 
        seq_printf(m, "DPIO_LPF_COEFF_A: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, _DPIO_LPF_COEFF_A));
+                  vlv_dpio_read(dev_priv, PIPE_A, _DPIO_LPF_COEFF_A));
        seq_printf(m, "DPIO_LPF_COEFF_B: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, _DPIO_LPF_COEFF_B));
+                  vlv_dpio_read(dev_priv, PIPE_A, _DPIO_LPF_COEFF_B));
 
        seq_printf(m, "DPIO_FASTCLK_DISABLE: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, DPIO_FASTCLK_DISABLE));
+                  vlv_dpio_read(dev_priv, PIPE_A, DPIO_FASTCLK_DISABLE));
 
        mutex_unlock(&dev_priv->dpio_lock);
 
@@ -1655,126 +1781,20 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
        struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 psrstat, psrperf;
-
-       if (!IS_HASWELL(dev)) {
-               seq_puts(m, "PSR not supported on this platform\n");
-       } else if (IS_HASWELL(dev) && I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE) {
-               seq_puts(m, "PSR enabled\n");
-       } else {
-               seq_puts(m, "PSR disabled: ");
-               switch (dev_priv->no_psr_reason) {
-               case PSR_NO_SOURCE:
-                       seq_puts(m, "not supported on this platform");
-                       break;
-               case PSR_NO_SINK:
-                       seq_puts(m, "not supported by panel");
-                       break;
-               case PSR_MODULE_PARAM:
-                       seq_puts(m, "disabled by flag");
-                       break;
-               case PSR_CRTC_NOT_ACTIVE:
-                       seq_puts(m, "crtc not active");
-                       break;
-               case PSR_PWR_WELL_ENABLED:
-                       seq_puts(m, "power well enabled");
-                       break;
-               case PSR_NOT_TILED:
-                       seq_puts(m, "not tiled");
-                       break;
-               case PSR_SPRITE_ENABLED:
-                       seq_puts(m, "sprite enabled");
-                       break;
-               case PSR_S3D_ENABLED:
-                       seq_puts(m, "stereo 3d enabled");
-                       break;
-               case PSR_INTERLACED_ENABLED:
-                       seq_puts(m, "interlaced enabled");
-                       break;
-               case PSR_HSW_NOT_DDIA:
-                       seq_puts(m, "HSW ties PSR to DDI A (eDP)");
-                       break;
-               default:
-                       seq_puts(m, "unknown reason");
-               }
-               seq_puts(m, "\n");
-               return 0;
-       }
-
-       psrstat = I915_READ(EDP_PSR_STATUS_CTL);
-
-       seq_puts(m, "PSR Current State: ");
-       switch (psrstat & EDP_PSR_STATUS_STATE_MASK) {
-       case EDP_PSR_STATUS_STATE_IDLE:
-               seq_puts(m, "Reset state\n");
-               break;
-       case EDP_PSR_STATUS_STATE_SRDONACK:
-               seq_puts(m, "Wait for TG/Stream to send on frame of data after SRD conditions are met\n");
-               break;
-       case EDP_PSR_STATUS_STATE_SRDENT:
-               seq_puts(m, "SRD entry\n");
-               break;
-       case EDP_PSR_STATUS_STATE_BUFOFF:
-               seq_puts(m, "Wait for buffer turn off\n");
-               break;
-       case EDP_PSR_STATUS_STATE_BUFON:
-               seq_puts(m, "Wait for buffer turn on\n");
-               break;
-       case EDP_PSR_STATUS_STATE_AUXACK:
-               seq_puts(m, "Wait for AUX to acknowledge on SRD exit\n");
-               break;
-       case EDP_PSR_STATUS_STATE_SRDOFFACK:
-               seq_puts(m, "Wait for TG/Stream to acknowledge the SRD VDM exit\n");
-               break;
-       default:
-               seq_puts(m, "Unknown\n");
-               break;
-       }
-
-       seq_puts(m, "Link Status: ");
-       switch (psrstat & EDP_PSR_STATUS_LINK_MASK) {
-       case EDP_PSR_STATUS_LINK_FULL_OFF:
-               seq_puts(m, "Link is fully off\n");
-               break;
-       case EDP_PSR_STATUS_LINK_FULL_ON:
-               seq_puts(m, "Link is fully on\n");
-               break;
-       case EDP_PSR_STATUS_LINK_STANDBY:
-               seq_puts(m, "Link is in standby\n");
-               break;
-       default:
-               seq_puts(m, "Unknown\n");
-               break;
-       }
-
-       seq_printf(m, "PSR Entry Count: %u\n",
-                  psrstat >> EDP_PSR_STATUS_COUNT_SHIFT &
-                  EDP_PSR_STATUS_COUNT_MASK);
-
-       seq_printf(m, "Max Sleep Timer Counter: %u\n",
-                  psrstat >> EDP_PSR_STATUS_MAX_SLEEP_TIMER_SHIFT &
-                  EDP_PSR_STATUS_MAX_SLEEP_TIMER_MASK);
-
-       seq_printf(m, "Had AUX error: %s\n",
-                  yesno(psrstat & EDP_PSR_STATUS_AUX_ERROR));
-
-       seq_printf(m, "Sending AUX: %s\n",
-                  yesno(psrstat & EDP_PSR_STATUS_AUX_SENDING));
-
-       seq_printf(m, "Sending Idle: %s\n",
-                  yesno(psrstat & EDP_PSR_STATUS_SENDING_IDLE));
-
-       seq_printf(m, "Sending TP2 TP3: %s\n",
-                  yesno(psrstat & EDP_PSR_STATUS_SENDING_TP2_TP3));
+       u32 psrperf = 0;
+       bool enabled = false;
 
-       seq_printf(m, "Sending TP1: %s\n",
-                  yesno(psrstat & EDP_PSR_STATUS_SENDING_TP1));
+       seq_printf(m, "Sink_Support: %s\n", yesno(dev_priv->psr.sink_support));
+       seq_printf(m, "Source_OK: %s\n", yesno(dev_priv->psr.source_ok));
 
-       seq_printf(m, "Idle Count: %u\n",
-                  psrstat & EDP_PSR_STATUS_IDLE_MASK);
+       enabled = HAS_PSR(dev) &&
+               I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
+       seq_printf(m, "Enabled: %s\n", yesno(enabled));
 
-       psrperf = (I915_READ(EDP_PSR_PERF_CNT)) & EDP_PSR_PERF_CNT_MASK;
-       seq_printf(m, "Performance Counter: %u\n", psrperf);
+       if (HAS_PSR(dev))
+               psrperf = I915_READ(EDP_PSR_PERF_CNT(dev)) &
+                       EDP_PSR_PERF_CNT_MASK;
+       seq_printf(m, "Performance_Counter: %u\n", psrperf);
 
        return 0;
 }
@@ -1825,152 +1845,965 @@ static int i915_pc8_status(struct seq_file *m, void *unused)
        return 0;
 }
 
-static int
-i915_wedged_get(void *data, u64 *val)
+struct pipe_crc_info {
+       const char *name;
+       struct drm_device *dev;
+       enum pipe pipe;
+};
+
+static int i915_pipe_crc_open(struct inode *inode, struct file *filep)
 {
-       struct drm_device *dev = data;
-       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct pipe_crc_info *info = inode->i_private;
+       struct drm_i915_private *dev_priv = info->dev->dev_private;
+       struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe];
 
-       *val = atomic_read(&dev_priv->gpu_error.reset_counter);
+       spin_lock_irq(&pipe_crc->lock);
+
+       if (pipe_crc->opened) {
+               spin_unlock_irq(&pipe_crc->lock);
+               return -EBUSY; /* already open */
+       }
+
+       pipe_crc->opened = true;
+       filep->private_data = inode->i_private;
+
+       spin_unlock_irq(&pipe_crc->lock);
 
        return 0;
 }
 
-static int
-i915_wedged_set(void *data, u64 val)
+static int i915_pipe_crc_release(struct inode *inode, struct file *filep)
 {
-       struct drm_device *dev = data;
+       struct pipe_crc_info *info = inode->i_private;
+       struct drm_i915_private *dev_priv = info->dev->dev_private;
+       struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe];
 
-       DRM_INFO("Manually setting wedged to %llu\n", val);
-       i915_handle_error(dev, val);
+       spin_lock_irq(&pipe_crc->lock);
+       pipe_crc->opened = false;
+       spin_unlock_irq(&pipe_crc->lock);
 
        return 0;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(i915_wedged_fops,
-                       i915_wedged_get, i915_wedged_set,
-                       "%llu\n");
+/* (6 fields, 8 chars each, space separated (5) + '\n') */
+#define PIPE_CRC_LINE_LEN      (6 * 8 + 5 + 1)
+/* account for \'0' */
+#define PIPE_CRC_BUFFER_LEN    (PIPE_CRC_LINE_LEN + 1)
 
-static int
-i915_ring_stop_get(void *data, u64 *val)
+static int pipe_crc_data_count(struct intel_pipe_crc *pipe_crc)
 {
-       struct drm_device *dev = data;
-       drm_i915_private_t *dev_priv = dev->dev_private;
-
-       *val = dev_priv->gpu_error.stop_rings;
-
-       return 0;
+       assert_spin_locked(&pipe_crc->lock);
+       return CIRC_CNT(pipe_crc->head, pipe_crc->tail,
+                       INTEL_PIPE_CRC_ENTRIES_NR);
 }
 
-static int
-i915_ring_stop_set(void *data, u64 val)
+static ssize_t
+i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count,
+                  loff_t *pos)
 {
-       struct drm_device *dev = data;
+       struct pipe_crc_info *info = filep->private_data;
+       struct drm_device *dev = info->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int ret;
+       struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe];
+       char buf[PIPE_CRC_BUFFER_LEN];
+       int head, tail, n_entries, n;
+       ssize_t bytes_read;
 
-       DRM_DEBUG_DRIVER("Stopping rings 0x%08llx\n", val);
+       /*
+        * Don't allow user space to provide buffers not big enough to hold
+        * a line of data.
+        */
+       if (count < PIPE_CRC_LINE_LEN)
+               return -EINVAL;
 
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
-       if (ret)
-               return ret;
+       if (pipe_crc->source == INTEL_PIPE_CRC_SOURCE_NONE)
+               return 0;
 
-       dev_priv->gpu_error.stop_rings = val;
-       mutex_unlock(&dev->struct_mutex);
+       /* nothing to read */
+       spin_lock_irq(&pipe_crc->lock);
+       while (pipe_crc_data_count(pipe_crc) == 0) {
+               int ret;
 
-       return 0;
-}
+               if (filep->f_flags & O_NONBLOCK) {
+                       spin_unlock_irq(&pipe_crc->lock);
+                       return -EAGAIN;
+               }
 
-DEFINE_SIMPLE_ATTRIBUTE(i915_ring_stop_fops,
-                       i915_ring_stop_get, i915_ring_stop_set,
-                       "0x%08llx\n");
+               ret = wait_event_interruptible_lock_irq(pipe_crc->wq,
+                               pipe_crc_data_count(pipe_crc), pipe_crc->lock);
+               if (ret) {
+                       spin_unlock_irq(&pipe_crc->lock);
+                       return ret;
+               }
+       }
 
-#define DROP_UNBOUND 0x1
-#define DROP_BOUND 0x2
-#define DROP_RETIRE 0x4
-#define DROP_ACTIVE 0x8
-#define DROP_ALL (DROP_UNBOUND | \
-                 DROP_BOUND | \
-                 DROP_RETIRE | \
-                 DROP_ACTIVE)
-static int
-i915_drop_caches_get(void *data, u64 *val)
-{
-       *val = DROP_ALL;
+       /* We now have one or more entries to read */
+       head = pipe_crc->head;
+       tail = pipe_crc->tail;
+       n_entries = min((size_t)CIRC_CNT(head, tail, INTEL_PIPE_CRC_ENTRIES_NR),
+                       count / PIPE_CRC_LINE_LEN);
+       spin_unlock_irq(&pipe_crc->lock);
 
-       return 0;
-}
+       bytes_read = 0;
+       n = 0;
+       do {
+               struct intel_pipe_crc_entry *entry = &pipe_crc->entries[tail];
+               int ret;
 
-static int
-i915_drop_caches_set(void *data, u64 val)
-{
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_i915_gem_object *obj, *next;
-       struct i915_address_space *vm;
-       struct i915_vma *vma, *x;
-       int ret;
+               bytes_read += snprintf(buf, PIPE_CRC_BUFFER_LEN,
+                                      "%8u %8x %8x %8x %8x %8x\n",
+                                      entry->frame, entry->crc[0],
+                                      entry->crc[1], entry->crc[2],
+                                      entry->crc[3], entry->crc[4]);
 
-       DRM_DEBUG_DRIVER("Dropping caches: 0x%08llx\n", val);
+               ret = copy_to_user(user_buf + n * PIPE_CRC_LINE_LEN,
+                                  buf, PIPE_CRC_LINE_LEN);
+               if (ret == PIPE_CRC_LINE_LEN)
+                       return -EFAULT;
 
-       /* No need to check and wait for gpu resets, only libdrm auto-restarts
-        * on ioctls on -EAGAIN. */
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
-       if (ret)
-               return ret;
+               BUILD_BUG_ON_NOT_POWER_OF_2(INTEL_PIPE_CRC_ENTRIES_NR);
+               tail = (tail + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1);
+               n++;
+       } while (--n_entries);
 
-       if (val & DROP_ACTIVE) {
-               ret = i915_gpu_idle(dev);
-               if (ret)
-                       goto unlock;
-       }
+       spin_lock_irq(&pipe_crc->lock);
+       pipe_crc->tail = tail;
+       spin_unlock_irq(&pipe_crc->lock);
 
-       if (val & (DROP_RETIRE | DROP_ACTIVE))
-               i915_gem_retire_requests(dev);
+       return bytes_read;
+}
 
-       if (val & DROP_BOUND) {
-               list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
-                       list_for_each_entry_safe(vma, x, &vm->inactive_list,
-                                                mm_list) {
-                               if (vma->obj->pin_count)
-                                       continue;
+static const struct file_operations i915_pipe_crc_fops = {
+       .owner = THIS_MODULE,
+       .open = i915_pipe_crc_open,
+       .read = i915_pipe_crc_read,
+       .release = i915_pipe_crc_release,
+};
 
-                               ret = i915_vma_unbind(vma);
-                               if (ret)
-                                       goto unlock;
-                       }
-               }
-       }
+static struct pipe_crc_info i915_pipe_crc_data[I915_MAX_PIPES] = {
+       {
+               .name = "i915_pipe_A_crc",
+               .pipe = PIPE_A,
+       },
+       {
+               .name = "i915_pipe_B_crc",
+               .pipe = PIPE_B,
+       },
+       {
+               .name = "i915_pipe_C_crc",
+               .pipe = PIPE_C,
+       },
+};
 
-       if (val & DROP_UNBOUND) {
-               list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list,
-                                        global_list)
-                       if (obj->pages_pin_count == 0) {
-                               ret = i915_gem_object_put_pages(obj);
-                               if (ret)
-                                       goto unlock;
-                       }
-       }
+static int i915_pipe_crc_create(struct dentry *root, struct drm_minor *minor,
+                               enum pipe pipe)
+{
+       struct drm_device *dev = minor->dev;
+       struct dentry *ent;
+       struct pipe_crc_info *info = &i915_pipe_crc_data[pipe];
 
-unlock:
-       mutex_unlock(&dev->struct_mutex);
+       info->dev = dev;
+       ent = debugfs_create_file(info->name, S_IRUGO, root, info,
+                                 &i915_pipe_crc_fops);
+       if (IS_ERR(ent))
+               return PTR_ERR(ent);
 
-       return ret;
+       return drm_add_fake_info_node(minor, ent, info);
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(i915_drop_caches_fops,
-                       i915_drop_caches_get, i915_drop_caches_set,
-                       "0x%08llx\n");
+static const char * const pipe_crc_sources[] = {
+       "none",
+       "plane1",
+       "plane2",
+       "pf",
+       "pipe",
+       "TV",
+       "DP-B",
+       "DP-C",
+       "DP-D",
+       "auto",
+};
 
-static int
-i915_max_freq_get(void *data, u64 *val)
+static const char *pipe_crc_source_name(enum intel_pipe_crc_source source)
 {
-       struct drm_device *dev = data;
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       int ret;
+       BUILD_BUG_ON(ARRAY_SIZE(pipe_crc_sources) != INTEL_PIPE_CRC_SOURCE_MAX);
+       return pipe_crc_sources[source];
+}
 
-       if (!(IS_GEN6(dev) || IS_GEN7(dev)))
-               return -ENODEV;
+static int display_crc_ctl_show(struct seq_file *m, void *data)
+{
+       struct drm_device *dev = m->private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int i;
+
+       for (i = 0; i < I915_MAX_PIPES; i++)
+               seq_printf(m, "%c %s\n", pipe_name(i),
+                          pipe_crc_source_name(dev_priv->pipe_crc[i].source));
+
+       return 0;
+}
+
+static int display_crc_ctl_open(struct inode *inode, struct file *file)
+{
+       struct drm_device *dev = inode->i_private;
+
+       return single_open(file, display_crc_ctl_show, dev);
+}
+
+static int i8xx_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
+                                uint32_t *val)
+{
+       if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
+               *source = INTEL_PIPE_CRC_SOURCE_PIPE;
+
+       switch (*source) {
+       case INTEL_PIPE_CRC_SOURCE_PIPE:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_INCLUDE_BORDER_I8XX;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_NONE:
+               *val = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
+                                    enum intel_pipe_crc_source *source)
+{
+       struct intel_encoder *encoder;
+       struct intel_crtc *crtc;
+       struct intel_digital_port *dig_port;
+       int ret = 0;
+
+       *source = INTEL_PIPE_CRC_SOURCE_PIPE;
+
+       mutex_lock(&dev->mode_config.mutex);
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               if (!encoder->base.crtc)
+                       continue;
+
+               crtc = to_intel_crtc(encoder->base.crtc);
+
+               if (crtc->pipe != pipe)
+                       continue;
+
+               switch (encoder->type) {
+               case INTEL_OUTPUT_TVOUT:
+                       *source = INTEL_PIPE_CRC_SOURCE_TV;
+                       break;
+               case INTEL_OUTPUT_DISPLAYPORT:
+               case INTEL_OUTPUT_EDP:
+                       dig_port = enc_to_dig_port(&encoder->base);
+                       switch (dig_port->port) {
+                       case PORT_B:
+                               *source = INTEL_PIPE_CRC_SOURCE_DP_B;
+                               break;
+                       case PORT_C:
+                               *source = INTEL_PIPE_CRC_SOURCE_DP_C;
+                               break;
+                       case PORT_D:
+                               *source = INTEL_PIPE_CRC_SOURCE_DP_D;
+                               break;
+                       default:
+                               WARN(1, "nonexisting DP port %c\n",
+                                    port_name(dig_port->port));
+                               break;
+                       }
+                       break;
+               }
+       }
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
+}
+
+static int vlv_pipe_crc_ctl_reg(struct drm_device *dev,
+                               enum pipe pipe,
+                               enum intel_pipe_crc_source *source,
+                               uint32_t *val)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       bool need_stable_symbols = false;
+
+       if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) {
+               int ret = i9xx_pipe_crc_auto_source(dev, pipe, source);
+               if (ret)
+                       return ret;
+       }
+
+       switch (*source) {
+       case INTEL_PIPE_CRC_SOURCE_PIPE:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_VLV;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_DP_B:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_VLV;
+               need_stable_symbols = true;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_DP_C:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_VLV;
+               need_stable_symbols = true;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_NONE:
+               *val = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /*
+        * When the pipe CRC tap point is after the transcoders we need
+        * to tweak symbol-level features to produce a deterministic series of
+        * symbols for a given frame. We need to reset those features only once
+        * a frame (instead of every nth symbol):
+        *   - DC-balance: used to ensure a better clock recovery from the data
+        *     link (SDVO)
+        *   - DisplayPort scrambling: used for EMI reduction
+        */
+       if (need_stable_symbols) {
+               uint32_t tmp = I915_READ(PORT_DFT2_G4X);
+
+               WARN_ON(!IS_G4X(dev));
+
+               tmp |= DC_BALANCE_RESET_VLV;
+               if (pipe == PIPE_A)
+                       tmp |= PIPE_A_SCRAMBLE_RESET;
+               else
+                       tmp |= PIPE_B_SCRAMBLE_RESET;
+
+               I915_WRITE(PORT_DFT2_G4X, tmp);
+       }
+
+       return 0;
+}
+
+static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev,
+                                enum pipe pipe,
+                                enum intel_pipe_crc_source *source,
+                                uint32_t *val)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       bool need_stable_symbols = false;
+
+       if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) {
+               int ret = i9xx_pipe_crc_auto_source(dev, pipe, source);
+               if (ret)
+                       return ret;
+       }
+
+       switch (*source) {
+       case INTEL_PIPE_CRC_SOURCE_PIPE:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_I9XX;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_TV:
+               if (!SUPPORTS_TV(dev))
+                       return -EINVAL;
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_TV_PRE;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_DP_B:
+               if (!IS_G4X(dev))
+                       return -EINVAL;
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_G4X;
+               need_stable_symbols = true;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_DP_C:
+               if (!IS_G4X(dev))
+                       return -EINVAL;
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_G4X;
+               need_stable_symbols = true;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_DP_D:
+               if (!IS_G4X(dev))
+                       return -EINVAL;
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_G4X;
+               need_stable_symbols = true;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_NONE:
+               *val = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /*
+        * When the pipe CRC tap point is after the transcoders we need
+        * to tweak symbol-level features to produce a deterministic series of
+        * symbols for a given frame. We need to reset those features only once
+        * a frame (instead of every nth symbol):
+        *   - DC-balance: used to ensure a better clock recovery from the data
+        *     link (SDVO)
+        *   - DisplayPort scrambling: used for EMI reduction
+        */
+       if (need_stable_symbols) {
+               uint32_t tmp = I915_READ(PORT_DFT2_G4X);
+
+               WARN_ON(!IS_G4X(dev));
+
+               I915_WRITE(PORT_DFT_I9XX,
+                          I915_READ(PORT_DFT_I9XX) | DC_BALANCE_RESET);
+
+               if (pipe == PIPE_A)
+                       tmp |= PIPE_A_SCRAMBLE_RESET;
+               else
+                       tmp |= PIPE_B_SCRAMBLE_RESET;
+
+               I915_WRITE(PORT_DFT2_G4X, tmp);
+       }
+
+       return 0;
+}
+
+static void vlv_undo_pipe_scramble_reset(struct drm_device *dev,
+                                        enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t tmp = I915_READ(PORT_DFT2_G4X);
+
+       if (pipe == PIPE_A)
+               tmp &= ~PIPE_A_SCRAMBLE_RESET;
+       else
+               tmp &= ~PIPE_B_SCRAMBLE_RESET;
+       if (!(tmp & PIPE_SCRAMBLE_RESET_MASK))
+               tmp &= ~DC_BALANCE_RESET_VLV;
+       I915_WRITE(PORT_DFT2_G4X, tmp);
+
+}
+
+static void g4x_undo_pipe_scramble_reset(struct drm_device *dev,
+                                        enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t tmp = I915_READ(PORT_DFT2_G4X);
+
+       if (pipe == PIPE_A)
+               tmp &= ~PIPE_A_SCRAMBLE_RESET;
+       else
+               tmp &= ~PIPE_B_SCRAMBLE_RESET;
+       I915_WRITE(PORT_DFT2_G4X, tmp);
+
+       if (!(tmp & PIPE_SCRAMBLE_RESET_MASK)) {
+               I915_WRITE(PORT_DFT_I9XX,
+                          I915_READ(PORT_DFT_I9XX) & ~DC_BALANCE_RESET);
+       }
+}
+
+static int ilk_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
+                               uint32_t *val)
+{
+       if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
+               *source = INTEL_PIPE_CRC_SOURCE_PIPE;
+
+       switch (*source) {
+       case INTEL_PIPE_CRC_SOURCE_PLANE1:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PRIMARY_ILK;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_PLANE2:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_ILK;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_PIPE:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_ILK;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_NONE:
+               *val = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ivb_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
+                               uint32_t *val)
+{
+       if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
+               *source = INTEL_PIPE_CRC_SOURCE_PF;
+
+       switch (*source) {
+       case INTEL_PIPE_CRC_SOURCE_PLANE1:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PRIMARY_IVB;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_PLANE2:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_IVB;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_PF:
+               *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PF_IVB;
+               break;
+       case INTEL_PIPE_CRC_SOURCE_NONE:
+               *val = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
+                              enum intel_pipe_crc_source source)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
+       u32 val;
+       int ret;
+
+       if (pipe_crc->source == source)
+               return 0;
+
+       /* forbid changing the source without going back to 'none' */
+       if (pipe_crc->source && source)
+               return -EINVAL;
+
+       if (IS_GEN2(dev))
+               ret = i8xx_pipe_crc_ctl_reg(&source, &val);
+       else if (INTEL_INFO(dev)->gen < 5)
+               ret = i9xx_pipe_crc_ctl_reg(dev, pipe, &source, &val);
+       else if (IS_VALLEYVIEW(dev))
+               ret = vlv_pipe_crc_ctl_reg(dev,pipe, &source, &val);
+       else if (IS_GEN5(dev) || IS_GEN6(dev))
+               ret = ilk_pipe_crc_ctl_reg(&source, &val);
+       else
+               ret = ivb_pipe_crc_ctl_reg(&source, &val);
+
+       if (ret != 0)
+               return ret;
+
+       /* none -> real source transition */
+       if (source) {
+               DRM_DEBUG_DRIVER("collecting CRCs for pipe %c, %s\n",
+                                pipe_name(pipe), pipe_crc_source_name(source));
+
+               pipe_crc->entries = kzalloc(sizeof(*pipe_crc->entries) *
+                                           INTEL_PIPE_CRC_ENTRIES_NR,
+                                           GFP_KERNEL);
+               if (!pipe_crc->entries)
+                       return -ENOMEM;
+
+               spin_lock_irq(&pipe_crc->lock);
+               pipe_crc->head = 0;
+               pipe_crc->tail = 0;
+               spin_unlock_irq(&pipe_crc->lock);
+       }
+
+       pipe_crc->source = source;
+
+       I915_WRITE(PIPE_CRC_CTL(pipe), val);
+       POSTING_READ(PIPE_CRC_CTL(pipe));
+
+       /* real source -> none transition */
+       if (source == INTEL_PIPE_CRC_SOURCE_NONE) {
+               struct intel_pipe_crc_entry *entries;
+
+               DRM_DEBUG_DRIVER("stopping CRCs for pipe %c\n",
+                                pipe_name(pipe));
+
+               intel_wait_for_vblank(dev, pipe);
+
+               spin_lock_irq(&pipe_crc->lock);
+               entries = pipe_crc->entries;
+               pipe_crc->entries = NULL;
+               spin_unlock_irq(&pipe_crc->lock);
+
+               kfree(entries);
+
+               if (IS_G4X(dev))
+                       g4x_undo_pipe_scramble_reset(dev, pipe);
+               else if (IS_VALLEYVIEW(dev))
+                       vlv_undo_pipe_scramble_reset(dev, pipe);
+       }
+
+       return 0;
+}
+
+/*
+ * Parse pipe CRC command strings:
+ *   command: wsp* object wsp+ name wsp+ source wsp*
+ *   object: 'pipe'
+ *   name: (A | B | C)
+ *   source: (none | plane1 | plane2 | pf)
+ *   wsp: (#0x20 | #0x9 | #0xA)+
+ *
+ * eg.:
+ *  "pipe A plane1"  ->  Start CRC computations on plane1 of pipe A
+ *  "pipe A none"    ->  Stop CRC
+ */
+static int display_crc_ctl_tokenize(char *buf, char *words[], int max_words)
+{
+       int n_words = 0;
+
+       while (*buf) {
+               char *end;
+
+               /* skip leading white space */
+               buf = skip_spaces(buf);
+               if (!*buf)
+                       break;  /* end of buffer */
+
+               /* find end of word */
+               for (end = buf; *end && !isspace(*end); end++)
+                       ;
+
+               if (n_words == max_words) {
+                       DRM_DEBUG_DRIVER("too many words, allowed <= %d\n",
+                                        max_words);
+                       return -EINVAL; /* ran out of words[] before bytes */
+               }
+
+               if (*end)
+                       *end++ = '\0';
+               words[n_words++] = buf;
+               buf = end;
+       }
+
+       return n_words;
+}
+
+enum intel_pipe_crc_object {
+       PIPE_CRC_OBJECT_PIPE,
+};
+
+static const char * const pipe_crc_objects[] = {
+       "pipe",
+};
+
+static int
+display_crc_ctl_parse_object(const char *buf, enum intel_pipe_crc_object *o)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pipe_crc_objects); i++)
+               if (!strcmp(buf, pipe_crc_objects[i])) {
+                       *o = i;
+                       return 0;
+                   }
+
+       return -EINVAL;
+}
+
+static int display_crc_ctl_parse_pipe(const char *buf, enum pipe *pipe)
+{
+       const char name = buf[0];
+
+       if (name < 'A' || name >= pipe_name(I915_MAX_PIPES))
+               return -EINVAL;
+
+       *pipe = name - 'A';
+
+       return 0;
+}
+
+static int
+display_crc_ctl_parse_source(const char *buf, enum intel_pipe_crc_source *s)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pipe_crc_sources); i++)
+               if (!strcmp(buf, pipe_crc_sources[i])) {
+                       *s = i;
+                       return 0;
+                   }
+
+       return -EINVAL;
+}
+
+static int display_crc_ctl_parse(struct drm_device *dev, char *buf, size_t len)
+{
+#define N_WORDS 3
+       int n_words;
+       char *words[N_WORDS];
+       enum pipe pipe;
+       enum intel_pipe_crc_object object;
+       enum intel_pipe_crc_source source;
+
+       n_words = display_crc_ctl_tokenize(buf, words, N_WORDS);
+       if (n_words != N_WORDS) {
+               DRM_DEBUG_DRIVER("tokenize failed, a command is %d words\n",
+                                N_WORDS);
+               return -EINVAL;
+       }
+
+       if (display_crc_ctl_parse_object(words[0], &object) < 0) {
+               DRM_DEBUG_DRIVER("unknown object %s\n", words[0]);
+               return -EINVAL;
+       }
+
+       if (display_crc_ctl_parse_pipe(words[1], &pipe) < 0) {
+               DRM_DEBUG_DRIVER("unknown pipe %s\n", words[1]);
+               return -EINVAL;
+       }
+
+       if (display_crc_ctl_parse_source(words[2], &source) < 0) {
+               DRM_DEBUG_DRIVER("unknown source %s\n", words[2]);
+               return -EINVAL;
+       }
+
+       return pipe_crc_set_source(dev, pipe, source);
+}
+
+static ssize_t display_crc_ctl_write(struct file *file, const char __user *ubuf,
+                                    size_t len, loff_t *offp)
+{
+       struct seq_file *m = file->private_data;
+       struct drm_device *dev = m->private;
+       char *tmpbuf;
+       int ret;
+
+       if (len == 0)
+               return 0;
+
+       if (len > PAGE_SIZE - 1) {
+               DRM_DEBUG_DRIVER("expected <%lu bytes into pipe crc control\n",
+                                PAGE_SIZE);
+               return -E2BIG;
+       }
+
+       tmpbuf = kmalloc(len + 1, GFP_KERNEL);
+       if (!tmpbuf)
+               return -ENOMEM;
+
+       if (copy_from_user(tmpbuf, ubuf, len)) {
+               ret = -EFAULT;
+               goto out;
+       }
+       tmpbuf[len] = '\0';
+
+       ret = display_crc_ctl_parse(dev, tmpbuf, len);
+
+out:
+       kfree(tmpbuf);
+       if (ret < 0)
+               return ret;
+
+       *offp += len;
+       return len;
+}
+
+static const struct file_operations i915_display_crc_ctl_fops = {
+       .owner = THIS_MODULE,
+       .open = display_crc_ctl_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .write = display_crc_ctl_write
+};
+
+static int
+i915_wedged_get(void *data, u64 *val)
+{
+       struct drm_device *dev = data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       *val = atomic_read(&dev_priv->gpu_error.reset_counter);
+
+       return 0;
+}
+
+static int
+i915_wedged_set(void *data, u64 val)
+{
+       struct drm_device *dev = data;
+
+       DRM_INFO("Manually setting wedged to %llu\n", val);
+       i915_handle_error(dev, val);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(i915_wedged_fops,
+                       i915_wedged_get, i915_wedged_set,
+                       "%llu\n");
+
+static int
+i915_ring_stop_get(void *data, u64 *val)
+{
+       struct drm_device *dev = data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       *val = dev_priv->gpu_error.stop_rings;
+
+       return 0;
+}
+
+static int
+i915_ring_stop_set(void *data, u64 val)
+{
+       struct drm_device *dev = data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       DRM_DEBUG_DRIVER("Stopping rings 0x%08llx\n", val);
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       dev_priv->gpu_error.stop_rings = val;
+       mutex_unlock(&dev->struct_mutex);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(i915_ring_stop_fops,
+                       i915_ring_stop_get, i915_ring_stop_set,
+                       "0x%08llx\n");
+
+static int
+i915_ring_missed_irq_get(void *data, u64 *val)
+{
+       struct drm_device *dev = data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       *val = dev_priv->gpu_error.missed_irq_rings;
+       return 0;
+}
+
+static int
+i915_ring_missed_irq_set(void *data, u64 val)
+{
+       struct drm_device *dev = data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       /* Lock against concurrent debugfs callers */
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+       dev_priv->gpu_error.missed_irq_rings = val;
+       mutex_unlock(&dev->struct_mutex);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(i915_ring_missed_irq_fops,
+                       i915_ring_missed_irq_get, i915_ring_missed_irq_set,
+                       "0x%08llx\n");
+
+static int
+i915_ring_test_irq_get(void *data, u64 *val)
+{
+       struct drm_device *dev = data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       *val = dev_priv->gpu_error.test_irq_rings;
+
+       return 0;
+}
+
+static int
+i915_ring_test_irq_set(void *data, u64 val)
+{
+       struct drm_device *dev = data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       DRM_DEBUG_DRIVER("Masking interrupts on rings 0x%08llx\n", val);
+
+       /* Lock against concurrent debugfs callers */
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       dev_priv->gpu_error.test_irq_rings = val;
+       mutex_unlock(&dev->struct_mutex);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(i915_ring_test_irq_fops,
+                       i915_ring_test_irq_get, i915_ring_test_irq_set,
+                       "0x%08llx\n");
+
+#define DROP_UNBOUND 0x1
+#define DROP_BOUND 0x2
+#define DROP_RETIRE 0x4
+#define DROP_ACTIVE 0x8
+#define DROP_ALL (DROP_UNBOUND | \
+                 DROP_BOUND | \
+                 DROP_RETIRE | \
+                 DROP_ACTIVE)
+static int
+i915_drop_caches_get(void *data, u64 *val)
+{
+       *val = DROP_ALL;
+
+       return 0;
+}
+
+static int
+i915_drop_caches_set(void *data, u64 val)
+{
+       struct drm_device *dev = data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj, *next;
+       struct i915_address_space *vm;
+       struct i915_vma *vma, *x;
+       int ret;
+
+       DRM_DEBUG_DRIVER("Dropping caches: 0x%08llx\n", val);
+
+       /* No need to check and wait for gpu resets, only libdrm auto-restarts
+        * on ioctls on -EAGAIN. */
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       if (val & DROP_ACTIVE) {
+               ret = i915_gpu_idle(dev);
+               if (ret)
+                       goto unlock;
+       }
+
+       if (val & (DROP_RETIRE | DROP_ACTIVE))
+               i915_gem_retire_requests(dev);
+
+       if (val & DROP_BOUND) {
+               list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
+                       list_for_each_entry_safe(vma, x, &vm->inactive_list,
+                                                mm_list) {
+                               if (vma->obj->pin_count)
+                                       continue;
+
+                               ret = i915_vma_unbind(vma);
+                               if (ret)
+                                       goto unlock;
+                       }
+               }
+       }
+
+       if (val & DROP_UNBOUND) {
+               list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list,
+                                        global_list)
+                       if (obj->pages_pin_count == 0) {
+                               ret = i915_gem_object_put_pages(obj);
+                               if (ret)
+                                       goto unlock;
+                       }
+       }
+
+unlock:
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(i915_drop_caches_fops,
+                       i915_drop_caches_get, i915_drop_caches_set,
+                       "0x%08llx\n");
+
+static int
+i915_max_freq_get(void *data, u64 *val)
+{
+       struct drm_device *dev = data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int ret;
+
+       if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+               return -ENODEV;
+
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
 
        ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
        if (ret)
@@ -1996,6 +2829,8 @@ i915_max_freq_set(void *data, u64 val)
        if (!(IS_GEN6(dev) || IS_GEN7(dev)))
                return -ENODEV;
 
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
        DRM_DEBUG_DRIVER("Manually setting max freq to %llu\n", val);
 
        ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
@@ -2034,6 +2869,8 @@ i915_min_freq_get(void *data, u64 *val)
        if (!(IS_GEN6(dev) || IS_GEN7(dev)))
                return -ENODEV;
 
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
        ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
        if (ret)
                return ret;
@@ -2058,6 +2895,8 @@ i915_min_freq_set(void *data, u64 val)
        if (!(IS_GEN6(dev) || IS_GEN7(dev)))
                return -ENODEV;
 
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
        DRM_DEBUG_DRIVER("Manually setting min freq to %llu\n", val);
 
        ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
@@ -2136,32 +2975,6 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_cache_sharing_fops,
                        i915_cache_sharing_get, i915_cache_sharing_set,
                        "%llu\n");
 
-/* As the drm_debugfs_init() routines are called before dev->dev_private is
- * allocated we need to hook into the minor for release. */
-static int
-drm_add_fake_info_node(struct drm_minor *minor,
-                      struct dentry *ent,
-                      const void *key)
-{
-       struct drm_info_node *node;
-
-       node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
-       if (node == NULL) {
-               debugfs_remove(ent);
-               return -ENOMEM;
-       }
-
-       node->minor = minor;
-       node->dent = ent;
-       node->info_ent = (void *) key;
-
-       mutex_lock(&minor->debugfs_lock);
-       list_add(&node->list, &minor->debugfs_list);
-       mutex_unlock(&minor->debugfs_lock);
-
-       return 0;
-}
-
 static int i915_forcewake_open(struct inode *inode, struct file *file)
 {
        struct drm_device *dev = inode->i_private;
@@ -2227,7 +3040,7 @@ static int i915_debugfs_create(struct dentry *root,
        return drm_add_fake_info_node(minor, ent, fops);
 }
 
-static struct drm_info_list i915_debugfs_list[] = {
+static const struct drm_info_list i915_debugfs_list[] = {
        {"i915_capabilities", i915_capabilities, 0},
        {"i915_gem_objects", i915_gem_object_info, 0},
        {"i915_gem_gtt", i915_gem_gtt_info, 0},
@@ -2269,7 +3082,7 @@ static struct drm_info_list i915_debugfs_list[] = {
 };
 #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
 
-static struct i915_debugfs_files {
+static const struct i915_debugfs_files {
        const char *name;
        const struct file_operations *fops;
 } i915_debugfs_files[] = {
@@ -2278,11 +3091,28 @@ static struct i915_debugfs_files {
        {"i915_min_freq", &i915_min_freq_fops},
        {"i915_cache_sharing", &i915_cache_sharing_fops},
        {"i915_ring_stop", &i915_ring_stop_fops},
+       {"i915_ring_missed_irq", &i915_ring_missed_irq_fops},
+       {"i915_ring_test_irq", &i915_ring_test_irq_fops},
        {"i915_gem_drop_caches", &i915_drop_caches_fops},
        {"i915_error_state", &i915_error_state_fops},
        {"i915_next_seqno", &i915_next_seqno_fops},
+       {"i915_display_crc_ctl", &i915_display_crc_ctl_fops},
 };
 
+void intel_display_crc_init(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int i;
+
+       for (i = 0; i < INTEL_INFO(dev)->num_pipes; i++) {
+               struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[i];
+
+               pipe_crc->opened = false;
+               spin_lock_init(&pipe_crc->lock);
+               init_waitqueue_head(&pipe_crc->wq);
+       }
+}
+
 int i915_debugfs_init(struct drm_minor *minor)
 {
        int ret, i;
@@ -2291,6 +3121,12 @@ int i915_debugfs_init(struct drm_minor *minor)
        if (ret)
                return ret;
 
+       for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) {
+               ret = i915_pipe_crc_create(minor->debugfs_root, minor, i);
+               if (ret)
+                       return ret;
+       }
+
        for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) {
                ret = i915_debugfs_create(minor->debugfs_root, minor,
                                          i915_debugfs_files[i].name,
@@ -2310,8 +3146,17 @@ void i915_debugfs_cleanup(struct drm_minor *minor)
 
        drm_debugfs_remove_files(i915_debugfs_list,
                                 I915_DEBUGFS_ENTRIES, minor);
+
        drm_debugfs_remove_files((struct drm_info_list *) &i915_forcewake_fops,
                                 1, minor);
+
+       for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) {
+               struct drm_info_list *info_list =
+                       (struct drm_info_list *)&i915_pipe_crc_data[i];
+
+               drm_debugfs_remove_files(info_list, 1, minor);
+       }
+
        for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) {
                struct drm_info_list *info_list =
                        (struct drm_info_list *) i915_debugfs_files[i].fops;
index d5c784d..0cab2d0 100644 (file)
@@ -52,7 +52,7 @@
        intel_ring_emit(LP_RING(dev_priv), x)
 
 #define ADVANCE_LP_RING() \
-       intel_ring_advance(LP_RING(dev_priv))
+       __intel_ring_advance(LP_RING(dev_priv))
 
 /**
  * Lock test for when it's just for synchronization of ring access.
@@ -641,7 +641,7 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
 
        if (batch->num_cliprects) {
                cliprects = kcalloc(batch->num_cliprects,
-                                   sizeof(struct drm_clip_rect),
+                                   sizeof(*cliprects),
                                    GFP_KERNEL);
                if (cliprects == NULL)
                        return -ENOMEM;
@@ -703,7 +703,7 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
 
        if (cmdbuf->num_cliprects) {
                cliprects = kcalloc(cmdbuf->num_cliprects,
-                                   sizeof(struct drm_clip_rect), GFP_KERNEL);
+                                   sizeof(*cliprects), GFP_KERNEL);
                if (cliprects == NULL) {
                        ret = -ENOMEM;
                        goto fail_batch_free;
@@ -931,7 +931,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
                value = READ_BREADCRUMB(dev_priv);
                break;
        case I915_PARAM_CHIPSET_ID:
-               value = dev->pci_device;
+               value = dev->pdev->device;
                break;
        case I915_PARAM_HAS_GEM:
                value = 1;
@@ -1311,13 +1311,15 @@ static int i915_load_modeset_init(struct drm_device *dev)
        if (ret)
                goto cleanup_gem_stolen;
 
+       intel_power_domains_init_hw(dev);
+
        /* Important: The output setup functions called by modeset_init need
         * working irqs for e.g. gmbus and dp aux transfers. */
        intel_modeset_init(dev);
 
        ret = i915_gem_init(dev);
        if (ret)
-               goto cleanup_irq;
+               goto cleanup_power;
 
        INIT_WORK(&dev_priv->console_resume_work, intel_console_resume);
 
@@ -1325,9 +1327,11 @@ static int i915_load_modeset_init(struct drm_device *dev)
 
        /* Always safe in the mode setting case. */
        /* FIXME: do pre/post-mode set stuff in core KMS code */
-       dev->vblank_disable_allowed = 1;
-       if (INTEL_INFO(dev)->num_pipes == 0)
+       dev->vblank_disable_allowed = true;
+       if (INTEL_INFO(dev)->num_pipes == 0) {
+               intel_display_power_put(dev, POWER_DOMAIN_VGA);
                return 0;
+       }
 
        ret = intel_fbdev_init(dev);
        if (ret)
@@ -1362,7 +1366,8 @@ cleanup_gem:
        mutex_unlock(&dev->struct_mutex);
        i915_gem_cleanup_aliasing_ppgtt(dev);
        drm_mm_takedown(&dev_priv->gtt.base.mm);
-cleanup_irq:
+cleanup_power:
+       intel_display_power_put(dev, POWER_DOMAIN_VGA);
        drm_irq_uninstall(dev);
 cleanup_gem_stolen:
        i915_gem_cleanup_stolen(dev);
@@ -1398,6 +1403,7 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master)
        master->driver_priv = NULL;
 }
 
+#ifdef CONFIG_DRM_I915_FBDEV
 static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
 {
        struct apertures_struct *ap;
@@ -1418,6 +1424,11 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
 
        kfree(ap);
 }
+#else
+static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
+{
+}
+#endif
 
 static void i915_dump_device_info(struct drm_i915_private *dev_priv)
 {
@@ -1459,17 +1470,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        info = (struct intel_device_info *) flags;
 
        /* Refuse to load on gen6+ without kms enabled. */
-       if (info->gen >= 6 && !drm_core_check_feature(dev, DRIVER_MODESET))
+       if (info->gen >= 6 && !drm_core_check_feature(dev, DRIVER_MODESET)) {
+               DRM_INFO("Your hardware requires kernel modesetting (KMS)\n");
+               DRM_INFO("See CONFIG_DRM_I915_KMS, nomodeset, and i915.modeset parameters\n");
                return -ENODEV;
+       }
 
-       /* i915 has 4 more counters */
-       dev->counters += 4;
-       dev->types[6] = _DRM_STAT_IRQ;
-       dev->types[7] = _DRM_STAT_PRIMARY;
-       dev->types[8] = _DRM_STAT_SECONDARY;
-       dev->types[9] = _DRM_STAT_DMA;
-
-       dev_priv = kzalloc(sizeof(drm_i915_private_t), GFP_KERNEL);
+       dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
        if (dev_priv == NULL)
                return -ENOMEM;
 
@@ -1494,6 +1501,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        dev_priv->pc8.disable_count = 2; /* requirements_met + gpu_idle */
        INIT_DELAYED_WORK(&dev_priv->pc8.enable_work, hsw_enable_pc8_work);
 
+       intel_display_crc_init(dev);
+
        i915_dump_device_info(dev_priv);
 
        /* Not all pre-production machines fall into this category, only the
@@ -1531,19 +1540,14 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 
        intel_uncore_early_sanitize(dev);
 
-       if (IS_HASWELL(dev) && (I915_READ(HSW_EDRAM_PRESENT) == 1)) {
-               /* The docs do not explain exactly how the calculation can be
-                * made. It is somewhat guessable, but for now, it's always
-                * 128MB.
-                * NB: We can't write IDICR yet because we do not have gt funcs
-                * set up */
-               dev_priv->ellc_size = 128;
-               DRM_INFO("Found %zuMB of eLLC\n", dev_priv->ellc_size);
-       }
+       /* This must be called before any calls to HAS_PCH_* */
+       intel_detect_pch(dev);
+
+       intel_uncore_init(dev);
 
        ret = i915_gem_gtt_init(dev);
        if (ret)
-               goto put_bridge;
+               goto out_regs;
 
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                i915_kick_out_firmware_fb(dev_priv);
@@ -1572,7 +1576,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                                     aperture_size);
        if (dev_priv->gtt.mappable == NULL) {
                ret = -EIO;
-               goto out_rmmap;
+               goto out_gtt;
        }
 
        dev_priv->gtt.mtrr = arch_phys_wc_add(dev_priv->gtt.mappable_base,
@@ -1598,13 +1602,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                goto out_mtrrfree;
        }
 
-       /* This must be called before any calls to HAS_PCH_* */
-       intel_detect_pch(dev);
-
        intel_irq_init(dev);
        intel_pm_init(dev);
        intel_uncore_sanitize(dev);
-       intel_uncore_init(dev);
 
        /* Try to make sure MCHBAR is enabled before poking at it */
        intel_setup_mchbar(dev);
@@ -1640,13 +1640,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        }
 
        if (HAS_POWER_WELL(dev))
-               i915_init_power_well(dev);
+               intel_power_domains_init(dev);
 
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
                ret = i915_load_modeset_init(dev);
                if (ret < 0) {
                        DRM_ERROR("failed to init modeset\n");
-                       goto out_gem_unload;
+                       goto out_power_well;
                }
        } else {
                /* Start out suspended in ums mode. */
@@ -1666,6 +1666,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 
        return 0;
 
+out_power_well:
+       if (HAS_POWER_WELL(dev))
+               intel_power_domains_remove(dev);
+       drm_vblank_cleanup(dev);
 out_gem_unload:
        if (dev_priv->mm.inactive_shrinker.scan_objects)
                unregister_shrinker(&dev_priv->mm.inactive_shrinker);
@@ -1679,12 +1683,18 @@ out_gem_unload:
 out_mtrrfree:
        arch_phys_wc_del(dev_priv->gtt.mtrr);
        io_mapping_free(dev_priv->gtt.mappable);
+out_gtt:
+       list_del(&dev_priv->gtt.base.global_link);
+       drm_mm_takedown(&dev_priv->gtt.base.mm);
        dev_priv->gtt.base.cleanup(&dev_priv->gtt.base);
-out_rmmap:
+out_regs:
+       intel_uncore_fini(dev);
        pci_iounmap(dev->pdev, dev_priv->regs);
 put_bridge:
        pci_dev_put(dev_priv->bridge_dev);
 free_priv:
+       if (dev_priv->slab)
+               kmem_cache_destroy(dev_priv->slab);
        kfree(dev_priv);
        return ret;
 }
@@ -1700,8 +1710,8 @@ int i915_driver_unload(struct drm_device *dev)
                /* The i915.ko module is still not prepared to be loaded when
                 * the power well is not enabled, so just enable it in case
                 * we're going to unload/reload. */
-               intel_set_power_well(dev, true);
-               i915_remove_power_well(dev);
+               intel_display_set_init_power(dev, true);
+               intel_power_domains_remove(dev);
        }
 
        i915_teardown_sysfs(dev);
@@ -1709,15 +1719,9 @@ int i915_driver_unload(struct drm_device *dev)
        if (dev_priv->mm.inactive_shrinker.scan_objects)
                unregister_shrinker(&dev_priv->mm.inactive_shrinker);
 
-       mutex_lock(&dev->struct_mutex);
-       ret = i915_gpu_idle(dev);
+       ret = i915_gem_suspend(dev);
        if (ret)
                DRM_ERROR("failed to idle hardware: %d\n", ret);
-       i915_gem_retire_requests(dev);
-       mutex_unlock(&dev->struct_mutex);
-
-       /* Cancel the retire work handler, which should be idle now. */
-       cancel_delayed_work_sync(&dev_priv->mm.retire_work);
 
        io_mapping_free(dev_priv->gtt.mappable);
        arch_phys_wc_del(dev_priv->gtt.mtrr);
@@ -1774,8 +1778,8 @@ int i915_driver_unload(struct drm_device *dev)
        list_del(&dev_priv->gtt.base.global_link);
        WARN_ON(!list_empty(&dev_priv->vm_list));
        drm_mm_takedown(&dev_priv->gtt.base.mm);
-       if (dev_priv->regs != NULL)
-               pci_iounmap(dev->pdev, dev_priv->regs);
+
+       drm_vblank_cleanup(dev);
 
        intel_teardown_gmbus(dev);
        intel_teardown_mchbar(dev);
@@ -1785,6 +1789,10 @@ int i915_driver_unload(struct drm_device *dev)
 
        dev_priv->gtt.base.cleanup(&dev_priv->gtt.base);
 
+       intel_uncore_fini(dev);
+       if (dev_priv->regs != NULL)
+               pci_iounmap(dev->pdev, dev_priv->regs);
+
        if (dev_priv->slab)
                kmem_cache_destroy(dev_priv->slab);
 
@@ -1796,19 +1804,11 @@ int i915_driver_unload(struct drm_device *dev)
 
 int i915_driver_open(struct drm_device *dev, struct drm_file *file)
 {
-       struct drm_i915_file_private *file_priv;
-
-       DRM_DEBUG_DRIVER("\n");
-       file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
-       if (!file_priv)
-               return -ENOMEM;
-
-       file->driver_priv = file_priv;
-
-       spin_lock_init(&file_priv->mm.lock);
-       INIT_LIST_HEAD(&file_priv->mm.request_list);
+       int ret;
 
-       idr_init(&file_priv->context_idr);
+       ret = i915_gem_open(dev, file);
+       if (ret)
+               return ret;
 
        return 0;
 }
@@ -1836,7 +1836,7 @@ void i915_driver_lastclose(struct drm_device * dev)
                return;
 
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-               intel_fb_restore_mode(dev);
+               intel_fbdev_restore_mode(dev);
                vga_switcheroo_process_delayed_switch();
                return;
        }
index 2ad2788..989be12 100644 (file)
@@ -160,49 +160,58 @@ extern int intel_agp_enabled;
 static const struct intel_device_info intel_i830_info = {
        .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2,
        .has_overlay = 1, .overlay_needs_physical = 1,
+       .ring_mask = RENDER_RING,
 };
 
 static const struct intel_device_info intel_845g_info = {
        .gen = 2, .num_pipes = 1,
        .has_overlay = 1, .overlay_needs_physical = 1,
+       .ring_mask = RENDER_RING,
 };
 
 static const struct intel_device_info intel_i85x_info = {
        .gen = 2, .is_i85x = 1, .is_mobile = 1, .num_pipes = 2,
        .cursor_needs_physical = 1,
        .has_overlay = 1, .overlay_needs_physical = 1,
+       .ring_mask = RENDER_RING,
 };
 
 static const struct intel_device_info intel_i865g_info = {
        .gen = 2, .num_pipes = 1,
        .has_overlay = 1, .overlay_needs_physical = 1,
+       .ring_mask = RENDER_RING,
 };
 
 static const struct intel_device_info intel_i915g_info = {
        .gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, .num_pipes = 2,
        .has_overlay = 1, .overlay_needs_physical = 1,
+       .ring_mask = RENDER_RING,
 };
 static const struct intel_device_info intel_i915gm_info = {
        .gen = 3, .is_mobile = 1, .num_pipes = 2,
        .cursor_needs_physical = 1,
        .has_overlay = 1, .overlay_needs_physical = 1,
        .supports_tv = 1,
+       .ring_mask = RENDER_RING,
 };
 static const struct intel_device_info intel_i945g_info = {
        .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .num_pipes = 2,
        .has_overlay = 1, .overlay_needs_physical = 1,
+       .ring_mask = RENDER_RING,
 };
 static const struct intel_device_info intel_i945gm_info = {
        .gen = 3, .is_i945gm = 1, .is_mobile = 1, .num_pipes = 2,
        .has_hotplug = 1, .cursor_needs_physical = 1,
        .has_overlay = 1, .overlay_needs_physical = 1,
        .supports_tv = 1,
+       .ring_mask = RENDER_RING,
 };
 
 static const struct intel_device_info intel_i965g_info = {
        .gen = 4, .is_broadwater = 1, .num_pipes = 2,
        .has_hotplug = 1,
        .has_overlay = 1,
+       .ring_mask = RENDER_RING,
 };
 
 static const struct intel_device_info intel_i965gm_info = {
@@ -210,18 +219,20 @@ static const struct intel_device_info intel_i965gm_info = {
        .is_mobile = 1, .has_fbc = 1, .has_hotplug = 1,
        .has_overlay = 1,
        .supports_tv = 1,
+       .ring_mask = RENDER_RING,
 };
 
 static const struct intel_device_info intel_g33_info = {
        .gen = 3, .is_g33 = 1, .num_pipes = 2,
        .need_gfx_hws = 1, .has_hotplug = 1,
        .has_overlay = 1,
+       .ring_mask = RENDER_RING,
 };
 
 static const struct intel_device_info intel_g45_info = {
        .gen = 4, .is_g4x = 1, .need_gfx_hws = 1, .num_pipes = 2,
        .has_pipe_cxsr = 1, .has_hotplug = 1,
-       .has_bsd_ring = 1,
+       .ring_mask = RENDER_RING | BSD_RING,
 };
 
 static const struct intel_device_info intel_gm45_info = {
@@ -229,7 +240,7 @@ static const struct intel_device_info intel_gm45_info = {
        .is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1,
        .has_pipe_cxsr = 1, .has_hotplug = 1,
        .supports_tv = 1,
-       .has_bsd_ring = 1,
+       .ring_mask = RENDER_RING | BSD_RING,
 };
 
 static const struct intel_device_info intel_pineview_info = {
@@ -241,42 +252,36 @@ static const struct intel_device_info intel_pineview_info = {
 static const struct intel_device_info intel_ironlake_d_info = {
        .gen = 5, .num_pipes = 2,
        .need_gfx_hws = 1, .has_hotplug = 1,
-       .has_bsd_ring = 1,
+       .ring_mask = RENDER_RING | BSD_RING,
 };
 
 static const struct intel_device_info intel_ironlake_m_info = {
        .gen = 5, .is_mobile = 1, .num_pipes = 2,
        .need_gfx_hws = 1, .has_hotplug = 1,
        .has_fbc = 1,
-       .has_bsd_ring = 1,
+       .ring_mask = RENDER_RING | BSD_RING,
 };
 
 static const struct intel_device_info intel_sandybridge_d_info = {
        .gen = 6, .num_pipes = 2,
        .need_gfx_hws = 1, .has_hotplug = 1,
-       .has_bsd_ring = 1,
-       .has_blt_ring = 1,
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING,
        .has_llc = 1,
-       .has_force_wake = 1,
 };
 
 static const struct intel_device_info intel_sandybridge_m_info = {
        .gen = 6, .is_mobile = 1, .num_pipes = 2,
        .need_gfx_hws = 1, .has_hotplug = 1,
        .has_fbc = 1,
-       .has_bsd_ring = 1,
-       .has_blt_ring = 1,
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING,
        .has_llc = 1,
-       .has_force_wake = 1,
 };
 
 #define GEN7_FEATURES  \
        .gen = 7, .num_pipes = 3, \
        .need_gfx_hws = 1, .has_hotplug = 1, \
-       .has_bsd_ring = 1, \
-       .has_blt_ring = 1, \
-       .has_llc = 1, \
-       .has_force_wake = 1
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING, \
+       .has_llc = 1
 
 static const struct intel_device_info intel_ivybridge_d_info = {
        GEN7_FEATURES,
@@ -318,7 +323,7 @@ static const struct intel_device_info intel_haswell_d_info = {
        .is_haswell = 1,
        .has_ddi = 1,
        .has_fpga_dbg = 1,
-       .has_vebox_ring = 1,
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
 };
 
 static const struct intel_device_info intel_haswell_m_info = {
@@ -328,7 +333,25 @@ static const struct intel_device_info intel_haswell_m_info = {
        .has_ddi = 1,
        .has_fpga_dbg = 1,
        .has_fbc = 1,
-       .has_vebox_ring = 1,
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+};
+
+static const struct intel_device_info intel_broadwell_d_info = {
+       .is_preliminary = 1,
+       .gen = 8, .num_pipes = 3,
+       .need_gfx_hws = 1, .has_hotplug = 1,
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+       .has_llc = 1,
+       .has_ddi = 1,
+};
+
+static const struct intel_device_info intel_broadwell_m_info = {
+       .is_preliminary = 1,
+       .gen = 8, .is_mobile = 1, .num_pipes = 3,
+       .need_gfx_hws = 1, .has_hotplug = 1,
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+       .has_llc = 1,
+       .has_ddi = 1,
 };
 
 /*
@@ -362,7 +385,9 @@ static const struct intel_device_info intel_haswell_m_info = {
        INTEL_HSW_D_IDS(&intel_haswell_d_info), \
        INTEL_HSW_M_IDS(&intel_haswell_m_info), \
        INTEL_VLV_M_IDS(&intel_valleyview_m_info),      \
-       INTEL_VLV_D_IDS(&intel_valleyview_d_info)
+       INTEL_VLV_D_IDS(&intel_valleyview_d_info),      \
+       INTEL_BDW_M_IDS(&intel_broadwell_m_info),       \
+       INTEL_BDW_D_IDS(&intel_broadwell_d_info)
 
 static const struct pci_device_id pciidlist[] = {              /* aka */
        INTEL_PCI_IDS,
@@ -416,13 +441,19 @@ void intel_detect_pch(struct drm_device *dev)
                        } else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) {
                                /* PantherPoint is CPT compatible */
                                dev_priv->pch_type = PCH_CPT;
-                               DRM_DEBUG_KMS("Found PatherPoint PCH\n");
+                               DRM_DEBUG_KMS("Found PantherPoint PCH\n");
                                WARN_ON(!(IS_GEN6(dev) || IS_IVYBRIDGE(dev)));
                        } else if (id == INTEL_PCH_LPT_DEVICE_ID_TYPE) {
                                dev_priv->pch_type = PCH_LPT;
                                DRM_DEBUG_KMS("Found LynxPoint PCH\n");
                                WARN_ON(!IS_HASWELL(dev));
                                WARN_ON(IS_ULT(dev));
+                       } else if (IS_BROADWELL(dev)) {
+                               dev_priv->pch_type = PCH_LPT;
+                               dev_priv->pch_id =
+                                       INTEL_PCH_LPT_LP_DEVICE_ID_TYPE;
+                               DRM_DEBUG_KMS("This is Broadwell, assuming "
+                                             "LynxPoint LP PCH\n");
                        } else if (id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) {
                                dev_priv->pch_type = PCH_LPT;
                                DRM_DEBUG_KMS("Found LynxPoint LP PCH\n");
@@ -447,6 +478,12 @@ bool i915_semaphore_is_enabled(struct drm_device *dev)
        if (INTEL_INFO(dev)->gen < 6)
                return 0;
 
+       /* Until we get further testing... */
+       if (IS_GEN8(dev)) {
+               WARN_ON(!i915_preliminary_hw_support);
+               return 0;
+       }
+
        if (i915_semaphores >= 0)
                return i915_semaphores;
 
@@ -472,7 +509,7 @@ static int i915_drm_freeze(struct drm_device *dev)
        /* We do a lot of poking in a lot of registers, make sure they work
         * properly. */
        hsw_disable_package_c8(dev_priv);
-       intel_set_power_well(dev, true);
+       intel_display_set_init_power(dev, true);
 
        drm_kms_helper_poll_disable(dev);
 
@@ -482,9 +519,7 @@ static int i915_drm_freeze(struct drm_device *dev)
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
                int error;
 
-               mutex_lock(&dev->struct_mutex);
-               error = i915_gem_idle(dev);
-               mutex_unlock(&dev->struct_mutex);
+               error = i915_gem_suspend(dev);
                if (error) {
                        dev_err(&dev->pdev->dev,
                                "GEM idle failed, resume might fail\n");
@@ -578,11 +613,24 @@ static void intel_resume_hotplug(struct drm_device *dev)
        drm_helper_hpd_irq_event(dev);
 }
 
-static int __i915_drm_thaw(struct drm_device *dev)
+static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int error = 0;
 
+       intel_uncore_early_sanitize(dev);
+
+       intel_uncore_sanitize(dev);
+
+       if (drm_core_check_feature(dev, DRIVER_MODESET) &&
+           restore_gtt_mappings) {
+               mutex_lock(&dev->struct_mutex);
+               i915_gem_restore_gtt_mappings(dev);
+               mutex_unlock(&dev->struct_mutex);
+       }
+
+       intel_power_domains_init_hw(dev);
+
        i915_restore_state(dev);
        intel_opregion_setup(dev);
 
@@ -642,20 +690,10 @@ static int __i915_drm_thaw(struct drm_device *dev)
 
 static int i915_drm_thaw(struct drm_device *dev)
 {
-       int error = 0;
-
-       intel_uncore_sanitize(dev);
-
-       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-               mutex_lock(&dev->struct_mutex);
-               i915_gem_restore_gtt_mappings(dev);
-               mutex_unlock(&dev->struct_mutex);
-       } else if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
                i915_check_and_clear_faults(dev);
 
-       __i915_drm_thaw(dev);
-
-       return error;
+       return __i915_drm_thaw(dev, true);
 }
 
 int i915_resume(struct drm_device *dev)
@@ -671,20 +709,12 @@ int i915_resume(struct drm_device *dev)
 
        pci_set_master(dev->pdev);
 
-       intel_uncore_sanitize(dev);
-
        /*
         * Platforms with opregion should have sane BIOS, older ones (gen3 and
-        * earlier) need this since the BIOS might clear all our scratch PTEs.
+        * earlier) need to restore the GTT mappings since the BIOS might clear
+        * all our scratch PTEs.
         */
-       if (drm_core_check_feature(dev, DRIVER_MODESET) &&
-           !dev_priv->opregion.header) {
-               mutex_lock(&dev->struct_mutex);
-               i915_gem_restore_gtt_mappings(dev);
-               mutex_unlock(&dev->struct_mutex);
-       }
-
-       ret = __i915_drm_thaw(dev);
+       ret = __i915_drm_thaw(dev, !dev_priv->opregion.header);
        if (ret)
                return ret;
 
@@ -722,24 +752,19 @@ int i915_reset(struct drm_device *dev)
 
        simulated = dev_priv->gpu_error.stop_rings != 0;
 
-       if (!simulated && get_seconds() - dev_priv->gpu_error.last_reset < 5) {
-               DRM_ERROR("GPU hanging too fast, declaring wedged!\n");
-               ret = -ENODEV;
-       } else {
-               ret = intel_gpu_reset(dev);
-
-               /* Also reset the gpu hangman. */
-               if (simulated) {
-                       DRM_INFO("Simulated gpu hang, resetting stop_rings\n");
-                       dev_priv->gpu_error.stop_rings = 0;
-                       if (ret == -ENODEV) {
-                               DRM_ERROR("Reset not implemented, but ignoring "
-                                         "error for simulated gpu hangs\n");
-                               ret = 0;
-                       }
-               } else
-                       dev_priv->gpu_error.last_reset = get_seconds();
+       ret = intel_gpu_reset(dev);
+
+       /* Also reset the gpu hangman. */
+       if (simulated) {
+               DRM_INFO("Simulated gpu hang, resetting stop_rings\n");
+               dev_priv->gpu_error.stop_rings = 0;
+               if (ret == -ENODEV) {
+                       DRM_ERROR("Reset not implemented, but ignoring "
+                                 "error for simulated gpu hangs\n");
+                       ret = 0;
+               }
        }
+
        if (ret) {
                DRM_ERROR("Failed to reset chip.\n");
                mutex_unlock(&dev->struct_mutex);
@@ -762,30 +787,17 @@ int i915_reset(struct drm_device *dev)
         */
        if (drm_core_check_feature(dev, DRIVER_MODESET) ||
                        !dev_priv->ums.mm_suspended) {
-               struct intel_ring_buffer *ring;
-               int i;
-
+               bool hw_contexts_disabled = dev_priv->hw_contexts_disabled;
                dev_priv->ums.mm_suspended = 0;
 
-               i915_gem_init_swizzling(dev);
-
-               for_each_ring(ring, dev_priv, i)
-                       ring->init(ring);
-
-               i915_gem_context_init(dev);
-               if (dev_priv->mm.aliasing_ppgtt) {
-                       ret = dev_priv->mm.aliasing_ppgtt->enable(dev);
-                       if (ret)
-                               i915_gem_cleanup_aliasing_ppgtt(dev);
-               }
-
-               /*
-                * It would make sense to re-init all the other hw state, at
-                * least the rps/rc6/emon init done within modeset_init_hw. For
-                * some unknown reason, this blows up my ilk, so don't.
-                */
-
+               ret = i915_gem_init_hw(dev);
+               if (!hw_contexts_disabled && dev_priv->hw_contexts_disabled)
+                       DRM_ERROR("HW contexts didn't survive reset\n");
                mutex_unlock(&dev->struct_mutex);
+               if (ret) {
+                       DRM_ERROR("Failed hw init on reset %d\n", ret);
+                       return ret;
+               }
 
                drm_irq_uninstall(dev);
                drm_irq_install(dev);
@@ -802,6 +814,12 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        struct intel_device_info *intel_info =
                (struct intel_device_info *) ent->driver_data;
 
+       if (IS_PRELIMINARY_HW(intel_info) && !i915_preliminary_hw_support) {
+               DRM_INFO("This hardware requires preliminary hardware support.\n"
+                        "See CONFIG_DRM_I915_PRELIMINARY_HW_SUPPORT, and/or modparam preliminary_hw_support\n");
+               return -ENODEV;
+       }
+
        /* Only bind to function 0 of the device. Early generations
         * used function 1 as a placeholder for multi-head. This causes
         * us confusion instead, especially on the systems where both
@@ -949,7 +967,6 @@ static struct drm_driver driver = {
        .debugfs_init = i915_debugfs_init,
        .debugfs_cleanup = i915_debugfs_cleanup,
 #endif
-       .gem_init_object = i915_gem_init_object,
        .gem_free_object = i915_gem_free_object,
        .gem_vm_ops = &i915_gem_vm_ops,
 
index ab0f2c0..8600c31 100644 (file)
@@ -54,6 +54,7 @@
 #define DRIVER_DATE            "20080730"
 
 enum pipe {
+       INVALID_PIPE = -1,
        PIPE_A = 0,
        PIPE_B,
        PIPE_C,
@@ -98,13 +99,29 @@ enum intel_display_power_domain {
        POWER_DOMAIN_TRANSCODER_A,
        POWER_DOMAIN_TRANSCODER_B,
        POWER_DOMAIN_TRANSCODER_C,
-       POWER_DOMAIN_TRANSCODER_EDP = POWER_DOMAIN_TRANSCODER_A + 0xF,
+       POWER_DOMAIN_TRANSCODER_EDP,
+       POWER_DOMAIN_VGA,
+       POWER_DOMAIN_INIT,
+
+       POWER_DOMAIN_NUM,
 };
 
+#define POWER_DOMAIN_MASK (BIT(POWER_DOMAIN_NUM) - 1)
+
 #define POWER_DOMAIN_PIPE(pipe) ((pipe) + POWER_DOMAIN_PIPE_A)
 #define POWER_DOMAIN_PIPE_PANEL_FITTER(pipe) \
                ((pipe) + POWER_DOMAIN_PIPE_A_PANEL_FITTER)
-#define POWER_DOMAIN_TRANSCODER(tran) ((tran) + POWER_DOMAIN_TRANSCODER_A)
+#define POWER_DOMAIN_TRANSCODER(tran) \
+       ((tran) == TRANSCODER_EDP ? POWER_DOMAIN_TRANSCODER_EDP : \
+        (tran) + POWER_DOMAIN_TRANSCODER_A)
+
+#define HSW_ALWAYS_ON_POWER_DOMAINS (          \
+       BIT(POWER_DOMAIN_PIPE_A) |              \
+       BIT(POWER_DOMAIN_TRANSCODER_EDP))
+#define BDW_ALWAYS_ON_POWER_DOMAINS (          \
+       BIT(POWER_DOMAIN_PIPE_A) |              \
+       BIT(POWER_DOMAIN_TRANSCODER_EDP) |      \
+       BIT(POWER_DOMAIN_PIPE_A_PANEL_FITTER))
 
 enum hpd_pin {
        HPD_NONE = 0,
@@ -225,9 +242,12 @@ struct intel_opregion {
        struct opregion_header __iomem *header;
        struct opregion_acpi __iomem *acpi;
        struct opregion_swsci __iomem *swsci;
+       u32 swsci_gbda_sub_functions;
+       u32 swsci_sbcb_sub_functions;
        struct opregion_asle __iomem *asle;
        void __iomem *vbt;
        u32 __iomem *lid_state;
+       struct work_struct asle_work;
 };
 #define OPREGION_SIZE            (8*1024)
 
@@ -285,6 +305,7 @@ struct drm_i915_error_state {
        u32 cpu_ring_tail[I915_NUM_RINGS];
        u32 error; /* gen6+ */
        u32 err_int; /* gen7 */
+       u32 bbstate[I915_NUM_RINGS];
        u32 instpm[I915_NUM_RINGS];
        u32 instps[I915_NUM_RINGS];
        u32 extra_instdone[I915_NUM_INSTDONE_REG];
@@ -321,11 +342,13 @@ struct drm_i915_error_state {
                u32 dirty:1;
                u32 purgeable:1;
                s32 ring:4;
-               u32 cache_level:2;
+               u32 cache_level:3;
        } **active_bo, **pinned_bo;
        u32 *active_bo_count, *pinned_bo_count;
        struct intel_overlay_error_state *overlay;
        struct intel_display_error_state *display;
+       int hangcheck_score[I915_NUM_RINGS];
+       enum intel_ring_hangcheck_action hangcheck_action[I915_NUM_RINGS];
 };
 
 struct intel_crtc_config;
@@ -357,7 +380,7 @@ struct drm_i915_display_funcs {
                          int target, int refclk,
                          struct dpll *match_clock,
                          struct dpll *best_clock);
-       void (*update_wm)(struct drm_device *dev);
+       void (*update_wm)(struct drm_crtc *crtc);
        void (*update_sprite_wm)(struct drm_plane *plane,
                                 struct drm_crtc *crtc,
                                 uint32_t sprite_width, int pixel_size,
@@ -367,7 +390,6 @@ struct drm_i915_display_funcs {
         * fills out the pipe-config with the hw state. */
        bool (*get_pipe_config)(struct intel_crtc *,
                                struct intel_crtc_config *);
-       void (*get_clock)(struct intel_crtc *, struct intel_crtc_config *);
        int (*crtc_mode_set)(struct drm_crtc *crtc,
                             int x, int y,
                             struct drm_framebuffer *old_fb);
@@ -375,7 +397,8 @@ struct drm_i915_display_funcs {
        void (*crtc_disable)(struct drm_crtc *crtc);
        void (*off)(struct drm_crtc *crtc);
        void (*write_eld)(struct drm_connector *connector,
-                         struct drm_crtc *crtc);
+                         struct drm_crtc *crtc,
+                         struct drm_display_mode *mode);
        void (*fdi_link_train)(struct drm_crtc *crtc);
        void (*init_clock_gating)(struct drm_device *dev);
        int (*queue_flip)(struct drm_device *dev, struct drm_crtc *crtc,
@@ -395,6 +418,20 @@ struct drm_i915_display_funcs {
 struct intel_uncore_funcs {
        void (*force_wake_get)(struct drm_i915_private *dev_priv);
        void (*force_wake_put)(struct drm_i915_private *dev_priv);
+
+       uint8_t  (*mmio_readb)(struct drm_i915_private *dev_priv, off_t offset, bool trace);
+       uint16_t (*mmio_readw)(struct drm_i915_private *dev_priv, off_t offset, bool trace);
+       uint32_t (*mmio_readl)(struct drm_i915_private *dev_priv, off_t offset, bool trace);
+       uint64_t (*mmio_readq)(struct drm_i915_private *dev_priv, off_t offset, bool trace);
+
+       void (*mmio_writeb)(struct drm_i915_private *dev_priv, off_t offset,
+                               uint8_t val, bool trace);
+       void (*mmio_writew)(struct drm_i915_private *dev_priv, off_t offset,
+                               uint16_t val, bool trace);
+       void (*mmio_writel)(struct drm_i915_private *dev_priv, off_t offset,
+                               uint32_t val, bool trace);
+       void (*mmio_writeq)(struct drm_i915_private *dev_priv, off_t offset,
+                               uint64_t val, bool trace);
 };
 
 struct intel_uncore {
@@ -404,6 +441,8 @@ struct intel_uncore {
 
        unsigned fifo_count;
        unsigned forcewake_count;
+
+       struct delayed_work force_wake_work;
 };
 
 #define DEV_INFO_FOR_EACH_FLAG(func, sep) \
@@ -420,7 +459,7 @@ struct intel_uncore {
        func(is_ivybridge) sep \
        func(is_valleyview) sep \
        func(is_haswell) sep \
-       func(has_force_wake) sep \
+       func(is_preliminary) sep \
        func(has_fbc) sep \
        func(has_pipe_cxsr) sep \
        func(has_hotplug) sep \
@@ -428,9 +467,6 @@ struct intel_uncore {
        func(has_overlay) sep \
        func(overlay_needs_physical) sep \
        func(supports_tv) sep \
-       func(has_bsd_ring) sep \
-       func(has_blt_ring) sep \
-       func(has_vebox_ring) sep \
        func(has_llc) sep \
        func(has_ddi) sep \
        func(has_fpga_dbg)
@@ -442,6 +478,7 @@ struct intel_device_info {
        u32 display_mmio_offset;
        u8 num_pipes:3;
        u8 gen;
+       u8 ring_mask; /* Rings supported by the HW */
        DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG, SEP_SEMICOLON);
 };
 
@@ -542,10 +579,21 @@ struct i915_gtt {
 struct i915_hw_ppgtt {
        struct i915_address_space base;
        unsigned num_pd_entries;
-       struct page **pt_pages;
-       uint32_t pd_offset;
-       dma_addr_t *pt_dma_addr;
-
+       union {
+               struct page **pt_pages;
+               struct page *gen8_pt_pages;
+       };
+       struct page *pd_pages;
+       int num_pd_pages;
+       int num_pt_pages;
+       union {
+               uint32_t pd_offset;
+               dma_addr_t pd_dma_addr[4];
+       };
+       union {
+               dma_addr_t *pt_dma_addr;
+               dma_addr_t *gen8_pt_dma_addr[4];
+       };
        int (*enable)(struct drm_device *dev);
 };
 
@@ -570,6 +618,13 @@ struct i915_vma {
        /** This vma's place in the batchbuffer or on the eviction list */
        struct list_head exec_list;
 
+       /**
+        * Used for performing relocations during execbuffer insertion.
+        */
+       struct hlist_node exec_node;
+       unsigned long exec_handle;
+       struct drm_i915_gem_exec_object2 *exec_entry;
+
 };
 
 struct i915_ctx_hang_stats {
@@ -578,6 +633,12 @@ struct i915_ctx_hang_stats {
 
        /* This context had batch active when hang was declared */
        unsigned batch_active;
+
+       /* Time when this context was last blamed for a GPU reset */
+       unsigned long guilty_ts;
+
+       /* This context is banned to submit more work */
+       bool banned;
 };
 
 /* This must match up with the value previously used for execbuf2.rsvd1. */
@@ -586,10 +647,13 @@ struct i915_hw_context {
        struct kref ref;
        int id;
        bool is_initialized;
+       uint8_t remap_slice;
        struct drm_i915_file_private *file_priv;
        struct intel_ring_buffer *ring;
        struct drm_i915_gem_object *obj;
        struct i915_ctx_hang_stats hang_stats;
+
+       struct list_head link;
 };
 
 struct i915_fbc {
@@ -623,17 +687,9 @@ struct i915_fbc {
        } no_fbc_reason;
 };
 
-enum no_psr_reason {
-       PSR_NO_SOURCE, /* Not supported on platform */
-       PSR_NO_SINK, /* Not supported by panel */
-       PSR_MODULE_PARAM,
-       PSR_CRTC_NOT_ACTIVE,
-       PSR_PWR_WELL_ENABLED,
-       PSR_NOT_TILED,
-       PSR_SPRITE_ENABLED,
-       PSR_S3D_ENABLED,
-       PSR_INTERLACED_ENABLED,
-       PSR_HSW_NOT_DDIA,
+struct i915_psr {
+       bool sink_support;
+       bool source_ok;
 };
 
 enum intel_pch {
@@ -704,6 +760,9 @@ struct i915_suspend_saved_registers {
        u32 saveBLC_HIST_CTL;
        u32 saveBLC_PWM_CTL;
        u32 saveBLC_PWM_CTL2;
+       u32 saveBLC_HIST_CTL_B;
+       u32 saveBLC_PWM_CTL_B;
+       u32 saveBLC_PWM_CTL2_B;
        u32 saveBLC_CPU_PWM_CTL;
        u32 saveBLC_CPU_PWM_CTL2;
        u32 saveFPB0;
@@ -823,17 +882,20 @@ struct intel_gen6_power_mgmt {
        struct work_struct work;
        u32 pm_iir;
 
-       /* On vlv we need to manually drop to Vmin with a delayed work. */
-       struct delayed_work vlv_work;
-
        /* The below variables an all the rps hw state are protected by
         * dev->struct mutext. */
        u8 cur_delay;
        u8 min_delay;
        u8 max_delay;
        u8 rpe_delay;
+       u8 rp1_delay;
+       u8 rp0_delay;
        u8 hw_max;
 
+       int last_adj;
+       enum { LOW_POWER, BETWEEN, HIGH_POWER } power;
+
+       bool enabled;
        struct delayed_work delayed_resume_work;
 
        /*
@@ -870,11 +932,21 @@ struct intel_ilk_power_mgmt {
 
 /* Power well structure for haswell */
 struct i915_power_well {
-       struct drm_device *device;
-       spinlock_t lock;
        /* power well enable/disable usage count */
        int count;
-       int i915_request;
+};
+
+#define I915_MAX_POWER_WELLS 1
+
+struct i915_power_domains {
+       /*
+        * Power wells needed for initialization at driver init and suspend
+        * time are on. They are kept on until after the first modeset.
+        */
+       bool init_power_on;
+
+       struct mutex lock;
+       struct i915_power_well power_wells[I915_MAX_POWER_WELLS];
 };
 
 struct i915_dri1_state {
@@ -902,9 +974,11 @@ struct i915_ums_state {
        int mm_suspended;
 };
 
+#define MAX_L3_SLICES 2
 struct intel_l3_parity {
-       u32 *remap_info;
+       u32 *remap_info[MAX_L3_SLICES];
        struct work_struct error_work;
+       int which_slice;
 };
 
 struct i915_gem_mm {
@@ -942,6 +1016,15 @@ struct i915_gem_mm {
        struct delayed_work retire_work;
 
        /**
+        * When we detect an idle GPU, we want to turn on
+        * powersaving features. So once we see that there
+        * are no more requests outstanding and no more
+        * arrive within a small period of time, we fire
+        * off the idle_work.
+        */
+       struct delayed_work idle_work;
+
+       /**
         * Are we in a non-interruptible section of code like
         * modesetting?
         */
@@ -979,6 +1062,9 @@ struct i915_gpu_error {
        /* For hangcheck timer */
 #define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */
 #define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)
+       /* Hang gpu twice in this window and your context gets banned */
+#define DRM_I915_CTX_BAN_PERIOD DIV_ROUND_UP(8*DRM_I915_HANGCHECK_PERIOD, 1000)
+
        struct timer_list hangcheck_timer;
 
        /* For reset and error_state handling. */
@@ -987,7 +1073,8 @@ struct i915_gpu_error {
        struct drm_i915_error_state *first_error;
        struct work_struct work;
 
-       unsigned long last_reset;
+
+       unsigned long missed_irq_rings;
 
        /**
         * State variable and reset counter controlling the reset flow
@@ -1027,6 +1114,9 @@ struct i915_gpu_error {
 
        /* For gpu hang simulation. */
        unsigned int stop_rings;
+
+       /* For missed irq/seqno simulation. */
+       unsigned int test_irq_rings;
 };
 
 enum modeset_restore {
@@ -1035,6 +1125,14 @@ enum modeset_restore {
        MODESET_SUSPENDED,
 };
 
+struct ddi_vbt_port_info {
+       uint8_t hdmi_level_shift;
+
+       uint8_t supports_dvi:1;
+       uint8_t supports_hdmi:1;
+       uint8_t supports_dp:1;
+};
+
 struct intel_vbt_data {
        struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
        struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
@@ -1060,10 +1158,17 @@ struct intel_vbt_data {
        int edp_bpp;
        struct edp_power_seq edp_pps;
 
+       /* MIPI DSI */
+       struct {
+               u16 panel_id;
+       } dsi;
+
        int crt_ddc_pin;
 
        int child_dev_num;
-       struct child_device_config *child_dev;
+       union child_device_config *child_dev;
+
+       struct ddi_vbt_port_info ddi_port_info[I915_MAX_PORTS];
 };
 
 enum intel_ddb_partitioning {
@@ -1079,6 +1184,15 @@ struct intel_wm_level {
        uint32_t fbc_val;
 };
 
+struct hsw_wm_values {
+       uint32_t wm_pipe[3];
+       uint32_t wm_lp[3];
+       uint32_t wm_lp_spr[3];
+       uint32_t wm_linetime[3];
+       bool enable_fbc_wm;
+       enum intel_ddb_partitioning partitioning;
+};
+
 /*
  * This struct tracks the state needed for the Package C8+ feature.
  *
@@ -1148,6 +1262,36 @@ struct i915_package_c8 {
        } regsave;
 };
 
+enum intel_pipe_crc_source {
+       INTEL_PIPE_CRC_SOURCE_NONE,
+       INTEL_PIPE_CRC_SOURCE_PLANE1,
+       INTEL_PIPE_CRC_SOURCE_PLANE2,
+       INTEL_PIPE_CRC_SOURCE_PF,
+       INTEL_PIPE_CRC_SOURCE_PIPE,
+       /* TV/DP on pre-gen5/vlv can't use the pipe source. */
+       INTEL_PIPE_CRC_SOURCE_TV,
+       INTEL_PIPE_CRC_SOURCE_DP_B,
+       INTEL_PIPE_CRC_SOURCE_DP_C,
+       INTEL_PIPE_CRC_SOURCE_DP_D,
+       INTEL_PIPE_CRC_SOURCE_AUTO,
+       INTEL_PIPE_CRC_SOURCE_MAX,
+};
+
+struct intel_pipe_crc_entry {
+       uint32_t frame;
+       uint32_t crc[5];
+};
+
+#define INTEL_PIPE_CRC_ENTRIES_NR      128
+struct intel_pipe_crc {
+       spinlock_t lock;
+       bool opened;            /* exclusive access to the result file */
+       struct intel_pipe_crc_entry *entries;
+       enum intel_pipe_crc_source source;
+       int head, tail;
+       wait_queue_head_t wq;
+};
+
 typedef struct drm_i915_private {
        struct drm_device *dev;
        struct kmem_cache *slab;
@@ -1193,7 +1337,10 @@ typedef struct drm_i915_private {
        struct mutex dpio_lock;
 
        /** Cached value of IMR to avoid reads in updating the bitfield */
-       u32 irq_mask;
+       union {
+               u32 irq_mask;
+               u32 de_irq_mask[I915_MAX_PIPES];
+       };
        u32 gt_irq_mask;
        u32 pm_irq_mask;
 
@@ -1272,6 +1419,10 @@ typedef struct drm_i915_private {
        struct drm_crtc *pipe_to_crtc_mapping[3];
        wait_queue_head_t pending_flip_queue;
 
+#ifdef CONFIG_DEBUG_FS
+       struct intel_pipe_crc pipe_crc[I915_MAX_PIPES];
+#endif
+
        int num_shared_dpll;
        struct intel_shared_dpll shared_dplls[I915_NUM_PLLS];
        struct intel_ddi_plls ddi_plls;
@@ -1297,17 +1448,18 @@ typedef struct drm_i915_private {
         * mchdev_lock in intel_pm.c */
        struct intel_ilk_power_mgmt ips;
 
-       /* Haswell power well */
-       struct i915_power_well power_well;
+       struct i915_power_domains power_domains;
 
-       enum no_psr_reason no_psr_reason;
+       struct i915_psr psr;
 
        struct i915_gpu_error gpu_error;
 
        struct drm_i915_gem_object *vlv_pctx;
 
+#ifdef CONFIG_DRM_I915_FBDEV
        /* list of fbdev register on this device */
        struct intel_fbdev *fbdev;
+#endif
 
        /*
         * The console may be contended at resume, but we don't
@@ -1320,6 +1472,7 @@ typedef struct drm_i915_private {
 
        bool hw_contexts_disabled;
        uint32_t hw_context_size;
+       struct list_head context_list;
 
        u32 fdi_rx_config;
 
@@ -1337,6 +1490,9 @@ typedef struct drm_i915_private {
                uint16_t spr_latency[5];
                /* cursor */
                uint16_t cur_latency[5];
+
+               /* current hardware state */
+               struct hsw_wm_values hw;
        } wm;
 
        struct i915_package_c8 pc8;
@@ -1400,8 +1556,6 @@ struct drm_i915_gem_object {
        struct list_head ring_list;
        /** Used in execbuf to temporarily hold a ref */
        struct list_head obj_exec_link;
-       /** This object's place in the batchbuffer or on the eviction list */
-       struct list_head exec_list;
 
        /**
         * This is set if the object is on the active lists (has pending
@@ -1487,13 +1641,6 @@ struct drm_i915_gem_object {
        void *dma_buf_vmapping;
        int vmapping_count;
 
-       /**
-        * Used for performing relocations during execbuffer insertion.
-        */
-       struct hlist_node exec_node;
-       unsigned long exec_handle;
-       struct drm_i915_gem_exec_object2 *exec_entry;
-
        struct intel_ring_buffer *ring;
 
        /** Breadcrumb of last rendering to the buffer. */
@@ -1505,11 +1652,14 @@ struct drm_i915_gem_object {
        /** Current tiling stride for the object, if it's tiled. */
        uint32_t stride;
 
+       /** References from framebuffers, locks out tiling changes. */
+       unsigned long framebuffer_references;
+
        /** Record of address bit 17 of each page at last unbind. */
        unsigned long *bit_17;
 
        /** User space pin count and filp owning the pin */
-       uint32_t user_pin_count;
+       unsigned long user_pin_count;
        struct drm_file *pin_filp;
 
        /** for phy allocated objects */
@@ -1560,48 +1710,56 @@ struct drm_i915_gem_request {
 };
 
 struct drm_i915_file_private {
+       struct drm_i915_private *dev_priv;
+
        struct {
                spinlock_t lock;
                struct list_head request_list;
+               struct delayed_work idle_work;
        } mm;
        struct idr context_idr;
 
        struct i915_ctx_hang_stats hang_stats;
+       atomic_t rps_wait_boost;
 };
 
 #define INTEL_INFO(dev)        (to_i915(dev)->info)
 
-#define IS_I830(dev)           ((dev)->pci_device == 0x3577)
-#define IS_845G(dev)           ((dev)->pci_device == 0x2562)
+#define IS_I830(dev)           ((dev)->pdev->device == 0x3577)
+#define IS_845G(dev)           ((dev)->pdev->device == 0x2562)
 #define IS_I85X(dev)           (INTEL_INFO(dev)->is_i85x)
-#define IS_I865G(dev)          ((dev)->pci_device == 0x2572)
+#define IS_I865G(dev)          ((dev)->pdev->device == 0x2572)
 #define IS_I915G(dev)          (INTEL_INFO(dev)->is_i915g)
-#define IS_I915GM(dev)         ((dev)->pci_device == 0x2592)
-#define IS_I945G(dev)          ((dev)->pci_device == 0x2772)
+#define IS_I915GM(dev)         ((dev)->pdev->device == 0x2592)
+#define IS_I945G(dev)          ((dev)->pdev->device == 0x2772)
 #define IS_I945GM(dev)         (INTEL_INFO(dev)->is_i945gm)
 #define IS_BROADWATER(dev)     (INTEL_INFO(dev)->is_broadwater)
 #define IS_CRESTLINE(dev)      (INTEL_INFO(dev)->is_crestline)
-#define IS_GM45(dev)           ((dev)->pci_device == 0x2A42)
+#define IS_GM45(dev)           ((dev)->pdev->device == 0x2A42)
 #define IS_G4X(dev)            (INTEL_INFO(dev)->is_g4x)
-#define IS_PINEVIEW_G(dev)     ((dev)->pci_device == 0xa001)
-#define IS_PINEVIEW_M(dev)     ((dev)->pci_device == 0xa011)
+#define IS_PINEVIEW_G(dev)     ((dev)->pdev->device == 0xa001)
+#define IS_PINEVIEW_M(dev)     ((dev)->pdev->device == 0xa011)
 #define IS_PINEVIEW(dev)       (INTEL_INFO(dev)->is_pineview)
 #define IS_G33(dev)            (INTEL_INFO(dev)->is_g33)
-#define IS_IRONLAKE_M(dev)     ((dev)->pci_device == 0x0046)
+#define IS_IRONLAKE_M(dev)     ((dev)->pdev->device == 0x0046)
 #define IS_IVYBRIDGE(dev)      (INTEL_INFO(dev)->is_ivybridge)
-#define IS_IVB_GT1(dev)                ((dev)->pci_device == 0x0156 || \
-                                (dev)->pci_device == 0x0152 || \
-                                (dev)->pci_device == 0x015a)
-#define IS_SNB_GT1(dev)                ((dev)->pci_device == 0x0102 || \
-                                (dev)->pci_device == 0x0106 || \
-                                (dev)->pci_device == 0x010A)
+#define IS_IVB_GT1(dev)                ((dev)->pdev->device == 0x0156 || \
+                                (dev)->pdev->device == 0x0152 || \
+                                (dev)->pdev->device == 0x015a)
+#define IS_SNB_GT1(dev)                ((dev)->pdev->device == 0x0102 || \
+                                (dev)->pdev->device == 0x0106 || \
+                                (dev)->pdev->device == 0x010A)
 #define IS_VALLEYVIEW(dev)     (INTEL_INFO(dev)->is_valleyview)
 #define IS_HASWELL(dev)        (INTEL_INFO(dev)->is_haswell)
+#define IS_BROADWELL(dev)      (INTEL_INFO(dev)->gen == 8)
 #define IS_MOBILE(dev)         (INTEL_INFO(dev)->is_mobile)
 #define IS_HSW_EARLY_SDV(dev)  (IS_HASWELL(dev) && \
-                                ((dev)->pci_device & 0xFF00) == 0x0C00)
+                                ((dev)->pdev->device & 0xFF00) == 0x0C00)
 #define IS_ULT(dev)            (IS_HASWELL(dev) && \
-                                ((dev)->pci_device & 0xFF00) == 0x0A00)
+                                ((dev)->pdev->device & 0xFF00) == 0x0A00)
+#define IS_HSW_GT3(dev)                (IS_HASWELL(dev) && \
+                                ((dev)->pdev->device & 0x00F0) == 0x0020)
+#define IS_PRELIMINARY_HW(intel_info) ((intel_info)->is_preliminary)
 
 /*
  * The genX designation typically refers to the render engine, so render
@@ -1615,10 +1773,15 @@ struct drm_i915_file_private {
 #define IS_GEN5(dev)   (INTEL_INFO(dev)->gen == 5)
 #define IS_GEN6(dev)   (INTEL_INFO(dev)->gen == 6)
 #define IS_GEN7(dev)   (INTEL_INFO(dev)->gen == 7)
-
-#define HAS_BSD(dev)            (INTEL_INFO(dev)->has_bsd_ring)
-#define HAS_BLT(dev)            (INTEL_INFO(dev)->has_blt_ring)
-#define HAS_VEBOX(dev)          (INTEL_INFO(dev)->has_vebox_ring)
+#define IS_GEN8(dev)   (INTEL_INFO(dev)->gen == 8)
+
+#define RENDER_RING            (1<<RCS)
+#define BSD_RING               (1<<VCS)
+#define BLT_RING               (1<<BCS)
+#define VEBOX_RING             (1<<VECS)
+#define HAS_BSD(dev)            (INTEL_INFO(dev)->ring_mask & BSD_RING)
+#define HAS_BLT(dev)            (INTEL_INFO(dev)->ring_mask & BLT_RING)
+#define HAS_VEBOX(dev)            (INTEL_INFO(dev)->ring_mask & VEBOX_RING)
 #define HAS_LLC(dev)            (INTEL_INFO(dev)->has_llc)
 #define HAS_WT(dev)            (IS_HASWELL(dev) && to_i915(dev)->ellc_size)
 #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
@@ -1640,7 +1803,6 @@ struct drm_i915_file_private {
 #define SUPPORTS_DIGITAL_OUTPUTS(dev)  (!IS_GEN2(dev) && !IS_PINEVIEW(dev))
 #define SUPPORTS_INTEGRATED_HDMI(dev)  (IS_G4X(dev) || IS_GEN5(dev))
 #define SUPPORTS_INTEGRATED_DP(dev)    (IS_G4X(dev) || IS_GEN5(dev))
-#define SUPPORTS_EDP(dev)              (IS_IRONLAKE_M(dev))
 #define SUPPORTS_TV(dev)               (INTEL_INFO(dev)->supports_tv)
 #define I915_HAS_HOTPLUG(dev)           (INTEL_INFO(dev)->has_hotplug)
 
@@ -1648,11 +1810,12 @@ struct drm_i915_file_private {
 #define HAS_PIPE_CXSR(dev) (INTEL_INFO(dev)->has_pipe_cxsr)
 #define I915_HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc)
 
-#define HAS_IPS(dev)           (IS_ULT(dev))
+#define HAS_IPS(dev)           (IS_ULT(dev) || IS_BROADWELL(dev))
 
 #define HAS_DDI(dev)           (INTEL_INFO(dev)->has_ddi)
-#define HAS_POWER_WELL(dev)    (IS_HASWELL(dev))
+#define HAS_POWER_WELL(dev)    (IS_HASWELL(dev) || IS_BROADWELL(dev))
 #define HAS_FPGA_DBG_UNCLAIMED(dev)    (INTEL_INFO(dev)->has_fpga_dbg)
+#define HAS_PSR(dev)           (IS_HASWELL(dev) || IS_BROADWELL(dev))
 
 #define INTEL_PCH_DEVICE_ID_MASK               0xff00
 #define INTEL_PCH_IBX_DEVICE_ID_TYPE           0x3b00
@@ -1668,35 +1831,14 @@ struct drm_i915_file_private {
 #define HAS_PCH_NOP(dev) (INTEL_PCH_TYPE(dev) == PCH_NOP)
 #define HAS_PCH_SPLIT(dev) (INTEL_PCH_TYPE(dev) != PCH_NONE)
 
-#define HAS_FORCE_WAKE(dev) (INTEL_INFO(dev)->has_force_wake)
-
-#define HAS_L3_GPU_CACHE(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
+/* DPF == dynamic parity feature */
+#define HAS_L3_DPF(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
+#define NUM_L3_SLICES(dev) (IS_HSW_GT3(dev) ? 2 : HAS_L3_DPF(dev))
 
 #define GT_FREQUENCY_MULTIPLIER 50
 
 #include "i915_trace.h"
 
-/**
- * RC6 is a special power stage which allows the GPU to enter an very
- * low-voltage mode when idle, using down to 0V while at this stage.  This
- * stage is entered automatically when the GPU is idle when RC6 support is
- * enabled, and as soon as new workload arises GPU wakes up automatically as well.
- *
- * There are different RC6 modes available in Intel GPU, which differentiate
- * among each other with the latency required to enter and leave RC6 and
- * voltage consumed by the GPU in different states.
- *
- * The combination of the following flags define which states GPU is allowed
- * to enter, while RC6 is the normal RC6 state, RC6p is the deep RC6, and
- * RC6pp is deepest RC6. Their support by hardware varies according to the
- * GPU, BIOS, chipset and platform. RC6 is usually the safest one and the one
- * which brings the most power savings; deeper states save more power, but
- * require higher latency to switch to and wake up.
- */
-#define INTEL_RC6_ENABLE                       (1<<0)
-#define INTEL_RC6p_ENABLE                      (1<<1)
-#define INTEL_RC6pp_ENABLE                     (1<<2)
-
 extern const struct drm_ioctl_desc i915_ioctls[];
 extern int i915_max_ioctl;
 extern unsigned int i915_fbpercrtc __always_unused;
@@ -1767,12 +1909,13 @@ extern void intel_uncore_early_sanitize(struct drm_device *dev);
 extern void intel_uncore_init(struct drm_device *dev);
 extern void intel_uncore_clear_errors(struct drm_device *dev);
 extern void intel_uncore_check_errors(struct drm_device *dev);
+extern void intel_uncore_fini(struct drm_device *dev);
 
 void
-i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
+i915_enable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask);
 
 void
-i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
+i915_disable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask);
 
 /* i915_gem.c */
 int i915_gem_init_ioctl(struct drm_device *dev, void *data,
@@ -1824,14 +1967,11 @@ int i915_gem_wait_ioctl(struct drm_device *dev, void *data,
 void i915_gem_load(struct drm_device *dev);
 void *i915_gem_object_alloc(struct drm_device *dev);
 void i915_gem_object_free(struct drm_i915_gem_object *obj);
-int i915_gem_init_object(struct drm_gem_object *obj);
 void i915_gem_object_init(struct drm_i915_gem_object *obj,
                         const struct drm_i915_gem_object_ops *ops);
 struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
                                                  size_t size);
 void i915_gem_free_object(struct drm_gem_object *obj);
-struct i915_vma *i915_gem_vma_create(struct drm_i915_gem_object *obj,
-                                    struct i915_address_space *vm);
 void i915_gem_vma_destroy(struct i915_vma *vma);
 
 int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj,
@@ -1870,9 +2010,8 @@ static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
 int __must_check i915_mutex_lock_interruptible(struct drm_device *dev);
 int i915_gem_object_sync(struct drm_i915_gem_object *obj,
                         struct intel_ring_buffer *to);
-void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
-                                   struct intel_ring_buffer *ring);
-
+void i915_vma_move_to_active(struct i915_vma *vma,
+                            struct intel_ring_buffer *ring);
 int i915_gem_dumb_create(struct drm_file *file_priv,
                         struct drm_device *dev,
                         struct drm_mode_create_dumb *args);
@@ -1913,7 +2052,7 @@ i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj)
        }
 }
 
-void i915_gem_retire_requests(struct drm_device *dev);
+bool i915_gem_retire_requests(struct drm_device *dev);
 void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring);
 int __must_check i915_gem_check_wedge(struct i915_gpu_error *error,
                                      bool interruptible);
@@ -1933,11 +2072,11 @@ bool i915_gem_clflush_object(struct drm_i915_gem_object *obj, bool force);
 int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj);
 int __must_check i915_gem_init(struct drm_device *dev);
 int __must_check i915_gem_init_hw(struct drm_device *dev);
-void i915_gem_l3_remap(struct drm_device *dev);
+int i915_gem_l3_remap(struct intel_ring_buffer *ring, int slice);
 void i915_gem_init_swizzling(struct drm_device *dev);
 void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
 int __must_check i915_gpu_idle(struct drm_device *dev);
-int __must_check i915_gem_idle(struct drm_device *dev);
+int __must_check i915_gem_suspend(struct drm_device *dev);
 int __i915_add_request(struct intel_ring_buffer *ring,
                       struct drm_file *file,
                       struct drm_i915_gem_object *batch_obj,
@@ -1964,6 +2103,7 @@ int i915_gem_attach_phys_object(struct drm_device *dev,
 void i915_gem_detach_phys_object(struct drm_device *dev,
                                 struct drm_i915_gem_object *obj);
 void i915_gem_free_all_phys_object(struct drm_device *dev);
+int i915_gem_open(struct drm_device *dev, struct drm_file *file);
 void i915_gem_release(struct drm_device *dev, struct drm_file *file);
 
 uint32_t
@@ -1995,6 +2135,9 @@ struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
 struct i915_vma *
 i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
                                  struct i915_address_space *vm);
+
+struct i915_vma *i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj);
+
 /* Some GGTT VM helpers */
 #define obj_to_ggtt(obj) \
        (&((struct drm_i915_private *)(obj)->base.dev->dev_private)->gtt.base)
@@ -2031,7 +2174,6 @@ i915_gem_obj_ggtt_pin(struct drm_i915_gem_object *obj,
        return i915_gem_object_pin(obj, obj_to_ggtt(obj), alignment,
                                   map_and_fenceable, nonblocking);
 }
-#undef obj_to_ggtt
 
 /* i915_gem_context.c */
 void i915_gem_context_init(struct drm_device *dev);
@@ -2094,6 +2236,7 @@ int __must_check i915_gem_evict_something(struct drm_device *dev,
                                          unsigned cache_level,
                                          bool mappable,
                                          bool nonblock);
+int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle);
 int i915_gem_evict_everything(struct drm_device *dev);
 
 /* i915_gem_stolen.c */
@@ -2133,6 +2276,11 @@ int i915_verify_lists(struct drm_device *dev);
 /* i915_debugfs.c */
 int i915_debugfs_init(struct drm_minor *minor);
 void i915_debugfs_cleanup(struct drm_minor *minor);
+#ifdef CONFIG_DEBUG_FS
+void intel_display_crc_init(struct drm_device *dev);
+#else
+static inline void intel_display_crc_init(struct drm_device *dev) {}
+#endif
 
 /* i915_gpu_error.c */
 __printf(2, 3)
@@ -2186,15 +2334,30 @@ static inline bool intel_gmbus_is_forced_bit(struct i2c_adapter *adapter)
 extern void intel_i2c_reset(struct drm_device *dev);
 
 /* intel_opregion.c */
+struct intel_encoder;
 extern int intel_opregion_setup(struct drm_device *dev);
 #ifdef CONFIG_ACPI
 extern void intel_opregion_init(struct drm_device *dev);
 extern void intel_opregion_fini(struct drm_device *dev);
 extern void intel_opregion_asle_intr(struct drm_device *dev);
+extern int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder,
+                                        bool enable);
+extern int intel_opregion_notify_adapter(struct drm_device *dev,
+                                        pci_power_t state);
 #else
 static inline void intel_opregion_init(struct drm_device *dev) { return; }
 static inline void intel_opregion_fini(struct drm_device *dev) { return; }
 static inline void intel_opregion_asle_intr(struct drm_device *dev) { return; }
+static inline int
+intel_opregion_notify_encoder(struct intel_encoder *intel_encoder, bool enable)
+{
+       return 0;
+}
+static inline int
+intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state)
+{
+       return 0;
+}
 #endif
 
 /* intel_acpi.c */
@@ -2256,8 +2419,16 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val)
 u32 vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr);
 void vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val);
 u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr);
-u32 vlv_dpio_read(struct drm_i915_private *dev_priv, int reg);
-void vlv_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val);
+u32 vlv_gpio_nc_read(struct drm_i915_private *dev_priv, u32 reg);
+void vlv_gpio_nc_write(struct drm_i915_private *dev_priv, u32 reg, u32 val);
+u32 vlv_cck_read(struct drm_i915_private *dev_priv, u32 reg);
+void vlv_cck_write(struct drm_i915_private *dev_priv, u32 reg, u32 val);
+u32 vlv_ccu_read(struct drm_i915_private *dev_priv, u32 reg);
+void vlv_ccu_write(struct drm_i915_private *dev_priv, u32 reg, u32 val);
+u32 vlv_gps_core_read(struct drm_i915_private *dev_priv, u32 reg);
+void vlv_gps_core_write(struct drm_i915_private *dev_priv, u32 reg, u32 val);
+u32 vlv_dpio_read(struct drm_i915_private *dev_priv, enum pipe pipe, int reg);
+void vlv_dpio_write(struct drm_i915_private *dev_priv, enum pipe pipe, int reg, u32 val);
 u32 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg,
                   enum intel_sbi_destination destination);
 void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value,
@@ -2266,37 +2437,21 @@ void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value,
 int vlv_gpu_freq(int ddr_freq, int val);
 int vlv_freq_opcode(int ddr_freq, int val);
 
-#define __i915_read(x) \
-       u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg, bool trace);
-__i915_read(8)
-__i915_read(16)
-__i915_read(32)
-__i915_read(64)
-#undef __i915_read
-
-#define __i915_write(x) \
-       void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val, bool trace);
-__i915_write(8)
-__i915_write(16)
-__i915_write(32)
-__i915_write(64)
-#undef __i915_write
-
-#define I915_READ8(reg)                i915_read8(dev_priv, (reg), true)
-#define I915_WRITE8(reg, val)  i915_write8(dev_priv, (reg), (val), true)
-
-#define I915_READ16(reg)       i915_read16(dev_priv, (reg), true)
-#define I915_WRITE16(reg, val) i915_write16(dev_priv, (reg), (val), true)
-#define I915_READ16_NOTRACE(reg)       i915_read16(dev_priv, (reg), false)
-#define I915_WRITE16_NOTRACE(reg, val) i915_write16(dev_priv, (reg), (val), false)
-
-#define I915_READ(reg)         i915_read32(dev_priv, (reg), true)
-#define I915_WRITE(reg, val)   i915_write32(dev_priv, (reg), (val), true)
-#define I915_READ_NOTRACE(reg)         i915_read32(dev_priv, (reg), false)
-#define I915_WRITE_NOTRACE(reg, val)   i915_write32(dev_priv, (reg), (val), false)
-
-#define I915_WRITE64(reg, val) i915_write64(dev_priv, (reg), (val), true)
-#define I915_READ64(reg)       i915_read64(dev_priv, (reg), true)
+#define I915_READ8(reg)                dev_priv->uncore.funcs.mmio_readb(dev_priv, (reg), true)
+#define I915_WRITE8(reg, val)  dev_priv->uncore.funcs.mmio_writeb(dev_priv, (reg), (val), true)
+
+#define I915_READ16(reg)       dev_priv->uncore.funcs.mmio_readw(dev_priv, (reg), true)
+#define I915_WRITE16(reg, val) dev_priv->uncore.funcs.mmio_writew(dev_priv, (reg), (val), true)
+#define I915_READ16_NOTRACE(reg)       dev_priv->uncore.funcs.mmio_readw(dev_priv, (reg), false)
+#define I915_WRITE16_NOTRACE(reg, val) dev_priv->uncore.funcs.mmio_writew(dev_priv, (reg), (val), false)
+
+#define I915_READ(reg)         dev_priv->uncore.funcs.mmio_readl(dev_priv, (reg), true)
+#define I915_WRITE(reg, val)   dev_priv->uncore.funcs.mmio_writel(dev_priv, (reg), (val), true)
+#define I915_READ_NOTRACE(reg)         dev_priv->uncore.funcs.mmio_readl(dev_priv, (reg), false)
+#define I915_WRITE_NOTRACE(reg, val)   dev_priv->uncore.funcs.mmio_writel(dev_priv, (reg), (val), false)
+
+#define I915_WRITE64(reg, val) dev_priv->uncore.funcs.mmio_writeq(dev_priv, (reg), (val), true)
+#define I915_READ64(reg)       dev_priv->uncore.funcs.mmio_readq(dev_priv, (reg), true)
 
 #define POSTING_READ(reg)      (void)I915_READ_NOTRACE(reg)
 #define POSTING_READ16(reg)    (void)I915_READ16_NOTRACE(reg)
index cdfb9da..12bbd5e 100644 (file)
@@ -41,6 +41,9 @@ static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *o
 static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj,
                                                   bool force);
 static __must_check int
+i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
+                              bool readonly);
+static __must_check int
 i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
                           struct i915_address_space *vm,
                           unsigned alignment,
@@ -61,8 +64,8 @@ static unsigned long i915_gem_inactive_count(struct shrinker *shrinker,
                                             struct shrink_control *sc);
 static unsigned long i915_gem_inactive_scan(struct shrinker *shrinker,
                                            struct shrink_control *sc);
-static long i915_gem_purge(struct drm_i915_private *dev_priv, long target);
-static long i915_gem_shrink_all(struct drm_i915_private *dev_priv);
+static unsigned long i915_gem_purge(struct drm_i915_private *dev_priv, long target);
+static unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv);
 static void i915_gem_object_truncate(struct drm_i915_gem_object *obj);
 
 static bool cpu_cache_is_coherent(struct drm_device *dev,
@@ -258,7 +261,7 @@ i915_gem_dumb_create(struct drm_file *file,
                     struct drm_mode_create_dumb *args)
 {
        /* have to work out size/pitch and return them */
-       args->pitch = ALIGN(args->width * ((args->bpp + 7) / 8), 64);
+       args->pitch = ALIGN(args->width * DIV_ROUND_UP(args->bpp, 8), 64);
        args->size = args->pitch * args->height;
        return i915_gem_create(file, dev,
                               args->size, &args->handle);
@@ -432,11 +435,9 @@ i915_gem_shmem_pread(struct drm_device *dev,
                 * optimizes for the case when the gpu will dirty the data
                 * anyway again before the next pread happens. */
                needs_clflush = !cpu_cache_is_coherent(dev, obj->cache_level);
-               if (i915_gem_obj_bound_any(obj)) {
-                       ret = i915_gem_object_set_to_gtt_domain(obj, false);
-                       if (ret)
-                               return ret;
-               }
+               ret = i915_gem_object_wait_rendering(obj, true);
+               if (ret)
+                       return ret;
        }
 
        ret = i915_gem_object_get_pages(obj);
@@ -748,11 +749,9 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
                 * optimizes for the case when the gpu will use the data
                 * right away and we therefore have to clflush anyway. */
                needs_clflush_after = cpu_write_needs_clflush(obj);
-               if (i915_gem_obj_bound_any(obj)) {
-                       ret = i915_gem_object_set_to_gtt_domain(obj, true);
-                       if (ret)
-                               return ret;
-               }
+               ret = i915_gem_object_wait_rendering(obj, false);
+               if (ret)
+                       return ret;
        }
        /* Same trick applies to invalidate partially written cachelines read
         * before writing. */
@@ -966,12 +965,31 @@ i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno)
        BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex));
 
        ret = 0;
-       if (seqno == ring->outstanding_lazy_request)
+       if (seqno == ring->outstanding_lazy_seqno)
                ret = i915_add_request(ring, NULL);
 
        return ret;
 }
 
+static void fake_irq(unsigned long data)
+{
+       wake_up_process((struct task_struct *)data);
+}
+
+static bool missed_irq(struct drm_i915_private *dev_priv,
+                      struct intel_ring_buffer *ring)
+{
+       return test_bit(ring->id, &dev_priv->gpu_error.missed_irq_rings);
+}
+
+static bool can_wait_boost(struct drm_i915_file_private *file_priv)
+{
+       if (file_priv == NULL)
+               return true;
+
+       return !atomic_xchg(&file_priv->rps_wait_boost, true);
+}
+
 /**
  * __wait_seqno - wait until execution of seqno has finished
  * @ring: the ring expected to report seqno
@@ -992,13 +1010,14 @@ i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno)
  */
 static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
                        unsigned reset_counter,
-                       bool interruptible, struct timespec *timeout)
+                       bool interruptible,
+                       struct timespec *timeout,
+                       struct drm_i915_file_private *file_priv)
 {
        drm_i915_private_t *dev_priv = ring->dev->dev_private;
-       struct timespec before, now, wait_time={1,0};
-       unsigned long timeout_jiffies;
-       long end;
-       bool wait_forever = true;
+       struct timespec before, now;
+       DEFINE_WAIT(wait);
+       long timeout_jiffies;
        int ret;
 
        WARN(dev_priv->pc8.irqs_disabled, "IRQs disabled\n");
@@ -1006,51 +1025,79 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
        if (i915_seqno_passed(ring->get_seqno(ring, true), seqno))
                return 0;
 
-       trace_i915_gem_request_wait_begin(ring, seqno);
+       timeout_jiffies = timeout ? timespec_to_jiffies_timeout(timeout) : 1;
 
-       if (timeout != NULL) {
-               wait_time = *timeout;
-               wait_forever = false;
+       if (dev_priv->info->gen >= 6 && can_wait_boost(file_priv)) {
+               gen6_rps_boost(dev_priv);
+               if (file_priv)
+                       mod_delayed_work(dev_priv->wq,
+                                        &file_priv->mm.idle_work,
+                                        msecs_to_jiffies(100));
        }
 
-       timeout_jiffies = timespec_to_jiffies_timeout(&wait_time);
-
-       if (WARN_ON(!ring->irq_get(ring)))
+       if (!(dev_priv->gpu_error.test_irq_rings & intel_ring_flag(ring)) &&
+           WARN_ON(!ring->irq_get(ring)))
                return -ENODEV;
 
-       /* Record current time in case interrupted by signal, or wedged * */
+       /* Record current time in case interrupted by signal, or wedged */
+       trace_i915_gem_request_wait_begin(ring, seqno);
        getrawmonotonic(&before);
+       for (;;) {
+               struct timer_list timer;
+               unsigned long expire;
 
-#define EXIT_COND \
-       (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \
-        i915_reset_in_progress(&dev_priv->gpu_error) || \
-        reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
-       do {
-               if (interruptible)
-                       end = wait_event_interruptible_timeout(ring->irq_queue,
-                                                              EXIT_COND,
-                                                              timeout_jiffies);
-               else
-                       end = wait_event_timeout(ring->irq_queue, EXIT_COND,
-                                                timeout_jiffies);
+               prepare_to_wait(&ring->irq_queue, &wait,
+                               interruptible ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
 
                /* We need to check whether any gpu reset happened in between
                 * the caller grabbing the seqno and now ... */
-               if (reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
-                       end = -EAGAIN;
+               if (reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) {
+                       /* ... but upgrade the -EAGAIN to an -EIO if the gpu
+                        * is truely gone. */
+                       ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible);
+                       if (ret == 0)
+                               ret = -EAGAIN;
+                       break;
+               }
 
-               /* ... but upgrade the -EGAIN to an -EIO if the gpu is truely
-                * gone. */
-               ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible);
-               if (ret)
-                       end = ret;
-       } while (end == 0 && wait_forever);
+               if (i915_seqno_passed(ring->get_seqno(ring, false), seqno)) {
+                       ret = 0;
+                       break;
+               }
 
+               if (interruptible && signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+
+               if (timeout_jiffies <= 0) {
+                       ret = -ETIME;
+                       break;
+               }
+
+               timer.function = NULL;
+               if (timeout || missed_irq(dev_priv, ring)) {
+                       setup_timer_on_stack(&timer, fake_irq, (unsigned long)current);
+                       expire = jiffies + (missed_irq(dev_priv, ring) ? 1: timeout_jiffies);
+                       mod_timer(&timer, expire);
+               }
+
+               io_schedule();
+
+               if (timeout)
+                       timeout_jiffies = expire - jiffies;
+
+               if (timer.function) {
+                       del_singleshot_timer_sync(&timer);
+                       destroy_timer_on_stack(&timer);
+               }
+       }
        getrawmonotonic(&now);
+       trace_i915_gem_request_wait_end(ring, seqno);
 
        ring->irq_put(ring);
-       trace_i915_gem_request_wait_end(ring, seqno);
-#undef EXIT_COND
+
+       finish_wait(&ring->irq_queue, &wait);
 
        if (timeout) {
                struct timespec sleep_time = timespec_sub(now, before);
@@ -1059,17 +1106,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
                        set_normalized_timespec(timeout, 0, 0);
        }
 
-       switch (end) {
-       case -EIO:
-       case -EAGAIN: /* Wedged */
-       case -ERESTARTSYS: /* Signal */
-               return (int)end;
-       case 0: /* Timeout */
-               return -ETIME;
-       default: /* Completed */
-               WARN_ON(end < 0); /* We're not aware of other errors */
-               return 0;
-       }
+       return ret;
 }
 
 /**
@@ -1097,7 +1134,7 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
 
        return __wait_seqno(ring, seqno,
                            atomic_read(&dev_priv->gpu_error.reset_counter),
-                           interruptible, NULL);
+                           interruptible, NULL, NULL);
 }
 
 static int
@@ -1147,6 +1184,7 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
  */
 static __must_check int
 i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
+                                           struct drm_file *file,
                                            bool readonly)
 {
        struct drm_device *dev = obj->base.dev;
@@ -1173,7 +1211,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
 
        reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
        mutex_unlock(&dev->struct_mutex);
-       ret = __wait_seqno(ring, seqno, reset_counter, true, NULL);
+       ret = __wait_seqno(ring, seqno, reset_counter, true, NULL, file->driver_priv);
        mutex_lock(&dev->struct_mutex);
        if (ret)
                return ret;
@@ -1222,7 +1260,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
         * We will repeat the flush holding the lock in the normal manner
         * to catch cases where we are gazumped.
         */
-       ret = i915_gem_object_wait_rendering__nonblocking(obj, !write_domain);
+       ret = i915_gem_object_wait_rendering__nonblocking(obj, file, !write_domain);
        if (ret)
                goto unref;
 
@@ -1690,13 +1728,13 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
        return 0;
 }
 
-static long
+static unsigned long
 __i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
                  bool purgeable_only)
 {
        struct list_head still_bound_list;
        struct drm_i915_gem_object *obj, *next;
-       long count = 0;
+       unsigned long count = 0;
 
        list_for_each_entry_safe(obj, next,
                                 &dev_priv->mm.unbound_list,
@@ -1762,13 +1800,13 @@ __i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
        return count;
 }
 
-static long
+static unsigned long
 i915_gem_purge(struct drm_i915_private *dev_priv, long target)
 {
        return __i915_gem_shrink(dev_priv, target, true);
 }
 
-static long
+static unsigned long
 i915_gem_shrink_all(struct drm_i915_private *dev_priv)
 {
        struct drm_i915_gem_object *obj, *next;
@@ -1778,9 +1816,8 @@ i915_gem_shrink_all(struct drm_i915_private *dev_priv)
 
        list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list,
                                 global_list) {
-               if (obj->pages_pin_count == 0)
+               if (i915_gem_object_put_pages(obj) == 0)
                        freed += obj->base.size >> PAGE_SHIFT;
-               i915_gem_object_put_pages(obj);
        }
        return freed;
 }
@@ -1865,6 +1902,9 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
                        sg->length += PAGE_SIZE;
                }
                last_pfn = page_to_pfn(page);
+
+               /* Check that the i965g/gm workaround works. */
+               WARN_ON((gfp & __GFP_DMA32) && (last_pfn >= 0x00100000UL));
        }
 #ifdef CONFIG_SWIOTLB
        if (!swiotlb_nr_tbl())
@@ -1918,7 +1958,7 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
        return 0;
 }
 
-void
+static void
 i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
                               struct intel_ring_buffer *ring)
 {
@@ -1957,6 +1997,13 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
        }
 }
 
+void i915_vma_move_to_active(struct i915_vma *vma,
+                            struct intel_ring_buffer *ring)
+{
+       list_move_tail(&vma->mm_list, &vma->vm->active_list);
+       return i915_gem_object_move_to_active(vma->obj, ring);
+}
+
 static void
 i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
 {
@@ -2078,11 +2125,10 @@ int __i915_add_request(struct intel_ring_buffer *ring,
        if (ret)
                return ret;
 
-       request = kmalloc(sizeof(*request), GFP_KERNEL);
-       if (request == NULL)
+       request = ring->preallocated_lazy_request;
+       if (WARN_ON(request == NULL))
                return -ENOMEM;
 
-
        /* Record the position of the start of the request so that
         * should we detect the updated seqno part-way through the
         * GPU processing the request, we never over-estimate the
@@ -2091,17 +2137,13 @@ int __i915_add_request(struct intel_ring_buffer *ring,
        request_ring_position = intel_ring_get_tail(ring);
 
        ret = ring->add_request(ring);
-       if (ret) {
-               kfree(request);
+       if (ret)
                return ret;
-       }
 
        request->seqno = intel_ring_get_seqno(ring);
        request->ring = ring;
        request->head = request_start;
        request->tail = request_ring_position;
-       request->ctx = ring->last_context;
-       request->batch_obj = obj;
 
        /* Whilst this request exists, batch_obj will be on the
         * active_list, and so will hold the active reference. Only when this
@@ -2109,7 +2151,12 @@ int __i915_add_request(struct intel_ring_buffer *ring,
         * inactive_list and lose its active reference. Hence we do not need
         * to explicitly hold another reference here.
         */
+       request->batch_obj = obj;
 
+       /* Hold a reference to the current context so that we can inspect
+        * it later in case a hangcheck error event fires.
+        */
+       request->ctx = ring->last_context;
        if (request->ctx)
                i915_gem_context_reference(request->ctx);
 
@@ -2129,12 +2176,14 @@ int __i915_add_request(struct intel_ring_buffer *ring,
        }
 
        trace_i915_gem_request_add(ring, request->seqno);
-       ring->outstanding_lazy_request = 0;
+       ring->outstanding_lazy_seqno = 0;
+       ring->preallocated_lazy_request = NULL;
 
        if (!dev_priv->ums.mm_suspended) {
                i915_queue_hangcheck(ring->dev);
 
                if (was_empty) {
+                       cancel_delayed_work_sync(&dev_priv->mm.idle_work);
                        queue_delayed_work(dev_priv->wq,
                                           &dev_priv->mm.retire_work,
                                           round_jiffies_up_relative(HZ));
@@ -2156,10 +2205,8 @@ i915_gem_request_remove_from_client(struct drm_i915_gem_request *request)
                return;
 
        spin_lock(&file_priv->mm.lock);
-       if (request->file_priv) {
-               list_del(&request->client_list);
-               request->file_priv = NULL;
-       }
+       list_del(&request->client_list);
+       request->file_priv = NULL;
        spin_unlock(&file_priv->mm.lock);
 }
 
@@ -2224,6 +2271,21 @@ static bool i915_request_guilty(struct drm_i915_gem_request *request,
        return false;
 }
 
+static bool i915_context_is_banned(const struct i915_ctx_hang_stats *hs)
+{
+       const unsigned long elapsed = get_seconds() - hs->guilty_ts;
+
+       if (hs->banned)
+               return true;
+
+       if (elapsed <= DRM_I915_CTX_BAN_PERIOD) {
+               DRM_ERROR("context hanging too fast, declaring banned!\n");
+               return true;
+       }
+
+       return false;
+}
+
 static void i915_set_reset_status(struct intel_ring_buffer *ring,
                                  struct drm_i915_gem_request *request,
                                  u32 acthd)
@@ -2260,10 +2322,13 @@ static void i915_set_reset_status(struct intel_ring_buffer *ring,
                hs = &request->file_priv->hang_stats;
 
        if (hs) {
-               if (guilty)
+               if (guilty) {
+                       hs->banned = i915_context_is_banned(hs);
                        hs->batch_active++;
-               else
+                       hs->guilty_ts = get_seconds();
+               } else {
                        hs->batch_pending++;
+               }
        }
 }
 
@@ -2341,6 +2406,8 @@ void i915_gem_reset(struct drm_device *dev)
        for_each_ring(ring, dev_priv, i)
                i915_gem_reset_ring_lists(dev_priv, ring);
 
+       i915_gem_cleanup_ringbuffer(dev);
+
        i915_gem_restore_fences(dev);
 }
 
@@ -2405,57 +2472,53 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
        WARN_ON(i915_verify_lists(ring->dev));
 }
 
-void
+bool
 i915_gem_retire_requests(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct intel_ring_buffer *ring;
+       bool idle = true;
        int i;
 
-       for_each_ring(ring, dev_priv, i)
+       for_each_ring(ring, dev_priv, i) {
                i915_gem_retire_requests_ring(ring);
+               idle &= list_empty(&ring->request_list);
+       }
+
+       if (idle)
+               mod_delayed_work(dev_priv->wq,
+                                  &dev_priv->mm.idle_work,
+                                  msecs_to_jiffies(100));
+
+       return idle;
 }
 
 static void
 i915_gem_retire_work_handler(struct work_struct *work)
 {
-       drm_i915_private_t *dev_priv;
-       struct drm_device *dev;
-       struct intel_ring_buffer *ring;
+       struct drm_i915_private *dev_priv =
+               container_of(work, typeof(*dev_priv), mm.retire_work.work);
+       struct drm_device *dev = dev_priv->dev;
        bool idle;
-       int i;
-
-       dev_priv = container_of(work, drm_i915_private_t,
-                               mm.retire_work.work);
-       dev = dev_priv->dev;
 
        /* Come back later if the device is busy... */
-       if (!mutex_trylock(&dev->struct_mutex)) {
-               queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work,
-                                  round_jiffies_up_relative(HZ));
-               return;
-       }
-
-       i915_gem_retire_requests(dev);
-
-       /* Send a periodic flush down the ring so we don't hold onto GEM
-        * objects indefinitely.
-        */
-       idle = true;
-       for_each_ring(ring, dev_priv, i) {
-               if (ring->gpu_caches_dirty)
-                       i915_add_request(ring, NULL);
-
-               idle &= list_empty(&ring->request_list);
+       idle = false;
+       if (mutex_trylock(&dev->struct_mutex)) {
+               idle = i915_gem_retire_requests(dev);
+               mutex_unlock(&dev->struct_mutex);
        }
-
-       if (!dev_priv->ums.mm_suspended && !idle)
+       if (!idle)
                queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work,
                                   round_jiffies_up_relative(HZ));
-       if (idle)
-               intel_mark_idle(dev);
+}
 
-       mutex_unlock(&dev->struct_mutex);
+static void
+i915_gem_idle_work_handler(struct work_struct *work)
+{
+       struct drm_i915_private *dev_priv =
+               container_of(work, typeof(*dev_priv), mm.idle_work.work);
+
+       intel_mark_idle(dev_priv->dev);
 }
 
 /**
@@ -2553,7 +2616,7 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
        reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
        mutex_unlock(&dev->struct_mutex);
 
-       ret = __wait_seqno(ring, seqno, reset_counter, true, timeout);
+       ret = __wait_seqno(ring, seqno, reset_counter, true, timeout, file->driver_priv);
        if (timeout)
                args->timeout_ns = timespec_to_ns(timeout);
        return ret;
@@ -2600,6 +2663,7 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj,
        if (ret)
                return ret;
 
+       trace_i915_gem_ring_sync_to(from, to, seqno);
        ret = to->sync_to(to, from, seqno);
        if (!ret)
                /* We use last_read_seqno because sync_to()
@@ -2641,11 +2705,17 @@ int i915_vma_unbind(struct i915_vma *vma)
        drm_i915_private_t *dev_priv = obj->base.dev->dev_private;
        int ret;
 
+       /* For now we only ever use 1 vma per object */
+       WARN_ON(!list_is_singular(&obj->vma_list));
+
        if (list_empty(&vma->vma_link))
                return 0;
 
-       if (!drm_mm_node_allocated(&vma->node))
-               goto destroy;
+       if (!drm_mm_node_allocated(&vma->node)) {
+               i915_gem_vma_destroy(vma);
+
+               return 0;
+       }
 
        if (obj->pin_count)
                return -EBUSY;
@@ -2685,13 +2755,10 @@ int i915_vma_unbind(struct i915_vma *vma)
 
        drm_mm_remove_node(&vma->node);
 
-destroy:
        i915_gem_vma_destroy(vma);
 
        /* Since the unbound list is global, only move to that list if
-        * no more VMAs exist.
-        * NB: Until we have real VMAs there will only ever be one */
-       WARN_ON(!list_empty(&obj->vma_list));
+        * no more VMAs exist. */
        if (list_empty(&obj->vma_list))
                list_move_tail(&obj->global_list, &dev_priv->mm.unbound_list);
 
@@ -2887,6 +2954,7 @@ static void i915_gem_write_fence(struct drm_device *dev, int reg,
             obj->stride, obj->tiling_mode);
 
        switch (INTEL_INFO(dev)->gen) {
+       case 8:
        case 7:
        case 6:
        case 5:
@@ -3389,8 +3457,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
 
        /* And bump the LRU for this access */
        if (i915_gem_object_is_inactive(obj)) {
-               struct i915_vma *vma = i915_gem_obj_to_vma(obj,
-                                                          &dev_priv->gtt.base);
+               struct i915_vma *vma = i915_gem_obj_to_ggtt(obj);
                if (vma)
                        list_move_tail(&vma->mm_list,
                                       &dev_priv->gtt.base.inactive_list);
@@ -3761,7 +3828,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
        if (seqno == 0)
                return 0;
 
-       ret = __wait_seqno(ring, seqno, reset_counter, true, NULL);
+       ret = __wait_seqno(ring, seqno, reset_counter, true, NULL, NULL);
        if (ret == 0)
                queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0);
 
@@ -3865,6 +3932,11 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
                goto out;
        }
 
+       if (obj->user_pin_count == ULONG_MAX) {
+               ret = -EBUSY;
+               goto out;
+       }
+
        if (obj->user_pin_count == 0) {
                ret = i915_gem_obj_ggtt_pin(obj, args->alignment, true, false);
                if (ret)
@@ -4015,7 +4087,6 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
 {
        INIT_LIST_HEAD(&obj->global_list);
        INIT_LIST_HEAD(&obj->ring_list);
-       INIT_LIST_HEAD(&obj->exec_list);
        INIT_LIST_HEAD(&obj->obj_exec_link);
        INIT_LIST_HEAD(&obj->vma_list);
 
@@ -4087,13 +4158,6 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
        return obj;
 }
 
-int i915_gem_init_object(struct drm_gem_object *obj)
-{
-       BUG();
-
-       return 0;
-}
-
 void i915_gem_free_object(struct drm_gem_object *gem_obj)
 {
        struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
@@ -4147,9 +4211,20 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
        i915_gem_object_free(obj);
 }
 
-struct i915_vma *i915_gem_vma_create(struct drm_i915_gem_object *obj,
+struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
                                     struct i915_address_space *vm)
 {
+       struct i915_vma *vma;
+       list_for_each_entry(vma, &obj->vma_list, vma_link)
+               if (vma->vm == vm)
+                       return vma;
+
+       return NULL;
+}
+
+static struct i915_vma *__i915_gem_vma_create(struct drm_i915_gem_object *obj,
+                                             struct i915_address_space *vm)
+{
        struct i915_vma *vma = kzalloc(sizeof(*vma), GFP_KERNEL);
        if (vma == NULL)
                return ERR_PTR(-ENOMEM);
@@ -4169,76 +4244,103 @@ struct i915_vma *i915_gem_vma_create(struct drm_i915_gem_object *obj,
        return vma;
 }
 
+struct i915_vma *
+i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
+                                 struct i915_address_space *vm)
+{
+       struct i915_vma *vma;
+
+       vma = i915_gem_obj_to_vma(obj, vm);
+       if (!vma)
+               vma = __i915_gem_vma_create(obj, vm);
+
+       return vma;
+}
+
 void i915_gem_vma_destroy(struct i915_vma *vma)
 {
        WARN_ON(vma->node.allocated);
+
+       /* Keep the vma as a placeholder in the execbuffer reservation lists */
+       if (!list_empty(&vma->exec_list))
+               return;
+
        list_del(&vma->vma_link);
+
        kfree(vma);
 }
 
 int
-i915_gem_idle(struct drm_device *dev)
+i915_gem_suspend(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       int ret;
+       int ret = 0;
 
-       if (dev_priv->ums.mm_suspended) {
-               mutex_unlock(&dev->struct_mutex);
-               return 0;
-       }
+       mutex_lock(&dev->struct_mutex);
+       if (dev_priv->ums.mm_suspended)
+               goto err;
 
        ret = i915_gpu_idle(dev);
-       if (ret) {
-               mutex_unlock(&dev->struct_mutex);
-               return ret;
-       }
+       if (ret)
+               goto err;
+
        i915_gem_retire_requests(dev);
 
        /* Under UMS, be paranoid and evict. */
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                i915_gem_evict_everything(dev);
 
-       del_timer_sync(&dev_priv->gpu_error.hangcheck_timer);
-
        i915_kernel_lost_context(dev);
        i915_gem_cleanup_ringbuffer(dev);
 
-       /* Cancel the retire work handler, which should be idle now. */
+       /* Hack!  Don't let anybody do execbuf while we don't control the chip.
+        * We need to replace this with a semaphore, or something.
+        * And not confound ums.mm_suspended!
+        */
+       dev_priv->ums.mm_suspended = !drm_core_check_feature(dev,
+                                                            DRIVER_MODESET);
+       mutex_unlock(&dev->struct_mutex);
+
+       del_timer_sync(&dev_priv->gpu_error.hangcheck_timer);
        cancel_delayed_work_sync(&dev_priv->mm.retire_work);
+       cancel_delayed_work_sync(&dev_priv->mm.idle_work);
 
        return 0;
+
+err:
+       mutex_unlock(&dev->struct_mutex);
+       return ret;
 }
 
-void i915_gem_l3_remap(struct drm_device *dev)
+int i915_gem_l3_remap(struct intel_ring_buffer *ring, int slice)
 {
+       struct drm_device *dev = ring->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
-       u32 misccpctl;
-       int i;
-
-       if (!HAS_L3_GPU_CACHE(dev))
-               return;
+       u32 reg_base = GEN7_L3LOG_BASE + (slice * 0x200);
+       u32 *remap_info = dev_priv->l3_parity.remap_info[slice];
+       int i, ret;
 
-       if (!dev_priv->l3_parity.remap_info)
-               return;
+       if (!HAS_L3_DPF(dev) || !remap_info)
+               return 0;
 
-       misccpctl = I915_READ(GEN7_MISCCPCTL);
-       I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE);
-       POSTING_READ(GEN7_MISCCPCTL);
+       ret = intel_ring_begin(ring, GEN7_L3LOG_SIZE / 4 * 3);
+       if (ret)
+               return ret;
 
+       /*
+        * Note: We do not worry about the concurrent register cacheline hang
+        * here because no other code should access these registers other than
+        * at initialization time.
+        */
        for (i = 0; i < GEN7_L3LOG_SIZE; i += 4) {
-               u32 remap = I915_READ(GEN7_L3LOG_BASE + i);
-               if (remap && remap != dev_priv->l3_parity.remap_info[i/4])
-                       DRM_DEBUG("0x%x was already programmed to %x\n",
-                                 GEN7_L3LOG_BASE + i, remap);
-               if (remap && !dev_priv->l3_parity.remap_info[i/4])
-                       DRM_DEBUG_DRIVER("Clearing remapped register\n");
-               I915_WRITE(GEN7_L3LOG_BASE + i, dev_priv->l3_parity.remap_info[i/4]);
+               intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+               intel_ring_emit(ring, reg_base + i);
+               intel_ring_emit(ring, remap_info[i/4]);
        }
 
-       /* Make sure all the writes land before disabling dop clock gating */
-       POSTING_READ(GEN7_L3LOG_BASE);
+       intel_ring_advance(ring);
 
-       I915_WRITE(GEN7_MISCCPCTL, misccpctl);
+       return ret;
 }
 
 void i915_gem_init_swizzling(struct drm_device *dev)
@@ -4260,6 +4362,8 @@ void i915_gem_init_swizzling(struct drm_device *dev)
                I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_SNB));
        else if (IS_GEN7(dev))
                I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_IVB));
+       else if (IS_GEN8(dev))
+               I915_WRITE(GAMTARBMODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_BDW));
        else
                BUG();
 }
@@ -4330,7 +4434,7 @@ int
 i915_gem_init_hw(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       int ret;
+       int ret, i;
 
        if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt())
                return -EIO;
@@ -4338,20 +4442,26 @@ i915_gem_init_hw(struct drm_device *dev)
        if (dev_priv->ellc_size)
                I915_WRITE(HSW_IDICR, I915_READ(HSW_IDICR) | IDIHASHMSK(0xf));
 
+       if (IS_HSW_GT3(dev))
+               I915_WRITE(MI_PREDICATE_RESULT_2, LOWER_SLICE_ENABLED);
+       else
+               I915_WRITE(MI_PREDICATE_RESULT_2, LOWER_SLICE_DISABLED);
+
        if (HAS_PCH_NOP(dev)) {
                u32 temp = I915_READ(GEN7_MSG_CTL);
                temp &= ~(WAIT_FOR_PCH_FLR_ACK | WAIT_FOR_PCH_RESET_ACK);
                I915_WRITE(GEN7_MSG_CTL, temp);
        }
 
-       i915_gem_l3_remap(dev);
-
        i915_gem_init_swizzling(dev);
 
        ret = i915_gem_init_rings(dev);
        if (ret)
                return ret;
 
+       for (i = 0; i < NUM_L3_SLICES(dev); i++)
+               i915_gem_l3_remap(&dev_priv->ring[RCS], i);
+
        /*
         * XXX: There was some w/a described somewhere suggesting loading
         * contexts before PPGTT.
@@ -4454,26 +4564,12 @@ int
 i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
                       struct drm_file *file_priv)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       int ret;
-
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                return 0;
 
        drm_irq_uninstall(dev);
 
-       mutex_lock(&dev->struct_mutex);
-       ret =  i915_gem_idle(dev);
-
-       /* Hack!  Don't let anybody do execbuf while we don't control the chip.
-        * We need to replace this with a semaphore, or something.
-        * And not confound ums.mm_suspended!
-        */
-       if (ret != 0)
-               dev_priv->ums.mm_suspended = 1;
-       mutex_unlock(&dev->struct_mutex);
-
-       return ret;
+       return i915_gem_suspend(dev);
 }
 
 void
@@ -4484,11 +4580,9 @@ i915_gem_lastclose(struct drm_device *dev)
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                return;
 
-       mutex_lock(&dev->struct_mutex);
-       ret = i915_gem_idle(dev);
+       ret = i915_gem_suspend(dev);
        if (ret)
                DRM_ERROR("failed to idle hardware: %d\n", ret);
-       mutex_unlock(&dev->struct_mutex);
 }
 
 static void
@@ -4523,6 +4617,7 @@ i915_gem_load(struct drm_device *dev)
        INIT_LIST_HEAD(&dev_priv->vm_list);
        i915_init_vm(dev_priv, &dev_priv->gtt.base);
 
+       INIT_LIST_HEAD(&dev_priv->context_list);
        INIT_LIST_HEAD(&dev_priv->mm.unbound_list);
        INIT_LIST_HEAD(&dev_priv->mm.bound_list);
        INIT_LIST_HEAD(&dev_priv->mm.fence_list);
@@ -4532,6 +4627,8 @@ i915_gem_load(struct drm_device *dev)
                INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list);
        INIT_DELAYED_WORK(&dev_priv->mm.retire_work,
                          i915_gem_retire_work_handler);
+       INIT_DELAYED_WORK(&dev_priv->mm.idle_work,
+                         i915_gem_idle_work_handler);
        init_waitqueue_head(&dev_priv->gpu_error.reset_queue);
 
        /* On GEN3 we really need to make sure the ARB C3 LP bit is set */
@@ -4582,7 +4679,7 @@ static int i915_gem_init_phys_object(struct drm_device *dev,
        if (dev_priv->mm.phys_objs[id - 1] || !size)
                return 0;
 
-       phys_obj = kzalloc(sizeof(struct drm_i915_gem_phys_object), GFP_KERNEL);
+       phys_obj = kzalloc(sizeof(*phys_obj), GFP_KERNEL);
        if (!phys_obj)
                return -ENOMEM;
 
@@ -4756,6 +4853,8 @@ void i915_gem_release(struct drm_device *dev, struct drm_file *file)
 {
        struct drm_i915_file_private *file_priv = file->driver_priv;
 
+       cancel_delayed_work_sync(&file_priv->mm.idle_work);
+
        /* Clean up our request list when the client is going away, so that
         * later retire_requests won't dereference our soon-to-be-gone
         * file_priv.
@@ -4773,6 +4872,38 @@ void i915_gem_release(struct drm_device *dev, struct drm_file *file)
        spin_unlock(&file_priv->mm.lock);
 }
 
+static void
+i915_gem_file_idle_work_handler(struct work_struct *work)
+{
+       struct drm_i915_file_private *file_priv =
+               container_of(work, typeof(*file_priv), mm.idle_work.work);
+
+       atomic_set(&file_priv->rps_wait_boost, false);
+}
+
+int i915_gem_open(struct drm_device *dev, struct drm_file *file)
+{
+       struct drm_i915_file_private *file_priv;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
+       if (!file_priv)
+               return -ENOMEM;
+
+       file->driver_priv = file_priv;
+       file_priv->dev_priv = dev->dev_private;
+
+       spin_lock_init(&file_priv->mm.lock);
+       INIT_LIST_HEAD(&file_priv->mm.request_list);
+       INIT_DELAYED_WORK(&file_priv->mm.idle_work,
+                         i915_gem_file_idle_work_handler);
+
+       idr_init(&file_priv->context_idr);
+
+       return 0;
+}
+
 static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
 {
        if (!mutex_is_locked(mutex))
@@ -4823,6 +4954,7 @@ i915_gem_inactive_count(struct shrinker *shrinker, struct shrink_control *sc)
 
        if (unlock)
                mutex_unlock(&dev->struct_mutex);
+
        return count;
 }
 
@@ -4859,11 +4991,10 @@ bool i915_gem_obj_bound(struct drm_i915_gem_object *o,
 
 bool i915_gem_obj_bound_any(struct drm_i915_gem_object *o)
 {
-       struct drm_i915_private *dev_priv = o->base.dev->dev_private;
-       struct i915_address_space *vm;
+       struct i915_vma *vma;
 
-       list_for_each_entry(vm, &dev_priv->vm_list, global_link)
-               if (i915_gem_obj_bound(o, vm))
+       list_for_each_entry(vma, &o->vma_list, vma_link)
+               if (drm_mm_node_allocated(&vma->node))
                        return true;
 
        return false;
@@ -4895,7 +5026,6 @@ i915_gem_inactive_scan(struct shrinker *shrinker, struct shrink_control *sc)
                             struct drm_i915_private,
                             mm.inactive_shrinker);
        struct drm_device *dev = dev_priv->dev;
-       int nr_to_scan = sc->nr_to_scan;
        unsigned long freed;
        bool unlock = true;
 
@@ -4909,38 +5039,30 @@ i915_gem_inactive_scan(struct shrinker *shrinker, struct shrink_control *sc)
                unlock = false;
        }
 
-       freed = i915_gem_purge(dev_priv, nr_to_scan);
-       if (freed < nr_to_scan)
-               freed += __i915_gem_shrink(dev_priv, nr_to_scan,
-                                                       false);
-       if (freed < nr_to_scan)
+       freed = i915_gem_purge(dev_priv, sc->nr_to_scan);
+       if (freed < sc->nr_to_scan)
+               freed += __i915_gem_shrink(dev_priv,
+                                          sc->nr_to_scan - freed,
+                                          false);
+       if (freed < sc->nr_to_scan)
                freed += i915_gem_shrink_all(dev_priv);
 
        if (unlock)
                mutex_unlock(&dev->struct_mutex);
+
        return freed;
 }
 
-struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
-                                    struct i915_address_space *vm)
+struct i915_vma *i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj)
 {
        struct i915_vma *vma;
-       list_for_each_entry(vma, &obj->vma_list, vma_link)
-               if (vma->vm == vm)
-                       return vma;
 
-       return NULL;
-}
-
-struct i915_vma *
-i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
-                                 struct i915_address_space *vm)
-{
-       struct i915_vma *vma;
+       if (WARN_ON(list_empty(&obj->vma_list)))
+               return NULL;
 
-       vma = i915_gem_obj_to_vma(obj, vm);
-       if (!vma)
-               vma = i915_gem_vma_create(obj, vm);
+       vma = list_first_entry(&obj->vma_list, typeof(*vma), vma_link);
+       if (WARN_ON(vma->vm != obj_to_ggtt(obj)))
+               return NULL;
 
        return vma;
 }
index 403309c..72a3df3 100644 (file)
@@ -73,7 +73,7 @@
  *
  * There are two confusing terms used above:
  *  The "current context" means the context which is currently running on the
- *  GPU. The GPU has loaded it's state already and has stored away the gtt
+ *  GPU. The GPU has loaded its state already and has stored away the gtt
  *  offset of the BO. The GPU is not actively referencing the data at this
  *  offset, but it will on the next context switch. The only way to avoid this
  *  is to do a GPU reset.
@@ -117,6 +117,9 @@ static int get_context_size(struct drm_device *dev)
                else
                        ret = GEN7_CXT_TOTAL_SIZE(reg) * 64;
                break;
+       case 8:
+               ret = GEN8_CXT_TOTAL_SIZE;
+               break;
        default:
                BUG();
        }
@@ -129,6 +132,7 @@ void i915_gem_context_free(struct kref *ctx_ref)
        struct i915_hw_context *ctx = container_of(ctx_ref,
                                                   typeof(*ctx), ref);
 
+       list_del(&ctx->link);
        drm_gem_object_unreference(&ctx->obj->base);
        kfree(ctx);
 }
@@ -147,6 +151,7 @@ create_hw_context(struct drm_device *dev,
 
        kref_init(&ctx->ref);
        ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size);
+       INIT_LIST_HEAD(&ctx->link);
        if (ctx->obj == NULL) {
                kfree(ctx);
                DRM_DEBUG_DRIVER("Context object allocated failed\n");
@@ -166,6 +171,7 @@ create_hw_context(struct drm_device *dev,
         * assertion in the context switch code.
         */
        ctx->ring = &dev_priv->ring[RCS];
+       list_add_tail(&ctx->link, &dev_priv->context_list);
 
        /* Default context will never have a file_priv */
        if (file_priv == NULL)
@@ -178,6 +184,10 @@ create_hw_context(struct drm_device *dev,
 
        ctx->file_priv = file_priv;
        ctx->id = ret;
+       /* NB: Mark all slices as needing a remap so that when the context first
+        * loads it will restore whatever remap state already exists. If there
+        * is no remap info, it will be a NOP. */
+       ctx->remap_slice = (1 << NUM_L3_SLICES(dev)) - 1;
 
        return ctx;
 
@@ -213,7 +223,6 @@ static int create_default_context(struct drm_i915_private *dev_priv)
         * may not be available. To avoid this we always pin the
         * default context.
         */
-       dev_priv->ring[RCS].default_context = ctx;
        ret = i915_gem_obj_ggtt_pin(ctx->obj, CONTEXT_ALIGN, false, false);
        if (ret) {
                DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret);
@@ -226,6 +235,8 @@ static int create_default_context(struct drm_i915_private *dev_priv)
                goto err_unpin;
        }
 
+       dev_priv->ring[RCS].default_context = ctx;
+
        DRM_DEBUG_DRIVER("Default HW context loaded\n");
        return 0;
 
@@ -281,16 +292,24 @@ void i915_gem_context_fini(struct drm_device *dev)
         * other code, leading to spurious errors. */
        intel_gpu_reset(dev);
 
-       i915_gem_object_unpin(dctx->obj);
-
        /* When default context is created and switched to, base object refcount
         * will be 2 (+1 from object creation and +1 from do_switch()).
         * i915_gem_context_fini() will be called after gpu_idle() has switched
         * to default context. So we need to unreference the base object once
         * to offset the do_switch part, so that i915_gem_context_unreference()
         * can then free the base object correctly. */
-       drm_gem_object_unreference(&dctx->obj->base);
+       WARN_ON(!dev_priv->ring[RCS].last_context);
+       if (dev_priv->ring[RCS].last_context == dctx) {
+               /* Fake switch to NULL context */
+               WARN_ON(dctx->obj->active);
+               i915_gem_object_unpin(dctx->obj);
+               i915_gem_context_unreference(dctx);
+       }
+
+       i915_gem_object_unpin(dctx->obj);
        i915_gem_context_unreference(dctx);
+       dev_priv->ring[RCS].default_context = NULL;
+       dev_priv->ring[RCS].last_context = NULL;
 }
 
 static int context_idr_cleanup(int id, void *p, void *data)
@@ -393,11 +412,11 @@ static int do_switch(struct i915_hw_context *to)
        struct intel_ring_buffer *ring = to->ring;
        struct i915_hw_context *from = ring->last_context;
        u32 hw_flags = 0;
-       int ret;
+       int ret, i;
 
        BUG_ON(from != NULL && from->obj != NULL && from->obj->pin_count == 0);
 
-       if (from == to)
+       if (from == to && !to->remap_slice)
                return 0;
 
        ret = i915_gem_obj_ggtt_pin(to->obj, CONTEXT_ALIGN, false, false);
@@ -420,8 +439,6 @@ static int do_switch(struct i915_hw_context *to)
 
        if (!to->is_initialized || is_default_context(to))
                hw_flags |= MI_RESTORE_INHIBIT;
-       else if (WARN_ON_ONCE(from == to)) /* not yet expected */
-               hw_flags |= MI_FORCE_RESTORE;
 
        ret = mi_set_context(ring, to, hw_flags);
        if (ret) {
@@ -429,6 +446,18 @@ static int do_switch(struct i915_hw_context *to)
                return ret;
        }
 
+       for (i = 0; i < MAX_L3_SLICES; i++) {
+               if (!(to->remap_slice & (1<<i)))
+                       continue;
+
+               ret = i915_gem_l3_remap(ring, i);
+               /* If it failed, try again next round */
+               if (ret)
+                       DRM_DEBUG_DRIVER("L3 remapping failed\n");
+               else
+                       to->remap_slice &= ~(1<<i);
+       }
+
        /* The backing object for the context is done after switching to the
         * *next* context. Therefore we cannot retire the previous context until
         * the next context has already started running. In fact, the below code
@@ -436,11 +465,8 @@ static int do_switch(struct i915_hw_context *to)
         * MI_SET_CONTEXT instead of when the next seqno has completed.
         */
        if (from != NULL) {
-               struct drm_i915_private *dev_priv = from->obj->base.dev->dev_private;
-               struct i915_address_space *ggtt = &dev_priv->gtt.base;
                from->obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
-               list_move_tail(&i915_gem_obj_to_vma(from->obj, ggtt)->mm_list, &ggtt->active_list);
-               i915_gem_object_move_to_active(from->obj, ring);
+               i915_vma_move_to_active(i915_gem_obj_to_ggtt(from->obj), ring);
                /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
                 * whole damn pipeline, we don't need to explicitly mark the
                 * object dirty. The only exception is that the context must be
@@ -451,17 +477,7 @@ static int do_switch(struct i915_hw_context *to)
                from->obj->dirty = 1;
                BUG_ON(from->obj->ring != ring);
 
-               ret = i915_add_request(ring, NULL);
-               if (ret) {
-                       /* Too late, we've already scheduled a context switch.
-                        * Try to undo the change so that the hw state is
-                        * consistent with out tracking. In case of emergency,
-                        * scream.
-                        */
-                       WARN_ON(mi_set_context(ring, from, MI_RESTORE_INHIBIT));
-                       return ret;
-               }
-
+               /* obj is kept alive until the next request by its active ref */
                i915_gem_object_unpin(from->obj);
                i915_gem_context_unreference(from);
        }
index 91b7001..b737653 100644 (file)
@@ -37,6 +37,9 @@ mark_free(struct i915_vma *vma, struct list_head *unwind)
        if (vma->obj->pin_count)
                return false;
 
+       if (WARN_ON(!list_empty(&vma->exec_list)))
+               return false;
+
        list_add(&vma->exec_list, unwind);
        return drm_mm_scan_add_block(&vma->node);
 }
@@ -113,7 +116,7 @@ none:
        }
 
        /* We expect the caller to unpin, evict all and try again, or give up.
-        * So calling i915_gem_evict_everything() is unnecessary.
+        * So calling i915_gem_evict_vm() is unnecessary.
         */
        return -ENOSPC;
 
@@ -152,12 +155,48 @@ found:
        return ret;
 }
 
+/**
+ * i915_gem_evict_vm - Try to free up VM space
+ *
+ * @vm: Address space to evict from
+ * @do_idle: Boolean directing whether to idle first.
+ *
+ * VM eviction is about freeing up virtual address space. If one wants fine
+ * grained eviction, they should see evict something for more details. In terms
+ * of freeing up actual system memory, this function may not accomplish the
+ * desired result. An object may be shared in multiple address space, and this
+ * function will not assert those objects be freed.
+ *
+ * Using do_idle will result in a more complete eviction because it retires, and
+ * inactivates current BOs.
+ */
+int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle)
+{
+       struct i915_vma *vma, *next;
+       int ret;
+
+       trace_i915_gem_evict_vm(vm);
+
+       if (do_idle) {
+               ret = i915_gpu_idle(vm->dev);
+               if (ret)
+                       return ret;
+
+               i915_gem_retire_requests(vm->dev);
+       }
+
+       list_for_each_entry_safe(vma, next, &vm->inactive_list, mm_list)
+               if (vma->obj->pin_count == 0)
+                       WARN_ON(i915_vma_unbind(vma));
+
+       return 0;
+}
+
 int
 i915_gem_evict_everything(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct i915_address_space *vm;
-       struct i915_vma *vma, *next;
        bool lists_empty = true;
        int ret;
 
@@ -184,11 +223,8 @@ i915_gem_evict_everything(struct drm_device *dev)
        i915_gem_retire_requests(dev);
 
        /* Having flushed everything, unbind() should never raise an error */
-       list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
-               list_for_each_entry_safe(vma, next, &vm->inactive_list, mm_list)
-                       if (vma->obj->pin_count == 0)
-                               WARN_ON(i915_vma_unbind(vma));
-       }
+       list_for_each_entry(vm, &dev_priv->vm_list, global_link)
+               WARN_ON(i915_gem_evict_vm(vm, false));
 
        return 0;
 }
index bf34577..885d595 100644 (file)
 #include "intel_drv.h"
 #include <linux/dma_remapping.h>
 
-struct eb_objects {
-       struct list_head objects;
+struct eb_vmas {
+       struct list_head vmas;
        int and;
        union {
-               struct drm_i915_gem_object *lut[0];
+               struct i915_vma *lut[0];
                struct hlist_head buckets[0];
        };
 };
 
-static struct eb_objects *
-eb_create(struct drm_i915_gem_execbuffer2 *args)
+static struct eb_vmas *
+eb_create(struct drm_i915_gem_execbuffer2 *args, struct i915_address_space *vm)
 {
-       struct eb_objects *eb = NULL;
+       struct eb_vmas *eb = NULL;
 
        if (args->flags & I915_EXEC_HANDLE_LUT) {
-               int size = args->buffer_count;
-               size *= sizeof(struct drm_i915_gem_object *);
-               size += sizeof(struct eb_objects);
+               unsigned size = args->buffer_count;
+               size *= sizeof(struct i915_vma *);
+               size += sizeof(struct eb_vmas);
                eb = kmalloc(size, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
        }
 
        if (eb == NULL) {
-               int size = args->buffer_count;
-               int count = PAGE_SIZE / sizeof(struct hlist_head) / 2;
+               unsigned size = args->buffer_count;
+               unsigned count = PAGE_SIZE / sizeof(struct hlist_head) / 2;
                BUILD_BUG_ON_NOT_POWER_OF_2(PAGE_SIZE / sizeof(struct hlist_head));
                while (count > 2*size)
                        count >>= 1;
                eb = kzalloc(count*sizeof(struct hlist_head) +
-                            sizeof(struct eb_objects),
+                            sizeof(struct eb_vmas),
                             GFP_TEMPORARY);
                if (eb == NULL)
                        return eb;
@@ -70,64 +70,102 @@ eb_create(struct drm_i915_gem_execbuffer2 *args)
        } else
                eb->and = -args->buffer_count;
 
-       INIT_LIST_HEAD(&eb->objects);
+       INIT_LIST_HEAD(&eb->vmas);
        return eb;
 }
 
 static void
-eb_reset(struct eb_objects *eb)
+eb_reset(struct eb_vmas *eb)
 {
        if (eb->and >= 0)
                memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head));
 }
 
 static int
-eb_lookup_objects(struct eb_objects *eb,
-                 struct drm_i915_gem_exec_object2 *exec,
-                 const struct drm_i915_gem_execbuffer2 *args,
-                 struct drm_file *file)
+eb_lookup_vmas(struct eb_vmas *eb,
+              struct drm_i915_gem_exec_object2 *exec,
+              const struct drm_i915_gem_execbuffer2 *args,
+              struct i915_address_space *vm,
+              struct drm_file *file)
 {
-       int i;
+       struct drm_i915_gem_object *obj;
+       struct list_head objects;
+       int i, ret = 0;
 
+       INIT_LIST_HEAD(&objects);
        spin_lock(&file->table_lock);
+       /* Grab a reference to the object and release the lock so we can lookup
+        * or create the VMA without using GFP_ATOMIC */
        for (i = 0; i < args->buffer_count; i++) {
-               struct drm_i915_gem_object *obj;
-
                obj = to_intel_bo(idr_find(&file->object_idr, exec[i].handle));
                if (obj == NULL) {
                        spin_unlock(&file->table_lock);
                        DRM_DEBUG("Invalid object handle %d at index %d\n",
                                   exec[i].handle, i);
-                       return -ENOENT;
+                       ret = -ENOENT;
+                       goto out;
                }
 
-               if (!list_empty(&obj->exec_list)) {
+               if (!list_empty(&obj->obj_exec_link)) {
                        spin_unlock(&file->table_lock);
                        DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n",
                                   obj, exec[i].handle, i);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto out;
                }
 
                drm_gem_object_reference(&obj->base);
-               list_add_tail(&obj->exec_list, &eb->objects);
+               list_add_tail(&obj->obj_exec_link, &objects);
+       }
+       spin_unlock(&file->table_lock);
 
-               obj->exec_entry = &exec[i];
+       i = 0;
+       list_for_each_entry(obj, &objects, obj_exec_link) {
+               struct i915_vma *vma;
+
+               /*
+                * NOTE: We can leak any vmas created here when something fails
+                * later on. But that's no issue since vma_unbind can deal with
+                * vmas which are not actually bound. And since only
+                * lookup_or_create exists as an interface to get at the vma
+                * from the (obj, vm) we don't run the risk of creating
+                * duplicated vmas for the same vm.
+                */
+               vma = i915_gem_obj_lookup_or_create_vma(obj, vm);
+               if (IS_ERR(vma)) {
+                       DRM_DEBUG("Failed to lookup VMA\n");
+                       ret = PTR_ERR(vma);
+                       goto out;
+               }
+
+               list_add_tail(&vma->exec_list, &eb->vmas);
+
+               vma->exec_entry = &exec[i];
                if (eb->and < 0) {
-                       eb->lut[i] = obj;
+                       eb->lut[i] = vma;
                } else {
                        uint32_t handle = args->flags & I915_EXEC_HANDLE_LUT ? i : exec[i].handle;
-                       obj->exec_handle = handle;
-                       hlist_add_head(&obj->exec_node,
+                       vma->exec_handle = handle;
+                       hlist_add_head(&vma->exec_node,
                                       &eb->buckets[handle & eb->and]);
                }
+               ++i;
        }
-       spin_unlock(&file->table_lock);
 
-       return 0;
+
+out:
+       while (!list_empty(&objects)) {
+               obj = list_first_entry(&objects,
+                                      struct drm_i915_gem_object,
+                                      obj_exec_link);
+               list_del_init(&obj->obj_exec_link);
+               if (ret)
+                       drm_gem_object_unreference(&obj->base);
+       }
+       return ret;
 }
 
-static struct drm_i915_gem_object *
-eb_get_object(struct eb_objects *eb, unsigned long handle)
+static struct i915_vma *eb_get_vma(struct eb_vmas *eb, unsigned long handle)
 {
        if (eb->and < 0) {
                if (handle >= -eb->and)
@@ -139,34 +177,33 @@ eb_get_object(struct eb_objects *eb, unsigned long handle)
 
                head = &eb->buckets[handle & eb->and];
                hlist_for_each(node, head) {
-                       struct drm_i915_gem_object *obj;
+                       struct i915_vma *vma;
 
-                       obj = hlist_entry(node, struct drm_i915_gem_object, exec_node);
-                       if (obj->exec_handle == handle)
-                               return obj;
+                       vma = hlist_entry(node, struct i915_vma, exec_node);
+                       if (vma->exec_handle == handle)
+                               return vma;
                }
                return NULL;
        }
 }
 
-static void
-eb_destroy(struct eb_objects *eb)
-{
-       while (!list_empty(&eb->objects)) {
-               struct drm_i915_gem_object *obj;
+static void eb_destroy(struct eb_vmas *eb) {
+       while (!list_empty(&eb->vmas)) {
+               struct i915_vma *vma;
 
-               obj = list_first_entry(&eb->objects,
-                                      struct drm_i915_gem_object,
+               vma = list_first_entry(&eb->vmas,
+                                      struct i915_vma,
                                       exec_list);
-               list_del_init(&obj->exec_list);
-               drm_gem_object_unreference(&obj->base);
+               list_del_init(&vma->exec_list);
+               drm_gem_object_unreference(&vma->obj->base);
        }
        kfree(eb);
 }
 
 static inline int use_cpu_reloc(struct drm_i915_gem_object *obj)
 {
-       return (obj->base.write_domain == I915_GEM_DOMAIN_CPU ||
+       return (HAS_LLC(obj->base.dev) ||
+               obj->base.write_domain == I915_GEM_DOMAIN_CPU ||
                !obj->map_and_fenceable ||
                obj->cache_level != I915_CACHE_NONE);
 }
@@ -175,17 +212,31 @@ static int
 relocate_entry_cpu(struct drm_i915_gem_object *obj,
                   struct drm_i915_gem_relocation_entry *reloc)
 {
+       struct drm_device *dev = obj->base.dev;
        uint32_t page_offset = offset_in_page(reloc->offset);
        char *vaddr;
        int ret = -EINVAL;
 
-       ret = i915_gem_object_set_to_cpu_domain(obj, 1);
+       ret = i915_gem_object_set_to_cpu_domain(obj, true);
        if (ret)
                return ret;
 
        vaddr = kmap_atomic(i915_gem_object_get_page(obj,
                                reloc->offset >> PAGE_SHIFT));
        *(uint32_t *)(vaddr + page_offset) = reloc->delta;
+
+       if (INTEL_INFO(dev)->gen >= 8) {
+               page_offset = offset_in_page(page_offset + sizeof(uint32_t));
+
+               if (page_offset == 0) {
+                       kunmap_atomic(vaddr);
+                       vaddr = kmap_atomic(i915_gem_object_get_page(obj,
+                           (reloc->offset + sizeof(uint32_t)) >> PAGE_SHIFT));
+               }
+
+               *(uint32_t *)(vaddr + page_offset) = 0;
+       }
+
        kunmap_atomic(vaddr);
 
        return 0;
@@ -216,6 +267,21 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj,
        reloc_entry = (uint32_t __iomem *)
                (reloc_page + offset_in_page(reloc->offset));
        iowrite32(reloc->delta, reloc_entry);
+
+       if (INTEL_INFO(dev)->gen >= 8) {
+               reloc_entry += 1;
+
+               if (offset_in_page(reloc->offset + sizeof(uint32_t)) == 0) {
+                       io_mapping_unmap_atomic(reloc_page);
+                       reloc_page = io_mapping_map_atomic_wc(
+                                       dev_priv->gtt.mappable,
+                                       reloc->offset + sizeof(uint32_t));
+                       reloc_entry = reloc_page;
+               }
+
+               iowrite32(0, reloc_entry);
+       }
+
        io_mapping_unmap_atomic(reloc_page);
 
        return 0;
@@ -223,22 +289,24 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj,
 
 static int
 i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
-                                  struct eb_objects *eb,
+                                  struct eb_vmas *eb,
                                   struct drm_i915_gem_relocation_entry *reloc,
                                   struct i915_address_space *vm)
 {
        struct drm_device *dev = obj->base.dev;
        struct drm_gem_object *target_obj;
        struct drm_i915_gem_object *target_i915_obj;
+       struct i915_vma *target_vma;
        uint32_t target_offset;
        int ret = -EINVAL;
 
        /* we've already hold a reference to all valid objects */
-       target_obj = &eb_get_object(eb, reloc->target_handle)->base;
-       if (unlikely(target_obj == NULL))
+       target_vma = eb_get_vma(eb, reloc->target_handle);
+       if (unlikely(target_vma == NULL))
                return -ENOENT;
+       target_i915_obj = target_vma->obj;
+       target_obj = &target_vma->obj->base;
 
-       target_i915_obj = to_intel_bo(target_obj);
        target_offset = i915_gem_obj_ggtt_offset(target_i915_obj);
 
        /* Sandybridge PPGTT errata: We need a global gtt mapping for MI and
@@ -284,7 +352,8 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
                return 0;
 
        /* Check that the relocation address is valid... */
-       if (unlikely(reloc->offset > obj->base.size - 4)) {
+       if (unlikely(reloc->offset >
+               obj->base.size - (INTEL_INFO(dev)->gen >= 8 ? 8 : 4))) {
                DRM_DEBUG("Relocation beyond object bounds: "
                          "obj %p target %d offset %d size %d.\n",
                          obj, reloc->target_handle,
@@ -320,14 +389,13 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
 }
 
 static int
-i915_gem_execbuffer_relocate_object(struct drm_i915_gem_object *obj,
-                                   struct eb_objects *eb,
-                                   struct i915_address_space *vm)
+i915_gem_execbuffer_relocate_vma(struct i915_vma *vma,
+                                struct eb_vmas *eb)
 {
 #define N_RELOC(x) ((x) / sizeof(struct drm_i915_gem_relocation_entry))
        struct drm_i915_gem_relocation_entry stack_reloc[N_RELOC(512)];
        struct drm_i915_gem_relocation_entry __user *user_relocs;
-       struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
+       struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
        int remain, ret;
 
        user_relocs = to_user_ptr(entry->relocs_ptr);
@@ -346,8 +414,8 @@ i915_gem_execbuffer_relocate_object(struct drm_i915_gem_object *obj,
                do {
                        u64 offset = r->presumed_offset;
 
-                       ret = i915_gem_execbuffer_relocate_entry(obj, eb, r,
-                                                                vm);
+                       ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, r,
+                                                                vma->vm);
                        if (ret)
                                return ret;
 
@@ -368,17 +436,16 @@ i915_gem_execbuffer_relocate_object(struct drm_i915_gem_object *obj,
 }
 
 static int
-i915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj,
-                                        struct eb_objects *eb,
-                                        struct drm_i915_gem_relocation_entry *relocs,
-                                        struct i915_address_space *vm)
+i915_gem_execbuffer_relocate_vma_slow(struct i915_vma *vma,
+                                     struct eb_vmas *eb,
+                                     struct drm_i915_gem_relocation_entry *relocs)
 {
-       const struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
+       const struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
        int i, ret;
 
        for (i = 0; i < entry->relocation_count; i++) {
-               ret = i915_gem_execbuffer_relocate_entry(obj, eb, &relocs[i],
-                                                        vm);
+               ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, &relocs[i],
+                                                        vma->vm);
                if (ret)
                        return ret;
        }
@@ -387,10 +454,10 @@ i915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj,
 }
 
 static int
-i915_gem_execbuffer_relocate(struct eb_objects *eb,
+i915_gem_execbuffer_relocate(struct eb_vmas *eb,
                             struct i915_address_space *vm)
 {
-       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
        int ret = 0;
 
        /* This is the fast path and we cannot handle a pagefault whilst
@@ -401,8 +468,8 @@ i915_gem_execbuffer_relocate(struct eb_objects *eb,
         * lockdep complains vehemently.
         */
        pagefault_disable();
-       list_for_each_entry(obj, &eb->objects, exec_list) {
-               ret = i915_gem_execbuffer_relocate_object(obj, eb, vm);
+       list_for_each_entry(vma, &eb->vmas, exec_list) {
+               ret = i915_gem_execbuffer_relocate_vma(vma, eb);
                if (ret)
                        break;
        }
@@ -415,31 +482,32 @@ i915_gem_execbuffer_relocate(struct eb_objects *eb,
 #define  __EXEC_OBJECT_HAS_FENCE (1<<30)
 
 static int
-need_reloc_mappable(struct drm_i915_gem_object *obj)
+need_reloc_mappable(struct i915_vma *vma)
 {
-       struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
-       return entry->relocation_count && !use_cpu_reloc(obj);
+       struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
+       return entry->relocation_count && !use_cpu_reloc(vma->obj) &&
+               i915_is_ggtt(vma->vm);
 }
 
 static int
-i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj,
-                                  struct intel_ring_buffer *ring,
-                                  struct i915_address_space *vm,
-                                  bool *need_reloc)
+i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
+                               struct intel_ring_buffer *ring,
+                               bool *need_reloc)
 {
-       struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
-       struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
+       struct drm_i915_private *dev_priv = ring->dev->dev_private;
+       struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
        bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
        bool need_fence, need_mappable;
+       struct drm_i915_gem_object *obj = vma->obj;
        int ret;
 
        need_fence =
                has_fenced_gpu_access &&
                entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
                obj->tiling_mode != I915_TILING_NONE;
-       need_mappable = need_fence || need_reloc_mappable(obj);
+       need_mappable = need_fence || need_reloc_mappable(vma);
 
-       ret = i915_gem_object_pin(obj, vm, entry->alignment, need_mappable,
+       ret = i915_gem_object_pin(obj, vma->vm, entry->alignment, need_mappable,
                                  false);
        if (ret)
                return ret;
@@ -467,8 +535,8 @@ i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj,
                obj->has_aliasing_ppgtt_mapping = 1;
        }
 
-       if (entry->offset != i915_gem_obj_offset(obj, vm)) {
-               entry->offset = i915_gem_obj_offset(obj, vm);
+       if (entry->offset != vma->node.start) {
+               entry->offset = vma->node.start;
                *need_reloc = true;
        }
 
@@ -485,14 +553,15 @@ i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj,
 }
 
 static void
-i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj)
+i915_gem_execbuffer_unreserve_vma(struct i915_vma *vma)
 {
        struct drm_i915_gem_exec_object2 *entry;
+       struct drm_i915_gem_object *obj = vma->obj;
 
-       if (!i915_gem_obj_bound_any(obj))
+       if (!drm_mm_node_allocated(&vma->node))
                return;
 
-       entry = obj->exec_entry;
+       entry = vma->exec_entry;
 
        if (entry->flags & __EXEC_OBJECT_HAS_FENCE)
                i915_gem_object_unpin_fence(obj);
@@ -505,41 +574,46 @@ i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj)
 
 static int
 i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
-                           struct list_head *objects,
-                           struct i915_address_space *vm,
+                           struct list_head *vmas,
                            bool *need_relocs)
 {
        struct drm_i915_gem_object *obj;
-       struct list_head ordered_objects;
+       struct i915_vma *vma;
+       struct i915_address_space *vm;
+       struct list_head ordered_vmas;
        bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
        int retry;
 
-       INIT_LIST_HEAD(&ordered_objects);
-       while (!list_empty(objects)) {
+       if (list_empty(vmas))
+               return 0;
+
+       vm = list_first_entry(vmas, struct i915_vma, exec_list)->vm;
+
+       INIT_LIST_HEAD(&ordered_vmas);
+       while (!list_empty(vmas)) {
                struct drm_i915_gem_exec_object2 *entry;
                bool need_fence, need_mappable;
 
-               obj = list_first_entry(objects,
-                                      struct drm_i915_gem_object,
-                                      exec_list);
-               entry = obj->exec_entry;
+               vma = list_first_entry(vmas, struct i915_vma, exec_list);
+               obj = vma->obj;
+               entry = vma->exec_entry;
 
                need_fence =
                        has_fenced_gpu_access &&
                        entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
                        obj->tiling_mode != I915_TILING_NONE;
-               need_mappable = need_fence || need_reloc_mappable(obj);
+               need_mappable = need_fence || need_reloc_mappable(vma);
 
                if (need_mappable)
-                       list_move(&obj->exec_list, &ordered_objects);
+                       list_move(&vma->exec_list, &ordered_vmas);
                else
-                       list_move_tail(&obj->exec_list, &ordered_objects);
+                       list_move_tail(&vma->exec_list, &ordered_vmas);
 
                obj->base.pending_read_domains = I915_GEM_GPU_DOMAINS & ~I915_GEM_DOMAIN_COMMAND;
                obj->base.pending_write_domain = 0;
                obj->pending_fenced_gpu_access = false;
        }
-       list_splice(&ordered_objects, objects);
+       list_splice(&ordered_vmas, vmas);
 
        /* Attempt to pin all of the buffers into the GTT.
         * This is done in 3 phases:
@@ -558,52 +632,52 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
                int ret = 0;
 
                /* Unbind any ill-fitting objects or pin. */
-               list_for_each_entry(obj, objects, exec_list) {
-                       struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
+               list_for_each_entry(vma, vmas, exec_list) {
+                       struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
                        bool need_fence, need_mappable;
-                       u32 obj_offset;
 
-                       if (!i915_gem_obj_bound(obj, vm))
+                       obj = vma->obj;
+
+                       if (!drm_mm_node_allocated(&vma->node))
                                continue;
 
-                       obj_offset = i915_gem_obj_offset(obj, vm);
                        need_fence =
                                has_fenced_gpu_access &&
                                entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
                                obj->tiling_mode != I915_TILING_NONE;
-                       need_mappable = need_fence || need_reloc_mappable(obj);
+                       need_mappable = need_fence || need_reloc_mappable(vma);
 
                        WARN_ON((need_mappable || need_fence) &&
-                               !i915_is_ggtt(vm));
+                              !i915_is_ggtt(vma->vm));
 
                        if ((entry->alignment &&
-                            obj_offset & (entry->alignment - 1)) ||
+                            vma->node.start & (entry->alignment - 1)) ||
                            (need_mappable && !obj->map_and_fenceable))
-                               ret = i915_vma_unbind(i915_gem_obj_to_vma(obj, vm));
+                               ret = i915_vma_unbind(vma);
                        else
-                               ret = i915_gem_execbuffer_reserve_object(obj, ring, vm, need_relocs);
+                               ret = i915_gem_execbuffer_reserve_vma(vma, ring, need_relocs);
                        if (ret)
                                goto err;
                }
 
                /* Bind fresh objects */
-               list_for_each_entry(obj, objects, exec_list) {
-                       if (i915_gem_obj_bound(obj, vm))
+               list_for_each_entry(vma, vmas, exec_list) {
+                       if (drm_mm_node_allocated(&vma->node))
                                continue;
 
-                       ret = i915_gem_execbuffer_reserve_object(obj, ring, vm, need_relocs);
+                       ret = i915_gem_execbuffer_reserve_vma(vma, ring, need_relocs);
                        if (ret)
                                goto err;
                }
 
 err:           /* Decrement pin count for bound objects */
-               list_for_each_entry(obj, objects, exec_list)
-                       i915_gem_execbuffer_unreserve_object(obj);
+               list_for_each_entry(vma, vmas, exec_list)
+                       i915_gem_execbuffer_unreserve_vma(vma);
 
                if (ret != -ENOSPC || retry++)
                        return ret;
 
-               ret = i915_gem_evict_everything(ring->dev);
+               ret = i915_gem_evict_vm(vm, true);
                if (ret)
                        return ret;
        } while (1);
@@ -614,24 +688,27 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev,
                                  struct drm_i915_gem_execbuffer2 *args,
                                  struct drm_file *file,
                                  struct intel_ring_buffer *ring,
-                                 struct eb_objects *eb,
-                                 struct drm_i915_gem_exec_object2 *exec,
-                                 struct i915_address_space *vm)
+                                 struct eb_vmas *eb,
+                                 struct drm_i915_gem_exec_object2 *exec)
 {
        struct drm_i915_gem_relocation_entry *reloc;
-       struct drm_i915_gem_object *obj;
+       struct i915_address_space *vm;
+       struct i915_vma *vma;
        bool need_relocs;
        int *reloc_offset;
        int i, total, ret;
-       int count = args->buffer_count;
+       unsigned count = args->buffer_count;
+
+       if (WARN_ON(list_empty(&eb->vmas)))
+               return 0;
+
+       vm = list_first_entry(&eb->vmas, struct i915_vma, exec_list)->vm;
 
        /* We may process another execbuffer during the unlock... */
-       while (!list_empty(&eb->objects)) {
-               obj = list_first_entry(&eb->objects,
-                                      struct drm_i915_gem_object,
-                                      exec_list);
-               list_del_init(&obj->exec_list);
-               drm_gem_object_unreference(&obj->base);
+       while (!list_empty(&eb->vmas)) {
+               vma = list_first_entry(&eb->vmas, struct i915_vma, exec_list);
+               list_del_init(&vma->exec_list);
+               drm_gem_object_unreference(&vma->obj->base);
        }
 
        mutex_unlock(&dev->struct_mutex);
@@ -695,20 +772,19 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev,
 
        /* reacquire the objects */
        eb_reset(eb);
-       ret = eb_lookup_objects(eb, exec, args, file);
+       ret = eb_lookup_vmas(eb, exec, args, vm, file);
        if (ret)
                goto err;
 
        need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0;
-       ret = i915_gem_execbuffer_reserve(ring, &eb->objects, vm, &need_relocs);
+       ret = i915_gem_execbuffer_reserve(ring, &eb->vmas, &need_relocs);
        if (ret)
                goto err;
 
-       list_for_each_entry(obj, &eb->objects, exec_list) {
-               int offset = obj->exec_entry - exec;
-               ret = i915_gem_execbuffer_relocate_object_slow(obj, eb,
-                                                              reloc + reloc_offset[offset],
-                                                              vm);
+       list_for_each_entry(vma, &eb->vmas, exec_list) {
+               int offset = vma->exec_entry - exec;
+               ret = i915_gem_execbuffer_relocate_vma_slow(vma, eb,
+                                                           reloc + reloc_offset[offset]);
                if (ret)
                        goto err;
        }
@@ -727,14 +803,15 @@ err:
 
 static int
 i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring,
-                               struct list_head *objects)
+                               struct list_head *vmas)
 {
-       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
        uint32_t flush_domains = 0;
        bool flush_chipset = false;
        int ret;
 
-       list_for_each_entry(obj, objects, exec_list) {
+       list_for_each_entry(vma, vmas, exec_list) {
+               struct drm_i915_gem_object *obj = vma->obj;
                ret = i915_gem_object_sync(obj, ring);
                if (ret)
                        return ret;
@@ -771,8 +848,8 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
                   int count)
 {
        int i;
-       int relocs_total = 0;
-       int relocs_max = INT_MAX / sizeof(struct drm_i915_gem_relocation_entry);
+       unsigned relocs_total = 0;
+       unsigned relocs_max = UINT_MAX / sizeof(struct drm_i915_gem_relocation_entry);
 
        for (i = 0; i < count; i++) {
                char __user *ptr = to_user_ptr(exec[i].relocs_ptr);
@@ -809,13 +886,13 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
 }
 
 static void
-i915_gem_execbuffer_move_to_active(struct list_head *objects,
-                                  struct i915_address_space *vm,
+i915_gem_execbuffer_move_to_active(struct list_head *vmas,
                                   struct intel_ring_buffer *ring)
 {
-       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
 
-       list_for_each_entry(obj, objects, exec_list) {
+       list_for_each_entry(vma, vmas, exec_list) {
+               struct drm_i915_gem_object *obj = vma->obj;
                u32 old_read = obj->base.read_domains;
                u32 old_write = obj->base.write_domain;
 
@@ -825,9 +902,7 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects,
                obj->base.read_domains = obj->base.pending_read_domains;
                obj->fenced_gpu_access = obj->pending_fenced_gpu_access;
 
-               /* FIXME: This lookup gets fixed later <-- danvet */
-               list_move_tail(&i915_gem_obj_to_vma(obj, vm)->mm_list, &vm->active_list);
-               i915_gem_object_move_to_active(obj, ring);
+               i915_vma_move_to_active(vma, ring);
                if (obj->base.write_domain) {
                        obj->dirty = 1;
                        obj->last_write_seqno = intel_ring_get_seqno(ring);
@@ -885,10 +960,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                       struct i915_address_space *vm)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       struct eb_objects *eb;
+       struct eb_vmas *eb;
        struct drm_i915_gem_object *batch_obj;
        struct drm_clip_rect *cliprects = NULL;
        struct intel_ring_buffer *ring;
+       struct i915_ctx_hang_stats *hs;
        u32 ctx_id = i915_execbuffer2_get_context_id(*args);
        u32 exec_start, exec_len;
        u32 mask, flags;
@@ -1000,7 +1076,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                        return -EINVAL;
                }
 
-               cliprects = kmalloc(args->num_cliprects * sizeof(*cliprects),
+               cliprects = kcalloc(args->num_cliprects,
+                                   sizeof(*cliprects),
                                    GFP_KERNEL);
                if (cliprects == NULL) {
                        ret = -ENOMEM;
@@ -1025,7 +1102,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                goto pre_mutex_err;
        }
 
-       eb = eb_create(args);
+       eb = eb_create(args, vm);
        if (eb == NULL) {
                mutex_unlock(&dev->struct_mutex);
                ret = -ENOMEM;
@@ -1033,18 +1110,16 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        }
 
        /* Look up object handles */
-       ret = eb_lookup_objects(eb, exec, args, file);
+       ret = eb_lookup_vmas(eb, exec, args, vm, file);
        if (ret)
                goto err;
 
        /* take note of the batch buffer before we might reorder the lists */
-       batch_obj = list_entry(eb->objects.prev,
-                              struct drm_i915_gem_object,
-                              exec_list);
+       batch_obj = list_entry(eb->vmas.prev, struct i915_vma, exec_list)->obj;
 
        /* Move the objects en-masse into the GTT, evicting if necessary. */
        need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0;
-       ret = i915_gem_execbuffer_reserve(ring, &eb->objects, vm, &need_relocs);
+       ret = i915_gem_execbuffer_reserve(ring, &eb->vmas, &need_relocs);
        if (ret)
                goto err;
 
@@ -1054,7 +1129,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        if (ret) {
                if (ret == -EFAULT) {
                        ret = i915_gem_execbuffer_relocate_slow(dev, args, file, ring,
-                                                               eb, exec, vm);
+                                                               eb, exec);
                        BUG_ON(!mutex_is_locked(&dev->struct_mutex));
                }
                if (ret)
@@ -1071,15 +1146,25 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
 
        /* snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
         * batch" bit. Hence we need to pin secure batches into the global gtt.
-        * hsw should have this fixed, but let's be paranoid and do it
-        * unconditionally for now. */
+        * hsw should have this fixed, but bdw mucks it up again. */
        if (flags & I915_DISPATCH_SECURE && !batch_obj->has_global_gtt_mapping)
                i915_gem_gtt_bind_object(batch_obj, batch_obj->cache_level);
 
-       ret = i915_gem_execbuffer_move_to_gpu(ring, &eb->objects);
+       ret = i915_gem_execbuffer_move_to_gpu(ring, &eb->vmas);
        if (ret)
                goto err;
 
+       hs = i915_gem_context_get_hang_stats(dev, file, ctx_id);
+       if (IS_ERR(hs)) {
+               ret = PTR_ERR(hs);
+               goto err;
+       }
+
+       if (hs->banned) {
+               ret = -EIO;
+               goto err;
+       }
+
        ret = i915_switch_context(ring, file, ctx_id);
        if (ret)
                goto err;
@@ -1131,7 +1216,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
 
        trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags);
 
-       i915_gem_execbuffer_move_to_active(&eb->objects, vm, ring);
+       i915_gem_execbuffer_move_to_active(&eb->vmas, ring);
        i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj);
 
 err:
index 1f7b4ca..3620a1b 100644 (file)
@@ -30,6 +30,8 @@
 
 #define GEN6_PPGTT_PD_ENTRIES 512
 #define I915_PPGTT_PT_ENTRIES (PAGE_SIZE / sizeof(gen6_gtt_pte_t))
+typedef uint64_t gen8_gtt_pte_t;
+typedef gen8_gtt_pte_t gen8_ppgtt_pde_t;
 
 /* PPGTT stuff */
 #define GEN6_GTT_ADDR_ENCODE(addr)     ((addr) | (((addr) >> 28) & 0xff0))
 #define HSW_WB_ELLC_LLC_AGE0           HSW_CACHEABILITY_CONTROL(0xb)
 #define HSW_WT_ELLC_LLC_AGE0           HSW_CACHEABILITY_CONTROL(0x6)
 
+#define GEN8_PTES_PER_PAGE             (PAGE_SIZE / sizeof(gen8_gtt_pte_t))
+#define GEN8_PDES_PER_PAGE             (PAGE_SIZE / sizeof(gen8_ppgtt_pde_t))
+#define GEN8_LEGACY_PDPS               4
+
+#define PPAT_UNCACHED_INDEX            (_PAGE_PWT | _PAGE_PCD)
+#define PPAT_CACHED_PDE_INDEX          0 /* WB LLC */
+#define PPAT_CACHED_INDEX              _PAGE_PAT /* WB LLCeLLC */
+#define PPAT_DISPLAY_ELLC_INDEX                _PAGE_PCD /* WT eLLC */
+
+static inline gen8_gtt_pte_t gen8_pte_encode(dma_addr_t addr,
+                                            enum i915_cache_level level,
+                                            bool valid)
+{
+       gen8_gtt_pte_t pte = valid ? _PAGE_PRESENT | _PAGE_RW : 0;
+       pte |= addr;
+       if (level != I915_CACHE_NONE)
+               pte |= PPAT_CACHED_INDEX;
+       else
+               pte |= PPAT_UNCACHED_INDEX;
+       return pte;
+}
+
+static inline gen8_ppgtt_pde_t gen8_pde_encode(struct drm_device *dev,
+                                            dma_addr_t addr,
+                                            enum i915_cache_level level)
+{
+       gen8_ppgtt_pde_t pde = _PAGE_PRESENT | _PAGE_RW;
+       pde |= addr;
+       if (level != I915_CACHE_NONE)
+               pde |= PPAT_CACHED_PDE_INDEX;
+       else
+               pde |= PPAT_UNCACHED_INDEX;
+       return pde;
+}
+
 static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr,
                                     enum i915_cache_level level,
                                     bool valid)
@@ -158,6 +195,257 @@ static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
        return pte;
 }
 
+/* Broadwell Page Directory Pointer Descriptors */
+static int gen8_write_pdp(struct intel_ring_buffer *ring, unsigned entry,
+                          uint64_t val)
+{
+       int ret;
+
+       BUG_ON(entry >= 4);
+
+       ret = intel_ring_begin(ring, 6);
+       if (ret)
+               return ret;
+
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+       intel_ring_emit(ring, GEN8_RING_PDP_UDW(ring, entry));
+       intel_ring_emit(ring, (u32)(val >> 32));
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+       intel_ring_emit(ring, GEN8_RING_PDP_LDW(ring, entry));
+       intel_ring_emit(ring, (u32)(val));
+       intel_ring_advance(ring);
+
+       return 0;
+}
+
+static int gen8_ppgtt_enable(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring;
+       struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
+       int i, j, ret;
+
+       /* bit of a hack to find the actual last used pd */
+       int used_pd = ppgtt->num_pd_entries / GEN8_PDES_PER_PAGE;
+
+       for_each_ring(ring, dev_priv, j) {
+               I915_WRITE(RING_MODE_GEN7(ring),
+                          _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
+       }
+
+       for (i = used_pd - 1; i >= 0; i--) {
+               dma_addr_t addr = ppgtt->pd_dma_addr[i];
+               for_each_ring(ring, dev_priv, j) {
+                       ret = gen8_write_pdp(ring, i, addr);
+                       if (ret)
+                               return ret;
+               }
+       }
+       return 0;
+}
+
+static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
+                                  unsigned first_entry,
+                                  unsigned num_entries,
+                                  bool use_scratch)
+{
+       struct i915_hw_ppgtt *ppgtt =
+               container_of(vm, struct i915_hw_ppgtt, base);
+       gen8_gtt_pte_t *pt_vaddr, scratch_pte;
+       unsigned act_pt = first_entry / GEN8_PTES_PER_PAGE;
+       unsigned first_pte = first_entry % GEN8_PTES_PER_PAGE;
+       unsigned last_pte, i;
+
+       scratch_pte = gen8_pte_encode(ppgtt->base.scratch.addr,
+                                     I915_CACHE_LLC, use_scratch);
+
+       while (num_entries) {
+               struct page *page_table = &ppgtt->gen8_pt_pages[act_pt];
+
+               last_pte = first_pte + num_entries;
+               if (last_pte > GEN8_PTES_PER_PAGE)
+                       last_pte = GEN8_PTES_PER_PAGE;
+
+               pt_vaddr = kmap_atomic(page_table);
+
+               for (i = first_pte; i < last_pte; i++)
+                       pt_vaddr[i] = scratch_pte;
+
+               kunmap_atomic(pt_vaddr);
+
+               num_entries -= last_pte - first_pte;
+               first_pte = 0;
+               act_pt++;
+       }
+}
+
+static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
+                                     struct sg_table *pages,
+                                     unsigned first_entry,
+                                     enum i915_cache_level cache_level)
+{
+       struct i915_hw_ppgtt *ppgtt =
+               container_of(vm, struct i915_hw_ppgtt, base);
+       gen8_gtt_pte_t *pt_vaddr;
+       unsigned act_pt = first_entry / GEN8_PTES_PER_PAGE;
+       unsigned act_pte = first_entry % GEN8_PTES_PER_PAGE;
+       struct sg_page_iter sg_iter;
+
+       pt_vaddr = kmap_atomic(&ppgtt->gen8_pt_pages[act_pt]);
+       for_each_sg_page(pages->sgl, &sg_iter, pages->nents, 0) {
+               dma_addr_t page_addr;
+
+               page_addr = sg_dma_address(sg_iter.sg) +
+                               (sg_iter.sg_pgoffset << PAGE_SHIFT);
+               pt_vaddr[act_pte] = gen8_pte_encode(page_addr, cache_level,
+                                                   true);
+               if (++act_pte == GEN8_PTES_PER_PAGE) {
+                       kunmap_atomic(pt_vaddr);
+                       act_pt++;
+                       pt_vaddr = kmap_atomic(&ppgtt->gen8_pt_pages[act_pt]);
+                       act_pte = 0;
+
+               }
+       }
+       kunmap_atomic(pt_vaddr);
+}
+
+static void gen8_ppgtt_cleanup(struct i915_address_space *vm)
+{
+       struct i915_hw_ppgtt *ppgtt =
+               container_of(vm, struct i915_hw_ppgtt, base);
+       int i, j;
+
+       for (i = 0; i < ppgtt->num_pd_pages ; i++) {
+               if (ppgtt->pd_dma_addr[i]) {
+                       pci_unmap_page(ppgtt->base.dev->pdev,
+                                      ppgtt->pd_dma_addr[i],
+                                      PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+
+                       for (j = 0; j < GEN8_PDES_PER_PAGE; j++) {
+                               dma_addr_t addr = ppgtt->gen8_pt_dma_addr[i][j];
+                               if (addr)
+                                       pci_unmap_page(ppgtt->base.dev->pdev,
+                                                      addr,
+                                                      PAGE_SIZE,
+                                                      PCI_DMA_BIDIRECTIONAL);
+
+                       }
+               }
+               kfree(ppgtt->gen8_pt_dma_addr[i]);
+       }
+
+       __free_pages(ppgtt->gen8_pt_pages, ppgtt->num_pt_pages << PAGE_SHIFT);
+       __free_pages(ppgtt->pd_pages, ppgtt->num_pd_pages << PAGE_SHIFT);
+}
+
+/**
+ * GEN8 legacy ppgtt programming is accomplished through 4 PDP registers with a
+ * net effect resembling a 2-level page table in normal x86 terms. Each PDP
+ * represents 1GB of memory
+ * 4 * 512 * 512 * 4096 = 4GB legacy 32b address space.
+ *
+ * TODO: Do something with the size parameter
+ **/
+static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
+{
+       struct page *pt_pages;
+       int i, j, ret = -ENOMEM;
+       const int max_pdp = DIV_ROUND_UP(size, 1 << 30);
+       const int num_pt_pages = GEN8_PDES_PER_PAGE * max_pdp;
+
+       if (size % (1<<30))
+               DRM_INFO("Pages will be wasted unless GTT size (%llu) is divisible by 1GB\n", size);
+
+       /* FIXME: split allocation into smaller pieces. For now we only ever do
+        * this once, but with full PPGTT, the multiple contiguous allocations
+        * will be bad.
+        */
+       ppgtt->pd_pages = alloc_pages(GFP_KERNEL, get_order(max_pdp << PAGE_SHIFT));
+       if (!ppgtt->pd_pages)
+               return -ENOMEM;
+
+       pt_pages = alloc_pages(GFP_KERNEL, get_order(num_pt_pages << PAGE_SHIFT));
+       if (!pt_pages) {
+               __free_pages(ppgtt->pd_pages, get_order(max_pdp << PAGE_SHIFT));
+               return -ENOMEM;
+       }
+
+       ppgtt->gen8_pt_pages = pt_pages;
+       ppgtt->num_pd_pages = 1 << get_order(max_pdp << PAGE_SHIFT);
+       ppgtt->num_pt_pages = 1 << get_order(num_pt_pages << PAGE_SHIFT);
+       ppgtt->num_pd_entries = max_pdp * GEN8_PDES_PER_PAGE;
+       ppgtt->enable = gen8_ppgtt_enable;
+       ppgtt->base.clear_range = gen8_ppgtt_clear_range;
+       ppgtt->base.insert_entries = gen8_ppgtt_insert_entries;
+       ppgtt->base.cleanup = gen8_ppgtt_cleanup;
+
+       BUG_ON(ppgtt->num_pd_pages > GEN8_LEGACY_PDPS);
+
+       /*
+        * - Create a mapping for the page directories.
+        * - For each page directory:
+        *      allocate space for page table mappings.
+        *      map each page table
+        */
+       for (i = 0; i < max_pdp; i++) {
+               dma_addr_t temp;
+               temp = pci_map_page(ppgtt->base.dev->pdev,
+                                   &ppgtt->pd_pages[i], 0,
+                                   PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+               if (pci_dma_mapping_error(ppgtt->base.dev->pdev, temp))
+                       goto err_out;
+
+               ppgtt->pd_dma_addr[i] = temp;
+
+               ppgtt->gen8_pt_dma_addr[i] = kmalloc(sizeof(dma_addr_t) * GEN8_PDES_PER_PAGE, GFP_KERNEL);
+               if (!ppgtt->gen8_pt_dma_addr[i])
+                       goto err_out;
+
+               for (j = 0; j < GEN8_PDES_PER_PAGE; j++) {
+                       struct page *p = &pt_pages[i * GEN8_PDES_PER_PAGE + j];
+                       temp = pci_map_page(ppgtt->base.dev->pdev,
+                                           p, 0, PAGE_SIZE,
+                                           PCI_DMA_BIDIRECTIONAL);
+
+                       if (pci_dma_mapping_error(ppgtt->base.dev->pdev, temp))
+                               goto err_out;
+
+                       ppgtt->gen8_pt_dma_addr[i][j] = temp;
+               }
+       }
+
+       /* For now, the PPGTT helper functions all require that the PDEs are
+        * plugged in correctly. So we do that now/here. For aliasing PPGTT, we
+        * will never need to touch the PDEs again */
+       for (i = 0; i < max_pdp; i++) {
+               gen8_ppgtt_pde_t *pd_vaddr;
+               pd_vaddr = kmap_atomic(&ppgtt->pd_pages[i]);
+               for (j = 0; j < GEN8_PDES_PER_PAGE; j++) {
+                       dma_addr_t addr = ppgtt->gen8_pt_dma_addr[i][j];
+                       pd_vaddr[j] = gen8_pde_encode(ppgtt->base.dev, addr,
+                                                     I915_CACHE_LLC);
+               }
+               kunmap_atomic(pd_vaddr);
+       }
+
+       ppgtt->base.clear_range(&ppgtt->base, 0,
+                               ppgtt->num_pd_entries * GEN8_PTES_PER_PAGE,
+                               true);
+
+       DRM_DEBUG_DRIVER("Allocated %d pages for page directories (%d wasted)\n",
+                        ppgtt->num_pd_pages, ppgtt->num_pd_pages - max_pdp);
+       DRM_DEBUG_DRIVER("Allocated %d pages for page tables (%lld wasted)\n",
+                        ppgtt->num_pt_pages,
+                        (ppgtt->num_pt_pages - num_pt_pages) +
+                        size % (1<<30));
+       return 0;
+
+err_out:
+       ppgtt->base.cleanup(&ppgtt->base);
+       return ret;
+}
+
 static void gen6_write_pdes(struct i915_hw_ppgtt *ppgtt)
 {
        struct drm_i915_private *dev_priv = ppgtt->base.dev->dev_private;
@@ -342,7 +630,7 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
        ppgtt->base.insert_entries = gen6_ppgtt_insert_entries;
        ppgtt->base.cleanup = gen6_ppgtt_cleanup;
        ppgtt->base.scratch = dev_priv->gtt.base.scratch;
-       ppgtt->pt_pages = kzalloc(sizeof(struct page *)*ppgtt->num_pd_entries,
+       ppgtt->pt_pages = kcalloc(ppgtt->num_pd_entries, sizeof(struct page *),
                                  GFP_KERNEL);
        if (!ppgtt->pt_pages)
                return -ENOMEM;
@@ -353,7 +641,7 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
                        goto err_pt_alloc;
        }
 
-       ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t) *ppgtt->num_pd_entries,
+       ppgtt->pt_dma_addr = kcalloc(ppgtt->num_pd_entries, sizeof(dma_addr_t),
                                     GFP_KERNEL);
        if (!ppgtt->pt_dma_addr)
                goto err_pt_alloc;
@@ -410,6 +698,8 @@ static int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
 
        if (INTEL_INFO(dev)->gen < 8)
                ret = gen6_ppgtt_init(ppgtt);
+       else if (IS_GEN8(dev))
+               ret = gen8_ppgtt_init(ppgtt, dev_priv->gtt.base.total);
        else
                BUG();
 
@@ -573,6 +863,57 @@ int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
        return 0;
 }
 
+static inline void gen8_set_pte(void __iomem *addr, gen8_gtt_pte_t pte)
+{
+#ifdef writeq
+       writeq(pte, addr);
+#else
+       iowrite32((u32)pte, addr);
+       iowrite32(pte >> 32, addr + 4);
+#endif
+}
+
+static void gen8_ggtt_insert_entries(struct i915_address_space *vm,
+                                    struct sg_table *st,
+                                    unsigned int first_entry,
+                                    enum i915_cache_level level)
+{
+       struct drm_i915_private *dev_priv = vm->dev->dev_private;
+       gen8_gtt_pte_t __iomem *gtt_entries =
+               (gen8_gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
+       int i = 0;
+       struct sg_page_iter sg_iter;
+       dma_addr_t addr;
+
+       for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) {
+               addr = sg_dma_address(sg_iter.sg) +
+                       (sg_iter.sg_pgoffset << PAGE_SHIFT);
+               gen8_set_pte(&gtt_entries[i],
+                            gen8_pte_encode(addr, level, true));
+               i++;
+       }
+
+       /*
+        * XXX: This serves as a posting read to make sure that the PTE has
+        * actually been updated. There is some concern that even though
+        * registers and PTEs are within the same BAR that they are potentially
+        * of NUMA access patterns. Therefore, even with the way we assume
+        * hardware should work, we must keep this posting read for paranoia.
+        */
+       if (i != 0)
+               WARN_ON(readq(&gtt_entries[i-1])
+                       != gen8_pte_encode(addr, level, true));
+
+#if 0 /* TODO: Still needed on GEN8? */
+       /* This next bit makes the above posting read even more important. We
+        * want to flush the TLBs only after we're certain all the PTE updates
+        * have finished.
+        */
+       I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN);
+       POSTING_READ(GFX_FLSH_CNTL_GEN6);
+#endif
+}
+
 /*
  * Binds an object into the global gtt with the specified cache level. The object
  * will be accessible to the GPU via commands whose operands reference offsets
@@ -615,6 +956,30 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
        POSTING_READ(GFX_FLSH_CNTL_GEN6);
 }
 
+static void gen8_ggtt_clear_range(struct i915_address_space *vm,
+                                 unsigned int first_entry,
+                                 unsigned int num_entries,
+                                 bool use_scratch)
+{
+       struct drm_i915_private *dev_priv = vm->dev->dev_private;
+       gen8_gtt_pte_t scratch_pte, __iomem *gtt_base =
+               (gen8_gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry;
+       const int max_entries = gtt_total_entries(dev_priv->gtt) - first_entry;
+       int i;
+
+       if (WARN(num_entries > max_entries,
+                "First entry = %d; Num entries = %d (max=%d)\n",
+                first_entry, num_entries, max_entries))
+               num_entries = max_entries;
+
+       scratch_pte = gen8_pte_encode(vm->scratch.addr,
+                                     I915_CACHE_LLC,
+                                     use_scratch);
+       for (i = 0; i < num_entries; i++)
+               gen8_set_pte(&gtt_base[i], scratch_pte);
+       readl(gtt_base);
+}
+
 static void gen6_ggtt_clear_range(struct i915_address_space *vm,
                                  unsigned int first_entry,
                                  unsigned int num_entries,
@@ -638,7 +1003,6 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm,
        readl(gtt_base);
 }
 
-
 static void i915_ggtt_insert_entries(struct i915_address_space *vm,
                                     struct sg_table *st,
                                     unsigned int pg_start,
@@ -720,6 +1084,7 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node,
                        *end -= 4096;
        }
 }
+
 void i915_gem_setup_global_gtt(struct drm_device *dev,
                               unsigned long start,
                               unsigned long mappable_end,
@@ -817,7 +1182,8 @@ void i915_gem_init_global_gtt(struct drm_device *dev)
 
                DRM_ERROR("Aliased PPGTT setup failed %d\n", ret);
                drm_mm_takedown(&dev_priv->gtt.base.mm);
-               gtt_size += GEN6_PPGTT_PD_ENTRIES * PAGE_SIZE;
+               if (INTEL_INFO(dev)->gen < 8)
+                       gtt_size += GEN6_PPGTT_PD_ENTRIES*PAGE_SIZE;
        }
        i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size);
 }
@@ -867,6 +1233,15 @@ static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl)
        return snb_gmch_ctl << 20;
 }
 
+static inline unsigned int gen8_get_total_gtt_size(u16 bdw_gmch_ctl)
+{
+       bdw_gmch_ctl >>= BDW_GMCH_GGMS_SHIFT;
+       bdw_gmch_ctl &= BDW_GMCH_GGMS_MASK;
+       if (bdw_gmch_ctl)
+               bdw_gmch_ctl = 1 << bdw_gmch_ctl;
+       return bdw_gmch_ctl << 20;
+}
+
 static inline size_t gen6_get_stolen_size(u16 snb_gmch_ctl)
 {
        snb_gmch_ctl >>= SNB_GMCH_GMS_SHIFT;
@@ -874,6 +1249,108 @@ static inline size_t gen6_get_stolen_size(u16 snb_gmch_ctl)
        return snb_gmch_ctl << 25; /* 32 MB units */
 }
 
+static inline size_t gen8_get_stolen_size(u16 bdw_gmch_ctl)
+{
+       bdw_gmch_ctl >>= BDW_GMCH_GMS_SHIFT;
+       bdw_gmch_ctl &= BDW_GMCH_GMS_MASK;
+       return bdw_gmch_ctl << 25; /* 32 MB units */
+}
+
+static int ggtt_probe_common(struct drm_device *dev,
+                            size_t gtt_size)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       phys_addr_t gtt_bus_addr;
+       int ret;
+
+       /* For Modern GENs the PTEs and register space are split in the BAR */
+       gtt_bus_addr = pci_resource_start(dev->pdev, 0) +
+               (pci_resource_len(dev->pdev, 0) / 2);
+
+       dev_priv->gtt.gsm = ioremap_wc(gtt_bus_addr, gtt_size);
+       if (!dev_priv->gtt.gsm) {
+               DRM_ERROR("Failed to map the gtt page table\n");
+               return -ENOMEM;
+       }
+
+       ret = setup_scratch_page(dev);
+       if (ret) {
+               DRM_ERROR("Scratch setup failed\n");
+               /* iounmap will also get called at remove, but meh */
+               iounmap(dev_priv->gtt.gsm);
+       }
+
+       return ret;
+}
+
+/* The GGTT and PPGTT need a private PPAT setup in order to handle cacheability
+ * bits. When using advanced contexts each context stores its own PAT, but
+ * writing this data shouldn't be harmful even in those cases. */
+static void gen8_setup_private_ppat(struct drm_i915_private *dev_priv)
+{
+#define GEN8_PPAT_UC           (0<<0)
+#define GEN8_PPAT_WC           (1<<0)
+#define GEN8_PPAT_WT           (2<<0)
+#define GEN8_PPAT_WB           (3<<0)
+#define GEN8_PPAT_ELLC_OVERRIDE        (0<<2)
+/* FIXME(BDW): Bspec is completely confused about cache control bits. */
+#define GEN8_PPAT_LLC          (1<<2)
+#define GEN8_PPAT_LLCELLC      (2<<2)
+#define GEN8_PPAT_LLCeLLC      (3<<2)
+#define GEN8_PPAT_AGE(x)       (x<<4)
+#define GEN8_PPAT(i, x) ((uint64_t) (x) << ((i) * 8))
+       uint64_t pat;
+
+       pat = GEN8_PPAT(0, GEN8_PPAT_WB | GEN8_PPAT_LLC)     | /* for normal objects, no eLLC */
+             GEN8_PPAT(1, GEN8_PPAT_WC | GEN8_PPAT_LLCELLC) | /* for something pointing to ptes? */
+             GEN8_PPAT(2, GEN8_PPAT_WT | GEN8_PPAT_LLCELLC) | /* for scanout with eLLC */
+             GEN8_PPAT(3, GEN8_PPAT_UC)                     | /* Uncached objects, mostly for scanout */
+             GEN8_PPAT(4, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(0)) |
+             GEN8_PPAT(5, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(1)) |
+             GEN8_PPAT(6, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2)) |
+             GEN8_PPAT(7, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
+
+       /* XXX: spec defines this as 2 distinct registers. It's unclear if a 64b
+        * write would work. */
+       I915_WRITE(GEN8_PRIVATE_PAT, pat);
+       I915_WRITE(GEN8_PRIVATE_PAT + 4, pat >> 32);
+}
+
+static int gen8_gmch_probe(struct drm_device *dev,
+                          size_t *gtt_total,
+                          size_t *stolen,
+                          phys_addr_t *mappable_base,
+                          unsigned long *mappable_end)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned int gtt_size;
+       u16 snb_gmch_ctl;
+       int ret;
+
+       /* TODO: We're not aware of mappable constraints on gen8 yet */
+       *mappable_base = pci_resource_start(dev->pdev, 2);
+       *mappable_end = pci_resource_len(dev->pdev, 2);
+
+       if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(39)))
+               pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(39));
+
+       pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
+
+       *stolen = gen8_get_stolen_size(snb_gmch_ctl);
+
+       gtt_size = gen8_get_total_gtt_size(snb_gmch_ctl);
+       *gtt_total = (gtt_size / sizeof(gen8_gtt_pte_t)) << PAGE_SHIFT;
+
+       gen8_setup_private_ppat(dev_priv);
+
+       ret = ggtt_probe_common(dev, gtt_size);
+
+       dev_priv->gtt.base.clear_range = gen8_ggtt_clear_range;
+       dev_priv->gtt.base.insert_entries = gen8_ggtt_insert_entries;
+
+       return ret;
+}
+
 static int gen6_gmch_probe(struct drm_device *dev,
                           size_t *gtt_total,
                           size_t *stolen,
@@ -881,7 +1358,6 @@ static int gen6_gmch_probe(struct drm_device *dev,
                           unsigned long *mappable_end)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       phys_addr_t gtt_bus_addr;
        unsigned int gtt_size;
        u16 snb_gmch_ctl;
        int ret;
@@ -901,24 +1377,13 @@ static int gen6_gmch_probe(struct drm_device *dev,
        if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(40)))
                pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(40));
        pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
-       gtt_size = gen6_get_total_gtt_size(snb_gmch_ctl);
 
        *stolen = gen6_get_stolen_size(snb_gmch_ctl);
-       *gtt_total = (gtt_size / sizeof(gen6_gtt_pte_t)) << PAGE_SHIFT;
-
-       /* For Modern GENs the PTEs and register space are split in the BAR */
-       gtt_bus_addr = pci_resource_start(dev->pdev, 0) +
-               (pci_resource_len(dev->pdev, 0) / 2);
 
-       dev_priv->gtt.gsm = ioremap_wc(gtt_bus_addr, gtt_size);
-       if (!dev_priv->gtt.gsm) {
-               DRM_ERROR("Failed to map the gtt page table\n");
-               return -ENOMEM;
-       }
+       gtt_size = gen6_get_total_gtt_size(snb_gmch_ctl);
+       *gtt_total = (gtt_size / sizeof(gen6_gtt_pte_t)) << PAGE_SHIFT;
 
-       ret = setup_scratch_page(dev);
-       if (ret)
-               DRM_ERROR("Scratch setup failed\n");
+       ret = ggtt_probe_common(dev, gtt_size);
 
        dev_priv->gtt.base.clear_range = gen6_ggtt_clear_range;
        dev_priv->gtt.base.insert_entries = gen6_ggtt_insert_entries;
@@ -972,7 +1437,7 @@ int i915_gem_gtt_init(struct drm_device *dev)
        if (INTEL_INFO(dev)->gen <= 5) {
                gtt->gtt_probe = i915_gmch_probe;
                gtt->base.cleanup = i915_gmch_remove;
-       } else {
+       } else if (INTEL_INFO(dev)->gen < 8) {
                gtt->gtt_probe = gen6_gmch_probe;
                gtt->base.cleanup = gen6_gmch_remove;
                if (IS_HASWELL(dev) && dev_priv->ellc_size)
@@ -985,6 +1450,9 @@ int i915_gem_gtt_init(struct drm_device *dev)
                        gtt->base.pte_encode = ivb_pte_encode;
                else
                        gtt->base.pte_encode = snb_pte_encode;
+       } else {
+               dev_priv->gtt.gtt_probe = gen8_gmch_probe;
+               dev_priv->gtt.base.cleanup = gen6_gmch_remove;
        }
 
        ret = gtt->gtt_probe(dev, &gtt->base.total, &gtt->stolen_size,
index e15a1d9..d284d89 100644 (file)
@@ -395,7 +395,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
        if (gtt_offset == I915_GTT_OFFSET_NONE)
                return obj;
 
-       vma = i915_gem_vma_create(obj, ggtt);
+       vma = i915_gem_obj_lookup_or_create_vma(obj, ggtt);
        if (IS_ERR(vma)) {
                ret = PTR_ERR(vma);
                goto err_out;
index 032e9ef..b139053 100644 (file)
@@ -308,7 +308,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
                return -EINVAL;
        }
 
-       if (obj->pin_count) {
+       if (obj->pin_count || obj->framebuffer_references) {
                drm_gem_object_unreference_unlocked(&obj->base);
                return -EBUSY;
        }
@@ -393,7 +393,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
        /* Try to preallocate memory required to save swizzling on put-pages */
        if (i915_gem_object_needs_bit17_swizzle(obj)) {
                if (obj->bit_17 == NULL) {
-                       obj->bit_17 = kmalloc(BITS_TO_LONGS(obj->base.size >> PAGE_SHIFT) *
+                       obj->bit_17 = kcalloc(BITS_TO_LONGS(obj->base.size >> PAGE_SHIFT),
                                              sizeof(long), GFP_KERNEL);
                }
        } else {
@@ -504,8 +504,8 @@ i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj)
        int i;
 
        if (obj->bit_17 == NULL) {
-               obj->bit_17 = kmalloc(BITS_TO_LONGS(page_count) *
-                                          sizeof(long), GFP_KERNEL);
+               obj->bit_17 = kcalloc(BITS_TO_LONGS(page_count),
+                                     sizeof(long), GFP_KERNEL);
                if (obj->bit_17 == NULL) {
                        DRM_ERROR("Failed to allocate memory for bit 17 "
                                  "record\n");
index dae364f..79dcb8f 100644 (file)
@@ -215,6 +215,24 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m,
        }
 }
 
+static const char *hangcheck_action_to_str(enum intel_ring_hangcheck_action a)
+{
+       switch (a) {
+       case HANGCHECK_IDLE:
+               return "idle";
+       case HANGCHECK_WAIT:
+               return "wait";
+       case HANGCHECK_ACTIVE:
+               return "active";
+       case HANGCHECK_KICK:
+               return "kick";
+       case HANGCHECK_HUNG:
+               return "hung";
+       }
+
+       return "unknown";
+}
+
 static void i915_ring_error_state(struct drm_i915_error_state_buf *m,
                                  struct drm_device *dev,
                                  struct drm_i915_error_state *error,
@@ -231,7 +249,8 @@ static void i915_ring_error_state(struct drm_i915_error_state_buf *m,
        err_printf(m, "  INSTDONE: 0x%08x\n", error->instdone[ring]);
        if (ring == RCS && INTEL_INFO(dev)->gen >= 4)
                err_printf(m, "  BBADDR: 0x%08llx\n", error->bbaddr);
-
+       if (INTEL_INFO(dev)->gen >= 4)
+               err_printf(m, "  BB_STATE: 0x%08x\n", error->bbstate[ring]);
        if (INTEL_INFO(dev)->gen >= 4)
                err_printf(m, "  INSTPS: 0x%08x\n", error->instps[ring]);
        err_printf(m, "  INSTPM: 0x%08x\n", error->instpm[ring]);
@@ -255,6 +274,9 @@ static void i915_ring_error_state(struct drm_i915_error_state_buf *m,
        err_printf(m, "  waiting: %s\n", yesno(error->waiting[ring]));
        err_printf(m, "  ring->head: 0x%08x\n", error->cpu_ring_head[ring]);
        err_printf(m, "  ring->tail: 0x%08x\n", error->cpu_ring_tail[ring]);
+       err_printf(m, "  hangcheck: %s [%d]\n",
+                  hangcheck_action_to_str(error->hangcheck_action[ring]),
+                  error->hangcheck_score[ring]);
 }
 
 void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
@@ -283,13 +305,14 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
        err_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec,
                   error->time.tv_usec);
        err_printf(m, "Kernel: " UTS_RELEASE "\n");
-       err_printf(m, "PCI ID: 0x%04x\n", dev->pci_device);
+       err_printf(m, "PCI ID: 0x%04x\n", dev->pdev->device);
        err_printf(m, "EIR: 0x%08x\n", error->eir);
        err_printf(m, "IER: 0x%08x\n", error->ier);
        err_printf(m, "PGTBL_ER: 0x%08x\n", error->pgtbl_er);
        err_printf(m, "FORCEWAKE: 0x%08x\n", error->forcewake);
        err_printf(m, "DERRMR: 0x%08x\n", error->derrmr);
        err_printf(m, "CCID: 0x%08x\n", error->ccid);
+       err_printf(m, "Missed interrupts: 0x%08lx\n", dev_priv->gpu_error.missed_irq_rings);
 
        for (i = 0; i < dev_priv->num_fence_regs; i++)
                err_printf(m, "  fence[%d] = %08llx\n", i, error->fence[i]);
@@ -601,6 +624,7 @@ static void i915_gem_record_fences(struct drm_device *dev,
 
        /* Fences */
        switch (INTEL_INFO(dev)->gen) {
+       case 8:
        case 7:
        case 6:
                for (i = 0; i < dev_priv->num_fence_regs; i++)
@@ -703,6 +727,7 @@ static void i915_record_ring_state(struct drm_device *dev,
                error->instps[ring->id] = I915_READ(RING_INSTPS(ring->mmio_base));
                if (ring->id == RCS)
                        error->bbaddr = I915_READ64(BB_ADDR);
+               error->bbstate[ring->id] = I915_READ(RING_BBSTATE(ring->mmio_base));
        } else {
                error->faddr[ring->id] = I915_READ(DMA_FADD_I8XX);
                error->ipeir[ring->id] = I915_READ(IPEIR);
@@ -720,6 +745,9 @@ static void i915_record_ring_state(struct drm_device *dev,
 
        error->cpu_ring_head[ring->id] = ring->head;
        error->cpu_ring_tail[ring->id] = ring->tail;
+
+       error->hangcheck_score[ring->id] = ring->hangcheck.score;
+       error->hangcheck_action[ring->id] = ring->hangcheck.action;
 }
 
 
@@ -769,7 +797,7 @@ static void i915_gem_record_rings(struct drm_device *dev,
 
                error->ring[i].num_requests = count;
                error->ring[i].requests =
-                       kmalloc(count*sizeof(struct drm_i915_error_request),
+                       kcalloc(count, sizeof(*error->ring[i].requests),
                                GFP_ATOMIC);
                if (error->ring[i].requests == NULL) {
                        error->ring[i].num_requests = 0;
@@ -811,7 +839,7 @@ static void i915_gem_capture_vm(struct drm_i915_private *dev_priv,
        error->pinned_bo_count[ndx] = i - error->active_bo_count[ndx];
 
        if (i) {
-               active_bo = kmalloc(sizeof(*active_bo)*i, GFP_ATOMIC);
+               active_bo = kcalloc(i, sizeof(*active_bo), GFP_ATOMIC);
                if (active_bo)
                        pinned_bo = active_bo + error->active_bo_count[ndx];
        }
@@ -885,8 +913,12 @@ void i915_capture_error_state(struct drm_device *dev)
                return;
        }
 
-       DRM_INFO("capturing error event; look for more information in "
-                "/sys/class/drm/card%d/error\n", dev->primary->index);
+       DRM_INFO("GPU crash dump saved to /sys/class/drm/card%d/error\n",
+                dev->primary->index);
+       DRM_INFO("GPU hangs can indicate a bug anywhere in the entire gfx stack, including userspace.\n");
+       DRM_INFO("Please file a _new_ bug report on bugs.freedesktop.org against DRI -> DRM/Intel\n");
+       DRM_INFO("drm/i915 developers can then reassign to the right component if it's not a kernel issue.\n");
+       DRM_INFO("The gpu crash dump is required to analyze gpu hangs, so please always attach it.\n");
 
        kref_init(&error->ref);
        error->eir = I915_READ(EIR);
@@ -988,6 +1020,7 @@ const char *i915_cache_level_str(int type)
        case I915_CACHE_NONE: return " uncached";
        case I915_CACHE_LLC: return " snooped or LLC";
        case I915_CACHE_L3_LLC: return " L3+LLC";
+       case I915_CACHE_WT: return " WT";
        default: return "";
        }
 }
@@ -1012,6 +1045,7 @@ void i915_get_extra_instdone(struct drm_device *dev, uint32_t *instdone)
        default:
                WARN_ONCE(1, "Unsupported platform\n");
        case 7:
+       case 8:
                instdone[0] = I915_READ(GEN7_INSTDONE_1);
                instdone[1] = I915_READ(GEN7_SC_INSTDONE);
                instdone[2] = I915_READ(GEN7_SAMPLER_INSTDONE);
index 4b91228..5d1dedc 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <linux/sysrq.h>
 #include <linux/slab.h>
+#include <linux/circ_buf.h>
 #include <drm/drmP.h>
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
@@ -269,6 +270,21 @@ static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
        }
 }
 
+static void broadwell_set_fifo_underrun_reporting(struct drm_device *dev,
+                                                 enum pipe pipe, bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       assert_spin_locked(&dev_priv->irq_lock);
+
+       if (enable)
+               dev_priv->de_irq_mask[pipe] &= ~GEN8_PIPE_FIFO_UNDERRUN;
+       else
+               dev_priv->de_irq_mask[pipe] |= GEN8_PIPE_FIFO_UNDERRUN;
+       I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
+       POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
+}
+
 /**
  * ibx_display_interrupt_update - update SDEIMR
  * @dev_priv: driver private
@@ -381,6 +397,8 @@ bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
                ironlake_set_fifo_underrun_reporting(dev, pipe, enable);
        else if (IS_GEN7(dev))
                ivybridge_set_fifo_underrun_reporting(dev, pipe, enable);
+       else if (IS_GEN8(dev))
+               broadwell_set_fifo_underrun_reporting(dev, pipe, enable);
 
 done:
        spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
@@ -441,7 +459,7 @@ done:
 
 
 void
-i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
+i915_enable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask)
 {
        u32 reg = PIPESTAT(pipe);
        u32 pipestat = I915_READ(reg) & 0x7fff0000;
@@ -458,7 +476,7 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
 }
 
 void
-i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
+i915_disable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask)
 {
        u32 reg = PIPESTAT(pipe);
        u32 pipestat = I915_READ(reg) & 0x7fff0000;
@@ -486,9 +504,10 @@ static void i915_enable_asle_pipestat(struct drm_device *dev)
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
 
-       i915_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_B, PIPE_LEGACY_BLC_EVENT_ENABLE);
        if (INTEL_INFO(dev)->gen >= 4)
-               i915_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE);
+               i915_enable_pipestat(dev_priv, PIPE_A,
+                                    PIPE_LEGACY_BLC_EVENT_ENABLE);
 
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
@@ -518,6 +537,12 @@ i915_pipe_enabled(struct drm_device *dev, int pipe)
        }
 }
 
+static u32 i8xx_get_vblank_counter(struct drm_device *dev, int pipe)
+{
+       /* Gen2 doesn't have a hardware frame counter */
+       return 0;
+}
+
 /* Called from drm generic code, passed a 'crtc', which
  * we use as a pipe index
  */
@@ -526,7 +551,7 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        unsigned long high_frame;
        unsigned long low_frame;
-       u32 high1, high2, low;
+       u32 high1, high2, low, pixel, vbl_start;
 
        if (!i915_pipe_enabled(dev, pipe)) {
                DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
@@ -534,6 +559,24 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
                return 0;
        }
 
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               struct intel_crtc *intel_crtc =
+                       to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+               const struct drm_display_mode *mode =
+                       &intel_crtc->config.adjusted_mode;
+
+               vbl_start = mode->crtc_vblank_start * mode->crtc_htotal;
+       } else {
+               enum transcoder cpu_transcoder =
+                       intel_pipe_to_cpu_transcoder(dev_priv, pipe);
+               u32 htotal;
+
+               htotal = ((I915_READ(HTOTAL(cpu_transcoder)) >> 16) & 0x1fff) + 1;
+               vbl_start = (I915_READ(VBLANK(cpu_transcoder)) & 0x1fff) + 1;
+
+               vbl_start *= htotal;
+       }
+
        high_frame = PIPEFRAME(pipe);
        low_frame = PIPEFRAMEPIXEL(pipe);
 
@@ -544,13 +587,20 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
         */
        do {
                high1 = I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK;
-               low   = I915_READ(low_frame)  & PIPE_FRAME_LOW_MASK;
+               low   = I915_READ(low_frame);
                high2 = I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK;
        } while (high1 != high2);
 
        high1 >>= PIPE_FRAME_HIGH_SHIFT;
+       pixel = low & PIPE_PIXEL_MASK;
        low >>= PIPE_FRAME_LOW_SHIFT;
-       return (high1 << 8) | low;
+
+       /*
+        * The frame counter increments at beginning of active.
+        * Cook up a vblank counter by also checking the pixel
+        * counter against vblank start.
+        */
+       return ((high1 << 8) | low) + (pixel >= vbl_start);
 }
 
 static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
@@ -567,66 +617,163 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
        return I915_READ(reg);
 }
 
+/* raw reads, only for fast reads of display block, no need for forcewake etc. */
+#define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__))
+#define __raw_i915_read16(dev_priv__, reg__) readw((dev_priv__)->regs + (reg__))
+
+static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t status;
+       int reg;
+
+       if (IS_VALLEYVIEW(dev)) {
+               status = pipe == PIPE_A ?
+                       I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
+                       I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
+
+               reg = VLV_ISR;
+       } else if (IS_GEN2(dev)) {
+               status = pipe == PIPE_A ?
+                       I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
+                       I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
+
+               reg = ISR;
+       } else if (INTEL_INFO(dev)->gen < 5) {
+               status = pipe == PIPE_A ?
+                       I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
+                       I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
+
+               reg = ISR;
+       } else if (INTEL_INFO(dev)->gen < 7) {
+               status = pipe == PIPE_A ?
+                       DE_PIPEA_VBLANK :
+                       DE_PIPEB_VBLANK;
+
+               reg = DEISR;
+       } else {
+               switch (pipe) {
+               default:
+               case PIPE_A:
+                       status = DE_PIPEA_VBLANK_IVB;
+                       break;
+               case PIPE_B:
+                       status = DE_PIPEB_VBLANK_IVB;
+                       break;
+               case PIPE_C:
+                       status = DE_PIPEC_VBLANK_IVB;
+                       break;
+               }
+
+               reg = DEISR;
+       }
+
+       if (IS_GEN2(dev))
+               return __raw_i915_read16(dev_priv, reg) & status;
+       else
+               return __raw_i915_read32(dev_priv, reg) & status;
+}
+
 static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
-                            int *vpos, int *hpos)
+                            int *vpos, int *hpos, ktime_t *stime, ktime_t *etime)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       u32 vbl = 0, position = 0;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       const struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode;
+       int position;
        int vbl_start, vbl_end, htotal, vtotal;
        bool in_vbl = true;
        int ret = 0;
-       enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
-                                                                     pipe);
+       unsigned long irqflags;
 
-       if (!i915_pipe_enabled(dev, pipe)) {
+       if (!intel_crtc->active) {
                DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
                                 "pipe %c\n", pipe_name(pipe));
                return 0;
        }
 
-       /* Get vtotal. */
-       vtotal = 1 + ((I915_READ(VTOTAL(cpu_transcoder)) >> 16) & 0x1fff);
+       htotal = mode->crtc_htotal;
+       vtotal = mode->crtc_vtotal;
+       vbl_start = mode->crtc_vblank_start;
+       vbl_end = mode->crtc_vblank_end;
 
-       if (INTEL_INFO(dev)->gen >= 4) {
+       ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
+
+       /*
+        * Lock uncore.lock, as we will do multiple timing critical raw
+        * register reads, potentially with preemption disabled, so the
+        * following code must not block on uncore.lock.
+        */
+       spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
+       
+       /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
+
+       /* Get optional system timestamp before query. */
+       if (stime)
+               *stime = ktime_get();
+
+       if (IS_GEN2(dev) || IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
                /* No obvious pixelcount register. Only query vertical
                 * scanout position from Display scan line register.
                 */
-               position = I915_READ(PIPEDSL(pipe));
+               if (IS_GEN2(dev))
+                       position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN2;
+               else
+                       position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
 
-               /* Decode into vertical scanout position. Don't have
-                * horizontal scanout position.
+               /*
+                * The scanline counter increments at the leading edge
+                * of hsync, ie. it completely misses the active portion
+                * of the line. Fix up the counter at both edges of vblank
+                * to get a more accurate picture whether we're in vblank
+                * or not.
                 */
-               *vpos = position & 0x1fff;
-               *hpos = 0;
+               in_vbl = intel_pipe_in_vblank_locked(dev, pipe);
+               if ((in_vbl && position == vbl_start - 1) ||
+                   (!in_vbl && position == vbl_end - 1))
+                       position = (position + 1) % vtotal;
        } else {
                /* Have access to pixelcount since start of frame.
                 * We can split this into vertical and horizontal
                 * scanout position.
                 */
-               position = (I915_READ(PIPEFRAMEPIXEL(pipe)) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT;
+               position = (__raw_i915_read32(dev_priv, PIPEFRAMEPIXEL(pipe)) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT;
 
-               htotal = 1 + ((I915_READ(HTOTAL(cpu_transcoder)) >> 16) & 0x1fff);
-               *vpos = position / htotal;
-               *hpos = position - (*vpos * htotal);
+               /* convert to pixel counts */
+               vbl_start *= htotal;
+               vbl_end *= htotal;
+               vtotal *= htotal;
        }
 
-       /* Query vblank area. */
-       vbl = I915_READ(VBLANK(cpu_transcoder));
+       /* Get optional system timestamp after query. */
+       if (etime)
+               *etime = ktime_get();
 
-       /* Test position against vblank region. */
-       vbl_start = vbl & 0x1fff;
-       vbl_end = (vbl >> 16) & 0x1fff;
+       /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
 
-       if ((*vpos < vbl_start) || (*vpos > vbl_end))
-               in_vbl = false;
+       spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
 
-       /* Inside "upper part" of vblank area? Apply corrective offset: */
-       if (in_vbl && (*vpos >= vbl_start))
-               *vpos = *vpos - vtotal;
+       in_vbl = position >= vbl_start && position < vbl_end;
 
-       /* Readouts valid? */
-       if (vbl > 0)
-               ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
+       /*
+        * While in vblank, position will be negative
+        * counting up towards 0 at vbl_end. And outside
+        * vblank, position will be positive counting
+        * up since vbl_end.
+        */
+       if (position >= vbl_start)
+               position -= vbl_end;
+       else
+               position += vtotal - vbl_end;
+
+       if (IS_GEN2(dev) || IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
+               *vpos = position;
+               *hpos = 0;
+       } else {
+               *vpos = position / htotal;
+               *hpos = position - (*vpos * htotal);
+       }
 
        /* In vblank? */
        if (in_vbl)
@@ -665,7 +812,8 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe,
                                                     crtc);
 }
 
-static int intel_hpd_irq_event(struct drm_device *dev, struct drm_connector *connector)
+static bool intel_hpd_irq_event(struct drm_device *dev,
+                               struct drm_connector *connector)
 {
        enum drm_connector_status old_status;
 
@@ -673,11 +821,16 @@ static int intel_hpd_irq_event(struct drm_device *dev, struct drm_connector *con
        old_status = connector->status;
 
        connector->status = connector->funcs->detect(connector, false);
-       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
+       if (old_status == connector->status)
+               return false;
+
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
                      connector->base.id,
                      drm_get_connector_name(connector),
-                     old_status, connector->status);
-       return (old_status != connector->status);
+                     drm_get_connector_status_name(old_status),
+                     drm_get_connector_status_name(connector->status));
+
+       return true;
 }
 
 /*
@@ -801,7 +954,7 @@ static void notify_ring(struct drm_device *dev,
        if (ring->obj == NULL)
                return;
 
-       trace_i915_gem_request_complete(ring, ring->get_seqno(ring, false));
+       trace_i915_gem_request_complete(ring);
 
        wake_up_all(&ring->irq_queue);
        i915_queue_hangcheck(dev);
@@ -812,7 +965,7 @@ static void gen6_pm_rps_work(struct work_struct *work)
        drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
                                                    rps.work);
        u32 pm_iir;
-       u8 new_delay;
+       int new_delay, adj;
 
        spin_lock_irq(&dev_priv->irq_lock);
        pm_iir = dev_priv->rps.pm_iir;
@@ -829,40 +982,49 @@ static void gen6_pm_rps_work(struct work_struct *work)
 
        mutex_lock(&dev_priv->rps.hw_lock);
 
+       adj = dev_priv->rps.last_adj;
        if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
-               new_delay = dev_priv->rps.cur_delay + 1;
+               if (adj > 0)
+                       adj *= 2;
+               else
+                       adj = 1;
+               new_delay = dev_priv->rps.cur_delay + adj;
 
                /*
                 * For better performance, jump directly
                 * to RPe if we're below it.
                 */
-               if (IS_VALLEYVIEW(dev_priv->dev) &&
-                   dev_priv->rps.cur_delay < dev_priv->rps.rpe_delay)
+               if (new_delay < dev_priv->rps.rpe_delay)
+                       new_delay = dev_priv->rps.rpe_delay;
+       } else if (pm_iir & GEN6_PM_RP_DOWN_TIMEOUT) {
+               if (dev_priv->rps.cur_delay > dev_priv->rps.rpe_delay)
                        new_delay = dev_priv->rps.rpe_delay;
-       } else
-               new_delay = dev_priv->rps.cur_delay - 1;
+               else
+                       new_delay = dev_priv->rps.min_delay;
+               adj = 0;
+       } else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) {
+               if (adj < 0)
+                       adj *= 2;
+               else
+                       adj = -1;
+               new_delay = dev_priv->rps.cur_delay + adj;
+       } else { /* unknown event */
+               new_delay = dev_priv->rps.cur_delay;
+       }
 
        /* sysfs frequency interfaces may have snuck in while servicing the
         * interrupt
         */
-       if (new_delay >= dev_priv->rps.min_delay &&
-           new_delay <= dev_priv->rps.max_delay) {
-               if (IS_VALLEYVIEW(dev_priv->dev))
-                       valleyview_set_rps(dev_priv->dev, new_delay);
-               else
-                       gen6_set_rps(dev_priv->dev, new_delay);
-       }
-
-       if (IS_VALLEYVIEW(dev_priv->dev)) {
-               /*
-                * On VLV, when we enter RC6 we may not be at the minimum
-                * voltage level, so arm a timer to check.  It should only
-                * fire when there's activity or once after we've entered
-                * RC6, and then won't be re-armed until the next RPS interrupt.
-                */
-               mod_delayed_work(dev_priv->wq, &dev_priv->rps.vlv_work,
-                                msecs_to_jiffies(100));
-       }
+       if (new_delay < (int)dev_priv->rps.min_delay)
+               new_delay = dev_priv->rps.min_delay;
+       if (new_delay > (int)dev_priv->rps.max_delay)
+               new_delay = dev_priv->rps.max_delay;
+       dev_priv->rps.last_adj = new_delay - dev_priv->rps.cur_delay;
+
+       if (IS_VALLEYVIEW(dev_priv->dev))
+               valleyview_set_rps(dev_priv->dev, new_delay);
+       else
+               gen6_set_rps(dev_priv->dev, new_delay);
 
        mutex_unlock(&dev_priv->rps.hw_lock);
 }
@@ -882,9 +1044,10 @@ static void ivybridge_parity_work(struct work_struct *work)
        drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
                                                    l3_parity.error_work);
        u32 error_status, row, bank, subbank;
-       char *parity_event[5];
+       char *parity_event[6];
        uint32_t misccpctl;
        unsigned long flags;
+       uint8_t slice = 0;
 
        /* We must turn off DOP level clock gating to access the L3 registers.
         * In order to prevent a get/put style interface, acquire struct mutex
@@ -892,55 +1055,81 @@ static void ivybridge_parity_work(struct work_struct *work)
         */
        mutex_lock(&dev_priv->dev->struct_mutex);
 
+       /* If we've screwed up tracking, just let the interrupt fire again */
+       if (WARN_ON(!dev_priv->l3_parity.which_slice))
+               goto out;
+
        misccpctl = I915_READ(GEN7_MISCCPCTL);
        I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE);
        POSTING_READ(GEN7_MISCCPCTL);
 
-       error_status = I915_READ(GEN7_L3CDERRST1);
-       row = GEN7_PARITY_ERROR_ROW(error_status);
-       bank = GEN7_PARITY_ERROR_BANK(error_status);
-       subbank = GEN7_PARITY_ERROR_SUBBANK(error_status);
+       while ((slice = ffs(dev_priv->l3_parity.which_slice)) != 0) {
+               u32 reg;
 
-       I915_WRITE(GEN7_L3CDERRST1, GEN7_PARITY_ERROR_VALID |
-                                   GEN7_L3CDERRST1_ENABLE);
-       POSTING_READ(GEN7_L3CDERRST1);
+               slice--;
+               if (WARN_ON_ONCE(slice >= NUM_L3_SLICES(dev_priv->dev)))
+                       break;
 
-       I915_WRITE(GEN7_MISCCPCTL, misccpctl);
+               dev_priv->l3_parity.which_slice &= ~(1<<slice);
 
-       spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       ilk_enable_gt_irq(dev_priv, GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
-       spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+               reg = GEN7_L3CDERRST1 + (slice * 0x200);
 
-       mutex_unlock(&dev_priv->dev->struct_mutex);
+               error_status = I915_READ(reg);
+               row = GEN7_PARITY_ERROR_ROW(error_status);
+               bank = GEN7_PARITY_ERROR_BANK(error_status);
+               subbank = GEN7_PARITY_ERROR_SUBBANK(error_status);
 
-       parity_event[0] = I915_L3_PARITY_UEVENT "=1";
-       parity_event[1] = kasprintf(GFP_KERNEL, "ROW=%d", row);
-       parity_event[2] = kasprintf(GFP_KERNEL, "BANK=%d", bank);
-       parity_event[3] = kasprintf(GFP_KERNEL, "SUBBANK=%d", subbank);
-       parity_event[4] = NULL;
+               I915_WRITE(reg, GEN7_PARITY_ERROR_VALID | GEN7_L3CDERRST1_ENABLE);
+               POSTING_READ(reg);
+
+               parity_event[0] = I915_L3_PARITY_UEVENT "=1";
+               parity_event[1] = kasprintf(GFP_KERNEL, "ROW=%d", row);
+               parity_event[2] = kasprintf(GFP_KERNEL, "BANK=%d", bank);
+               parity_event[3] = kasprintf(GFP_KERNEL, "SUBBANK=%d", subbank);
+               parity_event[4] = kasprintf(GFP_KERNEL, "SLICE=%d", slice);
+               parity_event[5] = NULL;
+
+               kobject_uevent_env(&dev_priv->dev->primary->kdev->kobj,
+                                  KOBJ_CHANGE, parity_event);
+
+               DRM_DEBUG("Parity error: Slice = %d, Row = %d, Bank = %d, Sub bank = %d.\n",
+                         slice, row, bank, subbank);
+
+               kfree(parity_event[4]);
+               kfree(parity_event[3]);
+               kfree(parity_event[2]);
+               kfree(parity_event[1]);
+       }
 
-       kobject_uevent_env(&dev_priv->dev->primary->kdev.kobj,
-                          KOBJ_CHANGE, parity_event);
+       I915_WRITE(GEN7_MISCCPCTL, misccpctl);
 
-       DRM_DEBUG("Parity error: Row = %d, Bank = %d, Sub bank = %d.\n",
-                 row, bank, subbank);
+out:
+       WARN_ON(dev_priv->l3_parity.which_slice);
+       spin_lock_irqsave(&dev_priv->irq_lock, flags);
+       ilk_enable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv->dev));
+       spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
 
-       kfree(parity_event[3]);
-       kfree(parity_event[2]);
-       kfree(parity_event[1]);
+       mutex_unlock(&dev_priv->dev->struct_mutex);
 }
 
-static void ivybridge_parity_error_irq_handler(struct drm_device *dev)
+static void ivybridge_parity_error_irq_handler(struct drm_device *dev, u32 iir)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
 
-       if (!HAS_L3_GPU_CACHE(dev))
+       if (!HAS_L3_DPF(dev))
                return;
 
        spin_lock(&dev_priv->irq_lock);
-       ilk_disable_gt_irq(dev_priv, GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
+       ilk_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev));
        spin_unlock(&dev_priv->irq_lock);
 
+       iir &= GT_PARITY_ERROR(dev);
+       if (iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT_S1)
+               dev_priv->l3_parity.which_slice |= 1 << 1;
+
+       if (iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT)
+               dev_priv->l3_parity.which_slice |= 1 << 0;
+
        queue_work(dev_priv->wq, &dev_priv->l3_parity.error_work);
 }
 
@@ -975,8 +1164,58 @@ static void snb_gt_irq_handler(struct drm_device *dev,
                i915_handle_error(dev, false);
        }
 
-       if (gt_iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT)
-               ivybridge_parity_error_irq_handler(dev);
+       if (gt_iir & GT_PARITY_ERROR(dev))
+               ivybridge_parity_error_irq_handler(dev, gt_iir);
+}
+
+static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
+                                      struct drm_i915_private *dev_priv,
+                                      u32 master_ctl)
+{
+       u32 rcs, bcs, vcs;
+       uint32_t tmp = 0;
+       irqreturn_t ret = IRQ_NONE;
+
+       if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) {
+               tmp = I915_READ(GEN8_GT_IIR(0));
+               if (tmp) {
+                       ret = IRQ_HANDLED;
+                       rcs = tmp >> GEN8_RCS_IRQ_SHIFT;
+                       bcs = tmp >> GEN8_BCS_IRQ_SHIFT;
+                       if (rcs & GT_RENDER_USER_INTERRUPT)
+                               notify_ring(dev, &dev_priv->ring[RCS]);
+                       if (bcs & GT_RENDER_USER_INTERRUPT)
+                               notify_ring(dev, &dev_priv->ring[BCS]);
+                       I915_WRITE(GEN8_GT_IIR(0), tmp);
+               } else
+                       DRM_ERROR("The master control interrupt lied (GT0)!\n");
+       }
+
+       if (master_ctl & GEN8_GT_VCS1_IRQ) {
+               tmp = I915_READ(GEN8_GT_IIR(1));
+               if (tmp) {
+                       ret = IRQ_HANDLED;
+                       vcs = tmp >> GEN8_VCS1_IRQ_SHIFT;
+                       if (vcs & GT_RENDER_USER_INTERRUPT)
+                               notify_ring(dev, &dev_priv->ring[VCS]);
+                       I915_WRITE(GEN8_GT_IIR(1), tmp);
+               } else
+                       DRM_ERROR("The master control interrupt lied (GT1)!\n");
+       }
+
+       if (master_ctl & GEN8_GT_VECS_IRQ) {
+               tmp = I915_READ(GEN8_GT_IIR(3));
+               if (tmp) {
+                       ret = IRQ_HANDLED;
+                       vcs = tmp >> GEN8_VECS_IRQ_SHIFT;
+                       if (vcs & GT_RENDER_USER_INTERRUPT)
+                               notify_ring(dev, &dev_priv->ring[VECS]);
+                       I915_WRITE(GEN8_GT_IIR(3), tmp);
+               } else
+                       DRM_ERROR("The master control interrupt lied (GT3)!\n");
+       }
+
+       return ret;
 }
 
 #define HPD_STORM_DETECT_PERIOD 1000
@@ -1050,6 +1289,102 @@ static void dp_aux_irq_handler(struct drm_device *dev)
        wake_up_all(&dev_priv->gmbus_wait_queue);
 }
 
+#if defined(CONFIG_DEBUG_FS)
+static void display_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe,
+                                        uint32_t crc0, uint32_t crc1,
+                                        uint32_t crc2, uint32_t crc3,
+                                        uint32_t crc4)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
+       struct intel_pipe_crc_entry *entry;
+       int head, tail;
+
+       spin_lock(&pipe_crc->lock);
+
+       if (!pipe_crc->entries) {
+               spin_unlock(&pipe_crc->lock);
+               DRM_ERROR("spurious interrupt\n");
+               return;
+       }
+
+       head = pipe_crc->head;
+       tail = pipe_crc->tail;
+
+       if (CIRC_SPACE(head, tail, INTEL_PIPE_CRC_ENTRIES_NR) < 1) {
+               spin_unlock(&pipe_crc->lock);
+               DRM_ERROR("CRC buffer overflowing\n");
+               return;
+       }
+
+       entry = &pipe_crc->entries[head];
+
+       entry->frame = dev->driver->get_vblank_counter(dev, pipe);
+       entry->crc[0] = crc0;
+       entry->crc[1] = crc1;
+       entry->crc[2] = crc2;
+       entry->crc[3] = crc3;
+       entry->crc[4] = crc4;
+
+       head = (head + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1);
+       pipe_crc->head = head;
+
+       spin_unlock(&pipe_crc->lock);
+
+       wake_up_interruptible(&pipe_crc->wq);
+}
+#else
+static inline void
+display_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe,
+                            uint32_t crc0, uint32_t crc1,
+                            uint32_t crc2, uint32_t crc3,
+                            uint32_t crc4) {}
+#endif
+
+
+static void hsw_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       display_pipe_crc_irq_handler(dev, pipe,
+                                    I915_READ(PIPE_CRC_RES_1_IVB(pipe)),
+                                    0, 0, 0, 0);
+}
+
+static void ivb_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       display_pipe_crc_irq_handler(dev, pipe,
+                                    I915_READ(PIPE_CRC_RES_1_IVB(pipe)),
+                                    I915_READ(PIPE_CRC_RES_2_IVB(pipe)),
+                                    I915_READ(PIPE_CRC_RES_3_IVB(pipe)),
+                                    I915_READ(PIPE_CRC_RES_4_IVB(pipe)),
+                                    I915_READ(PIPE_CRC_RES_5_IVB(pipe)));
+}
+
+static void i9xx_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t res1, res2;
+
+       if (INTEL_INFO(dev)->gen >= 3)
+               res1 = I915_READ(PIPE_CRC_RES_RES1_I915(pipe));
+       else
+               res1 = 0;
+
+       if (INTEL_INFO(dev)->gen >= 5 || IS_G4X(dev))
+               res2 = I915_READ(PIPE_CRC_RES_RES2_G4X(pipe));
+       else
+               res2 = 0;
+
+       display_pipe_crc_irq_handler(dev, pipe,
+                                    I915_READ(PIPE_CRC_RES_RED(pipe)),
+                                    I915_READ(PIPE_CRC_RES_GREEN(pipe)),
+                                    I915_READ(PIPE_CRC_RES_BLUE(pipe)),
+                                    res1, res2);
+}
+
 /* The RPS events need forcewake, so we add them to a work queue and mask their
  * IMR bits until the work is done. Other interrupts can be processed without
  * the work queue. */
@@ -1117,13 +1452,16 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
                spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
                for_each_pipe(pipe) {
-                       if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
+                       if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
                                drm_handle_vblank(dev, pipe);
 
                        if (pipe_stats[pipe] & PLANE_FLIPDONE_INT_STATUS_VLV) {
                                intel_prepare_page_flip(dev, pipe);
                                intel_finish_page_flip(dev, pipe);
                        }
+
+                       if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+                               i9xx_pipe_crc_irq_handler(dev, pipe);
                }
 
                /* Consume port.  Then clear IIR or we'll miss events */
@@ -1212,21 +1550,26 @@ static void ivb_err_int_handler(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 err_int = I915_READ(GEN7_ERR_INT);
+       enum pipe pipe;
 
        if (err_int & ERR_INT_POISON)
                DRM_ERROR("Poison interrupt\n");
 
-       if (err_int & ERR_INT_FIFO_UNDERRUN_A)
-               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false))
-                       DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n");
-
-       if (err_int & ERR_INT_FIFO_UNDERRUN_B)
-               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false))
-                       DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n");
+       for_each_pipe(pipe) {
+               if (err_int & ERR_INT_FIFO_UNDERRUN(pipe)) {
+                       if (intel_set_cpu_fifo_underrun_reporting(dev, pipe,
+                                                                 false))
+                               DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
+                                                pipe_name(pipe));
+               }
 
-       if (err_int & ERR_INT_FIFO_UNDERRUN_C)
-               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_C, false))
-                       DRM_DEBUG_DRIVER("Pipe C FIFO underrun\n");
+               if (err_int & ERR_INT_PIPE_CRC_DONE(pipe)) {
+                       if (IS_IVYBRIDGE(dev))
+                               ivb_pipe_crc_irq_handler(dev, pipe);
+                       else
+                               hsw_pipe_crc_irq_handler(dev, pipe);
+               }
+       }
 
        I915_WRITE(GEN7_ERR_INT, err_int);
 }
@@ -1297,6 +1640,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
 static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe;
 
        if (de_iir & DE_AUX_CHANNEL_A)
                dp_aux_irq_handler(dev);
@@ -1304,31 +1648,26 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
        if (de_iir & DE_GSE)
                intel_opregion_asle_intr(dev);
 
-       if (de_iir & DE_PIPEA_VBLANK)
-               drm_handle_vblank(dev, 0);
-
-       if (de_iir & DE_PIPEB_VBLANK)
-               drm_handle_vblank(dev, 1);
-
        if (de_iir & DE_POISON)
                DRM_ERROR("Poison interrupt\n");
 
-       if (de_iir & DE_PIPEA_FIFO_UNDERRUN)
-               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false))
-                       DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n");
+       for_each_pipe(pipe) {
+               if (de_iir & DE_PIPE_VBLANK(pipe))
+                       drm_handle_vblank(dev, pipe);
 
-       if (de_iir & DE_PIPEB_FIFO_UNDERRUN)
-               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false))
-                       DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n");
+               if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
+                       if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
+                               DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
+                                                pipe_name(pipe));
 
-       if (de_iir & DE_PLANEA_FLIP_DONE) {
-               intel_prepare_page_flip(dev, 0);
-               intel_finish_page_flip_plane(dev, 0);
-       }
+               if (de_iir & DE_PIPE_CRC_DONE(pipe))
+                       i9xx_pipe_crc_irq_handler(dev, pipe);
 
-       if (de_iir & DE_PLANEB_FLIP_DONE) {
-               intel_prepare_page_flip(dev, 1);
-               intel_finish_page_flip_plane(dev, 1);
+               /* plane/pipes map 1:1 on ilk+ */
+               if (de_iir & DE_PLANE_FLIP_DONE(pipe)) {
+                       intel_prepare_page_flip(dev, pipe);
+                       intel_finish_page_flip_plane(dev, pipe);
+               }
        }
 
        /* check event from PCH */
@@ -1351,7 +1690,7 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
 static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int i;
+       enum pipe i;
 
        if (de_iir & DE_ERR_INT_IVB)
                ivb_err_int_handler(dev);
@@ -1362,10 +1701,12 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
        if (de_iir & DE_GSE_IVB)
                intel_opregion_asle_intr(dev);
 
-       for (i = 0; i < 3; i++) {
-               if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
+       for_each_pipe(i) {
+               if (de_iir & (DE_PIPE_VBLANK_IVB(i)))
                        drm_handle_vblank(dev, i);
-               if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) {
+
+               /* plane/pipes map 1:1 on ilk+ */
+               if (de_iir & DE_PLANE_FLIP_DONE_IVB(i)) {
                        intel_prepare_page_flip(dev, i);
                        intel_finish_page_flip_plane(dev, i);
                }
@@ -1388,7 +1729,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 de_iir, gt_iir, de_ier, sde_ier = 0;
        irqreturn_t ret = IRQ_NONE;
-       bool err_int_reenable = false;
 
        atomic_inc(&dev_priv->irq_received);
 
@@ -1412,17 +1752,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
                POSTING_READ(SDEIER);
        }
 
-       /* On Haswell, also mask ERR_INT because we don't want to risk
-        * generating "unclaimed register" interrupts from inside the interrupt
-        * handler. */
-       if (IS_HASWELL(dev)) {
-               spin_lock(&dev_priv->irq_lock);
-               err_int_reenable = ~dev_priv->irq_mask & DE_ERR_INT_IVB;
-               if (err_int_reenable)
-                       ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
-               spin_unlock(&dev_priv->irq_lock);
-       }
-
        gt_iir = I915_READ(GTIIR);
        if (gt_iir) {
                if (INTEL_INFO(dev)->gen >= 6)
@@ -1452,13 +1781,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
                }
        }
 
-       if (err_int_reenable) {
-               spin_lock(&dev_priv->irq_lock);
-               if (ivb_can_enable_err_int(dev))
-                       ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
-               spin_unlock(&dev_priv->irq_lock);
-       }
-
        I915_WRITE(DEIER, de_ier);
        POSTING_READ(DEIER);
        if (!HAS_PCH_NOP(dev)) {
@@ -1469,6 +1791,117 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        return ret;
 }
 
+static irqreturn_t gen8_irq_handler(int irq, void *arg)
+{
+       struct drm_device *dev = arg;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 master_ctl;
+       irqreturn_t ret = IRQ_NONE;
+       uint32_t tmp = 0;
+       enum pipe pipe;
+
+       atomic_inc(&dev_priv->irq_received);
+
+       master_ctl = I915_READ(GEN8_MASTER_IRQ);
+       master_ctl &= ~GEN8_MASTER_IRQ_CONTROL;
+       if (!master_ctl)
+               return IRQ_NONE;
+
+       I915_WRITE(GEN8_MASTER_IRQ, 0);
+       POSTING_READ(GEN8_MASTER_IRQ);
+
+       ret = gen8_gt_irq_handler(dev, dev_priv, master_ctl);
+
+       if (master_ctl & GEN8_DE_MISC_IRQ) {
+               tmp = I915_READ(GEN8_DE_MISC_IIR);
+               if (tmp & GEN8_DE_MISC_GSE)
+                       intel_opregion_asle_intr(dev);
+               else if (tmp)
+                       DRM_ERROR("Unexpected DE Misc interrupt\n");
+               else
+                       DRM_ERROR("The master control interrupt lied (DE MISC)!\n");
+
+               if (tmp) {
+                       I915_WRITE(GEN8_DE_MISC_IIR, tmp);
+                       ret = IRQ_HANDLED;
+               }
+       }
+
+       if (master_ctl & GEN8_DE_PORT_IRQ) {
+               tmp = I915_READ(GEN8_DE_PORT_IIR);
+               if (tmp & GEN8_AUX_CHANNEL_A)
+                       dp_aux_irq_handler(dev);
+               else if (tmp)
+                       DRM_ERROR("Unexpected DE Port interrupt\n");
+               else
+                       DRM_ERROR("The master control interrupt lied (DE PORT)!\n");
+
+               if (tmp) {
+                       I915_WRITE(GEN8_DE_PORT_IIR, tmp);
+                       ret = IRQ_HANDLED;
+               }
+       }
+
+       for_each_pipe(pipe) {
+               uint32_t pipe_iir;
+
+               if (!(master_ctl & GEN8_DE_PIPE_IRQ(pipe)))
+                       continue;
+
+               pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
+               if (pipe_iir & GEN8_PIPE_VBLANK)
+                       drm_handle_vblank(dev, pipe);
+
+               if (pipe_iir & GEN8_PIPE_FLIP_DONE) {
+                       intel_prepare_page_flip(dev, pipe);
+                       intel_finish_page_flip_plane(dev, pipe);
+               }
+
+               if (pipe_iir & GEN8_PIPE_CDCLK_CRC_DONE)
+                       hsw_pipe_crc_irq_handler(dev, pipe);
+
+               if (pipe_iir & GEN8_PIPE_FIFO_UNDERRUN) {
+                       if (intel_set_cpu_fifo_underrun_reporting(dev, pipe,
+                                                                 false))
+                               DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
+                                                pipe_name(pipe));
+               }
+
+               if (pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS) {
+                       DRM_ERROR("Fault errors on pipe %c\n: 0x%08x",
+                                 pipe_name(pipe),
+                                 pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS);
+               }
+
+               if (pipe_iir) {
+                       ret = IRQ_HANDLED;
+                       I915_WRITE(GEN8_DE_PIPE_IIR(pipe), pipe_iir);
+               } else
+                       DRM_ERROR("The master control interrupt lied (DE PIPE)!\n");
+       }
+
+       if (!HAS_PCH_NOP(dev) && master_ctl & GEN8_DE_PCH_IRQ) {
+               /*
+                * FIXME(BDW): Assume for now that the new interrupt handling
+                * scheme also closed the SDE interrupt handling race we've seen
+                * on older pch-split platforms. But this needs testing.
+                */
+               u32 pch_iir = I915_READ(SDEIIR);
+
+               cpt_irq_handler(dev, pch_iir);
+
+               if (pch_iir) {
+                       I915_WRITE(SDEIIR, pch_iir);
+                       ret = IRQ_HANDLED;
+               }
+       }
+
+       I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
+       POSTING_READ(GEN8_MASTER_IRQ);
+
+       return ret;
+}
+
 static void i915_error_wake_up(struct drm_i915_private *dev_priv,
                               bool reset_completed)
 {
@@ -1516,7 +1949,7 @@ static void i915_error_work_func(struct work_struct *work)
        char *reset_done_event[] = { I915_ERROR_UEVENT "=0", NULL };
        int ret;
 
-       kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
+       kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, error_event);
 
        /*
         * Note that there's only one work item which does gpu resets, so we
@@ -1530,7 +1963,7 @@ static void i915_error_work_func(struct work_struct *work)
         */
        if (i915_reset_in_progress(error) && !i915_terminally_wedged(error)) {
                DRM_DEBUG_DRIVER("resetting chip\n");
-               kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE,
+               kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE,
                                   reset_event);
 
                /*
@@ -1557,7 +1990,7 @@ static void i915_error_work_func(struct work_struct *work)
                        smp_mb__before_atomic_inc();
                        atomic_inc(&dev_priv->gpu_error.reset_counter);
 
-                       kobject_uevent_env(&dev->primary->kdev.kobj,
+                       kobject_uevent_env(&dev->primary->kdev->kobj,
                                           KOBJ_CHANGE, reset_done_event);
                } else {
                        atomic_set(&error->reset_counter, I915_WEDGED);
@@ -1787,7 +2220,7 @@ static int ironlake_enable_vblank(struct drm_device *dev, int pipe)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        unsigned long irqflags;
        uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) :
-                                                    DE_PIPE_VBLANK_ILK(pipe);
+                                                    DE_PIPE_VBLANK(pipe);
 
        if (!i915_pipe_enabled(dev, pipe))
                return -EINVAL;
@@ -1810,7 +2243,7 @@ static int valleyview_enable_vblank(struct drm_device *dev, int pipe)
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
        imr = I915_READ(VLV_IMR);
-       if (pipe == 0)
+       if (pipe == PIPE_A)
                imr &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
        else
                imr &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
@@ -1822,6 +2255,22 @@ static int valleyview_enable_vblank(struct drm_device *dev, int pipe)
        return 0;
 }
 
+static int gen8_enable_vblank(struct drm_device *dev, int pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long irqflags;
+
+       if (!i915_pipe_enabled(dev, pipe))
+               return -EINVAL;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       dev_priv->de_irq_mask[pipe] &= ~GEN8_PIPE_VBLANK;
+       I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
+       POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+       return 0;
+}
+
 /* Called from drm generic code, passed 'crtc' which
  * we use as a pipe index
  */
@@ -1845,7 +2294,7 @@ static void ironlake_disable_vblank(struct drm_device *dev, int pipe)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        unsigned long irqflags;
        uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) :
-                                                    DE_PIPE_VBLANK_ILK(pipe);
+                                                    DE_PIPE_VBLANK(pipe);
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
        ironlake_disable_display_irq(dev_priv, bit);
@@ -1862,7 +2311,7 @@ static void valleyview_disable_vblank(struct drm_device *dev, int pipe)
        i915_disable_pipestat(dev_priv, pipe,
                              PIPE_START_VBLANK_INTERRUPT_ENABLE);
        imr = I915_READ(VLV_IMR);
-       if (pipe == 0)
+       if (pipe == PIPE_A)
                imr |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
        else
                imr |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
@@ -1870,6 +2319,21 @@ static void valleyview_disable_vblank(struct drm_device *dev, int pipe)
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
 
+static void gen8_disable_vblank(struct drm_device *dev, int pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long irqflags;
+
+       if (!i915_pipe_enabled(dev, pipe))
+               return;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       dev_priv->de_irq_mask[pipe] |= GEN8_PIPE_VBLANK;
+       I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
+       POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+}
+
 static u32
 ring_last_seqno(struct intel_ring_buffer *ring)
 {
@@ -1965,6 +2429,7 @@ ring_stuck(struct intel_ring_buffer *ring, u32 acthd)
        if (tmp & RING_WAIT) {
                DRM_ERROR("Kicking stuck wait on %s\n",
                          ring->name);
+               i915_handle_error(dev, false);
                I915_WRITE_CTL(ring, tmp);
                return HANGCHECK_KICK;
        }
@@ -1976,6 +2441,7 @@ ring_stuck(struct intel_ring_buffer *ring, u32 acthd)
                case 1:
                        DRM_ERROR("Kicking stuck semaphore on %s\n",
                                  ring->name);
+                       i915_handle_error(dev, false);
                        I915_WRITE_CTL(ring, tmp);
                        return HANGCHECK_KICK;
                case 0:
@@ -2021,12 +2487,21 @@ static void i915_hangcheck_elapsed(unsigned long data)
 
                if (ring->hangcheck.seqno == seqno) {
                        if (ring_idle(ring, seqno)) {
+                               ring->hangcheck.action = HANGCHECK_IDLE;
+
                                if (waitqueue_active(&ring->irq_queue)) {
                                        /* Issue a wake-up to catch stuck h/w. */
-                                       DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
-                                                 ring->name);
-                                       wake_up_all(&ring->irq_queue);
-                                       ring->hangcheck.score += HUNG;
+                                       if (!test_and_set_bit(ring->id, &dev_priv->gpu_error.missed_irq_rings)) {
+                                               if (!(dev_priv->gpu_error.test_irq_rings & intel_ring_flag(ring)))
+                                                       DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
+                                                                 ring->name);
+                                               else
+                                                       DRM_INFO("Fake missed irq on %s\n",
+                                                                ring->name);
+                                               wake_up_all(&ring->irq_queue);
+                                       }
+                                       /* Safeguard against driver failure */
+                                       ring->hangcheck.score += BUSY;
                                } else
                                        busy = false;
                        } else {
@@ -2049,6 +2524,7 @@ static void i915_hangcheck_elapsed(unsigned long data)
                                                                    acthd);
 
                                switch (ring->hangcheck.action) {
+                               case HANGCHECK_IDLE:
                                case HANGCHECK_WAIT:
                                        break;
                                case HANGCHECK_ACTIVE:
@@ -2064,6 +2540,8 @@ static void i915_hangcheck_elapsed(unsigned long data)
                                }
                        }
                } else {
+                       ring->hangcheck.action = HANGCHECK_ACTIVE;
+
                        /* Gradually reduce the count so that we catch DoS
                         * attempts across multiple batches.
                         */
@@ -2190,6 +2668,53 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
        POSTING_READ(VLV_IER);
 }
 
+static void gen8_irq_preinstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe;
+
+       atomic_set(&dev_priv->irq_received, 0);
+
+       I915_WRITE(GEN8_MASTER_IRQ, 0);
+       POSTING_READ(GEN8_MASTER_IRQ);
+
+       /* IIR can theoretically queue up two events. Be paranoid */
+#define GEN8_IRQ_INIT_NDX(type, which) do { \
+               I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
+               POSTING_READ(GEN8_##type##_IMR(which)); \
+               I915_WRITE(GEN8_##type##_IER(which), 0); \
+               I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+               POSTING_READ(GEN8_##type##_IIR(which)); \
+               I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+       } while (0)
+
+#define GEN8_IRQ_INIT(type) do { \
+               I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
+               POSTING_READ(GEN8_##type##_IMR); \
+               I915_WRITE(GEN8_##type##_IER, 0); \
+               I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
+               POSTING_READ(GEN8_##type##_IIR); \
+               I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
+       } while (0)
+
+       GEN8_IRQ_INIT_NDX(GT, 0);
+       GEN8_IRQ_INIT_NDX(GT, 1);
+       GEN8_IRQ_INIT_NDX(GT, 2);
+       GEN8_IRQ_INIT_NDX(GT, 3);
+
+       for_each_pipe(pipe) {
+               GEN8_IRQ_INIT_NDX(DE_PIPE, pipe);
+       }
+
+       GEN8_IRQ_INIT(DE_PORT);
+       GEN8_IRQ_INIT(DE_MISC);
+       GEN8_IRQ_INIT(PCU);
+#undef GEN8_IRQ_INIT
+#undef GEN8_IRQ_INIT_NDX
+
+       POSTING_READ(GEN8_PCU_IIR);
+}
+
 static void ibx_hpd_irq_setup(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -2254,10 +2779,10 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
        pm_irqs = gt_irqs = 0;
 
        dev_priv->gt_irq_mask = ~0;
-       if (HAS_L3_GPU_CACHE(dev)) {
+       if (HAS_L3_DPF(dev)) {
                /* L3 parity interrupt is always unmasked. */
-               dev_priv->gt_irq_mask = ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
-               gt_irqs |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
+               dev_priv->gt_irq_mask = ~GT_PARITY_ERROR(dev);
+               gt_irqs |= GT_PARITY_ERROR(dev);
        }
 
        gt_irqs |= GT_RENDER_USER_INTERRUPT;
@@ -2306,8 +2831,10 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
        } else {
                display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
                                DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
-                               DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN |
-                               DE_PIPEA_FIFO_UNDERRUN | DE_POISON);
+                               DE_AUX_CHANNEL_A |
+                               DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN |
+                               DE_PIPEB_CRC_DONE | DE_PIPEA_CRC_DONE |
+                               DE_POISON);
                extra_mask = DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT;
        }
 
@@ -2341,7 +2868,8 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 enable_mask;
-       u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV;
+       u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV |
+               PIPE_CRC_DONE_ENABLE;
        unsigned long irqflags;
 
        enable_mask = I915_DISPLAY_PORT_INTERRUPT;
@@ -2371,9 +2899,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        /* Interrupt setup is already guaranteed to be single-threaded, this is
         * just to make the assert_spin_locked check happy. */
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-       i915_enable_pipestat(dev_priv, 0, pipestat_enable);
-       i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
-       i915_enable_pipestat(dev_priv, 1, pipestat_enable);
+       i915_enable_pipestat(dev_priv, PIPE_A, pipestat_enable);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_EVENT_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_B, pipestat_enable);
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
        I915_WRITE(VLV_IIR, 0xffffffff);
@@ -2392,6 +2920,117 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        return 0;
 }
 
+static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv)
+{
+       int i;
+
+       /* These are interrupts we'll toggle with the ring mask register */
+       uint32_t gt_interrupts[] = {
+               GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
+                       GT_RENDER_L3_PARITY_ERROR_INTERRUPT |
+                       GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT,
+               GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT |
+                       GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT,
+               0,
+               GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT
+               };
+
+       for (i = 0; i < ARRAY_SIZE(gt_interrupts); i++) {
+               u32 tmp = I915_READ(GEN8_GT_IIR(i));
+               if (tmp)
+                       DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n",
+                                 i, tmp);
+               I915_WRITE(GEN8_GT_IMR(i), ~gt_interrupts[i]);
+               I915_WRITE(GEN8_GT_IER(i), gt_interrupts[i]);
+       }
+       POSTING_READ(GEN8_GT_IER(0));
+}
+
+static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       uint32_t de_pipe_masked = GEN8_PIPE_FLIP_DONE |
+               GEN8_PIPE_CDCLK_CRC_DONE |
+               GEN8_PIPE_FIFO_UNDERRUN |
+               GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
+       uint32_t de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK;
+       int pipe;
+       dev_priv->de_irq_mask[PIPE_A] = ~de_pipe_masked;
+       dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_masked;
+       dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_masked;
+
+       for_each_pipe(pipe) {
+               u32 tmp = I915_READ(GEN8_DE_PIPE_IIR(pipe));
+               if (tmp)
+                       DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n",
+                                 pipe, tmp);
+               I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
+               I915_WRITE(GEN8_DE_PIPE_IER(pipe), de_pipe_enables);
+       }
+       POSTING_READ(GEN8_DE_PIPE_ISR(0));
+
+       I915_WRITE(GEN8_DE_PORT_IMR, ~GEN8_AUX_CHANNEL_A);
+       I915_WRITE(GEN8_DE_PORT_IER, GEN8_AUX_CHANNEL_A);
+       POSTING_READ(GEN8_DE_PORT_IER);
+}
+
+static int gen8_irq_postinstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       gen8_gt_irq_postinstall(dev_priv);
+       gen8_de_irq_postinstall(dev_priv);
+
+       ibx_irq_postinstall(dev);
+
+       I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL);
+       POSTING_READ(GEN8_MASTER_IRQ);
+
+       return 0;
+}
+
+static void gen8_irq_uninstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe;
+
+       if (!dev_priv)
+               return;
+
+       atomic_set(&dev_priv->irq_received, 0);
+
+       I915_WRITE(GEN8_MASTER_IRQ, 0);
+
+#define GEN8_IRQ_FINI_NDX(type, which) do { \
+               I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
+               I915_WRITE(GEN8_##type##_IER(which), 0); \
+               I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+       } while (0)
+
+#define GEN8_IRQ_FINI(type) do { \
+               I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
+               I915_WRITE(GEN8_##type##_IER, 0); \
+               I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
+       } while (0)
+
+       GEN8_IRQ_FINI_NDX(GT, 0);
+       GEN8_IRQ_FINI_NDX(GT, 1);
+       GEN8_IRQ_FINI_NDX(GT, 2);
+       GEN8_IRQ_FINI_NDX(GT, 3);
+
+       for_each_pipe(pipe) {
+               GEN8_IRQ_FINI_NDX(DE_PIPE, pipe);
+       }
+
+       GEN8_IRQ_FINI(DE_PORT);
+       GEN8_IRQ_FINI(DE_MISC);
+       GEN8_IRQ_FINI(PCU);
+#undef GEN8_IRQ_FINI
+#undef GEN8_IRQ_FINI_NDX
+
+       POSTING_READ(GEN8_PCU_IIR);
+}
+
 static void valleyview_irq_uninstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -2464,6 +3103,7 @@ static void i8xx_irq_preinstall(struct drm_device * dev)
 static int i8xx_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       unsigned long irqflags;
 
        I915_WRITE16(EMR,
                     ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
@@ -2484,6 +3124,13 @@ static int i8xx_irq_postinstall(struct drm_device *dev)
                     I915_USER_INTERRUPT);
        POSTING_READ16(IER);
 
+       /* Interrupt setup is already guaranteed to be single-threaded, this is
+        * just to make the assert_spin_locked check happy. */
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
        return 0;
 }
 
@@ -2570,13 +3217,14 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
                if (iir & I915_USER_INTERRUPT)
                        notify_ring(dev, &dev_priv->ring[RCS]);
 
-               if (pipe_stats[0] & PIPE_VBLANK_INTERRUPT_STATUS &&
-                   i8xx_handle_vblank(dev, 0, iir))
-                       flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(0);
+               for_each_pipe(pipe) {
+                       if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS &&
+                           i8xx_handle_vblank(dev, pipe, iir))
+                               flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe);
 
-               if (pipe_stats[1] & PIPE_VBLANK_INTERRUPT_STATUS &&
-                   i8xx_handle_vblank(dev, 1, iir))
-                       flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(1);
+                       if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+                               i9xx_pipe_crc_irq_handler(dev, pipe);
+               }
 
                iir = new_iir;
        }
@@ -2623,6 +3271,7 @@ static int i915_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 enable_mask;
+       unsigned long irqflags;
 
        I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
 
@@ -2658,6 +3307,13 @@ static int i915_irq_postinstall(struct drm_device *dev)
 
        i915_enable_asle_pipestat(dev);
 
+       /* Interrupt setup is already guaranteed to be single-threaded, this is
+        * just to make the assert_spin_locked check happy. */
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
        return 0;
 }
 
@@ -2769,6 +3425,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
 
                        if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
                                blc_event = true;
+
+                       if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+                               i9xx_pipe_crc_irq_handler(dev, pipe);
                }
 
                if (blc_event || (iir & I915_ASLE_INTERRUPT))
@@ -2867,7 +3526,9 @@ static int i965_irq_postinstall(struct drm_device *dev)
        /* Interrupt setup is already guaranteed to be single-threaded, this is
         * just to make the assert_spin_locked check happy. */
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-       i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_EVENT_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
        /*
@@ -3013,6 +3674,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
 
                        if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
                                blc_event = true;
+
+                       if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+                               i9xx_pipe_crc_irq_handler(dev, pipe);
                }
 
 
@@ -3122,18 +3786,21 @@ void intel_irq_init(struct drm_device *dev)
 
        pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
 
-       dev->driver->get_vblank_counter = i915_get_vblank_counter;
-       dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
-       if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
+       if (IS_GEN2(dev)) {
+               dev->max_vblank_count = 0;
+               dev->driver->get_vblank_counter = i8xx_get_vblank_counter;
+       } else if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
                dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
                dev->driver->get_vblank_counter = gm45_get_vblank_counter;
+       } else {
+               dev->driver->get_vblank_counter = i915_get_vblank_counter;
+               dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
        }
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
                dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
-       else
-               dev->driver->get_vblank_timestamp = NULL;
-       dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
+               dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
+       }
 
        if (IS_VALLEYVIEW(dev)) {
                dev->driver->irq_handler = valleyview_irq_handler;
@@ -3143,6 +3810,14 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->enable_vblank = valleyview_enable_vblank;
                dev->driver->disable_vblank = valleyview_disable_vblank;
                dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
+       } else if (IS_GEN8(dev)) {
+               dev->driver->irq_handler = gen8_irq_handler;
+               dev->driver->irq_preinstall = gen8_irq_preinstall;
+               dev->driver->irq_postinstall = gen8_irq_postinstall;
+               dev->driver->irq_uninstall = gen8_irq_uninstall;
+               dev->driver->enable_vblank = gen8_enable_vblank;
+               dev->driver->disable_vblank = gen8_disable_vblank;
+               dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup;
        } else if (HAS_PCH_SPLIT(dev)) {
                dev->driver->irq_handler = ironlake_irq_handler;
                dev->driver->irq_preinstall = ironlake_irq_preinstall;
index ef9b354..f9eafb6 100644 (file)
@@ -26,6 +26,7 @@
 #define _I915_REG_H_
 
 #define _PIPE(pipe, a, b) ((a) + (pipe)*((b)-(a)))
+#define _PIPE_INC(pipe, base, inc) ((base) + (pipe)*(inc))
 #define _TRANSCODER(tran, a, b) ((a) + (tran)*((b)-(a)))
 
 #define _PORT(port, a, b) ((a) + (port)*((b)-(a)))
 #define RING_PP_DIR_DCLV(ring)         ((ring)->mmio_base+0x220)
 #define   PP_DIR_DCLV_2G               0xffffffff
 
+#define GEN8_RING_PDP_UDW(ring, n)     ((ring)->mmio_base+0x270 + ((n) * 8 + 4))
+#define GEN8_RING_PDP_LDW(ring, n)     ((ring)->mmio_base+0x270 + (n) * 8)
+
 #define GAM_ECOCHK                     0x4090
 #define   ECOCHK_SNB_BIT               (1<<10)
 #define   HSW_ECOCHK_ARB_PRIO_SOL      (1<<6)
 #define   MI_BATCH_NON_SECURE_HSW      (1<<13)
 #define MI_BATCH_BUFFER_START  MI_INSTR(0x31, 0)
 #define   MI_BATCH_GTT             (2<<6) /* aliased with (1<<7) on gen4 */
+#define MI_BATCH_BUFFER_START_GEN8     MI_INSTR(0x31, 1)
 #define MI_SEMAPHORE_MBOX      MI_INSTR(0x16, 1) /* gen6+ */
 #define  MI_SEMAPHORE_GLOBAL_GTT    (1<<22)
 #define  MI_SEMAPHORE_UPDATE       (1<<21)
 #define  MI_SEMAPHORE_SYNC_VVE     (1<<16) /* VECS wait for VCS  (VEVSYNC) */
 #define  MI_SEMAPHORE_SYNC_RVE     (2<<16) /* VECS wait for RCS  (VERSYNC) */
 #define  MI_SEMAPHORE_SYNC_INVALID  (3<<16)
+
+#define MI_PREDICATE_RESULT_2  (0x2214)
+#define  LOWER_SLICE_ENABLED   (1<<0)
+#define  LOWER_SLICE_DISABLED  (0<<0)
+
 /*
  * 3D instructions used by the kernel
  */
 #define   IOSF_PORT_PUNIT                      0x4
 #define   IOSF_PORT_NC                         0x11
 #define   IOSF_PORT_DPIO                       0x12
+#define   IOSF_PORT_GPIO_NC                    0x13
+#define   IOSF_PORT_CCK                                0x14
+#define   IOSF_PORT_CCU                                0xA9
+#define   IOSF_PORT_GPS_CORE                   0x48
 #define VLV_IOSF_DATA                          (VLV_DISPLAY_BASE + 0x2104)
 #define VLV_IOSF_ADDR                          (VLV_DISPLAY_BASE + 0x2108)
 
 #define PUNIT_OPCODE_REG_READ                  6
 #define PUNIT_OPCODE_REG_WRITE                 7
 
+#define PUNIT_REG_PWRGT_CTRL                   0x60
+#define PUNIT_REG_PWRGT_STATUS                 0x61
+#define          PUNIT_CLK_GATE                        1
+#define          PUNIT_PWR_RESET                       2
+#define          PUNIT_PWR_GATE                        3
+#define          RENDER_PWRGT                          (PUNIT_PWR_GATE << 0)
+#define          MEDIA_PWRGT                           (PUNIT_PWR_GATE << 2)
+#define          DISP2D_PWRGT                          (PUNIT_PWR_GATE << 6)
+
 #define PUNIT_REG_GPU_LFM                      0xd3
 #define PUNIT_REG_GPU_FREQ_REQ                 0xd4
 #define PUNIT_REG_GPU_FREQ_STS                 0xd8
 #define   FB_FMAX_VMIN_FREQ_LO_SHIFT           27
 #define   FB_FMAX_VMIN_FREQ_LO_MASK            0xf8000000
 
+/* vlv2 north clock has */
+#define CCK_FUSE_REG                           0x8
+#define  CCK_FUSE_HPLL_FREQ_MASK               0x3
+#define CCK_REG_DSI_PLL_FUSE                   0x44
+#define CCK_REG_DSI_PLL_CONTROL                        0x48
+#define  DSI_PLL_VCO_EN                                (1 << 31)
+#define  DSI_PLL_LDO_GATE                      (1 << 30)
+#define  DSI_PLL_P1_POST_DIV_SHIFT             17
+#define  DSI_PLL_P1_POST_DIV_MASK              (0x1ff << 17)
+#define  DSI_PLL_P2_MUX_DSI0_DIV2              (1 << 13)
+#define  DSI_PLL_P3_MUX_DSI1_DIV2              (1 << 12)
+#define  DSI_PLL_MUX_MASK                      (3 << 9)
+#define  DSI_PLL_MUX_DSI0_DSIPLL               (0 << 10)
+#define  DSI_PLL_MUX_DSI0_CCK                  (1 << 10)
+#define  DSI_PLL_MUX_DSI1_DSIPLL               (0 << 9)
+#define  DSI_PLL_MUX_DSI1_CCK                  (1 << 9)
+#define  DSI_PLL_CLK_GATE_MASK                 (0xf << 5)
+#define  DSI_PLL_CLK_GATE_DSI0_DSIPLL          (1 << 8)
+#define  DSI_PLL_CLK_GATE_DSI1_DSIPLL          (1 << 7)
+#define  DSI_PLL_CLK_GATE_DSI0_CCK             (1 << 6)
+#define  DSI_PLL_CLK_GATE_DSI1_CCK             (1 << 5)
+#define  DSI_PLL_LOCK                          (1 << 0)
+#define CCK_REG_DSI_PLL_DIVIDER                        0x4c
+#define  DSI_PLL_LFSR                          (1 << 31)
+#define  DSI_PLL_FRACTION_EN                   (1 << 30)
+#define  DSI_PLL_FRAC_COUNTER_SHIFT            27
+#define  DSI_PLL_FRAC_COUNTER_MASK             (7 << 27)
+#define  DSI_PLL_USYNC_CNT_SHIFT               18
+#define  DSI_PLL_USYNC_CNT_MASK                        (0x1ff << 18)
+#define  DSI_PLL_N1_DIV_SHIFT                  16
+#define  DSI_PLL_N1_DIV_MASK                   (3 << 16)
+#define  DSI_PLL_M1_DIV_SHIFT                  0
+#define  DSI_PLL_M1_DIV_MASK                   (0x1ff << 0)
+
 /*
  * DPIO - a special bus for various display related registers to hide behind
  *
 #define  DPIO_MODSEL1                  (1<<3) /* if ref clk b == 27 */
 #define  DPIO_MODSEL0                  (1<<2) /* if ref clk a == 27 */
 #define  DPIO_SFR_BYPASS               (1<<1)
-#define  DPIO_RESET                    (1<<0)
+#define  DPIO_CMNRST                   (1<<0)
 
 #define _DPIO_TX3_SWING_CTL4_A         0x690
 #define _DPIO_TX3_SWING_CTL4_B         0x2a90
-#define DPIO_TX3_SWING_CTL4(pipe) _PIPE(pipe, _DPIO_TX_SWING_CTL4_A, \
+#define DPIO_TX3_SWING_CTL4(pipe) _PIPE(pipe, _DPIO_TX3_SWING_CTL4_A, \
                                        _DPIO_TX3_SWING_CTL4_B)
 
 /*
 #define ARB_MODE               0x04030
 #define   ARB_MODE_SWIZZLE_SNB (1<<4)
 #define   ARB_MODE_SWIZZLE_IVB (1<<5)
+#define GAMTARBMODE            0x04a08
+#define   ARB_MODE_BWGTLB_DISABLE (1<<9)
+#define   ARB_MODE_SWIZZLE_BDW (1<<1)
 #define RENDER_HWS_PGA_GEN7    (0x04080)
 #define RING_FAULT_REG(ring)   (0x4094 + 0x100*(ring)->id)
 #define   RING_FAULT_GTTSEL_MASK (1<<11)
 #define   RING_FAULT_FAULT_TYPE(x) ((x >> 1) & 0x3)
 #define   RING_FAULT_VALID     (1<<0)
 #define DONE_REG               0x40b0
+#define GEN8_PRIVATE_PAT       0x40e0
 #define BSD_HWS_PGA_GEN7       (0x04180)
 #define BLT_HWS_PGA_GEN7       (0x04280)
 #define VEBOX_HWS_PGA_GEN7     (0x04380)
 #define NOPID          0x02094
 #define HWSTAM         0x02098
 #define DMA_FADD_I8XX  0x020d0
+#define RING_BBSTATE(base)     ((base)+0x110)
 
 #define ERROR_GEN6     0x040a0
 #define GEN7_ERR_INT   0x44040
 #define   ERR_INT_POISON               (1<<31)
 #define   ERR_INT_MMIO_UNCLAIMED       (1<<13)
+#define   ERR_INT_PIPE_CRC_DONE_C      (1<<8)
 #define   ERR_INT_FIFO_UNDERRUN_C      (1<<6)
+#define   ERR_INT_PIPE_CRC_DONE_B      (1<<5)
 #define   ERR_INT_FIFO_UNDERRUN_B      (1<<3)
+#define   ERR_INT_PIPE_CRC_DONE_A      (1<<2)
+#define   ERR_INT_PIPE_CRC_DONE(pipe)  (1<<(2 + pipe*3))
 #define   ERR_INT_FIFO_UNDERRUN_A      (1<<0)
 #define   ERR_INT_FIFO_UNDERRUN(pipe)  (1<<(pipe*3))
 
 #define   FPGA_DBG_RM_NOCLAIM  (1<<31)
 
 #define DERRMR         0x44050
+/* Note that HBLANK events are reserved on bdw+ */
 #define   DERRMR_PIPEA_SCANLINE                (1<<0)
 #define   DERRMR_PIPEA_PRI_FLIP_DONE   (1<<1)
 #define   DERRMR_PIPEA_SPR_FLIP_DONE   (1<<2)
 #define _3D_CHICKEN3   0x02090
 #define  _3D_CHICKEN_SF_DISABLE_OBJEND_CULL            (1 << 10)
 #define  _3D_CHICKEN3_SF_DISABLE_FASTCLIP_CULL         (1 << 5)
+#define  _3D_CHICKEN_SDE_LIMIT_FIFO_POLY_DEPTH(x)      ((x)<<1)
 
 #define MI_MODE                0x0209c
 # define VS_TIMER_DISPATCH                             (1 << 6)
 #define GT_BLT_USER_INTERRUPT                  (1 << 22)
 #define GT_BSD_CS_ERROR_INTERRUPT              (1 << 15)
 #define GT_BSD_USER_INTERRUPT                  (1 << 12)
+#define GT_RENDER_L3_PARITY_ERROR_INTERRUPT_S1 (1 << 11) /* hsw+; rsvd on snb, ivb, vlv */
 #define GT_RENDER_L3_PARITY_ERROR_INTERRUPT    (1 <<  5) /* !snb */
 #define GT_RENDER_PIPECTL_NOTIFY_INTERRUPT     (1 <<  4)
 #define GT_RENDER_CS_MASTER_ERROR_INTERRUPT    (1 <<  3)
 #define PM_VEBOX_CS_ERROR_INTERRUPT            (1 << 12) /* hsw+ */
 #define PM_VEBOX_USER_INTERRUPT                        (1 << 10) /* hsw+ */
 
+#define GT_PARITY_ERROR(dev) \
+       (GT_RENDER_L3_PARITY_ERROR_INTERRUPT | \
+        (IS_HASWELL(dev) ? GT_RENDER_L3_PARITY_ERROR_INTERRUPT_S1 : 0))
+
 /* These are all the "old" interrupts */
 #define ILK_BSD_USER_INTERRUPT                         (1<<5)
 #define I915_PIPE_CONTROL_NOTIFY_INTERRUPT             (1<<18)
                                             _HSW_PIPE_SLICE_CHICKEN_1_A, + \
                                             _HSW_PIPE_SLICE_CHICKEN_1_B)
 
-#define HSW_CLKGATE_DISABLE_PART_1     0x46500
-#define   HSW_DPFC_GATING_DISABLE      (1<<23)
-
 /*
  * GPIO regs
  */
 
 #define MI_ARB_VLV             (VLV_DISPLAY_BASE + 0x6504)
 
+#define CZCLK_CDCLK_FREQ_RATIO (VLV_DISPLAY_BASE + 0x6508)
+#define   CDCLK_FREQ_SHIFT     4
+#define   CDCLK_FREQ_MASK      (0x1f << CDCLK_FREQ_SHIFT)
+#define   CZCLK_FREQ_MASK      0xf
+#define GMBUSFREQ_VLV          (VLV_DISPLAY_BASE + 0x6510)
+
 /*
  * Palette regs
  */
  * device 0 function 0's pci config register 0x44 or 0x48 and matches it in
  * every way.  It is not accessible from the CP register read instructions.
  *
+ * Starting from Haswell, you can't write registers using the MCHBAR mirror,
+ * just read.
  */
 #define MCHBAR_MIRROR_BASE     0x10000
 
 #define MCHBAR_MIRROR_BASE_SNB 0x140000
 
 /* Memory controller frequency in MCHBAR for Haswell (possible SNB+) */
-#define DCLK 0x5e04
+#define DCLK (MCHBAR_MIRROR_BASE_SNB + 0x5e04)
 
 /** 915-945 and GM965 MCH register controlling DRAM channel access */
 #define DCC                    0x10200
 #define GEN6_GT_THREAD_STATUS_CORE_MASK 0x7
 #define GEN6_GT_THREAD_STATUS_CORE_MASK_HSW (0x7 | (0x07 << 16))
 
-#define GEN6_GT_PERF_STATUS    0x145948
-#define GEN6_RP_STATE_LIMITS   0x145994
-#define GEN6_RP_STATE_CAP      0x145998
+#define GEN6_GT_PERF_STATUS    (MCHBAR_MIRROR_BASE_SNB + 0x5948)
+#define GEN6_RP_STATE_LIMITS   (MCHBAR_MIRROR_BASE_SNB + 0x5994)
+#define GEN6_RP_STATE_CAP      (MCHBAR_MIRROR_BASE_SNB + 0x5998)
 
 /*
  * Logical Context regs
  * on HSW) - so the final size is 66944 bytes, which rounds to 17 pages.
  */
 #define HSW_CXT_TOTAL_SIZE             (17 * PAGE_SIZE)
+/* Same as Haswell, but 72064 bytes now. */
+#define GEN8_CXT_TOTAL_SIZE            (18 * PAGE_SIZE)
+
+
+#define VLV_CLK_CTL2                   0x101104
+#define   CLK_CTL2_CZCOUNT_30NS_SHIFT  28
 
 /*
  * Overlay regs
  * Display engine regs
  */
 
+/* Pipe A CRC regs */
+#define _PIPE_CRC_CTL_A                (dev_priv->info->display_mmio_offset + 0x60050)
+#define   PIPE_CRC_ENABLE              (1 << 31)
+/* ivb+ source selection */
+#define   PIPE_CRC_SOURCE_PRIMARY_IVB  (0 << 29)
+#define   PIPE_CRC_SOURCE_SPRITE_IVB   (1 << 29)
+#define   PIPE_CRC_SOURCE_PF_IVB       (2 << 29)
+/* ilk+ source selection */
+#define   PIPE_CRC_SOURCE_PRIMARY_ILK  (0 << 28)
+#define   PIPE_CRC_SOURCE_SPRITE_ILK   (1 << 28)
+#define   PIPE_CRC_SOURCE_PIPE_ILK     (2 << 28)
+/* embedded DP port on the north display block, reserved on ivb */
+#define   PIPE_CRC_SOURCE_PORT_A_ILK   (4 << 28)
+#define   PIPE_CRC_SOURCE_FDI_ILK      (5 << 28) /* reserved on ivb */
+/* vlv source selection */
+#define   PIPE_CRC_SOURCE_PIPE_VLV     (0 << 27)
+#define   PIPE_CRC_SOURCE_HDMIB_VLV    (1 << 27)
+#define   PIPE_CRC_SOURCE_HDMIC_VLV    (2 << 27)
+/* with DP port the pipe source is invalid */
+#define   PIPE_CRC_SOURCE_DP_D_VLV     (3 << 27)
+#define   PIPE_CRC_SOURCE_DP_B_VLV     (6 << 27)
+#define   PIPE_CRC_SOURCE_DP_C_VLV     (7 << 27)
+/* gen3+ source selection */
+#define   PIPE_CRC_SOURCE_PIPE_I9XX    (0 << 28)
+#define   PIPE_CRC_SOURCE_SDVOB_I9XX   (1 << 28)
+#define   PIPE_CRC_SOURCE_SDVOC_I9XX   (2 << 28)
+/* with DP/TV port the pipe source is invalid */
+#define   PIPE_CRC_SOURCE_DP_D_G4X     (3 << 28)
+#define   PIPE_CRC_SOURCE_TV_PRE       (4 << 28)
+#define   PIPE_CRC_SOURCE_TV_POST      (5 << 28)
+#define   PIPE_CRC_SOURCE_DP_B_G4X     (6 << 28)
+#define   PIPE_CRC_SOURCE_DP_C_G4X     (7 << 28)
+/* gen2 doesn't have source selection bits */
+#define   PIPE_CRC_INCLUDE_BORDER_I8XX (1 << 30)
+
+#define _PIPE_CRC_RES_1_A_IVB          0x60064
+#define _PIPE_CRC_RES_2_A_IVB          0x60068
+#define _PIPE_CRC_RES_3_A_IVB          0x6006c
+#define _PIPE_CRC_RES_4_A_IVB          0x60070
+#define _PIPE_CRC_RES_5_A_IVB          0x60074
+
+#define _PIPE_CRC_RES_RED_A            (dev_priv->info->display_mmio_offset + 0x60060)
+#define _PIPE_CRC_RES_GREEN_A          (dev_priv->info->display_mmio_offset + 0x60064)
+#define _PIPE_CRC_RES_BLUE_A           (dev_priv->info->display_mmio_offset + 0x60068)
+#define _PIPE_CRC_RES_RES1_A_I915      (dev_priv->info->display_mmio_offset + 0x6006c)
+#define _PIPE_CRC_RES_RES2_A_G4X       (dev_priv->info->display_mmio_offset + 0x60080)
+
+/* Pipe B CRC regs */
+#define _PIPE_CRC_RES_1_B_IVB          0x61064
+#define _PIPE_CRC_RES_2_B_IVB          0x61068
+#define _PIPE_CRC_RES_3_B_IVB          0x6106c
+#define _PIPE_CRC_RES_4_B_IVB          0x61070
+#define _PIPE_CRC_RES_5_B_IVB          0x61074
+
+#define PIPE_CRC_CTL(pipe)     _PIPE_INC(pipe, _PIPE_CRC_CTL_A, 0x01000)
+#define PIPE_CRC_RES_1_IVB(pipe)       \
+       _PIPE(pipe, _PIPE_CRC_RES_1_A_IVB, _PIPE_CRC_RES_1_B_IVB)
+#define PIPE_CRC_RES_2_IVB(pipe)       \
+       _PIPE(pipe, _PIPE_CRC_RES_2_A_IVB, _PIPE_CRC_RES_2_B_IVB)
+#define PIPE_CRC_RES_3_IVB(pipe)       \
+       _PIPE(pipe, _PIPE_CRC_RES_3_A_IVB, _PIPE_CRC_RES_3_B_IVB)
+#define PIPE_CRC_RES_4_IVB(pipe)       \
+       _PIPE(pipe, _PIPE_CRC_RES_4_A_IVB, _PIPE_CRC_RES_4_B_IVB)
+#define PIPE_CRC_RES_5_IVB(pipe)       \
+       _PIPE(pipe, _PIPE_CRC_RES_5_A_IVB, _PIPE_CRC_RES_5_B_IVB)
+
+#define PIPE_CRC_RES_RED(pipe) \
+       _PIPE_INC(pipe, _PIPE_CRC_RES_RED_A, 0x01000)
+#define PIPE_CRC_RES_GREEN(pipe) \
+       _PIPE_INC(pipe, _PIPE_CRC_RES_GREEN_A, 0x01000)
+#define PIPE_CRC_RES_BLUE(pipe) \
+       _PIPE_INC(pipe, _PIPE_CRC_RES_BLUE_A, 0x01000)
+#define PIPE_CRC_RES_RES1_I915(pipe) \
+       _PIPE_INC(pipe, _PIPE_CRC_RES_RES1_A_I915, 0x01000)
+#define PIPE_CRC_RES_RES2_G4X(pipe) \
+       _PIPE_INC(pipe, _PIPE_CRC_RES_RES2_A_G4X, 0x01000)
+
 /* Pipe A timing regs */
 #define _HTOTAL_A      (dev_priv->info->display_mmio_offset + 0x60000)
 #define _HBLANK_A      (dev_priv->info->display_mmio_offset + 0x60004)
 #define _BCLRPAT_B     (dev_priv->info->display_mmio_offset + 0x61020)
 #define _VSYNCSHIFT_B  (dev_priv->info->display_mmio_offset + 0x61028)
 
-
 #define HTOTAL(trans) _TRANSCODER(trans, _HTOTAL_A, _HTOTAL_B)
 #define HBLANK(trans) _TRANSCODER(trans, _HBLANK_A, _HBLANK_B)
 #define HSYNC(trans) _TRANSCODER(trans, _HSYNC_A, _HSYNC_B)
 #define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
 #define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
 
-/* HSW eDP PSR registers */
-#define EDP_PSR_CTL                            0x64800
+/* HSW+ eDP PSR registers */
+#define EDP_PSR_BASE(dev)                       (IS_HASWELL(dev) ? 0x64800 : 0x6f800)
+#define EDP_PSR_CTL(dev)                       (EDP_PSR_BASE(dev) + 0)
 #define   EDP_PSR_ENABLE                       (1<<31)
 #define   EDP_PSR_LINK_DISABLE                 (0<<27)
 #define   EDP_PSR_LINK_STANDBY                 (1<<27)
 #define   EDP_PSR_TP1_TIME_0us                 (3<<4)
 #define   EDP_PSR_IDLE_FRAME_SHIFT             0
 
-#define EDP_PSR_AUX_CTL                        0x64810
-#define EDP_PSR_AUX_DATA1              0x64814
+#define EDP_PSR_AUX_CTL(dev)                   (EDP_PSR_BASE(dev) + 0x10)
+#define EDP_PSR_AUX_DATA1(dev)                 (EDP_PSR_BASE(dev) + 0x14)
 #define   EDP_PSR_DPCD_COMMAND         0x80060000
-#define EDP_PSR_AUX_DATA2              0x64818
+#define EDP_PSR_AUX_DATA2(dev)                 (EDP_PSR_BASE(dev) + 0x18)
 #define   EDP_PSR_DPCD_NORMAL_OPERATION        (1<<24)
-#define EDP_PSR_AUX_DATA3              0x6481c
-#define EDP_PSR_AUX_DATA4              0x64820
-#define EDP_PSR_AUX_DATA5              0x64824
+#define EDP_PSR_AUX_DATA3(dev)                 (EDP_PSR_BASE(dev) + 0x1c)
+#define EDP_PSR_AUX_DATA4(dev)                 (EDP_PSR_BASE(dev) + 0x20)
+#define EDP_PSR_AUX_DATA5(dev)                 (EDP_PSR_BASE(dev) + 0x24)
 
-#define EDP_PSR_STATUS_CTL                     0x64840
+#define EDP_PSR_STATUS_CTL(dev)                        (EDP_PSR_BASE(dev) + 0x40)
 #define   EDP_PSR_STATUS_STATE_MASK            (7<<29)
 #define   EDP_PSR_STATUS_STATE_IDLE            (0<<29)
 #define   EDP_PSR_STATUS_STATE_SRDONACK                (1<<29)
 #define   EDP_PSR_STATUS_SENDING_TP1           (1<<4)
 #define   EDP_PSR_STATUS_IDLE_MASK             0xf
 
-#define EDP_PSR_PERF_CNT               0x64844
+#define EDP_PSR_PERF_CNT(dev)          (EDP_PSR_BASE(dev) + 0x44)
 #define   EDP_PSR_PERF_CNT_MASK                0xffffff
 
-#define EDP_PSR_DEBUG_CTL              0x64860
+#define EDP_PSR_DEBUG_CTL(dev)         (EDP_PSR_BASE(dev) + 0x60)
 #define   EDP_PSR_DEBUG_MASK_LPSP      (1<<27)
 #define   EDP_PSR_DEBUG_MASK_MEMUP     (1<<26)
 #define   EDP_PSR_DEBUG_MASK_HPD       (1<<25)
 #define PCH_HDMIC      0xe1150
 #define PCH_HDMID      0xe1160
 
+#define PORT_DFT_I9XX                          0x61150
+#define   DC_BALANCE_RESET                     (1 << 25)
+#define PORT_DFT2_G4X                          0x61154
+#define   DC_BALANCE_RESET_VLV                 (1 << 31)
+#define   PIPE_SCRAMBLE_RESET_MASK             (0x3 << 0)
+#define   PIPE_B_SCRAMBLE_RESET                        (1 << 1)
+#define   PIPE_A_SCRAMBLE_RESET                        (1 << 0)
+
 /* Gen 3 SDVO bits: */
 #define   SDVO_ENABLE                          (1 << 31)
 #define   SDVO_PIPE_SEL(pipe)                  ((pipe) << 30)
 
 /* Gen 4 SDVO/HDMI bits: */
 #define   SDVO_COLOR_FORMAT_8bpc               (0 << 26)
+#define   SDVO_COLOR_FORMAT_MASK               (7 << 26)
 #define   SDVO_ENCODING_SDVO                   (0 << 10)
 #define   SDVO_ENCODING_HDMI                   (2 << 10)
 #define   HDMI_MODE_SELECT_HDMI                        (1 << 9) /* HDMI only */
 
 #define PFIT_AUTO_RATIOS (dev_priv->info->display_mmio_offset + 0x61238)
 
+#define _VLV_BLC_PWM_CTL2_A (dev_priv->info->display_mmio_offset + 0x61250)
+#define _VLV_BLC_PWM_CTL2_B (dev_priv->info->display_mmio_offset + 0x61350)
+#define VLV_BLC_PWM_CTL2(pipe) _PIPE(pipe, _VLV_BLC_PWM_CTL2_A, \
+                                    _VLV_BLC_PWM_CTL2_B)
+
+#define _VLV_BLC_PWM_CTL_A (dev_priv->info->display_mmio_offset + 0x61254)
+#define _VLV_BLC_PWM_CTL_B (dev_priv->info->display_mmio_offset + 0x61354)
+#define VLV_BLC_PWM_CTL(pipe) _PIPE(pipe, _VLV_BLC_PWM_CTL_A, \
+                                   _VLV_BLC_PWM_CTL_B)
+
+#define _VLV_BLC_HIST_CTL_A (dev_priv->info->display_mmio_offset + 0x61260)
+#define _VLV_BLC_HIST_CTL_B (dev_priv->info->display_mmio_offset + 0x61360)
+#define VLV_BLC_HIST_CTL(pipe) _PIPE(pipe, _VLV_BLC_HIST_CTL_A, \
+                                    _VLV_BLC_HIST_CTL_B)
+
 /* Backlight control */
 #define BLC_PWM_CTL2   (dev_priv->info->display_mmio_offset + 0x61250) /* 965+ only */
 #define   BLM_PWM_ENABLE               (1 << 31)
 #define   PIPECONF_DISABLE     0
 #define   PIPECONF_DOUBLE_WIDE (1<<30)
 #define   I965_PIPECONF_ACTIVE (1<<30)
+#define   PIPECONF_DSI_PLL_LOCKED      (1<<29) /* vlv & pipe A only */
 #define   PIPECONF_FRAME_START_DELAY_MASK (3<<27)
 #define   PIPECONF_SINGLE_WIDE 0
 #define   PIPECONF_PIPE_UNLOCKED 0
 #define PIPEFRAMEPIXEL(pipe)  _PIPE(pipe, _PIPEAFRAMEPIXEL, _PIPEBFRAMEPIXEL)
 #define PIPESTAT(pipe) _PIPE(pipe, _PIPEASTAT, _PIPEBSTAT)
 
+#define _PIPE_MISC_A                   0x70030
+#define _PIPE_MISC_B                   0x71030
+#define   PIPEMISC_DITHER_BPC_MASK     (7<<5)
+#define   PIPEMISC_DITHER_8_BPC                (0<<5)
+#define   PIPEMISC_DITHER_10_BPC       (1<<5)
+#define   PIPEMISC_DITHER_6_BPC                (2<<5)
+#define   PIPEMISC_DITHER_12_BPC       (3<<5)
+#define   PIPEMISC_DITHER_ENABLE       (1<<4)
+#define   PIPEMISC_DITHER_TYPE_MASK    (3<<2)
+#define   PIPEMISC_DITHER_TYPE_SP      (0<<2)
+#define PIPEMISC(pipe) _PIPE(pipe, _PIPE_MISC_A, _PIPE_MISC_B)
+
 #define VLV_DPFLIPSTAT                         (VLV_DISPLAY_BASE + 0x70028)
 #define   PIPEB_LINE_COMPARE_INT_EN            (1<<29)
 #define   PIPEB_HLINE_INT_EN                   (1<<28)
 
 /* define the Watermark register on Ironlake */
 #define WM0_PIPEA_ILK          0x45100
-#define  WM0_PIPE_PLANE_MASK   (0x7f<<16)
+#define  WM0_PIPE_PLANE_MASK   (0xffff<<16)
 #define  WM0_PIPE_PLANE_SHIFT  16
-#define  WM0_PIPE_SPRITE_MASK  (0x3f<<8)
+#define  WM0_PIPE_SPRITE_MASK  (0xff<<8)
 #define  WM0_PIPE_SPRITE_SHIFT 8
-#define  WM0_PIPE_CURSOR_MASK  (0x1f)
+#define  WM0_PIPE_CURSOR_MASK  (0xff)
 
 #define WM0_PIPEB_ILK          0x45104
 #define WM0_PIPEC_IVB          0x45200
 #define  WM1_LP_LATENCY_MASK   (0x7f<<24)
 #define  WM1_LP_FBC_MASK       (0xf<<20)
 #define  WM1_LP_FBC_SHIFT      20
-#define  WM1_LP_SR_MASK                (0x1ff<<8)
+#define  WM1_LP_FBC_SHIFT_BDW  19
+#define  WM1_LP_SR_MASK                (0x7ff<<8)
 #define  WM1_LP_SR_SHIFT       8
-#define  WM1_LP_CURSOR_MASK    (0x3f)
+#define  WM1_LP_CURSOR_MASK    (0xff)
 #define WM2_LP_ILK             0x4510c
 #define  WM2_LP_EN             (1<<31)
 #define WM3_LP_ILK             0x45110
  *  } while (high1 != high2);
  *  frame = (high1 << 8) | low1;
  */
-#define _PIPEAFRAMEHIGH          (dev_priv->info->display_mmio_offset + 0x70040)
+#define _PIPEAFRAMEHIGH          0x70040
 #define   PIPE_FRAME_HIGH_MASK    0x0000ffff
 #define   PIPE_FRAME_HIGH_SHIFT   0
-#define _PIPEAFRAMEPIXEL         (dev_priv->info->display_mmio_offset + 0x70044)
+#define _PIPEAFRAMEPIXEL         0x70044
 #define   PIPE_FRAME_LOW_MASK     0xff000000
 #define   PIPE_FRAME_LOW_SHIFT    24
 #define   PIPE_PIXEL_MASK         0x00ffffff
 #define   PIPE_PIXEL_SHIFT        0
 /* GM45+ just has to be different */
-#define _PIPEA_FRMCOUNT_GM45   0x70040
-#define _PIPEA_FLIPCOUNT_GM45  0x70044
+#define _PIPEA_FRMCOUNT_GM45   (dev_priv->info->display_mmio_offset + 0x70040)
+#define _PIPEA_FLIPCOUNT_GM45  (dev_priv->info->display_mmio_offset + 0x70044)
 #define PIPE_FRMCOUNT_GM45(pipe) _PIPE(pipe, _PIPEA_FRMCOUNT_GM45, _PIPEB_FRMCOUNT_GM45)
 
 /* Cursor A & B regs */
 #define _PIPEBDSL              (dev_priv->info->display_mmio_offset + 0x71000)
 #define _PIPEBCONF             (dev_priv->info->display_mmio_offset + 0x71008)
 #define _PIPEBSTAT             (dev_priv->info->display_mmio_offset + 0x71024)
-#define _PIPEBFRAMEHIGH                (dev_priv->info->display_mmio_offset + 0x71040)
-#define _PIPEBFRAMEPIXEL       (dev_priv->info->display_mmio_offset + 0x71044)
-#define _PIPEB_FRMCOUNT_GM45   0x71040
-#define _PIPEB_FLIPCOUNT_GM45  0x71044
+#define _PIPEBFRAMEHIGH                0x71040
+#define _PIPEBFRAMEPIXEL       0x71044
+#define _PIPEB_FRMCOUNT_GM45   (dev_priv->info->display_mmio_offset + 0x71040)
+#define _PIPEB_FLIPCOUNT_GM45  (dev_priv->info->display_mmio_offset + 0x71044)
 
 
 /* Display B control */
 #define DE_SPRITEA_FLIP_DONE    (1 << 28)
 #define DE_PLANEB_FLIP_DONE     (1 << 27)
 #define DE_PLANEA_FLIP_DONE     (1 << 26)
+#define DE_PLANE_FLIP_DONE(plane) (1 << (26 + (plane)))
 #define DE_PCU_EVENT            (1 << 25)
 #define DE_GTT_FAULT            (1 << 24)
 #define DE_POISON               (1 << 23)
 #define DE_PIPEB_ODD_FIELD      (1 << 13)
 #define DE_PIPEB_LINE_COMPARE   (1 << 12)
 #define DE_PIPEB_VSYNC          (1 << 11)
+#define DE_PIPEB_CRC_DONE      (1 << 10)
 #define DE_PIPEB_FIFO_UNDERRUN  (1 << 8)
 #define DE_PIPEA_VBLANK         (1 << 7)
+#define DE_PIPE_VBLANK(pipe)    (1 << (7 + 8*(pipe)))
 #define DE_PIPEA_EVEN_FIELD     (1 << 6)
 #define DE_PIPEA_ODD_FIELD      (1 << 5)
 #define DE_PIPEA_LINE_COMPARE   (1 << 4)
 #define DE_PIPEA_VSYNC          (1 << 3)
+#define DE_PIPEA_CRC_DONE      (1 << 2)
+#define DE_PIPE_CRC_DONE(pipe) (1 << (2 + 8*(pipe)))
 #define DE_PIPEA_FIFO_UNDERRUN  (1 << 0)
+#define DE_PIPE_FIFO_UNDERRUN(pipe)  (1 << (8*(pipe)))
 
 /* More Ivybridge lolz */
 #define DE_ERR_INT_IVB                 (1<<30)
 #define DE_PIPEB_VBLANK_IVB            (1<<5)
 #define DE_SPRITEA_FLIP_DONE_IVB       (1<<4)
 #define DE_PLANEA_FLIP_DONE_IVB                (1<<3)
+#define DE_PLANE_FLIP_DONE_IVB(plane)  (1<< (3 + 5*(plane)))
 #define DE_PIPEA_VBLANK_IVB            (1<<0)
-
-#define DE_PIPE_VBLANK_ILK(pipe)       (1 << ((pipe * 8) + 7))
 #define DE_PIPE_VBLANK_IVB(pipe)       (1 << (pipe * 5))
 
 #define VLV_MASTER_IER                 0x4400c /* Gunit master IER */
 #define GTIIR   0x44018
 #define GTIER   0x4401c
 
+#define GEN8_MASTER_IRQ                        0x44200
+#define  GEN8_MASTER_IRQ_CONTROL       (1<<31)
+#define  GEN8_PCU_IRQ                  (1<<30)
+#define  GEN8_DE_PCH_IRQ               (1<<23)
+#define  GEN8_DE_MISC_IRQ              (1<<22)
+#define  GEN8_DE_PORT_IRQ              (1<<20)
+#define  GEN8_DE_PIPE_C_IRQ            (1<<18)
+#define  GEN8_DE_PIPE_B_IRQ            (1<<17)
+#define  GEN8_DE_PIPE_A_IRQ            (1<<16)
+#define  GEN8_DE_PIPE_IRQ(pipe)                (1<<(16+pipe))
+#define  GEN8_GT_VECS_IRQ              (1<<6)
+#define  GEN8_GT_VCS2_IRQ              (1<<3)
+#define  GEN8_GT_VCS1_IRQ              (1<<2)
+#define  GEN8_GT_BCS_IRQ               (1<<1)
+#define  GEN8_GT_RCS_IRQ               (1<<0)
+
+#define GEN8_GT_ISR(which) (0x44300 + (0x10 * (which)))
+#define GEN8_GT_IMR(which) (0x44304 + (0x10 * (which)))
+#define GEN8_GT_IIR(which) (0x44308 + (0x10 * (which)))
+#define GEN8_GT_IER(which) (0x4430c + (0x10 * (which)))
+
+#define GEN8_BCS_IRQ_SHIFT 16
+#define GEN8_RCS_IRQ_SHIFT 0
+#define GEN8_VCS2_IRQ_SHIFT 16
+#define GEN8_VCS1_IRQ_SHIFT 0
+#define GEN8_VECS_IRQ_SHIFT 0
+
+#define GEN8_DE_PIPE_ISR(pipe) (0x44400 + (0x10 * (pipe)))
+#define GEN8_DE_PIPE_IMR(pipe) (0x44404 + (0x10 * (pipe)))
+#define GEN8_DE_PIPE_IIR(pipe) (0x44408 + (0x10 * (pipe)))
+#define GEN8_DE_PIPE_IER(pipe) (0x4440c + (0x10 * (pipe)))
+#define  GEN8_PIPE_FIFO_UNDERRUN       (1 << 31)
+#define  GEN8_PIPE_CDCLK_CRC_ERROR     (1 << 29)
+#define  GEN8_PIPE_CDCLK_CRC_DONE      (1 << 28)
+#define  GEN8_PIPE_CURSOR_FAULT                (1 << 10)
+#define  GEN8_PIPE_SPRITE_FAULT                (1 << 9)
+#define  GEN8_PIPE_PRIMARY_FAULT       (1 << 8)
+#define  GEN8_PIPE_SPRITE_FLIP_DONE    (1 << 5)
+#define  GEN8_PIPE_FLIP_DONE           (1 << 4)
+#define  GEN8_PIPE_SCAN_LINE_EVENT     (1 << 2)
+#define  GEN8_PIPE_VSYNC               (1 << 1)
+#define  GEN8_PIPE_VBLANK              (1 << 0)
+#define GEN8_DE_PIPE_IRQ_FAULT_ERRORS \
+       (GEN8_PIPE_CURSOR_FAULT | \
+        GEN8_PIPE_SPRITE_FAULT | \
+        GEN8_PIPE_PRIMARY_FAULT)
+
+#define GEN8_DE_PORT_ISR 0x44440
+#define GEN8_DE_PORT_IMR 0x44444
+#define GEN8_DE_PORT_IIR 0x44448
+#define GEN8_DE_PORT_IER 0x4444c
+#define  GEN8_PORT_DP_A_HOTPLUG                (1 << 3)
+#define  GEN8_AUX_CHANNEL_A            (1 << 0)
+
+#define GEN8_DE_MISC_ISR 0x44460
+#define GEN8_DE_MISC_IMR 0x44464
+#define GEN8_DE_MISC_IIR 0x44468
+#define GEN8_DE_MISC_IER 0x4446c
+#define  GEN8_DE_MISC_GSE              (1 << 27)
+
+#define GEN8_PCU_ISR 0x444e0
+#define GEN8_PCU_IMR 0x444e4
+#define GEN8_PCU_IIR 0x444e8
+#define GEN8_PCU_IER 0x444ec
+
 #define ILK_DISPLAY_CHICKEN2   0x42004
 /* Required on all Ironlake and Sandybridge according to the B-Spec. */
 #define  ILK_ELPIN_409_SELECT  (1 << 25)
 # define CHICKEN3_DGMG_DONE_FIX_DISABLE                (1 << 2)
 
 #define CHICKEN_PAR1_1         0x42080
+#define  DPA_MASK_VBLANK_SRD   (1 << 15)
 #define  FORCE_ARB_IDLE_PLANES (1 << 14)
 
+#define _CHICKEN_PIPESL_1_A    0x420b0
+#define _CHICKEN_PIPESL_1_B    0x420b4
+#define  DPRS_MASK_VBLANK_SRD  (1 << 0)
+#define CHICKEN_PIPESL_1(pipe) _PIPE(pipe, _CHICKEN_PIPESL_1_A, _CHICKEN_PIPESL_1_B)
+
 #define DISP_ARB_CTL   0x45000
 #define  DISP_TILE_SURFACE_SWIZZLING   (1<<13)
 #define  DISP_FBC_WM_DIS               (1<<15)
 /* GEN7 chicken */
 #define GEN7_COMMON_SLICE_CHICKEN1             0x7010
 # define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC     ((1<<10) | (1<<26))
+#define COMMON_SLICE_CHICKEN2                  0x7014
+# define GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE  (1<<0)
 
 #define GEN7_L3CNTLREG1                                0xB01C
 #define  GEN7_WA_FOR_GEN7_L3_CONTROL                   0x3C4FFF8C
 #define PIPEA_PP_STATUS         (VLV_DISPLAY_BASE + 0x61200)
 #define PIPEA_PP_CONTROL        (VLV_DISPLAY_BASE + 0x61204)
 #define PIPEA_PP_ON_DELAYS      (VLV_DISPLAY_BASE + 0x61208)
+#define  PANEL_PORT_SELECT_DPB_VLV     (1 << 30)
+#define  PANEL_PORT_SELECT_DPC_VLV     (2 << 30)
 #define PIPEA_PP_OFF_DELAYS     (VLV_DISPLAY_BASE + 0x6120c)
 #define PIPEA_PP_DIVISOR        (VLV_DISPLAY_BASE + 0x61210)
 
 #define  PANEL_PORT_SELECT_MASK        (3 << 30)
 #define  PANEL_PORT_SELECT_LVDS        (0 << 30)
 #define  PANEL_PORT_SELECT_DPA (1 << 30)
-#define  EDP_PANEL             (1 << 30)
 #define  PANEL_PORT_SELECT_DPC (2 << 30)
 #define  PANEL_PORT_SELECT_DPD (3 << 30)
 #define  PANEL_POWER_UP_DELAY_MASK     (0x1fff0000)
 #define  PANEL_LIGHT_ON_DELAY_SHIFT    0
 
 #define PCH_PP_OFF_DELAYS      0xc720c
-#define  PANEL_POWER_PORT_SELECT_MASK  (0x3 << 30)
-#define  PANEL_POWER_PORT_LVDS         (0 << 30)
-#define  PANEL_POWER_PORT_DP_A         (1 << 30)
-#define  PANEL_POWER_PORT_DP_C         (2 << 30)
-#define  PANEL_POWER_PORT_DP_D         (3 << 30)
 #define  PANEL_POWER_DOWN_DELAY_MASK   (0x1fff0000)
 #define  PANEL_POWER_DOWN_DELAY_SHIFT  16
 #define  PANEL_LIGHT_OFF_DELAY_MASK    (0x1fff)
 #define   GEN6_RP_UP_IDLE_MIN                  (0x1<<3)
 #define   GEN6_RP_UP_BUSY_AVG                  (0x2<<3)
 #define   GEN6_RP_UP_BUSY_CONT                 (0x4<<3)
-#define   GEN7_RP_DOWN_IDLE_AVG                        (0x2<<0)
+#define   GEN6_RP_DOWN_IDLE_AVG                        (0x2<<0)
 #define   GEN6_RP_DOWN_IDLE_CONT               (0x1<<0)
 #define GEN6_RP_UP_THRESHOLD                   0xA02C
 #define GEN6_RP_DOWN_THRESHOLD                 0xA030
                                                 GEN6_PM_RP_DOWN_TIMEOUT)
 
 #define GEN6_GT_GFX_RC6_LOCKED                 0x138104
+#define VLV_COUNTER_CONTROL                    0x138104
+#define   VLV_COUNT_RANGE_HIGH                 (1<<15)
+#define   VLV_MEDIA_RC6_COUNT_EN               (1<<1)
+#define   VLV_RENDER_RC6_COUNT_EN              (1<<0)
 #define GEN6_GT_GFX_RC6                                0x138108
 #define GEN6_GT_GFX_RC6p                       0x13810C
 #define GEN6_GT_GFX_RC6pp                      0x138110
 #define   GEN6_PCODE_READ_MIN_FREQ_TABLE       0x9
 #define          GEN6_PCODE_WRITE_RC6VIDS              0x4
 #define          GEN6_PCODE_READ_RC6VIDS               0x5
+#define   GEN6_PCODE_READ_D_COMP               0x10
+#define   GEN6_PCODE_WRITE_D_COMP              0x11
 #define   GEN6_ENCODE_RC6_VID(mv)              (((mv) - 245) / 5)
 #define   GEN6_DECODE_RC6_VID(vids)            (((vids) * 5) + 245)
+#define   DISPLAY_IPS_CONTROL                  0x19
 #define GEN6_PCODE_DATA                                0x138128
 #define   GEN6_PCODE_FREQ_IA_RATIO_SHIFT       8
 #define   GEN6_PCODE_FREQ_RING_RATIO_SHIFT     16
 
 /* IVYBRIDGE DPF */
 #define GEN7_L3CDERRST1                        0xB008 /* L3CD Error Status 1 */
+#define HSW_L3CDERRST11                        0xB208 /* L3CD Error Status register 1 slice 1 */
 #define   GEN7_L3CDERRST1_ROW_MASK     (0x7ff<<14)
 #define   GEN7_PARITY_ERROR_VALID      (1<<13)
 #define   GEN7_L3CDERRST1_BANK_MASK    (3<<11)
 #define   GEN7_L3CDERRST1_ENABLE       (1<<7)
 
 #define GEN7_L3LOG_BASE                        0xB070
+#define HSW_L3LOG_BASE_SLICE1          0xB270
 #define GEN7_L3LOG_SIZE                        0x80
 
 #define GEN7_HALF_SLICE_CHICKEN1       0xe100 /* IVB GT1 + VLV */
 #define GEN7_HALF_SLICE_CHICKEN1_GT2   0xf100
 #define   GEN7_MAX_PS_THREAD_DEP               (8<<12)
+#define   GEN7_SINGLE_SUBSCAN_DISPATCH_ENABLE  (1<<10)
 #define   GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE (1<<3)
 
 #define GEN7_ROW_CHICKEN2              0xe4f4
 #define HSW_ROW_CHICKEN3               0xe49c
 #define  HSW_ROW_CHICKEN3_L3_GLOBAL_ATOMICS_DISABLE    (1 << 6)
 
+#define HALF_SLICE_CHICKEN3            0xe184
+#define   GEN8_CENTROID_PIXEL_OPT_DIS  (1<<8)
+#define   GEN8_SAMPLER_POWER_BYPASS_DIS        (1<<1)
+
 #define G4X_AUD_VID_DID                        (dev_priv->info->display_mmio_offset + 0x62020)
 #define INTEL_AUDIO_DEVCL              0x808629FB
 #define INTEL_AUDIO_DEVBLC             0x80862801
                                        CPT_AUD_CNTL_ST_B)
 #define CPT_AUD_CNTRL_ST2              0xE50C0
 
+#define VLV_HDMIW_HDMIEDID_A           (VLV_DISPLAY_BASE + 0x62050)
+#define VLV_HDMIW_HDMIEDID_B           (VLV_DISPLAY_BASE + 0x62150)
+#define VLV_HDMIW_HDMIEDID(pipe) _PIPE(pipe, \
+                                       VLV_HDMIW_HDMIEDID_A, \
+                                       VLV_HDMIW_HDMIEDID_B)
+#define VLV_AUD_CNTL_ST_A              (VLV_DISPLAY_BASE + 0x620B4)
+#define VLV_AUD_CNTL_ST_B              (VLV_DISPLAY_BASE + 0x621B4)
+#define VLV_AUD_CNTL_ST(pipe) _PIPE(pipe, \
+                                       VLV_AUD_CNTL_ST_A, \
+                                       VLV_AUD_CNTL_ST_B)
+#define VLV_AUD_CNTL_ST2               (VLV_DISPLAY_BASE + 0x620C0)
+
 /* These are the 4 32-bit write offset registers for each stream
  * output buffer.  It determines the offset from the
  * 3DSTATE_SO_BUFFERs that the next streamed vertex output goes to.
 #define CPT_AUD_CFG(pipe) _PIPE(pipe, \
                                        CPT_AUD_CONFIG_A, \
                                        CPT_AUD_CONFIG_B)
+#define VLV_AUD_CONFIG_A               (VLV_DISPLAY_BASE + 0x62000)
+#define VLV_AUD_CONFIG_B               (VLV_DISPLAY_BASE + 0x62100)
+#define VLV_AUD_CFG(pipe) _PIPE(pipe, \
+                                       VLV_AUD_CONFIG_A, \
+                                       VLV_AUD_CONFIG_B)
+
 #define   AUD_CONFIG_N_VALUE_INDEX             (1 << 29)
 #define   AUD_CONFIG_N_PROG_ENABLE             (1 << 28)
 #define   AUD_CONFIG_UPPER_N_SHIFT             20
 #define   AUD_CONFIG_LOWER_N_SHIFT             4
 #define   AUD_CONFIG_LOWER_N_VALUE             (0xfff << 4)
 #define   AUD_CONFIG_PIXEL_CLOCK_HDMI_SHIFT    16
-#define   AUD_CONFIG_PIXEL_CLOCK_HDMI          (0xf << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_MASK     (0xf << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_25175    (0 << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_25200    (1 << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_27000    (2 << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_27027    (3 << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_54000    (4 << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_54054    (5 << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_74176    (6 << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_74250    (7 << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_148352   (8 << 16)
+#define   AUD_CONFIG_PIXEL_CLOCK_HDMI_148500   (9 << 16)
 #define   AUD_CONFIG_DISABLE_NCTS              (1 << 3)
 
 /* HSW Audio */
 #define DDI_BUF_CTL_B                          0x64100
 #define DDI_BUF_CTL(port) _PORT(port, DDI_BUF_CTL_A, DDI_BUF_CTL_B)
 #define  DDI_BUF_CTL_ENABLE                    (1<<31)
+/* Haswell */
 #define  DDI_BUF_EMP_400MV_0DB_HSW             (0<<24)   /* Sel0 */
 #define  DDI_BUF_EMP_400MV_3_5DB_HSW           (1<<24)   /* Sel1 */
 #define  DDI_BUF_EMP_400MV_6DB_HSW             (2<<24)   /* Sel2 */
 #define  DDI_BUF_EMP_600MV_6DB_HSW             (6<<24)   /* Sel6 */
 #define  DDI_BUF_EMP_800MV_0DB_HSW             (7<<24)   /* Sel7 */
 #define  DDI_BUF_EMP_800MV_3_5DB_HSW           (8<<24)   /* Sel8 */
+/* Broadwell */
+#define  DDI_BUF_EMP_400MV_0DB_BDW             (0<<24)   /* Sel0 */
+#define  DDI_BUF_EMP_400MV_3_5DB_BDW           (1<<24)   /* Sel1 */
+#define  DDI_BUF_EMP_400MV_6DB_BDW             (2<<24)   /* Sel2 */
+#define  DDI_BUF_EMP_600MV_0DB_BDW             (3<<24)   /* Sel3 */
+#define  DDI_BUF_EMP_600MV_3_5DB_BDW           (4<<24)   /* Sel4 */
+#define  DDI_BUF_EMP_600MV_6DB_BDW             (5<<24)   /* Sel5 */
+#define  DDI_BUF_EMP_800MV_0DB_BDW             (6<<24)   /* Sel6 */
+#define  DDI_BUF_EMP_800MV_3_5DB_BDW           (7<<24)   /* Sel7 */
+#define  DDI_BUF_EMP_1200MV_0DB_BDW            (8<<24)   /* Sel8 */
 #define  DDI_BUF_EMP_MASK                      (0xf<<24)
 #define  DDI_BUF_PORT_REVERSAL                 (1<<16)
 #define  DDI_BUF_IS_IDLE                       (1<<7)
 #define  LCPLL_PLL_LOCK                        (1<<30)
 #define  LCPLL_CLK_FREQ_MASK           (3<<26)
 #define  LCPLL_CLK_FREQ_450            (0<<26)
+#define  LCPLL_CLK_FREQ_54O_BDW                (1<<26)
+#define  LCPLL_CLK_FREQ_337_5_BDW      (2<<26)
+#define  LCPLL_CLK_FREQ_675_BDW                (3<<26)
 #define  LCPLL_CD_CLOCK_DISABLE                (1<<25)
 #define  LCPLL_CD2X_CLOCK_DISABLE      (1<<23)
 #define  LCPLL_POWER_DOWN_ALLOW                (1<<22)
 #define PIPE_CSC_POSTOFF_ME(pipe) _PIPE(pipe, _PIPE_A_CSC_POSTOFF_ME, _PIPE_B_CSC_POSTOFF_ME)
 #define PIPE_CSC_POSTOFF_LO(pipe) _PIPE(pipe, _PIPE_A_CSC_POSTOFF_LO, _PIPE_B_CSC_POSTOFF_LO)
 
+/* VLV MIPI registers */
+
+#define _MIPIA_PORT_CTRL                       (VLV_DISPLAY_BASE + 0x61190)
+#define _MIPIB_PORT_CTRL                       (VLV_DISPLAY_BASE + 0x61700)
+#define MIPI_PORT_CTRL(pipe)           _PIPE(pipe, _MIPIA_PORT_CTRL, _MIPIB_PORT_CTRL)
+#define  DPI_ENABLE                                    (1 << 31) /* A + B */
+#define  MIPIA_MIPI4DPHY_DELAY_COUNT_SHIFT             27
+#define  MIPIA_MIPI4DPHY_DELAY_COUNT_MASK              (0xf << 27)
+#define  DUAL_LINK_MODE_MASK                           (1 << 26)
+#define  DUAL_LINK_MODE_FRONT_BACK                     (0 << 26)
+#define  DUAL_LINK_MODE_PIXEL_ALTERNATIVE              (1 << 26)
+#define  DITHERING_ENABLE                              (1 << 25) /* A + B */
+#define  FLOPPED_HSTX                                  (1 << 23)
+#define  DE_INVERT                                     (1 << 19) /* XXX */
+#define  MIPIA_FLISDSI_DELAY_COUNT_SHIFT               18
+#define  MIPIA_FLISDSI_DELAY_COUNT_MASK                        (0xf << 18)
+#define  AFE_LATCHOUT                                  (1 << 17)
+#define  LP_OUTPUT_HOLD                                        (1 << 16)
+#define  MIPIB_FLISDSI_DELAY_COUNT_HIGH_SHIFT          15
+#define  MIPIB_FLISDSI_DELAY_COUNT_HIGH_MASK           (1 << 15)
+#define  MIPIB_MIPI4DPHY_DELAY_COUNT_SHIFT             11
+#define  MIPIB_MIPI4DPHY_DELAY_COUNT_MASK              (0xf << 11)
+#define  CSB_SHIFT                                     9
+#define  CSB_MASK                                      (3 << 9)
+#define  CSB_20MHZ                                     (0 << 9)
+#define  CSB_10MHZ                                     (1 << 9)
+#define  CSB_40MHZ                                     (2 << 9)
+#define  BANDGAP_MASK                                  (1 << 8)
+#define  BANDGAP_PNW_CIRCUIT                           (0 << 8)
+#define  BANDGAP_LNC_CIRCUIT                           (1 << 8)
+#define  MIPIB_FLISDSI_DELAY_COUNT_LOW_SHIFT           5
+#define  MIPIB_FLISDSI_DELAY_COUNT_LOW_MASK            (7 << 5)
+#define  TEARING_EFFECT_DELAY                          (1 << 4) /* A + B */
+#define  TEARING_EFFECT_SHIFT                          2 /* A + B */
+#define  TEARING_EFFECT_MASK                           (3 << 2)
+#define  TEARING_EFFECT_OFF                            (0 << 2)
+#define  TEARING_EFFECT_DSI                            (1 << 2)
+#define  TEARING_EFFECT_GPIO                           (2 << 2)
+#define  LANE_CONFIGURATION_SHIFT                      0
+#define  LANE_CONFIGURATION_MASK                       (3 << 0)
+#define  LANE_CONFIGURATION_4LANE                      (0 << 0)
+#define  LANE_CONFIGURATION_DUAL_LINK_A                        (1 << 0)
+#define  LANE_CONFIGURATION_DUAL_LINK_B                        (2 << 0)
+
+#define _MIPIA_TEARING_CTRL                    (VLV_DISPLAY_BASE + 0x61194)
+#define _MIPIB_TEARING_CTRL                    (VLV_DISPLAY_BASE + 0x61704)
+#define MIPI_TEARING_CTRL(pipe)                _PIPE(pipe, _MIPIA_TEARING_CTRL, _MIPIB_TEARING_CTRL)
+#define  TEARING_EFFECT_DELAY_SHIFT                    0
+#define  TEARING_EFFECT_DELAY_MASK                     (0xffff << 0)
+
+/* XXX: all bits reserved */
+#define _MIPIA_AUTOPWG                         (VLV_DISPLAY_BASE + 0x611a0)
+
+/* MIPI DSI Controller and D-PHY registers */
+
+#define _MIPIA_DEVICE_READY                    (VLV_DISPLAY_BASE + 0xb000)
+#define _MIPIB_DEVICE_READY                    (VLV_DISPLAY_BASE + 0xb800)
+#define MIPI_DEVICE_READY(pipe)                _PIPE(pipe, _MIPIA_DEVICE_READY, _MIPIB_DEVICE_READY)
+#define  BUS_POSSESSION                                        (1 << 3) /* set to give bus to receiver */
+#define  ULPS_STATE_MASK                               (3 << 1)
+#define  ULPS_STATE_ENTER                              (2 << 1)
+#define  ULPS_STATE_EXIT                               (1 << 1)
+#define  ULPS_STATE_NORMAL_OPERATION                   (0 << 1)
+#define  DEVICE_READY                                  (1 << 0)
+
+#define _MIPIA_INTR_STAT                       (VLV_DISPLAY_BASE + 0xb004)
+#define _MIPIB_INTR_STAT                       (VLV_DISPLAY_BASE + 0xb804)
+#define MIPI_INTR_STAT(pipe)           _PIPE(pipe, _MIPIA_INTR_STAT, _MIPIB_INTR_STAT)
+#define _MIPIA_INTR_EN                         (VLV_DISPLAY_BASE + 0xb008)
+#define _MIPIB_INTR_EN                         (VLV_DISPLAY_BASE + 0xb808)
+#define MIPI_INTR_EN(pipe)             _PIPE(pipe, _MIPIA_INTR_EN, _MIPIB_INTR_EN)
+#define  TEARING_EFFECT                                        (1 << 31)
+#define  SPL_PKT_SENT_INTERRUPT                                (1 << 30)
+#define  GEN_READ_DATA_AVAIL                           (1 << 29)
+#define  LP_GENERIC_WR_FIFO_FULL                       (1 << 28)
+#define  HS_GENERIC_WR_FIFO_FULL                       (1 << 27)
+#define  RX_PROT_VIOLATION                             (1 << 26)
+#define  RX_INVALID_TX_LENGTH                          (1 << 25)
+#define  ACK_WITH_NO_ERROR                             (1 << 24)
+#define  TURN_AROUND_ACK_TIMEOUT                       (1 << 23)
+#define  LP_RX_TIMEOUT                                 (1 << 22)
+#define  HS_TX_TIMEOUT                                 (1 << 21)
+#define  DPI_FIFO_UNDERRUN                             (1 << 20)
+#define  LOW_CONTENTION                                        (1 << 19)
+#define  HIGH_CONTENTION                               (1 << 18)
+#define  TXDSI_VC_ID_INVALID                           (1 << 17)
+#define  TXDSI_DATA_TYPE_NOT_RECOGNISED                        (1 << 16)
+#define  TXCHECKSUM_ERROR                              (1 << 15)
+#define  TXECC_MULTIBIT_ERROR                          (1 << 14)
+#define  TXECC_SINGLE_BIT_ERROR                                (1 << 13)
+#define  TXFALSE_CONTROL_ERROR                         (1 << 12)
+#define  RXDSI_VC_ID_INVALID                           (1 << 11)
+#define  RXDSI_DATA_TYPE_NOT_REGOGNISED                        (1 << 10)
+#define  RXCHECKSUM_ERROR                              (1 << 9)
+#define  RXECC_MULTIBIT_ERROR                          (1 << 8)
+#define  RXECC_SINGLE_BIT_ERROR                                (1 << 7)
+#define  RXFALSE_CONTROL_ERROR                         (1 << 6)
+#define  RXHS_RECEIVE_TIMEOUT_ERROR                    (1 << 5)
+#define  RX_LP_TX_SYNC_ERROR                           (1 << 4)
+#define  RXEXCAPE_MODE_ENTRY_ERROR                     (1 << 3)
+#define  RXEOT_SYNC_ERROR                              (1 << 2)
+#define  RXSOT_SYNC_ERROR                              (1 << 1)
+#define  RXSOT_ERROR                                   (1 << 0)
+
+#define _MIPIA_DSI_FUNC_PRG                    (VLV_DISPLAY_BASE + 0xb00c)
+#define _MIPIB_DSI_FUNC_PRG                    (VLV_DISPLAY_BASE + 0xb80c)
+#define MIPI_DSI_FUNC_PRG(pipe)                _PIPE(pipe, _MIPIA_DSI_FUNC_PRG, _MIPIB_DSI_FUNC_PRG)
+#define  CMD_MODE_DATA_WIDTH_MASK                      (7 << 13)
+#define  CMD_MODE_NOT_SUPPORTED                                (0 << 13)
+#define  CMD_MODE_DATA_WIDTH_16_BIT                    (1 << 13)
+#define  CMD_MODE_DATA_WIDTH_9_BIT                     (2 << 13)
+#define  CMD_MODE_DATA_WIDTH_8_BIT                     (3 << 13)
+#define  CMD_MODE_DATA_WIDTH_OPTION1                   (4 << 13)
+#define  CMD_MODE_DATA_WIDTH_OPTION2                   (5 << 13)
+#define  VID_MODE_FORMAT_MASK                          (0xf << 7)
+#define  VID_MODE_NOT_SUPPORTED                                (0 << 7)
+#define  VID_MODE_FORMAT_RGB565                                (1 << 7)
+#define  VID_MODE_FORMAT_RGB666                                (2 << 7)
+#define  VID_MODE_FORMAT_RGB666_LOOSE                  (3 << 7)
+#define  VID_MODE_FORMAT_RGB888                                (4 << 7)
+#define  CMD_MODE_CHANNEL_NUMBER_SHIFT                 5
+#define  CMD_MODE_CHANNEL_NUMBER_MASK                  (3 << 5)
+#define  VID_MODE_CHANNEL_NUMBER_SHIFT                 3
+#define  VID_MODE_CHANNEL_NUMBER_MASK                  (3 << 3)
+#define  DATA_LANES_PRG_REG_SHIFT                      0
+#define  DATA_LANES_PRG_REG_MASK                       (7 << 0)
+
+#define _MIPIA_HS_TX_TIMEOUT                   (VLV_DISPLAY_BASE + 0xb010)
+#define _MIPIB_HS_TX_TIMEOUT                   (VLV_DISPLAY_BASE + 0xb810)
+#define MIPI_HS_TX_TIMEOUT(pipe)       _PIPE(pipe, _MIPIA_HS_TX_TIMEOUT, _MIPIB_HS_TX_TIMEOUT)
+#define  HIGH_SPEED_TX_TIMEOUT_COUNTER_MASK            0xffffff
+
+#define _MIPIA_LP_RX_TIMEOUT                   (VLV_DISPLAY_BASE + 0xb014)
+#define _MIPIB_LP_RX_TIMEOUT                   (VLV_DISPLAY_BASE + 0xb814)
+#define MIPI_LP_RX_TIMEOUT(pipe)       _PIPE(pipe, _MIPIA_LP_RX_TIMEOUT, _MIPIB_LP_RX_TIMEOUT)
+#define  LOW_POWER_RX_TIMEOUT_COUNTER_MASK             0xffffff
+
+#define _MIPIA_TURN_AROUND_TIMEOUT             (VLV_DISPLAY_BASE + 0xb018)
+#define _MIPIB_TURN_AROUND_TIMEOUT             (VLV_DISPLAY_BASE + 0xb818)
+#define MIPI_TURN_AROUND_TIMEOUT(pipe) _PIPE(pipe, _MIPIA_TURN_AROUND_TIMEOUT, _MIPIB_TURN_AROUND_TIMEOUT)
+#define  TURN_AROUND_TIMEOUT_MASK                      0x3f
+
+#define _MIPIA_DEVICE_RESET_TIMER              (VLV_DISPLAY_BASE + 0xb01c)
+#define _MIPIB_DEVICE_RESET_TIMER              (VLV_DISPLAY_BASE + 0xb81c)
+#define MIPI_DEVICE_RESET_TIMER(pipe)  _PIPE(pipe, _MIPIA_DEVICE_RESET_TIMER, _MIPIB_DEVICE_RESET_TIMER)
+#define  DEVICE_RESET_TIMER_MASK                       0xffff
+
+#define _MIPIA_DPI_RESOLUTION                  (VLV_DISPLAY_BASE + 0xb020)
+#define _MIPIB_DPI_RESOLUTION                  (VLV_DISPLAY_BASE + 0xb820)
+#define MIPI_DPI_RESOLUTION(pipe)      _PIPE(pipe, _MIPIA_DPI_RESOLUTION, _MIPIB_DPI_RESOLUTION)
+#define  VERTICAL_ADDRESS_SHIFT                                16
+#define  VERTICAL_ADDRESS_MASK                         (0xffff << 16)
+#define  HORIZONTAL_ADDRESS_SHIFT                      0
+#define  HORIZONTAL_ADDRESS_MASK                       0xffff
+
+#define _MIPIA_DBI_FIFO_THROTTLE               (VLV_DISPLAY_BASE + 0xb024)
+#define _MIPIB_DBI_FIFO_THROTTLE               (VLV_DISPLAY_BASE + 0xb824)
+#define MIPI_DBI_FIFO_THROTTLE(pipe)   _PIPE(pipe, _MIPIA_DBI_FIFO_THROTTLE, _MIPIB_DBI_FIFO_THROTTLE)
+#define  DBI_FIFO_EMPTY_HALF                           (0 << 0)
+#define  DBI_FIFO_EMPTY_QUARTER                                (1 << 0)
+#define  DBI_FIFO_EMPTY_7_LOCATIONS                    (2 << 0)
+
+/* regs below are bits 15:0 */
+#define _MIPIA_HSYNC_PADDING_COUNT             (VLV_DISPLAY_BASE + 0xb028)
+#define _MIPIB_HSYNC_PADDING_COUNT             (VLV_DISPLAY_BASE + 0xb828)
+#define MIPI_HSYNC_PADDING_COUNT(pipe) _PIPE(pipe, _MIPIA_HSYNC_PADDING_COUNT, _MIPIB_HSYNC_PADDING_COUNT)
+
+#define _MIPIA_HBP_COUNT                       (VLV_DISPLAY_BASE + 0xb02c)
+#define _MIPIB_HBP_COUNT                       (VLV_DISPLAY_BASE + 0xb82c)
+#define MIPI_HBP_COUNT(pipe)           _PIPE(pipe, _MIPIA_HBP_COUNT, _MIPIB_HBP_COUNT)
+
+#define _MIPIA_HFP_COUNT                       (VLV_DISPLAY_BASE + 0xb030)
+#define _MIPIB_HFP_COUNT                       (VLV_DISPLAY_BASE + 0xb830)
+#define MIPI_HFP_COUNT(pipe)           _PIPE(pipe, _MIPIA_HFP_COUNT, _MIPIB_HFP_COUNT)
+
+#define _MIPIA_HACTIVE_AREA_COUNT              (VLV_DISPLAY_BASE + 0xb034)
+#define _MIPIB_HACTIVE_AREA_COUNT              (VLV_DISPLAY_BASE + 0xb834)
+#define MIPI_HACTIVE_AREA_COUNT(pipe)  _PIPE(pipe, _MIPIA_HACTIVE_AREA_COUNT, _MIPIB_HACTIVE_AREA_COUNT)
+
+#define _MIPIA_VSYNC_PADDING_COUNT             (VLV_DISPLAY_BASE + 0xb038)
+#define _MIPIB_VSYNC_PADDING_COUNT             (VLV_DISPLAY_BASE + 0xb838)
+#define MIPI_VSYNC_PADDING_COUNT(pipe) _PIPE(pipe, _MIPIA_VSYNC_PADDING_COUNT, _MIPIB_VSYNC_PADDING_COUNT)
+
+#define _MIPIA_VBP_COUNT                       (VLV_DISPLAY_BASE + 0xb03c)
+#define _MIPIB_VBP_COUNT                       (VLV_DISPLAY_BASE + 0xb83c)
+#define MIPI_VBP_COUNT(pipe)           _PIPE(pipe, _MIPIA_VBP_COUNT, _MIPIB_VBP_COUNT)
+
+#define _MIPIA_VFP_COUNT                       (VLV_DISPLAY_BASE + 0xb040)
+#define _MIPIB_VFP_COUNT                       (VLV_DISPLAY_BASE + 0xb840)
+#define MIPI_VFP_COUNT(pipe)           _PIPE(pipe, _MIPIA_VFP_COUNT, _MIPIB_VFP_COUNT)
+
+#define _MIPIA_HIGH_LOW_SWITCH_COUNT           (VLV_DISPLAY_BASE + 0xb044)
+#define _MIPIB_HIGH_LOW_SWITCH_COUNT           (VLV_DISPLAY_BASE + 0xb844)
+#define MIPI_HIGH_LOW_SWITCH_COUNT(pipe)       _PIPE(pipe, _MIPIA_HIGH_LOW_SWITCH_COUNT, _MIPIB_HIGH_LOW_SWITCH_COUNT)
+/* regs above are bits 15:0 */
+
+#define _MIPIA_DPI_CONTROL                     (VLV_DISPLAY_BASE + 0xb048)
+#define _MIPIB_DPI_CONTROL                     (VLV_DISPLAY_BASE + 0xb848)
+#define MIPI_DPI_CONTROL(pipe)         _PIPE(pipe, _MIPIA_DPI_CONTROL, _MIPIB_DPI_CONTROL)
+#define  DPI_LP_MODE                                   (1 << 6)
+#define  BACKLIGHT_OFF                                 (1 << 5)
+#define  BACKLIGHT_ON                                  (1 << 4)
+#define  COLOR_MODE_OFF                                        (1 << 3)
+#define  COLOR_MODE_ON                                 (1 << 2)
+#define  TURN_ON                                       (1 << 1)
+#define  SHUTDOWN                                      (1 << 0)
+
+#define _MIPIA_DPI_DATA                                (VLV_DISPLAY_BASE + 0xb04c)
+#define _MIPIB_DPI_DATA                                (VLV_DISPLAY_BASE + 0xb84c)
+#define MIPI_DPI_DATA(pipe)            _PIPE(pipe, _MIPIA_DPI_DATA, _MIPIB_DPI_DATA)
+#define  COMMAND_BYTE_SHIFT                            0
+#define  COMMAND_BYTE_MASK                             (0x3f << 0)
+
+#define _MIPIA_INIT_COUNT                      (VLV_DISPLAY_BASE + 0xb050)
+#define _MIPIB_INIT_COUNT                      (VLV_DISPLAY_BASE + 0xb850)
+#define MIPI_INIT_COUNT(pipe)          _PIPE(pipe, _MIPIA_INIT_COUNT, _MIPIB_INIT_COUNT)
+#define  MASTER_INIT_TIMER_SHIFT                       0
+#define  MASTER_INIT_TIMER_MASK                                (0xffff << 0)
+
+#define _MIPIA_MAX_RETURN_PKT_SIZE             (VLV_DISPLAY_BASE + 0xb054)
+#define _MIPIB_MAX_RETURN_PKT_SIZE             (VLV_DISPLAY_BASE + 0xb854)
+#define MIPI_MAX_RETURN_PKT_SIZE(pipe) _PIPE(pipe, _MIPIA_MAX_RETURN_PKT_SIZE, _MIPIB_MAX_RETURN_PKT_SIZE)
+#define  MAX_RETURN_PKT_SIZE_SHIFT                     0
+#define  MAX_RETURN_PKT_SIZE_MASK                      (0x3ff << 0)
+
+#define _MIPIA_VIDEO_MODE_FORMAT               (VLV_DISPLAY_BASE + 0xb058)
+#define _MIPIB_VIDEO_MODE_FORMAT               (VLV_DISPLAY_BASE + 0xb858)
+#define MIPI_VIDEO_MODE_FORMAT(pipe)   _PIPE(pipe, _MIPIA_VIDEO_MODE_FORMAT, _MIPIB_VIDEO_MODE_FORMAT)
+#define  RANDOM_DPI_DISPLAY_RESOLUTION                 (1 << 4)
+#define  DISABLE_VIDEO_BTA                             (1 << 3)
+#define  IP_TG_CONFIG                                  (1 << 2)
+#define  VIDEO_MODE_NON_BURST_WITH_SYNC_PULSE          (1 << 0)
+#define  VIDEO_MODE_NON_BURST_WITH_SYNC_EVENTS         (2 << 0)
+#define  VIDEO_MODE_BURST                              (3 << 0)
+
+#define _MIPIA_EOT_DISABLE                     (VLV_DISPLAY_BASE + 0xb05c)
+#define _MIPIB_EOT_DISABLE                     (VLV_DISPLAY_BASE + 0xb85c)
+#define MIPI_EOT_DISABLE(pipe)         _PIPE(pipe, _MIPIA_EOT_DISABLE, _MIPIB_EOT_DISABLE)
+#define  LP_RX_TIMEOUT_ERROR_RECOVERY_DISABLE          (1 << 7)
+#define  HS_RX_TIMEOUT_ERROR_RECOVERY_DISABLE          (1 << 6)
+#define  LOW_CONTENTION_RECOVERY_DISABLE               (1 << 5)
+#define  HIGH_CONTENTION_RECOVERY_DISABLE              (1 << 4)
+#define  TXDSI_TYPE_NOT_RECOGNISED_ERROR_RECOVERY_DISABLE (1 << 3)
+#define  TXECC_MULTIBIT_ERROR_RECOVERY_DISABLE         (1 << 2)
+#define  CLOCKSTOP                                     (1 << 1)
+#define  EOT_DISABLE                                   (1 << 0)
+
+#define _MIPIA_LP_BYTECLK                      (VLV_DISPLAY_BASE + 0xb060)
+#define _MIPIB_LP_BYTECLK                      (VLV_DISPLAY_BASE + 0xb860)
+#define MIPI_LP_BYTECLK(pipe)          _PIPE(pipe, _MIPIA_LP_BYTECLK, _MIPIB_LP_BYTECLK)
+#define  LP_BYTECLK_SHIFT                              0
+#define  LP_BYTECLK_MASK                               (0xffff << 0)
+
+/* bits 31:0 */
+#define _MIPIA_LP_GEN_DATA                     (VLV_DISPLAY_BASE + 0xb064)
+#define _MIPIB_LP_GEN_DATA                     (VLV_DISPLAY_BASE + 0xb864)
+#define MIPI_LP_GEN_DATA(pipe)         _PIPE(pipe, _MIPIA_LP_GEN_DATA, _MIPIB_LP_GEN_DATA)
+
+/* bits 31:0 */
+#define _MIPIA_HS_GEN_DATA                     (VLV_DISPLAY_BASE + 0xb068)
+#define _MIPIB_HS_GEN_DATA                     (VLV_DISPLAY_BASE + 0xb868)
+#define MIPI_HS_GEN_DATA(pipe)         _PIPE(pipe, _MIPIA_HS_GEN_DATA, _MIPIB_HS_GEN_DATA)
+
+#define _MIPIA_LP_GEN_CTRL                     (VLV_DISPLAY_BASE + 0xb06c)
+#define _MIPIB_LP_GEN_CTRL                     (VLV_DISPLAY_BASE + 0xb86c)
+#define MIPI_LP_GEN_CTRL(pipe)         _PIPE(pipe, _MIPIA_LP_GEN_CTRL, _MIPIB_LP_GEN_CTRL)
+#define _MIPIA_HS_GEN_CTRL                     (VLV_DISPLAY_BASE + 0xb070)
+#define _MIPIB_HS_GEN_CTRL                     (VLV_DISPLAY_BASE + 0xb870)
+#define MIPI_HS_GEN_CTRL(pipe)         _PIPE(pipe, _MIPIA_HS_GEN_CTRL, _MIPIB_HS_GEN_CTRL)
+#define  LONG_PACKET_WORD_COUNT_SHIFT                  8
+#define  LONG_PACKET_WORD_COUNT_MASK                   (0xffff << 8)
+#define  SHORT_PACKET_PARAM_SHIFT                      8
+#define  SHORT_PACKET_PARAM_MASK                       (0xffff << 8)
+#define  VIRTUAL_CHANNEL_SHIFT                         6
+#define  VIRTUAL_CHANNEL_MASK                          (3 << 6)
+#define  DATA_TYPE_SHIFT                               0
+#define  DATA_TYPE_MASK                                        (3f << 0)
+/* data type values, see include/video/mipi_display.h */
+
+#define _MIPIA_GEN_FIFO_STAT                   (VLV_DISPLAY_BASE + 0xb074)
+#define _MIPIB_GEN_FIFO_STAT                   (VLV_DISPLAY_BASE + 0xb874)
+#define MIPI_GEN_FIFO_STAT(pipe)       _PIPE(pipe, _MIPIA_GEN_FIFO_STAT, _MIPIB_GEN_FIFO_STAT)
+#define  DPI_FIFO_EMPTY                                        (1 << 28)
+#define  DBI_FIFO_EMPTY                                        (1 << 27)
+#define  LP_CTRL_FIFO_EMPTY                            (1 << 26)
+#define  LP_CTRL_FIFO_HALF_EMPTY                       (1 << 25)
+#define  LP_CTRL_FIFO_FULL                             (1 << 24)
+#define  HS_CTRL_FIFO_EMPTY                            (1 << 18)
+#define  HS_CTRL_FIFO_HALF_EMPTY                       (1 << 17)
+#define  HS_CTRL_FIFO_FULL                             (1 << 16)
+#define  LP_DATA_FIFO_EMPTY                            (1 << 10)
+#define  LP_DATA_FIFO_HALF_EMPTY                       (1 << 9)
+#define  LP_DATA_FIFO_FULL                             (1 << 8)
+#define  HS_DATA_FIFO_EMPTY                            (1 << 2)
+#define  HS_DATA_FIFO_HALF_EMPTY                       (1 << 1)
+#define  HS_DATA_FIFO_FULL                             (1 << 0)
+
+#define _MIPIA_HS_LS_DBI_ENABLE                        (VLV_DISPLAY_BASE + 0xb078)
+#define _MIPIB_HS_LS_DBI_ENABLE                        (VLV_DISPLAY_BASE + 0xb878)
+#define MIPI_HS_LP_DBI_ENABLE(pipe)    _PIPE(pipe, _MIPIA_HS_LS_DBI_ENABLE, _MIPIB_HS_LS_DBI_ENABLE)
+#define  DBI_HS_LP_MODE_MASK                           (1 << 0)
+#define  DBI_LP_MODE                                   (1 << 0)
+#define  DBI_HS_MODE                                   (0 << 0)
+
+#define _MIPIA_DPHY_PARAM                      (VLV_DISPLAY_BASE + 0xb080)
+#define _MIPIB_DPHY_PARAM                      (VLV_DISPLAY_BASE + 0xb880)
+#define MIPI_DPHY_PARAM(pipe)          _PIPE(pipe, _MIPIA_DPHY_PARAM, _MIPIB_DPHY_PARAM)
+#define  EXIT_ZERO_COUNT_SHIFT                         24
+#define  EXIT_ZERO_COUNT_MASK                          (0x3f << 24)
+#define  TRAIL_COUNT_SHIFT                             16
+#define  TRAIL_COUNT_MASK                              (0x1f << 16)
+#define  CLK_ZERO_COUNT_SHIFT                          8
+#define  CLK_ZERO_COUNT_MASK                           (0xff << 8)
+#define  PREPARE_COUNT_SHIFT                           0
+#define  PREPARE_COUNT_MASK                            (0x3f << 0)
+
+/* bits 31:0 */
+#define _MIPIA_DBI_BW_CTRL                     (VLV_DISPLAY_BASE + 0xb084)
+#define _MIPIB_DBI_BW_CTRL                     (VLV_DISPLAY_BASE + 0xb884)
+#define MIPI_DBI_BW_CTRL(pipe)         _PIPE(pipe, _MIPIA_DBI_BW_CTRL, _MIPIB_DBI_BW_CTRL)
+
+#define _MIPIA_CLK_LANE_SWITCH_TIME_CNT                (VLV_DISPLAY_BASE + 0xb088)
+#define _MIPIB_CLK_LANE_SWITCH_TIME_CNT                (VLV_DISPLAY_BASE + 0xb888)
+#define MIPI_CLK_LANE_SWITCH_TIME_CNT(pipe)    _PIPE(pipe, _MIPIA_CLK_LANE_SWITCH_TIME_CNT, _MIPIB_CLK_LANE_SWITCH_TIME_CNT)
+#define  LP_HS_SSW_CNT_SHIFT                           16
+#define  LP_HS_SSW_CNT_MASK                            (0xffff << 16)
+#define  HS_LP_PWR_SW_CNT_SHIFT                                0
+#define  HS_LP_PWR_SW_CNT_MASK                         (0xffff << 0)
+
+#define _MIPIA_STOP_STATE_STALL                        (VLV_DISPLAY_BASE + 0xb08c)
+#define _MIPIB_STOP_STATE_STALL                        (VLV_DISPLAY_BASE + 0xb88c)
+#define MIPI_STOP_STATE_STALL(pipe)    _PIPE(pipe, _MIPIA_STOP_STATE_STALL, _MIPIB_STOP_STATE_STALL)
+#define  STOP_STATE_STALL_COUNTER_SHIFT                        0
+#define  STOP_STATE_STALL_COUNTER_MASK                 (0xff << 0)
+
+#define _MIPIA_INTR_STAT_REG_1                 (VLV_DISPLAY_BASE + 0xb090)
+#define _MIPIB_INTR_STAT_REG_1                 (VLV_DISPLAY_BASE + 0xb890)
+#define MIPI_INTR_STAT_REG_1(pipe)     _PIPE(pipe, _MIPIA_INTR_STAT_REG_1, _MIPIB_INTR_STAT_REG_1)
+#define _MIPIA_INTR_EN_REG_1                   (VLV_DISPLAY_BASE + 0xb094)
+#define _MIPIB_INTR_EN_REG_1                   (VLV_DISPLAY_BASE + 0xb894)
+#define MIPI_INTR_EN_REG_1(pipe)       _PIPE(pipe, _MIPIA_INTR_EN_REG_1, _MIPIB_INTR_EN_REG_1)
+#define  RX_CONTENTION_DETECTED                                (1 << 0)
+
+/* XXX: only pipe A ?!? */
+#define MIPIA_DBI_TYPEC_CTRL                   (VLV_DISPLAY_BASE + 0xb100)
+#define  DBI_TYPEC_ENABLE                              (1 << 31)
+#define  DBI_TYPEC_WIP                                 (1 << 30)
+#define  DBI_TYPEC_OPTION_SHIFT                                28
+#define  DBI_TYPEC_OPTION_MASK                         (3 << 28)
+#define  DBI_TYPEC_FREQ_SHIFT                          24
+#define  DBI_TYPEC_FREQ_MASK                           (0xf << 24)
+#define  DBI_TYPEC_OVERRIDE                            (1 << 8)
+#define  DBI_TYPEC_OVERRIDE_COUNTER_SHIFT              0
+#define  DBI_TYPEC_OVERRIDE_COUNTER_MASK               (0xff << 0)
+
+
+/* MIPI adapter registers */
+
+#define _MIPIA_CTRL                            (VLV_DISPLAY_BASE + 0xb104)
+#define _MIPIB_CTRL                            (VLV_DISPLAY_BASE + 0xb904)
+#define MIPI_CTRL(pipe)                        _PIPE(pipe, _MIPIA_CTRL, _MIPIB_CTRL)
+#define  ESCAPE_CLOCK_DIVIDER_SHIFT                    5 /* A only */
+#define  ESCAPE_CLOCK_DIVIDER_MASK                     (3 << 5)
+#define  ESCAPE_CLOCK_DIVIDER_1                                (0 << 5)
+#define  ESCAPE_CLOCK_DIVIDER_2                                (1 << 5)
+#define  ESCAPE_CLOCK_DIVIDER_4                                (2 << 5)
+#define  READ_REQUEST_PRIORITY_SHIFT                   3
+#define  READ_REQUEST_PRIORITY_MASK                    (3 << 3)
+#define  READ_REQUEST_PRIORITY_LOW                     (0 << 3)
+#define  READ_REQUEST_PRIORITY_HIGH                    (3 << 3)
+#define  RGB_FLIP_TO_BGR                               (1 << 2)
+
+#define _MIPIA_DATA_ADDRESS                    (VLV_DISPLAY_BASE + 0xb108)
+#define _MIPIB_DATA_ADDRESS                    (VLV_DISPLAY_BASE + 0xb908)
+#define MIPI_DATA_ADDRESS(pipe)                _PIPE(pipe, _MIPIA_DATA_ADDRESS, _MIPIB_DATA_ADDRESS)
+#define  DATA_MEM_ADDRESS_SHIFT                                5
+#define  DATA_MEM_ADDRESS_MASK                         (0x7ffffff << 5)
+#define  DATA_VALID                                    (1 << 0)
+
+#define _MIPIA_DATA_LENGTH                     (VLV_DISPLAY_BASE + 0xb10c)
+#define _MIPIB_DATA_LENGTH                     (VLV_DISPLAY_BASE + 0xb90c)
+#define MIPI_DATA_LENGTH(pipe)         _PIPE(pipe, _MIPIA_DATA_LENGTH, _MIPIB_DATA_LENGTH)
+#define  DATA_LENGTH_SHIFT                             0
+#define  DATA_LENGTH_MASK                              (0xfffff << 0)
+
+#define _MIPIA_COMMAND_ADDRESS                 (VLV_DISPLAY_BASE + 0xb110)
+#define _MIPIB_COMMAND_ADDRESS                 (VLV_DISPLAY_BASE + 0xb910)
+#define MIPI_COMMAND_ADDRESS(pipe)     _PIPE(pipe, _MIPIA_COMMAND_ADDRESS, _MIPIB_COMMAND_ADDRESS)
+#define  COMMAND_MEM_ADDRESS_SHIFT                     5
+#define  COMMAND_MEM_ADDRESS_MASK                      (0x7ffffff << 5)
+#define  AUTO_PWG_ENABLE                               (1 << 2)
+#define  MEMORY_WRITE_DATA_FROM_PIPE_RENDERING         (1 << 1)
+#define  COMMAND_VALID                                 (1 << 0)
+
+#define _MIPIA_COMMAND_LENGTH                  (VLV_DISPLAY_BASE + 0xb114)
+#define _MIPIB_COMMAND_LENGTH                  (VLV_DISPLAY_BASE + 0xb914)
+#define MIPI_COMMAND_LENGTH(pipe)      _PIPE(pipe, _MIPIA_COMMAND_LENGTH, _MIPIB_COMMAND_LENGTH)
+#define  COMMAND_LENGTH_SHIFT(n)                       (8 * (n)) /* n: 0...3 */
+#define  COMMAND_LENGTH_MASK(n)                                (0xff << (8 * (n)))
+
+#define _MIPIA_READ_DATA_RETURN0               (VLV_DISPLAY_BASE + 0xb118)
+#define _MIPIB_READ_DATA_RETURN0               (VLV_DISPLAY_BASE + 0xb918)
+#define MIPI_READ_DATA_RETURN(pipe, n) \
+       (_PIPE(pipe, _MIPIA_READ_DATA_RETURN0, _MIPIB_READ_DATA_RETURN0) + 4 * (n)) /* n: 0...7 */
+
+#define _MIPIA_READ_DATA_VALID                 (VLV_DISPLAY_BASE + 0xb138)
+#define _MIPIB_READ_DATA_VALID                 (VLV_DISPLAY_BASE + 0xb938)
+#define MIPI_READ_DATA_VALID(pipe)     _PIPE(pipe, _MIPIA_READ_DATA_VALID, _MIPIB_READ_DATA_VALID)
+#define  READ_DATA_VALID(n)                            (1 << (n))
+
 #endif /* _I915_REG_H_ */
index 70db618..98790c7 100644 (file)
@@ -214,6 +214,22 @@ static void i915_save_display(struct drm_device *dev)
                dev_priv->regfile.saveBLC_CPU_PWM_CTL2 = I915_READ(BLC_PWM_CPU_CTL2);
                if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
                        dev_priv->regfile.saveLVDS = I915_READ(PCH_LVDS);
+       } else if (IS_VALLEYVIEW(dev)) {
+               dev_priv->regfile.savePP_CONTROL = I915_READ(PP_CONTROL);
+               dev_priv->regfile.savePFIT_PGM_RATIOS = I915_READ(PFIT_PGM_RATIOS);
+
+               dev_priv->regfile.saveBLC_PWM_CTL =
+                       I915_READ(VLV_BLC_PWM_CTL(PIPE_A));
+               dev_priv->regfile.saveBLC_HIST_CTL =
+                       I915_READ(VLV_BLC_HIST_CTL(PIPE_A));
+               dev_priv->regfile.saveBLC_PWM_CTL2 =
+                       I915_READ(VLV_BLC_PWM_CTL2(PIPE_A));
+               dev_priv->regfile.saveBLC_PWM_CTL_B =
+                       I915_READ(VLV_BLC_PWM_CTL(PIPE_B));
+               dev_priv->regfile.saveBLC_HIST_CTL_B =
+                       I915_READ(VLV_BLC_HIST_CTL(PIPE_B));
+               dev_priv->regfile.saveBLC_PWM_CTL2_B =
+                       I915_READ(VLV_BLC_PWM_CTL2(PIPE_B));
        } else {
                dev_priv->regfile.savePP_CONTROL = I915_READ(PP_CONTROL);
                dev_priv->regfile.savePFIT_PGM_RATIOS = I915_READ(PFIT_PGM_RATIOS);
@@ -302,6 +318,19 @@ static void i915_restore_display(struct drm_device *dev)
                I915_WRITE(PCH_PP_CONTROL, dev_priv->regfile.savePP_CONTROL);
                I915_WRITE(RSTDBYCTL,
                           dev_priv->regfile.saveMCHBAR_RENDER_STANDBY);
+       } else if (IS_VALLEYVIEW(dev)) {
+               I915_WRITE(VLV_BLC_PWM_CTL(PIPE_A),
+                          dev_priv->regfile.saveBLC_PWM_CTL);
+               I915_WRITE(VLV_BLC_HIST_CTL(PIPE_A),
+                          dev_priv->regfile.saveBLC_HIST_CTL);
+               I915_WRITE(VLV_BLC_PWM_CTL2(PIPE_A),
+                          dev_priv->regfile.saveBLC_PWM_CTL2);
+               I915_WRITE(VLV_BLC_PWM_CTL(PIPE_B),
+                          dev_priv->regfile.saveBLC_PWM_CTL);
+               I915_WRITE(VLV_BLC_HIST_CTL(PIPE_B),
+                          dev_priv->regfile.saveBLC_HIST_CTL);
+               I915_WRITE(VLV_BLC_PWM_CTL2(PIPE_B),
+                          dev_priv->regfile.saveBLC_PWM_CTL2);
        } else {
                I915_WRITE(PFIT_PGM_RATIOS, dev_priv->regfile.savePFIT_PGM_RATIOS);
                I915_WRITE(BLC_PWM_CTL, dev_priv->regfile.saveBLC_PWM_CTL);
@@ -340,7 +369,9 @@ int i915_save_state(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
        int i;
 
-       pci_read_config_byte(dev->pdev, LBB, &dev_priv->regfile.saveLBB);
+       if (INTEL_INFO(dev)->gen <= 4)
+               pci_read_config_byte(dev->pdev, LBB,
+                                    &dev_priv->regfile.saveLBB);
 
        mutex_lock(&dev->struct_mutex);
 
@@ -367,7 +398,8 @@ int i915_save_state(struct drm_device *dev)
        intel_disable_gt_powersave(dev);
 
        /* Cache mode state */
-       dev_priv->regfile.saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
+       if (INTEL_INFO(dev)->gen < 7)
+               dev_priv->regfile.saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
 
        /* Memory Arbitration state */
        dev_priv->regfile.saveMI_ARB_STATE = I915_READ(MI_ARB_STATE);
@@ -390,7 +422,9 @@ int i915_restore_state(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
        int i;
 
-       pci_write_config_byte(dev->pdev, LBB, dev_priv->regfile.saveLBB);
+       if (INTEL_INFO(dev)->gen <= 4)
+               pci_write_config_byte(dev->pdev, LBB,
+                                     dev_priv->regfile.saveLBB);
 
        mutex_lock(&dev->struct_mutex);
 
@@ -414,7 +448,9 @@ int i915_restore_state(struct drm_device *dev)
        }
 
        /* Cache mode state */
-       I915_WRITE(CACHE_MODE_0, dev_priv->regfile.saveCACHE_MODE_0 | 0xffff0000);
+       if (INTEL_INFO(dev)->gen < 7)
+               I915_WRITE(CACHE_MODE_0, dev_priv->regfile.saveCACHE_MODE_0 |
+                          0xffff0000);
 
        /* Memory arbitration state */
        I915_WRITE(MI_ARB_STATE, dev_priv->regfile.saveMI_ARB_STATE | 0xffff0000);
index c8c4112..cef38fd 100644 (file)
 #include "intel_drv.h"
 #include "i915_drv.h"
 
+#define dev_to_drm_minor(d) dev_get_drvdata((d))
+
 #ifdef CONFIG_PM
 static u32 calc_residency(struct drm_device *dev, const u32 reg)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u64 raw_time; /* 32b value may overflow during fixed point math */
+       u64 units = 128ULL, div = 100000ULL, bias = 100ULL;
 
        if (!intel_enable_rc6(dev))
                return 0;
 
-       raw_time = I915_READ(reg) * 128ULL;
-       return DIV_ROUND_UP_ULL(raw_time, 100000);
+       /* On VLV, residency time is in CZ units rather than 1.28us */
+       if (IS_VALLEYVIEW(dev)) {
+               u32 clkctl2;
+
+               clkctl2 = I915_READ(VLV_CLK_CTL2) >>
+                       CLK_CTL2_CZCOUNT_30NS_SHIFT;
+               if (!clkctl2) {
+                       WARN(!clkctl2, "bogus CZ count value");
+                       return 0;
+               }
+               units = DIV_ROUND_UP_ULL(30ULL * bias, (u64)clkctl2);
+               if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH)
+                       units <<= 8;
+
+               div = 1000000ULL * bias;
+       }
+
+       raw_time = I915_READ(reg) * units;
+       return DIV_ROUND_UP_ULL(raw_time, div);
 }
 
 static ssize_t
 show_rc6_mask(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *dminor = dev_to_drm_minor(kdev);
        return snprintf(buf, PAGE_SIZE, "%x\n", intel_enable_rc6(dminor->dev));
 }
 
 static ssize_t
 show_rc6_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *dminor = dev_get_drvdata(kdev);
        u32 rc6_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6);
        return snprintf(buf, PAGE_SIZE, "%u\n", rc6_residency);
 }
@@ -63,16 +83,20 @@ show_rc6_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 static ssize_t
 show_rc6p_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *dminor = dev_to_drm_minor(kdev);
        u32 rc6p_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6p);
+       if (IS_VALLEYVIEW(dminor->dev))
+               rc6p_residency = 0;
        return snprintf(buf, PAGE_SIZE, "%u\n", rc6p_residency);
 }
 
 static ssize_t
 show_rc6pp_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *dminor = dev_to_drm_minor(kdev);
        u32 rc6pp_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6pp);
+       if (IS_VALLEYVIEW(dminor->dev))
+               rc6pp_residency = 0;
        return snprintf(buf, PAGE_SIZE, "%u\n", rc6pp_residency);
 }
 
@@ -97,7 +121,7 @@ static struct attribute_group rc6_attr_group = {
 
 static int l3_access_valid(struct drm_device *dev, loff_t offset)
 {
-       if (!HAS_L3_GPU_CACHE(dev))
+       if (!HAS_L3_DPF(dev))
                return -EPERM;
 
        if (offset % 4 != 0)
@@ -115,31 +139,34 @@ i915_l3_read(struct file *filp, struct kobject *kobj,
             loff_t offset, size_t count)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
-       struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
+       struct drm_minor *dminor = dev_to_drm_minor(dev);
        struct drm_device *drm_dev = dminor->dev;
        struct drm_i915_private *dev_priv = drm_dev->dev_private;
-       uint32_t misccpctl;
-       int i, ret;
+       int slice = (int)(uintptr_t)attr->private;
+       int ret;
+
+       count = round_down(count, 4);
 
        ret = l3_access_valid(drm_dev, offset);
        if (ret)
                return ret;
 
+       count = min_t(size_t, GEN7_L3LOG_SIZE - offset, count);
+
        ret = i915_mutex_lock_interruptible(drm_dev);
        if (ret)
                return ret;
 
-       misccpctl = I915_READ(GEN7_MISCCPCTL);
-       I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE);
-
-       for (i = offset; count >= 4 && i < GEN7_L3LOG_SIZE; i += 4, count -= 4)
-               *((uint32_t *)(&buf[i])) = I915_READ(GEN7_L3LOG_BASE + i);
-
-       I915_WRITE(GEN7_MISCCPCTL, misccpctl);
+       if (dev_priv->l3_parity.remap_info[slice])
+               memcpy(buf,
+                      dev_priv->l3_parity.remap_info[slice] + (offset/4),
+                      count);
+       else
+               memset(buf, 0, count);
 
        mutex_unlock(&drm_dev->struct_mutex);
 
-       return i - offset;
+       return count;
 }
 
 static ssize_t
@@ -148,21 +175,26 @@ i915_l3_write(struct file *filp, struct kobject *kobj,
              loff_t offset, size_t count)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
-       struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
+       struct drm_minor *dminor = dev_to_drm_minor(dev);
        struct drm_device *drm_dev = dminor->dev;
        struct drm_i915_private *dev_priv = drm_dev->dev_private;
+       struct i915_hw_context *ctx;
        u32 *temp = NULL; /* Just here to make handling failures easy */
+       int slice = (int)(uintptr_t)attr->private;
        int ret;
 
        ret = l3_access_valid(drm_dev, offset);
        if (ret)
                return ret;
 
+       if (dev_priv->hw_contexts_disabled)
+               return -ENXIO;
+
        ret = i915_mutex_lock_interruptible(drm_dev);
        if (ret)
                return ret;
 
-       if (!dev_priv->l3_parity.remap_info) {
+       if (!dev_priv->l3_parity.remap_info[slice]) {
                temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL);
                if (!temp) {
                        mutex_unlock(&drm_dev->struct_mutex);
@@ -182,13 +214,13 @@ i915_l3_write(struct file *filp, struct kobject *kobj,
         * at this point it is left as a TODO.
        */
        if (temp)
-               dev_priv->l3_parity.remap_info = temp;
+               dev_priv->l3_parity.remap_info[slice] = temp;
 
-       memcpy(dev_priv->l3_parity.remap_info + (offset/4),
-              buf + (offset/4),
-              count);
+       memcpy(dev_priv->l3_parity.remap_info[slice] + (offset/4), buf, count);
 
-       i915_gem_l3_remap(drm_dev);
+       /* NB: We defer the remapping until we switch to the context */
+       list_for_each_entry(ctx, &dev_priv->context_list, link)
+               ctx->remap_slice |= (1<<slice);
 
        mutex_unlock(&drm_dev->struct_mutex);
 
@@ -200,17 +232,29 @@ static struct bin_attribute dpf_attrs = {
        .size = GEN7_L3LOG_SIZE,
        .read = i915_l3_read,
        .write = i915_l3_write,
-       .mmap = NULL
+       .mmap = NULL,
+       .private = (void *)0
+};
+
+static struct bin_attribute dpf_attrs_1 = {
+       .attr = {.name = "l3_parity_slice_1", .mode = (S_IRUSR | S_IWUSR)},
+       .size = GEN7_L3LOG_SIZE,
+       .read = i915_l3_read,
+       .write = i915_l3_write,
+       .mmap = NULL,
+       .private = (void *)1
 };
 
 static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
                                    struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *minor = dev_to_drm_minor(kdev);
        struct drm_device *dev = minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
 
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
        mutex_lock(&dev_priv->rps.hw_lock);
        if (IS_VALLEYVIEW(dev_priv->dev)) {
                u32 freq;
@@ -227,7 +271,7 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
 static ssize_t vlv_rpe_freq_mhz_show(struct device *kdev,
                                     struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *minor = dev_to_drm_minor(kdev);
        struct drm_device *dev = minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -238,11 +282,13 @@ static ssize_t vlv_rpe_freq_mhz_show(struct device *kdev,
 
 static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *minor = dev_to_drm_minor(kdev);
        struct drm_device *dev = minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
 
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
        mutex_lock(&dev_priv->rps.hw_lock);
        if (IS_VALLEYVIEW(dev_priv->dev))
                ret = vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.max_delay);
@@ -257,7 +303,7 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev,
                                     struct device_attribute *attr,
                                     const char *buf, size_t count)
 {
-       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *minor = dev_to_drm_minor(kdev);
        struct drm_device *dev = minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 val, rp_state_cap, hw_max, hw_min, non_oc_max;
@@ -267,6 +313,8 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev,
        if (ret)
                return ret;
 
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
        mutex_lock(&dev_priv->rps.hw_lock);
 
        if (IS_VALLEYVIEW(dev_priv->dev)) {
@@ -310,11 +358,13 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev,
 
 static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *minor = dev_to_drm_minor(kdev);
        struct drm_device *dev = minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
 
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
        mutex_lock(&dev_priv->rps.hw_lock);
        if (IS_VALLEYVIEW(dev_priv->dev))
                ret = vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.min_delay);
@@ -329,7 +379,7 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev,
                                     struct device_attribute *attr,
                                     const char *buf, size_t count)
 {
-       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *minor = dev_to_drm_minor(kdev);
        struct drm_device *dev = minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 val, rp_state_cap, hw_max, hw_min;
@@ -339,6 +389,8 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev,
        if (ret)
                return ret;
 
+       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
        mutex_lock(&dev_priv->rps.hw_lock);
 
        if (IS_VALLEYVIEW(dev)) {
@@ -388,7 +440,7 @@ static DEVICE_ATTR(gt_RPn_freq_mhz, S_IRUGO, gt_rp_mhz_show, NULL);
 /* For now we have a static number of RP states */
 static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *minor = dev_to_drm_minor(kdev);
        struct drm_device *dev = minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 val, rp_state_cap;
@@ -436,7 +488,7 @@ static ssize_t error_state_read(struct file *filp, struct kobject *kobj,
 {
 
        struct device *kdev = container_of(kobj, struct device, kobj);
-       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *minor = dev_to_drm_minor(kdev);
        struct drm_device *dev = minor->dev;
        struct i915_error_state_file_priv error_priv;
        struct drm_i915_error_state_buf error_str;
@@ -471,7 +523,7 @@ static ssize_t error_state_write(struct file *file, struct kobject *kobj,
                                 loff_t off, size_t count)
 {
        struct device *kdev = container_of(kobj, struct device, kobj);
-       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_minor *minor = dev_to_drm_minor(kdev);
        struct drm_device *dev = minor->dev;
        int ret;
 
@@ -501,27 +553,34 @@ void i915_setup_sysfs(struct drm_device *dev)
 
 #ifdef CONFIG_PM
        if (INTEL_INFO(dev)->gen >= 6) {
-               ret = sysfs_merge_group(&dev->primary->kdev.kobj,
+               ret = sysfs_merge_group(&dev->primary->kdev->kobj,
                                        &rc6_attr_group);
                if (ret)
                        DRM_ERROR("RC6 residency sysfs setup failed\n");
        }
 #endif
-       if (HAS_L3_GPU_CACHE(dev)) {
-               ret = device_create_bin_file(&dev->primary->kdev, &dpf_attrs);
+       if (HAS_L3_DPF(dev)) {
+               ret = device_create_bin_file(dev->primary->kdev, &dpf_attrs);
                if (ret)
                        DRM_ERROR("l3 parity sysfs setup failed\n");
+
+               if (NUM_L3_SLICES(dev) > 1) {
+                       ret = device_create_bin_file(dev->primary->kdev,
+                                                    &dpf_attrs_1);
+                       if (ret)
+                               DRM_ERROR("l3 parity slice 1 setup failed\n");
+               }
        }
 
        ret = 0;
        if (IS_VALLEYVIEW(dev))
-               ret = sysfs_create_files(&dev->primary->kdev.kobj, vlv_attrs);
+               ret = sysfs_create_files(&dev->primary->kdev->kobj, vlv_attrs);
        else if (INTEL_INFO(dev)->gen >= 6)
-               ret = sysfs_create_files(&dev->primary->kdev.kobj, gen6_attrs);
+               ret = sysfs_create_files(&dev->primary->kdev->kobj, gen6_attrs);
        if (ret)
                DRM_ERROR("RPS sysfs setup failed\n");
 
-       ret = sysfs_create_bin_file(&dev->primary->kdev.kobj,
+       ret = sysfs_create_bin_file(&dev->primary->kdev->kobj,
                                    &error_state_attr);
        if (ret)
                DRM_ERROR("error_state sysfs setup failed\n");
@@ -529,13 +588,14 @@ void i915_setup_sysfs(struct drm_device *dev)
 
 void i915_teardown_sysfs(struct drm_device *dev)
 {
-       sysfs_remove_bin_file(&dev->primary->kdev.kobj, &error_state_attr);
+       sysfs_remove_bin_file(&dev->primary->kdev->kobj, &error_state_attr);
        if (IS_VALLEYVIEW(dev))
-               sysfs_remove_files(&dev->primary->kdev.kobj, vlv_attrs);
+               sysfs_remove_files(&dev->primary->kdev->kobj, vlv_attrs);
        else
-               sysfs_remove_files(&dev->primary->kdev.kobj, gen6_attrs);
-       device_remove_bin_file(&dev->primary->kdev,  &dpf_attrs);
+               sysfs_remove_files(&dev->primary->kdev->kobj, gen6_attrs);
+       device_remove_bin_file(dev->primary->kdev,  &dpf_attrs_1);
+       device_remove_bin_file(dev->primary->kdev,  &dpf_attrs);
 #ifdef CONFIG_PM
-       sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group);
+       sysfs_unmerge_group(&dev->primary->kdev->kobj, &rc6_attr_group);
 #endif
 }
index e2c5ee6..6e580c9 100644 (file)
@@ -233,6 +233,47 @@ TRACE_EVENT(i915_gem_evict_everything,
            TP_printk("dev=%d", __entry->dev)
 );
 
+TRACE_EVENT(i915_gem_evict_vm,
+           TP_PROTO(struct i915_address_space *vm),
+           TP_ARGS(vm),
+
+           TP_STRUCT__entry(
+                            __field(struct i915_address_space *, vm)
+                           ),
+
+           TP_fast_assign(
+                          __entry->vm = vm;
+                         ),
+
+           TP_printk("dev=%d, vm=%p", __entry->vm->dev->primary->index, __entry->vm)
+);
+
+TRACE_EVENT(i915_gem_ring_sync_to,
+           TP_PROTO(struct intel_ring_buffer *from,
+                    struct intel_ring_buffer *to,
+                    u32 seqno),
+           TP_ARGS(from, to, seqno),
+
+           TP_STRUCT__entry(
+                            __field(u32, dev)
+                            __field(u32, sync_from)
+                            __field(u32, sync_to)
+                            __field(u32, seqno)
+                            ),
+
+           TP_fast_assign(
+                          __entry->dev = from->dev->primary->index;
+                          __entry->sync_from = from->id;
+                          __entry->sync_to = to->id;
+                          __entry->seqno = seqno;
+                          ),
+
+           TP_printk("dev=%u, sync-from=%u, sync-to=%u, seqno=%u",
+                     __entry->dev,
+                     __entry->sync_from, __entry->sync_to,
+                     __entry->seqno)
+);
+
 TRACE_EVENT(i915_gem_ring_dispatch,
            TP_PROTO(struct intel_ring_buffer *ring, u32 seqno, u32 flags),
            TP_ARGS(ring, seqno, flags),
@@ -304,9 +345,24 @@ DEFINE_EVENT(i915_gem_request, i915_gem_request_add,
            TP_ARGS(ring, seqno)
 );
 
-DEFINE_EVENT(i915_gem_request, i915_gem_request_complete,
-           TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
-           TP_ARGS(ring, seqno)
+TRACE_EVENT(i915_gem_request_complete,
+           TP_PROTO(struct intel_ring_buffer *ring),
+           TP_ARGS(ring),
+
+           TP_STRUCT__entry(
+                            __field(u32, dev)
+                            __field(u32, ring)
+                            __field(u32, seqno)
+                            ),
+
+           TP_fast_assign(
+                          __entry->dev = ring->dev->primary->index;
+                          __entry->ring = ring->id;
+                          __entry->seqno = ring->get_seqno(ring, false);
+                          ),
+
+           TP_printk("dev=%u, ring=%u, seqno=%u",
+                     __entry->dev, __entry->ring, __entry->seqno)
 );
 
 DEFINE_EVENT(i915_gem_request, i915_gem_request_retire,
index 43959ed..dfff090 100644 (file)
@@ -196,7 +196,7 @@ static bool intel_dsm_pci_probe(struct pci_dev *pdev)
        acpi_handle dhandle;
        int ret;
 
-       dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
+       dhandle = ACPI_HANDLE(&pdev->dev);
        if (!dhandle)
                return false;
 
index 53f2bed..6dd622d 100644 (file)
@@ -389,7 +389,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
 {
        struct sdvo_device_mapping *p_mapping;
        struct bdb_general_definitions *p_defs;
-       struct child_device_config *p_child;
+       union child_device_config *p_child;
        int i, child_device_num, count;
        u16     block_size;
 
@@ -416,36 +416,36 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
        count = 0;
        for (i = 0; i < child_device_num; i++) {
                p_child = &(p_defs->devices[i]);
-               if (!p_child->device_type) {
+               if (!p_child->old.device_type) {
                        /* skip the device block if device type is invalid */
                        continue;
                }
-               if (p_child->slave_addr != SLAVE_ADDR1 &&
-                       p_child->slave_addr != SLAVE_ADDR2) {
+               if (p_child->old.slave_addr != SLAVE_ADDR1 &&
+                       p_child->old.slave_addr != SLAVE_ADDR2) {
                        /*
                         * If the slave address is neither 0x70 nor 0x72,
                         * it is not a SDVO device. Skip it.
                         */
                        continue;
                }
-               if (p_child->dvo_port != DEVICE_PORT_DVOB &&
-                       p_child->dvo_port != DEVICE_PORT_DVOC) {
+               if (p_child->old.dvo_port != DEVICE_PORT_DVOB &&
+                       p_child->old.dvo_port != DEVICE_PORT_DVOC) {
                        /* skip the incorrect SDVO port */
                        DRM_DEBUG_KMS("Incorrect SDVO port. Skip it\n");
                        continue;
                }
                DRM_DEBUG_KMS("the SDVO device with slave addr %2x is found on"
                                " %s port\n",
-                               p_child->slave_addr,
-                               (p_child->dvo_port == DEVICE_PORT_DVOB) ?
+                               p_child->old.slave_addr,
+                               (p_child->old.dvo_port == DEVICE_PORT_DVOB) ?
                                        "SDVOB" : "SDVOC");
-               p_mapping = &(dev_priv->sdvo_mappings[p_child->dvo_port - 1]);
+               p_mapping = &(dev_priv->sdvo_mappings[p_child->old.dvo_port - 1]);
                if (!p_mapping->initialized) {
-                       p_mapping->dvo_port = p_child->dvo_port;
-                       p_mapping->slave_addr = p_child->slave_addr;
-                       p_mapping->dvo_wiring = p_child->dvo_wiring;
-                       p_mapping->ddc_pin = p_child->ddc_pin;
-                       p_mapping->i2c_pin = p_child->i2c_pin;
+                       p_mapping->dvo_port = p_child->old.dvo_port;
+                       p_mapping->slave_addr = p_child->old.slave_addr;
+                       p_mapping->dvo_wiring = p_child->old.dvo_wiring;
+                       p_mapping->ddc_pin = p_child->old.ddc_pin;
+                       p_mapping->i2c_pin = p_child->old.i2c_pin;
                        p_mapping->initialized = 1;
                        DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d\n",
                                      p_mapping->dvo_port,
@@ -457,7 +457,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
                        DRM_DEBUG_KMS("Maybe one SDVO port is shared by "
                                         "two SDVO device.\n");
                }
-               if (p_child->slave2_addr) {
+               if (p_child->old.slave2_addr) {
                        /* Maybe this is a SDVO device with multiple inputs */
                        /* And the mapping info is not added */
                        DRM_DEBUG_KMS("there exists the slave2_addr. Maybe this"
@@ -477,15 +477,13 @@ static void
 parse_driver_features(struct drm_i915_private *dev_priv,
                       struct bdb_header *bdb)
 {
-       struct drm_device *dev = dev_priv->dev;
        struct bdb_driver_features *driver;
 
        driver = find_section(bdb, BDB_DRIVER_FEATURES);
        if (!driver)
                return;
 
-       if (SUPPORTS_EDP(dev) &&
-           driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
+       if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
                dev_priv->vbt.edp_support = 1;
 
        if (driver->dual_frequency)
@@ -501,7 +499,7 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
 
        edp = find_section(bdb, BDB_EDP);
        if (!edp) {
-               if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->vbt.edp_support)
+               if (dev_priv->vbt.edp_support)
                        DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported.\n");
                return;
        }
@@ -569,11 +567,149 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
 }
 
 static void
+parse_mipi(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
+{
+       struct bdb_mipi *mipi;
+
+       mipi = find_section(bdb, BDB_MIPI);
+       if (!mipi) {
+               DRM_DEBUG_KMS("No MIPI BDB found");
+               return;
+       }
+
+       /* XXX: add more info */
+       dev_priv->vbt.dsi.panel_id = mipi->panel_id;
+}
+
+static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
+                          struct bdb_header *bdb)
+{
+       union child_device_config *it, *child = NULL;
+       struct ddi_vbt_port_info *info = &dev_priv->vbt.ddi_port_info[port];
+       uint8_t hdmi_level_shift;
+       int i, j;
+       bool is_dvi, is_hdmi, is_dp, is_edp, is_crt;
+       uint8_t aux_channel;
+       /* Each DDI port can have more than one value on the "DVO Port" field,
+        * so look for all the possible values for each port and abort if more
+        * than one is found. */
+       int dvo_ports[][2] = {
+               {DVO_PORT_HDMIA, DVO_PORT_DPA},
+               {DVO_PORT_HDMIB, DVO_PORT_DPB},
+               {DVO_PORT_HDMIC, DVO_PORT_DPC},
+               {DVO_PORT_HDMID, DVO_PORT_DPD},
+               {DVO_PORT_CRT, -1 /* Port E can only be DVO_PORT_CRT */ },
+       };
+
+       /* Find the child device to use, abort if more than one found. */
+       for (i = 0; i < dev_priv->vbt.child_dev_num; i++) {
+               it = dev_priv->vbt.child_dev + i;
+
+               for (j = 0; j < 2; j++) {
+                       if (dvo_ports[port][j] == -1)
+                               break;
+
+                       if (it->common.dvo_port == dvo_ports[port][j]) {
+                               if (child) {
+                                       DRM_DEBUG_KMS("More than one child device for port %c in VBT.\n",
+                                                     port_name(port));
+                                       return;
+                               }
+                               child = it;
+                       }
+               }
+       }
+       if (!child)
+               return;
+
+       aux_channel = child->raw[25];
+
+       is_dvi = child->common.device_type & DEVICE_TYPE_TMDS_DVI_SIGNALING;
+       is_dp = child->common.device_type & DEVICE_TYPE_DISPLAYPORT_OUTPUT;
+       is_crt = child->common.device_type & DEVICE_TYPE_ANALOG_OUTPUT;
+       is_hdmi = is_dvi && (child->common.device_type & DEVICE_TYPE_NOT_HDMI_OUTPUT) == 0;
+       is_edp = is_dp && (child->common.device_type & DEVICE_TYPE_INTERNAL_CONNECTOR);
+
+       info->supports_dvi = is_dvi;
+       info->supports_hdmi = is_hdmi;
+       info->supports_dp = is_dp;
+
+       DRM_DEBUG_KMS("Port %c VBT info: DP:%d HDMI:%d DVI:%d EDP:%d CRT:%d\n",
+                     port_name(port), is_dp, is_hdmi, is_dvi, is_edp, is_crt);
+
+       if (is_edp && is_dvi)
+               DRM_DEBUG_KMS("Internal DP port %c is TMDS compatible\n",
+                             port_name(port));
+       if (is_crt && port != PORT_E)
+               DRM_DEBUG_KMS("Port %c is analog\n", port_name(port));
+       if (is_crt && (is_dvi || is_dp))
+               DRM_DEBUG_KMS("Analog port %c is also DP or TMDS compatible\n",
+                             port_name(port));
+       if (is_dvi && (port == PORT_A || port == PORT_E))
+               DRM_DEBUG_KMS("Port %c is TMDS compabile\n", port_name(port));
+       if (!is_dvi && !is_dp && !is_crt)
+               DRM_DEBUG_KMS("Port %c is not DP/TMDS/CRT compatible\n",
+                             port_name(port));
+       if (is_edp && (port == PORT_B || port == PORT_C || port == PORT_E))
+               DRM_DEBUG_KMS("Port %c is internal DP\n", port_name(port));
+
+       if (is_dvi) {
+               if (child->common.ddc_pin == 0x05 && port != PORT_B)
+                       DRM_DEBUG_KMS("Unexpected DDC pin for port B\n");
+               if (child->common.ddc_pin == 0x04 && port != PORT_C)
+                       DRM_DEBUG_KMS("Unexpected DDC pin for port C\n");
+               if (child->common.ddc_pin == 0x06 && port != PORT_D)
+                       DRM_DEBUG_KMS("Unexpected DDC pin for port D\n");
+       }
+
+       if (is_dp) {
+               if (aux_channel == 0x40 && port != PORT_A)
+                       DRM_DEBUG_KMS("Unexpected AUX channel for port A\n");
+               if (aux_channel == 0x10 && port != PORT_B)
+                       DRM_DEBUG_KMS("Unexpected AUX channel for port B\n");
+               if (aux_channel == 0x20 && port != PORT_C)
+                       DRM_DEBUG_KMS("Unexpected AUX channel for port C\n");
+               if (aux_channel == 0x30 && port != PORT_D)
+                       DRM_DEBUG_KMS("Unexpected AUX channel for port D\n");
+       }
+
+       if (bdb->version >= 158) {
+               /* The VBT HDMI level shift values match the table we have. */
+               hdmi_level_shift = child->raw[7] & 0xF;
+               if (hdmi_level_shift < 0xC) {
+                       DRM_DEBUG_KMS("VBT HDMI level shift for port %c: %d\n",
+                                     port_name(port),
+                                     hdmi_level_shift);
+                       info->hdmi_level_shift = hdmi_level_shift;
+               }
+       }
+}
+
+static void parse_ddi_ports(struct drm_i915_private *dev_priv,
+                           struct bdb_header *bdb)
+{
+       struct drm_device *dev = dev_priv->dev;
+       enum port port;
+
+       if (!HAS_DDI(dev))
+               return;
+
+       if (!dev_priv->vbt.child_dev_num)
+               return;
+
+       if (bdb->version < 155)
+               return;
+
+       for (port = PORT_A; port < I915_MAX_PORTS; port++)
+               parse_ddi_port(dev_priv, port, bdb);
+}
+
+static void
 parse_device_mapping(struct drm_i915_private *dev_priv,
                       struct bdb_header *bdb)
 {
        struct bdb_general_definitions *p_defs;
-       struct child_device_config *p_child, *child_dev_ptr;
+       union child_device_config *p_child, *child_dev_ptr;
        int i, child_device_num, count;
        u16     block_size;
 
@@ -601,7 +737,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
        /* get the number of child device that is present */
        for (i = 0; i < child_device_num; i++) {
                p_child = &(p_defs->devices[i]);
-               if (!p_child->device_type) {
+               if (!p_child->common.device_type) {
                        /* skip the device block if device type is invalid */
                        continue;
                }
@@ -621,7 +757,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
        count = 0;
        for (i = 0; i < child_device_num; i++) {
                p_child = &(p_defs->devices[i]);
-               if (!p_child->device_type) {
+               if (!p_child->common.device_type) {
                        /* skip the device block if device type is invalid */
                        continue;
                }
@@ -637,6 +773,7 @@ static void
 init_vbt_defaults(struct drm_i915_private *dev_priv)
 {
        struct drm_device *dev = dev_priv->dev;
+       enum port port;
 
        dev_priv->vbt.crt_ddc_pin = GMBUS_PORT_VGADDC;
 
@@ -655,6 +792,18 @@ init_vbt_defaults(struct drm_i915_private *dev_priv)
        dev_priv->vbt.lvds_use_ssc = 1;
        dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1);
        DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->vbt.lvds_ssc_freq);
+
+       for (port = PORT_A; port < I915_MAX_PORTS; port++) {
+               struct ddi_vbt_port_info *info =
+                       &dev_priv->vbt.ddi_port_info[port];
+
+               /* Recommended BSpec default: 800mV 0dB. */
+               info->hdmi_level_shift = 6;
+
+               info->supports_dvi = (port != PORT_A && port != PORT_E);
+               info->supports_hdmi = info->supports_dvi;
+               info->supports_dp = (port != PORT_E);
+       }
 }
 
 static int __init intel_no_opregion_vbt_callback(const struct dmi_system_id *id)
@@ -745,6 +894,8 @@ intel_parse_bios(struct drm_device *dev)
        parse_device_mapping(dev_priv, bdb);
        parse_driver_features(dev_priv, bdb);
        parse_edp(dev_priv, bdb);
+       parse_mipi(dev_priv, bdb);
+       parse_ddi_ports(dev_priv, bdb);
 
        if (bios)
                pci_unmap_rom(pdev, bios);
index e088d6f..f580a2b 100644 (file)
@@ -104,6 +104,7 @@ struct vbios_data {
 #define BDB_LVDS_LFP_DATA       42
 #define BDB_LVDS_BACKLIGHT      43
 #define BDB_LVDS_POWER          44
+#define BDB_MIPI                50
 #define BDB_SKIP               254 /* VBIOS private block, ignore */
 
 struct bdb_general_features {
@@ -201,7 +202,10 @@ struct bdb_general_features {
 #define DEVICE_PORT_DVOB       0x01
 #define DEVICE_PORT_DVOC       0x02
 
-struct child_device_config {
+/* We used to keep this struct but without any version control. We should avoid
+ * using it in the future, but it should be safe to keep using it in the old
+ * code. */
+struct old_child_dev_config {
        u16 handle;
        u16 device_type;
        u8  device_id[10]; /* ascii string */
@@ -223,6 +227,32 @@ struct child_device_config {
        u8  dvo_function;
 } __attribute__((packed));
 
+/* This one contains field offsets that are known to be common for all BDB
+ * versions. Notice that the meaning of the contents contents may still change,
+ * but at least the offsets are consistent. */
+struct common_child_dev_config {
+       u16 handle;
+       u16 device_type;
+       u8 not_common1[12];
+       u8 dvo_port;
+       u8 not_common2[2];
+       u8 ddc_pin;
+       u16 edid_ptr;
+} __attribute__((packed));
+
+/* This field changes depending on the BDB version, so the most reliable way to
+ * read it is by checking the BDB version and reading the raw pointer. */
+union child_device_config {
+       /* This one is safe to be used anywhere, but the code should still check
+        * the BDB version. */
+       u8 raw[33];
+       /* This one should only be kept for legacy code. */
+       struct old_child_dev_config old;
+       /* This one should also be safe to use anywhere, even without version
+        * checks. */
+       struct common_child_dev_config common;
+};
+
 struct bdb_general_definitions {
        /* DDC GPIO */
        u8 crt_ddc_gmbus_pin;
@@ -248,7 +278,7 @@ struct bdb_general_definitions {
         * number = (block_size - sizeof(bdb_general_definitions))/
         *           sizeof(child_device_config);
         */
-       struct child_device_config devices[0];
+       union child_device_config devices[0];
 } __attribute__((packed));
 
 struct bdb_lvds_options {
@@ -608,6 +638,40 @@ int intel_parse_bios(struct drm_device *dev);
 #define         DEVICE_TYPE_DP         0x68C6
 #define         DEVICE_TYPE_eDP        0x78C6
 
+#define  DEVICE_TYPE_CLASS_EXTENSION   (1 << 15)
+#define  DEVICE_TYPE_POWER_MANAGEMENT  (1 << 14)
+#define  DEVICE_TYPE_HOTPLUG_SIGNALING (1 << 13)
+#define  DEVICE_TYPE_INTERNAL_CONNECTOR        (1 << 12)
+#define  DEVICE_TYPE_NOT_HDMI_OUTPUT   (1 << 11)
+#define  DEVICE_TYPE_MIPI_OUTPUT       (1 << 10)
+#define  DEVICE_TYPE_COMPOSITE_OUTPUT  (1 << 9)
+#define  DEVICE_TYPE_DUAL_CHANNEL      (1 << 8)
+#define  DEVICE_TYPE_HIGH_SPEED_LINK   (1 << 6)
+#define  DEVICE_TYPE_LVDS_SINGALING    (1 << 5)
+#define  DEVICE_TYPE_TMDS_DVI_SIGNALING        (1 << 4)
+#define  DEVICE_TYPE_VIDEO_SIGNALING   (1 << 3)
+#define  DEVICE_TYPE_DISPLAYPORT_OUTPUT        (1 << 2)
+#define  DEVICE_TYPE_DIGITAL_OUTPUT    (1 << 1)
+#define  DEVICE_TYPE_ANALOG_OUTPUT     (1 << 0)
+
+/*
+ * Bits we care about when checking for DEVICE_TYPE_eDP
+ * Depending on the system, the other bits may or may not
+ * be set for eDP outputs.
+ */
+#define DEVICE_TYPE_eDP_BITS \
+       (DEVICE_TYPE_INTERNAL_CONNECTOR | \
+        DEVICE_TYPE_NOT_HDMI_OUTPUT | \
+        DEVICE_TYPE_MIPI_OUTPUT | \
+        DEVICE_TYPE_COMPOSITE_OUTPUT | \
+        DEVICE_TYPE_DUAL_CHANNEL | \
+        DEVICE_TYPE_LVDS_SINGALING | \
+        DEVICE_TYPE_TMDS_DVI_SIGNALING | \
+        DEVICE_TYPE_VIDEO_SIGNALING | \
+        DEVICE_TYPE_DISPLAYPORT_OUTPUT | \
+        DEVICE_TYPE_DIGITAL_OUTPUT | \
+        DEVICE_TYPE_ANALOG_OUTPUT)
+
 /* define the DVO port for HDMI output type */
 #define                DVO_B           1
 #define                DVO_C           2
@@ -618,4 +682,57 @@ int intel_parse_bios(struct drm_device *dev);
 #define                PORT_IDPC       8
 #define                PORT_IDPD       9
 
+/* Possible values for the "DVO Port" field for versions >= 155: */
+#define DVO_PORT_HDMIA 0
+#define DVO_PORT_HDMIB 1
+#define DVO_PORT_HDMIC 2
+#define DVO_PORT_HDMID 3
+#define DVO_PORT_LVDS  4
+#define DVO_PORT_TV    5
+#define DVO_PORT_CRT   6
+#define DVO_PORT_DPB   7
+#define DVO_PORT_DPC   8
+#define DVO_PORT_DPD   9
+#define DVO_PORT_DPA   10
+
+/* MIPI DSI panel info */
+struct bdb_mipi {
+       u16 panel_id;
+       u16 bridge_revision;
+
+       /* General params */
+       u32 dithering:1;
+       u32 bpp_pixel_format:1;
+       u32 rsvd1:1;
+       u32 dphy_valid:1;
+       u32 resvd2:28;
+
+       u16 port_info;
+       u16 rsvd3:2;
+       u16 num_lanes:2;
+       u16 rsvd4:12;
+
+       /* DSI config */
+       u16 virt_ch_num:2;
+       u16 vtm:2;
+       u16 rsvd5:12;
+
+       u32 dsi_clock;
+       u32 bridge_ref_clk;
+       u16 rsvd_pwr;
+
+       /* Dphy Params */
+       u32 prepare_cnt:5;
+       u32 rsvd6:3;
+       u32 clk_zero_cnt:8;
+       u32 trail_cnt:5;
+       u32 rsvd7:3;
+       u32 exit_zero_cnt:6;
+       u32 rsvd8:2;
+
+       u32 hl_switch_cnt;
+       u32 lp_byte_clk;
+       u32 clk_lane_switch_cnt;
+} __attribute__((packed));
+
 #endif /* _I830_BIOS_H_ */
index 10d1de5..b5b1b9b 100644 (file)
@@ -107,7 +107,17 @@ static unsigned int intel_crt_get_flags(struct intel_encoder *encoder)
 static void intel_crt_get_config(struct intel_encoder *encoder,
                                 struct intel_crtc_config *pipe_config)
 {
+       struct drm_device *dev = encoder->base.dev;
+       int dotclock;
+
        pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder);
+
+       dotclock = pipe_config->port_clock;
+
+       if (HAS_PCH_SPLIT(dev))
+               ironlake_check_encoder_dotclock(pipe_config, dotclock);
+
+       pipe_config->adjusted_mode.crtc_clock = dotclock;
 }
 
 static void hsw_crt_get_config(struct intel_encoder *encoder,
@@ -264,7 +274,7 @@ static void intel_crt_mode_set(struct intel_encoder *encoder)
        struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
        u32 adpa;
 
-       if (HAS_PCH_SPLIT(dev))
+       if (INTEL_INFO(dev)->gen >= 5)
                adpa = ADPA_HOTPLUG_BITS;
        else
                adpa = 0;
@@ -366,9 +376,6 @@ static bool valleyview_crt_detect_hotplug(struct drm_connector *connector)
 
        DRM_DEBUG_KMS("valleyview hotplug adpa=0x%x, result %d\n", adpa, ret);
 
-       /* FIXME: debug force function and remove */
-       ret = true;
-
        return ret;
 }
 
@@ -670,7 +677,6 @@ intel_crt_detect(struct drm_connector *connector, bool force)
 
 static void intel_crt_destroy(struct drm_connector *connector)
 {
-       drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
        kfree(connector);
 }
@@ -776,7 +782,7 @@ void intel_crt_init(struct drm_device *dev)
        if (!crt)
                return;
 
-       intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
+       intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
        if (!intel_connector) {
                kfree(crt);
                return;
@@ -816,16 +822,15 @@ void intel_crt_init(struct drm_device *dev)
        crt->base.mode_set = intel_crt_mode_set;
        crt->base.disable = intel_disable_crt;
        crt->base.enable = intel_enable_crt;
-       if (IS_HASWELL(dev))
-               crt->base.get_config = hsw_crt_get_config;
-       else
-               crt->base.get_config = intel_crt_get_config;
        if (I915_HAS_HOTPLUG(dev))
                crt->base.hpd_pin = HPD_CRT;
-       if (HAS_DDI(dev))
+       if (HAS_DDI(dev)) {
+               crt->base.get_config = hsw_crt_get_config;
                crt->base.get_hw_state = intel_ddi_get_hw_state;
-       else
+       } else {
+               crt->base.get_config = intel_crt_get_config;
                crt->base.get_hw_state = intel_crt_get_hw_state;
+       }
        intel_connector->get_hw_state = intel_connector_get_hw_state;
 
        drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
index b53fff8..1591576 100644 (file)
@@ -42,7 +42,6 @@ static const u32 hsw_ddi_translations_dp[] = {
        0x80C30FFF, 0x000B0000,
        0x00FFFFFF, 0x00040006,
        0x80D75FFF, 0x000B0000,
-       0x00FFFFFF, 0x00040006          /* HDMI parameters */
 };
 
 static const u32 hsw_ddi_translations_fdi[] = {
@@ -55,10 +54,64 @@ static const u32 hsw_ddi_translations_fdi[] = {
        0x00C30FFF, 0x001E0000,
        0x00FFFFFF, 0x00060006,
        0x00D75FFF, 0x001E0000,
-       0x00FFFFFF, 0x00040006          /* HDMI parameters */
 };
 
-static enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
+static const u32 hsw_ddi_translations_hdmi[] = {
+                               /* Idx  NT mV diff      T mV diff       db  */
+       0x00FFFFFF, 0x0006000E, /* 0:   400             400             0   */
+       0x00E79FFF, 0x000E000C, /* 1:   400             500             2   */
+       0x00D75FFF, 0x0005000A, /* 2:   400             600             3.5 */
+       0x00FFFFFF, 0x0005000A, /* 3:   600             600             0   */
+       0x00E79FFF, 0x001D0007, /* 4:   600             750             2   */
+       0x00D75FFF, 0x000C0004, /* 5:   600             900             3.5 */
+       0x00FFFFFF, 0x00040006, /* 6:   800             800             0   */
+       0x80E79FFF, 0x00030002, /* 7:   800             1000            2   */
+       0x00FFFFFF, 0x00140005, /* 8:   850             850             0   */
+       0x00FFFFFF, 0x000C0004, /* 9:   900             900             0   */
+       0x00FFFFFF, 0x001C0003, /* 10:  950             950             0   */
+       0x80FFFFFF, 0x00030002, /* 11:  1000            1000            0   */
+};
+
+static const u32 bdw_ddi_translations_edp[] = {
+       0x00FFFFFF, 0x00000012,         /* DP parameters */
+       0x00EBAFFF, 0x00020011,
+       0x00C71FFF, 0x0006000F,
+       0x00FFFFFF, 0x00020011,
+       0x00DB6FFF, 0x0005000F,
+       0x00BEEFFF, 0x000A000C,
+       0x00FFFFFF, 0x0005000F,
+       0x00DB6FFF, 0x000A000C,
+       0x00FFFFFF, 0x000A000C,
+       0x00FFFFFF, 0x00140006          /* HDMI parameters 800mV 0dB*/
+};
+
+static const u32 bdw_ddi_translations_dp[] = {
+       0x00FFFFFF, 0x0007000E,         /* DP parameters */
+       0x00D75FFF, 0x000E000A,
+       0x00BEFFFF, 0x00140006,
+       0x00FFFFFF, 0x000E000A,
+       0x00D75FFF, 0x00180004,
+       0x80CB2FFF, 0x001B0002,
+       0x00F7DFFF, 0x00180004,
+       0x80D75FFF, 0x001B0002,
+       0x80FFFFFF, 0x001B0002,
+       0x00FFFFFF, 0x00140006          /* HDMI parameters 800mV 0dB*/
+};
+
+static const u32 bdw_ddi_translations_fdi[] = {
+       0x00FFFFFF, 0x0001000E,         /* FDI parameters */
+       0x00D75FFF, 0x0004000A,
+       0x00C30FFF, 0x00070006,
+       0x00AAAFFF, 0x000C0000,
+       0x00FFFFFF, 0x0004000A,
+       0x00D75FFF, 0x00090004,
+       0x00C30FFF, 0x000C0000,
+       0x00FFFFFF, 0x00070006,
+       0x00D75FFF, 0x000C0000,
+       0x00FFFFFF, 0x00140006          /* HDMI parameters 800mV 0dB*/
+};
+
+enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
 {
        struct drm_encoder *encoder = &intel_encoder->base;
        int type = intel_encoder->type;
@@ -78,8 +131,9 @@ static enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
        }
 }
 
-/* On Haswell, DDI port buffers must be programmed with correct values
- * in advance. The buffer values are different for FDI and DP modes,
+/*
+ * Starting with Haswell, DDI port buffers must be programmed with correct
+ * values in advance. The buffer values are different for FDI and DP modes,
  * but the HDMI/DVI fields are shared among those. So we program the DDI
  * in either FDI or DP modes only, as HDMI connections will work with both
  * of those
@@ -89,15 +143,58 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg;
        int i;
-       const u32 *ddi_translations = (port == PORT_E) ?
-               hsw_ddi_translations_fdi :
-               hsw_ddi_translations_dp;
+       int hdmi_level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift;
+       const u32 *ddi_translations_fdi;
+       const u32 *ddi_translations_dp;
+       const u32 *ddi_translations_edp;
+       const u32 *ddi_translations;
+
+       if (IS_BROADWELL(dev)) {
+               ddi_translations_fdi = bdw_ddi_translations_fdi;
+               ddi_translations_dp = bdw_ddi_translations_dp;
+               ddi_translations_edp = bdw_ddi_translations_edp;
+       } else if (IS_HASWELL(dev)) {
+               ddi_translations_fdi = hsw_ddi_translations_fdi;
+               ddi_translations_dp = hsw_ddi_translations_dp;
+               ddi_translations_edp = hsw_ddi_translations_dp;
+       } else {
+               WARN(1, "ddi translation table missing\n");
+               ddi_translations_edp = bdw_ddi_translations_dp;
+               ddi_translations_fdi = bdw_ddi_translations_fdi;
+               ddi_translations_dp = bdw_ddi_translations_dp;
+       }
+
+       switch (port) {
+       case PORT_A:
+               ddi_translations = ddi_translations_edp;
+               break;
+       case PORT_B:
+       case PORT_C:
+               ddi_translations = ddi_translations_dp;
+               break;
+       case PORT_D:
+               if (intel_dpd_is_edp(dev))
+                       ddi_translations = ddi_translations_edp;
+               else
+                       ddi_translations = ddi_translations_dp;
+               break;
+       case PORT_E:
+               ddi_translations = ddi_translations_fdi;
+               break;
+       default:
+               BUG();
+       }
 
        for (i = 0, reg = DDI_BUF_TRANS(port);
             i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) {
                I915_WRITE(reg, ddi_translations[i]);
                reg += 4;
        }
+       /* Entry 9 is for HDMI: */
+       for (i = 0; i < 2; i++) {
+               I915_WRITE(reg, hsw_ddi_translations_hdmi[hdmi_level * 2 + i]);
+               reg += 4;
+       }
 }
 
 /* Program DDI buffers translations for DP. By default, program ports A-D in DP
@@ -296,9 +393,6 @@ static void intel_ddi_mode_set(struct intel_encoder *encoder)
                        DRM_DEBUG_DRIVER("DP audio: write eld information\n");
                        intel_write_eld(&encoder->base, adjusted_mode);
                }
-
-               intel_dp_init_link_config(intel_dp);
-
        } else if (type == INTEL_OUTPUT_HDMI) {
                struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
 
@@ -739,7 +833,8 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
        struct drm_encoder *encoder = &intel_encoder->base;
-       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        enum pipe pipe = intel_crtc->pipe;
        enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
        enum port port = intel_ddi_get_encoder_port(intel_encoder);
@@ -767,18 +862,19 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
                BUG();
        }
 
-       if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC)
+       if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_PVSYNC)
                temp |= TRANS_DDI_PVSYNC;
-       if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC)
+       if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_PHSYNC)
                temp |= TRANS_DDI_PHSYNC;
 
        if (cpu_transcoder == TRANSCODER_EDP) {
                switch (pipe) {
                case PIPE_A:
-                       /* Can only use the always-on power well for eDP when
-                        * not using the panel fitter, and when not using motion
-                         * blur mitigation (which we don't support). */
-                       if (intel_crtc->config.pch_pfit.enabled)
+                       /* On Haswell, can only use the always-on power well for
+                        * eDP when not using the panel fitter, and when not
+                        * using motion blur mitigation (which we don't
+                        * support). */
+                       if (IS_HASWELL(dev) && intel_crtc->config.pch_pfit.enabled)
                                temp |= TRANS_DDI_EDP_INPUT_A_ONOFF;
                        else
                                temp |= TRANS_DDI_EDP_INPUT_A_ON;
@@ -1139,18 +1235,29 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
 
 int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv)
 {
+       struct drm_device *dev = dev_priv->dev;
        uint32_t lcpll = I915_READ(LCPLL_CTL);
+       uint32_t freq = lcpll & LCPLL_CLK_FREQ_MASK;
 
-       if (lcpll & LCPLL_CD_SOURCE_FCLK)
+       if (lcpll & LCPLL_CD_SOURCE_FCLK) {
                return 800000;
-       else if (I915_READ(HSW_FUSE_STRAP) & HSW_CDCLK_LIMIT)
+       } else if (I915_READ(HSW_FUSE_STRAP) & HSW_CDCLK_LIMIT) {
                return 450000;
-       else if ((lcpll & LCPLL_CLK_FREQ_MASK) == LCPLL_CLK_FREQ_450)
+       } else if (freq == LCPLL_CLK_FREQ_450) {
                return 450000;
-       else if (IS_ULT(dev_priv->dev))
-               return 337500;
-       else
-               return 540000;
+       } else if (IS_HASWELL(dev)) {
+               if (IS_ULT(dev))
+                       return 337500;
+               else
+                       return 540000;
+       } else {
+               if (freq == LCPLL_CLK_FREQ_54O_BDW)
+                       return 540000;
+               else if (freq == LCPLL_CLK_FREQ_337_5_BDW)
+                       return 337500;
+               else
+                       return 675000;
+       }
 }
 
 void intel_ddi_pll_init(struct drm_device *dev)
@@ -1202,7 +1309,7 @@ void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
 
        val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST |
              DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE;
-       if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN)
+       if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
                val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
        I915_WRITE(DP_TP_CTL(port), val);
        POSTING_READ(DP_TP_CTL(port));
@@ -1285,6 +1392,20 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
        default:
                break;
        }
+
+       switch (temp & TRANS_DDI_MODE_SELECT_MASK) {
+       case TRANS_DDI_MODE_SELECT_HDMI:
+       case TRANS_DDI_MODE_SELECT_DVI:
+       case TRANS_DDI_MODE_SELECT_FDI:
+               break;
+       case TRANS_DDI_MODE_SELECT_DP_SST:
+       case TRANS_DDI_MODE_SELECT_DP_MST:
+               pipe_config->has_dp_encoder = true;
+               intel_dp_get_m_n(intel_crtc, pipe_config);
+               break;
+       default:
+               break;
+       }
 }
 
 static void intel_ddi_destroy(struct drm_encoder *encoder)
@@ -1314,6 +1435,41 @@ static const struct drm_encoder_funcs intel_ddi_funcs = {
        .destroy = intel_ddi_destroy,
 };
 
+static struct intel_connector *
+intel_ddi_init_dp_connector(struct intel_digital_port *intel_dig_port)
+{
+       struct intel_connector *connector;
+       enum port port = intel_dig_port->port;
+
+       connector = kzalloc(sizeof(*connector), GFP_KERNEL);
+       if (!connector)
+               return NULL;
+
+       intel_dig_port->dp.output_reg = DDI_BUF_CTL(port);
+       if (!intel_dp_init_connector(intel_dig_port, connector)) {
+               kfree(connector);
+               return NULL;
+       }
+
+       return connector;
+}
+
+static struct intel_connector *
+intel_ddi_init_hdmi_connector(struct intel_digital_port *intel_dig_port)
+{
+       struct intel_connector *connector;
+       enum port port = intel_dig_port->port;
+
+       connector = kzalloc(sizeof(*connector), GFP_KERNEL);
+       if (!connector)
+               return NULL;
+
+       intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port);
+       intel_hdmi_init_connector(intel_dig_port, connector);
+
+       return connector;
+}
+
 void intel_ddi_init(struct drm_device *dev, enum port port)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1322,17 +1478,22 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
        struct drm_encoder *encoder;
        struct intel_connector *hdmi_connector = NULL;
        struct intel_connector *dp_connector = NULL;
+       bool init_hdmi, init_dp;
+
+       init_hdmi = (dev_priv->vbt.ddi_port_info[port].supports_dvi ||
+                    dev_priv->vbt.ddi_port_info[port].supports_hdmi);
+       init_dp = dev_priv->vbt.ddi_port_info[port].supports_dp;
+       if (!init_dp && !init_hdmi) {
+               DRM_DEBUG_KMS("VBT says port %c is not DVI/HDMI/DP compatible\n",
+                             port_name(port));
+               init_hdmi = true;
+               init_dp = true;
+       }
 
-       intel_dig_port = kzalloc(sizeof(struct intel_digital_port), GFP_KERNEL);
+       intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL);
        if (!intel_dig_port)
                return;
 
-       dp_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
-       if (!dp_connector) {
-               kfree(intel_dig_port);
-               return;
-       }
-
        intel_encoder = &intel_dig_port->base;
        encoder = &intel_encoder->base;
 
@@ -1352,28 +1513,22 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
        intel_dig_port->saved_port_bits = I915_READ(DDI_BUF_CTL(port)) &
                                          (DDI_BUF_PORT_REVERSAL |
                                           DDI_A_4_LANES);
-       intel_dig_port->dp.output_reg = DDI_BUF_CTL(port);
 
        intel_encoder->type = INTEL_OUTPUT_UNKNOWN;
        intel_encoder->crtc_mask =  (1 << 0) | (1 << 1) | (1 << 2);
        intel_encoder->cloneable = false;
        intel_encoder->hot_plug = intel_ddi_hot_plug;
 
-       if (!intel_dp_init_connector(intel_dig_port, dp_connector)) {
-               drm_encoder_cleanup(encoder);
-               kfree(intel_dig_port);
-               kfree(dp_connector);
-               return;
-       }
+       if (init_dp)
+               dp_connector = intel_ddi_init_dp_connector(intel_dig_port);
 
-       if (intel_encoder->type != INTEL_OUTPUT_EDP) {
-               hdmi_connector = kzalloc(sizeof(struct intel_connector),
-                                        GFP_KERNEL);
-               if (!hdmi_connector) {
-                       return;
-               }
+       /* In theory we don't need the encoder->type check, but leave it just in
+        * case we have some really bad VBTs... */
+       if (intel_encoder->type != INTEL_OUTPUT_EDP && init_hdmi)
+               hdmi_connector = intel_ddi_init_hdmi_connector(intel_dig_port);
 
-               intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port);
-               intel_hdmi_init_connector(intel_dig_port, hdmi_connector);
+       if (!dp_connector && !hdmi_connector) {
+               drm_encoder_cleanup(encoder);
+               kfree(intel_dig_port);
        }
 }
index d78d33f..3cddd50 100644 (file)
 #include <drm/drm_crtc_helper.h>
 #include <linux/dma_remapping.h>
 
-bool intel_pipe_has_type(struct drm_crtc *crtc, int type);
 static void intel_increase_pllclock(struct drm_crtc *crtc);
 static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
 
 static void i9xx_crtc_clock_get(struct intel_crtc *crtc,
                                struct intel_crtc_config *pipe_config);
-static void ironlake_crtc_clock_get(struct intel_crtc *crtc,
-                                   struct intel_crtc_config *pipe_config);
+static void ironlake_pch_clock_get(struct intel_crtc *crtc,
+                                  struct intel_crtc_config *pipe_config);
 
 static int intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
                          int x, int y, struct drm_framebuffer *old_fb);
@@ -69,9 +68,6 @@ struct intel_limit {
        intel_p2_t          p2;
 };
 
-/* FDI */
-#define IRONLAKE_FDI_FREQ              2700000 /* in kHz for mode->clock */
-
 int
 intel_pch_rawclk(struct drm_device *dev)
 {
@@ -313,44 +309,44 @@ static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
                .p2_slow = 7, .p2_fast = 7 },
 };
 
-static const intel_limit_t intel_limits_vlv_dac = {
-       .dot = { .min = 25000, .max = 270000 },
+static const intel_limit_t intel_limits_vlv = {
+        /*
+         * These are the data rate limits (measured in fast clocks)
+         * since those are the strictest limits we have. The fast
+         * clock and actual rate limits are more relaxed, so checking
+         * them would make no difference.
+         */
+       .dot = { .min = 25000 * 5, .max = 270000 * 5 },
        .vco = { .min = 4000000, .max = 6000000 },
        .n = { .min = 1, .max = 7 },
-       .m = { .min = 22, .max = 450 }, /* guess */
        .m1 = { .min = 2, .max = 3 },
        .m2 = { .min = 11, .max = 156 },
-       .p = { .min = 10, .max = 30 },
-       .p1 = { .min = 1, .max = 3 },
-       .p2 = { .dot_limit = 270000,
-               .p2_slow = 2, .p2_fast = 20 },
-};
-
-static const intel_limit_t intel_limits_vlv_hdmi = {
-       .dot = { .min = 25000, .max = 270000 },
-       .vco = { .min = 4000000, .max = 6000000 },
-       .n = { .min = 1, .max = 7 },
-       .m = { .min = 60, .max = 300 }, /* guess */
-       .m1 = { .min = 2, .max = 3 },
-       .m2 = { .min = 11, .max = 156 },
-       .p = { .min = 10, .max = 30 },
        .p1 = { .min = 2, .max = 3 },
-       .p2 = { .dot_limit = 270000,
-               .p2_slow = 2, .p2_fast = 20 },
+       .p2 = { .p2_slow = 2, .p2_fast = 20 }, /* slow=min, fast=max */
 };
 
-static const intel_limit_t intel_limits_vlv_dp = {
-       .dot = { .min = 25000, .max = 270000 },
-       .vco = { .min = 4000000, .max = 6000000 },
-       .n = { .min = 1, .max = 7 },
-       .m = { .min = 22, .max = 450 },
-       .m1 = { .min = 2, .max = 3 },
-       .m2 = { .min = 11, .max = 156 },
-       .p = { .min = 10, .max = 30 },
-       .p1 = { .min = 1, .max = 3 },
-       .p2 = { .dot_limit = 270000,
-               .p2_slow = 2, .p2_fast = 20 },
-};
+static void vlv_clock(int refclk, intel_clock_t *clock)
+{
+       clock->m = clock->m1 * clock->m2;
+       clock->p = clock->p1 * clock->p2;
+       clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n);
+       clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);
+}
+
+/**
+ * Returns whether any output on the specified pipe is of the specified type
+ */
+static bool intel_pipe_has_type(struct drm_crtc *crtc, int type)
+{
+       struct drm_device *dev = crtc->dev;
+       struct intel_encoder *encoder;
+
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->type == type)
+                       return true;
+
+       return false;
+}
 
 static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
                                                int refclk)
@@ -412,12 +408,7 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk)
                else
                        limit = &intel_limits_pineview_sdvo;
        } else if (IS_VALLEYVIEW(dev)) {
-               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG))
-                       limit = &intel_limits_vlv_dac;
-               else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI))
-                       limit = &intel_limits_vlv_hdmi;
-               else
-                       limit = &intel_limits_vlv_dp;
+               limit = &intel_limits_vlv;
        } else if (!IS_GEN2(dev)) {
                if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
                        limit = &intel_limits_i9xx_lvds;
@@ -439,8 +430,8 @@ static void pineview_clock(int refclk, intel_clock_t *clock)
 {
        clock->m = clock->m2 + 2;
        clock->p = clock->p1 * clock->p2;
-       clock->vco = refclk * clock->m / clock->n;
-       clock->dot = clock->vco / clock->p;
+       clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n);
+       clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);
 }
 
 static uint32_t i9xx_dpll_compute_m(struct dpll *dpll)
@@ -452,23 +443,8 @@ static void i9xx_clock(int refclk, intel_clock_t *clock)
 {
        clock->m = i9xx_dpll_compute_m(clock);
        clock->p = clock->p1 * clock->p2;
-       clock->vco = refclk * clock->m / (clock->n + 2);
-       clock->dot = clock->vco / clock->p;
-}
-
-/**
- * Returns whether any output on the specified pipe is of the specified type
- */
-bool intel_pipe_has_type(struct drm_crtc *crtc, int type)
-{
-       struct drm_device *dev = crtc->dev;
-       struct intel_encoder *encoder;
-
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->type == type)
-                       return true;
-
-       return false;
+       clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n + 2);
+       clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);
 }
 
 #define INTELPllInvalid(s)   do { /* DRM_DEBUG(s); */ return false; } while (0)
@@ -481,20 +457,26 @@ static bool intel_PLL_is_valid(struct drm_device *dev,
                               const intel_limit_t *limit,
                               const intel_clock_t *clock)
 {
+       if (clock->n   < limit->n.min   || limit->n.max   < clock->n)
+               INTELPllInvalid("n out of range\n");
        if (clock->p1  < limit->p1.min  || limit->p1.max  < clock->p1)
                INTELPllInvalid("p1 out of range\n");
-       if (clock->p   < limit->p.min   || limit->p.max   < clock->p)
-               INTELPllInvalid("p out of range\n");
        if (clock->m2  < limit->m2.min  || limit->m2.max  < clock->m2)
                INTELPllInvalid("m2 out of range\n");
        if (clock->m1  < limit->m1.min  || limit->m1.max  < clock->m1)
                INTELPllInvalid("m1 out of range\n");
-       if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev))
-               INTELPllInvalid("m1 <= m2\n");
-       if (clock->m   < limit->m.min   || limit->m.max   < clock->m)
-               INTELPllInvalid("m out of range\n");
-       if (clock->n   < limit->n.min   || limit->n.max   < clock->n)
-               INTELPllInvalid("n out of range\n");
+
+       if (!IS_PINEVIEW(dev) && !IS_VALLEYVIEW(dev))
+               if (clock->m1 <= clock->m2)
+                       INTELPllInvalid("m1 <= m2\n");
+
+       if (!IS_VALLEYVIEW(dev)) {
+               if (clock->p < limit->p.min || limit->p.max < clock->p)
+                       INTELPllInvalid("p out of range\n");
+               if (clock->m < limit->m.min || limit->m.max < clock->m)
+                       INTELPllInvalid("m out of range\n");
+       }
+
        if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
                INTELPllInvalid("vco out of range\n");
        /* XXX: We may need to be checking "Dot clock" depending on the multiplier,
@@ -688,67 +670,73 @@ vlv_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc,
                   int target, int refclk, intel_clock_t *match_clock,
                   intel_clock_t *best_clock)
 {
-       u32 p1, p2, m1, m2, vco, bestn, bestm1, bestm2, bestp1, bestp2;
-       u32 m, n, fastclk;
-       u32 updrate, minupdate, p;
-       unsigned long bestppm, ppm, absppm;
-       int dotclk, flag;
-
-       flag = 0;
-       dotclk = target * 1000;
-       bestppm = 1000000;
-       ppm = absppm = 0;
-       fastclk = dotclk / (2*100);
-       updrate = 0;
-       minupdate = 19200;
-       n = p = p1 = p2 = m = m1 = m2 = vco = bestn = 0;
-       bestm1 = bestm2 = bestp1 = bestp2 = 0;
+       struct drm_device *dev = crtc->dev;
+       intel_clock_t clock;
+       unsigned int bestppm = 1000000;
+       /* min update 19.2 MHz */
+       int max_n = min(limit->n.max, refclk / 19200);
+       bool found = false;
+
+       target *= 5; /* fast clock */
+
+       memset(best_clock, 0, sizeof(*best_clock));
 
        /* based on hardware requirement, prefer smaller n to precision */
-       for (n = limit->n.min; n <= ((refclk) / minupdate); n++) {
-               updrate = refclk / n;
-               for (p1 = limit->p1.max; p1 > limit->p1.min; p1--) {
-                       for (p2 = limit->p2.p2_fast+1; p2 > 0; p2--) {
-                               if (p2 > 10)
-                                       p2 = p2 - 1;
-                               p = p1 * p2;
+       for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
+               for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
+                       for (clock.p2 = limit->p2.p2_fast; clock.p2 >= limit->p2.p2_slow;
+                            clock.p2 -= clock.p2 > 10 ? 2 : 1) {
+                               clock.p = clock.p1 * clock.p2;
                                /* based on hardware requirement, prefer bigger m1,m2 values */
-                               for (m1 = limit->m1.min; m1 <= limit->m1.max; m1++) {
-                                       m2 = (((2*(fastclk * p * n / m1 )) +
-                                              refclk) / (2*refclk));
-                                       m = m1 * m2;
-                                       vco = updrate * m;
-                                       if (vco >= limit->vco.min && vco < limit->vco.max) {
-                                               ppm = 1000000 * ((vco / p) - fastclk) / fastclk;
-                                               absppm = (ppm > 0) ? ppm : (-ppm);
-                                               if (absppm < 100 && ((p1 * p2) > (bestp1 * bestp2))) {
-                                                       bestppm = 0;
-                                                       flag = 1;
-                                               }
-                                               if (absppm < bestppm - 10) {
-                                                       bestppm = absppm;
-                                                       flag = 1;
-                                               }
-                                               if (flag) {
-                                                       bestn = n;
-                                                       bestm1 = m1;
-                                                       bestm2 = m2;
-                                                       bestp1 = p1;
-                                                       bestp2 = p2;
-                                                       flag = 0;
-                                               }
+                               for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
+                                       unsigned int ppm, diff;
+
+                                       clock.m2 = DIV_ROUND_CLOSEST(target * clock.p * clock.n,
+                                                                    refclk * clock.m1);
+
+                                       vlv_clock(refclk, &clock);
+
+                                       if (!intel_PLL_is_valid(dev, limit,
+                                                               &clock))
+                                               continue;
+
+                                       diff = abs(clock.dot - target);
+                                       ppm = div_u64(1000000ULL * diff, target);
+
+                                       if (ppm < 100 && clock.p > best_clock->p) {
+                                               bestppm = 0;
+                                               *best_clock = clock;
+                                               found = true;
+                                       }
+
+                                       if (bestppm >= 10 && ppm < bestppm - 10) {
+                                               bestppm = ppm;
+                                               *best_clock = clock;
+                                               found = true;
                                        }
                                }
                        }
                }
        }
-       best_clock->n = bestn;
-       best_clock->m1 = bestm1;
-       best_clock->m2 = bestm2;
-       best_clock->p1 = bestp1;
-       best_clock->p2 = bestp2;
 
-       return true;
+       return found;
+}
+
+bool intel_crtc_active(struct drm_crtc *crtc)
+{
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       /* Be paranoid as we can arrive here with only partial
+        * state retrieved from the hardware during setup.
+        *
+        * We can ditch the adjusted_mode.crtc_clock check as soon
+        * as Haswell has gained clock readout/fastboot support.
+        *
+        * We can ditch the crtc->fb check as soon as we can
+        * properly reconstruct framebuffers.
+        */
+       return intel_crtc->active && crtc->fb &&
+               intel_crtc->config.adjusted_mode.crtc_clock;
 }
 
 enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv,
@@ -812,6 +800,25 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe)
                DRM_DEBUG_KMS("vblank wait timed out\n");
 }
 
+static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 reg = PIPEDSL(pipe);
+       u32 line1, line2;
+       u32 line_mask;
+
+       if (IS_GEN2(dev))
+               line_mask = DSL_LINEMASK_GEN2;
+       else
+               line_mask = DSL_LINEMASK_GEN3;
+
+       line1 = I915_READ(reg) & line_mask;
+       mdelay(5);
+       line2 = I915_READ(reg) & line_mask;
+
+       return line1 == line2;
+}
+
 /*
  * intel_wait_for_pipe_off - wait for pipe to turn off
  * @dev: drm device
@@ -843,22 +850,8 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe)
                             100))
                        WARN(1, "pipe_off wait timed out\n");
        } else {
-               u32 last_line, line_mask;
-               int reg = PIPEDSL(pipe);
-               unsigned long timeout = jiffies + msecs_to_jiffies(100);
-
-               if (IS_GEN2(dev))
-                       line_mask = DSL_LINEMASK_GEN2;
-               else
-                       line_mask = DSL_LINEMASK_GEN3;
-
                /* Wait for the display line to settle */
-               do {
-                       last_line = I915_READ(reg) & line_mask;
-                       mdelay(5);
-               } while (((I915_READ(reg) & line_mask) != last_line) &&
-                        time_after(timeout, jiffies));
-               if (time_after(jiffies, timeout))
+               if (wait_for(pipe_dsl_stopped(dev, pipe), 100))
                        WARN(1, "pipe_off wait timed out\n");
        }
 }
@@ -929,6 +922,24 @@ void assert_pll(struct drm_i915_private *dev_priv,
             state_string(state), state_string(cur_state));
 }
 
+/* XXX: the dsi pll is shared between MIPI DSI ports */
+static void assert_dsi_pll(struct drm_i915_private *dev_priv, bool state)
+{
+       u32 val;
+       bool cur_state;
+
+       mutex_lock(&dev_priv->dpio_lock);
+       val = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_CONTROL);
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       cur_state = val & DSI_PLL_VCO_EN;
+       WARN(cur_state != state,
+            "DSI PLL state assertion failure (expected %s, current %s)\n",
+            state_string(state), state_string(cur_state));
+}
+#define assert_dsi_pll_enabled(d) assert_dsi_pll(d, true)
+#define assert_dsi_pll_disabled(d) assert_dsi_pll(d, false)
+
 struct intel_shared_dpll *
 intel_crtc_to_shared_dpll(struct intel_crtc *crtc)
 {
@@ -1069,6 +1080,26 @@ static void assert_panel_unlocked(struct drm_i915_private *dev_priv,
             pipe_name(pipe));
 }
 
+static void assert_cursor(struct drm_i915_private *dev_priv,
+                         enum pipe pipe, bool state)
+{
+       struct drm_device *dev = dev_priv->dev;
+       bool cur_state;
+
+       if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
+               cur_state = I915_READ(CURCNTR_IVB(pipe)) & CURSOR_MODE;
+       else if (IS_845G(dev) || IS_I865G(dev))
+               cur_state = I915_READ(_CURACNTR) & CURSOR_ENABLE;
+       else
+               cur_state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
+
+       WARN(cur_state != state,
+            "cursor on pipe %c assertion failure (expected %s, current %s)\n",
+            pipe_name(pipe), state_string(state), state_string(cur_state));
+}
+#define assert_cursor_enabled(d, p) assert_cursor(d, p, true)
+#define assert_cursor_disabled(d, p) assert_cursor(d, p, false)
+
 void assert_pipe(struct drm_i915_private *dev_priv,
                 enum pipe pipe, bool state)
 {
@@ -1323,6 +1354,26 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
        assert_pch_hdmi_disabled(dev_priv, pipe, PCH_HDMID);
 }
 
+static void intel_init_dpio(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (!IS_VALLEYVIEW(dev))
+               return;
+
+       /*
+        * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx -
+        *  6.  De-assert cmn_reset/side_reset. Same as VLV X0.
+        *   a. GUnit 0x2110 bit[0] set to 1 (def 0)
+        *   b. The other bits such as sfr settings / modesel may all be set
+        *      to 0.
+        *
+        * This should only be done on init and resume from S3 with both
+        * PLLs disabled, or we risk losing DPIO and PLL synchronization.
+        */
+       I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) | DPIO_CMNRST);
+}
+
 static void vlv_enable_pll(struct intel_crtc *crtc)
 {
        struct drm_device *dev = crtc->base.dev;
@@ -1429,6 +1480,20 @@ static void i9xx_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
        POSTING_READ(DPLL(pipe));
 }
 
+static void vlv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
+{
+       u32 val = 0;
+
+       /* Make sure the pipe isn't still relying on us */
+       assert_pipe_disabled(dev_priv, pipe);
+
+       /* Leave integrated clock source enabled */
+       if (pipe == PIPE_B)
+               val = DPLL_INTEGRATED_CRI_CLK_VLV;
+       I915_WRITE(DPLL(pipe), val);
+       POSTING_READ(DPLL(pipe));
+}
+
 void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port)
 {
        u32 port_mask;
@@ -1661,7 +1726,7 @@ static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv)
  * returning.
  */
 static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
-                             bool pch_port)
+                             bool pch_port, bool dsi)
 {
        enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
                                                                      pipe);
@@ -1670,6 +1735,7 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
        u32 val;
 
        assert_planes_disabled(dev_priv, pipe);
+       assert_cursor_disabled(dev_priv, pipe);
        assert_sprites_disabled(dev_priv, pipe);
 
        if (HAS_PCH_LPT(dev_priv->dev))
@@ -1683,7 +1749,10 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
         * need the check.
         */
        if (!HAS_PCH_SPLIT(dev_priv->dev))
-               assert_pll_enabled(dev_priv, pipe);
+               if (dsi)
+                       assert_dsi_pll_enabled(dev_priv);
+               else
+                       assert_pll_enabled(dev_priv, pipe);
        else {
                if (pch_port) {
                        /* if driving the PCH, we need FDI enabled */
@@ -1728,6 +1797,7 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv,
         * or we might hang the display.
         */
        assert_planes_disabled(dev_priv, pipe);
+       assert_cursor_disabled(dev_priv, pipe);
        assert_sprites_disabled(dev_priv, pipe);
 
        /* Don't disable pipe A or pipe A PLLs if needed */
@@ -1747,63 +1817,75 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv,
  * Plane regs are double buffered, going from enabled->disabled needs a
  * trigger in order to latch.  The display address reg provides this.
  */
-void intel_flush_display_plane(struct drm_i915_private *dev_priv,
-                                     enum plane plane)
+void intel_flush_primary_plane(struct drm_i915_private *dev_priv,
+                              enum plane plane)
 {
-       if (dev_priv->info->gen >= 4)
-               I915_WRITE(DSPSURF(plane), I915_READ(DSPSURF(plane)));
-       else
-               I915_WRITE(DSPADDR(plane), I915_READ(DSPADDR(plane)));
+       u32 reg = dev_priv->info->gen >= 4 ? DSPSURF(plane) : DSPADDR(plane);
+
+       I915_WRITE(reg, I915_READ(reg));
+       POSTING_READ(reg);
 }
 
 /**
- * intel_enable_plane - enable a display plane on a given pipe
+ * intel_enable_primary_plane - enable the primary plane on a given pipe
  * @dev_priv: i915 private structure
  * @plane: plane to enable
  * @pipe: pipe being fed
  *
  * Enable @plane on @pipe, making sure that @pipe is running first.
  */
-static void intel_enable_plane(struct drm_i915_private *dev_priv,
-                              enum plane plane, enum pipe pipe)
+static void intel_enable_primary_plane(struct drm_i915_private *dev_priv,
+                                      enum plane plane, enum pipe pipe)
 {
+       struct intel_crtc *intel_crtc =
+               to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
        int reg;
        u32 val;
 
        /* If the pipe isn't enabled, we can't pump pixels and may hang */
        assert_pipe_enabled(dev_priv, pipe);
 
+       WARN(intel_crtc->primary_enabled, "Primary plane already enabled\n");
+
+       intel_crtc->primary_enabled = true;
+
        reg = DSPCNTR(plane);
        val = I915_READ(reg);
        if (val & DISPLAY_PLANE_ENABLE)
                return;
 
        I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE);
-       intel_flush_display_plane(dev_priv, plane);
+       intel_flush_primary_plane(dev_priv, plane);
        intel_wait_for_vblank(dev_priv->dev, pipe);
 }
 
 /**
- * intel_disable_plane - disable a display plane
+ * intel_disable_primary_plane - disable the primary plane
  * @dev_priv: i915 private structure
  * @plane: plane to disable
  * @pipe: pipe consuming the data
  *
  * Disable @plane; should be an independent operation.
  */
-static void intel_disable_plane(struct drm_i915_private *dev_priv,
-                               enum plane plane, enum pipe pipe)
+static void intel_disable_primary_plane(struct drm_i915_private *dev_priv,
+                                       enum plane plane, enum pipe pipe)
 {
+       struct intel_crtc *intel_crtc =
+               to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
        int reg;
        u32 val;
 
+       WARN(!intel_crtc->primary_enabled, "Primary plane already disabled\n");
+
+       intel_crtc->primary_enabled = false;
+
        reg = DSPCNTR(plane);
        val = I915_READ(reg);
        if ((val & DISPLAY_PLANE_ENABLE) == 0)
                return;
 
        I915_WRITE(reg, val & ~DISPLAY_PLANE_ENABLE);
-       intel_flush_display_plane(dev_priv, plane);
+       intel_flush_primary_plane(dev_priv, plane);
        intel_wait_for_vblank(dev_priv->dev, pipe);
 }
 
@@ -1839,10 +1921,7 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev,
                alignment = 0;
                break;
        case I915_TILING_Y:
-               /* Despite that we check this in framebuffer_init userspace can
-                * screw us over and change the tiling after the fact. Only
-                * pinned buffers can't change their tiling. */
-               DRM_DEBUG_DRIVER("Y tiled not allowed for scan out buffers\n");
+               WARN(1, "Y tiled bo slipped through, driver bug!\n");
                return -EINVAL;
        default:
                BUG();
@@ -2077,7 +2156,7 @@ static int ironlake_update_plane(struct drm_crtc *crtc,
        else
                dspcntr &= ~DISPPLANE_TILED;
 
-       if (IS_HASWELL(dev))
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev))
                dspcntr &= ~DISPPLANE_TRICKLE_FEED_DISABLE;
        else
                dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
@@ -2097,7 +2176,7 @@ static int ironlake_update_plane(struct drm_crtc *crtc,
        I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
        I915_MODIFY_DISPBASE(DSPSURF(plane),
                             i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
-       if (IS_HASWELL(dev)) {
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
                I915_WRITE(DSPOFFSET(plane), (y << 16) | x);
        } else {
                I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
@@ -2244,11 +2323,26 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                return ret;
        }
 
-       /* Update pipe size and adjust fitter if needed */
+       /*
+        * Update pipe size and adjust fitter if needed: the reason for this is
+        * that in compute_mode_changes we check the native mode (not the pfit
+        * mode) to see if we can flip rather than do a full mode set. In the
+        * fastboot case, we'll flip, but if we don't update the pipesrc and
+        * pfit state, we'll end up with a big fb scanned out into the wrong
+        * sized surface.
+        *
+        * To fix this properly, we need to hoist the checks up into
+        * compute_mode_changes (or above), check the actual pfit state and
+        * whether the platform allows pfit disable with pipe active, and only
+        * then update the pipesrc and pfit state, even on the flip path.
+        */
        if (i915_fastboot) {
+               const struct drm_display_mode *adjusted_mode =
+                       &intel_crtc->config.adjusted_mode;
+
                I915_WRITE(PIPESRC(intel_crtc->pipe),
-                          ((crtc->mode.hdisplay - 1) << 16) |
-                          (crtc->mode.vdisplay - 1));
+                          ((adjusted_mode->crtc_hdisplay - 1) << 16) |
+                          (adjusted_mode->crtc_vdisplay - 1));
                if (!intel_crtc->config.pch_pfit.enabled &&
                    (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) ||
                     intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) {
@@ -2873,6 +2967,7 @@ static void lpt_program_iclkip(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       int clock = to_intel_crtc(crtc)->config.adjusted_mode.crtc_clock;
        u32 divsel, phaseinc, auxdiv, phasedir = 0;
        u32 temp;
 
@@ -2890,14 +2985,14 @@ static void lpt_program_iclkip(struct drm_crtc *crtc)
                        SBI_ICLK);
 
        /* 20MHz is a corner case which is out of range for the 7-bit divisor */
-       if (crtc->mode.clock == 20000) {
+       if (clock == 20000) {
                auxdiv = 1;
                divsel = 0x41;
                phaseinc = 0x20;
        } else {
                /* The iCLK virtual clock root frequency is in MHz,
-                * but the crtc->mode.clock in in KHz. To get the divisors,
-                * it is necessary to divide one by another, so we
+                * but the adjusted_mode->crtc_clock in in KHz. To get the
+                * divisors, it is necessary to divide one by another, so we
                 * convert the virtual clock precision to KHz here for higher
                 * precision.
                 */
@@ -2905,7 +3000,7 @@ static void lpt_program_iclkip(struct drm_crtc *crtc)
                u32 iclk_pi_range = 64;
                u32 desired_divisor, msb_divisor_value, pi_value;
 
-               desired_divisor = (iclk_virtual_root_freq / crtc->mode.clock);
+               desired_divisor = (iclk_virtual_root_freq / clock);
                msb_divisor_value = desired_divisor / iclk_pi_range;
                pi_value = desired_divisor % iclk_pi_range;
 
@@ -2921,7 +3016,7 @@ static void lpt_program_iclkip(struct drm_crtc *crtc)
                ~SBI_SSCDIVINTPHASE_INCVAL_MASK);
 
        DRM_DEBUG_KMS("iCLKIP clock: found settings for %dKHz refresh rate: auxdiv=%x, divsel=%x, phasedir=%x, phaseinc=%x\n",
-                       crtc->mode.clock,
+                       clock,
                        auxdiv,
                        divsel,
                        phasedir,
@@ -3286,6 +3381,108 @@ static void intel_disable_planes(struct drm_crtc *crtc)
                        intel_plane_disable(&intel_plane->base);
 }
 
+void hsw_enable_ips(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+
+       if (!crtc->config.ips_enabled)
+               return;
+
+       /* We can only enable IPS after we enable a plane and wait for a vblank.
+        * We guarantee that the plane is enabled by calling intel_enable_ips
+        * only after intel_enable_plane. And intel_enable_plane already waits
+        * for a vblank, so all we need to do here is to enable the IPS bit. */
+       assert_plane_enabled(dev_priv, crtc->plane);
+       if (IS_BROADWELL(crtc->base.dev)) {
+               mutex_lock(&dev_priv->rps.hw_lock);
+               WARN_ON(sandybridge_pcode_write(dev_priv, DISPLAY_IPS_CONTROL, 0xc0000000));
+               mutex_unlock(&dev_priv->rps.hw_lock);
+               /* Quoting Art Runyan: "its not safe to expect any particular
+                * value in IPS_CTL bit 31 after enabling IPS through the
+                * mailbox." Therefore we need to defer waiting on the state
+                * change.
+                * TODO: need to fix this for state checker
+                */
+       } else {
+               I915_WRITE(IPS_CTL, IPS_ENABLE);
+               /* The bit only becomes 1 in the next vblank, so this wait here
+                * is essentially intel_wait_for_vblank. If we don't have this
+                * and don't wait for vblanks until the end of crtc_enable, then
+                * the HW state readout code will complain that the expected
+                * IPS_CTL value is not the one we read. */
+               if (wait_for(I915_READ_NOTRACE(IPS_CTL) & IPS_ENABLE, 50))
+                       DRM_ERROR("Timed out waiting for IPS enable\n");
+       }
+}
+
+void hsw_disable_ips(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (!crtc->config.ips_enabled)
+               return;
+
+       assert_plane_enabled(dev_priv, crtc->plane);
+       if (IS_BROADWELL(crtc->base.dev)) {
+               mutex_lock(&dev_priv->rps.hw_lock);
+               WARN_ON(sandybridge_pcode_write(dev_priv, DISPLAY_IPS_CONTROL, 0));
+               mutex_unlock(&dev_priv->rps.hw_lock);
+       } else
+               I915_WRITE(IPS_CTL, 0);
+       POSTING_READ(IPS_CTL);
+
+       /* We need to wait for a vblank before we can disable the plane. */
+       intel_wait_for_vblank(dev, crtc->pipe);
+}
+
+/** Loads the palette/gamma unit for the CRTC with the prepared values */
+static void intel_crtc_load_lut(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       enum pipe pipe = intel_crtc->pipe;
+       int palreg = PALETTE(pipe);
+       int i;
+       bool reenable_ips = false;
+
+       /* The clocks have to be on to load the palette. */
+       if (!crtc->enabled || !intel_crtc->active)
+               return;
+
+       if (!HAS_PCH_SPLIT(dev_priv->dev)) {
+               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI))
+                       assert_dsi_pll_enabled(dev_priv);
+               else
+                       assert_pll_enabled(dev_priv, pipe);
+       }
+
+       /* use legacy palette for Ironlake */
+       if (HAS_PCH_SPLIT(dev))
+               palreg = LGC_PALETTE(pipe);
+
+       /* Workaround : Do not read or write the pipe palette/gamma data while
+        * GAMMA_MODE is configured for split gamma and IPS_CTL has IPS enabled.
+        */
+       if (intel_crtc->config.ips_enabled &&
+           ((I915_READ(GAMMA_MODE(pipe)) & GAMMA_MODE_MODE_MASK) ==
+            GAMMA_MODE_MODE_SPLIT)) {
+               hsw_disable_ips(intel_crtc);
+               reenable_ips = true;
+       }
+
+       for (i = 0; i < 256; i++) {
+               I915_WRITE(palreg + 4 * i,
+                          (intel_crtc->lut_r[i] << 16) |
+                          (intel_crtc->lut_g[i] << 8) |
+                          intel_crtc->lut_b[i]);
+       }
+
+       if (reenable_ips)
+               hsw_enable_ips(intel_crtc);
+}
+
 static void ironlake_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -3305,8 +3502,6 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
        intel_set_pch_fifo_underrun_reporting(dev, pipe, true);
 
-       intel_update_watermarks(dev);
-
        for_each_encoder_on_crtc(dev, crtc, encoder)
                if (encoder->pre_enable)
                        encoder->pre_enable(encoder);
@@ -3329,9 +3524,10 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
         */
        intel_crtc_load_lut(crtc);
 
+       intel_update_watermarks(crtc);
        intel_enable_pipe(dev_priv, pipe,
-                         intel_crtc->config.has_pch_encoder);
-       intel_enable_plane(dev_priv, plane, pipe);
+                         intel_crtc->config.has_pch_encoder, false);
+       intel_enable_primary_plane(dev_priv, plane, pipe);
        intel_enable_planes(crtc);
        intel_crtc_update_cursor(crtc, true);
 
@@ -3365,34 +3561,74 @@ static bool hsw_crtc_supports_ips(struct intel_crtc *crtc)
        return HAS_IPS(crtc->base.dev) && crtc->pipe == PIPE_A;
 }
 
-static void hsw_enable_ips(struct intel_crtc *crtc)
+static void haswell_crtc_enable_planes(struct drm_crtc *crtc)
 {
-       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
 
-       if (!crtc->config.ips_enabled)
-               return;
+       intel_enable_primary_plane(dev_priv, plane, pipe);
+       intel_enable_planes(crtc);
+       intel_crtc_update_cursor(crtc, true);
 
-       /* We can only enable IPS after we enable a plane and wait for a vblank.
-        * We guarantee that the plane is enabled by calling intel_enable_ips
-        * only after intel_enable_plane. And intel_enable_plane already waits
-        * for a vblank, so all we need to do here is to enable the IPS bit. */
-       assert_plane_enabled(dev_priv, crtc->plane);
-       I915_WRITE(IPS_CTL, IPS_ENABLE);
+       hsw_enable_ips(intel_crtc);
+
+       mutex_lock(&dev->struct_mutex);
+       intel_update_fbc(dev);
+       mutex_unlock(&dev->struct_mutex);
 }
 
-static void hsw_disable_ips(struct intel_crtc *crtc)
+static void haswell_crtc_disable_planes(struct drm_crtc *crtc)
 {
-       struct drm_device *dev = crtc->base.dev;
+       struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
 
-       if (!crtc->config.ips_enabled)
-               return;
+       intel_crtc_wait_for_pending_flips(crtc);
+       drm_vblank_off(dev, pipe);
 
-       assert_plane_enabled(dev_priv, crtc->plane);
-       I915_WRITE(IPS_CTL, 0);
+       /* FBC must be disabled before disabling the plane on HSW. */
+       if (dev_priv->fbc.plane == plane)
+               intel_disable_fbc(dev);
 
-       /* We need to wait for a vblank before we can disable the plane. */
-       intel_wait_for_vblank(dev, crtc->pipe);
+       hsw_disable_ips(intel_crtc);
+
+       intel_crtc_update_cursor(crtc, false);
+       intel_disable_planes(crtc);
+       intel_disable_primary_plane(dev_priv, plane, pipe);
+}
+
+/*
+ * This implements the workaround described in the "notes" section of the mode
+ * set sequence documentation. When going from no pipes or single pipe to
+ * multiple pipes, and planes are enabled after the pipe, we need to wait at
+ * least 2 vblanks on the first pipe before enabling planes on the second pipe.
+ */
+static void haswell_mode_set_planes_workaround(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct intel_crtc *crtc_it, *other_active_crtc = NULL;
+
+       /* We want to get the other_active_crtc only if there's only 1 other
+        * active crtc. */
+       list_for_each_entry(crtc_it, &dev->mode_config.crtc_list, base.head) {
+               if (!crtc_it->active || crtc_it == crtc)
+                       continue;
+
+               if (other_active_crtc)
+                       return;
+
+               other_active_crtc = crtc_it;
+       }
+       if (!other_active_crtc)
+               return;
+
+       intel_wait_for_vblank(dev, other_active_crtc->pipe);
+       intel_wait_for_vblank(dev, other_active_crtc->pipe);
 }
 
 static void haswell_crtc_enable(struct drm_crtc *crtc)
@@ -3402,7 +3638,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->plane;
 
        WARN_ON(!crtc->enabled);
 
@@ -3415,8 +3650,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        if (intel_crtc->config.has_pch_encoder)
                intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true);
 
-       intel_update_watermarks(dev);
-
        if (intel_crtc->config.has_pch_encoder)
                dev_priv->display.fdi_link_train(crtc);
 
@@ -3437,23 +3670,22 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        intel_ddi_set_pipe_settings(crtc);
        intel_ddi_enable_transcoder_func(crtc);
 
+       intel_update_watermarks(crtc);
        intel_enable_pipe(dev_priv, pipe,
-                         intel_crtc->config.has_pch_encoder);
-       intel_enable_plane(dev_priv, plane, pipe);
-       intel_enable_planes(crtc);
-       intel_crtc_update_cursor(crtc, true);
-
-       hsw_enable_ips(intel_crtc);
+                         intel_crtc->config.has_pch_encoder, false);
 
        if (intel_crtc->config.has_pch_encoder)
                lpt_pch_enable(crtc);
 
-       mutex_lock(&dev->struct_mutex);
-       intel_update_fbc(dev);
-       mutex_unlock(&dev->struct_mutex);
-
-       for_each_encoder_on_crtc(dev, crtc, encoder)
+       for_each_encoder_on_crtc(dev, crtc, encoder) {
                encoder->enable(encoder);
+               intel_opregion_notify_encoder(encoder, true);
+       }
+
+       /* If we change the relative order between pipe/planes enabling, we need
+        * to change the workaround. */
+       haswell_mode_set_planes_workaround(intel_crtc);
+       haswell_crtc_enable_planes(crtc);
 
        /*
         * There seems to be a race in PCH platform hw (at least on some
@@ -3506,7 +3738,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
 
        intel_crtc_update_cursor(crtc, false);
        intel_disable_planes(crtc);
-       intel_disable_plane(dev_priv, plane, pipe);
+       intel_disable_primary_plane(dev_priv, plane, pipe);
 
        if (intel_crtc->config.has_pch_encoder)
                intel_set_pch_fifo_underrun_reporting(dev, pipe, false);
@@ -3547,7 +3779,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        }
 
        intel_crtc->active = false;
-       intel_update_watermarks(dev);
+       intel_update_watermarks(crtc);
 
        mutex_lock(&dev->struct_mutex);
        intel_update_fbc(dev);
@@ -3561,27 +3793,17 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->plane;
        enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
 
        if (!intel_crtc->active)
                return;
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               encoder->disable(encoder);
+       haswell_crtc_disable_planes(crtc);
 
-       intel_crtc_wait_for_pending_flips(crtc);
-       drm_vblank_off(dev, pipe);
-
-       /* FBC must be disabled before disabling the plane on HSW. */
-       if (dev_priv->fbc.plane == plane)
-               intel_disable_fbc(dev);
-
-       hsw_disable_ips(intel_crtc);
-
-       intel_crtc_update_cursor(crtc, false);
-       intel_disable_planes(crtc);
-       intel_disable_plane(dev_priv, plane, pipe);
+       for_each_encoder_on_crtc(dev, crtc, encoder) {
+               intel_opregion_notify_encoder(encoder, false);
+               encoder->disable(encoder);
+       }
 
        if (intel_crtc->config.has_pch_encoder)
                intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, false);
@@ -3604,7 +3826,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
        }
 
        intel_crtc->active = false;
-       intel_update_watermarks(dev);
+       intel_update_watermarks(crtc);
 
        mutex_lock(&dev->struct_mutex);
        intel_update_fbc(dev);
@@ -3696,6 +3918,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
        struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
+       bool is_dsi;
 
        WARN_ON(!crtc->enabled);
 
@@ -3703,13 +3926,15 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
                return;
 
        intel_crtc->active = true;
-       intel_update_watermarks(dev);
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
                if (encoder->pre_pll_enable)
                        encoder->pre_pll_enable(encoder);
 
-       vlv_enable_pll(intel_crtc);
+       is_dsi = intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI);
+
+       if (!is_dsi)
+               vlv_enable_pll(intel_crtc);
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
                if (encoder->pre_enable)
@@ -3719,8 +3944,9 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
 
        intel_crtc_load_lut(crtc);
 
-       intel_enable_pipe(dev_priv, pipe, false);
-       intel_enable_plane(dev_priv, plane, pipe);
+       intel_update_watermarks(crtc);
+       intel_enable_pipe(dev_priv, pipe, false, is_dsi);
+       intel_enable_primary_plane(dev_priv, plane, pipe);
        intel_enable_planes(crtc);
        intel_crtc_update_cursor(crtc, true);
 
@@ -3745,7 +3971,6 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
                return;
 
        intel_crtc->active = true;
-       intel_update_watermarks(dev);
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
                if (encoder->pre_enable)
@@ -3757,8 +3982,9 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
 
        intel_crtc_load_lut(crtc);
 
-       intel_enable_pipe(dev_priv, pipe, false);
-       intel_enable_plane(dev_priv, plane, pipe);
+       intel_update_watermarks(crtc);
+       intel_enable_pipe(dev_priv, pipe, false, false);
+       intel_enable_primary_plane(dev_priv, plane, pipe);
        intel_enable_planes(crtc);
        /* The fixup needs to happen before cursor is enabled */
        if (IS_G4X(dev))
@@ -3814,7 +4040,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
        intel_crtc_dpms_overlay(intel_crtc, false);
        intel_crtc_update_cursor(crtc, false);
        intel_disable_planes(crtc);
-       intel_disable_plane(dev_priv, plane, pipe);
+       intel_disable_primary_plane(dev_priv, plane, pipe);
 
        intel_disable_pipe(dev_priv, pipe);
 
@@ -3824,11 +4050,15 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
                if (encoder->post_disable)
                        encoder->post_disable(encoder);
 
-       i9xx_disable_pll(dev_priv, pipe);
+       if (IS_VALLEYVIEW(dev) && !intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI))
+               vlv_disable_pll(dev_priv, pipe);
+       else if (!IS_VALLEYVIEW(dev))
+               i9xx_disable_pll(dev_priv, pipe);
 
        intel_crtc->active = false;
+       intel_update_watermarks(crtc);
+
        intel_update_fbc(dev);
-       intel_update_watermarks(dev);
 }
 
 static void i9xx_crtc_off(struct drm_crtc *crtc)
@@ -3902,6 +4132,7 @@ static void intel_crtc_disable(struct drm_crtc *crtc)
        dev_priv->display.off(crtc);
 
        assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane);
+       assert_cursor_disabled(dev_priv, to_intel_crtc(crtc)->pipe);
        assert_pipe_disabled(dev->dev_private, to_intel_crtc(crtc)->pipe);
 
        if (crtc->fb) {
@@ -4029,7 +4260,7 @@ static bool ironlake_check_fdi_lanes(struct drm_device *dev, enum pipe pipe,
                return false;
        }
 
-       if (IS_HASWELL(dev)) {
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
                if (pipe_config->fdi_lanes > 2) {
                        DRM_DEBUG_KMS("only 2 lanes on haswell, required: %i lanes\n",
                                      pipe_config->fdi_lanes);
@@ -4091,8 +4322,7 @@ retry:
         */
        link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
 
-       fdi_dotclock = adjusted_mode->clock;
-       fdi_dotclock /= pipe_config->pixel_multiplier;
+       fdi_dotclock = adjusted_mode->crtc_clock;
 
        lane = ironlake_get_lanes_required(fdi_dotclock, link_bw,
                                           pipe_config->pipe_bpp);
@@ -4134,13 +4364,39 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc,
        struct drm_device *dev = crtc->base.dev;
        struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
 
-       if (HAS_PCH_SPLIT(dev)) {
-               /* FDI link clock is fixed at 2.7G */
-               if (pipe_config->requested_mode.clock * 3
-                   > IRONLAKE_FDI_FREQ * 4)
+       /* FIXME should check pixel clock limits on all platforms */
+       if (INTEL_INFO(dev)->gen < 4) {
+               struct drm_i915_private *dev_priv = dev->dev_private;
+               int clock_limit =
+                       dev_priv->display.get_display_clock_speed(dev);
+
+               /*
+                * Enable pixel doubling when the dot clock
+                * is > 90% of the (display) core speed.
+                *
+                * GDG double wide on either pipe,
+                * otherwise pipe A only.
+                */
+               if ((crtc->pipe == PIPE_A || IS_I915G(dev)) &&
+                   adjusted_mode->crtc_clock > clock_limit * 9 / 10) {
+                       clock_limit *= 2;
+                       pipe_config->double_wide = true;
+               }
+
+               if (adjusted_mode->crtc_clock > clock_limit * 9 / 10)
                        return -EINVAL;
        }
 
+       /*
+        * Pipe horizontal size must be even in:
+        * - DVO ganged mode
+        * - LVDS dual channel mode
+        * - Double wide pipe
+        */
+       if ((intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) &&
+            intel_is_dual_link_lvds(dev)) || pipe_config->double_wide)
+               pipe_config->pipe_src_w &= ~1;
+
        /* Cantiga+ cannot handle modes with a hsync front porch of 0.
         * WaPruneModeWithIncorrectHsyncOffset:ctg,elk,ilk,snb,ivb,vlv,hsw.
         */
@@ -4304,28 +4560,6 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
                && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
 }
 
-static int vlv_get_refclk(struct drm_crtc *crtc)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       int refclk = 27000; /* for DP & HDMI */
-
-       return 100000; /* only one validated so far */
-
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) {
-               refclk = 96000;
-       } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
-               if (intel_panel_use_ssc(dev_priv))
-                       refclk = 100000;
-               else
-                       refclk = 96000;
-       } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) {
-               refclk = 100000;
-       }
-
-       return refclk;
-}
-
 static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors)
 {
        struct drm_device *dev = crtc->dev;
@@ -4333,7 +4567,7 @@ static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors)
        int refclk;
 
        if (IS_VALLEYVIEW(dev)) {
-               refclk = vlv_get_refclk(crtc);
+               refclk = 100000;
        } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
            intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
                refclk = dev_priv->vbt.lvds_ssc_freq * 1000;
@@ -4391,7 +4625,8 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc,
        }
 }
 
-static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv)
+static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv, enum pipe
+               pipe)
 {
        u32 reg_val;
 
@@ -4399,24 +4634,24 @@ static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv)
         * PLLB opamp always calibrates to max value of 0x3f, force enable it
         * and set it to a reasonable value instead.
         */
-       reg_val = vlv_dpio_read(dev_priv, DPIO_IREF(1));
+       reg_val = vlv_dpio_read(dev_priv, pipe, DPIO_IREF(1));
        reg_val &= 0xffffff00;
        reg_val |= 0x00000030;
-       vlv_dpio_write(dev_priv, DPIO_IREF(1), reg_val);
+       vlv_dpio_write(dev_priv, pipe, DPIO_IREF(1), reg_val);
 
-       reg_val = vlv_dpio_read(dev_priv, DPIO_CALIBRATION);
+       reg_val = vlv_dpio_read(dev_priv, pipe, DPIO_CALIBRATION);
        reg_val &= 0x8cffffff;
        reg_val = 0x8c000000;
-       vlv_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val);
+       vlv_dpio_write(dev_priv, pipe, DPIO_CALIBRATION, reg_val);
 
-       reg_val = vlv_dpio_read(dev_priv, DPIO_IREF(1));
+       reg_val = vlv_dpio_read(dev_priv, pipe, DPIO_IREF(1));
        reg_val &= 0xffffff00;
-       vlv_dpio_write(dev_priv, DPIO_IREF(1), reg_val);
+       vlv_dpio_write(dev_priv, pipe, DPIO_IREF(1), reg_val);
 
-       reg_val = vlv_dpio_read(dev_priv, DPIO_CALIBRATION);
+       reg_val = vlv_dpio_read(dev_priv, pipe, DPIO_CALIBRATION);
        reg_val &= 0x00ffffff;
        reg_val |= 0xb0000000;
-       vlv_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val);
+       vlv_dpio_write(dev_priv, pipe, DPIO_CALIBRATION, reg_val);
 }
 
 static void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc,
@@ -4482,18 +4717,18 @@ static void vlv_update_pll(struct intel_crtc *crtc)
 
        /* PLL B needs special handling */
        if (pipe)
-               vlv_pllb_recal_opamp(dev_priv);
+               vlv_pllb_recal_opamp(dev_priv, pipe);
 
        /* Set up Tx target for periodic Rcomp update */
-       vlv_dpio_write(dev_priv, DPIO_IREF_BCAST, 0x0100000f);
+       vlv_dpio_write(dev_priv, pipe, DPIO_IREF_BCAST, 0x0100000f);
 
        /* Disable target IRef on PLL */
-       reg_val = vlv_dpio_read(dev_priv, DPIO_IREF_CTL(pipe));
+       reg_val = vlv_dpio_read(dev_priv, pipe, DPIO_IREF_CTL(pipe));
        reg_val &= 0x00ffffff;
-       vlv_dpio_write(dev_priv, DPIO_IREF_CTL(pipe), reg_val);
+       vlv_dpio_write(dev_priv, pipe, DPIO_IREF_CTL(pipe), reg_val);
 
        /* Disable fast lock */
-       vlv_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x610);
+       vlv_dpio_write(dev_priv, pipe, DPIO_FASTCLK_DISABLE, 0x610);
 
        /* Set idtafcrecal before PLL is enabled */
        mdiv = ((bestm1 << DPIO_M1DIV_SHIFT) | (bestm2 & DPIO_M2DIV_MASK));
@@ -4507,55 +4742,55 @@ static void vlv_update_pll(struct intel_crtc *crtc)
         * Note: don't use the DAC post divider as it seems unstable.
         */
        mdiv |= (DPIO_POST_DIV_HDMIDP << DPIO_POST_DIV_SHIFT);
-       vlv_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv);
+       vlv_dpio_write(dev_priv, pipe, DPIO_DIV(pipe), mdiv);
 
        mdiv |= DPIO_ENABLE_CALIBRATION;
-       vlv_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv);
+       vlv_dpio_write(dev_priv, pipe, DPIO_DIV(pipe), mdiv);
 
        /* Set HBR and RBR LPF coefficients */
        if (crtc->config.port_clock == 162000 ||
            intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_ANALOG) ||
            intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI))
-               vlv_dpio_write(dev_priv, DPIO_LPF_COEFF(pipe),
+               vlv_dpio_write(dev_priv, pipe, DPIO_LPF_COEFF(pipe),
                                 0x009f0003);
        else
-               vlv_dpio_write(dev_priv, DPIO_LPF_COEFF(pipe),
+               vlv_dpio_write(dev_priv, pipe, DPIO_LPF_COEFF(pipe),
                                 0x00d0000f);
 
        if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) ||
            intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) {
                /* Use SSC source */
                if (!pipe)
-                       vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe),
+                       vlv_dpio_write(dev_priv, pipe, DPIO_REFSFR(pipe),
                                         0x0df40000);
                else
-                       vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe),
+                       vlv_dpio_write(dev_priv, pipe, DPIO_REFSFR(pipe),
                                         0x0df70000);
        } else { /* HDMI or VGA */
                /* Use bend source */
                if (!pipe)
-                       vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe),
+                       vlv_dpio_write(dev_priv, pipe, DPIO_REFSFR(pipe),
                                         0x0df70000);
                else
-                       vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe),
+                       vlv_dpio_write(dev_priv, pipe, DPIO_REFSFR(pipe),
                                         0x0df40000);
        }
 
-       coreclk = vlv_dpio_read(dev_priv, DPIO_CORE_CLK(pipe));
+       coreclk = vlv_dpio_read(dev_priv, pipe, DPIO_CORE_CLK(pipe));
        coreclk = (coreclk & 0x0000ff00) | 0x01c00000;
        if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT) ||
            intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP))
                coreclk |= 0x01000000;
-       vlv_dpio_write(dev_priv, DPIO_CORE_CLK(pipe), coreclk);
+       vlv_dpio_write(dev_priv, pipe, DPIO_CORE_CLK(pipe), coreclk);
 
-       vlv_dpio_write(dev_priv, DPIO_PLL_CML(pipe), 0x87871000);
+       vlv_dpio_write(dev_priv, pipe, DPIO_PLL_CML(pipe), 0x87871000);
 
        /* Enable DPIO clock input */
        dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV |
                DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV;
-       if (pipe)
+       /* We should never disable this, set it here for state tracking */
+       if (pipe == PIPE_B)
                dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
-
        dpll |= DPLL_VCO_ENABLE;
        crtc->config.dpll_hw_state.dpll = dpll;
 
@@ -4693,7 +4928,6 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc)
        enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
        struct drm_display_mode *adjusted_mode =
                &intel_crtc->config.adjusted_mode;
-       struct drm_display_mode *mode = &intel_crtc->config.requested_mode;
        uint32_t vsyncshift, crtc_vtotal, crtc_vblank_end;
 
        /* We need to be careful not to changed the adjusted mode, for otherwise
@@ -4746,7 +4980,8 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc)
         * always be the user's requested size.
         */
        I915_WRITE(PIPESRC(pipe),
-                  ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+                  ((intel_crtc->config.pipe_src_w - 1) << 16) |
+                  (intel_crtc->config.pipe_src_h - 1));
 }
 
 static void intel_get_pipe_timings(struct intel_crtc *crtc,
@@ -4784,8 +5019,11 @@ static void intel_get_pipe_timings(struct intel_crtc *crtc,
        }
 
        tmp = I915_READ(PIPESRC(crtc->pipe));
-       pipe_config->requested_mode.vdisplay = (tmp & 0xffff) + 1;
-       pipe_config->requested_mode.hdisplay = ((tmp >> 16) & 0xffff) + 1;
+       pipe_config->pipe_src_h = (tmp & 0xffff) + 1;
+       pipe_config->pipe_src_w = ((tmp >> 16) & 0xffff) + 1;
+
+       pipe_config->requested_mode.vdisplay = pipe_config->pipe_src_h;
+       pipe_config->requested_mode.hdisplay = pipe_config->pipe_src_w;
 }
 
 static void intel_crtc_mode_from_pipe_config(struct intel_crtc *intel_crtc,
@@ -4805,7 +5043,7 @@ static void intel_crtc_mode_from_pipe_config(struct intel_crtc *intel_crtc,
 
        crtc->mode.flags = pipe_config->adjusted_mode.flags;
 
-       crtc->mode.clock = pipe_config->adjusted_mode.clock;
+       crtc->mode.clock = pipe_config->adjusted_mode.crtc_clock;
        crtc->mode.flags |= pipe_config->adjusted_mode.flags;
 }
 
@@ -4821,17 +5059,8 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
            I915_READ(PIPECONF(intel_crtc->pipe)) & PIPECONF_ENABLE)
                pipeconf |= PIPECONF_ENABLE;
 
-       if (intel_crtc->pipe == 0 && INTEL_INFO(dev)->gen < 4) {
-               /* Enable pixel doubling when the dot clock is > 90% of the (display)
-                * core speed.
-                *
-                * XXX: No double-wide on 915GM pipe B. Is that the only reason for the
-                * pipe == 0 check?
-                */
-               if (intel_crtc->config.requested_mode.clock >
-                   dev_priv->display.get_display_clock_speed(dev) * 9 / 10)
-                       pipeconf |= PIPECONF_DOUBLE_WIDE;
-       }
+       if (intel_crtc->config.double_wide)
+               pipeconf |= PIPECONF_DOUBLE_WIDE;
 
        /* only g4x and later have fancy bpc/dither controls */
        if (IS_G4X(dev) || IS_VALLEYVIEW(dev)) {
@@ -4885,14 +5114,13 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct drm_display_mode *mode = &intel_crtc->config.requested_mode;
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
        int refclk, num_connectors = 0;
        intel_clock_t clock, reduced_clock;
        u32 dspcntr;
        bool ok, has_reduced_clock = false;
-       bool is_lvds = false;
+       bool is_lvds = false, is_dsi = false;
        struct intel_encoder *encoder;
        const intel_limit_t *limit;
        int ret;
@@ -4902,42 +5130,49 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                case INTEL_OUTPUT_LVDS:
                        is_lvds = true;
                        break;
+               case INTEL_OUTPUT_DSI:
+                       is_dsi = true;
+                       break;
                }
 
                num_connectors++;
        }
 
-       refclk = i9xx_get_refclk(crtc, num_connectors);
+       if (is_dsi)
+               goto skip_dpll;
 
-       /*
-        * Returns a set of divisors for the desired target clock with the given
-        * refclk, or FALSE.  The returned values represent the clock equation:
-        * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
-        */
-       limit = intel_limit(crtc, refclk);
-       ok = dev_priv->display.find_dpll(limit, crtc,
-                                        intel_crtc->config.port_clock,
-                                        refclk, NULL, &clock);
-       if (!ok && !intel_crtc->config.clock_set) {
-               DRM_ERROR("Couldn't find PLL settings for mode!\n");
-               return -EINVAL;
-       }
+       if (!intel_crtc->config.clock_set) {
+               refclk = i9xx_get_refclk(crtc, num_connectors);
 
-       if (is_lvds && dev_priv->lvds_downclock_avail) {
                /*
-                * Ensure we match the reduced clock's P to the target clock.
-                * If the clocks don't match, we can't switch the display clock
-                * by using the FP0/FP1. In such case we will disable the LVDS
-                * downclock feature.
-               */
-               has_reduced_clock =
-                       dev_priv->display.find_dpll(limit, crtc,
-                                                   dev_priv->lvds_downclock,
-                                                   refclk, &clock,
-                                                   &reduced_clock);
-       }
-       /* Compat-code for transition, will disappear. */
-       if (!intel_crtc->config.clock_set) {
+                * Returns a set of divisors for the desired target clock with
+                * the given refclk, or FALSE.  The returned values represent
+                * the clock equation: reflck * (5 * (m1 + 2) + (m2 + 2)) / (n +
+                * 2) / p1 / p2.
+                */
+               limit = intel_limit(crtc, refclk);
+               ok = dev_priv->display.find_dpll(limit, crtc,
+                                                intel_crtc->config.port_clock,
+                                                refclk, NULL, &clock);
+               if (!ok) {
+                       DRM_ERROR("Couldn't find PLL settings for mode!\n");
+                       return -EINVAL;
+               }
+
+               if (is_lvds && dev_priv->lvds_downclock_avail) {
+                       /*
+                        * Ensure we match the reduced clock's P to the target
+                        * clock.  If the clocks don't match, we can't switch
+                        * the display clock by using the FP0/FP1. In such case
+                        * we will disable the LVDS downclock feature.
+                        */
+                       has_reduced_clock =
+                               dev_priv->display.find_dpll(limit, crtc,
+                                                           dev_priv->lvds_downclock,
+                                                           refclk, &clock,
+                                                           &reduced_clock);
+               }
+               /* Compat-code for transition, will disappear. */
                intel_crtc->config.dpll.n = clock.n;
                intel_crtc->config.dpll.m1 = clock.m1;
                intel_crtc->config.dpll.m2 = clock.m2;
@@ -4945,17 +5180,19 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                intel_crtc->config.dpll.p2 = clock.p2;
        }
 
-       if (IS_GEN2(dev))
+       if (IS_GEN2(dev)) {
                i8xx_update_pll(intel_crtc,
                                has_reduced_clock ? &reduced_clock : NULL,
                                num_connectors);
-       else if (IS_VALLEYVIEW(dev))
+       } else if (IS_VALLEYVIEW(dev)) {
                vlv_update_pll(intel_crtc);
-       else
+       } else {
                i9xx_update_pll(intel_crtc,
                                has_reduced_clock ? &reduced_clock : NULL,
                                 num_connectors);
+       }
 
+skip_dpll:
        /* Set up the display plane register */
        dspcntr = DISPPLANE_GAMMA_ENABLE;
 
@@ -4972,8 +5209,8 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
         * which should always be the user's requested size.
         */
        I915_WRITE(DSPSIZE(plane),
-                  ((mode->vdisplay - 1) << 16) |
-                  (mode->hdisplay - 1));
+                  ((intel_crtc->config.pipe_src_h - 1) << 16) |
+                  (intel_crtc->config.pipe_src_w - 1));
        I915_WRITE(DSPPOS(plane), 0);
 
        i9xx_set_pipeconf(intel_crtc);
@@ -4983,8 +5220,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
 
        ret = intel_pipe_set_base(crtc, x, y, fb);
 
-       intel_update_watermarks(dev);
-
        return ret;
 }
 
@@ -5015,6 +5250,32 @@ static void i9xx_get_pfit_config(struct intel_crtc *crtc,
                        I915_READ(LVDS) & LVDS_BORDER_ENABLE;
 }
 
+static void vlv_crtc_clock_get(struct intel_crtc *crtc,
+                              struct intel_crtc_config *pipe_config)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe = pipe_config->cpu_transcoder;
+       intel_clock_t clock;
+       u32 mdiv;
+       int refclk = 100000;
+
+       mutex_lock(&dev_priv->dpio_lock);
+       mdiv = vlv_dpio_read(dev_priv, pipe, DPIO_DIV(pipe));
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       clock.m1 = (mdiv >> DPIO_M1DIV_SHIFT) & 7;
+       clock.m2 = mdiv & DPIO_M2DIV_MASK;
+       clock.n = (mdiv >> DPIO_N_SHIFT) & 0xf;
+       clock.p1 = (mdiv >> DPIO_P1_SHIFT) & 7;
+       clock.p2 = (mdiv >> DPIO_P2_SHIFT) & 0x1f;
+
+       vlv_clock(refclk, &clock);
+
+       /* clock.dot is the fast clock */
+       pipe_config->port_clock = clock.dot / 5;
+}
+
 static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
                                 struct intel_crtc_config *pipe_config)
 {
@@ -5045,6 +5306,9 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
                }
        }
 
+       if (INTEL_INFO(dev)->gen < 4)
+               pipe_config->double_wide = tmp & PIPECONF_DOUBLE_WIDE;
+
        intel_get_pipe_timings(crtc, pipe_config);
 
        i9xx_get_pfit_config(crtc, pipe_config);
@@ -5077,6 +5341,11 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
                                                     DPLL_PORTB_READY_MASK);
        }
 
+       if (IS_VALLEYVIEW(dev))
+               vlv_crtc_clock_get(crtc, pipe_config);
+       else
+               i9xx_crtc_clock_get(crtc, pipe_config);
+
        return true;
 }
 
@@ -5565,14 +5834,16 @@ static void intel_set_pipe_csc(struct drm_crtc *crtc)
 
 static void haswell_set_pipeconf(struct drm_crtc *crtc)
 {
-       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       enum pipe pipe = intel_crtc->pipe;
        enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
        uint32_t val;
 
        val = 0;
 
-       if (intel_crtc->config.dither)
+       if (IS_HASWELL(dev) && intel_crtc->config.dither)
                val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP);
 
        if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE)
@@ -5585,6 +5856,33 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc)
 
        I915_WRITE(GAMMA_MODE(intel_crtc->pipe), GAMMA_MODE_MODE_8BIT);
        POSTING_READ(GAMMA_MODE(intel_crtc->pipe));
+
+       if (IS_BROADWELL(dev)) {
+               val = 0;
+
+               switch (intel_crtc->config.pipe_bpp) {
+               case 18:
+                       val |= PIPEMISC_DITHER_6_BPC;
+                       break;
+               case 24:
+                       val |= PIPEMISC_DITHER_8_BPC;
+                       break;
+               case 30:
+                       val |= PIPEMISC_DITHER_10_BPC;
+                       break;
+               case 36:
+                       val |= PIPEMISC_DITHER_12_BPC;
+                       break;
+               default:
+                       /* Case prevented by pipe_config_set_bpp. */
+                       BUG();
+               }
+
+               if (intel_crtc->config.dither)
+                       val |= PIPEMISC_DITHER_ENABLE | PIPEMISC_DITHER_TYPE_SP;
+
+               I915_WRITE(PIPEMISC(pipe), val);
+       }
 }
 
 static bool ironlake_compute_clocks(struct drm_crtc *crtc,
@@ -5819,11 +6117,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        else
                intel_crtc->lowfreq_avail = false;
 
-       if (intel_crtc->config.has_pch_encoder) {
-               pll = intel_crtc_to_shared_dpll(intel_crtc);
-
-       }
-
        intel_set_pipe_timings(intel_crtc);
 
        if (intel_crtc->config.has_pch_encoder) {
@@ -5839,25 +6132,67 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
 
        ret = intel_pipe_set_base(crtc, x, y, fb);
 
-       intel_update_watermarks(dev);
-
        return ret;
 }
 
-static void ironlake_get_fdi_m_n_config(struct intel_crtc *crtc,
-                                       struct intel_crtc_config *pipe_config)
+static void intel_pch_transcoder_get_m_n(struct intel_crtc *crtc,
+                                        struct intel_link_m_n *m_n)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe = crtc->pipe;
+
+       m_n->link_m = I915_READ(PCH_TRANS_LINK_M1(pipe));
+       m_n->link_n = I915_READ(PCH_TRANS_LINK_N1(pipe));
+       m_n->gmch_m = I915_READ(PCH_TRANS_DATA_M1(pipe))
+               & ~TU_SIZE_MASK;
+       m_n->gmch_n = I915_READ(PCH_TRANS_DATA_N1(pipe));
+       m_n->tu = ((I915_READ(PCH_TRANS_DATA_M1(pipe))
+                   & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1;
+}
+
+static void intel_cpu_transcoder_get_m_n(struct intel_crtc *crtc,
+                                        enum transcoder transcoder,
+                                        struct intel_link_m_n *m_n)
 {
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       enum transcoder transcoder = pipe_config->cpu_transcoder;
+       enum pipe pipe = crtc->pipe;
+
+       if (INTEL_INFO(dev)->gen >= 5) {
+               m_n->link_m = I915_READ(PIPE_LINK_M1(transcoder));
+               m_n->link_n = I915_READ(PIPE_LINK_N1(transcoder));
+               m_n->gmch_m = I915_READ(PIPE_DATA_M1(transcoder))
+                       & ~TU_SIZE_MASK;
+               m_n->gmch_n = I915_READ(PIPE_DATA_N1(transcoder));
+               m_n->tu = ((I915_READ(PIPE_DATA_M1(transcoder))
+                           & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1;
+       } else {
+               m_n->link_m = I915_READ(PIPE_LINK_M_G4X(pipe));
+               m_n->link_n = I915_READ(PIPE_LINK_N_G4X(pipe));
+               m_n->gmch_m = I915_READ(PIPE_DATA_M_G4X(pipe))
+                       & ~TU_SIZE_MASK;
+               m_n->gmch_n = I915_READ(PIPE_DATA_N_G4X(pipe));
+               m_n->tu = ((I915_READ(PIPE_DATA_M_G4X(pipe))
+                           & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1;
+       }
+}
+
+void intel_dp_get_m_n(struct intel_crtc *crtc,
+                     struct intel_crtc_config *pipe_config)
+{
+       if (crtc->config.has_pch_encoder)
+               intel_pch_transcoder_get_m_n(crtc, &pipe_config->dp_m_n);
+       else
+               intel_cpu_transcoder_get_m_n(crtc, pipe_config->cpu_transcoder,
+                                            &pipe_config->dp_m_n);
+}
 
-       pipe_config->fdi_m_n.link_m = I915_READ(PIPE_LINK_M1(transcoder));
-       pipe_config->fdi_m_n.link_n = I915_READ(PIPE_LINK_N1(transcoder));
-       pipe_config->fdi_m_n.gmch_m = I915_READ(PIPE_DATA_M1(transcoder))
-                                       & ~TU_SIZE_MASK;
-       pipe_config->fdi_m_n.gmch_n = I915_READ(PIPE_DATA_N1(transcoder));
-       pipe_config->fdi_m_n.tu = ((I915_READ(PIPE_DATA_M1(transcoder))
-                                  & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1;
+static void ironlake_get_fdi_m_n_config(struct intel_crtc *crtc,
+                                       struct intel_crtc_config *pipe_config)
+{
+       intel_cpu_transcoder_get_m_n(crtc, pipe_config->cpu_transcoder,
+                                    &pipe_config->fdi_m_n);
 }
 
 static void ironlake_get_pfit_config(struct intel_crtc *crtc,
@@ -5946,6 +6281,8 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
                pipe_config->pixel_multiplier =
                        ((tmp & PLL_REF_SDVO_HDMI_MULTIPLIER_MASK)
                         >> PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT) + 1;
+
+               ironlake_pch_clock_get(crtc, pipe_config);
        } else {
                pipe_config->pixel_multiplier = 1;
        }
@@ -6002,8 +6339,8 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
  * register. Callers should take care of disabling all the display engine
  * functions, doing the mode unset, fixing interrupts, etc.
  */
-void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
-                      bool switch_to_fclk, bool allow_power_down)
+static void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
+                             bool switch_to_fclk, bool allow_power_down)
 {
        uint32_t val;
 
@@ -6031,7 +6368,10 @@ void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
 
        val = I915_READ(D_COMP);
        val |= D_COMP_COMP_DISABLE;
-       I915_WRITE(D_COMP, val);
+       mutex_lock(&dev_priv->rps.hw_lock);
+       if (sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_D_COMP, val))
+               DRM_ERROR("Failed to disable D_COMP\n");
+       mutex_unlock(&dev_priv->rps.hw_lock);
        POSTING_READ(D_COMP);
        ndelay(100);
 
@@ -6050,7 +6390,7 @@ void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
  * Fully restores LCPLL, disallowing power down and switching back to LCPLL
  * source.
  */
-void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
+static void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
 {
        uint32_t val;
 
@@ -6073,7 +6413,10 @@ void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
        val = I915_READ(D_COMP);
        val |= D_COMP_COMP_FORCE;
        val &= ~D_COMP_COMP_DISABLE;
-       I915_WRITE(D_COMP, val);
+       mutex_lock(&dev_priv->rps.hw_lock);
+       if (sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_D_COMP, val))
+               DRM_ERROR("Failed to enable D_COMP\n");
+       mutex_unlock(&dev_priv->rps.hw_lock);
        POSTING_READ(D_COMP);
 
        val = I915_READ(LCPLL_CTL);
@@ -6256,22 +6599,79 @@ static void hsw_package_c8_gpu_busy(struct drm_i915_private *dev_priv)
        }
 }
 
-static void haswell_modeset_global_resources(struct drm_device *dev)
+#define for_each_power_domain(domain, mask)                            \
+       for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++)     \
+               if ((1 << (domain)) & (mask))
+
+static unsigned long get_pipe_power_domains(struct drm_device *dev,
+                                           enum pipe pipe, bool pfit_enabled)
 {
-       bool enable = false;
+       unsigned long mask;
+       enum transcoder transcoder;
+
+       transcoder = intel_pipe_to_cpu_transcoder(dev->dev_private, pipe);
+
+       mask = BIT(POWER_DOMAIN_PIPE(pipe));
+       mask |= BIT(POWER_DOMAIN_TRANSCODER(transcoder));
+       if (pfit_enabled)
+               mask |= BIT(POWER_DOMAIN_PIPE_PANEL_FITTER(pipe));
+
+       return mask;
+}
+
+void intel_display_set_init_power(struct drm_device *dev, bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->power_domains.init_power_on == enable)
+               return;
+
+       if (enable)
+               intel_display_power_get(dev, POWER_DOMAIN_INIT);
+       else
+               intel_display_power_put(dev, POWER_DOMAIN_INIT);
+
+       dev_priv->power_domains.init_power_on = enable;
+}
+
+static void modeset_update_power_wells(struct drm_device *dev)
+{
+       unsigned long pipe_domains[I915_MAX_PIPES] = { 0, };
        struct intel_crtc *crtc;
 
+       /*
+        * First get all needed power domains, then put all unneeded, to avoid
+        * any unnecessary toggling of the power wells.
+        */
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+               enum intel_display_power_domain domain;
+
                if (!crtc->base.enabled)
                        continue;
 
-               if (crtc->pipe != PIPE_A || crtc->config.pch_pfit.enabled ||
-                   crtc->config.cpu_transcoder != TRANSCODER_EDP)
-                       enable = true;
+               pipe_domains[crtc->pipe] = get_pipe_power_domains(dev,
+                                               crtc->pipe,
+                                               crtc->config.pch_pfit.enabled);
+
+               for_each_power_domain(domain, pipe_domains[crtc->pipe])
+                       intel_display_power_get(dev, domain);
+       }
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+               enum intel_display_power_domain domain;
+
+               for_each_power_domain(domain, crtc->enabled_power_domains)
+                       intel_display_power_put(dev, domain);
+
+               crtc->enabled_power_domains = pipe_domains[crtc->pipe];
        }
 
-       intel_set_power_well(dev, enable);
+       intel_display_set_init_power(dev, false);
+}
 
+static void haswell_modeset_global_resources(struct drm_device *dev)
+{
+       modeset_update_power_wells(dev);
        hsw_update_package_c8(dev);
 }
 
@@ -6310,8 +6710,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc,
 
        ret = intel_pipe_set_base(crtc, x, y, fb);
 
-       intel_update_watermarks(dev);
-
        return ret;
 }
 
@@ -6419,6 +6817,44 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        return 0;
 }
 
+static struct {
+       int clock;
+       u32 config;
+} hdmi_audio_clock[] = {
+       { DIV_ROUND_UP(25200 * 1000, 1001), AUD_CONFIG_PIXEL_CLOCK_HDMI_25175 },
+       { 25200, AUD_CONFIG_PIXEL_CLOCK_HDMI_25200 }, /* default per bspec */
+       { 27000, AUD_CONFIG_PIXEL_CLOCK_HDMI_27000 },
+       { 27000 * 1001 / 1000, AUD_CONFIG_PIXEL_CLOCK_HDMI_27027 },
+       { 54000, AUD_CONFIG_PIXEL_CLOCK_HDMI_54000 },
+       { 54000 * 1001 / 1000, AUD_CONFIG_PIXEL_CLOCK_HDMI_54054 },
+       { DIV_ROUND_UP(74250 * 1000, 1001), AUD_CONFIG_PIXEL_CLOCK_HDMI_74176 },
+       { 74250, AUD_CONFIG_PIXEL_CLOCK_HDMI_74250 },
+       { DIV_ROUND_UP(148500 * 1000, 1001), AUD_CONFIG_PIXEL_CLOCK_HDMI_148352 },
+       { 148500, AUD_CONFIG_PIXEL_CLOCK_HDMI_148500 },
+};
+
+/* get AUD_CONFIG_PIXEL_CLOCK_HDMI_* value for mode */
+static u32 audio_config_hdmi_pixel_clock(struct drm_display_mode *mode)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(hdmi_audio_clock); i++) {
+               if (mode->clock == hdmi_audio_clock[i].clock)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(hdmi_audio_clock)) {
+               DRM_DEBUG_KMS("HDMI audio pixel clock setting for %d not found, falling back to defaults\n", mode->clock);
+               i = 1;
+       }
+
+       DRM_DEBUG_KMS("Configuring HDMI audio for pixel clock %d (0x%08x)\n",
+                     hdmi_audio_clock[i].clock,
+                     hdmi_audio_clock[i].config);
+
+       return hdmi_audio_clock[i].config;
+}
+
 static bool intel_eld_uptodate(struct drm_connector *connector,
                               int reg_eldv, uint32_t bits_eldv,
                               int reg_elda, uint32_t bits_elda,
@@ -6449,7 +6885,8 @@ static bool intel_eld_uptodate(struct drm_connector *connector,
 }
 
 static void g4x_write_eld(struct drm_connector *connector,
-                         struct drm_crtc *crtc)
+                         struct drm_crtc *crtc,
+                         struct drm_display_mode *mode)
 {
        struct drm_i915_private *dev_priv = connector->dev->dev_private;
        uint8_t *eld = connector->eld;
@@ -6489,7 +6926,8 @@ static void g4x_write_eld(struct drm_connector *connector,
 }
 
 static void haswell_write_eld(struct drm_connector *connector,
-                                    struct drm_crtc *crtc)
+                             struct drm_crtc *crtc,
+                             struct drm_display_mode *mode)
 {
        struct drm_i915_private *dev_priv = connector->dev->dev_private;
        uint8_t *eld = connector->eld;
@@ -6542,8 +6980,9 @@ static void haswell_write_eld(struct drm_connector *connector,
                DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
                eld[5] |= (1 << 2);     /* Conn_Type, 0x1 = DisplayPort */
                I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */
-       } else
-               I915_WRITE(aud_config, 0);
+       } else {
+               I915_WRITE(aud_config, audio_config_hdmi_pixel_clock(mode));
+       }
 
        if (intel_eld_uptodate(connector,
                               aud_cntrl_st2, eldv,
@@ -6576,7 +7015,8 @@ static void haswell_write_eld(struct drm_connector *connector,
 }
 
 static void ironlake_write_eld(struct drm_connector *connector,
-                                    struct drm_crtc *crtc)
+                              struct drm_crtc *crtc,
+                              struct drm_display_mode *mode)
 {
        struct drm_i915_private *dev_priv = connector->dev->dev_private;
        uint8_t *eld = connector->eld;
@@ -6594,6 +7034,11 @@ static void ironlake_write_eld(struct drm_connector *connector,
                aud_config = IBX_AUD_CFG(pipe);
                aud_cntl_st = IBX_AUD_CNTL_ST(pipe);
                aud_cntrl_st2 = IBX_AUD_CNTL_ST2;
+       } else if (IS_VALLEYVIEW(connector->dev)) {
+               hdmiw_hdmiedid = VLV_HDMIW_HDMIEDID(pipe);
+               aud_config = VLV_AUD_CFG(pipe);
+               aud_cntl_st = VLV_AUD_CNTL_ST(pipe);
+               aud_cntrl_st2 = VLV_AUD_CNTL_ST2;
        } else {
                hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID(pipe);
                aud_config = CPT_AUD_CFG(pipe);
@@ -6603,8 +7048,19 @@ static void ironlake_write_eld(struct drm_connector *connector,
 
        DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe));
 
-       i = I915_READ(aud_cntl_st);
-       i = (i >> 29) & DIP_PORT_SEL_MASK;              /* DIP_Port_Select, 0x1 = PortB */
+       if (IS_VALLEYVIEW(connector->dev))  {
+               struct intel_encoder *intel_encoder;
+               struct intel_digital_port *intel_dig_port;
+
+               intel_encoder = intel_attached_encoder(connector);
+               intel_dig_port = enc_to_dig_port(&intel_encoder->base);
+               i = intel_dig_port->port;
+       } else {
+               i = I915_READ(aud_cntl_st);
+               i = (i >> 29) & DIP_PORT_SEL_MASK;
+               /* DIP_Port_Select, 0x1 = PortB */
+       }
+
        if (!i) {
                DRM_DEBUG_DRIVER("Audio directed to unknown port\n");
                /* operate blindly on all ports */
@@ -6620,8 +7076,9 @@ static void ironlake_write_eld(struct drm_connector *connector,
                DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
                eld[5] |= (1 << 2);     /* Conn_Type, 0x1 = DisplayPort */
                I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */
-       } else
-               I915_WRITE(aud_config, 0);
+       } else {
+               I915_WRITE(aud_config, audio_config_hdmi_pixel_clock(mode));
+       }
 
        if (intel_eld_uptodate(connector,
                               aud_cntrl_st2, eldv,
@@ -6671,50 +7128,7 @@ void intel_write_eld(struct drm_encoder *encoder,
        connector->eld[6] = drm_av_sync_delay(connector, mode) / 2;
 
        if (dev_priv->display.write_eld)
-               dev_priv->display.write_eld(connector, crtc);
-}
-
-/** Loads the palette/gamma unit for the CRTC with the prepared values */
-void intel_crtc_load_lut(struct drm_crtc *crtc)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       enum pipe pipe = intel_crtc->pipe;
-       int palreg = PALETTE(pipe);
-       int i;
-       bool reenable_ips = false;
-
-       /* The clocks have to be on to load the palette. */
-       if (!crtc->enabled || !intel_crtc->active)
-               return;
-
-       if (!HAS_PCH_SPLIT(dev_priv->dev))
-               assert_pll_enabled(dev_priv, pipe);
-
-       /* use legacy palette for Ironlake */
-       if (HAS_PCH_SPLIT(dev))
-               palreg = LGC_PALETTE(pipe);
-
-       /* Workaround : Do not read or write the pipe palette/gamma data while
-        * GAMMA_MODE is configured for split gamma and IPS_CTL has IPS enabled.
-        */
-       if (intel_crtc->config.ips_enabled &&
-           ((I915_READ(GAMMA_MODE(pipe)) & GAMMA_MODE_MODE_MASK) ==
-            GAMMA_MODE_MODE_SPLIT)) {
-               hsw_disable_ips(intel_crtc);
-               reenable_ips = true;
-       }
-
-       for (i = 0; i < 256; i++) {
-               I915_WRITE(palreg + 4 * i,
-                          (intel_crtc->lut_r[i] << 16) |
-                          (intel_crtc->lut_g[i] << 8) |
-                          intel_crtc->lut_b[i]);
-       }
-
-       if (reenable_ips)
-               hsw_enable_ips(intel_crtc);
+               dev_priv->display.write_eld(connector, crtc, mode);
 }
 
 static void i845_update_cursor(struct drm_crtc *crtc, u32 base)
@@ -6790,7 +7204,7 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
                        cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
                        cntl |= CURSOR_MODE_DISABLE;
                }
-               if (IS_HASWELL(dev)) {
+               if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
                        cntl |= CURSOR_PIPE_CSC_ENABLE;
                        cntl &= ~CURSOR_TRICKLE_FEED_DISABLE;
                }
@@ -6812,23 +7226,20 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
        int pipe = intel_crtc->pipe;
        int x = intel_crtc->cursor_x;
        int y = intel_crtc->cursor_y;
-       u32 base, pos;
+       u32 base = 0, pos = 0;
        bool visible;
 
-       pos = 0;
-
-       if (on && crtc->enabled && crtc->fb) {
+       if (on)
                base = intel_crtc->cursor_addr;
-               if (x > (int) crtc->fb->width)
-                       base = 0;
 
-               if (y > (int) crtc->fb->height)
-                       base = 0;
-       } else
+       if (x >= intel_crtc->config.pipe_src_w)
+               base = 0;
+
+       if (y >= intel_crtc->config.pipe_src_h)
                base = 0;
 
        if (x < 0) {
-               if (x + intel_crtc->cursor_width < 0)
+               if (x + intel_crtc->cursor_width <= 0)
                        base = 0;
 
                pos |= CURSOR_POS_SIGN << CURSOR_X_SHIFT;
@@ -6837,7 +7248,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
        pos |= x << CURSOR_X_SHIFT;
 
        if (y < 0) {
-               if (y + intel_crtc->cursor_height < 0)
+               if (y + intel_crtc->cursor_height <= 0)
                        base = 0;
 
                pos |= CURSOR_POS_SIGN << CURSOR_Y_SHIFT;
@@ -6849,7 +7260,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
        if (!visible && !intel_crtc->cursor_visible)
                return;
 
-       if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) {
+       if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev)) {
                I915_WRITE(CURPOS_IVB(pipe), pos);
                ivb_update_cursor(crtc, base);
        } else {
@@ -6980,8 +7391,8 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-       intel_crtc->cursor_x = x;
-       intel_crtc->cursor_y = y;
+       intel_crtc->cursor_x = clamp_t(int, x, SHRT_MIN, SHRT_MAX);
+       intel_crtc->cursor_y = clamp_t(int, y, SHRT_MIN, SHRT_MAX);
 
        if (intel_crtc->active)
                intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
@@ -6989,27 +7400,6 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
        return 0;
 }
 
-/** Sets the color ramps on behalf of RandR */
-void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
-                                u16 blue, int regno)
-{
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-
-       intel_crtc->lut_r[regno] = red >> 8;
-       intel_crtc->lut_g[regno] = green >> 8;
-       intel_crtc->lut_b[regno] = blue >> 8;
-}
-
-void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
-                            u16 *blue, int regno)
-{
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-
-       *red = intel_crtc->lut_r[regno] << 8;
-       *green = intel_crtc->lut_g[regno] << 8;
-       *blue = intel_crtc->lut_b[regno] << 8;
-}
-
 static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
                                 u16 *blue, uint32_t start, uint32_t size)
 {
@@ -7045,14 +7435,21 @@ intel_framebuffer_create(struct drm_device *dev,
                return ERR_PTR(-ENOMEM);
        }
 
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               goto err;
+
        ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
-       if (ret) {
-               drm_gem_object_unreference_unlocked(&obj->base);
-               kfree(intel_fb);
-               return ERR_PTR(ret);
-       }
+       mutex_unlock(&dev->struct_mutex);
+       if (ret)
+               goto err;
 
        return &intel_fb->base;
+err:
+       drm_gem_object_unreference_unlocked(&obj->base);
+       kfree(intel_fb);
+
+       return ERR_PTR(ret);
 }
 
 static u32
@@ -7095,6 +7492,7 @@ static struct drm_framebuffer *
 mode_fits_in_fbdev(struct drm_device *dev,
                   struct drm_display_mode *mode)
 {
+#ifdef CONFIG_DRM_I915_FBDEV
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj;
        struct drm_framebuffer *fb;
@@ -7115,6 +7513,9 @@ mode_fits_in_fbdev(struct drm_device *dev,
                return NULL;
 
        return fb;
+#else
+       return NULL;
+#endif
 }
 
 bool intel_get_load_detect_pipe(struct drm_connector *connector,
@@ -7258,6 +7659,22 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
        mutex_unlock(&crtc->mutex);
 }
 
+static int i9xx_pll_refclk(struct drm_device *dev,
+                          const struct intel_crtc_config *pipe_config)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 dpll = pipe_config->dpll_hw_state.dpll;
+
+       if ((dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN)
+               return dev_priv->vbt.lvds_ssc_freq * 1000;
+       else if (HAS_PCH_SPLIT(dev))
+               return 120000;
+       else if (!IS_GEN2(dev))
+               return 96000;
+       else
+               return 48000;
+}
+
 /* Returns the clock of the currently programmed mode of the given pipe. */
 static void i9xx_crtc_clock_get(struct intel_crtc *crtc,
                                struct intel_crtc_config *pipe_config)
@@ -7265,14 +7682,15 @@ static void i9xx_crtc_clock_get(struct intel_crtc *crtc,
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int pipe = pipe_config->cpu_transcoder;
-       u32 dpll = I915_READ(DPLL(pipe));
+       u32 dpll = pipe_config->dpll_hw_state.dpll;
        u32 fp;
        intel_clock_t clock;
+       int refclk = i9xx_pll_refclk(dev, pipe_config);
 
        if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
-               fp = I915_READ(FP0(pipe));
+               fp = pipe_config->dpll_hw_state.fp0;
        else
-               fp = I915_READ(FP1(pipe));
+               fp = pipe_config->dpll_hw_state.fp1;
 
        clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
        if (IS_PINEVIEW(dev)) {
@@ -7303,14 +7721,13 @@ static void i9xx_crtc_clock_get(struct intel_crtc *crtc,
                default:
                        DRM_DEBUG_KMS("Unknown DPLL mode %08x in programmed "
                                  "mode\n", (int)(dpll & DPLL_MODE_MASK));
-                       pipe_config->adjusted_mode.clock = 0;
                        return;
                }
 
                if (IS_PINEVIEW(dev))
-                       pineview_clock(96000, &clock);
+                       pineview_clock(refclk, &clock);
                else
-                       i9xx_clock(96000, &clock);
+                       i9xx_clock(refclk, &clock);
        } else {
                bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN);
 
@@ -7318,13 +7735,6 @@ static void i9xx_crtc_clock_get(struct intel_crtc *crtc,
                        clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >>
                                       DPLL_FPA01_P1_POST_DIV_SHIFT);
                        clock.p2 = 14;
-
-                       if ((dpll & PLL_REF_INPUT_MASK) ==
-                           PLLB_REF_INPUT_SPREADSPECTRUMIN) {
-                               /* XXX: might not be 66MHz */
-                               i9xx_clock(66000, &clock);
-                       } else
-                               i9xx_clock(48000, &clock);
                } else {
                        if (dpll & PLL_P1_DIVIDE_BY_TWO)
                                clock.p1 = 2;
@@ -7336,59 +7746,55 @@ static void i9xx_crtc_clock_get(struct intel_crtc *crtc,
                                clock.p2 = 4;
                        else
                                clock.p2 = 2;
-
-                       i9xx_clock(48000, &clock);
                }
+
+               i9xx_clock(refclk, &clock);
        }
 
-       pipe_config->adjusted_mode.clock = clock.dot;
+       /*
+        * This value includes pixel_multiplier. We will use
+        * port_clock to compute adjusted_mode.crtc_clock in the
+        * encoder's get_config() function.
+        */
+       pipe_config->port_clock = clock.dot;
 }
 
-static void ironlake_crtc_clock_get(struct intel_crtc *crtc,
-                                   struct intel_crtc_config *pipe_config)
+int intel_dotclock_calculate(int link_freq,
+                            const struct intel_link_m_n *m_n)
 {
-       struct drm_device *dev = crtc->base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       enum transcoder cpu_transcoder = pipe_config->cpu_transcoder;
-       int link_freq, repeat;
-       u64 clock;
-       u32 link_m, link_n;
-
-       repeat = pipe_config->pixel_multiplier;
-
        /*
         * The calculation for the data clock is:
-        * pixel_clock = ((m/n)*(link_clock * nr_lanes * repeat))/bpp
+        * pixel_clock = ((m/n)*(link_clock * nr_lanes))/bpp
         * But we want to avoid losing precison if possible, so:
-        * pixel_clock = ((m * link_clock * nr_lanes * repeat)/(n*bpp))
+        * pixel_clock = ((m * link_clock * nr_lanes)/(n*bpp))
         *
         * and the link clock is simpler:
-        * link_clock = (m * link_clock * repeat) / n
+        * link_clock = (m * link_clock) / n
         */
 
-       /*
-        * We need to get the FDI or DP link clock here to derive
-        * the M/N dividers.
-        *
-        * For FDI, we read it from the BIOS or use a fixed 2.7GHz.
-        * For DP, it's either 1.62GHz or 2.7GHz.
-        * We do our calculations in 10*MHz since we don't need much precison.
-        */
-       if (pipe_config->has_pch_encoder)
-               link_freq = intel_fdi_link_freq(dev) * 10000;
-       else
-               link_freq = pipe_config->port_clock;
+       if (!m_n->link_n)
+               return 0;
 
-       link_m = I915_READ(PIPE_LINK_M1(cpu_transcoder));
-       link_n = I915_READ(PIPE_LINK_N1(cpu_transcoder));
+       return div_u64((u64)m_n->link_m * link_freq, m_n->link_n);
+}
 
-       if (!link_m || !link_n)
-               return;
+static void ironlake_pch_clock_get(struct intel_crtc *crtc,
+                                  struct intel_crtc_config *pipe_config)
+{
+       struct drm_device *dev = crtc->base.dev;
 
-       clock = ((u64)link_m * (u64)link_freq * (u64)repeat);
-       do_div(clock, link_n);
+       /* read out port_clock from the DPLL */
+       i9xx_crtc_clock_get(crtc, pipe_config);
 
-       pipe_config->adjusted_mode.clock = clock;
+       /*
+        * This value does not include pixel_multiplier.
+        * We will check that port_clock and adjusted_mode.crtc_clock
+        * agree once we know their relationship in the encoder's
+        * get_config() function.
+        */
+       pipe_config->adjusted_mode.crtc_clock =
+               intel_dotclock_calculate(intel_fdi_link_freq(dev) * 10000,
+                                        &pipe_config->fdi_m_n);
 }
 
 /** Returns the currently programmed mode of the given pipe. */
@@ -7404,6 +7810,7 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
        int hsync = I915_READ(HSYNC(cpu_transcoder));
        int vtot = I915_READ(VTOTAL(cpu_transcoder));
        int vsync = I915_READ(VSYNC(cpu_transcoder));
+       enum pipe pipe = intel_crtc->pipe;
 
        mode = kzalloc(sizeof(*mode), GFP_KERNEL);
        if (!mode)
@@ -7416,11 +7823,14 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
         * Note, if LVDS ever uses a non-1 pixel multiplier, we'll need
         * to use a real value here instead.
         */
-       pipe_config.cpu_transcoder = (enum transcoder) intel_crtc->pipe;
+       pipe_config.cpu_transcoder = (enum transcoder) pipe;
        pipe_config.pixel_multiplier = 1;
+       pipe_config.dpll_hw_state.dpll = I915_READ(DPLL(pipe));
+       pipe_config.dpll_hw_state.fp0 = I915_READ(FP0(pipe));
+       pipe_config.dpll_hw_state.fp1 = I915_READ(FP1(pipe));
        i9xx_crtc_clock_get(intel_crtc, &pipe_config);
 
-       mode->clock = pipe_config.adjusted_mode.clock;
+       mode->clock = pipe_config.port_clock / pipe_config.pixel_multiplier;
        mode->hdisplay = (htot & 0xffff) + 1;
        mode->htotal = ((htot & 0xffff0000) >> 16) + 1;
        mode->hsync_start = (hsync & 0xffff) + 1;
@@ -7526,6 +7936,9 @@ void intel_mark_idle(struct drm_device *dev)
 
                intel_decrease_pllclock(crtc);
        }
+
+       if (dev_priv->info->gen >= 6)
+               gen6_rps_idle(dev->dev_private);
 }
 
 void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
@@ -7714,7 +8127,7 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, 0); /* aux display base address, unused */
 
        intel_mark_page_flip_active(intel_crtc);
-       intel_ring_advance(ring);
+       __intel_ring_advance(ring);
        return 0;
 
 err_unpin:
@@ -7756,7 +8169,7 @@ static int intel_gen3_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, MI_NOOP);
 
        intel_mark_page_flip_active(intel_crtc);
-       intel_ring_advance(ring);
+       __intel_ring_advance(ring);
        return 0;
 
 err_unpin:
@@ -7805,7 +8218,7 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, pf | pipesrc);
 
        intel_mark_page_flip_active(intel_crtc);
-       intel_ring_advance(ring);
+       __intel_ring_advance(ring);
        return 0;
 
 err_unpin:
@@ -7850,7 +8263,7 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, pf | pipesrc);
 
        intel_mark_page_flip_active(intel_crtc);
-       intel_ring_advance(ring);
+       __intel_ring_advance(ring);
        return 0;
 
 err_unpin:
@@ -7929,7 +8342,7 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, (MI_NOOP));
 
        intel_mark_page_flip_active(intel_crtc);
-       intel_ring_advance(ring);
+       __intel_ring_advance(ring);
        return 0;
 
 err_unpin:
@@ -7974,7 +8387,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
             fb->pitches[0] != crtc->fb->pitches[0]))
                return -EINVAL;
 
-       work = kzalloc(sizeof *work, GFP_KERNEL);
+       work = kzalloc(sizeof(*work), GFP_KERNEL);
        if (work == NULL)
                return -ENOMEM;
 
@@ -8209,6 +8622,17 @@ compute_baseline_pipe_bpp(struct intel_crtc *crtc,
        return bpp;
 }
 
+static void intel_dump_crtc_timings(const struct drm_display_mode *mode)
+{
+       DRM_DEBUG_KMS("crtc timings: %d %d %d %d %d %d %d %d %d, "
+                       "type: 0x%x flags: 0x%x\n",
+               mode->crtc_clock,
+               mode->crtc_hdisplay, mode->crtc_hsync_start,
+               mode->crtc_hsync_end, mode->crtc_htotal,
+               mode->crtc_vdisplay, mode->crtc_vsync_start,
+               mode->crtc_vsync_end, mode->crtc_vtotal, mode->type, mode->flags);
+}
+
 static void intel_dump_pipe_config(struct intel_crtc *crtc,
                                   struct intel_crtc_config *pipe_config,
                                   const char *context)
@@ -8225,10 +8649,19 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
                      pipe_config->fdi_m_n.gmch_m, pipe_config->fdi_m_n.gmch_n,
                      pipe_config->fdi_m_n.link_m, pipe_config->fdi_m_n.link_n,
                      pipe_config->fdi_m_n.tu);
+       DRM_DEBUG_KMS("dp: %i, gmch_m: %u, gmch_n: %u, link_m: %u, link_n: %u, tu: %u\n",
+                     pipe_config->has_dp_encoder,
+                     pipe_config->dp_m_n.gmch_m, pipe_config->dp_m_n.gmch_n,
+                     pipe_config->dp_m_n.link_m, pipe_config->dp_m_n.link_n,
+                     pipe_config->dp_m_n.tu);
        DRM_DEBUG_KMS("requested mode:\n");
        drm_mode_debug_printmodeline(&pipe_config->requested_mode);
        DRM_DEBUG_KMS("adjusted mode:\n");
        drm_mode_debug_printmodeline(&pipe_config->adjusted_mode);
+       intel_dump_crtc_timings(&pipe_config->adjusted_mode);
+       DRM_DEBUG_KMS("port clock: %d\n", pipe_config->port_clock);
+       DRM_DEBUG_KMS("pipe src size: %dx%d\n",
+                     pipe_config->pipe_src_w, pipe_config->pipe_src_h);
        DRM_DEBUG_KMS("gmch pfit: control: 0x%08x, ratios: 0x%08x, lvds border: 0x%08x\n",
                      pipe_config->gmch_pfit.control,
                      pipe_config->gmch_pfit.pgm_ratios,
@@ -8238,6 +8671,7 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
                      pipe_config->pch_pfit.size,
                      pipe_config->pch_pfit.enabled ? "enabled" : "disabled");
        DRM_DEBUG_KMS("ips: %i\n", pipe_config->ips_enabled);
+       DRM_DEBUG_KMS("double wide: %i\n", pipe_config->double_wide);
 }
 
 static bool check_encoder_cloning(struct drm_crtc *crtc)
@@ -8281,6 +8715,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc,
 
        drm_mode_copy(&pipe_config->adjusted_mode, mode);
        drm_mode_copy(&pipe_config->requested_mode, mode);
+
        pipe_config->cpu_transcoder =
                (enum transcoder) to_intel_crtc(crtc)->pipe;
        pipe_config->shared_dpll = DPLL_ID_PRIVATE;
@@ -8307,13 +8742,25 @@ intel_modeset_pipe_config(struct drm_crtc *crtc,
        if (plane_bpp < 0)
                goto fail;
 
+       /*
+        * Determine the real pipe dimensions. Note that stereo modes can
+        * increase the actual pipe size due to the frame doubling and
+        * insertion of additional space for blanks between the frame. This
+        * is stored in the crtc timings. We use the requested mode to do this
+        * computation to clearly distinguish it from the adjusted mode, which
+        * can be changed by the connectors in the below retry loop.
+        */
+       drm_mode_set_crtcinfo(&pipe_config->requested_mode, CRTC_STEREO_DOUBLE);
+       pipe_config->pipe_src_w = pipe_config->requested_mode.crtc_hdisplay;
+       pipe_config->pipe_src_h = pipe_config->requested_mode.crtc_vdisplay;
+
 encoder_retry:
        /* Ensure the port clock defaults are reset when retrying. */
        pipe_config->port_clock = 0;
        pipe_config->pixel_multiplier = 1;
 
        /* Fill in default crtc timings, allow encoders to overwrite them. */
-       drm_mode_set_crtcinfo(&pipe_config->adjusted_mode, 0);
+       drm_mode_set_crtcinfo(&pipe_config->adjusted_mode, CRTC_STEREO_DOUBLE);
 
        /* Pass our mode to the connectors and the CRTC to give them a chance to
         * adjust it according to limitations or connector properties, and also
@@ -8334,7 +8781,8 @@ encoder_retry:
        /* Set default port clock if not overwritten by the encoder. Needs to be
         * done afterwards in case the encoder adjusts the mode. */
        if (!pipe_config->port_clock)
-               pipe_config->port_clock = pipe_config->adjusted_mode.clock;
+               pipe_config->port_clock = pipe_config->adjusted_mode.crtc_clock
+                       * pipe_config->pixel_multiplier;
 
        ret = intel_crtc_compute_config(to_intel_crtc(crtc), pipe_config);
        if (ret < 0) {
@@ -8521,13 +8969,9 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes)
 
 }
 
-static bool intel_fuzzy_clock_check(struct intel_crtc_config *cur,
-                                   struct intel_crtc_config *new)
+static bool intel_fuzzy_clock_check(int clock1, int clock2)
 {
-       int clock1, clock2, diff;
-
-       clock1 = cur->adjusted_mode.clock;
-       clock2 = new->adjusted_mode.clock;
+       int diff;
 
        if (clock1 == clock2)
                return true;
@@ -8581,6 +9025,15 @@ intel_pipe_config_compare(struct drm_device *dev,
                return false; \
        }
 
+#define PIPE_CONF_CHECK_CLOCK_FUZZY(name) \
+       if (!intel_fuzzy_clock_check(current_config->name, pipe_config->name)) { \
+               DRM_ERROR("mismatch in " #name " " \
+                         "(expected %i, found %i)\n", \
+                         current_config->name, \
+                         pipe_config->name); \
+               return false; \
+       }
+
 #define PIPE_CONF_QUIRK(quirk) \
        ((current_config->quirks | pipe_config->quirks) & (quirk))
 
@@ -8594,6 +9047,13 @@ intel_pipe_config_compare(struct drm_device *dev,
        PIPE_CONF_CHECK_I(fdi_m_n.link_n);
        PIPE_CONF_CHECK_I(fdi_m_n.tu);
 
+       PIPE_CONF_CHECK_I(has_dp_encoder);
+       PIPE_CONF_CHECK_I(dp_m_n.gmch_m);
+       PIPE_CONF_CHECK_I(dp_m_n.gmch_n);
+       PIPE_CONF_CHECK_I(dp_m_n.link_m);
+       PIPE_CONF_CHECK_I(dp_m_n.link_n);
+       PIPE_CONF_CHECK_I(dp_m_n.tu);
+
        PIPE_CONF_CHECK_I(adjusted_mode.crtc_hdisplay);
        PIPE_CONF_CHECK_I(adjusted_mode.crtc_htotal);
        PIPE_CONF_CHECK_I(adjusted_mode.crtc_hblank_start);
@@ -8624,8 +9084,8 @@ intel_pipe_config_compare(struct drm_device *dev,
                                      DRM_MODE_FLAG_NVSYNC);
        }
 
-       PIPE_CONF_CHECK_I(requested_mode.hdisplay);
-       PIPE_CONF_CHECK_I(requested_mode.vdisplay);
+       PIPE_CONF_CHECK_I(pipe_src_w);
+       PIPE_CONF_CHECK_I(pipe_src_h);
 
        PIPE_CONF_CHECK_I(gmch_pfit.control);
        /* pfit ratios are autocomputed by the hw on gen4+ */
@@ -8640,6 +9100,8 @@ intel_pipe_config_compare(struct drm_device *dev,
 
        PIPE_CONF_CHECK_I(ips_enabled);
 
+       PIPE_CONF_CHECK_I(double_wide);
+
        PIPE_CONF_CHECK_I(shared_dpll);
        PIPE_CONF_CHECK_X(dpll_hw_state.dpll);
        PIPE_CONF_CHECK_X(dpll_hw_state.dpll_md);
@@ -8649,20 +9111,17 @@ intel_pipe_config_compare(struct drm_device *dev,
        if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5)
                PIPE_CONF_CHECK_I(pipe_bpp);
 
+       if (!IS_HASWELL(dev)) {
+               PIPE_CONF_CHECK_CLOCK_FUZZY(adjusted_mode.crtc_clock);
+               PIPE_CONF_CHECK_CLOCK_FUZZY(port_clock);
+       }
+
 #undef PIPE_CONF_CHECK_X
 #undef PIPE_CONF_CHECK_I
 #undef PIPE_CONF_CHECK_FLAGS
+#undef PIPE_CONF_CHECK_CLOCK_FUZZY
 #undef PIPE_CONF_QUIRK
 
-       if (!IS_HASWELL(dev)) {
-               if (!intel_fuzzy_clock_check(current_config, pipe_config)) {
-                       DRM_ERROR("mismatch in clock (expected %d, found %d)\n",
-                                 current_config->adjusted_mode.clock,
-                                 pipe_config->adjusted_mode.clock);
-                       return false;
-               }
-       }
-
        return true;
 }
 
@@ -8794,9 +9253,6 @@ check_crtc_state(struct drm_device *dev)
                                encoder->get_config(encoder, &pipe_config);
                }
 
-               if (dev_priv->display.get_clock)
-                       dev_priv->display.get_clock(crtc, &pipe_config);
-
                WARN(crtc->active != active,
                     "crtc active state doesn't match with hw state "
                     "(expected %i, found %i)\n", crtc->active, active);
@@ -8871,6 +9327,18 @@ intel_modeset_check_state(struct drm_device *dev)
        check_shared_dpll_state(dev);
 }
 
+void ironlake_check_encoder_dotclock(const struct intel_crtc_config *pipe_config,
+                                    int dotclock)
+{
+       /*
+        * FDI already provided one idea for the dotclock.
+        * Yell if the encoder disagrees.
+        */
+       WARN(!intel_fuzzy_clock_check(pipe_config->adjusted_mode.crtc_clock, dotclock),
+            "FDI dotclock and encoder dotclock mismatch, fdi: %i, encoder: %i\n",
+            pipe_config->adjusted_mode.crtc_clock, dotclock);
+}
+
 static int __intel_set_mode(struct drm_crtc *crtc,
                            struct drm_display_mode *mode,
                            int x, int y, struct drm_framebuffer *fb)
@@ -8883,7 +9351,7 @@ static int __intel_set_mode(struct drm_crtc *crtc,
        unsigned disable_pipes, prepare_pipes, modeset_pipes;
        int ret = 0;
 
-       saved_mode = kmalloc(2 * sizeof(*saved_mode), GFP_KERNEL);
+       saved_mode = kcalloc(2, sizeof(*saved_mode), GFP_KERNEL);
        if (!saved_mode)
                return -ENOMEM;
        saved_hwmode = saved_mode + 1;
@@ -9422,7 +9890,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
        struct intel_crtc *intel_crtc;
        int i;
 
-       intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
+       intel_crtc = kzalloc(sizeof(*intel_crtc), GFP_KERNEL);
        if (intel_crtc == NULL)
                return;
 
@@ -9451,6 +9919,18 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
        drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
 }
 
+enum pipe intel_get_pipe_from_connector(struct intel_connector *connector)
+{
+       struct drm_encoder *encoder = connector->base.encoder;
+
+       WARN_ON(!mutex_is_locked(&connector->base.dev->mode_config.mutex));
+
+       if (!encoder)
+               return INVALID_PIPE;
+
+       return to_intel_crtc(encoder->crtc)->pipe;
+}
+
 int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
                                struct drm_file *file)
 {
@@ -9466,7 +9946,7 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
 
        if (!drmmode_obj) {
                DRM_ERROR("no such CRTC id\n");
-               return -EINVAL;
+               return -ENOENT;
        }
 
        crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
@@ -9573,7 +10053,13 @@ static void intel_setup_outputs(struct drm_device *dev)
                if (I915_READ(PCH_DP_D) & DP_DETECTED)
                        intel_dp_init(dev, PCH_DP_D, PORT_D);
        } else if (IS_VALLEYVIEW(dev)) {
-               /* Check for built-in panel first. Shares lanes with HDMI on SDVOC */
+               if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED) {
+                       intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIB,
+                                       PORT_B);
+                       if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED)
+                               intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B);
+               }
+
                if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIC) & SDVO_DETECTED) {
                        intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIC,
                                        PORT_C);
@@ -9582,12 +10068,7 @@ static void intel_setup_outputs(struct drm_device *dev)
                                              PORT_C);
                }
 
-               if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED) {
-                       intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIB,
-                                       PORT_B);
-                       if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED)
-                               intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B);
-               }
+               intel_dsi_init(dev);
        } else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) {
                bool found = false;
 
@@ -9643,6 +10124,7 @@ static void intel_setup_outputs(struct drm_device *dev)
 void intel_framebuffer_fini(struct intel_framebuffer *fb)
 {
        drm_framebuffer_cleanup(&fb->base);
+       WARN_ON(!fb->obj->framebuffer_references--);
        drm_gem_object_unreference_unlocked(&fb->obj->base);
 }
 
@@ -9674,9 +10156,12 @@ int intel_framebuffer_init(struct drm_device *dev,
                           struct drm_mode_fb_cmd2 *mode_cmd,
                           struct drm_i915_gem_object *obj)
 {
+       int aligned_height, tile_height;
        int pitch_limit;
        int ret;
 
+       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
        if (obj->tiling_mode == I915_TILING_Y) {
                DRM_DEBUG("hardware does not support tiling Y\n");
                return -EINVAL;
@@ -9765,8 +10250,16 @@ int intel_framebuffer_init(struct drm_device *dev,
        if (mode_cmd->offsets[0] != 0)
                return -EINVAL;
 
+       tile_height = IS_GEN2(dev) ? 16 : 8;
+       aligned_height = ALIGN(mode_cmd->height,
+                              obj->tiling_mode ? tile_height : 1);
+       /* FIXME drm helper for size checks (especially planar formats)? */
+       if (obj->base.size < aligned_height * mode_cmd->pitches[0])
+               return -EINVAL;
+
        drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
        intel_fb->obj = obj;
+       intel_fb->obj->framebuffer_references++;
 
        ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
        if (ret) {
@@ -9792,9 +10285,15 @@ intel_user_framebuffer_create(struct drm_device *dev,
        return intel_framebuffer_create(dev, mode_cmd, obj);
 }
 
+#ifndef CONFIG_DRM_I915_FBDEV
+static inline void intel_fbdev_output_poll_changed(struct drm_device *dev)
+{
+}
+#endif
+
 static const struct drm_mode_config_funcs intel_mode_funcs = {
        .fb_create = intel_user_framebuffer_create,
-       .output_poll_changed = intel_fb_output_poll_changed,
+       .output_poll_changed = intel_fbdev_output_poll_changed,
 };
 
 /* Set up chip specific display functions */
@@ -9820,7 +10319,6 @@ static void intel_init_display(struct drm_device *dev)
                dev_priv->display.update_plane = ironlake_update_plane;
        } else if (HAS_PCH_SPLIT(dev)) {
                dev_priv->display.get_pipe_config = ironlake_get_pipe_config;
-               dev_priv->display.get_clock = ironlake_crtc_clock_get;
                dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set;
                dev_priv->display.crtc_enable = ironlake_crtc_enable;
                dev_priv->display.crtc_disable = ironlake_crtc_disable;
@@ -9828,7 +10326,6 @@ static void intel_init_display(struct drm_device *dev)
                dev_priv->display.update_plane = ironlake_update_plane;
        } else if (IS_VALLEYVIEW(dev)) {
                dev_priv->display.get_pipe_config = i9xx_get_pipe_config;
-               dev_priv->display.get_clock = i9xx_crtc_clock_get;
                dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
                dev_priv->display.crtc_enable = valleyview_crtc_enable;
                dev_priv->display.crtc_disable = i9xx_crtc_disable;
@@ -9836,7 +10333,6 @@ static void intel_init_display(struct drm_device *dev)
                dev_priv->display.update_plane = i9xx_update_plane;
        } else {
                dev_priv->display.get_pipe_config = i9xx_get_pipe_config;
-               dev_priv->display.get_clock = i9xx_crtc_clock_get;
                dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
                dev_priv->display.crtc_enable = i9xx_crtc_enable;
                dev_priv->display.crtc_disable = i9xx_crtc_disable;
@@ -9886,7 +10382,7 @@ static void intel_init_display(struct drm_device *dev)
                        dev_priv->display.write_eld = ironlake_write_eld;
                        dev_priv->display.modeset_global_resources =
                                ivb_modeset_global_resources;
-               } else if (IS_HASWELL(dev)) {
+               } else if (IS_HASWELL(dev) || IS_GEN8(dev)) {
                        dev_priv->display.fdi_link_train = hsw_fdi_link_train;
                        dev_priv->display.write_eld = haswell_write_eld;
                        dev_priv->display.modeset_global_resources =
@@ -9894,7 +10390,8 @@ static void intel_init_display(struct drm_device *dev)
                }
        } else if (IS_G4X(dev)) {
                dev_priv->display.write_eld = g4x_write_eld;
-       }
+       } else if (IS_VALLEYVIEW(dev))
+               dev_priv->display.write_eld = ironlake_write_eld;
 
        /* Default just returns -ENODEV to indicate unsupported */
        dev_priv->display.queue_flip = intel_default_queue_flip;
@@ -9917,6 +10414,7 @@ static void intel_init_display(struct drm_device *dev)
                dev_priv->display.queue_flip = intel_gen6_queue_flip;
                break;
        case 7:
+       case 8: /* FIXME(BDW): Check that the gen8 RCS flip works. */
                dev_priv->display.queue_flip = intel_gen7_queue_flip;
                break;
        }
@@ -10012,8 +10510,7 @@ static struct intel_quirk intel_quirks[] = {
        /* ThinkPad T60 needs pipe A force quirk (bug #16494) */
        { 0x2782, 0x17aa, 0x201a, quirk_pipea_force },
 
-       /* 830/845 need to leave pipe A & dpll A up */
-       { 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
+       /* 830 needs to leave pipe A & dpll A up */
        { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
 
        /* Lenovo U160 cannot use SSC on LVDS */
@@ -10022,20 +10519,11 @@ static struct intel_quirk intel_quirks[] = {
        /* Sony Vaio Y cannot use SSC on LVDS */
        { 0x0046, 0x104d, 0x9076, quirk_ssc_force_disable },
 
-       /* Acer Aspire 5734Z must invert backlight brightness */
-       { 0x2a42, 0x1025, 0x0459, quirk_invert_brightness },
-
-       /* Acer/eMachines G725 */
-       { 0x2a42, 0x1025, 0x0210, quirk_invert_brightness },
-
-       /* Acer/eMachines e725 */
-       { 0x2a42, 0x1025, 0x0212, quirk_invert_brightness },
-
-       /* Acer/Packard Bell NCL20 */
-       { 0x2a42, 0x1025, 0x034b, quirk_invert_brightness },
-
-       /* Acer Aspire 4736Z */
-       { 0x2a42, 0x1025, 0x0260, quirk_invert_brightness },
+       /*
+        * All GM45 Acer (and its brands eMachines and Packard Bell) laptops
+        * seem to use inverted backlight PWM.
+        */
+       { 0x2a42, 0x1025, PCI_ANY_ID, quirk_invert_brightness },
 
        /* Dell XPS13 HD Sandy Bridge */
        { 0x0116, 0x1028, 0x052e, quirk_no_pcm_pwm_enable },
@@ -10084,12 +10572,19 @@ static void i915_disable_vga(struct drm_device *dev)
 
 void intel_modeset_init_hw(struct drm_device *dev)
 {
-       intel_init_power_well(dev);
+       struct drm_i915_private *dev_priv = dev->dev_private;
 
        intel_prepare_ddi(dev);
 
        intel_init_clock_gating(dev);
 
+       /* Enable the CRI clock source so we can get at the display */
+       if (IS_VALLEYVIEW(dev))
+               I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) |
+                          DPLL_INTEGRATED_CRI_CLK_VLV);
+
+       intel_init_dpio(dev);
+
        mutex_lock(&dev->struct_mutex);
        intel_enable_gt_powersave(dev);
        mutex_unlock(&dev->struct_mutex);
@@ -10357,7 +10852,7 @@ void i915_redisable_vga(struct drm_device *dev)
            (I915_READ(HSW_PWR_WELL_DRIVER) & HSW_PWR_WELL_STATE_ENABLED) == 0)
                return;
 
-       if (I915_READ(vga_reg) != VGA_DISP_DISABLE) {
+       if (!(I915_READ(vga_reg) & VGA_DISP_DISABLE)) {
                DRM_DEBUG_KMS("Something enabled VGA plane, disabling it\n");
                i915_disable_vga(dev);
        }
@@ -10380,6 +10875,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
                                                                 &crtc->config);
 
                crtc->base.enabled = crtc->active;
+               crtc->primary_enabled = crtc->active;
 
                DRM_DEBUG_KMS("[CRTC:%d] hw state readout: %s\n",
                              crtc->base.base.id,
@@ -10420,20 +10916,11 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
                }
 
                encoder->connectors_active = false;
-               DRM_DEBUG_KMS("[ENCODER:%d:%s] hw state readout: %s, pipe=%i\n",
+               DRM_DEBUG_KMS("[ENCODER:%d:%s] hw state readout: %s, pipe %c\n",
                              encoder->base.base.id,
                              drm_get_encoder_name(&encoder->base),
                              encoder->base.crtc ? "enabled" : "disabled",
-                             pipe);
-       }
-
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-                           base.head) {
-               if (!crtc->active)
-                       continue;
-               if (dev_priv->display.get_clock)
-                       dev_priv->display.get_clock(crtc,
-                                                   &crtc->config);
+                             pipe_name(pipe));
        }
 
        list_for_each_entry(connector, &dev->mode_config.connector_list,
@@ -10460,7 +10947,6 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        enum pipe pipe;
-       struct drm_plane *plane;
        struct intel_crtc *crtc;
        struct intel_encoder *encoder;
        int i;
@@ -10507,7 +10993,12 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
                pll->on = false;
        }
 
+       if (IS_HASWELL(dev))
+               ilk_wm_get_hw_state(dev);
+
        if (force_restore) {
+               i915_redisable_vga(dev);
+
                /*
                 * We need to use raw interfaces for restoring state to avoid
                 * checking (bogus) intermediate states.
@@ -10519,10 +11010,6 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
                        __intel_set_mode(crtc, &crtc->mode, crtc->x, crtc->y,
                                         crtc->fb);
                }
-               list_for_each_entry(plane, &dev->mode_config.plane_list, head)
-                       intel_plane_restore(plane);
-
-               i915_redisable_vga(dev);
        } else {
                intel_modeset_update_staged_output_state(dev);
        }
@@ -10545,6 +11032,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_crtc *crtc;
+       struct drm_connector *connector;
 
        /*
         * Interrupts and polling as the first thing to avoid creating havoc.
@@ -10585,6 +11073,10 @@ void intel_modeset_cleanup(struct drm_device *dev)
        /* destroy backlight, if any, before the connectors */
        intel_panel_destroy_backlight(dev);
 
+       /* destroy the sysfs files before encoders/connectors */
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+               drm_sysfs_connector_remove(connector);
+
        drm_mode_config_cleanup(dev);
 
        intel_cleanup_overlay(dev);
@@ -10680,7 +11172,7 @@ intel_display_capture_error_state(struct drm_device *dev)
        if (INTEL_INFO(dev)->num_pipes == 0)
                return NULL;
 
-       error = kmalloc(sizeof(*error), GFP_ATOMIC);
+       error = kzalloc(sizeof(*error), GFP_ATOMIC);
        if (error == NULL)
                return NULL;
 
@@ -10688,6 +11180,9 @@ intel_display_capture_error_state(struct drm_device *dev)
                error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER);
 
        for_each_pipe(i) {
+               if (!intel_display_power_enabled(dev, POWER_DOMAIN_PIPE(i)))
+                       continue;
+
                if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) {
                        error->cursor[i].control = I915_READ(CURCNTR(i));
                        error->cursor[i].position = I915_READ(CURPOS(i));
@@ -10721,6 +11216,10 @@ intel_display_capture_error_state(struct drm_device *dev)
        for (i = 0; i < error->num_transcoders; i++) {
                enum transcoder cpu_transcoder = transcoders[i];
 
+               if (!intel_display_power_enabled(dev,
+                               POWER_DOMAIN_TRANSCODER(cpu_transcoder)))
+                       continue;
+
                error->transcoder[i].cpu_transcoder = cpu_transcoder;
 
                error->transcoder[i].conf = I915_READ(PIPECONF(cpu_transcoder));
@@ -10732,12 +11231,6 @@ intel_display_capture_error_state(struct drm_device *dev)
                error->transcoder[i].vsync = I915_READ(VSYNC(cpu_transcoder));
        }
 
-       /* In the code above we read the registers without checking if the power
-        * well was on, so here we have to clear the FPGA_DBG_RM_NOCLAIM bit to
-        * prevent the next I915_WRITE from detecting it and printing an error
-        * message. */
-       intel_uncore_clear_errors(dev);
-
        return error;
 }
 
@@ -10782,7 +11275,7 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
        }
 
        for (i = 0; i < error->num_transcoders; i++) {
-               err_printf(m, "  CPU transcoder: %c\n",
+               err_printf(m, "CPU transcoder: %c\n",
                           transcoder_name(error->transcoder[i].cpu_transcoder));
                err_printf(m, "  CONF: %08x\n", error->transcoder[i].conf);
                err_printf(m, "  HTOTAL: %08x\n", error->transcoder[i].htotal);
index 1a43137..eb8139d 100644 (file)
 
 #define DP_LINK_CHECK_TIMEOUT  (10 * 1000)
 
+struct dp_link_dpll {
+       int link_bw;
+       struct dpll dpll;
+};
+
+static const struct dp_link_dpll gen4_dpll[] = {
+       { DP_LINK_BW_1_62,
+               { .p1 = 2, .p2 = 10, .n = 2, .m1 = 23, .m2 = 8 } },
+       { DP_LINK_BW_2_7,
+               { .p1 = 1, .p2 = 10, .n = 1, .m1 = 14, .m2 = 2 } }
+};
+
+static const struct dp_link_dpll pch_dpll[] = {
+       { DP_LINK_BW_1_62,
+               { .p1 = 2, .p2 = 10, .n = 1, .m1 = 12, .m2 = 9 } },
+       { DP_LINK_BW_2_7,
+               { .p1 = 1, .p2 = 10, .n = 2, .m1 = 14, .m2 = 8 } }
+};
+
+static const struct dp_link_dpll vlv_dpll[] = {
+       { DP_LINK_BW_1_62,
+               { .p1 = 3, .p2 = 2, .n = 5, .m1 = 3, .m2 = 81 } },
+       { DP_LINK_BW_2_7,
+               { .p1 = 2, .p2 = 2, .n = 1, .m1 = 2, .m2 = 27 } }
+};
+
 /**
  * is_edp - is the given port attached to an eDP panel (either CPU or PCH)
  * @intel_dp: DP struct
@@ -211,24 +237,77 @@ intel_hrawclk(struct drm_device *dev)
        }
 }
 
+static void
+intel_dp_init_panel_power_sequencer(struct drm_device *dev,
+                                   struct intel_dp *intel_dp,
+                                   struct edp_power_seq *out);
+static void
+intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
+                                             struct intel_dp *intel_dp,
+                                             struct edp_power_seq *out);
+
+static enum pipe
+vlv_power_sequencer_pipe(struct intel_dp *intel_dp)
+{
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct drm_crtc *crtc = intel_dig_port->base.base.crtc;
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum port port = intel_dig_port->port;
+       enum pipe pipe;
+
+       /* modeset should have pipe */
+       if (crtc)
+               return to_intel_crtc(crtc)->pipe;
+
+       /* init time, try to find a pipe with this port selected */
+       for (pipe = PIPE_A; pipe <= PIPE_B; pipe++) {
+               u32 port_sel = I915_READ(VLV_PIPE_PP_ON_DELAYS(pipe)) &
+                       PANEL_PORT_SELECT_MASK;
+               if (port_sel == PANEL_PORT_SELECT_DPB_VLV && port == PORT_B)
+                       return pipe;
+               if (port_sel == PANEL_PORT_SELECT_DPC_VLV && port == PORT_C)
+                       return pipe;
+       }
+
+       /* shrug */
+       return PIPE_A;
+}
+
+static u32 _pp_ctrl_reg(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp_to_dev(intel_dp);
+
+       if (HAS_PCH_SPLIT(dev))
+               return PCH_PP_CONTROL;
+       else
+               return VLV_PIPE_PP_CONTROL(vlv_power_sequencer_pipe(intel_dp));
+}
+
+static u32 _pp_stat_reg(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp_to_dev(intel_dp);
+
+       if (HAS_PCH_SPLIT(dev))
+               return PCH_PP_STATUS;
+       else
+               return VLV_PIPE_PP_STATUS(vlv_power_sequencer_pipe(intel_dp));
+}
+
 static bool ironlake_edp_have_panel_power(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 pp_stat_reg;
 
-       pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS;
-       return (I915_READ(pp_stat_reg) & PP_ON) != 0;
+       return (I915_READ(_pp_stat_reg(intel_dp)) & PP_ON) != 0;
 }
 
 static bool ironlake_edp_have_panel_vdd(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 pp_ctrl_reg;
 
-       pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL;
-       return (I915_READ(pp_ctrl_reg) & EDP_FORCE_VDD) != 0;
+       return (I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD) != 0;
 }
 
 static void
@@ -236,19 +315,15 @@ intel_dp_check_edp(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 pp_stat_reg, pp_ctrl_reg;
 
        if (!is_edp(intel_dp))
                return;
 
-       pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS;
-       pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL;
-
        if (!ironlake_edp_have_panel_power(intel_dp) && !ironlake_edp_have_panel_vdd(intel_dp)) {
                WARN(1, "eDP powered off while attempting aux channel communication.\n");
                DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n",
-                               I915_READ(pp_stat_reg),
-                               I915_READ(pp_ctrl_reg));
+                             I915_READ(_pp_stat_reg(intel_dp)),
+                             I915_READ(_pp_ctrl_reg(intel_dp)));
        }
 }
 
@@ -330,6 +405,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
        uint32_t status;
        int try, precharge, clock = 0;
        bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev);
+       uint32_t timeout;
 
        /* dp aux is extremely sensitive to irq latency, hence request the
         * lowest possible wakeup latency and so prevent the cpu from going into
@@ -344,6 +420,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
        else
                precharge = 5;
 
+       if (IS_BROADWELL(dev) && ch_ctl == DPA_AUX_CH_CTL)
+               timeout = DP_AUX_CH_CTL_TIME_OUT_600us;
+       else
+               timeout = DP_AUX_CH_CTL_TIME_OUT_400us;
+
        intel_aux_display_runtime_get(dev_priv);
 
        /* Try to wait for any previous AUX channel activity */
@@ -361,6 +442,12 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
                goto out;
        }
 
+       /* Only 5 data registers! */
+       if (WARN_ON(send_bytes > 20 || recv_size > 20)) {
+               ret = -E2BIG;
+               goto out;
+       }
+
        while ((aux_clock_divider = get_aux_clock_divider(intel_dp, clock++))) {
                /* Must try at least 3 times according to DP spec */
                for (try = 0; try < 5; try++) {
@@ -373,7 +460,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
                        I915_WRITE(ch_ctl,
                                   DP_AUX_CH_CTL_SEND_BUSY |
                                   (has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) |
-                                  DP_AUX_CH_CTL_TIME_OUT_400us |
+                                  timeout |
                                   (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
                                   (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
                                   (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
@@ -451,9 +538,10 @@ intel_dp_aux_native_write(struct intel_dp *intel_dp,
        int msg_bytes;
        uint8_t ack;
 
+       if (WARN_ON(send_bytes > 16))
+               return -E2BIG;
+
        intel_dp_check_edp(intel_dp);
-       if (send_bytes > 16)
-               return -1;
        msg[0] = AUX_NATIVE_WRITE << 4;
        msg[1] = address >> 8;
        msg[2] = address & 0xff;
@@ -494,6 +582,9 @@ intel_dp_aux_native_read(struct intel_dp *intel_dp,
        uint8_t ack;
        int ret;
 
+       if (WARN_ON(recv_bytes > 19))
+               return -E2BIG;
+
        intel_dp_check_edp(intel_dp);
        msg[0] = AUX_NATIVE_READ << 4;
        msg[1] = address >> 8;
@@ -538,6 +629,7 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
        int reply_bytes;
        int ret;
 
+       ironlake_edp_panel_vdd_on(intel_dp);
        intel_dp_check_edp(intel_dp);
        /* Set up the command byte */
        if (mode & MODE_I2C_READ)
@@ -569,13 +661,18 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
                break;
        }
 
-       for (retry = 0; retry < 5; retry++) {
+       /*
+        * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device is
+        * required to retry at least seven times upon receiving AUX_DEFER
+        * before giving up the AUX transaction.
+        */
+       for (retry = 0; retry < 7; retry++) {
                ret = intel_dp_aux_ch(intel_dp,
                                      msg, msg_bytes,
                                      reply, reply_bytes);
                if (ret < 0) {
                        DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
-                       return ret;
+                       goto out;
                }
 
                switch (reply[0] & AUX_NATIVE_REPLY_MASK) {
@@ -586,7 +683,8 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
                        break;
                case AUX_NATIVE_REPLY_NACK:
                        DRM_DEBUG_KMS("aux_ch native nack\n");
-                       return -EREMOTEIO;
+                       ret = -EREMOTEIO;
+                       goto out;
                case AUX_NATIVE_REPLY_DEFER:
                        /*
                         * For now, just give more slack to branch devices. We
@@ -604,7 +702,8 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
                default:
                        DRM_ERROR("aux_ch invalid native reply 0x%02x\n",
                                  reply[0]);
-                       return -EREMOTEIO;
+                       ret = -EREMOTEIO;
+                       goto out;
                }
 
                switch (reply[0] & AUX_I2C_REPLY_MASK) {
@@ -612,22 +711,29 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
                        if (mode == MODE_I2C_READ) {
                                *read_byte = reply[1];
                        }
-                       return reply_bytes - 1;
+                       ret = reply_bytes - 1;
+                       goto out;
                case AUX_I2C_REPLY_NACK:
                        DRM_DEBUG_KMS("aux_i2c nack\n");
-                       return -EREMOTEIO;
+                       ret = -EREMOTEIO;
+                       goto out;
                case AUX_I2C_REPLY_DEFER:
                        DRM_DEBUG_KMS("aux_i2c defer\n");
                        udelay(100);
                        break;
                default:
                        DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]);
-                       return -EREMOTEIO;
+                       ret = -EREMOTEIO;
+                       goto out;
                }
        }
 
        DRM_ERROR("too many retries, giving up\n");
-       return -EREMOTEIO;
+       ret = -EREMOTEIO;
+
+out:
+       ironlake_edp_panel_vdd_off(intel_dp, false);
+       return ret;
 }
 
 static int
@@ -647,11 +753,9 @@ intel_dp_i2c_init(struct intel_dp *intel_dp,
        strncpy(intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
        intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0';
        intel_dp->adapter.algo_data = &intel_dp->algo;
-       intel_dp->adapter.dev.parent = &intel_connector->base.kdev;
+       intel_dp->adapter.dev.parent = intel_connector->base.kdev;
 
-       ironlake_edp_panel_vdd_on(intel_dp);
        ret = i2c_dp_aux_add_bus(&intel_dp->adapter);
-       ironlake_edp_panel_vdd_off(intel_dp, false);
        return ret;
 }
 
@@ -660,41 +764,30 @@ intel_dp_set_clock(struct intel_encoder *encoder,
                   struct intel_crtc_config *pipe_config, int link_bw)
 {
        struct drm_device *dev = encoder->base.dev;
+       const struct dp_link_dpll *divisor = NULL;
+       int i, count = 0;
 
        if (IS_G4X(dev)) {
-               if (link_bw == DP_LINK_BW_1_62) {
-                       pipe_config->dpll.p1 = 2;
-                       pipe_config->dpll.p2 = 10;
-                       pipe_config->dpll.n = 2;
-                       pipe_config->dpll.m1 = 23;
-                       pipe_config->dpll.m2 = 8;
-               } else {
-                       pipe_config->dpll.p1 = 1;
-                       pipe_config->dpll.p2 = 10;
-                       pipe_config->dpll.n = 1;
-                       pipe_config->dpll.m1 = 14;
-                       pipe_config->dpll.m2 = 2;
-               }
-               pipe_config->clock_set = true;
+               divisor = gen4_dpll;
+               count = ARRAY_SIZE(gen4_dpll);
        } else if (IS_HASWELL(dev)) {
                /* Haswell has special-purpose DP DDI clocks. */
        } else if (HAS_PCH_SPLIT(dev)) {
-               if (link_bw == DP_LINK_BW_1_62) {
-                       pipe_config->dpll.n = 1;
-                       pipe_config->dpll.p1 = 2;
-                       pipe_config->dpll.p2 = 10;
-                       pipe_config->dpll.m1 = 12;
-                       pipe_config->dpll.m2 = 9;
-               } else {
-                       pipe_config->dpll.n = 2;
-                       pipe_config->dpll.p1 = 1;
-                       pipe_config->dpll.p2 = 10;
-                       pipe_config->dpll.m1 = 14;
-                       pipe_config->dpll.m2 = 8;
-               }
-               pipe_config->clock_set = true;
+               divisor = pch_dpll;
+               count = ARRAY_SIZE(pch_dpll);
        } else if (IS_VALLEYVIEW(dev)) {
-               /* FIXME: Need to figure out optimized DP clocks for vlv. */
+               divisor = vlv_dpll;
+               count = ARRAY_SIZE(vlv_dpll);
+       }
+
+       if (divisor && count) {
+               for (i = 0; i < count; i++) {
+                       if (link_bw == divisor[i].link_bw) {
+                               pipe_config->dpll = divisor[i].dpll;
+                               pipe_config->clock_set = true;
+                               break;
+                       }
+               }
        }
 }
 
@@ -737,19 +830,22 @@ intel_dp_compute_config(struct intel_encoder *encoder,
 
        DRM_DEBUG_KMS("DP link computation with max lane count %i "
                      "max bw %02x pixel clock %iKHz\n",
-                     max_lane_count, bws[max_clock], adjusted_mode->clock);
+                     max_lane_count, bws[max_clock],
+                     adjusted_mode->crtc_clock);
 
        /* Walk through all bpp values. Luckily they're all nicely spaced with 2
         * bpc in between. */
        bpp = pipe_config->pipe_bpp;
-       if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp) {
+       if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp &&
+           dev_priv->vbt.edp_bpp < bpp) {
                DRM_DEBUG_KMS("clamping bpp for eDP panel to BIOS-provided %i\n",
                              dev_priv->vbt.edp_bpp);
-               bpp = min_t(int, bpp, dev_priv->vbt.edp_bpp);
+               bpp = dev_priv->vbt.edp_bpp;
        }
 
        for (; bpp >= 6*3; bpp -= 2*3) {
-               mode_rate = intel_dp_link_required(adjusted_mode->clock, bpp);
+               mode_rate = intel_dp_link_required(adjusted_mode->crtc_clock,
+                                                  bpp);
 
                for (clock = 0; clock <= max_clock; clock++) {
                        for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
@@ -794,7 +890,8 @@ found:
                      mode_rate, link_avail);
 
        intel_link_compute_m_n(bpp, lane_count,
-                              adjusted_mode->clock, pipe_config->port_clock,
+                              adjusted_mode->crtc_clock,
+                              pipe_config->port_clock,
                               &pipe_config->dp_m_n);
 
        intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw);
@@ -802,21 +899,6 @@ found:
        return true;
 }
 
-void intel_dp_init_link_config(struct intel_dp *intel_dp)
-{
-       memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
-       intel_dp->link_configuration[0] = intel_dp->link_bw;
-       intel_dp->link_configuration[1] = intel_dp->lane_count;
-       intel_dp->link_configuration[8] = DP_SET_ANSI_8B10B;
-       /*
-        * Check for DPCD version > 1.1 and enhanced framing support
-        */
-       if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
-           (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) {
-               intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
-       }
-}
-
 static void ironlake_set_pll_cpu_edp(struct intel_dp *intel_dp)
 {
        struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
@@ -889,8 +971,6 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
                intel_write_eld(&encoder->base, adjusted_mode);
        }
 
-       intel_dp_init_link_config(intel_dp);
-
        /* Split out the IBX/CPU vs CPT settings */
 
        if (port == PORT_A && IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) {
@@ -900,7 +980,7 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
                        intel_dp->DP |= DP_SYNC_VS_HIGH;
                intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
 
-               if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN)
+               if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
                        intel_dp->DP |= DP_ENHANCED_FRAMING;
 
                intel_dp->DP |= crtc->pipe << 29;
@@ -914,7 +994,7 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
                        intel_dp->DP |= DP_SYNC_VS_HIGH;
                intel_dp->DP |= DP_LINK_TRAIN_OFF;
 
-               if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN)
+               if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
                        intel_dp->DP |= DP_ENHANCED_FRAMING;
 
                if (crtc->pipe == 1)
@@ -944,8 +1024,8 @@ static void ironlake_wait_panel_status(struct intel_dp *intel_dp,
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 pp_stat_reg, pp_ctrl_reg;
 
-       pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS;
-       pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL;
+       pp_stat_reg = _pp_stat_reg(intel_dp);
+       pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
 
        DRM_DEBUG_KMS("mask %08x value %08x status %08x control %08x\n",
                        mask, value,
@@ -987,11 +1067,8 @@ static  u32 ironlake_get_pp_control(struct intel_dp *intel_dp)
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 control;
-       u32 pp_ctrl_reg;
-
-       pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL;
-       control = I915_READ(pp_ctrl_reg);
 
+       control = I915_READ(_pp_ctrl_reg(intel_dp));
        control &= ~PANEL_UNLOCK_MASK;
        control |= PANEL_UNLOCK_REGS;
        return control;
@@ -1006,17 +1083,16 @@ void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp)
 
        if (!is_edp(intel_dp))
                return;
-       DRM_DEBUG_KMS("Turn eDP VDD on\n");
 
        WARN(intel_dp->want_panel_vdd,
             "eDP VDD already requested on\n");
 
        intel_dp->want_panel_vdd = true;
 
-       if (ironlake_edp_have_panel_vdd(intel_dp)) {
-               DRM_DEBUG_KMS("eDP VDD already on\n");
+       if (ironlake_edp_have_panel_vdd(intel_dp))
                return;
-       }
+
+       DRM_DEBUG_KMS("Turning eDP VDD on\n");
 
        if (!ironlake_edp_have_panel_power(intel_dp))
                ironlake_wait_panel_power_cycle(intel_dp);
@@ -1024,8 +1100,8 @@ void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp)
        pp = ironlake_get_pp_control(intel_dp);
        pp |= EDP_FORCE_VDD;
 
-       pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS;
-       pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL;
+       pp_stat_reg = _pp_stat_reg(intel_dp);
+       pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
 
        I915_WRITE(pp_ctrl_reg, pp);
        POSTING_READ(pp_ctrl_reg);
@@ -1050,11 +1126,13 @@ static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp)
        WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 
        if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) {
+               DRM_DEBUG_KMS("Turning eDP VDD off\n");
+
                pp = ironlake_get_pp_control(intel_dp);
                pp &= ~EDP_FORCE_VDD;
 
-               pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS;
-               pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL;
+               pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
+               pp_stat_reg = _pp_stat_reg(intel_dp);
 
                I915_WRITE(pp_ctrl_reg, pp);
                POSTING_READ(pp_ctrl_reg);
@@ -1082,7 +1160,6 @@ void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
        if (!is_edp(intel_dp))
                return;
 
-       DRM_DEBUG_KMS("Turn eDP VDD off %d\n", intel_dp->want_panel_vdd);
        WARN(!intel_dp->want_panel_vdd, "eDP VDD not forced on");
 
        intel_dp->want_panel_vdd = false;
@@ -1119,20 +1196,19 @@ void ironlake_edp_panel_on(struct intel_dp *intel_dp)
 
        ironlake_wait_panel_power_cycle(intel_dp);
 
+       pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
        pp = ironlake_get_pp_control(intel_dp);
        if (IS_GEN5(dev)) {
                /* ILK workaround: disable reset around power sequence */
                pp &= ~PANEL_POWER_RESET;
-               I915_WRITE(PCH_PP_CONTROL, pp);
-               POSTING_READ(PCH_PP_CONTROL);
+               I915_WRITE(pp_ctrl_reg, pp);
+               POSTING_READ(pp_ctrl_reg);
        }
 
        pp |= POWER_TARGET_ON;
        if (!IS_GEN5(dev))
                pp |= PANEL_POWER_RESET;
 
-       pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL;
-
        I915_WRITE(pp_ctrl_reg, pp);
        POSTING_READ(pp_ctrl_reg);
 
@@ -1140,8 +1216,8 @@ void ironlake_edp_panel_on(struct intel_dp *intel_dp)
 
        if (IS_GEN5(dev)) {
                pp |= PANEL_POWER_RESET; /* restore panel reset bit */
-               I915_WRITE(PCH_PP_CONTROL, pp);
-               POSTING_READ(PCH_PP_CONTROL);
+               I915_WRITE(pp_ctrl_reg, pp);
+               POSTING_READ(pp_ctrl_reg);
        }
 }
 
@@ -1164,7 +1240,7 @@ void ironlake_edp_panel_off(struct intel_dp *intel_dp)
         * panels get very unhappy and cease to work. */
        pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD | PANEL_POWER_RESET | EDP_BLC_ENABLE);
 
-       pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL;
+       pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
 
        I915_WRITE(pp_ctrl_reg, pp);
        POSTING_READ(pp_ctrl_reg);
@@ -1179,7 +1255,6 @@ void ironlake_edp_backlight_on(struct intel_dp *intel_dp)
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        struct drm_device *dev = intel_dig_port->base.base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int pipe = to_intel_crtc(intel_dig_port->base.base.crtc)->pipe;
        u32 pp;
        u32 pp_ctrl_reg;
 
@@ -1197,12 +1272,12 @@ void ironlake_edp_backlight_on(struct intel_dp *intel_dp)
        pp = ironlake_get_pp_control(intel_dp);
        pp |= EDP_BLC_ENABLE;
 
-       pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL;
+       pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
 
        I915_WRITE(pp_ctrl_reg, pp);
        POSTING_READ(pp_ctrl_reg);
 
-       intel_panel_enable_backlight(dev, pipe);
+       intel_panel_enable_backlight(intel_dp->attached_connector);
 }
 
 void ironlake_edp_backlight_off(struct intel_dp *intel_dp)
@@ -1215,13 +1290,13 @@ void ironlake_edp_backlight_off(struct intel_dp *intel_dp)
        if (!is_edp(intel_dp))
                return;
 
-       intel_panel_disable_backlight(dev);
+       intel_panel_disable_backlight(intel_dp->attached_connector);
 
        DRM_DEBUG_KMS("\n");
        pp = ironlake_get_pp_control(intel_dp);
        pp &= ~EDP_BLC_ENABLE;
 
-       pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL;
+       pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
 
        I915_WRITE(pp_ctrl_reg, pp);
        POSTING_READ(pp_ctrl_reg);
@@ -1368,6 +1443,7 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
        struct drm_i915_private *dev_priv = dev->dev_private;
        enum port port = dp_to_dig_port(intel_dp)->port;
        struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
+       int dotclock;
 
        if ((port == PORT_A) || !HAS_PCH_CPT(dev)) {
                tmp = I915_READ(intel_dp->output_reg);
@@ -1395,13 +1471,25 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
 
        pipe_config->adjusted_mode.flags |= flags;
 
-       if (dp_to_dig_port(intel_dp)->port == PORT_A) {
+       pipe_config->has_dp_encoder = true;
+
+       intel_dp_get_m_n(crtc, pipe_config);
+
+       if (port == PORT_A) {
                if ((I915_READ(DP_A) & DP_PLL_FREQ_MASK) == DP_PLL_FREQ_160MHZ)
                        pipe_config->port_clock = 162000;
                else
                        pipe_config->port_clock = 270000;
        }
 
+       dotclock = intel_dotclock_calculate(pipe_config->port_clock,
+                                           &pipe_config->dp_m_n);
+
+       if (HAS_PCH_SPLIT(dev_priv->dev) && port != PORT_A)
+               ironlake_check_encoder_dotclock(pipe_config, dotclock);
+
+       pipe_config->adjusted_mode.crtc_clock = dotclock;
+
        if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp &&
            pipe_config->pipe_bpp > dev_priv->vbt.edp_bpp) {
                /*
@@ -1423,20 +1511,21 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
        }
 }
 
-static bool is_edp_psr(struct intel_dp *intel_dp)
+static bool is_edp_psr(struct drm_device *dev)
 {
-       return is_edp(intel_dp) &&
-               intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       return dev_priv->psr.sink_support;
 }
 
 static bool intel_edp_is_psr_enabled(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (!IS_HASWELL(dev))
+       if (!HAS_PSR(dev))
                return false;
 
-       return I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE;
+       return I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
 }
 
 static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
@@ -1486,7 +1575,7 @@ static void intel_edp_psr_setup(struct intel_dp *intel_dp)
        intel_edp_psr_write_vsc(intel_dp, &psr_vsc);
 
        /* Avoid continuous PSR exit by masking memup and hpd */
-       I915_WRITE(EDP_PSR_DEBUG_CTL, EDP_PSR_DEBUG_MASK_MEMUP |
+       I915_WRITE(EDP_PSR_DEBUG_CTL(dev), EDP_PSR_DEBUG_MASK_MEMUP |
                   EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP);
 
        intel_dp->psr_setup_done = true;
@@ -1511,9 +1600,9 @@ static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
                                            DP_PSR_MAIN_LINK_ACTIVE);
 
        /* Setup AUX registers */
-       I915_WRITE(EDP_PSR_AUX_DATA1, EDP_PSR_DPCD_COMMAND);
-       I915_WRITE(EDP_PSR_AUX_DATA2, EDP_PSR_DPCD_NORMAL_OPERATION);
-       I915_WRITE(EDP_PSR_AUX_CTL,
+       I915_WRITE(EDP_PSR_AUX_DATA1(dev), EDP_PSR_DPCD_COMMAND);
+       I915_WRITE(EDP_PSR_AUX_DATA2(dev), EDP_PSR_DPCD_NORMAL_OPERATION);
+       I915_WRITE(EDP_PSR_AUX_CTL(dev),
                   DP_AUX_CH_CTL_TIME_OUT_400us |
                   (msg_size << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
                   (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
@@ -1527,6 +1616,7 @@ static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
        uint32_t max_sleep_time = 0x1f;
        uint32_t idle_frames = 1;
        uint32_t val = 0x0;
+       const uint32_t link_entry_time = EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES;
 
        if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT) {
                val |= EDP_PSR_LINK_STANDBY;
@@ -1536,8 +1626,8 @@ static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
        } else
                val |= EDP_PSR_LINK_DISABLE;
 
-       I915_WRITE(EDP_PSR_CTL, val |
-                  EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES |
+       I915_WRITE(EDP_PSR_CTL(dev), val |
+                  IS_BROADWELL(dev) ? 0 : link_entry_time |
                   max_sleep_time << EDP_PSR_MAX_SLEEP_TIME_SHIFT |
                   idle_frames << EDP_PSR_IDLE_FRAME_SHIFT |
                   EDP_PSR_ENABLE);
@@ -1553,42 +1643,33 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
        struct drm_i915_gem_object *obj = to_intel_framebuffer(crtc->fb)->obj;
        struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base;
 
-       if (!IS_HASWELL(dev)) {
+       dev_priv->psr.source_ok = false;
+
+       if (!HAS_PSR(dev)) {
                DRM_DEBUG_KMS("PSR not supported on this platform\n");
-               dev_priv->no_psr_reason = PSR_NO_SOURCE;
                return false;
        }
 
        if ((intel_encoder->type != INTEL_OUTPUT_EDP) ||
            (dig_port->port != PORT_A)) {
                DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n");
-               dev_priv->no_psr_reason = PSR_HSW_NOT_DDIA;
-               return false;
-       }
-
-       if (!is_edp_psr(intel_dp)) {
-               DRM_DEBUG_KMS("PSR not supported by this panel\n");
-               dev_priv->no_psr_reason = PSR_NO_SINK;
                return false;
        }
 
        if (!i915_enable_psr) {
                DRM_DEBUG_KMS("PSR disable by flag\n");
-               dev_priv->no_psr_reason = PSR_MODULE_PARAM;
                return false;
        }
 
        crtc = dig_port->base.base.crtc;
        if (crtc == NULL) {
                DRM_DEBUG_KMS("crtc not active for PSR\n");
-               dev_priv->no_psr_reason = PSR_CRTC_NOT_ACTIVE;
                return false;
        }
 
        intel_crtc = to_intel_crtc(crtc);
-       if (!intel_crtc->active || !crtc->fb || !crtc->mode.clock) {
+       if (!intel_crtc_active(crtc)) {
                DRM_DEBUG_KMS("crtc not active for PSR\n");
-               dev_priv->no_psr_reason = PSR_CRTC_NOT_ACTIVE;
                return false;
        }
 
@@ -1596,29 +1677,26 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
        if (obj->tiling_mode != I915_TILING_X ||
            obj->fence_reg == I915_FENCE_REG_NONE) {
                DRM_DEBUG_KMS("PSR condition failed: fb not tiled or fenced\n");
-               dev_priv->no_psr_reason = PSR_NOT_TILED;
                return false;
        }
 
        if (I915_READ(SPRCTL(intel_crtc->pipe)) & SPRITE_ENABLE) {
                DRM_DEBUG_KMS("PSR condition failed: Sprite is Enabled\n");
-               dev_priv->no_psr_reason = PSR_SPRITE_ENABLED;
                return false;
        }
 
        if (I915_READ(HSW_STEREO_3D_CTL(intel_crtc->config.cpu_transcoder)) &
            S3D_ENABLE) {
                DRM_DEBUG_KMS("PSR condition failed: Stereo 3D is Enabled\n");
-               dev_priv->no_psr_reason = PSR_S3D_ENABLED;
                return false;
        }
 
-       if (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) {
+       if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) {
                DRM_DEBUG_KMS("PSR condition failed: Interlaced is Enabled\n");
-               dev_priv->no_psr_reason = PSR_INTERLACED_ENABLED;
                return false;
        }
 
+       dev_priv->psr.source_ok = true;
        return true;
 }
 
@@ -1657,10 +1735,11 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp)
        if (!intel_edp_is_psr_enabled(dev))
                return;
 
-       I915_WRITE(EDP_PSR_CTL, I915_READ(EDP_PSR_CTL) & ~EDP_PSR_ENABLE);
+       I915_WRITE(EDP_PSR_CTL(dev),
+                  I915_READ(EDP_PSR_CTL(dev)) & ~EDP_PSR_ENABLE);
 
        /* Wait till PSR is idle */
-       if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL) &
+       if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL(dev)) &
                       EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10))
                DRM_ERROR("Timed out waiting for PSR Idle State\n");
 }
@@ -1674,7 +1753,7 @@ void intel_edp_psr_update(struct drm_device *dev)
                if (encoder->type == INTEL_OUTPUT_EDP) {
                        intel_dp = enc_to_intel_dp(&encoder->base);
 
-                       if (!is_edp_psr(intel_dp))
+                       if (!is_edp_psr(dev))
                                return;
 
                        if (!intel_edp_psr_match_conditions(intel_dp))
@@ -1733,14 +1812,24 @@ static void intel_enable_dp(struct intel_encoder *encoder)
        ironlake_edp_panel_vdd_off(intel_dp, true);
        intel_dp_complete_link_train(intel_dp);
        intel_dp_stop_link_train(intel_dp);
+}
+
+static void g4x_enable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+
+       intel_enable_dp(encoder);
        ironlake_edp_backlight_on(intel_dp);
 }
 
 static void vlv_enable_dp(struct intel_encoder *encoder)
 {
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+
+       ironlake_edp_backlight_on(intel_dp);
 }
 
-static void intel_pre_enable_dp(struct intel_encoder *encoder)
+static void g4x_pre_enable_dp(struct intel_encoder *encoder)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
        struct intel_digital_port *dport = dp_to_dig_port(intel_dp);
@@ -1758,53 +1847,59 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder)
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
        int port = vlv_dport_to_channel(dport);
        int pipe = intel_crtc->pipe;
+       struct edp_power_seq power_seq;
        u32 val;
 
        mutex_lock(&dev_priv->dpio_lock);
 
-       val = vlv_dpio_read(dev_priv, DPIO_DATA_LANE_A(port));
+       val = vlv_dpio_read(dev_priv, pipe, DPIO_DATA_LANE_A(port));
        val = 0;
        if (pipe)
                val |= (1<<21);
        else
                val &= ~(1<<21);
        val |= 0x001000c4;
-       vlv_dpio_write(dev_priv, DPIO_DATA_CHANNEL(port), val);
-       vlv_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF0(port), 0x00760018);
-       vlv_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF8(port), 0x00400888);
+       vlv_dpio_write(dev_priv, pipe, DPIO_DATA_CHANNEL(port), val);
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_CLOCKBUF0(port), 0x00760018);
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_CLOCKBUF8(port), 0x00400888);
 
        mutex_unlock(&dev_priv->dpio_lock);
 
+       /* init power sequencer on this pipe and port */
+       intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
+       intel_dp_init_panel_power_sequencer_registers(dev, intel_dp,
+                                                     &power_seq);
+
        intel_enable_dp(encoder);
 
        vlv_wait_port_ready(dev_priv, port);
 }
 
-static void intel_dp_pre_pll_enable(struct intel_encoder *encoder)
+static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder)
 {
        struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc =
+               to_intel_crtc(encoder->base.crtc);
        int port = vlv_dport_to_channel(dport);
-
-       if (!IS_VALLEYVIEW(dev))
-               return;
+       int pipe = intel_crtc->pipe;
 
        /* Program Tx lane resets to default */
        mutex_lock(&dev_priv->dpio_lock);
-       vlv_dpio_write(dev_priv, DPIO_PCS_TX(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_TX(port),
                         DPIO_PCS_TX_LANE2_RESET |
                         DPIO_PCS_TX_LANE1_RESET);
-       vlv_dpio_write(dev_priv, DPIO_PCS_CLK(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_CLK(port),
                         DPIO_PCS_CLK_CRI_RXEB_EIOS_EN |
                         DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN |
                         (1<<DPIO_PCS_CLK_DATAWIDTH_SHIFT) |
                                 DPIO_PCS_CLK_SOFT_RESET);
 
        /* Fix up inter-pair skew failure */
-       vlv_dpio_write(dev_priv, DPIO_PCS_STAGGER1(port), 0x00750f00);
-       vlv_dpio_write(dev_priv, DPIO_TX_CTL(port), 0x00001500);
-       vlv_dpio_write(dev_priv, DPIO_TX_LANE(port), 0x40400000);
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_STAGGER1(port), 0x00750f00);
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_CTL(port), 0x00001500);
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_LANE(port), 0x40400000);
        mutex_unlock(&dev_priv->dpio_lock);
 }
 
@@ -1869,7 +1964,7 @@ intel_dp_voltage_max(struct intel_dp *intel_dp)
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        enum port port = dp_to_dig_port(intel_dp)->port;
 
-       if (IS_VALLEYVIEW(dev))
+       if (IS_VALLEYVIEW(dev) || IS_BROADWELL(dev))
                return DP_TRAIN_VOLTAGE_SWING_1200;
        else if (IS_GEN7(dev) && port == PORT_A)
                return DP_TRAIN_VOLTAGE_SWING_800;
@@ -1885,7 +1980,18 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        enum port port = dp_to_dig_port(intel_dp)->port;
 
-       if (HAS_DDI(dev)) {
+       if (IS_BROADWELL(dev)) {
+               switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+               case DP_TRAIN_VOLTAGE_SWING_400:
+               case DP_TRAIN_VOLTAGE_SWING_600:
+                       return DP_TRAIN_PRE_EMPHASIS_6;
+               case DP_TRAIN_VOLTAGE_SWING_800:
+                       return DP_TRAIN_PRE_EMPHASIS_3_5;
+               case DP_TRAIN_VOLTAGE_SWING_1200:
+               default:
+                       return DP_TRAIN_PRE_EMPHASIS_0;
+               }
+       } else if (IS_HASWELL(dev)) {
                switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
                case DP_TRAIN_VOLTAGE_SWING_400:
                        return DP_TRAIN_PRE_EMPHASIS_9_5;
@@ -1939,10 +2045,13 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp)
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_digital_port *dport = dp_to_dig_port(intel_dp);
+       struct intel_crtc *intel_crtc =
+               to_intel_crtc(dport->base.base.crtc);
        unsigned long demph_reg_value, preemph_reg_value,
                uniqtranscale_reg_value;
        uint8_t train_set = intel_dp->train_set[0];
        int port = vlv_dport_to_channel(dport);
+       int pipe = intel_crtc->pipe;
 
        switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
        case DP_TRAIN_PRE_EMPHASIS_0:
@@ -2018,21 +2127,22 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp)
        }
 
        mutex_lock(&dev_priv->dpio_lock);
-       vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0x00000000);
-       vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL4(port), demph_reg_value);
-       vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL2(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_OCALINIT(port), 0x00000000);
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_SWING_CTL4(port), demph_reg_value);
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_SWING_CTL2(port),
                         uniqtranscale_reg_value);
-       vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL3(port), 0x0C782040);
-       vlv_dpio_write(dev_priv, DPIO_PCS_STAGGER0(port), 0x00030000);
-       vlv_dpio_write(dev_priv, DPIO_PCS_CTL_OVER1(port), preemph_reg_value);
-       vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0x80000000);
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_SWING_CTL3(port), 0x0C782040);
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_STAGGER0(port), 0x00030000);
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_CTL_OVER1(port), preemph_reg_value);
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_OCALINIT(port), 0x80000000);
        mutex_unlock(&dev_priv->dpio_lock);
 
        return 0;
 }
 
 static void
-intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
+intel_get_adjust_train(struct intel_dp *intel_dp,
+                      const uint8_t link_status[DP_LINK_STATUS_SIZE])
 {
        uint8_t v = 0;
        uint8_t p = 0;
@@ -2193,6 +2303,41 @@ intel_hsw_signal_levels(uint8_t train_set)
        }
 }
 
+static uint32_t
+intel_bdw_signal_levels(uint8_t train_set)
+{
+       int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
+                                        DP_TRAIN_PRE_EMPHASIS_MASK);
+       switch (signal_levels) {
+       case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0:
+               return DDI_BUF_EMP_400MV_0DB_BDW;       /* Sel0 */
+       case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5:
+               return DDI_BUF_EMP_400MV_3_5DB_BDW;     /* Sel1 */
+       case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6:
+               return DDI_BUF_EMP_400MV_6DB_BDW;       /* Sel2 */
+
+       case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0:
+               return DDI_BUF_EMP_600MV_0DB_BDW;       /* Sel3 */
+       case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5:
+               return DDI_BUF_EMP_600MV_3_5DB_BDW;     /* Sel4 */
+       case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6:
+               return DDI_BUF_EMP_600MV_6DB_BDW;       /* Sel5 */
+
+       case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0:
+               return DDI_BUF_EMP_800MV_0DB_BDW;       /* Sel6 */
+       case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5:
+               return DDI_BUF_EMP_800MV_3_5DB_BDW;     /* Sel7 */
+
+       case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0:
+               return DDI_BUF_EMP_1200MV_0DB_BDW;      /* Sel8 */
+
+       default:
+               DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:"
+                             "0x%x\n", signal_levels);
+               return DDI_BUF_EMP_400MV_0DB_BDW;       /* Sel0 */
+       }
+}
+
 /* Properly updates "DP" with the correct signal levels. */
 static void
 intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
@@ -2203,7 +2348,10 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
        uint32_t signal_levels, mask;
        uint8_t train_set = intel_dp->train_set[0];
 
-       if (HAS_DDI(dev)) {
+       if (IS_BROADWELL(dev)) {
+               signal_levels = intel_bdw_signal_levels(train_set);
+               mask = DDI_BUF_EMP_MASK;
+       } else if (IS_HASWELL(dev)) {
                signal_levels = intel_hsw_signal_levels(train_set);
                mask = DDI_BUF_EMP_MASK;
        } else if (IS_VALLEYVIEW(dev)) {
@@ -2227,14 +2375,15 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
 
 static bool
 intel_dp_set_link_train(struct intel_dp *intel_dp,
-                       uint32_t dp_reg_value,
+                       uint32_t *DP,
                        uint8_t dp_train_pat)
 {
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        struct drm_device *dev = intel_dig_port->base.base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        enum port port = intel_dig_port->port;
-       int ret;
+       uint8_t buf[sizeof(intel_dp->train_set) + 1];
+       int ret, len;
 
        if (HAS_DDI(dev)) {
                uint32_t temp = I915_READ(DP_TP_CTL(port));
@@ -2263,62 +2412,93 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
                I915_WRITE(DP_TP_CTL(port), temp);
 
        } else if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) {
-               dp_reg_value &= ~DP_LINK_TRAIN_MASK_CPT;
+               *DP &= ~DP_LINK_TRAIN_MASK_CPT;
 
                switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
                case DP_TRAINING_PATTERN_DISABLE:
-                       dp_reg_value |= DP_LINK_TRAIN_OFF_CPT;
+                       *DP |= DP_LINK_TRAIN_OFF_CPT;
                        break;
                case DP_TRAINING_PATTERN_1:
-                       dp_reg_value |= DP_LINK_TRAIN_PAT_1_CPT;
+                       *DP |= DP_LINK_TRAIN_PAT_1_CPT;
                        break;
                case DP_TRAINING_PATTERN_2:
-                       dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT;
+                       *DP |= DP_LINK_TRAIN_PAT_2_CPT;
                        break;
                case DP_TRAINING_PATTERN_3:
                        DRM_ERROR("DP training pattern 3 not supported\n");
-                       dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT;
+                       *DP |= DP_LINK_TRAIN_PAT_2_CPT;
                        break;
                }
 
        } else {
-               dp_reg_value &= ~DP_LINK_TRAIN_MASK;
+               *DP &= ~DP_LINK_TRAIN_MASK;
 
                switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
                case DP_TRAINING_PATTERN_DISABLE:
-                       dp_reg_value |= DP_LINK_TRAIN_OFF;
+                       *DP |= DP_LINK_TRAIN_OFF;
                        break;
                case DP_TRAINING_PATTERN_1:
-                       dp_reg_value |= DP_LINK_TRAIN_PAT_1;
+                       *DP |= DP_LINK_TRAIN_PAT_1;
                        break;
                case DP_TRAINING_PATTERN_2:
-                       dp_reg_value |= DP_LINK_TRAIN_PAT_2;
+                       *DP |= DP_LINK_TRAIN_PAT_2;
                        break;
                case DP_TRAINING_PATTERN_3:
                        DRM_ERROR("DP training pattern 3 not supported\n");
-                       dp_reg_value |= DP_LINK_TRAIN_PAT_2;
+                       *DP |= DP_LINK_TRAIN_PAT_2;
                        break;
                }
        }
 
-       I915_WRITE(intel_dp->output_reg, dp_reg_value);
+       I915_WRITE(intel_dp->output_reg, *DP);
        POSTING_READ(intel_dp->output_reg);
 
-       intel_dp_aux_native_write_1(intel_dp,
-                                   DP_TRAINING_PATTERN_SET,
-                                   dp_train_pat);
-
-       if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) !=
+       buf[0] = dp_train_pat;
+       if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
            DP_TRAINING_PATTERN_DISABLE) {
-               ret = intel_dp_aux_native_write(intel_dp,
-                                               DP_TRAINING_LANE0_SET,
-                                               intel_dp->train_set,
-                                               intel_dp->lane_count);
-               if (ret != intel_dp->lane_count)
-                       return false;
+               /* don't write DP_TRAINING_LANEx_SET on disable */
+               len = 1;
+       } else {
+               /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
+               memcpy(buf + 1, intel_dp->train_set, intel_dp->lane_count);
+               len = intel_dp->lane_count + 1;
        }
 
-       return true;
+       ret = intel_dp_aux_native_write(intel_dp, DP_TRAINING_PATTERN_SET,
+                                       buf, len);
+
+       return ret == len;
+}
+
+static bool
+intel_dp_reset_link_train(struct intel_dp *intel_dp, uint32_t *DP,
+                       uint8_t dp_train_pat)
+{
+       memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
+       intel_dp_set_signal_levels(intel_dp, DP);
+       return intel_dp_set_link_train(intel_dp, DP, dp_train_pat);
+}
+
+static bool
+intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP,
+                          const uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       intel_get_adjust_train(intel_dp, link_status);
+       intel_dp_set_signal_levels(intel_dp, DP);
+
+       I915_WRITE(intel_dp->output_reg, *DP);
+       POSTING_READ(intel_dp->output_reg);
+
+       ret = intel_dp_aux_native_write(intel_dp, DP_TRAINING_LANE0_SET,
+                                       intel_dp->train_set,
+                                       intel_dp->lane_count);
+
+       return ret == intel_dp->lane_count;
 }
 
 static void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
@@ -2362,32 +2542,37 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
        uint8_t voltage;
        int voltage_tries, loop_tries;
        uint32_t DP = intel_dp->DP;
+       uint8_t link_config[2];
 
        if (HAS_DDI(dev))
                intel_ddi_prepare_link_retrain(encoder);
 
        /* Write the link configuration data */
-       intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
-                                 intel_dp->link_configuration,
-                                 DP_LINK_CONFIGURATION_SIZE);
+       link_config[0] = intel_dp->link_bw;
+       link_config[1] = intel_dp->lane_count;
+       if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
+               link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+       intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET, link_config, 2);
+
+       link_config[0] = 0;
+       link_config[1] = DP_SET_ANSI_8B10B;
+       intel_dp_aux_native_write(intel_dp, DP_DOWNSPREAD_CTRL, link_config, 2);
 
        DP |= DP_PORT_EN;
 
-       memset(intel_dp->train_set, 0, 4);
+       /* clock recovery */
+       if (!intel_dp_reset_link_train(intel_dp, &DP,
+                                      DP_TRAINING_PATTERN_1 |
+                                      DP_LINK_SCRAMBLING_DISABLE)) {
+               DRM_ERROR("failed to enable link training\n");
+               return;
+       }
+
        voltage = 0xff;
        voltage_tries = 0;
        loop_tries = 0;
        for (;;) {
-               /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
-               uint8_t     link_status[DP_LINK_STATUS_SIZE];
-
-               intel_dp_set_signal_levels(intel_dp, &DP);
-
-               /* Set training pattern 1 */
-               if (!intel_dp_set_link_train(intel_dp, DP,
-                                            DP_TRAINING_PATTERN_1 |
-                                            DP_LINK_SCRAMBLING_DISABLE))
-                       break;
+               uint8_t link_status[DP_LINK_STATUS_SIZE];
 
                drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd);
                if (!intel_dp_get_link_status(intel_dp, link_status)) {
@@ -2407,10 +2592,12 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
                if (i == intel_dp->lane_count) {
                        ++loop_tries;
                        if (loop_tries == 5) {
-                               DRM_DEBUG_KMS("too many full retries, give up\n");
+                               DRM_ERROR("too many full retries, give up\n");
                                break;
                        }
-                       memset(intel_dp->train_set, 0, 4);
+                       intel_dp_reset_link_train(intel_dp, &DP,
+                                                 DP_TRAINING_PATTERN_1 |
+                                                 DP_LINK_SCRAMBLING_DISABLE);
                        voltage_tries = 0;
                        continue;
                }
@@ -2419,15 +2606,18 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
                if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
                        ++voltage_tries;
                        if (voltage_tries == 5) {
-                               DRM_DEBUG_KMS("too many voltage retries, give up\n");
+                               DRM_ERROR("too many voltage retries, give up\n");
                                break;
                        }
                } else
                        voltage_tries = 0;
                voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
 
-               /* Compute new intel_dp->train_set as requested by target */
-               intel_get_adjust_train(intel_dp, link_status);
+               /* Update training set as requested by target */
+               if (!intel_dp_update_link_train(intel_dp, &DP, link_status)) {
+                       DRM_ERROR("failed to update link training\n");
+                       break;
+               }
        }
 
        intel_dp->DP = DP;
@@ -2441,11 +2631,18 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
        uint32_t DP = intel_dp->DP;
 
        /* channel equalization */
+       if (!intel_dp_set_link_train(intel_dp, &DP,
+                                    DP_TRAINING_PATTERN_2 |
+                                    DP_LINK_SCRAMBLING_DISABLE)) {
+               DRM_ERROR("failed to start channel equalization\n");
+               return;
+       }
+
        tries = 0;
        cr_tries = 0;
        channel_eq = false;
        for (;;) {
-               uint8_t     link_status[DP_LINK_STATUS_SIZE];
+               uint8_t link_status[DP_LINK_STATUS_SIZE];
 
                if (cr_tries > 5) {
                        DRM_ERROR("failed to train DP, aborting\n");
@@ -2453,21 +2650,18 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
                        break;
                }
 
-               intel_dp_set_signal_levels(intel_dp, &DP);
-
-               /* channel eq pattern */
-               if (!intel_dp_set_link_train(intel_dp, DP,
-                                            DP_TRAINING_PATTERN_2 |
-                                            DP_LINK_SCRAMBLING_DISABLE))
-                       break;
-
                drm_dp_link_train_channel_eq_delay(intel_dp->dpcd);
-               if (!intel_dp_get_link_status(intel_dp, link_status))
+               if (!intel_dp_get_link_status(intel_dp, link_status)) {
+                       DRM_ERROR("failed to get link status\n");
                        break;
+               }
 
                /* Make sure clock is still ok */
                if (!drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) {
                        intel_dp_start_link_train(intel_dp);
+                       intel_dp_set_link_train(intel_dp, &DP,
+                                               DP_TRAINING_PATTERN_2 |
+                                               DP_LINK_SCRAMBLING_DISABLE);
                        cr_tries++;
                        continue;
                }
@@ -2481,13 +2675,19 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
                if (tries > 5) {
                        intel_dp_link_down(intel_dp);
                        intel_dp_start_link_train(intel_dp);
+                       intel_dp_set_link_train(intel_dp, &DP,
+                                               DP_TRAINING_PATTERN_2 |
+                                               DP_LINK_SCRAMBLING_DISABLE);
                        tries = 0;
                        cr_tries++;
                        continue;
                }
 
-               /* Compute new intel_dp->train_set as requested by target */
-               intel_get_adjust_train(intel_dp, link_status);
+               /* Update training set as requested by target */
+               if (!intel_dp_update_link_train(intel_dp, &DP, link_status)) {
+                       DRM_ERROR("failed to update link training\n");
+                       break;
+               }
                ++tries;
        }
 
@@ -2502,7 +2702,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
 
 void intel_dp_stop_link_train(struct intel_dp *intel_dp)
 {
-       intel_dp_set_link_train(intel_dp, intel_dp->DP,
+       intel_dp_set_link_train(intel_dp, &intel_dp->DP,
                                DP_TRAINING_PATTERN_DISABLE);
 }
 
@@ -2589,6 +2789,10 @@ intel_dp_link_down(struct intel_dp *intel_dp)
 static bool
 intel_dp_get_dpcd(struct intel_dp *intel_dp)
 {
+       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+       struct drm_device *dev = dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
        char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3];
 
        if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd,
@@ -2604,11 +2808,16 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
 
        /* Check if the panel supports PSR */
        memset(intel_dp->psr_dpcd, 0, sizeof(intel_dp->psr_dpcd));
-       intel_dp_aux_native_read_retry(intel_dp, DP_PSR_SUPPORT,
-                                      intel_dp->psr_dpcd,
-                                      sizeof(intel_dp->psr_dpcd));
-       if (is_edp_psr(intel_dp))
-               DRM_DEBUG_KMS("Detected EDP PSR Panel.\n");
+       if (is_edp(intel_dp)) {
+               intel_dp_aux_native_read_retry(intel_dp, DP_PSR_SUPPORT,
+                                              intel_dp->psr_dpcd,
+                                              sizeof(intel_dp->psr_dpcd));
+               if (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED) {
+                       dev_priv->psr.sink_support = true;
+                       DRM_DEBUG_KMS("Detected EDP PSR Panel.\n");
+               }
+       }
+
        if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
              DP_DWN_STRM_PORT_PRESENT))
                return true; /* native DP sink */
@@ -2728,7 +2937,6 @@ static enum drm_connector_status
 intel_dp_detect_dpcd(struct intel_dp *intel_dp)
 {
        uint8_t *dpcd = intel_dp->dpcd;
-       bool hpd;
        uint8_t type;
 
        if (!intel_dp_get_dpcd(intel_dp))
@@ -2739,8 +2947,8 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp)
                return connector_status_connected;
 
        /* If we're HPD-aware, SINK_COUNT changes dynamically */
-       hpd = !!(intel_dp->downstream_ports[0] & DP_DS_PORT_HPD);
-       if (hpd) {
+       if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
+           intel_dp->downstream_ports[0] & DP_DS_PORT_HPD) {
                uint8_t reg;
                if (!intel_dp_aux_native_read_retry(intel_dp, DP_SINK_COUNT,
                                                    &reg, 1))
@@ -2754,9 +2962,18 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp)
                return connector_status_connected;
 
        /* Well we tried, say unknown for unreliable port types */
-       type = intel_dp->downstream_ports[0] & DP_DS_PORT_TYPE_MASK;
-       if (type == DP_DS_PORT_TYPE_VGA || type == DP_DS_PORT_TYPE_NON_EDID)
-               return connector_status_unknown;
+       if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) {
+               type = intel_dp->downstream_ports[0] & DP_DS_PORT_TYPE_MASK;
+               if (type == DP_DS_PORT_TYPE_VGA ||
+                   type == DP_DS_PORT_TYPE_NON_EDID)
+                       return connector_status_unknown;
+       } else {
+               type = intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+                       DP_DWN_STRM_PORT_TYPE_MASK;
+               if (type == DP_DWN_STRM_PORT_TYPE_ANALOG ||
+                   type == DP_DWN_STRM_PORT_TYPE_OTHER)
+                       return connector_status_unknown;
+       }
 
        /* Anything else is out of spec, warn and ignore */
        DRM_DEBUG_KMS("Broken DP branch device, ignoring\n");
@@ -2830,19 +3047,11 @@ intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
 
        /* use cached edid if we have one */
        if (intel_connector->edid) {
-               struct edid *edid;
-               int size;
-
                /* invalid edid */
                if (IS_ERR(intel_connector->edid))
                        return NULL;
 
-               size = (intel_connector->edid->extensions + 1) * EDID_LENGTH;
-               edid = kmemdup(intel_connector->edid, size, GFP_KERNEL);
-               if (!edid)
-                       return NULL;
-
-               return edid;
+               return drm_edid_duplicate(intel_connector->edid);
        }
 
        return drm_get_edid(connector, adapter);
@@ -3050,7 +3259,6 @@ intel_dp_connector_destroy(struct drm_connector *connector)
        if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
                intel_panel_fini(&intel_connector->panel);
 
-       drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
        kfree(connector);
 }
@@ -3121,7 +3329,7 @@ intel_trans_dp_port_sel(struct drm_crtc *crtc)
 bool intel_dpd_is_edp(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct child_device_config *p_child;
+       union child_device_config *p_child;
        int i;
 
        if (!dev_priv->vbt.child_dev_num)
@@ -3130,8 +3338,9 @@ bool intel_dpd_is_edp(struct drm_device *dev)
        for (i = 0; i < dev_priv->vbt.child_dev_num; i++) {
                p_child = dev_priv->vbt.child_dev + i;
 
-               if (p_child->dvo_port == PORT_IDPD &&
-                   p_child->device_type == DEVICE_TYPE_eDP)
+               if (p_child->common.dvo_port == PORT_IDPD &&
+                   (p_child->common.device_type & DEVICE_TYPE_eDP_BITS) ==
+                   (DEVICE_TYPE_eDP & DEVICE_TYPE_eDP_BITS))
                        return true;
        }
        return false;
@@ -3164,24 +3373,26 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev,
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct edp_power_seq cur, vbt, spec, final;
        u32 pp_on, pp_off, pp_div, pp;
-       int pp_control_reg, pp_on_reg, pp_off_reg, pp_div_reg;
+       int pp_ctrl_reg, pp_on_reg, pp_off_reg, pp_div_reg;
 
        if (HAS_PCH_SPLIT(dev)) {
-               pp_control_reg = PCH_PP_CONTROL;
+               pp_ctrl_reg = PCH_PP_CONTROL;
                pp_on_reg = PCH_PP_ON_DELAYS;
                pp_off_reg = PCH_PP_OFF_DELAYS;
                pp_div_reg = PCH_PP_DIVISOR;
        } else {
-               pp_control_reg = PIPEA_PP_CONTROL;
-               pp_on_reg = PIPEA_PP_ON_DELAYS;
-               pp_off_reg = PIPEA_PP_OFF_DELAYS;
-               pp_div_reg = PIPEA_PP_DIVISOR;
+               enum pipe pipe = vlv_power_sequencer_pipe(intel_dp);
+
+               pp_ctrl_reg = VLV_PIPE_PP_CONTROL(pipe);
+               pp_on_reg = VLV_PIPE_PP_ON_DELAYS(pipe);
+               pp_off_reg = VLV_PIPE_PP_OFF_DELAYS(pipe);
+               pp_div_reg = VLV_PIPE_PP_DIVISOR(pipe);
        }
 
        /* Workaround: Need to write PP_CONTROL with the unlock key as
         * the very first thing. */
        pp = ironlake_get_pp_control(intel_dp);
-       I915_WRITE(pp_control_reg, pp);
+       I915_WRITE(pp_ctrl_reg, pp);
 
        pp_on = I915_READ(pp_on_reg);
        pp_off = I915_READ(pp_off_reg);
@@ -3269,9 +3480,11 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
                pp_off_reg = PCH_PP_OFF_DELAYS;
                pp_div_reg = PCH_PP_DIVISOR;
        } else {
-               pp_on_reg = PIPEA_PP_ON_DELAYS;
-               pp_off_reg = PIPEA_PP_OFF_DELAYS;
-               pp_div_reg = PIPEA_PP_DIVISOR;
+               enum pipe pipe = vlv_power_sequencer_pipe(intel_dp);
+
+               pp_on_reg = VLV_PIPE_PP_ON_DELAYS(pipe);
+               pp_off_reg = VLV_PIPE_PP_OFF_DELAYS(pipe);
+               pp_div_reg = VLV_PIPE_PP_DIVISOR(pipe);
        }
 
        /* And finally store the new values in the power sequencer. */
@@ -3288,12 +3501,15 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
        /* Haswell doesn't have any port selection bits for the panel
         * power sequencer any more. */
        if (IS_VALLEYVIEW(dev)) {
-               port_sel = I915_READ(pp_on_reg) & 0xc0000000;
+               if (dp_to_dig_port(intel_dp)->port == PORT_B)
+                       port_sel = PANEL_PORT_SELECT_DPB_VLV;
+               else
+                       port_sel = PANEL_PORT_SELECT_DPC_VLV;
        } else if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) {
                if (dp_to_dig_port(intel_dp)->port == PORT_A)
-                       port_sel = PANEL_POWER_PORT_DP_A;
+                       port_sel = PANEL_PORT_SELECT_DPA;
                else
-                       port_sel = PANEL_POWER_PORT_DP_D;
+                       port_sel = PANEL_PORT_SELECT_DPD;
        }
 
        pp_on |= port_sel;
@@ -3346,7 +3562,6 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
        intel_dp_init_panel_power_sequencer_registers(dev, intel_dp,
                                                      &power_seq);
 
-       ironlake_edp_panel_vdd_on(intel_dp);
        edid = drm_get_edid(connector, &intel_dp->adapter);
        if (edid) {
                if (drm_add_edid_modes(connector, edid)) {
@@ -3378,8 +3593,6 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
                        fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
        }
 
-       ironlake_edp_panel_vdd_off(intel_dp, false);
-
        intel_panel_init(&intel_connector->panel, fixed_mode);
        intel_panel_setup_backlight(connector);
 
@@ -3536,11 +3749,11 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
        struct drm_encoder *encoder;
        struct intel_connector *intel_connector;
 
-       intel_dig_port = kzalloc(sizeof(struct intel_digital_port), GFP_KERNEL);
+       intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL);
        if (!intel_dig_port)
                return;
 
-       intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
+       intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
        if (!intel_connector) {
                kfree(intel_dig_port);
                return;
@@ -3559,12 +3772,12 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
        intel_encoder->get_hw_state = intel_dp_get_hw_state;
        intel_encoder->get_config = intel_dp_get_config;
        if (IS_VALLEYVIEW(dev)) {
-               intel_encoder->pre_pll_enable = intel_dp_pre_pll_enable;
+               intel_encoder->pre_pll_enable = vlv_dp_pre_pll_enable;
                intel_encoder->pre_enable = vlv_pre_enable_dp;
                intel_encoder->enable = vlv_enable_dp;
        } else {
-               intel_encoder->pre_enable = intel_pre_enable_dp;
-               intel_encoder->enable = intel_enable_dp;
+               intel_encoder->pre_enable = g4x_pre_enable_dp;
+               intel_encoder->enable = g4x_enable_dp;
        }
 
        intel_dig_port->port = port;
index 7f2b384..1e49aa8 100644 (file)
@@ -77,7 +77,6 @@
 /* the i915, i945 have a single sDVO i2c bus - which is different */
 #define MAX_OUTPUTS 6
 /* maximum connectors per crtcs in the mode set */
-#define INTELFB_CONN_LIMIT 4
 
 #define INTEL_I2C_BUS_DVO 1
 #define INTEL_I2C_BUS_SDVO 2
 #define INTEL_OUTPUT_HDMI 6
 #define INTEL_OUTPUT_DISPLAYPORT 7
 #define INTEL_OUTPUT_EDP 8
-#define INTEL_OUTPUT_UNKNOWN 9
+#define INTEL_OUTPUT_DSI 9
+#define INTEL_OUTPUT_UNKNOWN 10
 
 #define INTEL_DVO_CHIP_NONE 0
 #define INTEL_DVO_CHIP_LVDS 1
 #define INTEL_DVO_CHIP_TMDS 2
 #define INTEL_DVO_CHIP_TVOUT 4
 
+#define INTEL_DSI_COMMAND_MODE 0
+#define INTEL_DSI_VIDEO_MODE   1
+
 struct intel_framebuffer {
        struct drm_framebuffer base;
        struct drm_i915_gem_object *obj;
@@ -207,8 +210,21 @@ struct intel_crtc_config {
 #define PIPE_CONFIG_QUIRK_MODE_SYNC_FLAGS (1<<0) /* unreliable sync mode.flags */
        unsigned long quirks;
 
+       /* User requested mode, only valid as a starting point to
+        * compute adjusted_mode, except in the case of (S)DVO where
+        * it's also for the output timings of the (S)DVO chip.
+        * adjusted_mode will then correspond to the S(DVO) chip's
+        * preferred input timings. */
        struct drm_display_mode requested_mode;
+       /* Actual pipe timings ie. what we program into the pipe timing
+        * registers. adjusted_mode.crtc_clock is the pipe pixel clock. */
        struct drm_display_mode adjusted_mode;
+
+       /* Pipe source size (ie. panel fitter input size)
+        * All planes will be positioned inside this space,
+        * and get clipped at the edges. */
+       int pipe_src_w, pipe_src_h;
+
        /* Whether to set up the PCH/FDI. Note that we never allow sharing
         * between pch encoders and cpu encoders. */
        bool has_pch_encoder;
@@ -262,7 +278,8 @@ struct intel_crtc_config {
 
        /*
         * Frequence the dpll for the port should run at. Differs from the
-        * adjusted dotclock e.g. for DP or 12bpc hdmi mode.
+        * adjusted dotclock e.g. for DP or 12bpc hdmi mode. This is also
+        * already multiplied by pixel_multiplier.
         */
        int port_clock;
 
@@ -288,6 +305,14 @@ struct intel_crtc_config {
        struct intel_link_m_n fdi_m_n;
 
        bool ips_enabled;
+
+       bool double_wide;
+};
+
+struct intel_pipe_wm {
+       struct intel_wm_level wm[5];
+       uint32_t linetime;
+       bool fbc_wm_enabled;
 };
 
 struct intel_crtc {
@@ -301,8 +326,9 @@ struct intel_crtc {
         * some outputs connected to this crtc.
         */
        bool active;
+       unsigned long enabled_power_domains;
        bool eld_vld;
-       bool primary_disabled; /* is the crtc obscured by a plane? */
+       bool primary_enabled; /* is the primary plane (partially) visible? */
        bool lowfreq_avail;
        struct intel_overlay *overlay;
        struct intel_unpin_work *unpin_work;
@@ -330,6 +356,12 @@ struct intel_crtc {
        /* Access to these should be protected by dev_priv->irq_lock. */
        bool cpu_fifo_underrun_disabled;
        bool pch_fifo_underrun_disabled;
+
+       /* per-pipe watermark state */
+       struct {
+               /* watermarks currently being used  */
+               struct intel_pipe_wm active;
+       } wm;
 };
 
 struct intel_plane_wm_parameters {
@@ -417,13 +449,11 @@ struct intel_hdmi {
 };
 
 #define DP_MAX_DOWNSTREAM_PORTS                0x10
-#define DP_LINK_CONFIGURATION_SIZE     9
 
 struct intel_dp {
        uint32_t output_reg;
        uint32_t aux_ch_ctl_reg;
        uint32_t DP;
-       uint8_t  link_configuration[DP_LINK_CONFIGURATION_SIZE];
        bool has_audio;
        enum hdmi_force_audio force_audio;
        uint32_t color_range;
@@ -495,80 +525,6 @@ struct intel_unpin_work {
        bool enable_stall_check;
 };
 
-int intel_pch_rawclk(struct drm_device *dev);
-
-int intel_connector_update_modes(struct drm_connector *connector,
-                               struct edid *edid);
-int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
-
-extern void intel_attach_force_audio_property(struct drm_connector *connector);
-extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
-
-extern bool intel_pipe_has_type(struct drm_crtc *crtc, int type);
-extern void intel_crt_init(struct drm_device *dev);
-extern void intel_hdmi_init(struct drm_device *dev,
-                           int hdmi_reg, enum port port);
-extern void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
-                                     struct intel_connector *intel_connector);
-extern struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder);
-extern bool intel_hdmi_compute_config(struct intel_encoder *encoder,
-                                     struct intel_crtc_config *pipe_config);
-extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg,
-                           bool is_sdvob);
-extern void intel_dvo_init(struct drm_device *dev);
-extern void intel_tv_init(struct drm_device *dev);
-extern void intel_mark_busy(struct drm_device *dev);
-extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
-                              struct intel_ring_buffer *ring);
-extern void intel_mark_idle(struct drm_device *dev);
-extern void intel_lvds_init(struct drm_device *dev);
-extern bool intel_is_dual_link_lvds(struct drm_device *dev);
-extern void intel_dp_init(struct drm_device *dev, int output_reg,
-                         enum port port);
-extern bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
-                                   struct intel_connector *intel_connector);
-extern void intel_dp_init_link_config(struct intel_dp *intel_dp);
-extern void intel_dp_start_link_train(struct intel_dp *intel_dp);
-extern void intel_dp_complete_link_train(struct intel_dp *intel_dp);
-extern void intel_dp_stop_link_train(struct intel_dp *intel_dp);
-extern void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);
-extern void intel_dp_encoder_destroy(struct drm_encoder *encoder);
-extern void intel_dp_check_link_status(struct intel_dp *intel_dp);
-extern bool intel_dp_compute_config(struct intel_encoder *encoder,
-                                   struct intel_crtc_config *pipe_config);
-extern bool intel_dpd_is_edp(struct drm_device *dev);
-extern void ironlake_edp_backlight_on(struct intel_dp *intel_dp);
-extern void ironlake_edp_backlight_off(struct intel_dp *intel_dp);
-extern void ironlake_edp_panel_on(struct intel_dp *intel_dp);
-extern void ironlake_edp_panel_off(struct intel_dp *intel_dp);
-extern void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp);
-extern void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
-extern int intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane);
-extern void intel_flush_display_plane(struct drm_i915_private *dev_priv,
-                                     enum plane plane);
-
-/* intel_panel.c */
-extern int intel_panel_init(struct intel_panel *panel,
-                           struct drm_display_mode *fixed_mode);
-extern void intel_panel_fini(struct intel_panel *panel);
-
-extern void intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
-                                  struct drm_display_mode *adjusted_mode);
-extern void intel_pch_panel_fitting(struct intel_crtc *crtc,
-                                   struct intel_crtc_config *pipe_config,
-                                   int fitting_mode);
-extern void intel_gmch_panel_fitting(struct intel_crtc *crtc,
-                                    struct intel_crtc_config *pipe_config,
-                                    int fitting_mode);
-extern void intel_panel_set_backlight(struct drm_device *dev,
-                                     u32 level, u32 max);
-extern int intel_panel_setup_backlight(struct drm_connector *connector);
-extern void intel_panel_enable_backlight(struct drm_device *dev,
-                                        enum pipe pipe);
-extern void intel_panel_disable_backlight(struct drm_device *dev);
-extern void intel_panel_destroy_backlight(struct drm_device *dev);
-extern enum drm_connector_status intel_panel_detect(struct drm_device *dev);
-
 struct intel_set_config {
        struct drm_encoder **save_connector_encoders;
        struct drm_crtc **save_encoder_crtcs;
@@ -577,18 +533,14 @@ struct intel_set_config {
        bool mode_changed;
 };
 
-extern void intel_crtc_restore_mode(struct drm_crtc *crtc);
-extern void intel_crtc_load_lut(struct drm_crtc *crtc);
-extern void intel_crtc_update_dpms(struct drm_crtc *crtc);
-extern void intel_encoder_destroy(struct drm_encoder *encoder);
-extern void intel_connector_dpms(struct drm_connector *, int mode);
-extern bool intel_connector_get_hw_state(struct intel_connector *connector);
-extern void intel_modeset_check_state(struct drm_device *dev);
-extern void intel_plane_restore(struct drm_plane *plane);
-extern void intel_plane_disable(struct drm_plane *plane);
-
+struct intel_load_detect_pipe {
+       struct drm_framebuffer *release_fb;
+       bool load_detect_temp;
+       int dpms_mode;
+};
 
-static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector)
+static inline struct intel_encoder *
+intel_attached_encoder(struct drm_connector *connector)
 {
        return to_intel_connector(connector)->encoder;
 }
@@ -616,73 +568,95 @@ hdmi_to_dig_port(struct intel_hdmi *intel_hdmi)
        return container_of(intel_hdmi, struct intel_digital_port, hdmi);
 }
 
+
+/* i915_irq.c */
+bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
+                                          enum pipe pipe, bool enable);
+bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
+                                          enum transcoder pch_transcoder,
+                                          bool enable);
+void ilk_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void ilk_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void snb_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void snb_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void hsw_pc8_disable_interrupts(struct drm_device *dev);
+void hsw_pc8_restore_interrupts(struct drm_device *dev);
+
+
+/* intel_crt.c */
+void intel_crt_init(struct drm_device *dev);
+
+
+/* intel_ddi.c */
+void intel_prepare_ddi(struct drm_device *dev);
+void hsw_fdi_link_train(struct drm_crtc *crtc);
+void intel_ddi_init(struct drm_device *dev, enum port port);
+enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder);
+bool intel_ddi_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe);
+int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv);
+void intel_ddi_pll_init(struct drm_device *dev);
+void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc);
+void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
+                                      enum transcoder cpu_transcoder);
+void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc);
+void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc);
+void intel_ddi_setup_hw_pll_state(struct drm_device *dev);
+bool intel_ddi_pll_mode_set(struct drm_crtc *crtc);
+void intel_ddi_put_crtc_pll(struct drm_crtc *crtc);
+void intel_ddi_set_pipe_settings(struct drm_crtc *crtc);
+void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
+bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
+void intel_ddi_fdi_disable(struct drm_crtc *crtc);
+void intel_ddi_get_config(struct intel_encoder *encoder,
+                         struct intel_crtc_config *pipe_config);
+
+
+/* intel_display.c */
+int intel_pch_rawclk(struct drm_device *dev);
+void intel_mark_busy(struct drm_device *dev);
+void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
+                       struct intel_ring_buffer *ring);
+void intel_mark_idle(struct drm_device *dev);
+void intel_crtc_restore_mode(struct drm_crtc *crtc);
+void intel_crtc_update_dpms(struct drm_crtc *crtc);
+void intel_encoder_destroy(struct drm_encoder *encoder);
+void intel_connector_dpms(struct drm_connector *, int mode);
+bool intel_connector_get_hw_state(struct intel_connector *connector);
+void intel_modeset_check_state(struct drm_device *dev);
 bool ibx_digital_port_connected(struct drm_i915_private *dev_priv,
                                struct intel_digital_port *port);
-
-extern void intel_connector_attach_encoder(struct intel_connector *connector,
-                                          struct intel_encoder *encoder);
-extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
-
-extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
-                                                   struct drm_crtc *crtc);
+void intel_connector_attach_encoder(struct intel_connector *connector,
+                                   struct intel_encoder *encoder);
+struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
+struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
+                                            struct drm_crtc *crtc);
+enum pipe intel_get_pipe_from_connector(struct intel_connector *connector);
 int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
                                struct drm_file *file_priv);
-extern enum transcoder
-intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv,
-                            enum pipe pipe);
-extern void intel_wait_for_vblank(struct drm_device *dev, int pipe);
-extern void intel_wait_for_pipe_off(struct drm_device *dev, int pipe);
-extern int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp);
-extern void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port);
-
-struct intel_load_detect_pipe {
-       struct drm_framebuffer *release_fb;
-       bool load_detect_temp;
-       int dpms_mode;
-};
-extern bool intel_get_load_detect_pipe(struct drm_connector *connector,
-                                      struct drm_display_mode *mode,
-                                      struct intel_load_detect_pipe *old);
-extern void intel_release_load_detect_pipe(struct drm_connector *connector,
-                                          struct intel_load_detect_pipe *old);
-
-extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
-                                   u16 blue, int regno);
-extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
-                                   u16 *blue, int regno);
-
-extern int intel_pin_and_fence_fb_obj(struct drm_device *dev,
-                                     struct drm_i915_gem_object *obj,
-                                     struct intel_ring_buffer *pipelined);
-extern void intel_unpin_fb_obj(struct drm_i915_gem_object *obj);
-
-extern int intel_framebuffer_init(struct drm_device *dev,
-                                 struct intel_framebuffer *ifb,
-                                 struct drm_mode_fb_cmd2 *mode_cmd,
-                                 struct drm_i915_gem_object *obj);
-extern void intel_framebuffer_fini(struct intel_framebuffer *fb);
-extern int intel_fbdev_init(struct drm_device *dev);
-extern void intel_fbdev_initial_config(struct drm_device *dev);
-extern void intel_fbdev_fini(struct drm_device *dev);
-extern void intel_fbdev_set_suspend(struct drm_device *dev, int state);
-extern void intel_prepare_page_flip(struct drm_device *dev, int plane);
-extern void intel_finish_page_flip(struct drm_device *dev, int pipe);
-extern void intel_finish_page_flip_plane(struct drm_device *dev, int plane);
-
-extern void intel_setup_overlay(struct drm_device *dev);
-extern void intel_cleanup_overlay(struct drm_device *dev);
-extern int intel_overlay_switch_off(struct intel_overlay *overlay);
-extern int intel_overlay_put_image(struct drm_device *dev, void *data,
-                                  struct drm_file *file_priv);
-extern int intel_overlay_attrs(struct drm_device *dev, void *data,
-                              struct drm_file *file_priv);
-
-extern void intel_fb_output_poll_changed(struct drm_device *dev);
-extern void intel_fb_restore_mode(struct drm_device *dev);
-
-struct intel_shared_dpll *
-intel_crtc_to_shared_dpll(struct intel_crtc *crtc);
-
+enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv,
+                                            enum pipe pipe);
+void intel_wait_for_vblank(struct drm_device *dev, int pipe);
+void intel_wait_for_pipe_off(struct drm_device *dev, int pipe);
+int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp);
+void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port);
+bool intel_get_load_detect_pipe(struct drm_connector *connector,
+                               struct drm_display_mode *mode,
+                               struct intel_load_detect_pipe *old);
+void intel_release_load_detect_pipe(struct drm_connector *connector,
+                                   struct intel_load_detect_pipe *old);
+int intel_pin_and_fence_fb_obj(struct drm_device *dev,
+                              struct drm_i915_gem_object *obj,
+                              struct intel_ring_buffer *pipelined);
+void intel_unpin_fb_obj(struct drm_i915_gem_object *obj);
+int intel_framebuffer_init(struct drm_device *dev,
+                          struct intel_framebuffer *ifb,
+                          struct drm_mode_fb_cmd2 *mode_cmd,
+                          struct drm_i915_gem_object *obj);
+void intel_framebuffer_fini(struct intel_framebuffer *fb);
+void intel_prepare_page_flip(struct drm_device *dev, int plane);
+void intel_finish_page_flip(struct drm_device *dev, int pipe);
+void intel_finish_page_flip_plane(struct drm_device *dev, int plane);
+struct intel_shared_dpll *intel_crtc_to_shared_dpll(struct intel_crtc *crtc);
 void assert_shared_dpll(struct drm_i915_private *dev_priv,
                        struct intel_shared_dpll *pll,
                        bool state);
@@ -696,104 +670,199 @@ void assert_fdi_rx_pll(struct drm_i915_private *dev_priv,
                       enum pipe pipe, bool state);
 #define assert_fdi_rx_pll_enabled(d, p) assert_fdi_rx_pll(d, p, true)
 #define assert_fdi_rx_pll_disabled(d, p) assert_fdi_rx_pll(d, p, false)
-extern void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
-                       bool state);
+void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state);
 #define assert_pipe_enabled(d, p) assert_pipe(d, p, true)
 #define assert_pipe_disabled(d, p) assert_pipe(d, p, false)
+void intel_write_eld(struct drm_encoder *encoder,
+                    struct drm_display_mode *mode);
+unsigned long intel_gen4_compute_page_offset(int *x, int *y,
+                                            unsigned int tiling_mode,
+                                            unsigned int bpp,
+                                            unsigned int pitch);
+void intel_display_handle_reset(struct drm_device *dev);
+void hsw_enable_pc8_work(struct work_struct *__work);
+void hsw_enable_package_c8(struct drm_i915_private *dev_priv);
+void hsw_disable_package_c8(struct drm_i915_private *dev_priv);
+void intel_dp_get_m_n(struct intel_crtc *crtc,
+                     struct intel_crtc_config *pipe_config);
+int intel_dotclock_calculate(int link_freq, const struct intel_link_m_n *m_n);
+void
+ironlake_check_encoder_dotclock(const struct intel_crtc_config *pipe_config,
+                               int dotclock);
+bool intel_crtc_active(struct drm_crtc *crtc);
+void i915_disable_vga_mem(struct drm_device *dev);
+void hsw_enable_ips(struct intel_crtc *crtc);
+void hsw_disable_ips(struct intel_crtc *crtc);
+void intel_display_set_init_power(struct drm_device *dev, bool enable);
+
+
+/* intel_dp.c */
+void intel_dp_init(struct drm_device *dev, int output_reg, enum port port);
+bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
+                            struct intel_connector *intel_connector);
+void intel_dp_start_link_train(struct intel_dp *intel_dp);
+void intel_dp_complete_link_train(struct intel_dp *intel_dp);
+void intel_dp_stop_link_train(struct intel_dp *intel_dp);
+void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);
+void intel_dp_encoder_destroy(struct drm_encoder *encoder);
+void intel_dp_check_link_status(struct intel_dp *intel_dp);
+bool intel_dp_compute_config(struct intel_encoder *encoder,
+                            struct intel_crtc_config *pipe_config);
+bool intel_dpd_is_edp(struct drm_device *dev);
+void ironlake_edp_backlight_on(struct intel_dp *intel_dp);
+void ironlake_edp_backlight_off(struct intel_dp *intel_dp);
+void ironlake_edp_panel_on(struct intel_dp *intel_dp);
+void ironlake_edp_panel_off(struct intel_dp *intel_dp);
+void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp);
+void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
+void intel_edp_psr_enable(struct intel_dp *intel_dp);
+void intel_edp_psr_disable(struct intel_dp *intel_dp);
+void intel_edp_psr_update(struct drm_device *dev);
+
+
+/* intel_dsi.c */
+bool intel_dsi_init(struct drm_device *dev);
+
+
+/* intel_dvo.c */
+void intel_dvo_init(struct drm_device *dev);
+
+
+/* legacy fbdev emulation in intel_fbdev.c */
+#ifdef CONFIG_DRM_I915_FBDEV
+extern int intel_fbdev_init(struct drm_device *dev);
+extern void intel_fbdev_initial_config(struct drm_device *dev);
+extern void intel_fbdev_fini(struct drm_device *dev);
+extern void intel_fbdev_set_suspend(struct drm_device *dev, int state);
+extern void intel_fbdev_output_poll_changed(struct drm_device *dev);
+extern void intel_fbdev_restore_mode(struct drm_device *dev);
+#else
+static inline int intel_fbdev_init(struct drm_device *dev)
+{
+       return 0;
+}
 
-extern void intel_init_clock_gating(struct drm_device *dev);
-extern void intel_suspend_hw(struct drm_device *dev);
-extern void intel_write_eld(struct drm_encoder *encoder,
-                           struct drm_display_mode *mode);
-extern void intel_prepare_ddi(struct drm_device *dev);
-extern void hsw_fdi_link_train(struct drm_crtc *crtc);
-extern void intel_ddi_init(struct drm_device *dev, enum port port);
-
-/* For use by IVB LP watermark workaround in intel_sprite.c */
-extern void intel_update_watermarks(struct drm_device *dev);
-extern void intel_update_sprite_watermarks(struct drm_plane *plane,
-                                          struct drm_crtc *crtc,
-                                          uint32_t sprite_width, int pixel_size,
-                                          bool enabled, bool scaled);
-
-extern unsigned long intel_gen4_compute_page_offset(int *x, int *y,
-                                                   unsigned int tiling_mode,
-                                                   unsigned int bpp,
-                                                   unsigned int pitch);
-
-extern int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
-                                    struct drm_file *file_priv);
-extern int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
-                                    struct drm_file *file_priv);
-
-/* Power-related functions, located in intel_pm.c */
-extern void intel_init_pm(struct drm_device *dev);
-/* FBC */
-extern bool intel_fbc_enabled(struct drm_device *dev);
-extern void intel_update_fbc(struct drm_device *dev);
-/* IPS */
-extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv);
-extern void intel_gpu_ips_teardown(void);
-
-/* Power well */
-extern int i915_init_power_well(struct drm_device *dev);
-extern void i915_remove_power_well(struct drm_device *dev);
-
-extern bool intel_display_power_enabled(struct drm_device *dev,
-                                       enum intel_display_power_domain domain);
-extern void intel_init_power_well(struct drm_device *dev);
-extern void intel_set_power_well(struct drm_device *dev, bool enable);
-extern void intel_enable_gt_powersave(struct drm_device *dev);
-extern void intel_disable_gt_powersave(struct drm_device *dev);
-extern void ironlake_teardown_rc6(struct drm_device *dev);
+static inline void intel_fbdev_initial_config(struct drm_device *dev)
+{
+}
+
+static inline void intel_fbdev_fini(struct drm_device *dev)
+{
+}
+
+static inline void intel_fbdev_set_suspend(struct drm_device *dev, int state)
+{
+}
+
+static inline void intel_fbdev_restore_mode(struct drm_device *dev)
+{
+}
+#endif
+
+/* intel_hdmi.c */
+void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port);
+void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
+                              struct intel_connector *intel_connector);
+struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder);
+bool intel_hdmi_compute_config(struct intel_encoder *encoder,
+                              struct intel_crtc_config *pipe_config);
+
+
+/* intel_lvds.c */
+void intel_lvds_init(struct drm_device *dev);
+bool intel_is_dual_link_lvds(struct drm_device *dev);
+
+
+/* intel_modes.c */
+int intel_connector_update_modes(struct drm_connector *connector,
+                                struct edid *edid);
+int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
+void intel_attach_force_audio_property(struct drm_connector *connector);
+void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
+
+
+/* intel_overlay.c */
+void intel_setup_overlay(struct drm_device *dev);
+void intel_cleanup_overlay(struct drm_device *dev);
+int intel_overlay_switch_off(struct intel_overlay *overlay);
+int intel_overlay_put_image(struct drm_device *dev, void *data,
+                           struct drm_file *file_priv);
+int intel_overlay_attrs(struct drm_device *dev, void *data,
+                       struct drm_file *file_priv);
+
+
+/* intel_panel.c */
+int intel_panel_init(struct intel_panel *panel,
+                    struct drm_display_mode *fixed_mode);
+void intel_panel_fini(struct intel_panel *panel);
+void intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
+                           struct drm_display_mode *adjusted_mode);
+void intel_pch_panel_fitting(struct intel_crtc *crtc,
+                            struct intel_crtc_config *pipe_config,
+                            int fitting_mode);
+void intel_gmch_panel_fitting(struct intel_crtc *crtc,
+                             struct intel_crtc_config *pipe_config,
+                             int fitting_mode);
+void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
+                              u32 max);
+int intel_panel_setup_backlight(struct drm_connector *connector);
+void intel_panel_enable_backlight(struct intel_connector *connector);
+void intel_panel_disable_backlight(struct intel_connector *connector);
+void intel_panel_destroy_backlight(struct drm_device *dev);
+enum drm_connector_status intel_panel_detect(struct drm_device *dev);
+
+
+/* intel_pm.c */
+void intel_init_clock_gating(struct drm_device *dev);
+void intel_suspend_hw(struct drm_device *dev);
+void intel_update_watermarks(struct drm_crtc *crtc);
+void intel_update_sprite_watermarks(struct drm_plane *plane,
+                                   struct drm_crtc *crtc,
+                                   uint32_t sprite_width, int pixel_size,
+                                   bool enabled, bool scaled);
+void intel_init_pm(struct drm_device *dev);
+bool intel_fbc_enabled(struct drm_device *dev);
+void intel_update_fbc(struct drm_device *dev);
+void intel_gpu_ips_init(struct drm_i915_private *dev_priv);
+void intel_gpu_ips_teardown(void);
+int intel_power_domains_init(struct drm_device *dev);
+void intel_power_domains_remove(struct drm_device *dev);
+bool intel_display_power_enabled(struct drm_device *dev,
+                                enum intel_display_power_domain domain);
+void intel_display_power_get(struct drm_device *dev,
+                            enum intel_display_power_domain domain);
+void intel_display_power_put(struct drm_device *dev,
+                            enum intel_display_power_domain domain);
+void intel_power_domains_init_hw(struct drm_device *dev);
+void intel_set_power_well(struct drm_device *dev, bool enable);
+void intel_enable_gt_powersave(struct drm_device *dev);
+void intel_disable_gt_powersave(struct drm_device *dev);
+void ironlake_teardown_rc6(struct drm_device *dev);
 void gen6_update_ring_freq(struct drm_device *dev);
+void gen6_rps_idle(struct drm_i915_private *dev_priv);
+void gen6_rps_boost(struct drm_i915_private *dev_priv);
+void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv);
+void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv);
+void ilk_wm_get_hw_state(struct drm_device *dev);
+
+
+/* intel_sdvo.c */
+bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob);
+
+
+/* intel_sprite.c */
+int intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane);
+void intel_flush_primary_plane(struct drm_i915_private *dev_priv,
+                              enum plane plane);
+void intel_plane_restore(struct drm_plane *plane);
+void intel_plane_disable(struct drm_plane *plane);
+int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
+                             struct drm_file *file_priv);
+int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
+                             struct drm_file *file_priv);
+
 
-extern bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
-                                  enum pipe *pipe);
-extern int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv);
-extern void intel_ddi_pll_init(struct drm_device *dev);
-extern void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc);
-extern void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
-                                             enum transcoder cpu_transcoder);
-extern void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc);
-extern void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc);
-extern void intel_ddi_setup_hw_pll_state(struct drm_device *dev);
-extern bool intel_ddi_pll_mode_set(struct drm_crtc *crtc);
-extern void intel_ddi_put_crtc_pll(struct drm_crtc *crtc);
-extern void intel_ddi_set_pipe_settings(struct drm_crtc *crtc);
-extern void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
-extern bool
-intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
-extern void intel_ddi_fdi_disable(struct drm_crtc *crtc);
-extern void intel_ddi_get_config(struct intel_encoder *encoder,
-                                struct intel_crtc_config *pipe_config);
-
-extern void intel_display_handle_reset(struct drm_device *dev);
-extern bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
-                                                 enum pipe pipe,
-                                                 bool enable);
-extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
-                                                enum transcoder pch_transcoder,
-                                                bool enable);
-
-extern void intel_edp_psr_enable(struct intel_dp *intel_dp);
-extern void intel_edp_psr_disable(struct intel_dp *intel_dp);
-extern void intel_edp_psr_update(struct drm_device *dev);
-extern void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
-                             bool switch_to_fclk, bool allow_power_down);
-extern void hsw_restore_lcpll(struct drm_i915_private *dev_priv);
-extern void ilk_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
-extern void ilk_disable_gt_irq(struct drm_i915_private *dev_priv,
-                              uint32_t mask);
-extern void snb_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
-extern void snb_disable_pm_irq(struct drm_i915_private *dev_priv,
-                              uint32_t mask);
-extern void hsw_enable_pc8_work(struct work_struct *__work);
-extern void hsw_enable_package_c8(struct drm_i915_private *dev_priv);
-extern void hsw_disable_package_c8(struct drm_i915_private *dev_priv);
-extern void hsw_pc8_disable_interrupts(struct drm_device *dev);
-extern void hsw_pc8_restore_interrupts(struct drm_device *dev);
-extern void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv);
-extern void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv);
+/* intel_tv.c */
+void intel_tv_init(struct drm_device *dev);
 
 #endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
new file mode 100644 (file)
index 0000000..d257b09
--- /dev/null
@@ -0,0 +1,620 @@
+/*
+ * Copyright Â© 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Jani Nikula <jani.nikula@intel.com>
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include <drm/i915_drm.h>
+#include <linux/slab.h>
+#include "i915_drv.h"
+#include "intel_drv.h"
+#include "intel_dsi.h"
+#include "intel_dsi_cmd.h"
+
+/* the sub-encoders aka panel drivers */
+static const struct intel_dsi_device intel_dsi_devices[] = {
+};
+
+
+static void vlv_cck_modify(struct drm_i915_private *dev_priv, u32 reg, u32 val,
+                          u32 mask)
+{
+       u32 tmp = vlv_cck_read(dev_priv, reg);
+       tmp &= ~mask;
+       tmp |= val;
+       vlv_cck_write(dev_priv, reg, tmp);
+}
+
+static void band_gap_wa(struct drm_i915_private *dev_priv)
+{
+       mutex_lock(&dev_priv->dpio_lock);
+
+       /* Enable bandgap fix in GOP driver */
+       vlv_cck_modify(dev_priv, 0x6D, 0x00010000, 0x00030000);
+       msleep(20);
+       vlv_cck_modify(dev_priv, 0x6E, 0x00010000, 0x00030000);
+       msleep(20);
+       vlv_cck_modify(dev_priv, 0x6F, 0x00010000, 0x00030000);
+       msleep(20);
+       vlv_cck_modify(dev_priv, 0x00, 0x00008000, 0x00008000);
+       msleep(20);
+       vlv_cck_modify(dev_priv, 0x00, 0x00000000, 0x00008000);
+       msleep(20);
+
+       /* Turn Display Trunk on */
+       vlv_cck_modify(dev_priv, 0x6B, 0x00020000, 0x00030000);
+       msleep(20);
+
+       vlv_cck_modify(dev_priv, 0x6C, 0x00020000, 0x00030000);
+       msleep(20);
+
+       vlv_cck_modify(dev_priv, 0x6D, 0x00020000, 0x00030000);
+       msleep(20);
+       vlv_cck_modify(dev_priv, 0x6E, 0x00020000, 0x00030000);
+       msleep(20);
+       vlv_cck_modify(dev_priv, 0x6F, 0x00020000, 0x00030000);
+
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       /* Need huge delay, otherwise clock is not stable */
+       msleep(100);
+}
+
+static struct intel_dsi *intel_attached_dsi(struct drm_connector *connector)
+{
+       return container_of(intel_attached_encoder(connector),
+                           struct intel_dsi, base);
+}
+
+static inline bool is_vid_mode(struct intel_dsi *intel_dsi)
+{
+       return intel_dsi->dev.type == INTEL_DSI_VIDEO_MODE;
+}
+
+static inline bool is_cmd_mode(struct intel_dsi *intel_dsi)
+{
+       return intel_dsi->dev.type == INTEL_DSI_COMMAND_MODE;
+}
+
+static void intel_dsi_hot_plug(struct intel_encoder *encoder)
+{
+       DRM_DEBUG_KMS("\n");
+}
+
+static bool intel_dsi_compute_config(struct intel_encoder *encoder,
+                                    struct intel_crtc_config *config)
+{
+       struct intel_dsi *intel_dsi = container_of(encoder, struct intel_dsi,
+                                                  base);
+       struct intel_connector *intel_connector = intel_dsi->attached_connector;
+       struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
+       struct drm_display_mode *adjusted_mode = &config->adjusted_mode;
+       struct drm_display_mode *mode = &config->requested_mode;
+
+       DRM_DEBUG_KMS("\n");
+
+       if (fixed_mode)
+               intel_fixed_panel_mode(fixed_mode, adjusted_mode);
+
+       if (intel_dsi->dev.dev_ops->mode_fixup)
+               return intel_dsi->dev.dev_ops->mode_fixup(&intel_dsi->dev,
+                                                         mode, adjusted_mode);
+
+       return true;
+}
+
+static void intel_dsi_pre_pll_enable(struct intel_encoder *encoder)
+{
+       DRM_DEBUG_KMS("\n");
+
+       vlv_enable_dsi_pll(encoder);
+}
+
+static void intel_dsi_pre_enable(struct intel_encoder *encoder)
+{
+       DRM_DEBUG_KMS("\n");
+}
+
+static void intel_dsi_enable(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
+       struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+       int pipe = intel_crtc->pipe;
+       u32 temp;
+
+       DRM_DEBUG_KMS("\n");
+
+       temp = I915_READ(MIPI_DEVICE_READY(pipe));
+       if ((temp & DEVICE_READY) == 0) {
+               temp &= ~ULPS_STATE_MASK;
+               I915_WRITE(MIPI_DEVICE_READY(pipe), temp | DEVICE_READY);
+       } else if (temp & ULPS_STATE_MASK) {
+               temp &= ~ULPS_STATE_MASK;
+               I915_WRITE(MIPI_DEVICE_READY(pipe), temp | ULPS_STATE_EXIT);
+               /*
+                * We need to ensure that there is a minimum of 1 ms time
+                * available before clearing the UPLS exit state.
+                */
+               msleep(2);
+               I915_WRITE(MIPI_DEVICE_READY(pipe), temp);
+       }
+
+       if (is_cmd_mode(intel_dsi))
+               I915_WRITE(MIPI_MAX_RETURN_PKT_SIZE(pipe), 8 * 4);
+
+       if (is_vid_mode(intel_dsi)) {
+               msleep(20); /* XXX */
+               dpi_send_cmd(intel_dsi, TURN_ON);
+               msleep(100);
+
+               /* assert ip_tg_enable signal */
+               temp = I915_READ(MIPI_PORT_CTRL(pipe));
+               I915_WRITE(MIPI_PORT_CTRL(pipe), temp | DPI_ENABLE);
+               POSTING_READ(MIPI_PORT_CTRL(pipe));
+       }
+
+       intel_dsi->dev.dev_ops->enable(&intel_dsi->dev);
+}
+
+static void intel_dsi_disable(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
+       struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+       int pipe = intel_crtc->pipe;
+       u32 temp;
+
+       DRM_DEBUG_KMS("\n");
+
+       intel_dsi->dev.dev_ops->disable(&intel_dsi->dev);
+
+       if (is_vid_mode(intel_dsi)) {
+               dpi_send_cmd(intel_dsi, SHUTDOWN);
+               msleep(10);
+
+               /* de-assert ip_tg_enable signal */
+               temp = I915_READ(MIPI_PORT_CTRL(pipe));
+               I915_WRITE(MIPI_PORT_CTRL(pipe), temp & ~DPI_ENABLE);
+               POSTING_READ(MIPI_PORT_CTRL(pipe));
+
+               msleep(2);
+       }
+
+       temp = I915_READ(MIPI_DEVICE_READY(pipe));
+       if (temp & DEVICE_READY) {
+               temp &= ~DEVICE_READY;
+               temp &= ~ULPS_STATE_MASK;
+               I915_WRITE(MIPI_DEVICE_READY(pipe), temp);
+       }
+}
+
+static void intel_dsi_post_disable(struct intel_encoder *encoder)
+{
+       DRM_DEBUG_KMS("\n");
+
+       vlv_disable_dsi_pll(encoder);
+}
+
+static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
+                                  enum pipe *pipe)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       u32 port, func;
+       enum pipe p;
+
+       DRM_DEBUG_KMS("\n");
+
+       /* XXX: this only works for one DSI output */
+       for (p = PIPE_A; p <= PIPE_B; p++) {
+               port = I915_READ(MIPI_PORT_CTRL(p));
+               func = I915_READ(MIPI_DSI_FUNC_PRG(p));
+
+               if ((port & DPI_ENABLE) || (func & CMD_MODE_DATA_WIDTH_MASK)) {
+                       if (I915_READ(MIPI_DEVICE_READY(p)) & DEVICE_READY) {
+                               *pipe = p;
+                               return true;
+                       }
+               }
+       }
+
+       return false;
+}
+
+static void intel_dsi_get_config(struct intel_encoder *encoder,
+                                struct intel_crtc_config *pipe_config)
+{
+       DRM_DEBUG_KMS("\n");
+
+       /* XXX: read flags, set to adjusted_mode */
+}
+
+static int intel_dsi_mode_valid(struct drm_connector *connector,
+                               struct drm_display_mode *mode)
+{
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
+       struct intel_dsi *intel_dsi = intel_attached_dsi(connector);
+
+       DRM_DEBUG_KMS("\n");
+
+       if (mode->flags & DRM_MODE_FLAG_DBLSCAN) {
+               DRM_DEBUG_KMS("MODE_NO_DBLESCAN\n");
+               return MODE_NO_DBLESCAN;
+       }
+
+       if (fixed_mode) {
+               if (mode->hdisplay > fixed_mode->hdisplay)
+                       return MODE_PANEL;
+               if (mode->vdisplay > fixed_mode->vdisplay)
+                       return MODE_PANEL;
+       }
+
+       return intel_dsi->dev.dev_ops->mode_valid(&intel_dsi->dev, mode);
+}
+
+/* return txclkesc cycles in terms of divider and duration in us */
+static u16 txclkesc(u32 divider, unsigned int us)
+{
+       switch (divider) {
+       case ESCAPE_CLOCK_DIVIDER_1:
+       default:
+               return 20 * us;
+       case ESCAPE_CLOCK_DIVIDER_2:
+               return 10 * us;
+       case ESCAPE_CLOCK_DIVIDER_4:
+               return 5 * us;
+       }
+}
+
+/* return pixels in terms of txbyteclkhs */
+static u16 txbyteclkhs(u16 pixels, int bpp, int lane_count)
+{
+       return DIV_ROUND_UP(DIV_ROUND_UP(pixels * bpp, 8), lane_count);
+}
+
+static void set_dsi_timings(struct drm_encoder *encoder,
+                           const struct drm_display_mode *mode)
+{
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder);
+       int pipe = intel_crtc->pipe;
+       unsigned int bpp = intel_crtc->config.pipe_bpp;
+       unsigned int lane_count = intel_dsi->lane_count;
+
+       u16 hactive, hfp, hsync, hbp, vfp, vsync, vbp;
+
+       hactive = mode->hdisplay;
+       hfp = mode->hsync_start - mode->hdisplay;
+       hsync = mode->hsync_end - mode->hsync_start;
+       hbp = mode->htotal - mode->hsync_end;
+
+       vfp = mode->vsync_start - mode->vdisplay;
+       vsync = mode->vsync_end - mode->vsync_start;
+       vbp = mode->vtotal - mode->vsync_end;
+
+       /* horizontal values are in terms of high speed byte clock */
+       hactive = txbyteclkhs(hactive, bpp, lane_count);
+       hfp = txbyteclkhs(hfp, bpp, lane_count);
+       hsync = txbyteclkhs(hsync, bpp, lane_count);
+       hbp = txbyteclkhs(hbp, bpp, lane_count);
+
+       I915_WRITE(MIPI_HACTIVE_AREA_COUNT(pipe), hactive);
+       I915_WRITE(MIPI_HFP_COUNT(pipe), hfp);
+
+       /* meaningful for video mode non-burst sync pulse mode only, can be zero
+        * for non-burst sync events and burst modes */
+       I915_WRITE(MIPI_HSYNC_PADDING_COUNT(pipe), hsync);
+       I915_WRITE(MIPI_HBP_COUNT(pipe), hbp);
+
+       /* vertical values are in terms of lines */
+       I915_WRITE(MIPI_VFP_COUNT(pipe), vfp);
+       I915_WRITE(MIPI_VSYNC_PADDING_COUNT(pipe), vsync);
+       I915_WRITE(MIPI_VBP_COUNT(pipe), vbp);
+}
+
+static void intel_dsi_mode_set(struct intel_encoder *intel_encoder)
+{
+       struct drm_encoder *encoder = &intel_encoder->base;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder);
+       struct drm_display_mode *adjusted_mode =
+               &intel_crtc->config.adjusted_mode;
+       int pipe = intel_crtc->pipe;
+       unsigned int bpp = intel_crtc->config.pipe_bpp;
+       u32 val, tmp;
+
+       DRM_DEBUG_KMS("pipe %c\n", pipe_name(pipe));
+
+       /* Update the DSI PLL */
+       vlv_enable_dsi_pll(intel_encoder);
+
+       /* XXX: Location of the call */
+       band_gap_wa(dev_priv);
+
+       /* escape clock divider, 20MHz, shared for A and C. device ready must be
+        * off when doing this! txclkesc? */
+       tmp = I915_READ(MIPI_CTRL(0));
+       tmp &= ~ESCAPE_CLOCK_DIVIDER_MASK;
+       I915_WRITE(MIPI_CTRL(0), tmp | ESCAPE_CLOCK_DIVIDER_1);
+
+       /* read request priority is per pipe */
+       tmp = I915_READ(MIPI_CTRL(pipe));
+       tmp &= ~READ_REQUEST_PRIORITY_MASK;
+       I915_WRITE(MIPI_CTRL(pipe), tmp | READ_REQUEST_PRIORITY_HIGH);
+
+       /* XXX: why here, why like this? handling in irq handler?! */
+       I915_WRITE(MIPI_INTR_STAT(pipe), 0xffffffff);
+       I915_WRITE(MIPI_INTR_EN(pipe), 0xffffffff);
+
+       I915_WRITE(MIPI_DPHY_PARAM(pipe),
+                  0x3c << EXIT_ZERO_COUNT_SHIFT |
+                  0x1f << TRAIL_COUNT_SHIFT |
+                  0xc5 << CLK_ZERO_COUNT_SHIFT |
+                  0x1f << PREPARE_COUNT_SHIFT);
+
+       I915_WRITE(MIPI_DPI_RESOLUTION(pipe),
+                  adjusted_mode->vdisplay << VERTICAL_ADDRESS_SHIFT |
+                  adjusted_mode->hdisplay << HORIZONTAL_ADDRESS_SHIFT);
+
+       set_dsi_timings(encoder, adjusted_mode);
+
+       val = intel_dsi->lane_count << DATA_LANES_PRG_REG_SHIFT;
+       if (is_cmd_mode(intel_dsi)) {
+               val |= intel_dsi->channel << CMD_MODE_CHANNEL_NUMBER_SHIFT;
+               val |= CMD_MODE_DATA_WIDTH_8_BIT; /* XXX */
+       } else {
+               val |= intel_dsi->channel << VID_MODE_CHANNEL_NUMBER_SHIFT;
+
+               /* XXX: cross-check bpp vs. pixel format? */
+               val |= intel_dsi->pixel_format;
+       }
+       I915_WRITE(MIPI_DSI_FUNC_PRG(pipe), val);
+
+       /* timeouts for recovery. one frame IIUC. if counter expires, EOT and
+        * stop state. */
+
+       /*
+        * In burst mode, value greater than one DPI line Time in byte clock
+        * (txbyteclkhs) To timeout this timer 1+ of the above said value is
+        * recommended.
+        *
+        * In non-burst mode, Value greater than one DPI frame time in byte
+        * clock(txbyteclkhs) To timeout this timer 1+ of the above said value
+        * is recommended.
+        *
+        * In DBI only mode, value greater than one DBI frame time in byte
+        * clock(txbyteclkhs) To timeout this timer 1+ of the above said value
+        * is recommended.
+        */
+
+       if (is_vid_mode(intel_dsi) &&
+           intel_dsi->video_mode_format == VIDEO_MODE_BURST) {
+               I915_WRITE(MIPI_HS_TX_TIMEOUT(pipe),
+                          txbyteclkhs(adjusted_mode->htotal, bpp,
+                                      intel_dsi->lane_count) + 1);
+       } else {
+               I915_WRITE(MIPI_HS_TX_TIMEOUT(pipe),
+                          txbyteclkhs(adjusted_mode->vtotal *
+                                      adjusted_mode->htotal,
+                                      bpp, intel_dsi->lane_count) + 1);
+       }
+       I915_WRITE(MIPI_LP_RX_TIMEOUT(pipe), 8309); /* max */
+       I915_WRITE(MIPI_TURN_AROUND_TIMEOUT(pipe), 0x14); /* max */
+       I915_WRITE(MIPI_DEVICE_RESET_TIMER(pipe), 0xffff); /* max */
+
+       /* dphy stuff */
+
+       /* in terms of low power clock */
+       I915_WRITE(MIPI_INIT_COUNT(pipe), txclkesc(ESCAPE_CLOCK_DIVIDER_1, 100));
+
+       /* recovery disables */
+       I915_WRITE(MIPI_EOT_DISABLE(pipe), intel_dsi->eot_disable);
+
+       /* in terms of txbyteclkhs. actual high to low switch +
+        * MIPI_STOP_STATE_STALL * MIPI_LP_BYTECLK.
+        *
+        * XXX: write MIPI_STOP_STATE_STALL?
+        */
+       I915_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT(pipe), 0x46);
+
+       /* XXX: low power clock equivalence in terms of byte clock. the number
+        * of byte clocks occupied in one low power clock. based on txbyteclkhs
+        * and txclkesc. txclkesc time / txbyteclk time * (105 +
+        * MIPI_STOP_STATE_STALL) / 105.???
+        */
+       I915_WRITE(MIPI_LP_BYTECLK(pipe), 4);
+
+       /* the bw essential for transmitting 16 long packets containing 252
+        * bytes meant for dcs write memory command is programmed in this
+        * register in terms of byte clocks. based on dsi transfer rate and the
+        * number of lanes configured the time taken to transmit 16 long packets
+        * in a dsi stream varies. */
+       I915_WRITE(MIPI_DBI_BW_CTRL(pipe), 0x820);
+
+       I915_WRITE(MIPI_CLK_LANE_SWITCH_TIME_CNT(pipe),
+                  0xa << LP_HS_SSW_CNT_SHIFT |
+                  0x14 << HS_LP_PWR_SW_CNT_SHIFT);
+
+       if (is_vid_mode(intel_dsi))
+               I915_WRITE(MIPI_VIDEO_MODE_FORMAT(pipe),
+                          intel_dsi->video_mode_format);
+}
+
+static enum drm_connector_status
+intel_dsi_detect(struct drm_connector *connector, bool force)
+{
+       struct intel_dsi *intel_dsi = intel_attached_dsi(connector);
+       DRM_DEBUG_KMS("\n");
+       return intel_dsi->dev.dev_ops->detect(&intel_dsi->dev);
+}
+
+static int intel_dsi_get_modes(struct drm_connector *connector)
+{
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct drm_display_mode *mode;
+
+       DRM_DEBUG_KMS("\n");
+
+       if (!intel_connector->panel.fixed_mode) {
+               DRM_DEBUG_KMS("no fixed mode\n");
+               return 0;
+       }
+
+       mode = drm_mode_duplicate(connector->dev,
+                                 intel_connector->panel.fixed_mode);
+       if (!mode) {
+               DRM_DEBUG_KMS("drm_mode_duplicate failed\n");
+               return 0;
+       }
+
+       drm_mode_probed_add(connector, mode);
+       return 1;
+}
+
+static void intel_dsi_destroy(struct drm_connector *connector)
+{
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+
+       DRM_DEBUG_KMS("\n");
+       intel_panel_fini(&intel_connector->panel);
+       drm_connector_cleanup(connector);
+       kfree(connector);
+}
+
+static const struct drm_encoder_funcs intel_dsi_funcs = {
+       .destroy = intel_encoder_destroy,
+};
+
+static const struct drm_connector_helper_funcs intel_dsi_connector_helper_funcs = {
+       .get_modes = intel_dsi_get_modes,
+       .mode_valid = intel_dsi_mode_valid,
+       .best_encoder = intel_best_encoder,
+};
+
+static const struct drm_connector_funcs intel_dsi_connector_funcs = {
+       .dpms = intel_connector_dpms,
+       .detect = intel_dsi_detect,
+       .destroy = intel_dsi_destroy,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+};
+
+bool intel_dsi_init(struct drm_device *dev)
+{
+       struct intel_dsi *intel_dsi;
+       struct intel_encoder *intel_encoder;
+       struct drm_encoder *encoder;
+       struct intel_connector *intel_connector;
+       struct drm_connector *connector;
+       struct drm_display_mode *fixed_mode = NULL;
+       const struct intel_dsi_device *dsi;
+       unsigned int i;
+
+       DRM_DEBUG_KMS("\n");
+
+       intel_dsi = kzalloc(sizeof(*intel_dsi), GFP_KERNEL);
+       if (!intel_dsi)
+               return false;
+
+       intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
+       if (!intel_connector) {
+               kfree(intel_dsi);
+               return false;
+       }
+
+       intel_encoder = &intel_dsi->base;
+       encoder = &intel_encoder->base;
+       intel_dsi->attached_connector = intel_connector;
+
+       connector = &intel_connector->base;
+
+       drm_encoder_init(dev, encoder, &intel_dsi_funcs, DRM_MODE_ENCODER_DSI);
+
+       /* XXX: very likely not all of these are needed */
+       intel_encoder->hot_plug = intel_dsi_hot_plug;
+       intel_encoder->compute_config = intel_dsi_compute_config;
+       intel_encoder->pre_pll_enable = intel_dsi_pre_pll_enable;
+       intel_encoder->pre_enable = intel_dsi_pre_enable;
+       intel_encoder->enable = intel_dsi_enable;
+       intel_encoder->mode_set = intel_dsi_mode_set;
+       intel_encoder->disable = intel_dsi_disable;
+       intel_encoder->post_disable = intel_dsi_post_disable;
+       intel_encoder->get_hw_state = intel_dsi_get_hw_state;
+       intel_encoder->get_config = intel_dsi_get_config;
+
+       intel_connector->get_hw_state = intel_connector_get_hw_state;
+
+       for (i = 0; i < ARRAY_SIZE(intel_dsi_devices); i++) {
+               dsi = &intel_dsi_devices[i];
+               intel_dsi->dev = *dsi;
+
+               if (dsi->dev_ops->init(&intel_dsi->dev))
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(intel_dsi_devices)) {
+               DRM_DEBUG_KMS("no device found\n");
+               goto err;
+       }
+
+       intel_encoder->type = INTEL_OUTPUT_DSI;
+       intel_encoder->crtc_mask = (1 << 0); /* XXX */
+
+       intel_encoder->cloneable = false;
+       drm_connector_init(dev, connector, &intel_dsi_connector_funcs,
+                          DRM_MODE_CONNECTOR_DSI);
+
+       drm_connector_helper_add(connector, &intel_dsi_connector_helper_funcs);
+
+       connector->display_info.subpixel_order = SubPixelHorizontalRGB; /*XXX*/
+       connector->interlace_allowed = false;
+       connector->doublescan_allowed = false;
+
+       intel_connector_attach_encoder(intel_connector, intel_encoder);
+
+       drm_sysfs_connector_add(connector);
+
+       fixed_mode = dsi->dev_ops->get_modes(&intel_dsi->dev);
+       if (!fixed_mode) {
+               DRM_DEBUG_KMS("no fixed mode\n");
+               goto err;
+       }
+
+       fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
+       intel_panel_init(&intel_connector->panel, fixed_mode);
+
+       return true;
+
+err:
+       drm_encoder_cleanup(&intel_encoder->base);
+       kfree(intel_dsi);
+       kfree(intel_connector);
+
+       return false;
+}
diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h
new file mode 100644 (file)
index 0000000..c7765f3
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright Â© 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _INTEL_DSI_H
+#define _INTEL_DSI_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include "intel_drv.h"
+
+struct intel_dsi_device {
+       unsigned int panel_id;
+       const char *name;
+       int type;
+       const struct intel_dsi_dev_ops *dev_ops;
+       void *dev_priv;
+};
+
+struct intel_dsi_dev_ops {
+       bool (*init)(struct intel_dsi_device *dsi);
+
+       /* This callback must be able to assume DSI commands can be sent */
+       void (*enable)(struct intel_dsi_device *dsi);
+
+       /* This callback must be able to assume DSI commands can be sent */
+       void (*disable)(struct intel_dsi_device *dsi);
+
+       int (*mode_valid)(struct intel_dsi_device *dsi,
+                         struct drm_display_mode *mode);
+
+       bool (*mode_fixup)(struct intel_dsi_device *dsi,
+                          const struct drm_display_mode *mode,
+                          struct drm_display_mode *adjusted_mode);
+
+       void (*mode_set)(struct intel_dsi_device *dsi,
+                        struct drm_display_mode *mode,
+                        struct drm_display_mode *adjusted_mode);
+
+       enum drm_connector_status (*detect)(struct intel_dsi_device *dsi);
+
+       bool (*get_hw_state)(struct intel_dsi_device *dev);
+
+       struct drm_display_mode *(*get_modes)(struct intel_dsi_device *dsi);
+
+       void (*destroy) (struct intel_dsi_device *dsi);
+};
+
+struct intel_dsi {
+       struct intel_encoder base;
+
+       struct intel_dsi_device dev;
+
+       struct intel_connector *attached_connector;
+
+       /* if true, use HS mode, otherwise LP */
+       bool hs;
+
+       /* virtual channel */
+       int channel;
+
+       /* number of DSI lanes */
+       unsigned int lane_count;
+
+       /* video mode pixel format for MIPI_DSI_FUNC_PRG register */
+       u32 pixel_format;
+
+       /* video mode format for MIPI_VIDEO_MODE_FORMAT register */
+       u32 video_mode_format;
+
+       /* eot for MIPI_EOT_DISABLE register */
+       u32 eot_disable;
+};
+
+static inline struct intel_dsi *enc_to_intel_dsi(struct drm_encoder *encoder)
+{
+       return container_of(encoder, struct intel_dsi, base.base);
+}
+
+extern void vlv_enable_dsi_pll(struct intel_encoder *encoder);
+extern void vlv_disable_dsi_pll(struct intel_encoder *encoder);
+
+#endif /* _INTEL_DSI_H */
diff --git a/drivers/gpu/drm/i915/intel_dsi_cmd.c b/drivers/gpu/drm/i915/intel_dsi_cmd.c
new file mode 100644 (file)
index 0000000..7c40f98
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * Copyright Â© 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Jani Nikula <jani.nikula@intel.com>
+ */
+
+#include <linux/export.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <video/mipi_display.h>
+#include "i915_drv.h"
+#include "intel_drv.h"
+#include "intel_dsi.h"
+#include "intel_dsi_cmd.h"
+
+/*
+ * XXX: MIPI_DATA_ADDRESS, MIPI_DATA_LENGTH, MIPI_COMMAND_LENGTH, and
+ * MIPI_COMMAND_ADDRESS registers.
+ *
+ * Apparently these registers provide a MIPI adapter level way to send (lots of)
+ * commands and data to the receiver, without having to write the commands and
+ * data to MIPI_{HS,LP}_GEN_{CTRL,DATA} registers word by word.
+ *
+ * Presumably for anything other than MIPI_DCS_WRITE_MEMORY_START and
+ * MIPI_DCS_WRITE_MEMORY_CONTINUE (which are used to update the external
+ * framebuffer in command mode displays) these are just an optimization that can
+ * come later.
+ *
+ * For memory writes, these should probably be used for performance.
+ */
+
+static void print_stat(struct intel_dsi *intel_dsi)
+{
+       struct drm_encoder *encoder = &intel_dsi->base.base;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       enum pipe pipe = intel_crtc->pipe;
+       u32 val;
+
+       val = I915_READ(MIPI_INTR_STAT(pipe));
+
+#define STAT_BIT(val, bit) (val) & (bit) ? " " #bit : ""
+       DRM_DEBUG_KMS("MIPI_INTR_STAT(%d) = %08x"
+                     "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
+                     "\n", pipe, val,
+                     STAT_BIT(val, TEARING_EFFECT),
+                     STAT_BIT(val, SPL_PKT_SENT_INTERRUPT),
+                     STAT_BIT(val, GEN_READ_DATA_AVAIL),
+                     STAT_BIT(val, LP_GENERIC_WR_FIFO_FULL),
+                     STAT_BIT(val, HS_GENERIC_WR_FIFO_FULL),
+                     STAT_BIT(val, RX_PROT_VIOLATION),
+                     STAT_BIT(val, RX_INVALID_TX_LENGTH),
+                     STAT_BIT(val, ACK_WITH_NO_ERROR),
+                     STAT_BIT(val, TURN_AROUND_ACK_TIMEOUT),
+                     STAT_BIT(val, LP_RX_TIMEOUT),
+                     STAT_BIT(val, HS_TX_TIMEOUT),
+                     STAT_BIT(val, DPI_FIFO_UNDERRUN),
+                     STAT_BIT(val, LOW_CONTENTION),
+                     STAT_BIT(val, HIGH_CONTENTION),
+                     STAT_BIT(val, TXDSI_VC_ID_INVALID),
+                     STAT_BIT(val, TXDSI_DATA_TYPE_NOT_RECOGNISED),
+                     STAT_BIT(val, TXCHECKSUM_ERROR),
+                     STAT_BIT(val, TXECC_MULTIBIT_ERROR),
+                     STAT_BIT(val, TXECC_SINGLE_BIT_ERROR),
+                     STAT_BIT(val, TXFALSE_CONTROL_ERROR),
+                     STAT_BIT(val, RXDSI_VC_ID_INVALID),
+                     STAT_BIT(val, RXDSI_DATA_TYPE_NOT_REGOGNISED),
+                     STAT_BIT(val, RXCHECKSUM_ERROR),
+                     STAT_BIT(val, RXECC_MULTIBIT_ERROR),
+                     STAT_BIT(val, RXECC_SINGLE_BIT_ERROR),
+                     STAT_BIT(val, RXFALSE_CONTROL_ERROR),
+                     STAT_BIT(val, RXHS_RECEIVE_TIMEOUT_ERROR),
+                     STAT_BIT(val, RX_LP_TX_SYNC_ERROR),
+                     STAT_BIT(val, RXEXCAPE_MODE_ENTRY_ERROR),
+                     STAT_BIT(val, RXEOT_SYNC_ERROR),
+                     STAT_BIT(val, RXSOT_SYNC_ERROR),
+                     STAT_BIT(val, RXSOT_ERROR));
+#undef STAT_BIT
+}
+
+enum dsi_type {
+       DSI_DCS,
+       DSI_GENERIC,
+};
+
+/* enable or disable command mode hs transmissions */
+void dsi_hs_mode_enable(struct intel_dsi *intel_dsi, bool enable)
+{
+       struct drm_encoder *encoder = &intel_dsi->base.base;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       enum pipe pipe = intel_crtc->pipe;
+       u32 temp;
+       u32 mask = DBI_FIFO_EMPTY;
+
+       if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(pipe)) & mask) == mask, 50))
+               DRM_ERROR("Timeout waiting for DBI FIFO empty\n");
+
+       temp = I915_READ(MIPI_HS_LP_DBI_ENABLE(pipe));
+       temp &= DBI_HS_LP_MODE_MASK;
+       I915_WRITE(MIPI_HS_LP_DBI_ENABLE(pipe), enable ? DBI_HS_MODE : DBI_LP_MODE);
+
+       intel_dsi->hs = enable;
+}
+
+static int dsi_vc_send_short(struct intel_dsi *intel_dsi, int channel,
+                            u8 data_type, u16 data)
+{
+       struct drm_encoder *encoder = &intel_dsi->base.base;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       enum pipe pipe = intel_crtc->pipe;
+       u32 ctrl_reg;
+       u32 ctrl;
+       u32 mask;
+
+       DRM_DEBUG_KMS("channel %d, data_type %d, data %04x\n",
+                     channel, data_type, data);
+
+       if (intel_dsi->hs) {
+               ctrl_reg = MIPI_HS_GEN_CTRL(pipe);
+               mask = HS_CTRL_FIFO_FULL;
+       } else {
+               ctrl_reg = MIPI_LP_GEN_CTRL(pipe);
+               mask = LP_CTRL_FIFO_FULL;
+       }
+
+       if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(pipe)) & mask) == 0, 50)) {
+               DRM_ERROR("Timeout waiting for HS/LP CTRL FIFO !full\n");
+               print_stat(intel_dsi);
+       }
+
+       /*
+        * Note: This function is also used for long packets, with length passed
+        * as data, since SHORT_PACKET_PARAM_SHIFT ==
+        * LONG_PACKET_WORD_COUNT_SHIFT.
+        */
+       ctrl = data << SHORT_PACKET_PARAM_SHIFT |
+               channel << VIRTUAL_CHANNEL_SHIFT |
+               data_type << DATA_TYPE_SHIFT;
+
+       I915_WRITE(ctrl_reg, ctrl);
+
+       return 0;
+}
+
+static int dsi_vc_send_long(struct intel_dsi *intel_dsi, int channel,
+                           u8 data_type, const u8 *data, int len)
+{
+       struct drm_encoder *encoder = &intel_dsi->base.base;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       enum pipe pipe = intel_crtc->pipe;
+       u32 data_reg;
+       int i, j, n;
+       u32 mask;
+
+       DRM_DEBUG_KMS("channel %d, data_type %d, len %04x\n",
+                     channel, data_type, len);
+
+       if (intel_dsi->hs) {
+               data_reg = MIPI_HS_GEN_DATA(pipe);
+               mask = HS_DATA_FIFO_FULL;
+       } else {
+               data_reg = MIPI_LP_GEN_DATA(pipe);
+               mask = LP_DATA_FIFO_FULL;
+       }
+
+       if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(pipe)) & mask) == 0, 50))
+               DRM_ERROR("Timeout waiting for HS/LP DATA FIFO !full\n");
+
+       for (i = 0; i < len; i += n) {
+               u32 val = 0;
+               n = min_t(int, len - i, 4);
+
+               for (j = 0; j < n; j++)
+                       val |= *data++ << 8 * j;
+
+               I915_WRITE(data_reg, val);
+               /* XXX: check for data fifo full, once that is set, write 4
+                * dwords, then wait for not set, then continue. */
+       }
+
+       return dsi_vc_send_short(intel_dsi, channel, data_type, len);
+}
+
+static int dsi_vc_write_common(struct intel_dsi *intel_dsi,
+                              int channel, const u8 *data, int len,
+                              enum dsi_type type)
+{
+       int ret;
+
+       if (len == 0) {
+               BUG_ON(type == DSI_GENERIC);
+               ret = dsi_vc_send_short(intel_dsi, channel,
+                                       MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM,
+                                       0);
+       } else if (len == 1) {
+               ret = dsi_vc_send_short(intel_dsi, channel,
+                                       type == DSI_GENERIC ?
+                                       MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM :
+                                       MIPI_DSI_DCS_SHORT_WRITE, data[0]);
+       } else if (len == 2) {
+               ret = dsi_vc_send_short(intel_dsi, channel,
+                                       type == DSI_GENERIC ?
+                                       MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM :
+                                       MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+                                       (data[1] << 8) | data[0]);
+       } else {
+               ret = dsi_vc_send_long(intel_dsi, channel,
+                                      type == DSI_GENERIC ?
+                                      MIPI_DSI_GENERIC_LONG_WRITE :
+                                      MIPI_DSI_DCS_LONG_WRITE, data, len);
+       }
+
+       return ret;
+}
+
+int dsi_vc_dcs_write(struct intel_dsi *intel_dsi, int channel,
+                    const u8 *data, int len)
+{
+       return dsi_vc_write_common(intel_dsi, channel, data, len, DSI_DCS);
+}
+
+int dsi_vc_generic_write(struct intel_dsi *intel_dsi, int channel,
+                        const u8 *data, int len)
+{
+       return dsi_vc_write_common(intel_dsi, channel, data, len, DSI_GENERIC);
+}
+
+static int dsi_vc_dcs_send_read_request(struct intel_dsi *intel_dsi,
+                                       int channel, u8 dcs_cmd)
+{
+       return dsi_vc_send_short(intel_dsi, channel, MIPI_DSI_DCS_READ,
+                                dcs_cmd);
+}
+
+static int dsi_vc_generic_send_read_request(struct intel_dsi *intel_dsi,
+                                           int channel, u8 *reqdata,
+                                           int reqlen)
+{
+       u16 data;
+       u8 data_type;
+
+       switch (reqlen) {
+       case 0:
+               data_type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
+               data = 0;
+               break;
+       case 1:
+               data_type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
+               data = reqdata[0];
+               break;
+       case 2:
+               data_type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
+               data = (reqdata[1] << 8) | reqdata[0];
+               break;
+       default:
+               BUG();
+       }
+
+       return dsi_vc_send_short(intel_dsi, channel, data_type, data);
+}
+
+static int dsi_read_data_return(struct intel_dsi *intel_dsi,
+                               u8 *buf, int buflen)
+{
+       struct drm_encoder *encoder = &intel_dsi->base.base;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       enum pipe pipe = intel_crtc->pipe;
+       int i, len = 0;
+       u32 data_reg, val;
+
+       if (intel_dsi->hs) {
+               data_reg = MIPI_HS_GEN_DATA(pipe);
+       } else {
+               data_reg = MIPI_LP_GEN_DATA(pipe);
+       }
+
+       while (len < buflen) {
+               val = I915_READ(data_reg);
+               for (i = 0; i < 4 && len < buflen; i++, len++)
+                       buf[len] = val >> 8 * i;
+       }
+
+       return len;
+}
+
+int dsi_vc_dcs_read(struct intel_dsi *intel_dsi, int channel, u8 dcs_cmd,
+                   u8 *buf, int buflen)
+{
+       struct drm_encoder *encoder = &intel_dsi->base.base;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       enum pipe pipe = intel_crtc->pipe;
+       u32 mask;
+       int ret;
+
+       /*
+        * XXX: should issue multiple read requests and reads if request is
+        * longer than MIPI_MAX_RETURN_PKT_SIZE
+        */
+
+       I915_WRITE(MIPI_INTR_STAT(pipe), GEN_READ_DATA_AVAIL);
+
+       ret = dsi_vc_dcs_send_read_request(intel_dsi, channel, dcs_cmd);
+       if (ret)
+               return ret;
+
+       mask = GEN_READ_DATA_AVAIL;
+       if (wait_for((I915_READ(MIPI_INTR_STAT(pipe)) & mask) == mask, 50))
+               DRM_ERROR("Timeout waiting for read data.\n");
+
+       ret = dsi_read_data_return(intel_dsi, buf, buflen);
+       if (ret < 0)
+               return ret;
+
+       if (ret != buflen)
+               return -EIO;
+
+       return 0;
+}
+
+int dsi_vc_generic_read(struct intel_dsi *intel_dsi, int channel,
+                       u8 *reqdata, int reqlen, u8 *buf, int buflen)
+{
+       struct drm_encoder *encoder = &intel_dsi->base.base;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       enum pipe pipe = intel_crtc->pipe;
+       u32 mask;
+       int ret;
+
+       /*
+        * XXX: should issue multiple read requests and reads if request is
+        * longer than MIPI_MAX_RETURN_PKT_SIZE
+        */
+
+       I915_WRITE(MIPI_INTR_STAT(pipe), GEN_READ_DATA_AVAIL);
+
+       ret = dsi_vc_generic_send_read_request(intel_dsi, channel, reqdata,
+                                              reqlen);
+       if (ret)
+               return ret;
+
+       mask = GEN_READ_DATA_AVAIL;
+       if (wait_for((I915_READ(MIPI_INTR_STAT(pipe)) & mask) == mask, 50))
+               DRM_ERROR("Timeout waiting for read data.\n");
+
+       ret = dsi_read_data_return(intel_dsi, buf, buflen);
+       if (ret < 0)
+               return ret;
+
+       if (ret != buflen)
+               return -EIO;
+
+       return 0;
+}
+
+/*
+ * send a video mode command
+ *
+ * XXX: commands with data in MIPI_DPI_DATA?
+ */
+int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd)
+{
+       struct drm_encoder *encoder = &intel_dsi->base.base;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       enum pipe pipe = intel_crtc->pipe;
+       u32 mask;
+
+       /* XXX: pipe, hs */
+       if (intel_dsi->hs)
+               cmd &= ~DPI_LP_MODE;
+       else
+               cmd |= DPI_LP_MODE;
+
+       /* DPI virtual channel?! */
+
+       mask = DPI_FIFO_EMPTY;
+       if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(pipe)) & mask) == mask, 50))
+               DRM_ERROR("Timeout waiting for DPI FIFO empty.\n");
+
+       /* clear bit */
+       I915_WRITE(MIPI_INTR_STAT(pipe), SPL_PKT_SENT_INTERRUPT);
+
+       /* XXX: old code skips write if control unchanged */
+       if (cmd == I915_READ(MIPI_DPI_CONTROL(pipe)))
+               DRM_ERROR("Same special packet %02x twice in a row.\n", cmd);
+
+       I915_WRITE(MIPI_DPI_CONTROL(pipe), cmd);
+
+       mask = SPL_PKT_SENT_INTERRUPT;
+       if (wait_for((I915_READ(MIPI_INTR_STAT(pipe)) & mask) == mask, 100))
+               DRM_ERROR("Video mode command 0x%08x send failed.\n", cmd);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/i915/intel_dsi_cmd.h b/drivers/gpu/drm/i915/intel_dsi_cmd.h
new file mode 100644 (file)
index 0000000..54c8a23
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright Â© 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Jani Nikula <jani.nikula@intel.com>
+ */
+
+#ifndef _INTEL_DSI_DSI_H
+#define _INTEL_DSI_DSI_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <video/mipi_display.h>
+#include "i915_drv.h"
+#include "intel_drv.h"
+#include "intel_dsi.h"
+
+void dsi_hs_mode_enable(struct intel_dsi *intel_dsi, bool enable);
+
+int dsi_vc_dcs_write(struct intel_dsi *intel_dsi, int channel,
+                    const u8 *data, int len);
+
+int dsi_vc_generic_write(struct intel_dsi *intel_dsi, int channel,
+                        const u8 *data, int len);
+
+int dsi_vc_dcs_read(struct intel_dsi *intel_dsi, int channel, u8 dcs_cmd,
+                   u8 *buf, int buflen);
+
+int dsi_vc_generic_read(struct intel_dsi *intel_dsi, int channel,
+                       u8 *reqdata, int reqlen, u8 *buf, int buflen);
+
+int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd);
+
+/* XXX: questionable write helpers */
+static inline int dsi_vc_dcs_write_0(struct intel_dsi *intel_dsi,
+                                    int channel, u8 dcs_cmd)
+{
+       return dsi_vc_dcs_write(intel_dsi, channel, &dcs_cmd, 1);
+}
+
+static inline int dsi_vc_dcs_write_1(struct intel_dsi *intel_dsi,
+                                    int channel, u8 dcs_cmd, u8 param)
+{
+       u8 buf[2] = { dcs_cmd, param };
+       return dsi_vc_dcs_write(intel_dsi, channel, buf, 2);
+}
+
+static inline int dsi_vc_generic_write_0(struct intel_dsi *intel_dsi,
+                                        int channel)
+{
+       return dsi_vc_generic_write(intel_dsi, channel, NULL, 0);
+}
+
+static inline int dsi_vc_generic_write_1(struct intel_dsi *intel_dsi,
+                                        int channel, u8 param)
+{
+       return dsi_vc_generic_write(intel_dsi, channel, &param, 1);
+}
+
+static inline int dsi_vc_generic_write_2(struct intel_dsi *intel_dsi,
+                                        int channel, u8 param1, u8 param2)
+{
+       u8 buf[2] = { param1, param2 };
+       return dsi_vc_generic_write(intel_dsi, channel, buf, 2);
+}
+
+/* XXX: questionable read helpers */
+static inline int dsi_vc_generic_read_0(struct intel_dsi *intel_dsi,
+                                       int channel, u8 *buf, int buflen)
+{
+       return dsi_vc_generic_read(intel_dsi, channel, NULL, 0, buf, buflen);
+}
+
+static inline int dsi_vc_generic_read_1(struct intel_dsi *intel_dsi,
+                                       int channel, u8 param, u8 *buf,
+                                       int buflen)
+{
+       return dsi_vc_generic_read(intel_dsi, channel, &param, 1, buf, buflen);
+}
+
+static inline int dsi_vc_generic_read_2(struct intel_dsi *intel_dsi,
+                                       int channel, u8 param1, u8 param2,
+                                       u8 *buf, int buflen)
+{
+       u8 req[2] = { param1, param2 };
+
+       return dsi_vc_generic_read(intel_dsi, channel, req, 2, buf, buflen);
+}
+
+
+#endif /* _INTEL_DSI_DSI_H */
diff --git a/drivers/gpu/drm/i915/intel_dsi_pll.c b/drivers/gpu/drm/i915/intel_dsi_pll.c
new file mode 100644 (file)
index 0000000..44279b2
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * Copyright Â© 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *     Shobhit Kumar <shobhit.kumar@intel.com>
+ *     Yogesh Mohan Marimuthu <yogesh.mohan.marimuthu@intel.com>
+ */
+
+#include <linux/kernel.h>
+#include "intel_drv.h"
+#include "i915_drv.h"
+#include "intel_dsi.h"
+
+#define DSI_HSS_PACKET_SIZE            4
+#define DSI_HSE_PACKET_SIZE            4
+#define DSI_HSA_PACKET_EXTRA_SIZE      6
+#define DSI_HBP_PACKET_EXTRA_SIZE      6
+#define DSI_HACTIVE_PACKET_EXTRA_SIZE  6
+#define DSI_HFP_PACKET_EXTRA_SIZE      6
+#define DSI_EOTP_PACKET_SIZE           4
+
+struct dsi_mnp {
+       u32 dsi_pll_ctrl;
+       u32 dsi_pll_div;
+};
+
+static const u32 lfsr_converts[] = {
+       426, 469, 234, 373, 442, 221, 110, 311, 411,            /* 62 - 70 */
+       461, 486, 243, 377, 188, 350, 175, 343, 427, 213,       /* 71 - 80 */
+       106, 53, 282, 397, 354, 227, 113, 56, 284, 142,         /* 81 - 90 */
+       71, 35                                                  /* 91 - 92 */
+};
+
+static u32 dsi_rr_formula(const struct drm_display_mode *mode,
+                         int pixel_format, int video_mode_format,
+                         int lane_count, bool eotp)
+{
+       u32 bpp;
+       u32 hactive, vactive, hfp, hsync, hbp, vfp, vsync, vbp;
+       u32 hsync_bytes, hbp_bytes, hactive_bytes, hfp_bytes;
+       u32 bytes_per_line, bytes_per_frame;
+       u32 num_frames;
+       u32 bytes_per_x_frames, bytes_per_x_frames_x_lanes;
+       u32 dsi_bit_clock_hz;
+       u32 dsi_clk;
+
+       switch (pixel_format) {
+       default:
+       case VID_MODE_FORMAT_RGB888:
+       case VID_MODE_FORMAT_RGB666_LOOSE:
+               bpp = 24;
+               break;
+       case VID_MODE_FORMAT_RGB666:
+               bpp = 18;
+               break;
+       case VID_MODE_FORMAT_RGB565:
+               bpp = 16;
+               break;
+       }
+
+       hactive = mode->hdisplay;
+       vactive = mode->vdisplay;
+       hfp = mode->hsync_start - mode->hdisplay;
+       hsync = mode->hsync_end - mode->hsync_start;
+       hbp = mode->htotal - mode->hsync_end;
+
+       vfp = mode->vsync_start - mode->vdisplay;
+       vsync = mode->vsync_end - mode->vsync_start;
+       vbp = mode->vtotal - mode->vsync_end;
+
+       hsync_bytes = DIV_ROUND_UP(hsync * bpp, 8);
+       hbp_bytes = DIV_ROUND_UP(hbp * bpp, 8);
+       hactive_bytes = DIV_ROUND_UP(hactive * bpp, 8);
+       hfp_bytes = DIV_ROUND_UP(hfp * bpp, 8);
+
+       bytes_per_line = DSI_HSS_PACKET_SIZE + hsync_bytes +
+               DSI_HSA_PACKET_EXTRA_SIZE + DSI_HSE_PACKET_SIZE +
+               hbp_bytes + DSI_HBP_PACKET_EXTRA_SIZE +
+               hactive_bytes + DSI_HACTIVE_PACKET_EXTRA_SIZE +
+               hfp_bytes + DSI_HFP_PACKET_EXTRA_SIZE;
+
+       /*
+        * XXX: Need to accurately calculate LP to HS transition timeout and add
+        * it to bytes_per_line/bytes_per_frame.
+        */
+
+       if (eotp && video_mode_format == VIDEO_MODE_BURST)
+               bytes_per_line += DSI_EOTP_PACKET_SIZE;
+
+       bytes_per_frame = vsync * bytes_per_line + vbp * bytes_per_line +
+               vactive * bytes_per_line + vfp * bytes_per_line;
+
+       if (eotp &&
+           (video_mode_format == VIDEO_MODE_NON_BURST_WITH_SYNC_PULSE ||
+            video_mode_format == VIDEO_MODE_NON_BURST_WITH_SYNC_EVENTS))
+               bytes_per_frame += DSI_EOTP_PACKET_SIZE;
+
+       num_frames = drm_mode_vrefresh(mode);
+       bytes_per_x_frames = num_frames * bytes_per_frame;
+
+       bytes_per_x_frames_x_lanes = bytes_per_x_frames / lane_count;
+
+       /* the dsi clock is divided by 2 in the hardware to get dsi ddr clock */
+       dsi_bit_clock_hz = bytes_per_x_frames_x_lanes * 8;
+       dsi_clk = dsi_bit_clock_hz / (1000 * 1000);
+
+       if (eotp && video_mode_format == VIDEO_MODE_BURST)
+               dsi_clk *= 2;
+
+       return dsi_clk;
+}
+
+#ifdef MNP_FROM_TABLE
+
+struct dsi_clock_table {
+       u32 freq;
+       u8 m;
+       u8 p;
+};
+
+static const struct dsi_clock_table dsi_clk_tbl[] = {
+       {300, 72, 6}, {313, 75, 6}, {323, 78, 6}, {333, 80, 6},
+       {343, 82, 6}, {353, 85, 6}, {363, 87, 6}, {373, 90, 6},
+       {383, 92, 6}, {390, 78, 5}, {393, 79, 5}, {400, 80, 5},
+       {401, 80, 5}, {402, 80, 5}, {403, 81, 5}, {404, 81, 5},
+       {405, 81, 5}, {406, 81, 5}, {407, 81, 5}, {408, 82, 5},
+       {409, 82, 5}, {410, 82, 5}, {411, 82, 5}, {412, 82, 5},
+       {413, 83, 5}, {414, 83, 5}, {415, 83, 5}, {416, 83, 5},
+       {417, 83, 5}, {418, 84, 5}, {419, 84, 5}, {420, 84, 5},
+       {430, 86, 5}, {440, 88, 5}, {450, 90, 5}, {460, 92, 5},
+       {470, 75, 4}, {480, 77, 4}, {490, 78, 4}, {500, 80, 4},
+       {510, 82, 4}, {520, 83, 4}, {530, 85, 4}, {540, 86, 4},
+       {550, 88, 4}, {560, 90, 4}, {570, 91, 4}, {580, 70, 3},
+       {590, 71, 3}, {600, 72, 3}, {610, 73, 3}, {620, 74, 3},
+       {630, 76, 3}, {640, 77, 3}, {650, 78, 3}, {660, 79, 3},
+       {670, 80, 3}, {680, 82, 3}, {690, 83, 3}, {700, 84, 3},
+       {710, 85, 3}, {720, 86, 3}, {730, 88, 3}, {740, 89, 3},
+       {750, 90, 3}, {760, 91, 3}, {770, 92, 3}, {780, 62, 2},
+       {790, 63, 2}, {800, 64, 2}, {880, 70, 2}, {900, 72, 2},
+       {1000, 80, 2},          /* dsi clock frequency in Mhz*/
+};
+
+static int dsi_calc_mnp(u32 dsi_clk, struct dsi_mnp *dsi_mnp)
+{
+       unsigned int i;
+       u8 m;
+       u8 n;
+       u8 p;
+       u32 m_seed;
+
+       if (dsi_clk < 300 || dsi_clk > 1000)
+               return -ECHRNG;
+
+       for (i = 0; i <= ARRAY_SIZE(dsi_clk_tbl); i++) {
+               if (dsi_clk_tbl[i].freq > dsi_clk)
+                       break;
+       }
+
+       m = dsi_clk_tbl[i].m;
+       p = dsi_clk_tbl[i].p;
+       m_seed = lfsr_converts[m - 62];
+       n = 1;
+       dsi_mnp->dsi_pll_ctrl = 1 << (DSI_PLL_P1_POST_DIV_SHIFT + p - 2);
+       dsi_mnp->dsi_pll_div = (n - 1) << DSI_PLL_N1_DIV_SHIFT |
+               m_seed << DSI_PLL_M1_DIV_SHIFT;
+
+       return 0;
+}
+
+#else
+
+static int dsi_calc_mnp(u32 dsi_clk, struct dsi_mnp *dsi_mnp)
+{
+       u32 m, n, p;
+       u32 ref_clk;
+       u32 error;
+       u32 tmp_error;
+       u32 target_dsi_clk;
+       u32 calc_dsi_clk;
+       u32 calc_m;
+       u32 calc_p;
+       u32 m_seed;
+
+       if (dsi_clk < 300 || dsi_clk > 1150) {
+               DRM_ERROR("DSI CLK Out of Range\n");
+               return -ECHRNG;
+       }
+
+       ref_clk = 25000;
+       target_dsi_clk = dsi_clk * 1000;
+       error = 0xFFFFFFFF;
+       calc_m = 0;
+       calc_p = 0;
+
+       for (m = 62; m <= 92; m++) {
+               for (p = 2; p <= 6; p++) {
+
+                       calc_dsi_clk = (m * ref_clk) / p;
+                       if (calc_dsi_clk >= target_dsi_clk) {
+                               tmp_error = calc_dsi_clk - target_dsi_clk;
+                               if (tmp_error < error) {
+                                       error = tmp_error;
+                                       calc_m = m;
+                                       calc_p = p;
+                               }
+                       }
+               }
+       }
+
+       m_seed = lfsr_converts[calc_m - 62];
+       n = 1;
+       dsi_mnp->dsi_pll_ctrl = 1 << (DSI_PLL_P1_POST_DIV_SHIFT + calc_p - 2);
+       dsi_mnp->dsi_pll_div = (n - 1) << DSI_PLL_N1_DIV_SHIFT |
+               m_seed << DSI_PLL_M1_DIV_SHIFT;
+
+       return 0;
+}
+
+#endif
+
+/*
+ * XXX: The muxing and gating is hard coded for now. Need to add support for
+ * sharing PLLs with two DSI outputs.
+ */
+static void vlv_configure_dsi_pll(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
+       const struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode;
+       struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+       int ret;
+       struct dsi_mnp dsi_mnp;
+       u32 dsi_clk;
+
+       dsi_clk = dsi_rr_formula(mode, intel_dsi->pixel_format,
+                                intel_dsi->video_mode_format,
+                                intel_dsi->lane_count, !intel_dsi->eot_disable);
+
+       ret = dsi_calc_mnp(dsi_clk, &dsi_mnp);
+       if (ret) {
+               DRM_DEBUG_KMS("dsi_calc_mnp failed\n");
+               return;
+       }
+
+       dsi_mnp.dsi_pll_ctrl |= DSI_PLL_CLK_GATE_DSI0_DSIPLL;
+
+       DRM_DEBUG_KMS("dsi pll div %08x, ctrl %08x\n",
+                     dsi_mnp.dsi_pll_div, dsi_mnp.dsi_pll_ctrl);
+
+       vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, 0);
+       vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_DIVIDER, dsi_mnp.dsi_pll_div);
+       vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, dsi_mnp.dsi_pll_ctrl);
+}
+
+void vlv_enable_dsi_pll(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       u32 tmp;
+
+       DRM_DEBUG_KMS("\n");
+
+       mutex_lock(&dev_priv->dpio_lock);
+
+       vlv_configure_dsi_pll(encoder);
+
+       /* wait at least 0.5 us after ungating before enabling VCO */
+       usleep_range(1, 10);
+
+       tmp = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_CONTROL);
+       tmp |= DSI_PLL_VCO_EN;
+       vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, tmp);
+
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       if (wait_for(I915_READ(PIPECONF(PIPE_A)) & PIPECONF_DSI_PLL_LOCKED, 20)) {
+               DRM_ERROR("DSI PLL lock failed\n");
+               return;
+       }
+
+       DRM_DEBUG_KMS("DSI PLL locked\n");
+}
+
+void vlv_disable_dsi_pll(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       u32 tmp;
+
+       DRM_DEBUG_KMS("\n");
+
+       mutex_lock(&dev_priv->dpio_lock);
+
+       tmp = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_CONTROL);
+       tmp &= ~DSI_PLL_VCO_EN;
+       tmp |= DSI_PLL_LDO_GATE;
+       vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, tmp);
+
+       mutex_unlock(&dev_priv->dpio_lock);
+}
index 7fa7df5..3c77365 100644 (file)
@@ -153,6 +153,8 @@ static void intel_dvo_get_config(struct intel_encoder *encoder,
                flags |= DRM_MODE_FLAG_NVSYNC;
 
        pipe_config->adjusted_mode.flags |= flags;
+
+       pipe_config->adjusted_mode.crtc_clock = pipe_config->port_clock;
 }
 
 static void intel_disable_dvo(struct intel_encoder *encoder)
@@ -171,11 +173,16 @@ static void intel_enable_dvo(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
        struct intel_dvo *intel_dvo = enc_to_dvo(encoder);
+       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
        u32 dvo_reg = intel_dvo->dev.dvo_reg;
        u32 temp = I915_READ(dvo_reg);
 
        I915_WRITE(dvo_reg, temp | DVO_ENABLE);
        I915_READ(dvo_reg);
+       intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev,
+                                        &crtc->config.requested_mode,
+                                        &crtc->config.adjusted_mode);
+
        intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
 }
 
@@ -184,6 +191,7 @@ static void intel_dvo_dpms(struct drm_connector *connector, int mode)
 {
        struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
        struct drm_crtc *crtc;
+       struct intel_crtc_config *config;
 
        /* dvo supports only 2 dpms states. */
        if (mode != DRM_MODE_DPMS_ON)
@@ -204,10 +212,16 @@ static void intel_dvo_dpms(struct drm_connector *connector, int mode)
        /* We call connector dpms manually below in case pipe dpms doesn't
         * change due to cloning. */
        if (mode == DRM_MODE_DPMS_ON) {
+               config = &to_intel_crtc(crtc)->config;
+
                intel_dvo->base.connectors_active = true;
 
                intel_crtc_update_dpms(crtc);
 
+               intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev,
+                                                &config->requested_mode,
+                                                &config->adjusted_mode);
+
                intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
        } else {
                intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false);
@@ -267,11 +281,6 @@ static bool intel_dvo_compute_config(struct intel_encoder *encoder,
                drm_mode_set_crtcinfo(adjusted_mode, 0);
        }
 
-       if (intel_dvo->dev.dev_ops->mode_fixup)
-               return intel_dvo->dev.dev_ops->mode_fixup(&intel_dvo->dev,
-                                                         &pipe_config->requested_mode,
-                                                         adjusted_mode);
-
        return true;
 }
 
@@ -299,10 +308,6 @@ static void intel_dvo_mode_set(struct intel_encoder *encoder)
                break;
        }
 
-       intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev,
-                                        &crtc->config.requested_mode,
-                                        adjusted_mode);
-
        /* Save the data order, since I don't know what it should be set to. */
        dvo_val = I915_READ(dvo_reg) &
                  (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG);
@@ -370,7 +375,6 @@ static int intel_dvo_get_modes(struct drm_connector *connector)
 
 static void intel_dvo_destroy(struct drm_connector *connector)
 {
-       drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
        kfree(connector);
 }
@@ -451,11 +455,11 @@ void intel_dvo_init(struct drm_device *dev)
        int i;
        int encoder_type = DRM_MODE_ENCODER_NONE;
 
-       intel_dvo = kzalloc(sizeof(struct intel_dvo), GFP_KERNEL);
+       intel_dvo = kzalloc(sizeof(*intel_dvo), GFP_KERNEL);
        if (!intel_dvo)
                return;
 
-       intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
+       intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
        if (!intel_connector) {
                kfree(intel_dvo);
                return;
similarity index 89%
rename from drivers/gpu/drm/i915/intel_fb.c
rename to drivers/gpu/drm/i915/intel_fbdev.c
index bc21000..895fcb4 100644 (file)
@@ -78,8 +78,8 @@ static int intelfb_create(struct drm_fb_helper *helper,
        mode_cmd.width = sizes->surface_width;
        mode_cmd.height = sizes->surface_height;
 
-       mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((sizes->surface_bpp + 7) /
-                                                     8), 64);
+       mode_cmd.pitches[0] = ALIGN(mode_cmd.width *
+                                   DIV_ROUND_UP(sizes->surface_bpp, 8), 64);
        mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
                                                          sizes->surface_depth);
 
@@ -184,6 +184,27 @@ out:
        return ret;
 }
 
+/** Sets the color ramps on behalf of RandR */
+static void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+                                   u16 blue, int regno)
+{
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       intel_crtc->lut_r[regno] = red >> 8;
+       intel_crtc->lut_g[regno] = green >> 8;
+       intel_crtc->lut_b[regno] = blue >> 8;
+}
+
+static void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+                                   u16 *blue, int regno)
+{
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       *red = intel_crtc->lut_r[regno] << 8;
+       *green = intel_crtc->lut_g[regno] << 8;
+       *blue = intel_crtc->lut_b[regno] << 8;
+}
+
 static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
        .gamma_set = intel_crtc_fb_gamma_set,
        .gamma_get = intel_crtc_fb_gamma_get,
@@ -216,7 +237,7 @@ int intel_fbdev_init(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
 
-       ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL);
+       ifbdev = kzalloc(sizeof(*ifbdev), GFP_KERNEL);
        if (!ifbdev)
                return -ENOMEM;
 
@@ -225,7 +246,7 @@ int intel_fbdev_init(struct drm_device *dev)
 
        ret = drm_fb_helper_init(dev, &ifbdev->helper,
                                 INTEL_INFO(dev)->num_pipes,
-                                INTELFB_CONN_LIMIT);
+                                4);
        if (ret) {
                kfree(ifbdev);
                return ret;
@@ -278,13 +299,13 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state)
 
 MODULE_LICENSE("GPL and additional rights");
 
-void intel_fb_output_poll_changed(struct drm_device *dev)
+void intel_fbdev_output_poll_changed(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
 }
 
-void intel_fb_restore_mode(struct drm_device *dev)
+void intel_fbdev_restore_mode(struct drm_device *dev)
 {
        int ret;
        struct drm_i915_private *dev_priv = dev->dev_private;
index 4148cc8..03f9ca7 100644 (file)
@@ -713,6 +713,7 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
        u32 tmp, flags = 0;
+       int dotclock;
 
        tmp = I915_READ(intel_hdmi->hdmi_reg);
 
@@ -727,6 +728,16 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
                flags |= DRM_MODE_FLAG_NVSYNC;
 
        pipe_config->adjusted_mode.flags |= flags;
+
+       if ((tmp & SDVO_COLOR_FORMAT_MASK) == HDMI_COLOR_FORMAT_12bpc)
+               dotclock = pipe_config->port_clock * 2 / 3;
+       else
+               dotclock = pipe_config->port_clock;
+
+       if (HAS_PCH_SPLIT(dev_priv->dev))
+               ironlake_check_encoder_dotclock(pipe_config, dotclock);
+
+       pipe_config->adjusted_mode.crtc_clock = dotclock;
 }
 
 static void intel_enable_hdmi(struct intel_encoder *encoder)
@@ -836,7 +847,7 @@ static int hdmi_portclock_limit(struct intel_hdmi *hdmi)
 
        if (IS_G4X(dev))
                return 165000;
-       else if (IS_HASWELL(dev))
+       else if (IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8)
                return 300000;
        else
                return 225000;
@@ -862,7 +873,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        struct drm_device *dev = encoder->base.dev;
        struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
-       int clock_12bpc = pipe_config->requested_mode.clock * 3 / 2;
+       int clock_12bpc = pipe_config->adjusted_mode.crtc_clock * 3 / 2;
        int portclock_limit = hdmi_portclock_limit(intel_hdmi);
        int desired_bpp;
 
@@ -904,7 +915,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
                pipe_config->pipe_bpp = desired_bpp;
        }
 
-       if (adjusted_mode->clock > portclock_limit) {
+       if (adjusted_mode->crtc_clock > portclock_limit) {
                DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n");
                return false;
        }
@@ -1063,7 +1074,7 @@ done:
        return 0;
 }
 
-static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
+static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
 {
        struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
        struct drm_device *dev = encoder->base.dev;
@@ -1079,35 +1090,35 @@ static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
 
        /* Enable clock channels for this port */
        mutex_lock(&dev_priv->dpio_lock);
-       val = vlv_dpio_read(dev_priv, DPIO_DATA_LANE_A(port));
+       val = vlv_dpio_read(dev_priv, pipe, DPIO_DATA_LANE_A(port));
        val = 0;
        if (pipe)
                val |= (1<<21);
        else
                val &= ~(1<<21);
        val |= 0x001000c4;
-       vlv_dpio_write(dev_priv, DPIO_DATA_CHANNEL(port), val);
+       vlv_dpio_write(dev_priv, pipe, DPIO_DATA_CHANNEL(port), val);
 
        /* HDMI 1.0V-2dB */
-       vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0);
-       vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL4(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_OCALINIT(port), 0);
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_SWING_CTL4(port),
                         0x2b245f5f);
-       vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL2(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_SWING_CTL2(port),
                         0x5578b83a);
-       vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL3(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_SWING_CTL3(port),
                         0x0c782040);
-       vlv_dpio_write(dev_priv, DPIO_TX3_SWING_CTL4(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX3_SWING_CTL4(port),
                         0x2b247878);
-       vlv_dpio_write(dev_priv, DPIO_PCS_STAGGER0(port), 0x00030000);
-       vlv_dpio_write(dev_priv, DPIO_PCS_CTL_OVER1(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_STAGGER0(port), 0x00030000);
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_CTL_OVER1(port),
                         0x00002000);
-       vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_OCALINIT(port),
                         DPIO_TX_OCALINIT_EN);
 
        /* Program lane clock */
-       vlv_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF0(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_CLOCKBUF0(port),
                         0x00760018);
-       vlv_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF8(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_CLOCKBUF8(port),
                         0x00400888);
        mutex_unlock(&dev_priv->dpio_lock);
 
@@ -1116,55 +1127,60 @@ static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
        vlv_wait_port_ready(dev_priv, port);
 }
 
-static void intel_hdmi_pre_pll_enable(struct intel_encoder *encoder)
+static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
 {
        struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc =
+               to_intel_crtc(encoder->base.crtc);
        int port = vlv_dport_to_channel(dport);
+       int pipe = intel_crtc->pipe;
 
        if (!IS_VALLEYVIEW(dev))
                return;
 
        /* Program Tx lane resets to default */
        mutex_lock(&dev_priv->dpio_lock);
-       vlv_dpio_write(dev_priv, DPIO_PCS_TX(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_TX(port),
                         DPIO_PCS_TX_LANE2_RESET |
                         DPIO_PCS_TX_LANE1_RESET);
-       vlv_dpio_write(dev_priv, DPIO_PCS_CLK(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_CLK(port),
                         DPIO_PCS_CLK_CRI_RXEB_EIOS_EN |
                         DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN |
                         (1<<DPIO_PCS_CLK_DATAWIDTH_SHIFT) |
                         DPIO_PCS_CLK_SOFT_RESET);
 
        /* Fix up inter-pair skew failure */
-       vlv_dpio_write(dev_priv, DPIO_PCS_STAGGER1(port), 0x00750f00);
-       vlv_dpio_write(dev_priv, DPIO_TX_CTL(port), 0x00001500);
-       vlv_dpio_write(dev_priv, DPIO_TX_LANE(port), 0x40400000);
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_STAGGER1(port), 0x00750f00);
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_CTL(port), 0x00001500);
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_LANE(port), 0x40400000);
 
-       vlv_dpio_write(dev_priv, DPIO_PCS_CTL_OVER1(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_CTL_OVER1(port),
                         0x00002000);
-       vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port),
+       vlv_dpio_write(dev_priv, pipe, DPIO_TX_OCALINIT(port),
                         DPIO_TX_OCALINIT_EN);
        mutex_unlock(&dev_priv->dpio_lock);
 }
 
-static void intel_hdmi_post_disable(struct intel_encoder *encoder)
+static void vlv_hdmi_post_disable(struct intel_encoder *encoder)
 {
        struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_crtc *intel_crtc =
+               to_intel_crtc(encoder->base.crtc);
        int port = vlv_dport_to_channel(dport);
+       int pipe = intel_crtc->pipe;
 
        /* Reset lanes to avoid HDMI flicker (VLV w/a) */
        mutex_lock(&dev_priv->dpio_lock);
-       vlv_dpio_write(dev_priv, DPIO_PCS_TX(port), 0x00000000);
-       vlv_dpio_write(dev_priv, DPIO_PCS_CLK(port), 0x00e00060);
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_TX(port), 0x00000000);
+       vlv_dpio_write(dev_priv, pipe, DPIO_PCS_CLK(port), 0x00e00060);
        mutex_unlock(&dev_priv->dpio_lock);
 }
 
 static void intel_hdmi_destroy(struct drm_connector *connector)
 {
-       drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
        kfree(connector);
 }
@@ -1211,6 +1227,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
 
        connector->interlace_allowed = 1;
        connector->doublescan_allowed = 0;
+       connector->stereo_allowed = 1;
 
        switch (port) {
        case PORT_B:
@@ -1275,11 +1292,11 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
        struct intel_encoder *intel_encoder;
        struct intel_connector *intel_connector;
 
-       intel_dig_port = kzalloc(sizeof(struct intel_digital_port), GFP_KERNEL);
+       intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL);
        if (!intel_dig_port)
                return;
 
-       intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
+       intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
        if (!intel_connector) {
                kfree(intel_dig_port);
                return;
@@ -1296,10 +1313,10 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
        intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
        intel_encoder->get_config = intel_hdmi_get_config;
        if (IS_VALLEYVIEW(dev)) {
-               intel_encoder->pre_pll_enable = intel_hdmi_pre_pll_enable;
-               intel_encoder->pre_enable = intel_hdmi_pre_enable;
+               intel_encoder->pre_pll_enable = vlv_hdmi_pre_pll_enable;
+               intel_encoder->pre_enable = vlv_hdmi_pre_enable;
                intel_encoder->enable = vlv_enable_hdmi;
-               intel_encoder->post_disable = intel_hdmi_post_disable;
+               intel_encoder->post_disable = vlv_hdmi_post_disable;
        } else {
                intel_encoder->enable = intel_enable_hdmi;
        }
index d1c1e0f..2ca17b1 100644 (file)
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 
+enum disp_clk {
+       CDCLK,
+       CZCLK
+};
+
 struct gmbus_port {
        const char *name;
        int reg;
@@ -58,10 +63,69 @@ to_intel_gmbus(struct i2c_adapter *i2c)
        return container_of(i2c, struct intel_gmbus, adapter);
 }
 
+static int get_disp_clk_div(struct drm_i915_private *dev_priv,
+                           enum disp_clk clk)
+{
+       u32 reg_val;
+       int clk_ratio;
+
+       reg_val = I915_READ(CZCLK_CDCLK_FREQ_RATIO);
+
+       if (clk == CDCLK)
+               clk_ratio =
+                       ((reg_val & CDCLK_FREQ_MASK) >> CDCLK_FREQ_SHIFT) + 1;
+       else
+               clk_ratio = (reg_val & CZCLK_FREQ_MASK) + 1;
+
+       return clk_ratio;
+}
+
+static void gmbus_set_freq(struct drm_i915_private *dev_priv)
+{
+       int vco_freq[] = { 800, 1600, 2000, 2400 };
+       int gmbus_freq = 0, cdclk_div, hpll_freq;
+
+       BUG_ON(!IS_VALLEYVIEW(dev_priv->dev));
+
+       /* Skip setting the gmbus freq if BIOS has already programmed it */
+       if (I915_READ(GMBUSFREQ_VLV) != 0xA0)
+               return;
+
+       /* Obtain SKU information */
+       mutex_lock(&dev_priv->dpio_lock);
+       hpll_freq =
+               vlv_cck_read(dev_priv, CCK_FUSE_REG) & CCK_FUSE_HPLL_FREQ_MASK;
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       /* Get the CDCLK divide ratio */
+       cdclk_div = get_disp_clk_div(dev_priv, CDCLK);
+
+       /*
+        * Program the gmbus_freq based on the cdclk frequency.
+        * BSpec erroneously claims we should aim for 4MHz, but
+        * in fact 1MHz is the correct frequency.
+        */
+       if (cdclk_div)
+               gmbus_freq = (vco_freq[hpll_freq] << 1) / cdclk_div;
+
+       if (WARN_ON(gmbus_freq == 0))
+               return;
+
+       I915_WRITE(GMBUSFREQ_VLV, gmbus_freq);
+}
+
 void
 intel_i2c_reset(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /*
+        * In BIOS-less system, program the correct gmbus frequency
+        * before reading edid.
+        */
+       if (IS_VALLEYVIEW(dev))
+               gmbus_set_freq(dev_priv);
+
        I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0);
        I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0);
 }
index b8af94a..c3b4da7 100644 (file)
@@ -92,6 +92,7 @@ static void intel_lvds_get_config(struct intel_encoder *encoder,
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 lvds_reg, tmp, flags = 0;
+       int dotclock;
 
        if (HAS_PCH_SPLIT(dev))
                lvds_reg = PCH_LVDS;
@@ -116,6 +117,13 @@ static void intel_lvds_get_config(struct intel_encoder *encoder,
 
                pipe_config->gmch_pfit.control |= tmp & PANEL_8TO6_DITHER_ENABLE;
        }
+
+       dotclock = pipe_config->port_clock;
+
+       if (HAS_PCH_SPLIT(dev_priv->dev))
+               ironlake_check_encoder_dotclock(pipe_config, dotclock);
+
+       pipe_config->adjusted_mode.crtc_clock = dotclock;
 }
 
 /* The LVDS pin pair needs to be on before the DPLLs are enabled.
@@ -198,7 +206,8 @@ static void intel_enable_lvds(struct intel_encoder *encoder)
 {
        struct drm_device *dev = encoder->base.dev;
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
-       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
+       struct intel_connector *intel_connector =
+               &lvds_encoder->attached_connector->base;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 ctl_reg, stat_reg;
 
@@ -217,13 +226,15 @@ static void intel_enable_lvds(struct intel_encoder *encoder)
        if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000))
                DRM_ERROR("timed out waiting for panel to power on\n");
 
-       intel_panel_enable_backlight(dev, intel_crtc->pipe);
+       intel_panel_enable_backlight(intel_connector);
 }
 
 static void intel_disable_lvds(struct intel_encoder *encoder)
 {
        struct drm_device *dev = encoder->base.dev;
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
+       struct intel_connector *intel_connector =
+               &lvds_encoder->attached_connector->base;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 ctl_reg, stat_reg;
 
@@ -235,7 +246,7 @@ static void intel_disable_lvds(struct intel_encoder *encoder)
                stat_reg = PP_STATUS;
        }
 
-       intel_panel_disable_backlight(dev);
+       intel_panel_disable_backlight(intel_connector);
 
        I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON);
        if (wait_for((I915_READ(stat_reg) & PP_ON) == 0, 1000))
@@ -466,7 +477,6 @@ static void intel_lvds_destroy(struct drm_connector *connector)
 
        intel_panel_fini(&lvds_connector->base.panel);
 
-       drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
        kfree(connector);
 }
@@ -802,7 +812,8 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev,
                return true;
 
        for (i = 0; i < dev_priv->vbt.child_dev_num; i++) {
-               struct child_device_config *child = dev_priv->vbt.child_dev + i;
+               union child_device_config *uchild = dev_priv->vbt.child_dev + i;
+               struct old_child_dev_config *child = &uchild->old;
 
                /* If the device type is not LFP, continue.
                 * We have to check both the new identifiers as well as the
@@ -956,11 +967,11 @@ void intel_lvds_init(struct drm_device *dev)
                }
        }
 
-       lvds_encoder = kzalloc(sizeof(struct intel_lvds_encoder), GFP_KERNEL);
+       lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL);
        if (!lvds_encoder)
                return;
 
-       lvds_connector = kzalloc(sizeof(struct intel_lvds_connector), GFP_KERNEL);
+       lvds_connector = kzalloc(sizeof(*lvds_connector), GFP_KERNEL);
        if (!lvds_connector) {
                kfree(lvds_encoder);
                return;
index 119771f..6d69a9b 100644 (file)
 #include "i915_drv.h"
 #include "intel_drv.h"
 
-#define PCI_ASLE 0xe4
-#define PCI_ASLS 0xfc
+#define PCI_ASLE               0xe4
+#define PCI_ASLS               0xfc
+#define PCI_SWSCI              0xe8
+#define PCI_SWSCI_SCISEL       (1 << 15)
+#define PCI_SWSCI_GSSCIE       (1 << 0)
 
 #define OPREGION_HEADER_OFFSET 0
 #define OPREGION_ACPI_OFFSET   0x100
@@ -107,25 +110,38 @@ struct opregion_asle {
        u32 epfm;       /* enabled panel fitting modes */
        u8 plut[74];    /* panel LUT and identifier */
        u32 pfmb;       /* PWM freq and min brightness */
-       u8 rsvd[102];
+       u32 cddv;       /* color correction default values */
+       u32 pcft;       /* power conservation features */
+       u32 srot;       /* supported rotation angles */
+       u32 iuer;       /* IUER events */
+       u8 rsvd[86];
 } __attribute__((packed));
 
 /* Driver readiness indicator */
 #define ASLE_ARDY_READY                (1 << 0)
 #define ASLE_ARDY_NOT_READY    (0 << 0)
 
-/* ASLE irq request bits */
-#define ASLE_SET_ALS_ILLUM     (1 << 0)
-#define ASLE_SET_BACKLIGHT     (1 << 1)
-#define ASLE_SET_PFIT          (1 << 2)
-#define ASLE_SET_PWM_FREQ      (1 << 3)
-#define ASLE_REQ_MSK           0xf
-
-/* response bits of ASLE irq request */
-#define ASLE_ALS_ILLUM_FAILED  (1<<10)
-#define ASLE_BACKLIGHT_FAILED  (1<<12)
-#define ASLE_PFIT_FAILED       (1<<14)
-#define ASLE_PWM_FREQ_FAILED   (1<<16)
+/* ASLE Interrupt Command (ASLC) bits */
+#define ASLC_SET_ALS_ILLUM             (1 << 0)
+#define ASLC_SET_BACKLIGHT             (1 << 1)
+#define ASLC_SET_PFIT                  (1 << 2)
+#define ASLC_SET_PWM_FREQ              (1 << 3)
+#define ASLC_SUPPORTED_ROTATION_ANGLES (1 << 4)
+#define ASLC_BUTTON_ARRAY              (1 << 5)
+#define ASLC_CONVERTIBLE_INDICATOR     (1 << 6)
+#define ASLC_DOCKING_INDICATOR         (1 << 7)
+#define ASLC_ISCT_STATE_CHANGE         (1 << 8)
+#define ASLC_REQ_MSK                   0x1ff
+/* response bits */
+#define ASLC_ALS_ILLUM_FAILED          (1 << 10)
+#define ASLC_BACKLIGHT_FAILED          (1 << 12)
+#define ASLC_PFIT_FAILED               (1 << 14)
+#define ASLC_PWM_FREQ_FAILED           (1 << 16)
+#define ASLC_ROTATION_ANGLES_FAILED    (1 << 18)
+#define ASLC_BUTTON_ARRAY_FAILED       (1 << 20)
+#define ASLC_CONVERTIBLE_FAILED                (1 << 22)
+#define ASLC_DOCKING_FAILED            (1 << 24)
+#define ASLC_ISCT_STATE_FAILED         (1 << 26)
 
 /* Technology enabled indicator */
 #define ASLE_TCHE_ALS_EN       (1 << 0)
@@ -151,6 +167,60 @@ struct opregion_asle {
 
 #define ASLE_CBLV_VALID         (1<<31)
 
+/* IUER */
+#define ASLE_IUER_DOCKING              (1 << 7)
+#define ASLE_IUER_CONVERTIBLE          (1 << 6)
+#define ASLE_IUER_ROTATION_LOCK_BTN    (1 << 4)
+#define ASLE_IUER_VOLUME_DOWN_BTN      (1 << 3)
+#define ASLE_IUER_VOLUME_UP_BTN                (1 << 2)
+#define ASLE_IUER_WINDOWS_BTN          (1 << 1)
+#define ASLE_IUER_POWER_BTN            (1 << 0)
+
+/* Software System Control Interrupt (SWSCI) */
+#define SWSCI_SCIC_INDICATOR           (1 << 0)
+#define SWSCI_SCIC_MAIN_FUNCTION_SHIFT 1
+#define SWSCI_SCIC_MAIN_FUNCTION_MASK  (0xf << 1)
+#define SWSCI_SCIC_SUB_FUNCTION_SHIFT  8
+#define SWSCI_SCIC_SUB_FUNCTION_MASK   (0xff << 8)
+#define SWSCI_SCIC_EXIT_PARAMETER_SHIFT        8
+#define SWSCI_SCIC_EXIT_PARAMETER_MASK (0xff << 8)
+#define SWSCI_SCIC_EXIT_STATUS_SHIFT   5
+#define SWSCI_SCIC_EXIT_STATUS_MASK    (7 << 5)
+#define SWSCI_SCIC_EXIT_STATUS_SUCCESS 1
+
+#define SWSCI_FUNCTION_CODE(main, sub) \
+       ((main) << SWSCI_SCIC_MAIN_FUNCTION_SHIFT | \
+        (sub) << SWSCI_SCIC_SUB_FUNCTION_SHIFT)
+
+/* SWSCI: Get BIOS Data (GBDA) */
+#define SWSCI_GBDA                     4
+#define SWSCI_GBDA_SUPPORTED_CALLS     SWSCI_FUNCTION_CODE(SWSCI_GBDA, 0)
+#define SWSCI_GBDA_REQUESTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 1)
+#define SWSCI_GBDA_BOOT_DISPLAY_PREF   SWSCI_FUNCTION_CODE(SWSCI_GBDA, 4)
+#define SWSCI_GBDA_PANEL_DETAILS       SWSCI_FUNCTION_CODE(SWSCI_GBDA, 5)
+#define SWSCI_GBDA_TV_STANDARD         SWSCI_FUNCTION_CODE(SWSCI_GBDA, 6)
+#define SWSCI_GBDA_INTERNAL_GRAPHICS   SWSCI_FUNCTION_CODE(SWSCI_GBDA, 7)
+#define SWSCI_GBDA_SPREAD_SPECTRUM     SWSCI_FUNCTION_CODE(SWSCI_GBDA, 10)
+
+/* SWSCI: System BIOS Callbacks (SBCB) */
+#define SWSCI_SBCB                     6
+#define SWSCI_SBCB_SUPPORTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 0)
+#define SWSCI_SBCB_INIT_COMPLETION     SWSCI_FUNCTION_CODE(SWSCI_SBCB, 1)
+#define SWSCI_SBCB_PRE_HIRES_SET_MODE  SWSCI_FUNCTION_CODE(SWSCI_SBCB, 3)
+#define SWSCI_SBCB_POST_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 4)
+#define SWSCI_SBCB_DISPLAY_SWITCH      SWSCI_FUNCTION_CODE(SWSCI_SBCB, 5)
+#define SWSCI_SBCB_SET_TV_FORMAT       SWSCI_FUNCTION_CODE(SWSCI_SBCB, 6)
+#define SWSCI_SBCB_ADAPTER_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 7)
+#define SWSCI_SBCB_DISPLAY_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 8)
+#define SWSCI_SBCB_SET_BOOT_DISPLAY    SWSCI_FUNCTION_CODE(SWSCI_SBCB, 9)
+#define SWSCI_SBCB_SET_PANEL_DETAILS   SWSCI_FUNCTION_CODE(SWSCI_SBCB, 10)
+#define SWSCI_SBCB_SET_INTERNAL_GFX    SWSCI_FUNCTION_CODE(SWSCI_SBCB, 11)
+#define SWSCI_SBCB_POST_HIRES_TO_DOS_FS        SWSCI_FUNCTION_CODE(SWSCI_SBCB, 16)
+#define SWSCI_SBCB_SUSPEND_RESUME      SWSCI_FUNCTION_CODE(SWSCI_SBCB, 17)
+#define SWSCI_SBCB_SET_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 18)
+#define SWSCI_SBCB_POST_VBE_PM         SWSCI_FUNCTION_CODE(SWSCI_SBCB, 19)
+#define SWSCI_SBCB_ENABLE_DISABLE_AUDIO        SWSCI_FUNCTION_CODE(SWSCI_SBCB, 21)
+
 #define ACPI_OTHER_OUTPUT (0<<8)
 #define ACPI_VGA_OUTPUT (1<<8)
 #define ACPI_TV_OUTPUT (2<<8)
@@ -158,24 +228,224 @@ struct opregion_asle {
 #define ACPI_LVDS_OUTPUT (4<<8)
 
 #ifdef CONFIG_ACPI
+static int swsci(struct drm_device *dev, u32 function, u32 parm, u32 *parm_out)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct opregion_swsci __iomem *swsci = dev_priv->opregion.swsci;
+       u32 main_function, sub_function, scic;
+       u16 pci_swsci;
+       u32 dslp;
+
+       if (!swsci)
+               return -ENODEV;
+
+       main_function = (function & SWSCI_SCIC_MAIN_FUNCTION_MASK) >>
+               SWSCI_SCIC_MAIN_FUNCTION_SHIFT;
+       sub_function = (function & SWSCI_SCIC_SUB_FUNCTION_MASK) >>
+               SWSCI_SCIC_SUB_FUNCTION_SHIFT;
+
+       /* Check if we can call the function. See swsci_setup for details. */
+       if (main_function == SWSCI_SBCB) {
+               if ((dev_priv->opregion.swsci_sbcb_sub_functions &
+                    (1 << sub_function)) == 0)
+                       return -EINVAL;
+       } else if (main_function == SWSCI_GBDA) {
+               if ((dev_priv->opregion.swsci_gbda_sub_functions &
+                    (1 << sub_function)) == 0)
+                       return -EINVAL;
+       }
+
+       /* Driver sleep timeout in ms. */
+       dslp = ioread32(&swsci->dslp);
+       if (!dslp) {
+               /* The spec says 2ms should be the default, but it's too small
+                * for some machines. */
+               dslp = 50;
+       } else if (dslp > 500) {
+               /* Hey bios, trust must be earned. */
+               WARN_ONCE(1, "excessive driver sleep timeout (DSPL) %u\n", dslp);
+               dslp = 500;
+       }
+
+       /* The spec tells us to do this, but we are the only user... */
+       scic = ioread32(&swsci->scic);
+       if (scic & SWSCI_SCIC_INDICATOR) {
+               DRM_DEBUG_DRIVER("SWSCI request already in progress\n");
+               return -EBUSY;
+       }
+
+       scic = function | SWSCI_SCIC_INDICATOR;
+
+       iowrite32(parm, &swsci->parm);
+       iowrite32(scic, &swsci->scic);
+
+       /* Ensure SCI event is selected and event trigger is cleared. */
+       pci_read_config_word(dev->pdev, PCI_SWSCI, &pci_swsci);
+       if (!(pci_swsci & PCI_SWSCI_SCISEL) || (pci_swsci & PCI_SWSCI_GSSCIE)) {
+               pci_swsci |= PCI_SWSCI_SCISEL;
+               pci_swsci &= ~PCI_SWSCI_GSSCIE;
+               pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci);
+       }
+
+       /* Use event trigger to tell bios to check the mail. */
+       pci_swsci |= PCI_SWSCI_GSSCIE;
+       pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci);
+
+       /* Poll for the result. */
+#define C (((scic = ioread32(&swsci->scic)) & SWSCI_SCIC_INDICATOR) == 0)
+       if (wait_for(C, dslp)) {
+               DRM_DEBUG_DRIVER("SWSCI request timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       scic = (scic & SWSCI_SCIC_EXIT_STATUS_MASK) >>
+               SWSCI_SCIC_EXIT_STATUS_SHIFT;
+
+       /* Note: scic == 0 is an error! */
+       if (scic != SWSCI_SCIC_EXIT_STATUS_SUCCESS) {
+               DRM_DEBUG_DRIVER("SWSCI request error %u\n", scic);
+               return -EIO;
+       }
+
+       if (parm_out)
+               *parm_out = ioread32(&swsci->parm);
+
+       return 0;
+
+#undef C
+}
+
+#define DISPLAY_TYPE_CRT                       0
+#define DISPLAY_TYPE_TV                                1
+#define DISPLAY_TYPE_EXTERNAL_FLAT_PANEL       2
+#define DISPLAY_TYPE_INTERNAL_FLAT_PANEL       3
+
+int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder,
+                                 bool enable)
+{
+       struct drm_device *dev = intel_encoder->base.dev;
+       u32 parm = 0;
+       u32 type = 0;
+       u32 port;
+
+       /* don't care about old stuff for now */
+       if (!HAS_DDI(dev))
+               return 0;
+
+       port = intel_ddi_get_encoder_port(intel_encoder);
+       if (port == PORT_E) {
+               port = 0;
+       } else {
+               parm |= 1 << port;
+               port++;
+       }
+
+       if (!enable)
+               parm |= 4 << 8;
+
+       switch (intel_encoder->type) {
+       case INTEL_OUTPUT_ANALOG:
+               type = DISPLAY_TYPE_CRT;
+               break;
+       case INTEL_OUTPUT_UNKNOWN:
+       case INTEL_OUTPUT_DISPLAYPORT:
+       case INTEL_OUTPUT_HDMI:
+               type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL;
+               break;
+       case INTEL_OUTPUT_EDP:
+               type = DISPLAY_TYPE_INTERNAL_FLAT_PANEL;
+               break;
+       default:
+               WARN_ONCE(1, "unsupported intel_encoder type %d\n",
+                         intel_encoder->type);
+               return -EINVAL;
+       }
+
+       parm |= type << (16 + port * 3);
+
+       return swsci(dev, SWSCI_SBCB_DISPLAY_POWER_STATE, parm, NULL);
+}
+
+static const struct {
+       pci_power_t pci_power_state;
+       u32 parm;
+} power_state_map[] = {
+       { PCI_D0,       0x00 },
+       { PCI_D1,       0x01 },
+       { PCI_D2,       0x02 },
+       { PCI_D3hot,    0x04 },
+       { PCI_D3cold,   0x04 },
+};
+
+int intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state)
+{
+       int i;
+
+       if (!HAS_DDI(dev))
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(power_state_map); i++) {
+               if (state == power_state_map[i].pci_power_state)
+                       return swsci(dev, SWSCI_SBCB_ADAPTER_POWER_STATE,
+                                    power_state_map[i].parm, NULL);
+       }
+
+       return -EINVAL;
+}
+
 static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_encoder *encoder;
+       struct drm_connector *connector;
+       struct intel_connector *intel_connector = NULL;
+       struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[0];
        struct opregion_asle __iomem *asle = dev_priv->opregion.asle;
+       u32 ret = 0;
+       bool found = false;
 
        DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp);
 
        if (!(bclp & ASLE_BCLP_VALID))
-               return ASLE_BACKLIGHT_FAILED;
+               return ASLC_BACKLIGHT_FAILED;
 
        bclp &= ASLE_BCLP_MSK;
        if (bclp > 255)
-               return ASLE_BACKLIGHT_FAILED;
+               return ASLC_BACKLIGHT_FAILED;
+
+       mutex_lock(&dev->mode_config.mutex);
+       /*
+        * Could match the OpRegion connector here instead, but we'd also need
+        * to verify the connector could handle a backlight call.
+        */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+               if (encoder->crtc == crtc) {
+                       found = true;
+                       break;
+               }
+
+       if (!found) {
+               ret = ASLC_BACKLIGHT_FAILED;
+               goto out;
+       }
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+               if (connector->encoder == encoder)
+                       intel_connector = to_intel_connector(connector);
+
+       if (!intel_connector) {
+               ret = ASLC_BACKLIGHT_FAILED;
+               goto out;
+       }
 
-       intel_panel_set_backlight(dev, bclp, 255);
+       DRM_DEBUG_KMS("updating opregion backlight %d/255\n", bclp);
+       intel_panel_set_backlight(intel_connector, bclp, 255);
        iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv);
 
-       return 0;
+out:
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
 }
 
 static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi)
@@ -183,13 +453,13 @@ static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi)
        /* alsi is the current ALS reading in lux. 0 indicates below sensor
           range, 0xffff indicates above sensor range. 1-0xfffe are valid */
        DRM_DEBUG_DRIVER("Illum is not supported\n");
-       return ASLE_ALS_ILLUM_FAILED;
+       return ASLC_ALS_ILLUM_FAILED;
 }
 
 static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb)
 {
        DRM_DEBUG_DRIVER("PWM freq is not supported\n");
-       return ASLE_PWM_FREQ_FAILED;
+       return ASLC_PWM_FREQ_FAILED;
 }
 
 static u32 asle_set_pfit(struct drm_device *dev, u32 pfit)
@@ -197,39 +467,118 @@ static u32 asle_set_pfit(struct drm_device *dev, u32 pfit)
        /* Panel fitting is currently controlled by the X code, so this is a
           noop until modesetting support works fully */
        DRM_DEBUG_DRIVER("Pfit is not supported\n");
-       return ASLE_PFIT_FAILED;
+       return ASLC_PFIT_FAILED;
 }
 
-void intel_opregion_asle_intr(struct drm_device *dev)
+static u32 asle_set_supported_rotation_angles(struct drm_device *dev, u32 srot)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
+       DRM_DEBUG_DRIVER("SROT is not supported\n");
+       return ASLC_ROTATION_ANGLES_FAILED;
+}
+
+static u32 asle_set_button_array(struct drm_device *dev, u32 iuer)
+{
+       if (!iuer)
+               DRM_DEBUG_DRIVER("Button array event is not supported (nothing)\n");
+       if (iuer & ASLE_IUER_ROTATION_LOCK_BTN)
+               DRM_DEBUG_DRIVER("Button array event is not supported (rotation lock)\n");
+       if (iuer & ASLE_IUER_VOLUME_DOWN_BTN)
+               DRM_DEBUG_DRIVER("Button array event is not supported (volume down)\n");
+       if (iuer & ASLE_IUER_VOLUME_UP_BTN)
+               DRM_DEBUG_DRIVER("Button array event is not supported (volume up)\n");
+       if (iuer & ASLE_IUER_WINDOWS_BTN)
+               DRM_DEBUG_DRIVER("Button array event is not supported (windows)\n");
+       if (iuer & ASLE_IUER_POWER_BTN)
+               DRM_DEBUG_DRIVER("Button array event is not supported (power)\n");
+
+       return ASLC_BUTTON_ARRAY_FAILED;
+}
+
+static u32 asle_set_convertible(struct drm_device *dev, u32 iuer)
+{
+       if (iuer & ASLE_IUER_CONVERTIBLE)
+               DRM_DEBUG_DRIVER("Convertible is not supported (clamshell)\n");
+       else
+               DRM_DEBUG_DRIVER("Convertible is not supported (slate)\n");
+
+       return ASLC_CONVERTIBLE_FAILED;
+}
+
+static u32 asle_set_docking(struct drm_device *dev, u32 iuer)
+{
+       if (iuer & ASLE_IUER_DOCKING)
+               DRM_DEBUG_DRIVER("Docking is not supported (docked)\n");
+       else
+               DRM_DEBUG_DRIVER("Docking is not supported (undocked)\n");
+
+       return ASLC_DOCKING_FAILED;
+}
+
+static u32 asle_isct_state(struct drm_device *dev)
+{
+       DRM_DEBUG_DRIVER("ISCT is not supported\n");
+       return ASLC_ISCT_STATE_FAILED;
+}
+
+static void asle_work(struct work_struct *work)
+{
+       struct intel_opregion *opregion =
+               container_of(work, struct intel_opregion, asle_work);
+       struct drm_i915_private *dev_priv =
+               container_of(opregion, struct drm_i915_private, opregion);
+       struct drm_device *dev = dev_priv->dev;
        struct opregion_asle __iomem *asle = dev_priv->opregion.asle;
-       u32 asle_stat = 0;
-       u32 asle_req;
+       u32 aslc_stat = 0;
+       u32 aslc_req;
 
        if (!asle)
                return;
 
-       asle_req = ioread32(&asle->aslc) & ASLE_REQ_MSK;
+       aslc_req = ioread32(&asle->aslc);
 
-       if (!asle_req) {
-               DRM_DEBUG_DRIVER("non asle set request??\n");
+       if (!(aslc_req & ASLC_REQ_MSK)) {
+               DRM_DEBUG_DRIVER("No request on ASLC interrupt 0x%08x\n",
+                                aslc_req);
                return;
        }
 
-       if (asle_req & ASLE_SET_ALS_ILLUM)
-               asle_stat |= asle_set_als_illum(dev, ioread32(&asle->alsi));
+       if (aslc_req & ASLC_SET_ALS_ILLUM)
+               aslc_stat |= asle_set_als_illum(dev, ioread32(&asle->alsi));
+
+       if (aslc_req & ASLC_SET_BACKLIGHT)
+               aslc_stat |= asle_set_backlight(dev, ioread32(&asle->bclp));
+
+       if (aslc_req & ASLC_SET_PFIT)
+               aslc_stat |= asle_set_pfit(dev, ioread32(&asle->pfit));
+
+       if (aslc_req & ASLC_SET_PWM_FREQ)
+               aslc_stat |= asle_set_pwm_freq(dev, ioread32(&asle->pfmb));
 
-       if (asle_req & ASLE_SET_BACKLIGHT)
-               asle_stat |= asle_set_backlight(dev, ioread32(&asle->bclp));
+       if (aslc_req & ASLC_SUPPORTED_ROTATION_ANGLES)
+               aslc_stat |= asle_set_supported_rotation_angles(dev,
+                                                       ioread32(&asle->srot));
 
-       if (asle_req & ASLE_SET_PFIT)
-               asle_stat |= asle_set_pfit(dev, ioread32(&asle->pfit));
+       if (aslc_req & ASLC_BUTTON_ARRAY)
+               aslc_stat |= asle_set_button_array(dev, ioread32(&asle->iuer));
 
-       if (asle_req & ASLE_SET_PWM_FREQ)
-               asle_stat |= asle_set_pwm_freq(dev, ioread32(&asle->pfmb));
+       if (aslc_req & ASLC_CONVERTIBLE_INDICATOR)
+               aslc_stat |= asle_set_convertible(dev, ioread32(&asle->iuer));
 
-       iowrite32(asle_stat, &asle->aslc);
+       if (aslc_req & ASLC_DOCKING_INDICATOR)
+               aslc_stat |= asle_set_docking(dev, ioread32(&asle->iuer));
+
+       if (aslc_req & ASLC_ISCT_STATE_CHANGE)
+               aslc_stat |= asle_isct_state(dev);
+
+       iowrite32(aslc_stat, &asle->aslc);
+}
+
+void intel_opregion_asle_intr(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->opregion.asle)
+               schedule_work(&dev_priv->opregion.asle_work);
 }
 
 #define ACPI_EV_DISPLAY_SWITCH (1<<0)
@@ -289,7 +638,7 @@ static void intel_didl_outputs(struct drm_device *dev)
        u32 temp;
        int i = 0;
 
-       handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
+       handle = ACPI_HANDLE(&dev->pdev->dev);
        if (!handle || acpi_bus_get_device(handle, &acpi_dev))
                return;
 
@@ -432,6 +781,8 @@ void intel_opregion_fini(struct drm_device *dev)
        if (opregion->asle)
                iowrite32(ASLE_ARDY_NOT_READY, &opregion->asle->ardy);
 
+       cancel_work_sync(&dev_priv->opregion.asle_work);
+
        if (opregion->acpi) {
                iowrite32(0, &opregion->acpi->drdy);
 
@@ -446,8 +797,68 @@ void intel_opregion_fini(struct drm_device *dev)
        opregion->swsci = NULL;
        opregion->asle = NULL;
        opregion->vbt = NULL;
+       opregion->lid_state = NULL;
 }
-#endif
+
+static void swsci_setup(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_opregion *opregion = &dev_priv->opregion;
+       bool requested_callbacks = false;
+       u32 tmp;
+
+       /* Sub-function code 0 is okay, let's allow them. */
+       opregion->swsci_gbda_sub_functions = 1;
+       opregion->swsci_sbcb_sub_functions = 1;
+
+       /* We use GBDA to ask for supported GBDA calls. */
+       if (swsci(dev, SWSCI_GBDA_SUPPORTED_CALLS, 0, &tmp) == 0) {
+               /* make the bits match the sub-function codes */
+               tmp <<= 1;
+               opregion->swsci_gbda_sub_functions |= tmp;
+       }
+
+       /*
+        * We also use GBDA to ask for _requested_ SBCB callbacks. The driver
+        * must not call interfaces that are not specifically requested by the
+        * bios.
+        */
+       if (swsci(dev, SWSCI_GBDA_REQUESTED_CALLBACKS, 0, &tmp) == 0) {
+               /* here, the bits already match sub-function codes */
+               opregion->swsci_sbcb_sub_functions |= tmp;
+               requested_callbacks = true;
+       }
+
+       /*
+        * But we use SBCB to ask for _supported_ SBCB calls. This does not mean
+        * the callback is _requested_. But we still can't call interfaces that
+        * are not requested.
+        */
+       if (swsci(dev, SWSCI_SBCB_SUPPORTED_CALLBACKS, 0, &tmp) == 0) {
+               /* make the bits match the sub-function codes */
+               u32 low = tmp & 0x7ff;
+               u32 high = tmp & ~0xfff; /* bit 11 is reserved */
+               tmp = (high << 4) | (low << 1) | 1;
+
+               /* best guess what to do with supported wrt requested */
+               if (requested_callbacks) {
+                       u32 req = opregion->swsci_sbcb_sub_functions;
+                       if ((req & tmp) != req)
+                               DRM_DEBUG_DRIVER("SWSCI BIOS requested (%08x) SBCB callbacks that are not supported (%08x)\n", req, tmp);
+                       /* XXX: for now, trust the requested callbacks */
+                       /* opregion->swsci_sbcb_sub_functions &= tmp; */
+               } else {
+                       opregion->swsci_sbcb_sub_functions |= tmp;
+               }
+       }
+
+       DRM_DEBUG_DRIVER("SWSCI GBDA callbacks %08x, SBCB callbacks %08x\n",
+                        opregion->swsci_gbda_sub_functions,
+                        opregion->swsci_sbcb_sub_functions);
+}
+#else /* CONFIG_ACPI */
+static inline void swsci_setup(struct drm_device *dev) {}
+#endif  /* CONFIG_ACPI */
 
 int intel_opregion_setup(struct drm_device *dev)
 {
@@ -465,6 +876,10 @@ int intel_opregion_setup(struct drm_device *dev)
                return -ENOTSUPP;
        }
 
+#ifdef CONFIG_ACPI
+       INIT_WORK(&opregion->asle_work, asle_work);
+#endif
+
        base = acpi_os_ioremap(asls, OPREGION_SIZE);
        if (!base)
                return -ENOMEM;
@@ -490,6 +905,7 @@ int intel_opregion_setup(struct drm_device *dev)
        if (mboxes & MBOX_SWSCI) {
                DRM_DEBUG_DRIVER("SWSCI supported\n");
                opregion->swsci = base + OPREGION_SWSCI_OFFSET;
+               swsci_setup(dev);
        }
        if (mboxes & MBOX_ASLE) {
                DRM_DEBUG_DRIVER("ASLE supported\n");
index ddfd0ae..a98a990 100644 (file)
@@ -821,14 +821,11 @@ int intel_overlay_switch_off(struct intel_overlay *overlay)
 static int check_overlay_possible_on_crtc(struct intel_overlay *overlay,
                                          struct intel_crtc *crtc)
 {
-       drm_i915_private_t *dev_priv = overlay->dev->dev_private;
-
        if (!crtc->active)
                return -EINVAL;
 
        /* can't use the overlay with double wide pipe */
-       if (INTEL_INFO(overlay->dev)->gen < 4 &&
-           (I915_READ(PIPECONF(crtc->pipe)) & (PIPECONF_DOUBLE_WIDE | PIPECONF_ENABLE)) != PIPECONF_ENABLE)
+       if (crtc->config.double_wide)
                return -EINVAL;
 
        return 0;
@@ -1056,7 +1053,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
                return ret;
        }
 
-       params = kmalloc(sizeof(struct put_image_params), GFP_KERNEL);
+       params = kmalloc(sizeof(*params), GFP_KERNEL);
        if (!params)
                return -ENOMEM;
 
@@ -1323,7 +1320,7 @@ void intel_setup_overlay(struct drm_device *dev)
        if (!HAS_OVERLAY(dev))
                return;
 
-       overlay = kzalloc(sizeof(struct intel_overlay), GFP_KERNEL);
+       overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
        if (!overlay)
                return;
 
index 293564a..f161ac0 100644 (file)
@@ -50,23 +50,22 @@ intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
                        struct intel_crtc_config *pipe_config,
                        int fitting_mode)
 {
-       struct drm_display_mode *mode, *adjusted_mode;
+       struct drm_display_mode *adjusted_mode;
        int x, y, width, height;
 
-       mode = &pipe_config->requested_mode;
        adjusted_mode = &pipe_config->adjusted_mode;
 
        x = y = width = height = 0;
 
        /* Native modes don't need fitting */
-       if (adjusted_mode->hdisplay == mode->hdisplay &&
-           adjusted_mode->vdisplay == mode->vdisplay)
+       if (adjusted_mode->hdisplay == pipe_config->pipe_src_w &&
+           adjusted_mode->vdisplay == pipe_config->pipe_src_h)
                goto done;
 
        switch (fitting_mode) {
        case DRM_MODE_SCALE_CENTER:
-               width = mode->hdisplay;
-               height = mode->vdisplay;
+               width = pipe_config->pipe_src_w;
+               height = pipe_config->pipe_src_h;
                x = (adjusted_mode->hdisplay - width + 1)/2;
                y = (adjusted_mode->vdisplay - height + 1)/2;
                break;
@@ -74,17 +73,19 @@ intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
        case DRM_MODE_SCALE_ASPECT:
                /* Scale but preserve the aspect ratio */
                {
-                       u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
-                       u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
+                       u32 scaled_width = adjusted_mode->hdisplay
+                               * pipe_config->pipe_src_h;
+                       u32 scaled_height = pipe_config->pipe_src_w
+                               * adjusted_mode->vdisplay;
                        if (scaled_width > scaled_height) { /* pillar */
-                               width = scaled_height / mode->vdisplay;
+                               width = scaled_height / pipe_config->pipe_src_h;
                                if (width & 1)
                                        width++;
                                x = (adjusted_mode->hdisplay - width + 1) / 2;
                                y = 0;
                                height = adjusted_mode->vdisplay;
                        } else if (scaled_width < scaled_height) { /* letter */
-                               height = scaled_width / mode->hdisplay;
+                               height = scaled_width / pipe_config->pipe_src_w;
                                if (height & 1)
                                    height++;
                                y = (adjusted_mode->vdisplay - height + 1) / 2;
@@ -171,20 +172,96 @@ static inline u32 panel_fitter_scaling(u32 source, u32 target)
        return (FACTOR * ratio + FACTOR/2) / FACTOR;
 }
 
+static void i965_scale_aspect(struct intel_crtc_config *pipe_config,
+                             u32 *pfit_control)
+{
+       struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
+       u32 scaled_width = adjusted_mode->hdisplay *
+               pipe_config->pipe_src_h;
+       u32 scaled_height = pipe_config->pipe_src_w *
+               adjusted_mode->vdisplay;
+
+       /* 965+ is easy, it does everything in hw */
+       if (scaled_width > scaled_height)
+               *pfit_control |= PFIT_ENABLE |
+                       PFIT_SCALING_PILLAR;
+       else if (scaled_width < scaled_height)
+               *pfit_control |= PFIT_ENABLE |
+                       PFIT_SCALING_LETTER;
+       else if (adjusted_mode->hdisplay != pipe_config->pipe_src_w)
+               *pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
+}
+
+static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config,
+                             u32 *pfit_control, u32 *pfit_pgm_ratios,
+                             u32 *border)
+{
+       struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
+       u32 scaled_width = adjusted_mode->hdisplay *
+               pipe_config->pipe_src_h;
+       u32 scaled_height = pipe_config->pipe_src_w *
+               adjusted_mode->vdisplay;
+       u32 bits;
+
+       /*
+        * For earlier chips we have to calculate the scaling
+        * ratio by hand and program it into the
+        * PFIT_PGM_RATIO register
+        */
+       if (scaled_width > scaled_height) { /* pillar */
+               centre_horizontally(adjusted_mode,
+                                   scaled_height /
+                                   pipe_config->pipe_src_h);
+
+               *border = LVDS_BORDER_ENABLE;
+               if (pipe_config->pipe_src_h != adjusted_mode->vdisplay) {
+                       bits = panel_fitter_scaling(pipe_config->pipe_src_h,
+                                                   adjusted_mode->vdisplay);
+
+                       *pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
+                                            bits << PFIT_VERT_SCALE_SHIFT);
+                       *pfit_control |= (PFIT_ENABLE |
+                                         VERT_INTERP_BILINEAR |
+                                         HORIZ_INTERP_BILINEAR);
+               }
+       } else if (scaled_width < scaled_height) { /* letter */
+               centre_vertically(adjusted_mode,
+                                 scaled_width /
+                                 pipe_config->pipe_src_w);
+
+               *border = LVDS_BORDER_ENABLE;
+               if (pipe_config->pipe_src_w != adjusted_mode->hdisplay) {
+                       bits = panel_fitter_scaling(pipe_config->pipe_src_w,
+                                                   adjusted_mode->hdisplay);
+
+                       *pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
+                                            bits << PFIT_VERT_SCALE_SHIFT);
+                       *pfit_control |= (PFIT_ENABLE |
+                                         VERT_INTERP_BILINEAR |
+                                         HORIZ_INTERP_BILINEAR);
+               }
+       } else {
+               /* Aspects match, Let hw scale both directions */
+               *pfit_control |= (PFIT_ENABLE |
+                                 VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
+                                 VERT_INTERP_BILINEAR |
+                                 HORIZ_INTERP_BILINEAR);
+       }
+}
+
 void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
                              struct intel_crtc_config *pipe_config,
                              int fitting_mode)
 {
        struct drm_device *dev = intel_crtc->base.dev;
        u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
-       struct drm_display_mode *mode, *adjusted_mode;
+       struct drm_display_mode *adjusted_mode;
 
-       mode = &pipe_config->requested_mode;
        adjusted_mode = &pipe_config->adjusted_mode;
 
        /* Native modes don't need fitting */
-       if (adjusted_mode->hdisplay == mode->hdisplay &&
-           adjusted_mode->vdisplay == mode->vdisplay)
+       if (adjusted_mode->hdisplay == pipe_config->pipe_src_w &&
+           adjusted_mode->vdisplay == pipe_config->pipe_src_h)
                goto out;
 
        switch (fitting_mode) {
@@ -193,81 +270,25 @@ void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
                 * For centered modes, we have to calculate border widths &
                 * heights and modify the values programmed into the CRTC.
                 */
-               centre_horizontally(adjusted_mode, mode->hdisplay);
-               centre_vertically(adjusted_mode, mode->vdisplay);
+               centre_horizontally(adjusted_mode, pipe_config->pipe_src_w);
+               centre_vertically(adjusted_mode, pipe_config->pipe_src_h);
                border = LVDS_BORDER_ENABLE;
                break;
        case DRM_MODE_SCALE_ASPECT:
                /* Scale but preserve the aspect ratio */
-               if (INTEL_INFO(dev)->gen >= 4) {
-                       u32 scaled_width = adjusted_mode->hdisplay *
-                               mode->vdisplay;
-                       u32 scaled_height = mode->hdisplay *
-                               adjusted_mode->vdisplay;
-
-                       /* 965+ is easy, it does everything in hw */
-                       if (scaled_width > scaled_height)
-                               pfit_control |= PFIT_ENABLE |
-                                       PFIT_SCALING_PILLAR;
-                       else if (scaled_width < scaled_height)
-                               pfit_control |= PFIT_ENABLE |
-                                       PFIT_SCALING_LETTER;
-                       else if (adjusted_mode->hdisplay != mode->hdisplay)
-                               pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
-               } else {
-                       u32 scaled_width = adjusted_mode->hdisplay *
-                               mode->vdisplay;
-                       u32 scaled_height = mode->hdisplay *
-                               adjusted_mode->vdisplay;
-                       /*
-                        * For earlier chips we have to calculate the scaling
-                        * ratio by hand and program it into the
-                        * PFIT_PGM_RATIO register
-                        */
-                       if (scaled_width > scaled_height) { /* pillar */
-                               centre_horizontally(adjusted_mode,
-                                                   scaled_height /
-                                                   mode->vdisplay);
-
-                               border = LVDS_BORDER_ENABLE;
-                               if (mode->vdisplay != adjusted_mode->vdisplay) {
-                                       u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay);
-                                       pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
-                                                           bits << PFIT_VERT_SCALE_SHIFT);
-                                       pfit_control |= (PFIT_ENABLE |
-                                                        VERT_INTERP_BILINEAR |
-                                                        HORIZ_INTERP_BILINEAR);
-                               }
-                       } else if (scaled_width < scaled_height) { /* letter */
-                               centre_vertically(adjusted_mode,
-                                                 scaled_width /
-                                                 mode->hdisplay);
-
-                               border = LVDS_BORDER_ENABLE;
-                               if (mode->hdisplay != adjusted_mode->hdisplay) {
-                                       u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay);
-                                       pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
-                                                           bits << PFIT_VERT_SCALE_SHIFT);
-                                       pfit_control |= (PFIT_ENABLE |
-                                                        VERT_INTERP_BILINEAR |
-                                                        HORIZ_INTERP_BILINEAR);
-                               }
-                       } else {
-                               /* Aspects match, Let hw scale both directions */
-                               pfit_control |= (PFIT_ENABLE |
-                                                VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
-                                                VERT_INTERP_BILINEAR |
-                                                HORIZ_INTERP_BILINEAR);
-                       }
-               }
+               if (INTEL_INFO(dev)->gen >= 4)
+                       i965_scale_aspect(pipe_config, &pfit_control);
+               else
+                       i9xx_scale_aspect(pipe_config, &pfit_control,
+                                         &pfit_pgm_ratios, &border);
                break;
        case DRM_MODE_SCALE_FULLSCREEN:
                /*
                 * Full scaling, even if it changes the aspect ratio.
                 * Fortunately this is all done for us in hw.
                 */
-               if (mode->vdisplay != adjusted_mode->vdisplay ||
-                   mode->hdisplay != adjusted_mode->hdisplay) {
+               if (pipe_config->pipe_src_h != adjusted_mode->vdisplay ||
+                   pipe_config->pipe_src_w != adjusted_mode->hdisplay) {
                        pfit_control |= PFIT_ENABLE;
                        if (INTEL_INFO(dev)->gen >= 4)
                                pfit_control |= PFIT_SCALING_AUTO;
@@ -308,7 +329,7 @@ static int is_backlight_combination_mode(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (INTEL_INFO(dev)->gen >= 4)
+       if (IS_GEN4(dev))
                return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
 
        if (IS_GEN2(dev))
@@ -320,7 +341,7 @@ static int is_backlight_combination_mode(struct drm_device *dev)
 /* XXX: query mode clock or hardware clock and program max PWM appropriately
  * when it's 0.
  */
-static u32 i915_read_blc_pwm_ctl(struct drm_device *dev)
+static u32 i915_read_blc_pwm_ctl(struct drm_device *dev, enum pipe pipe)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 val;
@@ -337,6 +358,21 @@ static u32 i915_read_blc_pwm_ctl(struct drm_device *dev)
                        val = dev_priv->regfile.saveBLC_PWM_CTL2;
                        I915_WRITE(BLC_PWM_PCH_CTL2, val);
                }
+       } else if (IS_VALLEYVIEW(dev)) {
+               val = I915_READ(VLV_BLC_PWM_CTL(pipe));
+               if (dev_priv->regfile.saveBLC_PWM_CTL == 0) {
+                       dev_priv->regfile.saveBLC_PWM_CTL = val;
+                       dev_priv->regfile.saveBLC_PWM_CTL2 =
+                               I915_READ(VLV_BLC_PWM_CTL2(pipe));
+               } else if (val == 0) {
+                       val = dev_priv->regfile.saveBLC_PWM_CTL;
+                       I915_WRITE(VLV_BLC_PWM_CTL(pipe), val);
+                       I915_WRITE(VLV_BLC_PWM_CTL2(pipe),
+                                  dev_priv->regfile.saveBLC_PWM_CTL2);
+               }
+
+               if (!val)
+                       val = 0x0f42ffff;
        } else {
                val = I915_READ(BLC_PWM_CTL);
                if (dev_priv->regfile.saveBLC_PWM_CTL == 0) {
@@ -356,11 +392,12 @@ static u32 i915_read_blc_pwm_ctl(struct drm_device *dev)
        return val;
 }
 
-static u32 intel_panel_get_max_backlight(struct drm_device *dev)
+static u32 intel_panel_get_max_backlight(struct drm_device *dev,
+                                        enum pipe pipe)
 {
        u32 max;
 
-       max = i915_read_blc_pwm_ctl(dev);
+       max = i915_read_blc_pwm_ctl(dev, pipe);
 
        if (HAS_PCH_SPLIT(dev)) {
                max >>= 16;
@@ -386,7 +423,8 @@ MODULE_PARM_DESC(invert_brightness, "Invert backlight brightness "
        "to dri-devel@lists.freedesktop.org, if your machine needs it. "
        "It will then be included in an upcoming module version.");
 module_param_named(invert_brightness, i915_panel_invert_brightness, int, 0600);
-static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val)
+static u32 intel_panel_compute_brightness(struct drm_device *dev,
+                                         enum pipe pipe, u32 val)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -395,7 +433,7 @@ static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val)
 
        if (i915_panel_invert_brightness > 0 ||
            dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) {
-               u32 max = intel_panel_get_max_backlight(dev);
+               u32 max = intel_panel_get_max_backlight(dev, pipe);
                if (max)
                        return max - val;
        }
@@ -403,18 +441,25 @@ static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val)
        return val;
 }
 
-static u32 intel_panel_get_backlight(struct drm_device *dev)
+static u32 intel_panel_get_backlight(struct drm_device *dev,
+                                    enum pipe pipe)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 val;
        unsigned long flags;
+       int reg;
 
        spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
        if (HAS_PCH_SPLIT(dev)) {
                val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
        } else {
-               val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
+               if (IS_VALLEYVIEW(dev))
+                       reg = VLV_BLC_PWM_CTL(pipe);
+               else
+                       reg = BLC_PWM_CTL;
+
+               val = I915_READ(reg) & BACKLIGHT_DUTY_CYCLE_MASK;
                if (INTEL_INFO(dev)->gen < 4)
                        val >>= 1;
 
@@ -426,7 +471,7 @@ static u32 intel_panel_get_backlight(struct drm_device *dev)
                }
        }
 
-       val = intel_panel_compute_brightness(dev, val);
+       val = intel_panel_compute_brightness(dev, pipe, val);
 
        spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 
@@ -441,19 +486,21 @@ static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
        I915_WRITE(BLC_PWM_CPU_CTL, val | level);
 }
 
-static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level)
+static void intel_panel_actually_set_backlight(struct drm_device *dev,
+                                              enum pipe pipe, u32 level)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 tmp;
+       int reg;
 
        DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
-       level = intel_panel_compute_brightness(dev, level);
+       level = intel_panel_compute_brightness(dev, pipe, level);
 
        if (HAS_PCH_SPLIT(dev))
                return intel_pch_panel_set_backlight(dev, level);
 
        if (is_backlight_combination_mode(dev)) {
-               u32 max = intel_panel_get_max_backlight(dev);
+               u32 max = intel_panel_get_max_backlight(dev, pipe);
                u8 lbpc;
 
                /* we're screwed, but keep behaviour backwards compatible */
@@ -465,23 +512,34 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level
                pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc);
        }
 
-       tmp = I915_READ(BLC_PWM_CTL);
+       if (IS_VALLEYVIEW(dev))
+               reg = VLV_BLC_PWM_CTL(pipe);
+       else
+               reg = BLC_PWM_CTL;
+
+       tmp = I915_READ(reg);
        if (INTEL_INFO(dev)->gen < 4)
                level <<= 1;
        tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
-       I915_WRITE(BLC_PWM_CTL, tmp | level);
+       I915_WRITE(reg, tmp | level);
 }
 
 /* set backlight brightness to level in range [0..max] */
-void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max)
+void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
+                              u32 max)
 {
+       struct drm_device *dev = connector->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe = intel_get_pipe_from_connector(connector);
        u32 freq;
        unsigned long flags;
 
+       if (pipe == INVALID_PIPE)
+               return;
+
        spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
-       freq = intel_panel_get_max_backlight(dev);
+       freq = intel_panel_get_max_backlight(dev, pipe);
        if (!freq) {
                /* we are screwed, bail out */
                goto out;
@@ -498,16 +556,21 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max)
                dev_priv->backlight.device->props.brightness = level;
 
        if (dev_priv->backlight.enabled)
-               intel_panel_actually_set_backlight(dev, level);
+               intel_panel_actually_set_backlight(dev, pipe, level);
 out:
        spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 }
 
-void intel_panel_disable_backlight(struct drm_device *dev)
+void intel_panel_disable_backlight(struct intel_connector *connector)
 {
+       struct drm_device *dev = connector->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe = intel_get_pipe_from_connector(connector);
        unsigned long flags;
 
+       if (pipe == INVALID_PIPE)
+               return;
+
        /*
         * Do not disable backlight on the vgaswitcheroo path. When switching
         * away from i915, the other client may depend on i915 to handle the
@@ -522,12 +585,17 @@ void intel_panel_disable_backlight(struct drm_device *dev)
        spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
        dev_priv->backlight.enabled = false;
-       intel_panel_actually_set_backlight(dev, 0);
+       intel_panel_actually_set_backlight(dev, pipe, 0);
 
        if (INTEL_INFO(dev)->gen >= 4) {
                uint32_t reg, tmp;
 
-               reg = HAS_PCH_SPLIT(dev) ? BLC_PWM_CPU_CTL2 : BLC_PWM_CTL2;
+               if (HAS_PCH_SPLIT(dev))
+                       reg = BLC_PWM_CPU_CTL2;
+               else if (IS_VALLEYVIEW(dev))
+                       reg = VLV_BLC_PWM_CTL2(pipe);
+               else
+                       reg = BLC_PWM_CTL2;
 
                I915_WRITE(reg, I915_READ(reg) & ~BLM_PWM_ENABLE);
 
@@ -541,18 +609,25 @@ void intel_panel_disable_backlight(struct drm_device *dev)
        spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 }
 
-void intel_panel_enable_backlight(struct drm_device *dev,
-                                 enum pipe pipe)
+void intel_panel_enable_backlight(struct intel_connector *connector)
 {
+       struct drm_device *dev = connector->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe = intel_get_pipe_from_connector(connector);
        enum transcoder cpu_transcoder =
                intel_pipe_to_cpu_transcoder(dev_priv, pipe);
        unsigned long flags;
 
+       if (pipe == INVALID_PIPE)
+               return;
+
+       DRM_DEBUG_KMS("pipe %c\n", pipe_name(pipe));
+
        spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
        if (dev_priv->backlight.level == 0) {
-               dev_priv->backlight.level = intel_panel_get_max_backlight(dev);
+               dev_priv->backlight.level = intel_panel_get_max_backlight(dev,
+                                                                         pipe);
                if (dev_priv->backlight.device)
                        dev_priv->backlight.device->props.brightness =
                                dev_priv->backlight.level;
@@ -561,8 +636,12 @@ void intel_panel_enable_backlight(struct drm_device *dev,
        if (INTEL_INFO(dev)->gen >= 4) {
                uint32_t reg, tmp;
 
-               reg = HAS_PCH_SPLIT(dev) ? BLC_PWM_CPU_CTL2 : BLC_PWM_CTL2;
-
+               if (HAS_PCH_SPLIT(dev))
+                       reg = BLC_PWM_CPU_CTL2;
+               else if (IS_VALLEYVIEW(dev))
+                       reg = VLV_BLC_PWM_CTL2(pipe);
+               else
+                       reg = BLC_PWM_CTL2;
 
                tmp = I915_READ(reg);
 
@@ -602,16 +681,41 @@ set_level:
         * registers are set.
         */
        dev_priv->backlight.enabled = true;
-       intel_panel_actually_set_backlight(dev, dev_priv->backlight.level);
+       intel_panel_actually_set_backlight(dev, pipe,
+                                          dev_priv->backlight.level);
 
        spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 }
 
+/* FIXME: use VBT vals to init PWM_CTL and PWM_CTL2 correctly */
+static void intel_panel_init_backlight_regs(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (IS_VALLEYVIEW(dev)) {
+               enum pipe pipe;
+
+               for_each_pipe(pipe) {
+                       u32 cur_val = I915_READ(VLV_BLC_PWM_CTL(pipe));
+
+                       /* Skip if the modulation freq is already set */
+                       if (cur_val & ~BACKLIGHT_DUTY_CYCLE_MASK)
+                               continue;
+
+                       cur_val &= BACKLIGHT_DUTY_CYCLE_MASK;
+                       I915_WRITE(VLV_BLC_PWM_CTL(pipe), (0xf42 << 16) |
+                                  cur_val);
+               }
+       }
+}
+
 static void intel_panel_init_backlight(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       dev_priv->backlight.level = intel_panel_get_backlight(dev);
+       intel_panel_init_backlight_regs(dev);
+
+       dev_priv->backlight.level = intel_panel_get_backlight(dev, 0);
        dev_priv->backlight.enabled = dev_priv->backlight.level != 0;
 }
 
@@ -637,19 +741,34 @@ intel_panel_detect(struct drm_device *dev)
        }
 }
 
-#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+#if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE)
 static int intel_panel_update_status(struct backlight_device *bd)
 {
-       struct drm_device *dev = bl_get_data(bd);
-       intel_panel_set_backlight(dev, bd->props.brightness,
+       struct intel_connector *connector = bl_get_data(bd);
+       struct drm_device *dev = connector->base.dev;
+
+       mutex_lock(&dev->mode_config.mutex);
+       DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n",
+                     bd->props.brightness, bd->props.max_brightness);
+       intel_panel_set_backlight(connector, bd->props.brightness,
                                  bd->props.max_brightness);
+       mutex_unlock(&dev->mode_config.mutex);
        return 0;
 }
 
 static int intel_panel_get_brightness(struct backlight_device *bd)
 {
-       struct drm_device *dev = bl_get_data(bd);
-       return intel_panel_get_backlight(dev);
+       struct intel_connector *connector = bl_get_data(bd);
+       struct drm_device *dev = connector->base.dev;
+       enum pipe pipe;
+
+       mutex_lock(&dev->mode_config.mutex);
+       pipe = intel_get_pipe_from_connector(connector);
+       mutex_unlock(&dev->mode_config.mutex);
+       if (pipe == INVALID_PIPE)
+               return 0;
+
+       return intel_panel_get_backlight(connector->base.dev, pipe);
 }
 
 static const struct backlight_ops intel_panel_bl_ops = {
@@ -674,7 +793,7 @@ int intel_panel_setup_backlight(struct drm_connector *connector)
        props.brightness = dev_priv->backlight.level;
 
        spin_lock_irqsave(&dev_priv->backlight.lock, flags);
-       props.max_brightness = intel_panel_get_max_backlight(dev);
+       props.max_brightness = intel_panel_get_max_backlight(dev, 0);
        spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 
        if (props.max_brightness == 0) {
@@ -683,7 +802,8 @@ int intel_panel_setup_backlight(struct drm_connector *connector)
        }
        dev_priv->backlight.device =
                backlight_device_register("intel_backlight",
-                                         &connector->kdev, dev,
+                                         connector->kdev,
+                                         to_intel_connector(connector),
                                          &intel_panel_bl_ops, &props);
 
        if (IS_ERR(dev_priv->backlight.device)) {
index 26c2ea3..0a07d7c 100644 (file)
 #include <linux/module.h>
 #include <drm/i915_powerwell.h>
 
+/**
+ * RC6 is a special power stage which allows the GPU to enter an very
+ * low-voltage mode when idle, using down to 0V while at this stage.  This
+ * stage is entered automatically when the GPU is idle when RC6 support is
+ * enabled, and as soon as new workload arises GPU wakes up automatically as well.
+ *
+ * There are different RC6 modes available in Intel GPU, which differentiate
+ * among each other with the latency required to enter and leave RC6 and
+ * voltage consumed by the GPU in different states.
+ *
+ * The combination of the following flags define which states GPU is allowed
+ * to enter, while RC6 is the normal RC6 state, RC6p is the deep RC6, and
+ * RC6pp is deepest RC6. Their support by hardware varies according to the
+ * GPU, BIOS, chipset and platform. RC6 is usually the safest one and the one
+ * which brings the most power savings; deeper states save more power, but
+ * require higher latency to switch to and wake up.
+ */
+#define INTEL_RC6_ENABLE                       (1<<0)
+#define INTEL_RC6p_ENABLE                      (1<<1)
+#define INTEL_RC6pp_ENABLE                     (1<<2)
+
 /* FBC, or Frame Buffer Compression, is a technique employed to compress the
  * framebuffer contents in-memory, aiming at reducing the required bandwidth
  * during in-memory transfers and, therefore, reduce the power packet.
  * i915.i915_enable_fbc parameter
  */
 
-static bool intel_crtc_active(struct drm_crtc *crtc)
-{
-       /* Be paranoid as we can arrive here with only partial
-        * state retrieved from the hardware during setup.
-        */
-       return to_intel_crtc(crtc)->active && crtc->fb && crtc->mode.clock;
-}
-
 static void i8xx_disable_fbc(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -241,18 +254,6 @@ static void ironlake_disable_fbc(struct drm_device *dev)
                dpfc_ctl &= ~DPFC_CTL_EN;
                I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl);
 
-               if (IS_IVYBRIDGE(dev))
-                       /* WaFbcDisableDpfcClockGating:ivb */
-                       I915_WRITE(ILK_DSPCLK_GATE_D,
-                                  I915_READ(ILK_DSPCLK_GATE_D) &
-                                  ~ILK_DPFCUNIT_CLOCK_GATE_DISABLE);
-
-               if (IS_HASWELL(dev))
-                       /* WaFbcDisableDpfcClockGating:hsw */
-                       I915_WRITE(HSW_CLKGATE_DISABLE_PART_1,
-                                  I915_READ(HSW_CLKGATE_DISABLE_PART_1) &
-                                  ~HSW_DPFC_GATING_DISABLE);
-
                DRM_DEBUG_KMS("disabled FBC\n");
        }
 }
@@ -282,18 +283,10 @@ static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        if (IS_IVYBRIDGE(dev)) {
                /* WaFbcAsynchFlipDisableFbcQueue:ivb */
                I915_WRITE(ILK_DISPLAY_CHICKEN1, ILK_FBCQ_DIS);
-               /* WaFbcDisableDpfcClockGating:ivb */
-               I915_WRITE(ILK_DSPCLK_GATE_D,
-                          I915_READ(ILK_DSPCLK_GATE_D) |
-                          ILK_DPFCUNIT_CLOCK_GATE_DISABLE);
        } else {
                /* WaFbcAsynchFlipDisableFbcQueue:hsw */
                I915_WRITE(HSW_PIPE_SLICE_CHICKEN_1(intel_crtc->pipe),
                           HSW_BYPASS_FBC_QUEUE);
-               /* WaFbcDisableDpfcClockGating:hsw */
-               I915_WRITE(HSW_CLKGATE_DISABLE_PART_1,
-                          I915_READ(HSW_CLKGATE_DISABLE_PART_1) |
-                          HSW_DPFC_GATING_DISABLE);
        }
 
        I915_WRITE(SNB_DPFC_CTL_SA,
@@ -378,7 +371,7 @@ static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
 
        intel_cancel_fbc_work(dev_priv);
 
-       work = kzalloc(sizeof *work, GFP_KERNEL);
+       work = kzalloc(sizeof(*work), GFP_KERNEL);
        if (work == NULL) {
                DRM_ERROR("Failed to allocate FBC work structure\n");
                dev_priv->display.enable_fbc(crtc, interval);
@@ -458,7 +451,8 @@ void intel_update_fbc(struct drm_device *dev)
        struct drm_framebuffer *fb;
        struct intel_framebuffer *intel_fb;
        struct drm_i915_gem_object *obj;
-       unsigned int max_hdisplay, max_vdisplay;
+       const struct drm_display_mode *adjusted_mode;
+       unsigned int max_width, max_height;
 
        if (!I915_HAS_FBC(dev)) {
                set_no_fbc_reason(dev_priv, FBC_UNSUPPORTED);
@@ -482,7 +476,7 @@ void intel_update_fbc(struct drm_device *dev)
         */
        list_for_each_entry(tmp_crtc, &dev->mode_config.crtc_list, head) {
                if (intel_crtc_active(tmp_crtc) &&
-                   !to_intel_crtc(tmp_crtc)->primary_disabled) {
+                   to_intel_crtc(tmp_crtc)->primary_enabled) {
                        if (crtc) {
                                if (set_no_fbc_reason(dev_priv, FBC_MULTIPLE_PIPES))
                                        DRM_DEBUG_KMS("more than one pipe active, disabling compression\n");
@@ -502,6 +496,7 @@ void intel_update_fbc(struct drm_device *dev)
        fb = crtc->fb;
        intel_fb = to_intel_framebuffer(fb);
        obj = intel_fb->obj;
+       adjusted_mode = &intel_crtc->config.adjusted_mode;
 
        if (i915_enable_fbc < 0 &&
            INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev)) {
@@ -514,8 +509,8 @@ void intel_update_fbc(struct drm_device *dev)
                        DRM_DEBUG_KMS("fbc disabled per module param\n");
                goto out_disable;
        }
-       if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) ||
-           (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) {
+       if ((adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ||
+           (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)) {
                if (set_no_fbc_reason(dev_priv, FBC_UNSUPPORTED_MODE))
                        DRM_DEBUG_KMS("mode incompatible with compression, "
                                      "disabling\n");
@@ -523,14 +518,14 @@ void intel_update_fbc(struct drm_device *dev)
        }
 
        if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
-               max_hdisplay = 4096;
-               max_vdisplay = 2048;
+               max_width = 4096;
+               max_height = 2048;
        } else {
-               max_hdisplay = 2048;
-               max_vdisplay = 1536;
+               max_width = 2048;
+               max_height = 1536;
        }
-       if ((crtc->mode.hdisplay > max_hdisplay) ||
-           (crtc->mode.vdisplay > max_vdisplay)) {
+       if (intel_crtc->config.pipe_src_w > max_width ||
+           intel_crtc->config.pipe_src_h > max_height) {
                if (set_no_fbc_reason(dev_priv, FBC_MODE_TOO_LARGE))
                        DRM_DEBUG_KMS("mode too large for compression, disabling\n");
                goto out_disable;
@@ -1087,8 +1082,9 @@ static struct drm_crtc *single_enabled_crtc(struct drm_device *dev)
        return enabled;
 }
 
-static void pineview_update_wm(struct drm_device *dev)
+static void pineview_update_wm(struct drm_crtc *unused_crtc)
 {
+       struct drm_device *dev = unused_crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_crtc *crtc;
        const struct cxsr_latency *latency;
@@ -1105,8 +1101,12 @@ static void pineview_update_wm(struct drm_device *dev)
 
        crtc = single_enabled_crtc(dev);
        if (crtc) {
-               int clock = crtc->mode.clock;
+               const struct drm_display_mode *adjusted_mode;
                int pixel_size = crtc->fb->bits_per_pixel / 8;
+               int clock;
+
+               adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode;
+               clock = adjusted_mode->crtc_clock;
 
                /* Display SR */
                wm = intel_calculate_wm(clock, &pineview_display_wm,
@@ -1166,6 +1166,7 @@ static bool g4x_compute_wm0(struct drm_device *dev,
                            int *cursor_wm)
 {
        struct drm_crtc *crtc;
+       const struct drm_display_mode *adjusted_mode;
        int htotal, hdisplay, clock, pixel_size;
        int line_time_us, line_count;
        int entries, tlb_miss;
@@ -1177,9 +1178,10 @@ static bool g4x_compute_wm0(struct drm_device *dev,
                return false;
        }
 
-       htotal = crtc->mode.htotal;
-       hdisplay = crtc->mode.hdisplay;
-       clock = crtc->mode.clock;
+       adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode;
+       clock = adjusted_mode->crtc_clock;
+       htotal = adjusted_mode->htotal;
+       hdisplay = to_intel_crtc(crtc)->config.pipe_src_w;
        pixel_size = crtc->fb->bits_per_pixel / 8;
 
        /* Use the small buffer method to calculate plane watermark */
@@ -1250,6 +1252,7 @@ static bool g4x_compute_srwm(struct drm_device *dev,
                             int *display_wm, int *cursor_wm)
 {
        struct drm_crtc *crtc;
+       const struct drm_display_mode *adjusted_mode;
        int hdisplay, htotal, pixel_size, clock;
        unsigned long line_time_us;
        int line_count, line_size;
@@ -1262,9 +1265,10 @@ static bool g4x_compute_srwm(struct drm_device *dev,
        }
 
        crtc = intel_get_crtc_for_plane(dev, plane);
-       hdisplay = crtc->mode.hdisplay;
-       htotal = crtc->mode.htotal;
-       clock = crtc->mode.clock;
+       adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode;
+       clock = adjusted_mode->crtc_clock;
+       htotal = adjusted_mode->htotal;
+       hdisplay = to_intel_crtc(crtc)->config.pipe_src_w;
        pixel_size = crtc->fb->bits_per_pixel / 8;
 
        line_time_us = (htotal * 1000) / clock;
@@ -1303,7 +1307,7 @@ static bool vlv_compute_drain_latency(struct drm_device *dev,
        if (!intel_crtc_active(crtc))
                return false;
 
-       clock = crtc->mode.clock;       /* VESA DOT Clock */
+       clock = to_intel_crtc(crtc)->config.adjusted_mode.crtc_clock;
        pixel_size = crtc->fb->bits_per_pixel / 8;      /* BPP */
 
        entries = (clock / 1000) * pixel_size;
@@ -1365,8 +1369,9 @@ static void vlv_update_drain_latency(struct drm_device *dev)
 
 #define single_plane_enabled(mask) is_power_of_2(mask)
 
-static void valleyview_update_wm(struct drm_device *dev)
+static void valleyview_update_wm(struct drm_crtc *crtc)
 {
+       struct drm_device *dev = crtc->dev;
        static const int sr_latency_ns = 12000;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
@@ -1424,8 +1429,9 @@ static void valleyview_update_wm(struct drm_device *dev)
                   (cursor_sr << DSPFW_CURSOR_SR_SHIFT));
 }
 
-static void g4x_update_wm(struct drm_device *dev)
+static void g4x_update_wm(struct drm_crtc *crtc)
 {
+       struct drm_device *dev = crtc->dev;
        static const int sr_latency_ns = 12000;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
@@ -1476,8 +1482,9 @@ static void g4x_update_wm(struct drm_device *dev)
                   (cursor_sr << DSPFW_CURSOR_SR_SHIFT));
 }
 
-static void i965_update_wm(struct drm_device *dev)
+static void i965_update_wm(struct drm_crtc *unused_crtc)
 {
+       struct drm_device *dev = unused_crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_crtc *crtc;
        int srwm = 1;
@@ -1488,9 +1495,11 @@ static void i965_update_wm(struct drm_device *dev)
        if (crtc) {
                /* self-refresh has much higher latency */
                static const int sr_latency_ns = 12000;
-               int clock = crtc->mode.clock;
-               int htotal = crtc->mode.htotal;
-               int hdisplay = crtc->mode.hdisplay;
+               const struct drm_display_mode *adjusted_mode =
+                       &to_intel_crtc(crtc)->config.adjusted_mode;
+               int clock = adjusted_mode->crtc_clock;
+               int htotal = adjusted_mode->htotal;
+               int hdisplay = to_intel_crtc(crtc)->config.pipe_src_w;
                int pixel_size = crtc->fb->bits_per_pixel / 8;
                unsigned long line_time_us;
                int entries;
@@ -1541,8 +1550,9 @@ static void i965_update_wm(struct drm_device *dev)
        I915_WRITE(DSPFW3, (cursor_sr << DSPFW_CURSOR_SR_SHIFT));
 }
 
-static void i9xx_update_wm(struct drm_device *dev)
+static void i9xx_update_wm(struct drm_crtc *unused_crtc)
 {
+       struct drm_device *dev = unused_crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        const struct intel_watermark_params *wm_info;
        uint32_t fwater_lo;
@@ -1562,11 +1572,13 @@ static void i9xx_update_wm(struct drm_device *dev)
        fifo_size = dev_priv->display.get_fifo_size(dev, 0);
        crtc = intel_get_crtc_for_plane(dev, 0);
        if (intel_crtc_active(crtc)) {
+               const struct drm_display_mode *adjusted_mode;
                int cpp = crtc->fb->bits_per_pixel / 8;
                if (IS_GEN2(dev))
                        cpp = 4;
 
-               planea_wm = intel_calculate_wm(crtc->mode.clock,
+               adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode;
+               planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock,
                                               wm_info, fifo_size, cpp,
                                               latency_ns);
                enabled = crtc;
@@ -1576,11 +1588,13 @@ static void i9xx_update_wm(struct drm_device *dev)
        fifo_size = dev_priv->display.get_fifo_size(dev, 1);
        crtc = intel_get_crtc_for_plane(dev, 1);
        if (intel_crtc_active(crtc)) {
+               const struct drm_display_mode *adjusted_mode;
                int cpp = crtc->fb->bits_per_pixel / 8;
                if (IS_GEN2(dev))
                        cpp = 4;
 
-               planeb_wm = intel_calculate_wm(crtc->mode.clock,
+               adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode;
+               planeb_wm = intel_calculate_wm(adjusted_mode->crtc_clock,
                                               wm_info, fifo_size, cpp,
                                               latency_ns);
                if (enabled == NULL)
@@ -1607,9 +1621,11 @@ static void i9xx_update_wm(struct drm_device *dev)
        if (HAS_FW_BLC(dev) && enabled) {
                /* self-refresh has much higher latency */
                static const int sr_latency_ns = 6000;
-               int clock = enabled->mode.clock;
-               int htotal = enabled->mode.htotal;
-               int hdisplay = enabled->mode.hdisplay;
+               const struct drm_display_mode *adjusted_mode =
+                       &to_intel_crtc(enabled)->config.adjusted_mode;
+               int clock = adjusted_mode->crtc_clock;
+               int htotal = adjusted_mode->htotal;
+               int hdisplay = to_intel_crtc(crtc)->config.pipe_src_w;
                int pixel_size = enabled->fb->bits_per_pixel / 8;
                unsigned long line_time_us;
                int entries;
@@ -1658,10 +1674,12 @@ static void i9xx_update_wm(struct drm_device *dev)
        }
 }
 
-static void i830_update_wm(struct drm_device *dev)
+static void i830_update_wm(struct drm_crtc *unused_crtc)
 {
+       struct drm_device *dev = unused_crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_crtc *crtc;
+       const struct drm_display_mode *adjusted_mode;
        uint32_t fwater_lo;
        int planea_wm;
 
@@ -1669,7 +1687,9 @@ static void i830_update_wm(struct drm_device *dev)
        if (crtc == NULL)
                return;
 
-       planea_wm = intel_calculate_wm(crtc->mode.clock, &i830_wm_info,
+       adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode;
+       planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock,
+                                      &i830_wm_info,
                                       dev_priv->display.get_fifo_size(dev, 0),
                                       4, latency_ns);
        fwater_lo = I915_READ(FW_BLC) & ~0xfff;
@@ -1741,6 +1761,7 @@ static bool ironlake_compute_srwm(struct drm_device *dev, int level, int plane,
                                  int *fbc_wm, int *display_wm, int *cursor_wm)
 {
        struct drm_crtc *crtc;
+       const struct drm_display_mode *adjusted_mode;
        unsigned long line_time_us;
        int hdisplay, htotal, pixel_size, clock;
        int line_count, line_size;
@@ -1753,9 +1774,10 @@ static bool ironlake_compute_srwm(struct drm_device *dev, int level, int plane,
        }
 
        crtc = intel_get_crtc_for_plane(dev, plane);
-       hdisplay = crtc->mode.hdisplay;
-       htotal = crtc->mode.htotal;
-       clock = crtc->mode.clock;
+       adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode;
+       clock = adjusted_mode->crtc_clock;
+       htotal = adjusted_mode->htotal;
+       hdisplay = to_intel_crtc(crtc)->config.pipe_src_w;
        pixel_size = crtc->fb->bits_per_pixel / 8;
 
        line_time_us = (htotal * 1000) / clock;
@@ -1785,8 +1807,9 @@ static bool ironlake_compute_srwm(struct drm_device *dev, int level, int plane,
                                   display, cursor);
 }
 
-static void ironlake_update_wm(struct drm_device *dev)
+static void ironlake_update_wm(struct drm_crtc *crtc)
 {
+       struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int fbc_wm, plane_wm, cursor_wm;
        unsigned int enabled;
@@ -1868,8 +1891,9 @@ static void ironlake_update_wm(struct drm_device *dev)
         */
 }
 
-static void sandybridge_update_wm(struct drm_device *dev)
+static void sandybridge_update_wm(struct drm_crtc *crtc)
 {
+       struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int latency = dev_priv->wm.pri_latency[0] * 100;        /* In unit 0.1us */
        u32 val;
@@ -1970,8 +1994,9 @@ static void sandybridge_update_wm(struct drm_device *dev)
                   cursor_wm);
 }
 
-static void ivybridge_update_wm(struct drm_device *dev)
+static void ivybridge_update_wm(struct drm_crtc *crtc)
 {
+       struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int latency = dev_priv->wm.pri_latency[0] * 100;        /* In unit 0.1us */
        u32 val;
@@ -2098,7 +2123,7 @@ static uint32_t ilk_pipe_pixel_rate(struct drm_device *dev,
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        uint32_t pixel_rate;
 
-       pixel_rate = intel_crtc->config.adjusted_mode.clock;
+       pixel_rate = intel_crtc->config.adjusted_mode.crtc_clock;
 
        /* We only use IF-ID interlacing. If we ever use PF-ID we'll need to
         * adjust the pixel_rate here. */
@@ -2107,8 +2132,8 @@ static uint32_t ilk_pipe_pixel_rate(struct drm_device *dev,
                uint64_t pipe_w, pipe_h, pfit_w, pfit_h;
                uint32_t pfit_size = intel_crtc->config.pch_pfit.size;
 
-               pipe_w = intel_crtc->config.requested_mode.hdisplay;
-               pipe_h = intel_crtc->config.requested_mode.vdisplay;
+               pipe_w = intel_crtc->config.pipe_src_w;
+               pipe_h = intel_crtc->config.pipe_src_h;
                pfit_w = (pfit_size >> 16) & 0xFFFF;
                pfit_h = pfit_size & 0xFFFF;
                if (pipe_w < pfit_w)
@@ -2176,27 +2201,18 @@ struct hsw_wm_maximums {
        uint16_t fbc;
 };
 
-struct hsw_wm_values {
-       uint32_t wm_pipe[3];
-       uint32_t wm_lp[3];
-       uint32_t wm_lp_spr[3];
-       uint32_t wm_linetime[3];
-       bool enable_fbc_wm;
-};
-
 /* used in computing the new watermarks state */
 struct intel_wm_config {
        unsigned int num_pipes_active;
        bool sprites_enabled;
        bool sprites_scaled;
-       bool fbc_wm_enabled;
 };
 
 /*
  * For both WM_PIPE and WM_LP.
  * mem_value must be in 0.1us units.
  */
-static uint32_t ilk_compute_pri_wm(struct hsw_pipe_wm_parameters *params,
+static uint32_t ilk_compute_pri_wm(const struct hsw_pipe_wm_parameters *params,
                                   uint32_t mem_value,
                                   bool is_lp)
 {
@@ -2225,7 +2241,7 @@ static uint32_t ilk_compute_pri_wm(struct hsw_pipe_wm_parameters *params,
  * For both WM_PIPE and WM_LP.
  * mem_value must be in 0.1us units.
  */
-static uint32_t ilk_compute_spr_wm(struct hsw_pipe_wm_parameters *params,
+static uint32_t ilk_compute_spr_wm(const struct hsw_pipe_wm_parameters *params,
                                   uint32_t mem_value)
 {
        uint32_t method1, method2;
@@ -2248,7 +2264,7 @@ static uint32_t ilk_compute_spr_wm(struct hsw_pipe_wm_parameters *params,
  * For both WM_PIPE and WM_LP.
  * mem_value must be in 0.1us units.
  */
-static uint32_t ilk_compute_cur_wm(struct hsw_pipe_wm_parameters *params,
+static uint32_t ilk_compute_cur_wm(const struct hsw_pipe_wm_parameters *params,
                                   uint32_t mem_value)
 {
        if (!params->active || !params->cur.enabled)
@@ -2262,7 +2278,7 @@ static uint32_t ilk_compute_cur_wm(struct hsw_pipe_wm_parameters *params,
 }
 
 /* Only for WM_LP. */
-static uint32_t ilk_compute_fbc_wm(struct hsw_pipe_wm_parameters *params,
+static uint32_t ilk_compute_fbc_wm(const struct hsw_pipe_wm_parameters *params,
                                   uint32_t pri_val)
 {
        if (!params->active || !params->pri.enabled)
@@ -2275,7 +2291,9 @@ static uint32_t ilk_compute_fbc_wm(struct hsw_pipe_wm_parameters *params,
 
 static unsigned int ilk_display_fifo_size(const struct drm_device *dev)
 {
-       if (INTEL_INFO(dev)->gen >= 7)
+       if (INTEL_INFO(dev)->gen >= 8)
+               return 3072;
+       else if (INTEL_INFO(dev)->gen >= 7)
                return 768;
        else
                return 512;
@@ -2320,7 +2338,9 @@ static unsigned int ilk_plane_wm_max(const struct drm_device *dev,
        }
 
        /* clamp to max that the registers can hold */
-       if (INTEL_INFO(dev)->gen >= 7)
+       if (INTEL_INFO(dev)->gen >= 8)
+               max = level == 0 ? 255 : 2047;
+       else if (INTEL_INFO(dev)->gen >= 7)
                /* IVB/HSW primary/sprite plane watermarks */
                max = level == 0 ? 127 : 1023;
        else if (!is_sprite)
@@ -2350,27 +2370,30 @@ static unsigned int ilk_cursor_wm_max(const struct drm_device *dev,
 }
 
 /* Calculate the maximum FBC watermark */
-static unsigned int ilk_fbc_wm_max(void)
+static unsigned int ilk_fbc_wm_max(struct drm_device *dev)
 {
        /* max that registers can hold */
-       return 15;
+       if (INTEL_INFO(dev)->gen >= 8)
+               return 31;
+       else
+               return 15;
 }
 
-static void ilk_wm_max(struct drm_device *dev,
-                      int level,
-                      const struct intel_wm_config *config,
-                      enum intel_ddb_partitioning ddb_partitioning,
-                      struct hsw_wm_maximums *max)
+static void ilk_compute_wm_maximums(struct drm_device *dev,
+                                   int level,
+                                   const struct intel_wm_config *config,
+                                   enum intel_ddb_partitioning ddb_partitioning,
+                                   struct hsw_wm_maximums *max)
 {
        max->pri = ilk_plane_wm_max(dev, level, config, ddb_partitioning, false);
        max->spr = ilk_plane_wm_max(dev, level, config, ddb_partitioning, true);
        max->cur = ilk_cursor_wm_max(dev, level, config);
-       max->fbc = ilk_fbc_wm_max();
+       max->fbc = ilk_fbc_wm_max(dev);
 }
 
-static bool ilk_check_wm(int level,
-                        const struct hsw_wm_maximums *max,
-                        struct intel_wm_level *result)
+static bool ilk_validate_wm_level(int level,
+                                 const struct hsw_wm_maximums *max,
+                                 struct intel_wm_level *result)
 {
        bool ret;
 
@@ -2406,14 +2429,12 @@ static bool ilk_check_wm(int level,
                result->enable = true;
        }
 
-       DRM_DEBUG_KMS("WM%d: %sabled\n", level, result->enable ? "en" : "dis");
-
        return ret;
 }
 
 static void ilk_compute_wm_level(struct drm_i915_private *dev_priv,
                                 int level,
-                                struct hsw_pipe_wm_parameters *p,
+                                const struct hsw_pipe_wm_parameters *p,
                                 struct intel_wm_level *result)
 {
        uint16_t pri_latency = dev_priv->wm.pri_latency[level];
@@ -2434,55 +2455,6 @@ static void ilk_compute_wm_level(struct drm_i915_private *dev_priv,
        result->enable = true;
 }
 
-static bool hsw_compute_lp_wm(struct drm_i915_private *dev_priv,
-                             int level, struct hsw_wm_maximums *max,
-                             struct hsw_pipe_wm_parameters *params,
-                             struct intel_wm_level *result)
-{
-       enum pipe pipe;
-       struct intel_wm_level res[3];
-
-       for (pipe = PIPE_A; pipe <= PIPE_C; pipe++)
-               ilk_compute_wm_level(dev_priv, level, &params[pipe], &res[pipe]);
-
-       result->pri_val = max3(res[0].pri_val, res[1].pri_val, res[2].pri_val);
-       result->spr_val = max3(res[0].spr_val, res[1].spr_val, res[2].spr_val);
-       result->cur_val = max3(res[0].cur_val, res[1].cur_val, res[2].cur_val);
-       result->fbc_val = max3(res[0].fbc_val, res[1].fbc_val, res[2].fbc_val);
-       result->enable = true;
-
-       return ilk_check_wm(level, max, result);
-}
-
-static uint32_t hsw_compute_wm_pipe(struct drm_i915_private *dev_priv,
-                                   enum pipe pipe,
-                                   struct hsw_pipe_wm_parameters *params)
-{
-       uint32_t pri_val, cur_val, spr_val;
-       /* WM0 latency values stored in 0.1us units */
-       uint16_t pri_latency = dev_priv->wm.pri_latency[0];
-       uint16_t spr_latency = dev_priv->wm.spr_latency[0];
-       uint16_t cur_latency = dev_priv->wm.cur_latency[0];
-
-       pri_val = ilk_compute_pri_wm(params, pri_latency, false);
-       spr_val = ilk_compute_spr_wm(params, spr_latency);
-       cur_val = ilk_compute_cur_wm(params, cur_latency);
-
-       WARN(pri_val > 127,
-            "Primary WM error, mode not supported for pipe %c\n",
-            pipe_name(pipe));
-       WARN(spr_val > 127,
-            "Sprite WM error, mode not supported for pipe %c\n",
-            pipe_name(pipe));
-       WARN(cur_val > 63,
-            "Cursor WM error, mode not supported for pipe %c\n",
-            pipe_name(pipe));
-
-       return (pri_val << WM0_PIPE_PLANE_SHIFT) |
-              (spr_val << WM0_PIPE_SPRITE_SHIFT) |
-              cur_val;
-}
-
 static uint32_t
 hsw_compute_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc)
 {
@@ -2554,19 +2526,22 @@ static void intel_fixup_cur_wm_latency(struct drm_device *dev, uint16_t wm[5])
                wm[3] *= 2;
 }
 
-static void intel_print_wm_latency(struct drm_device *dev,
-                                  const char *name,
-                                  const uint16_t wm[5])
+static int ilk_wm_max_level(const struct drm_device *dev)
 {
-       int level, max_level;
-
        /* how many WM levels are we expecting */
        if (IS_HASWELL(dev))
-               max_level = 4;
+               return 4;
        else if (INTEL_INFO(dev)->gen >= 6)
-               max_level = 3;
+               return 3;
        else
-               max_level = 2;
+               return 2;
+}
+
+static void intel_print_wm_latency(struct drm_device *dev,
+                                  const char *name,
+                                  const uint16_t wm[5])
+{
+       int level, max_level = ilk_wm_max_level(dev);
 
        for (level = 0; level <= max_level; level++) {
                unsigned int latency = wm[level];
@@ -2606,218 +2581,321 @@ static void intel_setup_wm_latency(struct drm_device *dev)
        intel_print_wm_latency(dev, "Cursor", dev_priv->wm.cur_latency);
 }
 
-static void hsw_compute_wm_parameters(struct drm_device *dev,
-                                     struct hsw_pipe_wm_parameters *params,
-                                     struct hsw_wm_maximums *lp_max_1_2,
-                                     struct hsw_wm_maximums *lp_max_5_6)
+static void hsw_compute_wm_parameters(struct drm_crtc *crtc,
+                                     struct hsw_pipe_wm_parameters *p,
+                                     struct intel_wm_config *config)
 {
-       struct drm_crtc *crtc;
+       struct drm_device *dev = crtc->dev;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       enum pipe pipe = intel_crtc->pipe;
        struct drm_plane *plane;
-       enum pipe pipe;
-       struct intel_wm_config config = {};
-
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-               struct hsw_pipe_wm_parameters *p;
-
-               pipe = intel_crtc->pipe;
-               p = &params[pipe];
-
-               p->active = intel_crtc_active(crtc);
-               if (!p->active)
-                       continue;
-
-               config.num_pipes_active++;
 
+       p->active = intel_crtc_active(crtc);
+       if (p->active) {
                p->pipe_htotal = intel_crtc->config.adjusted_mode.htotal;
                p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc);
                p->pri.bytes_per_pixel = crtc->fb->bits_per_pixel / 8;
                p->cur.bytes_per_pixel = 4;
-               p->pri.horiz_pixels =
-                       intel_crtc->config.requested_mode.hdisplay;
+               p->pri.horiz_pixels = intel_crtc->config.pipe_src_w;
                p->cur.horiz_pixels = 64;
                /* TODO: for now, assume primary and cursor planes are always enabled. */
                p->pri.enabled = true;
                p->cur.enabled = true;
        }
 
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+               config->num_pipes_active += intel_crtc_active(crtc);
+
        list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
                struct intel_plane *intel_plane = to_intel_plane(plane);
-               struct hsw_pipe_wm_parameters *p;
 
-               pipe = intel_plane->pipe;
-               p = &params[pipe];
+               if (intel_plane->pipe == pipe)
+                       p->spr = intel_plane->wm;
 
-               p->spr = intel_plane->wm;
-
-               config.sprites_enabled |= p->spr.enabled;
-               config.sprites_scaled |= p->spr.scaled;
+               config->sprites_enabled |= intel_plane->wm.enabled;
+               config->sprites_scaled |= intel_plane->wm.scaled;
        }
+}
+
+/* Compute new watermarks for the pipe */
+static bool intel_compute_pipe_wm(struct drm_crtc *crtc,
+                                 const struct hsw_pipe_wm_parameters *params,
+                                 struct intel_pipe_wm *pipe_wm)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int level, max_level = ilk_wm_max_level(dev);
+       /* LP0 watermark maximums depend on this pipe alone */
+       struct intel_wm_config config = {
+               .num_pipes_active = 1,
+               .sprites_enabled = params->spr.enabled,
+               .sprites_scaled = params->spr.scaled,
+       };
+       struct hsw_wm_maximums max;
 
-       ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_1_2, lp_max_1_2);
+       /* LP0 watermarks always use 1/2 DDB partitioning */
+       ilk_compute_wm_maximums(dev, 0, &config, INTEL_DDB_PART_1_2, &max);
 
-       /* 5/6 split only in single pipe config on IVB+ */
-       if (INTEL_INFO(dev)->gen >= 7 && config.num_pipes_active <= 1)
-               ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_5_6, lp_max_5_6);
-       else
-               *lp_max_5_6 = *lp_max_1_2;
+       for (level = 0; level <= max_level; level++)
+               ilk_compute_wm_level(dev_priv, level, params,
+                                    &pipe_wm->wm[level]);
+
+       pipe_wm->linetime = hsw_compute_linetime_wm(dev, crtc);
+
+       /* At least LP0 must be valid */
+       return ilk_validate_wm_level(0, &max, &pipe_wm->wm[0]);
 }
 
-static void hsw_compute_wm_results(struct drm_device *dev,
-                                  struct hsw_pipe_wm_parameters *params,
-                                  struct hsw_wm_maximums *lp_maximums,
-                                  struct hsw_wm_values *results)
+/*
+ * Merge the watermarks from all active pipes for a specific level.
+ */
+static void ilk_merge_wm_level(struct drm_device *dev,
+                              int level,
+                              struct intel_wm_level *ret_wm)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_crtc *crtc;
-       struct intel_wm_level lp_results[4] = {};
-       enum pipe pipe;
-       int level, max_level, wm_lp;
+       const struct intel_crtc *intel_crtc;
 
-       for (level = 1; level <= 4; level++)
-               if (!hsw_compute_lp_wm(dev_priv, level,
-                                      lp_maximums, params,
-                                      &lp_results[level - 1]))
-                       break;
-       max_level = level - 1;
+       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
+               const struct intel_wm_level *wm =
+                       &intel_crtc->wm.active.wm[level];
+
+               if (!wm->enable)
+                       return;
+
+               ret_wm->pri_val = max(ret_wm->pri_val, wm->pri_val);
+               ret_wm->spr_val = max(ret_wm->spr_val, wm->spr_val);
+               ret_wm->cur_val = max(ret_wm->cur_val, wm->cur_val);
+               ret_wm->fbc_val = max(ret_wm->fbc_val, wm->fbc_val);
+       }
+
+       ret_wm->enable = true;
+}
 
-       memset(results, 0, sizeof(*results));
+/*
+ * Merge all low power watermarks for all active pipes.
+ */
+static void ilk_wm_merge(struct drm_device *dev,
+                        const struct hsw_wm_maximums *max,
+                        struct intel_pipe_wm *merged)
+{
+       int level, max_level = ilk_wm_max_level(dev);
+
+       merged->fbc_wm_enabled = true;
 
-       /* The spec says it is preferred to disable FBC WMs instead of disabling
-        * a WM level. */
-       results->enable_fbc_wm = true;
+       /* merge each WM1+ level */
        for (level = 1; level <= max_level; level++) {
-               if (lp_results[level - 1].fbc_val > lp_maximums->fbc) {
-                       results->enable_fbc_wm = false;
-                       lp_results[level - 1].fbc_val = 0;
+               struct intel_wm_level *wm = &merged->wm[level];
+
+               ilk_merge_wm_level(dev, level, wm);
+
+               if (!ilk_validate_wm_level(level, max, wm))
+                       break;
+
+               /*
+                * The spec says it is preferred to disable
+                * FBC WMs instead of disabling a WM level.
+                */
+               if (wm->fbc_val > max->fbc) {
+                       merged->fbc_wm_enabled = false;
+                       wm->fbc_val = 0;
                }
        }
+}
 
+static int ilk_wm_lp_to_level(int wm_lp, const struct intel_pipe_wm *pipe_wm)
+{
+       /* LP1,LP2,LP3 levels are either 1,2,3 or 1,3,4 */
+       return wm_lp + (wm_lp >= 2 && pipe_wm->wm[4].enable);
+}
+
+static void hsw_compute_wm_results(struct drm_device *dev,
+                                  const struct intel_pipe_wm *merged,
+                                  enum intel_ddb_partitioning partitioning,
+                                  struct hsw_wm_values *results)
+{
+       struct intel_crtc *intel_crtc;
+       int level, wm_lp;
+
+       results->enable_fbc_wm = merged->fbc_wm_enabled;
+       results->partitioning = partitioning;
+
+       /* LP1+ register values */
        for (wm_lp = 1; wm_lp <= 3; wm_lp++) {
                const struct intel_wm_level *r;
 
-               level = (max_level == 4 && wm_lp > 1) ? wm_lp + 1 : wm_lp;
-               if (level > max_level)
+               level = ilk_wm_lp_to_level(wm_lp, merged);
+
+               r = &merged->wm[level];
+               if (!r->enable)
                        break;
 
-               r = &lp_results[level - 1];
-               results->wm_lp[wm_lp - 1] = HSW_WM_LP_VAL(level * 2,
-                                                         r->fbc_val,
-                                                         r->pri_val,
-                                                         r->cur_val);
+               results->wm_lp[wm_lp - 1] = WM3_LP_EN |
+                       ((level * 2) << WM1_LP_LATENCY_SHIFT) |
+                       (r->pri_val << WM1_LP_SR_SHIFT) |
+                       r->cur_val;
+
+               if (INTEL_INFO(dev)->gen >= 8)
+                       results->wm_lp[wm_lp - 1] |=
+                               r->fbc_val << WM1_LP_FBC_SHIFT_BDW;
+               else
+                       results->wm_lp[wm_lp - 1] |=
+                               r->fbc_val << WM1_LP_FBC_SHIFT;
+
                results->wm_lp_spr[wm_lp - 1] = r->spr_val;
        }
 
-       for_each_pipe(pipe)
-               results->wm_pipe[pipe] = hsw_compute_wm_pipe(dev_priv, pipe,
-                                                            &params[pipe]);
+       /* LP0 register values */
+       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
+               enum pipe pipe = intel_crtc->pipe;
+               const struct intel_wm_level *r =
+                       &intel_crtc->wm.active.wm[0];
 
-       for_each_pipe(pipe) {
-               crtc = dev_priv->pipe_to_crtc_mapping[pipe];
-               results->wm_linetime[pipe] = hsw_compute_linetime_wm(dev, crtc);
+               if (WARN_ON(!r->enable))
+                       continue;
+
+               results->wm_linetime[pipe] = intel_crtc->wm.active.linetime;
+
+               results->wm_pipe[pipe] =
+                       (r->pri_val << WM0_PIPE_PLANE_SHIFT) |
+                       (r->spr_val << WM0_PIPE_SPRITE_SHIFT) |
+                       r->cur_val;
        }
 }
 
 /* Find the result with the highest level enabled. Check for enable_fbc_wm in
  * case both are at the same level. Prefer r1 in case they're the same. */
-static struct hsw_wm_values *hsw_find_best_result(struct hsw_wm_values *r1,
-                                                 struct hsw_wm_values *r2)
+static struct intel_pipe_wm *hsw_find_best_result(struct drm_device *dev,
+                                                 struct intel_pipe_wm *r1,
+                                                 struct intel_pipe_wm *r2)
 {
-       int i, val_r1 = 0, val_r2 = 0;
+       int level, max_level = ilk_wm_max_level(dev);
+       int level1 = 0, level2 = 0;
 
-       for (i = 0; i < 3; i++) {
-               if (r1->wm_lp[i] & WM3_LP_EN)
-                       val_r1 = r1->wm_lp[i] & WM1_LP_LATENCY_MASK;
-               if (r2->wm_lp[i] & WM3_LP_EN)
-                       val_r2 = r2->wm_lp[i] & WM1_LP_LATENCY_MASK;
+       for (level = 1; level <= max_level; level++) {
+               if (r1->wm[level].enable)
+                       level1 = level;
+               if (r2->wm[level].enable)
+                       level2 = level;
        }
 
-       if (val_r1 == val_r2) {
-               if (r2->enable_fbc_wm && !r1->enable_fbc_wm)
+       if (level1 == level2) {
+               if (r2->fbc_wm_enabled && !r1->fbc_wm_enabled)
                        return r2;
                else
                        return r1;
-       } else if (val_r1 > val_r2) {
+       } else if (level1 > level2) {
                return r1;
        } else {
                return r2;
        }
 }
 
+/* dirty bits used to track which watermarks need changes */
+#define WM_DIRTY_PIPE(pipe) (1 << (pipe))
+#define WM_DIRTY_LINETIME(pipe) (1 << (8 + (pipe)))
+#define WM_DIRTY_LP(wm_lp) (1 << (15 + (wm_lp)))
+#define WM_DIRTY_LP_ALL (WM_DIRTY_LP(1) | WM_DIRTY_LP(2) | WM_DIRTY_LP(3))
+#define WM_DIRTY_FBC (1 << 24)
+#define WM_DIRTY_DDB (1 << 25)
+
+static unsigned int ilk_compute_wm_dirty(struct drm_device *dev,
+                                        const struct hsw_wm_values *old,
+                                        const struct hsw_wm_values *new)
+{
+       unsigned int dirty = 0;
+       enum pipe pipe;
+       int wm_lp;
+
+       for_each_pipe(pipe) {
+               if (old->wm_linetime[pipe] != new->wm_linetime[pipe]) {
+                       dirty |= WM_DIRTY_LINETIME(pipe);
+                       /* Must disable LP1+ watermarks too */
+                       dirty |= WM_DIRTY_LP_ALL;
+               }
+
+               if (old->wm_pipe[pipe] != new->wm_pipe[pipe]) {
+                       dirty |= WM_DIRTY_PIPE(pipe);
+                       /* Must disable LP1+ watermarks too */
+                       dirty |= WM_DIRTY_LP_ALL;
+               }
+       }
+
+       if (old->enable_fbc_wm != new->enable_fbc_wm) {
+               dirty |= WM_DIRTY_FBC;
+               /* Must disable LP1+ watermarks too */
+               dirty |= WM_DIRTY_LP_ALL;
+       }
+
+       if (old->partitioning != new->partitioning) {
+               dirty |= WM_DIRTY_DDB;
+               /* Must disable LP1+ watermarks too */
+               dirty |= WM_DIRTY_LP_ALL;
+       }
+
+       /* LP1+ watermarks already deemed dirty, no need to continue */
+       if (dirty & WM_DIRTY_LP_ALL)
+               return dirty;
+
+       /* Find the lowest numbered LP1+ watermark in need of an update... */
+       for (wm_lp = 1; wm_lp <= 3; wm_lp++) {
+               if (old->wm_lp[wm_lp - 1] != new->wm_lp[wm_lp - 1] ||
+                   old->wm_lp_spr[wm_lp - 1] != new->wm_lp_spr[wm_lp - 1])
+                       break;
+       }
+
+       /* ...and mark it and all higher numbered LP1+ watermarks as dirty */
+       for (; wm_lp <= 3; wm_lp++)
+               dirty |= WM_DIRTY_LP(wm_lp);
+
+       return dirty;
+}
+
 /*
  * The spec says we shouldn't write when we don't need, because every write
  * causes WMs to be re-evaluated, expending some power.
  */
 static void hsw_write_wm_values(struct drm_i915_private *dev_priv,
-                               struct hsw_wm_values *results,
-                               enum intel_ddb_partitioning partitioning)
+                               struct hsw_wm_values *results)
 {
-       struct hsw_wm_values previous;
+       struct hsw_wm_values *previous = &dev_priv->wm.hw;
+       unsigned int dirty;
        uint32_t val;
-       enum intel_ddb_partitioning prev_partitioning;
-       bool prev_enable_fbc_wm;
-
-       previous.wm_pipe[0] = I915_READ(WM0_PIPEA_ILK);
-       previous.wm_pipe[1] = I915_READ(WM0_PIPEB_ILK);
-       previous.wm_pipe[2] = I915_READ(WM0_PIPEC_IVB);
-       previous.wm_lp[0] = I915_READ(WM1_LP_ILK);
-       previous.wm_lp[1] = I915_READ(WM2_LP_ILK);
-       previous.wm_lp[2] = I915_READ(WM3_LP_ILK);
-       previous.wm_lp_spr[0] = I915_READ(WM1S_LP_ILK);
-       previous.wm_lp_spr[1] = I915_READ(WM2S_LP_IVB);
-       previous.wm_lp_spr[2] = I915_READ(WM3S_LP_IVB);
-       previous.wm_linetime[0] = I915_READ(PIPE_WM_LINETIME(PIPE_A));
-       previous.wm_linetime[1] = I915_READ(PIPE_WM_LINETIME(PIPE_B));
-       previous.wm_linetime[2] = I915_READ(PIPE_WM_LINETIME(PIPE_C));
-
-       prev_partitioning = (I915_READ(WM_MISC) & WM_MISC_DATA_PARTITION_5_6) ?
-                               INTEL_DDB_PART_5_6 : INTEL_DDB_PART_1_2;
-
-       prev_enable_fbc_wm = !(I915_READ(DISP_ARB_CTL) & DISP_FBC_WM_DIS);
-
-       if (memcmp(results->wm_pipe, previous.wm_pipe,
-                  sizeof(results->wm_pipe)) == 0 &&
-           memcmp(results->wm_lp, previous.wm_lp,
-                  sizeof(results->wm_lp)) == 0 &&
-           memcmp(results->wm_lp_spr, previous.wm_lp_spr,
-                  sizeof(results->wm_lp_spr)) == 0 &&
-           memcmp(results->wm_linetime, previous.wm_linetime,
-                  sizeof(results->wm_linetime)) == 0 &&
-           partitioning == prev_partitioning &&
-           results->enable_fbc_wm == prev_enable_fbc_wm)
+
+       dirty = ilk_compute_wm_dirty(dev_priv->dev, previous, results);
+       if (!dirty)
                return;
 
-       if (previous.wm_lp[2] != 0)
+       if (dirty & WM_DIRTY_LP(3) && previous->wm_lp[2] != 0)
                I915_WRITE(WM3_LP_ILK, 0);
-       if (previous.wm_lp[1] != 0)
+       if (dirty & WM_DIRTY_LP(2) && previous->wm_lp[1] != 0)
                I915_WRITE(WM2_LP_ILK, 0);
-       if (previous.wm_lp[0] != 0)
+       if (dirty & WM_DIRTY_LP(1) && previous->wm_lp[0] != 0)
                I915_WRITE(WM1_LP_ILK, 0);
 
-       if (previous.wm_pipe[0] != results->wm_pipe[0])
+       if (dirty & WM_DIRTY_PIPE(PIPE_A))
                I915_WRITE(WM0_PIPEA_ILK, results->wm_pipe[0]);
-       if (previous.wm_pipe[1] != results->wm_pipe[1])
+       if (dirty & WM_DIRTY_PIPE(PIPE_B))
                I915_WRITE(WM0_PIPEB_ILK, results->wm_pipe[1]);
-       if (previous.wm_pipe[2] != results->wm_pipe[2])
+       if (dirty & WM_DIRTY_PIPE(PIPE_C))
                I915_WRITE(WM0_PIPEC_IVB, results->wm_pipe[2]);
 
-       if (previous.wm_linetime[0] != results->wm_linetime[0])
+       if (dirty & WM_DIRTY_LINETIME(PIPE_A))
                I915_WRITE(PIPE_WM_LINETIME(PIPE_A), results->wm_linetime[0]);
-       if (previous.wm_linetime[1] != results->wm_linetime[1])
+       if (dirty & WM_DIRTY_LINETIME(PIPE_B))
                I915_WRITE(PIPE_WM_LINETIME(PIPE_B), results->wm_linetime[1]);
-       if (previous.wm_linetime[2] != results->wm_linetime[2])
+       if (dirty & WM_DIRTY_LINETIME(PIPE_C))
                I915_WRITE(PIPE_WM_LINETIME(PIPE_C), results->wm_linetime[2]);
 
-       if (prev_partitioning != partitioning) {
+       if (dirty & WM_DIRTY_DDB) {
                val = I915_READ(WM_MISC);
-               if (partitioning == INTEL_DDB_PART_1_2)
+               if (results->partitioning == INTEL_DDB_PART_1_2)
                        val &= ~WM_MISC_DATA_PARTITION_5_6;
                else
                        val |= WM_MISC_DATA_PARTITION_5_6;
                I915_WRITE(WM_MISC, val);
        }
 
-       if (prev_enable_fbc_wm != results->enable_fbc_wm) {
+       if (dirty & WM_DIRTY_FBC) {
                val = I915_READ(DISP_ARB_CTL);
                if (results->enable_fbc_wm)
                        val &= ~DISP_FBC_WM_DIS;
@@ -2826,45 +2904,65 @@ static void hsw_write_wm_values(struct drm_i915_private *dev_priv,
                I915_WRITE(DISP_ARB_CTL, val);
        }
 
-       if (previous.wm_lp_spr[0] != results->wm_lp_spr[0])
+       if (dirty & WM_DIRTY_LP(1) && previous->wm_lp_spr[0] != results->wm_lp_spr[0])
                I915_WRITE(WM1S_LP_ILK, results->wm_lp_spr[0]);
-       if (previous.wm_lp_spr[1] != results->wm_lp_spr[1])
+       if (dirty & WM_DIRTY_LP(2) && previous->wm_lp_spr[1] != results->wm_lp_spr[1])
                I915_WRITE(WM2S_LP_IVB, results->wm_lp_spr[1]);
-       if (previous.wm_lp_spr[2] != results->wm_lp_spr[2])
+       if (dirty & WM_DIRTY_LP(3) && previous->wm_lp_spr[2] != results->wm_lp_spr[2])
                I915_WRITE(WM3S_LP_IVB, results->wm_lp_spr[2]);
 
-       if (results->wm_lp[0] != 0)
+       if (dirty & WM_DIRTY_LP(1) && results->wm_lp[0] != 0)
                I915_WRITE(WM1_LP_ILK, results->wm_lp[0]);
-       if (results->wm_lp[1] != 0)
+       if (dirty & WM_DIRTY_LP(2) && results->wm_lp[1] != 0)
                I915_WRITE(WM2_LP_ILK, results->wm_lp[1]);
-       if (results->wm_lp[2] != 0)
+       if (dirty & WM_DIRTY_LP(3) && results->wm_lp[2] != 0)
                I915_WRITE(WM3_LP_ILK, results->wm_lp[2]);
+
+       dev_priv->wm.hw = *results;
 }
 
-static void haswell_update_wm(struct drm_device *dev)
+static void haswell_update_wm(struct drm_crtc *crtc)
 {
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct hsw_wm_maximums lp_max_1_2, lp_max_5_6;
-       struct hsw_pipe_wm_parameters params[3];
-       struct hsw_wm_values results_1_2, results_5_6, *best_results;
+       struct hsw_wm_maximums max;
+       struct hsw_pipe_wm_parameters params = {};
+       struct hsw_wm_values results = {};
        enum intel_ddb_partitioning partitioning;
+       struct intel_pipe_wm pipe_wm = {};
+       struct intel_pipe_wm lp_wm_1_2 = {}, lp_wm_5_6 = {}, *best_lp_wm;
+       struct intel_wm_config config = {};
 
-       hsw_compute_wm_parameters(dev, params, &lp_max_1_2, &lp_max_5_6);
+       hsw_compute_wm_parameters(crtc, &params, &config);
+
+       intel_compute_pipe_wm(crtc, &params, &pipe_wm);
+
+       if (!memcmp(&intel_crtc->wm.active, &pipe_wm, sizeof(pipe_wm)))
+               return;
 
-       hsw_compute_wm_results(dev, params,
-                              &lp_max_1_2, &results_1_2);
-       if (lp_max_1_2.pri != lp_max_5_6.pri) {
-               hsw_compute_wm_results(dev, params,
-                                      &lp_max_5_6, &results_5_6);
-               best_results = hsw_find_best_result(&results_1_2, &results_5_6);
+       intel_crtc->wm.active = pipe_wm;
+
+       ilk_compute_wm_maximums(dev, 1, &config, INTEL_DDB_PART_1_2, &max);
+       ilk_wm_merge(dev, &max, &lp_wm_1_2);
+
+       /* 5/6 split only in single pipe config on IVB+ */
+       if (INTEL_INFO(dev)->gen >= 7 &&
+           config.num_pipes_active == 1 && config.sprites_enabled) {
+               ilk_compute_wm_maximums(dev, 1, &config, INTEL_DDB_PART_5_6, &max);
+               ilk_wm_merge(dev, &max, &lp_wm_5_6);
+
+               best_lp_wm = hsw_find_best_result(dev, &lp_wm_1_2, &lp_wm_5_6);
        } else {
-               best_results = &results_1_2;
+               best_lp_wm = &lp_wm_1_2;
        }
 
-       partitioning = (best_results == &results_1_2) ?
+       partitioning = (best_lp_wm == &lp_wm_1_2) ?
                       INTEL_DDB_PART_1_2 : INTEL_DDB_PART_5_6;
 
-       hsw_write_wm_values(dev_priv, best_results, partitioning);
+       hsw_compute_wm_results(dev, best_lp_wm, partitioning, &results);
+
+       hsw_write_wm_values(dev_priv, &results);
 }
 
 static void haswell_update_sprite_wm(struct drm_plane *plane,
@@ -2879,7 +2977,7 @@ static void haswell_update_sprite_wm(struct drm_plane *plane,
        intel_plane->wm.horiz_pixels = sprite_width;
        intel_plane->wm.bytes_per_pixel = pixel_size;
 
-       haswell_update_wm(plane->dev);
+       haswell_update_wm(crtc);
 }
 
 static bool
@@ -2898,7 +2996,7 @@ sandybridge_compute_sprite_wm(struct drm_device *dev, int plane,
                return false;
        }
 
-       clock = crtc->mode.clock;
+       clock = to_intel_crtc(crtc)->config.adjusted_mode.crtc_clock;
 
        /* Use the small buffer method to calculate the sprite watermark */
        entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000;
@@ -2933,7 +3031,7 @@ sandybridge_compute_sprite_srwm(struct drm_device *dev, int plane,
        }
 
        crtc = intel_get_crtc_for_plane(dev, plane);
-       clock = crtc->mode.clock;
+       clock = to_intel_crtc(crtc)->config.adjusted_mode.crtc_clock;
        if (!clock) {
                *sprite_wm = 0;
                return false;
@@ -3044,6 +3142,74 @@ static void sandybridge_update_sprite_wm(struct drm_plane *plane,
        I915_WRITE(WM3S_LP_IVB, sprite_wm);
 }
 
+static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct hsw_wm_values *hw = &dev_priv->wm.hw;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_pipe_wm *active = &intel_crtc->wm.active;
+       enum pipe pipe = intel_crtc->pipe;
+       static const unsigned int wm0_pipe_reg[] = {
+               [PIPE_A] = WM0_PIPEA_ILK,
+               [PIPE_B] = WM0_PIPEB_ILK,
+               [PIPE_C] = WM0_PIPEC_IVB,
+       };
+
+       hw->wm_pipe[pipe] = I915_READ(wm0_pipe_reg[pipe]);
+       hw->wm_linetime[pipe] = I915_READ(PIPE_WM_LINETIME(pipe));
+
+       if (intel_crtc_active(crtc)) {
+               u32 tmp = hw->wm_pipe[pipe];
+
+               /*
+                * For active pipes LP0 watermark is marked as
+                * enabled, and LP1+ watermaks as disabled since
+                * we can't really reverse compute them in case
+                * multiple pipes are active.
+                */
+               active->wm[0].enable = true;
+               active->wm[0].pri_val = (tmp & WM0_PIPE_PLANE_MASK) >> WM0_PIPE_PLANE_SHIFT;
+               active->wm[0].spr_val = (tmp & WM0_PIPE_SPRITE_MASK) >> WM0_PIPE_SPRITE_SHIFT;
+               active->wm[0].cur_val = tmp & WM0_PIPE_CURSOR_MASK;
+               active->linetime = hw->wm_linetime[pipe];
+       } else {
+               int level, max_level = ilk_wm_max_level(dev);
+
+               /*
+                * For inactive pipes, all watermark levels
+                * should be marked as enabled but zeroed,
+                * which is what we'd compute them to.
+                */
+               for (level = 0; level <= max_level; level++)
+                       active->wm[level].enable = true;
+       }
+}
+
+void ilk_wm_get_hw_state(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct hsw_wm_values *hw = &dev_priv->wm.hw;
+       struct drm_crtc *crtc;
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+               ilk_pipe_wm_get_hw_state(crtc);
+
+       hw->wm_lp[0] = I915_READ(WM1_LP_ILK);
+       hw->wm_lp[1] = I915_READ(WM2_LP_ILK);
+       hw->wm_lp[2] = I915_READ(WM3_LP_ILK);
+
+       hw->wm_lp_spr[0] = I915_READ(WM1S_LP_ILK);
+       hw->wm_lp_spr[1] = I915_READ(WM2S_LP_IVB);
+       hw->wm_lp_spr[2] = I915_READ(WM3S_LP_IVB);
+
+       hw->partitioning = (I915_READ(WM_MISC) & WM_MISC_DATA_PARTITION_5_6) ?
+               INTEL_DDB_PART_5_6 : INTEL_DDB_PART_1_2;
+
+       hw->enable_fbc_wm =
+               !(I915_READ(DISP_ARB_CTL) & DISP_FBC_WM_DIS);
+}
+
 /**
  * intel_update_watermarks - update FIFO watermark values based on current modes
  *
@@ -3076,12 +3242,12 @@ static void sandybridge_update_sprite_wm(struct drm_plane *plane,
  * We don't use the sprite, so we can ignore that.  And on Crestline we have
  * to set the non-SR watermarks to 8.
  */
-void intel_update_watermarks(struct drm_device *dev)
+void intel_update_watermarks(struct drm_crtc *crtc)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
 
        if (dev_priv->display.update_wm)
-               dev_priv->display.update_wm(dev);
+               dev_priv->display.update_wm(crtc);
 }
 
 void intel_update_sprite_watermarks(struct drm_plane *plane,
@@ -3287,6 +3453,98 @@ static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 *val)
        return limits;
 }
 
+static void gen6_set_rps_thresholds(struct drm_i915_private *dev_priv, u8 val)
+{
+       int new_power;
+
+       new_power = dev_priv->rps.power;
+       switch (dev_priv->rps.power) {
+       case LOW_POWER:
+               if (val > dev_priv->rps.rpe_delay + 1 && val > dev_priv->rps.cur_delay)
+                       new_power = BETWEEN;
+               break;
+
+       case BETWEEN:
+               if (val <= dev_priv->rps.rpe_delay && val < dev_priv->rps.cur_delay)
+                       new_power = LOW_POWER;
+               else if (val >= dev_priv->rps.rp0_delay && val > dev_priv->rps.cur_delay)
+                       new_power = HIGH_POWER;
+               break;
+
+       case HIGH_POWER:
+               if (val < (dev_priv->rps.rp1_delay + dev_priv->rps.rp0_delay) >> 1 && val < dev_priv->rps.cur_delay)
+                       new_power = BETWEEN;
+               break;
+       }
+       /* Max/min bins are special */
+       if (val == dev_priv->rps.min_delay)
+               new_power = LOW_POWER;
+       if (val == dev_priv->rps.max_delay)
+               new_power = HIGH_POWER;
+       if (new_power == dev_priv->rps.power)
+               return;
+
+       /* Note the units here are not exactly 1us, but 1280ns. */
+       switch (new_power) {
+       case LOW_POWER:
+               /* Upclock if more than 95% busy over 16ms */
+               I915_WRITE(GEN6_RP_UP_EI, 12500);
+               I915_WRITE(GEN6_RP_UP_THRESHOLD, 11800);
+
+               /* Downclock if less than 85% busy over 32ms */
+               I915_WRITE(GEN6_RP_DOWN_EI, 25000);
+               I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 21250);
+
+               I915_WRITE(GEN6_RP_CONTROL,
+                          GEN6_RP_MEDIA_TURBO |
+                          GEN6_RP_MEDIA_HW_NORMAL_MODE |
+                          GEN6_RP_MEDIA_IS_GFX |
+                          GEN6_RP_ENABLE |
+                          GEN6_RP_UP_BUSY_AVG |
+                          GEN6_RP_DOWN_IDLE_AVG);
+               break;
+
+       case BETWEEN:
+               /* Upclock if more than 90% busy over 13ms */
+               I915_WRITE(GEN6_RP_UP_EI, 10250);
+               I915_WRITE(GEN6_RP_UP_THRESHOLD, 9225);
+
+               /* Downclock if less than 75% busy over 32ms */
+               I915_WRITE(GEN6_RP_DOWN_EI, 25000);
+               I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 18750);
+
+               I915_WRITE(GEN6_RP_CONTROL,
+                          GEN6_RP_MEDIA_TURBO |
+                          GEN6_RP_MEDIA_HW_NORMAL_MODE |
+                          GEN6_RP_MEDIA_IS_GFX |
+                          GEN6_RP_ENABLE |
+                          GEN6_RP_UP_BUSY_AVG |
+                          GEN6_RP_DOWN_IDLE_AVG);
+               break;
+
+       case HIGH_POWER:
+               /* Upclock if more than 85% busy over 10ms */
+               I915_WRITE(GEN6_RP_UP_EI, 8000);
+               I915_WRITE(GEN6_RP_UP_THRESHOLD, 6800);
+
+               /* Downclock if less than 60% busy over 32ms */
+               I915_WRITE(GEN6_RP_DOWN_EI, 25000);
+               I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 15000);
+
+               I915_WRITE(GEN6_RP_CONTROL,
+                          GEN6_RP_MEDIA_TURBO |
+                          GEN6_RP_MEDIA_HW_NORMAL_MODE |
+                          GEN6_RP_MEDIA_IS_GFX |
+                          GEN6_RP_ENABLE |
+                          GEN6_RP_UP_BUSY_AVG |
+                          GEN6_RP_DOWN_IDLE_AVG);
+               break;
+       }
+
+       dev_priv->rps.power = new_power;
+       dev_priv->rps.last_adj = 0;
+}
+
 void gen6_set_rps(struct drm_device *dev, u8 val)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3299,6 +3557,8 @@ void gen6_set_rps(struct drm_device *dev, u8 val)
        if (val == dev_priv->rps.cur_delay)
                return;
 
+       gen6_set_rps_thresholds(dev_priv, val);
+
        if (IS_HASWELL(dev))
                I915_WRITE(GEN6_RPNSWREQ,
                           HSW_FREQUENCY(val));
@@ -3320,6 +3580,32 @@ void gen6_set_rps(struct drm_device *dev, u8 val)
        trace_intel_gpu_freq_change(val * 50);
 }
 
+void gen6_rps_idle(struct drm_i915_private *dev_priv)
+{
+       mutex_lock(&dev_priv->rps.hw_lock);
+       if (dev_priv->rps.enabled) {
+               if (dev_priv->info->is_valleyview)
+                       valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_delay);
+               else
+                       gen6_set_rps(dev_priv->dev, dev_priv->rps.min_delay);
+               dev_priv->rps.last_adj = 0;
+       }
+       mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
+void gen6_rps_boost(struct drm_i915_private *dev_priv)
+{
+       mutex_lock(&dev_priv->rps.hw_lock);
+       if (dev_priv->rps.enabled) {
+               if (dev_priv->info->is_valleyview)
+                       valleyview_set_rps(dev_priv->dev, dev_priv->rps.max_delay);
+               else
+                       gen6_set_rps(dev_priv->dev, dev_priv->rps.max_delay);
+               dev_priv->rps.last_adj = 0;
+       }
+       mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
 /*
  * Wait until the previous freq change has completed,
  * or the timeout elapsed, and then update our notion
@@ -3415,6 +3701,20 @@ static void valleyview_disable_rps(struct drm_device *dev)
        }
 }
 
+static void intel_print_rc6_info(struct drm_device *dev, u32 mode)
+{
+       if (IS_GEN6(dev))
+               DRM_DEBUG_DRIVER("Sandybridge: deep RC6 disabled\n");
+
+       if (IS_HASWELL(dev))
+               DRM_DEBUG_DRIVER("Haswell: only RC6 available\n");
+
+       DRM_INFO("Enabling RC6 states: RC6 %s, RC6p %s, RC6pp %s\n",
+                       (mode & GEN6_RC_CTL_RC6_ENABLE) ? "on" : "off",
+                       (mode & GEN6_RC_CTL_RC6p_ENABLE) ? "on" : "off",
+                       (mode & GEN6_RC_CTL_RC6pp_ENABLE) ? "on" : "off");
+}
+
 int intel_enable_rc6(const struct drm_device *dev)
 {
        /* No RC6 before Ironlake */
@@ -3429,18 +3729,13 @@ int intel_enable_rc6(const struct drm_device *dev)
        if (INTEL_INFO(dev)->gen == 5)
                return 0;
 
-       if (IS_HASWELL(dev)) {
-               DRM_DEBUG_DRIVER("Haswell: only RC6 available\n");
+       if (IS_HASWELL(dev))
                return INTEL_RC6_ENABLE;
-       }
 
        /* snb/ivb have more than one rc6 state. */
-       if (INTEL_INFO(dev)->gen == 6) {
-               DRM_DEBUG_DRIVER("Sandybridge: deep RC6 disabled\n");
+       if (INTEL_INFO(dev)->gen == 6)
                return INTEL_RC6_ENABLE;
-       }
 
-       DRM_DEBUG_DRIVER("RC6 and deep RC6 enabled\n");
        return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE);
 }
 
@@ -3467,6 +3762,78 @@ static void gen6_enable_rps_interrupts(struct drm_device *dev)
        I915_WRITE(GEN6_PMINTRMSK, ~enabled_intrs);
 }
 
+static void gen8_enable_rps(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring;
+       uint32_t rc6_mask = 0, rp_state_cap;
+       int unused;
+
+       /* 1a: Software RC state - RC0 */
+       I915_WRITE(GEN6_RC_STATE, 0);
+
+       /* 1c & 1d: Get forcewake during program sequence. Although the driver
+        * hasn't enabled a state yet where we need forcewake, BIOS may have.*/
+       gen6_gt_force_wake_get(dev_priv);
+
+       /* 2a: Disable RC states. */
+       I915_WRITE(GEN6_RC_CONTROL, 0);
+
+       rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+
+       /* 2b: Program RC6 thresholds.*/
+       I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16);
+       I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); /* 12500 * 1280ns */
+       I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25); /* 25 * 1280ns */
+       for_each_ring(ring, dev_priv, unused)
+               I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10);
+       I915_WRITE(GEN6_RC_SLEEP, 0);
+       I915_WRITE(GEN6_RC6_THRESHOLD, 50000); /* 50/125ms per EI */
+
+       /* 3: Enable RC6 */
+       if (intel_enable_rc6(dev) & INTEL_RC6_ENABLE)
+               rc6_mask = GEN6_RC_CTL_RC6_ENABLE;
+       DRM_INFO("RC6 %s\n", (rc6_mask & GEN6_RC_CTL_RC6_ENABLE) ? "on" : "off");
+       I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE |
+                       GEN6_RC_CTL_EI_MODE(1) |
+                       rc6_mask);
+
+       /* 4 Program defaults and thresholds for RPS*/
+       I915_WRITE(GEN6_RPNSWREQ, HSW_FREQUENCY(10)); /* Request 500 MHz */
+       I915_WRITE(GEN6_RC_VIDEO_FREQ, HSW_FREQUENCY(12)); /* Request 600 MHz */
+       /* NB: Docs say 1s, and 1000000 - which aren't equivalent */
+       I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 100000000 / 128); /* 1 second timeout */
+
+       /* Docs recommend 900MHz, and 300 MHz respectively */
+       I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
+                  dev_priv->rps.max_delay << 24 |
+                  dev_priv->rps.min_delay << 16);
+
+       I915_WRITE(GEN6_RP_UP_THRESHOLD, 7600000 / 128); /* 76ms busyness per EI, 90% */
+       I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 31300000 / 128); /* 313ms busyness per EI, 70%*/
+       I915_WRITE(GEN6_RP_UP_EI, 66000); /* 84.48ms, XXX: random? */
+       I915_WRITE(GEN6_RP_DOWN_EI, 350000); /* 448ms, XXX: random? */
+
+       I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
+
+       /* 5: Enable RPS */
+       I915_WRITE(GEN6_RP_CONTROL,
+                  GEN6_RP_MEDIA_TURBO |
+                  GEN6_RP_MEDIA_HW_NORMAL_MODE |
+                  GEN6_RP_MEDIA_IS_GFX |
+                  GEN6_RP_ENABLE |
+                  GEN6_RP_UP_BUSY_AVG |
+                  GEN6_RP_DOWN_IDLE_AVG);
+
+       /* 6: Ring frequency + overclocking (our driver does this later */
+
+       gen6_set_rps(dev, (I915_READ(GEN6_GT_PERF_STATUS) & 0xff00) >> 8);
+
+       gen6_enable_rps_interrupts(dev);
+
+       gen6_gt_force_wake_put(dev_priv);
+}
+
 static void gen6_enable_rps(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3501,7 +3868,10 @@ static void gen6_enable_rps(struct drm_device *dev)
 
        /* In units of 50MHz */
        dev_priv->rps.hw_max = dev_priv->rps.max_delay = rp_state_cap & 0xff;
-       dev_priv->rps.min_delay = (rp_state_cap & 0xff0000) >> 16;
+       dev_priv->rps.min_delay = (rp_state_cap >> 16) & 0xff;
+       dev_priv->rps.rp1_delay = (rp_state_cap >>  8) & 0xff;
+       dev_priv->rps.rp0_delay = (rp_state_cap >>  0) & 0xff;
+       dev_priv->rps.rpe_delay = dev_priv->rps.rp1_delay;
        dev_priv->rps.cur_delay = 0;
 
        /* disable the counters and set deterministic thresholds */
@@ -3539,48 +3909,16 @@ static void gen6_enable_rps(struct drm_device *dev)
                        rc6_mask |= GEN6_RC_CTL_RC6pp_ENABLE;
        }
 
-       DRM_INFO("Enabling RC6 states: RC6 %s, RC6p %s, RC6pp %s\n",
-                       (rc6_mask & GEN6_RC_CTL_RC6_ENABLE) ? "on" : "off",
-                       (rc6_mask & GEN6_RC_CTL_RC6p_ENABLE) ? "on" : "off",
-                       (rc6_mask & GEN6_RC_CTL_RC6pp_ENABLE) ? "on" : "off");
+       intel_print_rc6_info(dev, rc6_mask);
 
        I915_WRITE(GEN6_RC_CONTROL,
                   rc6_mask |
                   GEN6_RC_CTL_EI_MODE(1) |
                   GEN6_RC_CTL_HW_ENABLE);
 
-       if (IS_HASWELL(dev)) {
-               I915_WRITE(GEN6_RPNSWREQ,
-                          HSW_FREQUENCY(10));
-               I915_WRITE(GEN6_RC_VIDEO_FREQ,
-                          HSW_FREQUENCY(12));
-       } else {
-               I915_WRITE(GEN6_RPNSWREQ,
-                          GEN6_FREQUENCY(10) |
-                          GEN6_OFFSET(0) |
-                          GEN6_AGGRESSIVE_TURBO);
-               I915_WRITE(GEN6_RC_VIDEO_FREQ,
-                          GEN6_FREQUENCY(12));
-       }
-
-       I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000);
-       I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
-                  dev_priv->rps.max_delay << 24 |
-                  dev_priv->rps.min_delay << 16);
-
-       I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400);
-       I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000);
-       I915_WRITE(GEN6_RP_UP_EI, 66000);
-       I915_WRITE(GEN6_RP_DOWN_EI, 350000);
-
+       /* Power down if completely idle for over 50ms */
+       I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 50000);
        I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
-       I915_WRITE(GEN6_RP_CONTROL,
-                  GEN6_RP_MEDIA_TURBO |
-                  GEN6_RP_MEDIA_HW_NORMAL_MODE |
-                  GEN6_RP_MEDIA_IS_GFX |
-                  GEN6_RP_ENABLE |
-                  GEN6_RP_UP_BUSY_AVG |
-                  (IS_HASWELL(dev) ? GEN7_RP_DOWN_IDLE_AVG : GEN6_RP_DOWN_IDLE_CONT));
 
        ret = sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_MIN_FREQ_TABLE, 0);
        if (!ret) {
@@ -3596,7 +3934,8 @@ static void gen6_enable_rps(struct drm_device *dev)
                DRM_DEBUG_DRIVER("Failed to set the min frequency\n");
        }
 
-       gen6_set_rps(dev_priv->dev, (gt_perf_status & 0xff00) >> 8);
+       dev_priv->rps.power = HIGH_POWER; /* force a reset */
+       gen6_set_rps(dev_priv->dev, dev_priv->rps.min_delay);
 
        gen6_enable_rps_interrupts(dev);
 
@@ -3624,23 +3963,28 @@ void gen6_update_ring_freq(struct drm_device *dev)
        unsigned int gpu_freq;
        unsigned int max_ia_freq, min_ring_freq;
        int scaling_factor = 180;
+       struct cpufreq_policy *policy;
 
        WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
 
-       max_ia_freq = cpufreq_quick_get_max(0);
-       /*
-        * Default to measured freq if none found, PCU will ensure we don't go
-        * over
-        */
-       if (!max_ia_freq)
+       policy = cpufreq_cpu_get(0);
+       if (policy) {
+               max_ia_freq = policy->cpuinfo.max_freq;
+               cpufreq_cpu_put(policy);
+       } else {
+               /*
+                * Default to measured freq if none found, PCU will ensure we
+                * don't go over
+                */
                max_ia_freq = tsc_khz;
+       }
 
        /* Convert from kHz to MHz */
        max_ia_freq /= 1000;
 
-       min_ring_freq = I915_READ(MCHBAR_MIRROR_BASE_SNB + DCLK);
-       /* convert DDR frequency from units of 133.3MHz to bandwidth */
-       min_ring_freq = (2 * 4 * min_ring_freq + 2) / 3;
+       min_ring_freq = I915_READ(DCLK) & 0xf;
+       /* convert DDR frequency from units of 266.6MHz to bandwidth */
+       min_ring_freq = mult_frac(min_ring_freq, 8, 3);
 
        /*
         * For each potential GPU frequency, load a ring frequency we'd like
@@ -3652,8 +3996,11 @@ void gen6_update_ring_freq(struct drm_device *dev)
                int diff = dev_priv->rps.max_delay - gpu_freq;
                unsigned int ia_freq = 0, ring_freq = 0;
 
-               if (IS_HASWELL(dev)) {
-                       ring_freq = (gpu_freq * 5 + 3) / 4;
+               if (INTEL_INFO(dev)->gen >= 8) {
+                       /* max(2 * GT, DDR). NB: GT is 50MHz units */
+                       ring_freq = max(min_ring_freq, gpu_freq);
+               } else if (IS_HASWELL(dev)) {
+                       ring_freq = mult_frac(gpu_freq, 5, 4);
                        ring_freq = max(min_ring_freq, ring_freq);
                        /* leave ia_freq as the default, chosen by cpufreq */
                } else {
@@ -3709,24 +4056,6 @@ int valleyview_rps_min_freq(struct drm_i915_private *dev_priv)
        return vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM) & 0xff;
 }
 
-static void vlv_rps_timer_work(struct work_struct *work)
-{
-       drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
-                                                   rps.vlv_work.work);
-
-       /*
-        * Timer fired, we must be idle.  Drop to min voltage state.
-        * Note: we use RPe here since it should match the
-        * Vmin we were shooting for.  That should give us better
-        * perf when we come back out of RC6 than if we used the
-        * min freq available.
-        */
-       mutex_lock(&dev_priv->rps.hw_lock);
-       if (dev_priv->rps.cur_delay > dev_priv->rps.rpe_delay)
-               valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay);
-       mutex_unlock(&dev_priv->rps.hw_lock);
-}
-
 static void valleyview_setup_pctx(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3773,13 +4102,14 @@ static void valleyview_enable_rps(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_ring_buffer *ring;
-       u32 gtfifodbg, val;
+       u32 gtfifodbg, val, rc6_mode = 0;
        int i;
 
        WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
 
        if ((gtfifodbg = I915_READ(GTFIFODBG))) {
-               DRM_ERROR("GT fifo had a previous error %x\n", gtfifodbg);
+               DRM_DEBUG_DRIVER("GT fifo had a previous error %x\n",
+                                gtfifodbg);
                I915_WRITE(GTFIFODBG, gtfifodbg);
        }
 
@@ -3812,9 +4142,16 @@ static void valleyview_enable_rps(struct drm_device *dev)
        I915_WRITE(GEN6_RC6_THRESHOLD, 0xc350);
 
        /* allows RC6 residency counter to work */
-       I915_WRITE(0x138104, _MASKED_BIT_ENABLE(0x3));
-       I915_WRITE(GEN6_RC_CONTROL,
-                  GEN7_RC_CTL_TO_MODE);
+       I915_WRITE(VLV_COUNTER_CONTROL,
+                  _MASKED_BIT_ENABLE(VLV_COUNT_RANGE_HIGH |
+                                     VLV_MEDIA_RC6_COUNT_EN |
+                                     VLV_RENDER_RC6_COUNT_EN));
+       if (intel_enable_rc6(dev) & INTEL_RC6_ENABLE)
+               rc6_mode = GEN7_RC_CTL_TO_MODE;
+
+       intel_print_rc6_info(dev, rc6_mode);
+
+       I915_WRITE(GEN6_RC_CONTROL, rc6_mode);
 
        val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
        switch ((val >> 6) & 3) {
@@ -3985,6 +4322,8 @@ static void ironlake_enable_rc6(struct drm_device *dev)
 
        I915_WRITE(PWRCTXA, i915_gem_obj_ggtt_offset(dev_priv->ips.pwrctx) | PWRCTX_EN);
        I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
+
+       intel_print_rc6_info(dev, INTEL_RC6_ENABLE);
 }
 
 static unsigned long intel_pxfreq(u32 vidfreq)
@@ -4603,13 +4942,12 @@ void intel_disable_gt_powersave(struct drm_device *dev)
        } else if (INTEL_INFO(dev)->gen >= 6) {
                cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work);
                cancel_work_sync(&dev_priv->rps.work);
-               if (IS_VALLEYVIEW(dev))
-                       cancel_delayed_work_sync(&dev_priv->rps.vlv_work);
                mutex_lock(&dev_priv->rps.hw_lock);
                if (IS_VALLEYVIEW(dev))
                        valleyview_disable_rps(dev);
                else
                        gen6_disable_rps(dev);
+               dev_priv->rps.enabled = false;
                mutex_unlock(&dev_priv->rps.hw_lock);
        }
 }
@@ -4625,10 +4963,14 @@ static void intel_gen6_powersave_work(struct work_struct *work)
 
        if (IS_VALLEYVIEW(dev)) {
                valleyview_enable_rps(dev);
+       } else if (IS_BROADWELL(dev)) {
+               gen8_enable_rps(dev);
+               gen6_update_ring_freq(dev);
        } else {
                gen6_enable_rps(dev);
                gen6_update_ring_freq(dev);
        }
+       dev_priv->rps.enabled = true;
        mutex_unlock(&dev_priv->rps.hw_lock);
 }
 
@@ -4672,7 +5014,7 @@ static void g4x_disable_trickle_feed(struct drm_device *dev)
                I915_WRITE(DSPCNTR(pipe),
                           I915_READ(DSPCNTR(pipe)) |
                           DISPPLANE_TRICKLE_FEED_DISABLE);
-               intel_flush_display_plane(dev_priv, pipe);
+               intel_flush_primary_plane(dev_priv, pipe);
        }
 }
 
@@ -4932,6 +5274,50 @@ static void lpt_suspend_hw(struct drm_device *dev)
        }
 }
 
+static void gen8_init_clock_gating(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe i;
+
+       I915_WRITE(WM3_LP_ILK, 0);
+       I915_WRITE(WM2_LP_ILK, 0);
+       I915_WRITE(WM1_LP_ILK, 0);
+
+       /* FIXME(BDW): Check all the w/a, some might only apply to
+        * pre-production hw. */
+
+       WARN(!i915_preliminary_hw_support,
+            "GEN8_CENTROID_PIXEL_OPT_DIS not be needed for production\n");
+       I915_WRITE(HALF_SLICE_CHICKEN3,
+                  _MASKED_BIT_ENABLE(GEN8_CENTROID_PIXEL_OPT_DIS));
+       I915_WRITE(HALF_SLICE_CHICKEN3,
+                  _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS));
+       I915_WRITE(GAMTARBMODE, _MASKED_BIT_ENABLE(ARB_MODE_BWGTLB_DISABLE));
+
+       I915_WRITE(_3D_CHICKEN3,
+                  _3D_CHICKEN_SDE_LIMIT_FIFO_POLY_DEPTH(2));
+
+       I915_WRITE(COMMON_SLICE_CHICKEN2,
+                  _MASKED_BIT_ENABLE(GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE));
+
+       I915_WRITE(GEN7_HALF_SLICE_CHICKEN1,
+                  _MASKED_BIT_ENABLE(GEN7_SINGLE_SUBSCAN_DISPATCH_ENABLE));
+
+       /* WaSwitchSolVfFArbitrationPriority */
+       I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL);
+
+       /* WaPsrDPAMaskVBlankInSRD */
+       I915_WRITE(CHICKEN_PAR1_1,
+                  I915_READ(CHICKEN_PAR1_1) | DPA_MASK_VBLANK_SRD);
+
+       /* WaPsrDPRSUnmaskVBlankInSRD */
+       for_each_pipe(i) {
+               I915_WRITE(CHICKEN_PIPESL_1(i),
+                          I915_READ(CHICKEN_PIPESL_1(i) |
+                                    DPRS_MASK_VBLANK_SRD));
+       }
+}
+
 static void haswell_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -5255,6 +5641,25 @@ void intel_suspend_hw(struct drm_device *dev)
                lpt_suspend_hw(dev);
 }
 
+static bool is_always_on_power_domain(struct drm_device *dev,
+                                     enum intel_display_power_domain domain)
+{
+       unsigned long always_on_domains;
+
+       BUG_ON(BIT(domain) & ~POWER_DOMAIN_MASK);
+
+       if (IS_BROADWELL(dev)) {
+               always_on_domains = BDW_ALWAYS_ON_POWER_DOMAINS;
+       } else if (IS_HASWELL(dev)) {
+               always_on_domains = HSW_ALWAYS_ON_POWER_DOMAINS;
+       } else {
+               WARN_ON(1);
+               return true;
+       }
+
+       return BIT(domain) & always_on_domains;
+}
+
 /**
  * We should only use the power well if we explicitly asked the hardware to
  * enable it, so check if it's enabled and also check if we've requested it to
@@ -5268,23 +5673,11 @@ bool intel_display_power_enabled(struct drm_device *dev,
        if (!HAS_POWER_WELL(dev))
                return true;
 
-       switch (domain) {
-       case POWER_DOMAIN_PIPE_A:
-       case POWER_DOMAIN_TRANSCODER_EDP:
+       if (is_always_on_power_domain(dev, domain))
                return true;
-       case POWER_DOMAIN_PIPE_B:
-       case POWER_DOMAIN_PIPE_C:
-       case POWER_DOMAIN_PIPE_A_PANEL_FITTER:
-       case POWER_DOMAIN_PIPE_B_PANEL_FITTER:
-       case POWER_DOMAIN_PIPE_C_PANEL_FITTER:
-       case POWER_DOMAIN_TRANSCODER_A:
-       case POWER_DOMAIN_TRANSCODER_B:
-       case POWER_DOMAIN_TRANSCODER_C:
-               return I915_READ(HSW_PWR_WELL_DRIVER) ==
+
+       return I915_READ(HSW_PWR_WELL_DRIVER) ==
                     (HSW_PWR_WELL_ENABLE_REQUEST | HSW_PWR_WELL_STATE_ENABLED);
-       default:
-               BUG();
-       }
 }
 
 static void __intel_set_power_well(struct drm_device *dev, bool enable)
@@ -5328,83 +5721,136 @@ static void __intel_set_power_well(struct drm_device *dev, bool enable)
                        spin_lock_irqsave(&dev->vbl_lock, irqflags);
                        for_each_pipe(p)
                                if (p != PIPE_A)
-                                       dev->last_vblank[p] = 0;
+                                       dev->vblank[p].last = 0;
                        spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
                }
        }
 }
 
-static struct i915_power_well *hsw_pwr;
+static void __intel_power_well_get(struct drm_device *dev,
+                                  struct i915_power_well *power_well)
+{
+       if (!power_well->count++)
+               __intel_set_power_well(dev, true);
+}
+
+static void __intel_power_well_put(struct drm_device *dev,
+                                  struct i915_power_well *power_well)
+{
+       WARN_ON(!power_well->count);
+       if (!--power_well->count && i915_disable_power_well)
+               __intel_set_power_well(dev, false);
+}
+
+void intel_display_power_get(struct drm_device *dev,
+                            enum intel_display_power_domain domain)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct i915_power_domains *power_domains;
+
+       if (!HAS_POWER_WELL(dev))
+               return;
+
+       if (is_always_on_power_domain(dev, domain))
+               return;
+
+       power_domains = &dev_priv->power_domains;
+
+       mutex_lock(&power_domains->lock);
+       __intel_power_well_get(dev, &power_domains->power_wells[0]);
+       mutex_unlock(&power_domains->lock);
+}
+
+void intel_display_power_put(struct drm_device *dev,
+                            enum intel_display_power_domain domain)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct i915_power_domains *power_domains;
+
+       if (!HAS_POWER_WELL(dev))
+               return;
+
+       if (is_always_on_power_domain(dev, domain))
+               return;
+
+       power_domains = &dev_priv->power_domains;
+
+       mutex_lock(&power_domains->lock);
+       __intel_power_well_put(dev, &power_domains->power_wells[0]);
+       mutex_unlock(&power_domains->lock);
+}
+
+static struct i915_power_domains *hsw_pwr;
 
 /* Display audio driver power well request */
 void i915_request_power_well(void)
 {
+       struct drm_i915_private *dev_priv;
+
        if (WARN_ON(!hsw_pwr))
                return;
 
-       spin_lock_irq(&hsw_pwr->lock);
-       if (!hsw_pwr->count++ &&
-                       !hsw_pwr->i915_request)
-               __intel_set_power_well(hsw_pwr->device, true);
-       spin_unlock_irq(&hsw_pwr->lock);
+       dev_priv = container_of(hsw_pwr, struct drm_i915_private,
+                               power_domains);
+
+       mutex_lock(&hsw_pwr->lock);
+       __intel_power_well_get(dev_priv->dev, &hsw_pwr->power_wells[0]);
+       mutex_unlock(&hsw_pwr->lock);
 }
 EXPORT_SYMBOL_GPL(i915_request_power_well);
 
 /* Display audio driver power well release */
 void i915_release_power_well(void)
 {
+       struct drm_i915_private *dev_priv;
+
        if (WARN_ON(!hsw_pwr))
                return;
 
-       spin_lock_irq(&hsw_pwr->lock);
-       WARN_ON(!hsw_pwr->count);
-       if (!--hsw_pwr->count &&
-                      !hsw_pwr->i915_request)
-               __intel_set_power_well(hsw_pwr->device, false);
-       spin_unlock_irq(&hsw_pwr->lock);
+       dev_priv = container_of(hsw_pwr, struct drm_i915_private,
+                               power_domains);
+
+       mutex_lock(&hsw_pwr->lock);
+       __intel_power_well_put(dev_priv->dev, &hsw_pwr->power_wells[0]);
+       mutex_unlock(&hsw_pwr->lock);
 }
 EXPORT_SYMBOL_GPL(i915_release_power_well);
 
-int i915_init_power_well(struct drm_device *dev)
+int intel_power_domains_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct i915_power_domains *power_domains = &dev_priv->power_domains;
+       struct i915_power_well *power_well;
 
-       hsw_pwr = &dev_priv->power_well;
+       mutex_init(&power_domains->lock);
+       hsw_pwr = power_domains;
 
-       hsw_pwr->device = dev;
-       spin_lock_init(&hsw_pwr->lock);
-       hsw_pwr->count = 0;
+       power_well = &power_domains->power_wells[0];
+       power_well->count = 0;
 
        return 0;
 }
 
-void i915_remove_power_well(struct drm_device *dev)
+void intel_power_domains_remove(struct drm_device *dev)
 {
        hsw_pwr = NULL;
 }
 
-void intel_set_power_well(struct drm_device *dev, bool enable)
+static void intel_power_domains_resume(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct i915_power_well *power_well = &dev_priv->power_well;
+       struct i915_power_domains *power_domains = &dev_priv->power_domains;
+       struct i915_power_well *power_well;
 
        if (!HAS_POWER_WELL(dev))
                return;
 
-       if (!i915_disable_power_well && !enable)
-               return;
+       mutex_lock(&power_domains->lock);
 
-       spin_lock_irq(&power_well->lock);
-       power_well->i915_request = enable;
+       power_well = &power_domains->power_wells[0];
+       __intel_set_power_well(dev, power_well->count > 0);
 
-       /* only reject "disable" power well request */
-       if (power_well->count && !enable) {
-               spin_unlock_irq(&power_well->lock);
-               return;
-       }
-
-       __intel_set_power_well(dev, enable);
-       spin_unlock_irq(&power_well->lock);
+       mutex_unlock(&power_domains->lock);
 }
 
 /*
@@ -5413,7 +5859,7 @@ void intel_set_power_well(struct drm_device *dev, bool enable)
  * to be enabled, and it will only be disabled if none of the registers is
  * requesting it to be enabled.
  */
-void intel_init_power_well(struct drm_device *dev)
+void intel_power_domains_init_hw(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -5421,7 +5867,8 @@ void intel_init_power_well(struct drm_device *dev)
                return;
 
        /* For now, we need the power well to be always enabled. */
-       intel_set_power_well(dev, true);
+       intel_display_set_init_power(dev, true);
+       intel_power_domains_resume(dev);
 
        /* We're taking over the BIOS, so clear any requests made by it since
         * the driver is in charge now. */
@@ -5525,6 +5972,8 @@ void intel_init_pm(struct drm_device *dev)
                                dev_priv->display.update_wm = NULL;
                        }
                        dev_priv->display.init_clock_gating = haswell_init_clock_gating;
+               } else if (INTEL_INFO(dev)->gen == 8) {
+                       dev_priv->display.init_clock_gating = gen8_init_clock_gating;
                } else
                        dev_priv->display.update_wm = NULL;
        } else if (IS_VALLEYVIEW(dev)) {
@@ -5686,7 +6135,4 @@ void intel_pm_init(struct drm_device *dev)
 
        INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work,
                          intel_gen6_powersave_work);
-
-       INIT_DELAYED_WORK(&dev_priv->rps.vlv_work, vlv_rps_timer_work);
 }
-
index 460ee10..b620337 100644 (file)
@@ -41,6 +41,16 @@ static inline int ring_space(struct intel_ring_buffer *ring)
        return space;
 }
 
+void __intel_ring_advance(struct intel_ring_buffer *ring)
+{
+       struct drm_i915_private *dev_priv = ring->dev->dev_private;
+
+       ring->tail &= ring->size - 1;
+       if (dev_priv->gpu_error.stop_rings & intel_ring_flag(ring))
+               return;
+       ring->write_tail(ring, ring->tail);
+}
+
 static int
 gen2_render_ring_flush(struct intel_ring_buffer *ring,
                       u32      invalidate_domains,
@@ -350,6 +360,47 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring,
        return 0;
 }
 
+static int
+gen8_render_ring_flush(struct intel_ring_buffer *ring,
+                      u32 invalidate_domains, u32 flush_domains)
+{
+       u32 flags = 0;
+       u32 scratch_addr = ring->scratch.gtt_offset + 128;
+       int ret;
+
+       flags |= PIPE_CONTROL_CS_STALL;
+
+       if (flush_domains) {
+               flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
+               flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
+       }
+       if (invalidate_domains) {
+               flags |= PIPE_CONTROL_TLB_INVALIDATE;
+               flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
+               flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
+               flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
+               flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE;
+               flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
+               flags |= PIPE_CONTROL_QW_WRITE;
+               flags |= PIPE_CONTROL_GLOBAL_GTT_IVB;
+       }
+
+       ret = intel_ring_begin(ring, 6);
+       if (ret)
+               return ret;
+
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
+       intel_ring_emit(ring, flags);
+       intel_ring_emit(ring, scratch_addr);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, 0);
+       intel_ring_advance(ring);
+
+       return 0;
+
+}
+
 static void ring_write_tail(struct intel_ring_buffer *ring,
                            u32 value)
 {
@@ -385,8 +436,7 @@ static int init_ring_common(struct intel_ring_buffer *ring)
        int ret = 0;
        u32 head;
 
-       if (HAS_FORCE_WAKE(dev))
-               gen6_gt_force_wake_get(dev_priv);
+       gen6_gt_force_wake_get(dev_priv);
 
        if (I915_NEED_GFX_HWS(dev))
                intel_ring_setup_status_page(ring);
@@ -459,8 +509,7 @@ static int init_ring_common(struct intel_ring_buffer *ring)
        memset(&ring->hangcheck, 0, sizeof(ring->hangcheck));
 
 out:
-       if (HAS_FORCE_WAKE(dev))
-               gen6_gt_force_wake_put(dev_priv);
+       gen6_gt_force_wake_put(dev_priv);
 
        return ret;
 }
@@ -559,8 +608,8 @@ static int init_render_ring(struct intel_ring_buffer *ring)
        if (INTEL_INFO(dev)->gen >= 6)
                I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING));
 
-       if (HAS_L3_GPU_CACHE(dev))
-               I915_WRITE_IMR(ring, ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
+       if (HAS_L3_DPF(dev))
+               I915_WRITE_IMR(ring, ~GT_PARITY_ERROR(dev));
 
        return ret;
 }
@@ -593,7 +642,7 @@ update_mboxes(struct intel_ring_buffer *ring,
 #define MBOX_UPDATE_DWORDS 4
        intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
        intel_ring_emit(ring, mmio_offset);
-       intel_ring_emit(ring, ring->outstanding_lazy_request);
+       intel_ring_emit(ring, ring->outstanding_lazy_seqno);
        intel_ring_emit(ring, MI_NOOP);
 }
 
@@ -629,9 +678,9 @@ gen6_add_request(struct intel_ring_buffer *ring)
 
        intel_ring_emit(ring, MI_STORE_DWORD_INDEX);
        intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
-       intel_ring_emit(ring, ring->outstanding_lazy_request);
+       intel_ring_emit(ring, ring->outstanding_lazy_seqno);
        intel_ring_emit(ring, MI_USER_INTERRUPT);
-       intel_ring_advance(ring);
+       __intel_ring_advance(ring);
 
        return 0;
 }
@@ -723,7 +772,7 @@ pc_render_add_request(struct intel_ring_buffer *ring)
                        PIPE_CONTROL_WRITE_FLUSH |
                        PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE);
        intel_ring_emit(ring, ring->scratch.gtt_offset | PIPE_CONTROL_GLOBAL_GTT);
-       intel_ring_emit(ring, ring->outstanding_lazy_request);
+       intel_ring_emit(ring, ring->outstanding_lazy_seqno);
        intel_ring_emit(ring, 0);
        PIPE_CONTROL_FLUSH(ring, scratch_addr);
        scratch_addr += 128; /* write to separate cachelines */
@@ -742,9 +791,9 @@ pc_render_add_request(struct intel_ring_buffer *ring)
                        PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE |
                        PIPE_CONTROL_NOTIFY);
        intel_ring_emit(ring, ring->scratch.gtt_offset | PIPE_CONTROL_GLOBAL_GTT);
-       intel_ring_emit(ring, ring->outstanding_lazy_request);
+       intel_ring_emit(ring, ring->outstanding_lazy_seqno);
        intel_ring_emit(ring, 0);
-       intel_ring_advance(ring);
+       __intel_ring_advance(ring);
 
        return 0;
 }
@@ -963,9 +1012,9 @@ i9xx_add_request(struct intel_ring_buffer *ring)
 
        intel_ring_emit(ring, MI_STORE_DWORD_INDEX);
        intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
-       intel_ring_emit(ring, ring->outstanding_lazy_request);
+       intel_ring_emit(ring, ring->outstanding_lazy_seqno);
        intel_ring_emit(ring, MI_USER_INTERRUPT);
-       intel_ring_advance(ring);
+       __intel_ring_advance(ring);
 
        return 0;
 }
@@ -987,10 +1036,10 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring)
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
        if (ring->irq_refcount++ == 0) {
-               if (HAS_L3_GPU_CACHE(dev) && ring->id == RCS)
+               if (HAS_L3_DPF(dev) && ring->id == RCS)
                        I915_WRITE_IMR(ring,
                                       ~(ring->irq_enable_mask |
-                                        GT_RENDER_L3_PARITY_ERROR_INTERRUPT));
+                                        GT_PARITY_ERROR(dev)));
                else
                        I915_WRITE_IMR(ring, ~ring->irq_enable_mask);
                ilk_enable_gt_irq(dev_priv, ring->irq_enable_mask);
@@ -1009,9 +1058,8 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring)
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
        if (--ring->irq_refcount == 0) {
-               if (HAS_L3_GPU_CACHE(dev) && ring->id == RCS)
-                       I915_WRITE_IMR(ring,
-                                      ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
+               if (HAS_L3_DPF(dev) && ring->id == RCS)
+                       I915_WRITE_IMR(ring, ~GT_PARITY_ERROR(dev));
                else
                        I915_WRITE_IMR(ring, ~0);
                ilk_disable_gt_irq(dev_priv, ring->irq_enable_mask);
@@ -1059,6 +1107,52 @@ hsw_vebox_put_irq(struct intel_ring_buffer *ring)
        spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
 }
 
+static bool
+gen8_ring_get_irq(struct intel_ring_buffer *ring)
+{
+       struct drm_device *dev = ring->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long flags;
+
+       if (!dev->irq_enabled)
+               return false;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, flags);
+       if (ring->irq_refcount++ == 0) {
+               if (HAS_L3_DPF(dev) && ring->id == RCS) {
+                       I915_WRITE_IMR(ring,
+                                      ~(ring->irq_enable_mask |
+                                        GT_RENDER_L3_PARITY_ERROR_INTERRUPT));
+               } else {
+                       I915_WRITE_IMR(ring, ~ring->irq_enable_mask);
+               }
+               POSTING_READ(RING_IMR(ring->mmio_base));
+       }
+       spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+
+       return true;
+}
+
+static void
+gen8_ring_put_irq(struct intel_ring_buffer *ring)
+{
+       struct drm_device *dev = ring->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, flags);
+       if (--ring->irq_refcount == 0) {
+               if (HAS_L3_DPF(dev) && ring->id == RCS) {
+                       I915_WRITE_IMR(ring,
+                                      ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
+               } else {
+                       I915_WRITE_IMR(ring, ~0);
+               }
+               POSTING_READ(RING_IMR(ring->mmio_base));
+       }
+       spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+}
+
 static int
 i965_dispatch_execbuffer(struct intel_ring_buffer *ring,
                         u32 offset, u32 length,
@@ -1317,7 +1411,7 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring)
        /* Disable the ring buffer. The ring must be idle at this point */
        dev_priv = ring->dev->dev_private;
        ret = intel_ring_idle(ring);
-       if (ret)
+       if (ret && !i915_reset_in_progress(&dev_priv->gpu_error))
                DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n",
                          ring->name, ret);
 
@@ -1328,6 +1422,8 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring)
        i915_gem_object_unpin(ring->obj);
        drm_gem_object_unreference(&ring->obj->base);
        ring->obj = NULL;
+       ring->preallocated_lazy_request = NULL;
+       ring->outstanding_lazy_seqno = 0;
 
        if (ring->cleanup)
                ring->cleanup(ring);
@@ -1414,6 +1510,9 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
        if (ret != -ENOSPC)
                return ret;
 
+       /* force the tail write in case we have been skipping them */
+       __intel_ring_advance(ring);
+
        trace_i915_ring_wait_begin(ring);
        /* With GEM the hangcheck timer should kick us out of the loop,
         * leaving it early runs the risk of corrupting GEM state (due
@@ -1475,7 +1574,7 @@ int intel_ring_idle(struct intel_ring_buffer *ring)
        int ret;
 
        /* We need to add any requests required to flush the objects and ring */
-       if (ring->outstanding_lazy_request) {
+       if (ring->outstanding_lazy_seqno) {
                ret = i915_add_request(ring, NULL);
                if (ret)
                        return ret;
@@ -1495,10 +1594,20 @@ int intel_ring_idle(struct intel_ring_buffer *ring)
 static int
 intel_ring_alloc_seqno(struct intel_ring_buffer *ring)
 {
-       if (ring->outstanding_lazy_request)
+       if (ring->outstanding_lazy_seqno)
                return 0;
 
-       return i915_gem_get_seqno(ring->dev, &ring->outstanding_lazy_request);
+       if (ring->preallocated_lazy_request == NULL) {
+               struct drm_i915_gem_request *request;
+
+               request = kmalloc(sizeof(*request), GFP_KERNEL);
+               if (request == NULL)
+                       return -ENOMEM;
+
+               ring->preallocated_lazy_request = request;
+       }
+
+       return i915_gem_get_seqno(ring->dev, &ring->outstanding_lazy_seqno);
 }
 
 static int __intel_ring_begin(struct intel_ring_buffer *ring,
@@ -1545,7 +1654,7 @@ void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
 
-       BUG_ON(ring->outstanding_lazy_request);
+       BUG_ON(ring->outstanding_lazy_seqno);
 
        if (INTEL_INFO(ring->dev)->gen >= 6) {
                I915_WRITE(RING_SYNC_0(ring->mmio_base), 0);
@@ -1558,17 +1667,6 @@ void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno)
        ring->hangcheck.seqno = seqno;
 }
 
-void intel_ring_advance(struct intel_ring_buffer *ring)
-{
-       struct drm_i915_private *dev_priv = ring->dev->dev_private;
-
-       ring->tail &= ring->size - 1;
-       if (dev_priv->gpu_error.stop_rings & intel_ring_flag(ring))
-               return;
-       ring->write_tail(ring, ring->tail);
-}
-
-
 static void gen6_bsd_ring_write_tail(struct intel_ring_buffer *ring,
                                     u32 value)
 {
@@ -1613,6 +1711,8 @@ static int gen6_bsd_ring_flush(struct intel_ring_buffer *ring,
                return ret;
 
        cmd = MI_FLUSH_DW;
+       if (INTEL_INFO(ring->dev)->gen >= 8)
+               cmd += 1;
        /*
         * Bspec vol 1c.5 - video engine command streamer:
         * "If ENABLED, all TLBs will be invalidated once the flush
@@ -1624,9 +1724,38 @@ static int gen6_bsd_ring_flush(struct intel_ring_buffer *ring,
                        MI_FLUSH_DW_STORE_INDEX | MI_FLUSH_DW_OP_STOREDW;
        intel_ring_emit(ring, cmd);
        intel_ring_emit(ring, I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT);
+       if (INTEL_INFO(ring->dev)->gen >= 8) {
+               intel_ring_emit(ring, 0); /* upper addr */
+               intel_ring_emit(ring, 0); /* value */
+       } else  {
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, MI_NOOP);
+       }
+       intel_ring_advance(ring);
+       return 0;
+}
+
+static int
+gen8_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
+                             u32 offset, u32 len,
+                             unsigned flags)
+{
+       struct drm_i915_private *dev_priv = ring->dev->dev_private;
+       bool ppgtt = dev_priv->mm.aliasing_ppgtt != NULL &&
+               !(flags & I915_DISPATCH_SECURE);
+       int ret;
+
+       ret = intel_ring_begin(ring, 4);
+       if (ret)
+               return ret;
+
+       /* FIXME(BDW): Address space and security selectors. */
+       intel_ring_emit(ring, MI_BATCH_BUFFER_START_GEN8 | (ppgtt<<8));
+       intel_ring_emit(ring, offset);
        intel_ring_emit(ring, 0);
        intel_ring_emit(ring, MI_NOOP);
        intel_ring_advance(ring);
+
        return 0;
 }
 
@@ -1686,6 +1815,8 @@ static int gen6_ring_flush(struct intel_ring_buffer *ring,
                return ret;
 
        cmd = MI_FLUSH_DW;
+       if (INTEL_INFO(ring->dev)->gen >= 8)
+               cmd += 1;
        /*
         * Bspec vol 1c.3 - blitter engine command streamer:
         * "If ENABLED, all TLBs will be invalidated once the flush
@@ -1697,8 +1828,13 @@ static int gen6_ring_flush(struct intel_ring_buffer *ring,
                        MI_FLUSH_DW_OP_STOREDW;
        intel_ring_emit(ring, cmd);
        intel_ring_emit(ring, I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT);
-       intel_ring_emit(ring, 0);
-       intel_ring_emit(ring, MI_NOOP);
+       if (INTEL_INFO(ring->dev)->gen >= 8) {
+               intel_ring_emit(ring, 0); /* upper addr */
+               intel_ring_emit(ring, 0); /* value */
+       } else  {
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, MI_NOOP);
+       }
        intel_ring_advance(ring);
 
        if (IS_GEN7(dev) && flush)
@@ -1721,8 +1857,14 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
                ring->flush = gen7_render_ring_flush;
                if (INTEL_INFO(dev)->gen == 6)
                        ring->flush = gen6_render_ring_flush;
-               ring->irq_get = gen6_ring_get_irq;
-               ring->irq_put = gen6_ring_put_irq;
+               if (INTEL_INFO(dev)->gen >= 8) {
+                       ring->flush = gen8_render_ring_flush;
+                       ring->irq_get = gen8_ring_get_irq;
+                       ring->irq_put = gen8_ring_put_irq;
+               } else {
+                       ring->irq_get = gen6_ring_get_irq;
+                       ring->irq_put = gen6_ring_put_irq;
+               }
                ring->irq_enable_mask = GT_RENDER_USER_INTERRUPT;
                ring->get_seqno = gen6_ring_get_seqno;
                ring->set_seqno = ring_set_seqno;
@@ -1764,6 +1906,8 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
        ring->write_tail = ring_write_tail;
        if (IS_HASWELL(dev))
                ring->dispatch_execbuffer = hsw_ring_dispatch_execbuffer;
+       else if (IS_GEN8(dev))
+               ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer;
        else if (INTEL_INFO(dev)->gen >= 6)
                ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
        else if (INTEL_INFO(dev)->gen >= 4)
@@ -1877,7 +2021,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
        ring->id = VCS;
 
        ring->write_tail = ring_write_tail;
-       if (IS_GEN6(dev) || IS_GEN7(dev)) {
+       if (INTEL_INFO(dev)->gen >= 6) {
                ring->mmio_base = GEN6_BSD_RING_BASE;
                /* gen6 bsd needs a special wa for tail updates */
                if (IS_GEN6(dev))
@@ -1886,10 +2030,20 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
                ring->add_request = gen6_add_request;
                ring->get_seqno = gen6_ring_get_seqno;
                ring->set_seqno = ring_set_seqno;
-               ring->irq_enable_mask = GT_BSD_USER_INTERRUPT;
-               ring->irq_get = gen6_ring_get_irq;
-               ring->irq_put = gen6_ring_put_irq;
-               ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
+               if (INTEL_INFO(dev)->gen >= 8) {
+                       ring->irq_enable_mask =
+                               GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT;
+                       ring->irq_get = gen8_ring_get_irq;
+                       ring->irq_put = gen8_ring_put_irq;
+                       ring->dispatch_execbuffer =
+                               gen8_ring_dispatch_execbuffer;
+               } else {
+                       ring->irq_enable_mask = GT_BSD_USER_INTERRUPT;
+                       ring->irq_get = gen6_ring_get_irq;
+                       ring->irq_put = gen6_ring_put_irq;
+                       ring->dispatch_execbuffer =
+                               gen6_ring_dispatch_execbuffer;
+               }
                ring->sync_to = gen6_ring_sync;
                ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_VR;
                ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_INVALID;
@@ -1935,10 +2089,18 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
        ring->add_request = gen6_add_request;
        ring->get_seqno = gen6_ring_get_seqno;
        ring->set_seqno = ring_set_seqno;
-       ring->irq_enable_mask = GT_BLT_USER_INTERRUPT;
-       ring->irq_get = gen6_ring_get_irq;
-       ring->irq_put = gen6_ring_put_irq;
-       ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
+       if (INTEL_INFO(dev)->gen >= 8) {
+               ring->irq_enable_mask =
+                       GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT;
+               ring->irq_get = gen8_ring_get_irq;
+               ring->irq_put = gen8_ring_put_irq;
+               ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer;
+       } else {
+               ring->irq_enable_mask = GT_BLT_USER_INTERRUPT;
+               ring->irq_get = gen6_ring_get_irq;
+               ring->irq_put = gen6_ring_put_irq;
+               ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
+       }
        ring->sync_to = gen6_ring_sync;
        ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_BR;
        ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_BV;
@@ -1967,10 +2129,19 @@ int intel_init_vebox_ring_buffer(struct drm_device *dev)
        ring->add_request = gen6_add_request;
        ring->get_seqno = gen6_ring_get_seqno;
        ring->set_seqno = ring_set_seqno;
-       ring->irq_enable_mask = PM_VEBOX_USER_INTERRUPT;
-       ring->irq_get = hsw_vebox_get_irq;
-       ring->irq_put = hsw_vebox_put_irq;
-       ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
+
+       if (INTEL_INFO(dev)->gen >= 8) {
+               ring->irq_enable_mask =
+                       GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT;
+               ring->irq_get = gen8_ring_get_irq;
+               ring->irq_put = gen8_ring_put_irq;
+               ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer;
+       } else {
+               ring->irq_enable_mask = PM_VEBOX_USER_INTERRUPT;
+               ring->irq_get = hsw_vebox_get_irq;
+               ring->irq_put = hsw_vebox_put_irq;
+               ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
+       }
        ring->sync_to = gen6_ring_sync;
        ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_VER;
        ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_VEV;
index 68b1ca9..71a73f4 100644 (file)
@@ -34,6 +34,7 @@ struct  intel_hw_status_page {
 #define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR((ring)->mmio_base), val)
 
 enum intel_ring_hangcheck_action {
+       HANGCHECK_IDLE = 0,
        HANGCHECK_WAIT,
        HANGCHECK_ACTIVE,
        HANGCHECK_KICK,
@@ -140,7 +141,8 @@ struct  intel_ring_buffer {
        /**
         * Do we have some not yet emitted requests outstanding?
         */
-       u32 outstanding_lazy_request;
+       struct drm_i915_gem_request *preallocated_lazy_request;
+       u32 outstanding_lazy_seqno;
        bool gpu_caches_dirty;
        bool fbc_dirty;
 
@@ -237,7 +239,12 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring,
        iowrite32(data, ring->virtual_start + ring->tail);
        ring->tail += 4;
 }
-void intel_ring_advance(struct intel_ring_buffer *ring);
+static inline void intel_ring_advance(struct intel_ring_buffer *ring)
+{
+       ring->tail &= ring->size - 1;
+}
+void __intel_ring_advance(struct intel_ring_buffer *ring);
+
 int __must_check intel_ring_idle(struct intel_ring_buffer *ring);
 void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno);
 int intel_ring_flush_all_caches(struct intel_ring_buffer *ring);
@@ -258,8 +265,8 @@ static inline u32 intel_ring_get_tail(struct intel_ring_buffer *ring)
 
 static inline u32 intel_ring_get_seqno(struct intel_ring_buffer *ring)
 {
-       BUG_ON(ring->outstanding_lazy_request == 0);
-       return ring->outstanding_lazy_request;
+       BUG_ON(ring->outstanding_lazy_seqno == 0);
+       return ring->outstanding_lazy_seqno;
 }
 
 static inline void i915_trace_irq_get(struct intel_ring_buffer *ring, u32 seqno)
index 49482fd..a583e8f 100644 (file)
@@ -539,7 +539,7 @@ static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo,
                goto log_fail;
 
        while ((status == SDVO_CMD_STATUS_PENDING ||
-                       status == SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED) && --retry) {
+               status == SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED) && --retry) {
                if (retry < 10)
                        msleep(15);
                else
@@ -1068,7 +1068,7 @@ intel_sdvo_get_preferred_input_mode(struct intel_sdvo *intel_sdvo,
 
 static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc_config *pipe_config)
 {
-       unsigned dotclock = pipe_config->adjusted_mode.clock;
+       unsigned dotclock = pipe_config->port_clock;
        struct dpll *clock = &pipe_config->dpll;
 
        /* SDVO TV has fixed PLL values depend on its clock range,
@@ -1133,7 +1133,6 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
         */
        pipe_config->pixel_multiplier =
                intel_sdvo_get_pixel_multiplier(adjusted_mode);
-       adjusted_mode->clock *= pipe_config->pixel_multiplier;
 
        if (intel_sdvo->color_range_auto) {
                /* See CEA-861-E - 5.1 Default Encoding Parameters */
@@ -1217,11 +1216,7 @@ static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder)
            !intel_sdvo_set_tv_format(intel_sdvo))
                return;
 
-       /* We have tried to get input timing in mode_fixup, and filled into
-        * adjusted_mode.
-        */
        intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
-       input_dtd.part1.clock /= crtc->config.pixel_multiplier;
 
        if (intel_sdvo->is_tv || intel_sdvo->is_lvds)
                input_dtd.part2.sdvo_flags = intel_sdvo->dtd_sdvo_flags;
@@ -1330,6 +1325,7 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
        struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
        struct intel_sdvo_dtd dtd;
        int encoder_pixel_multiplier = 0;
+       int dotclock;
        u32 flags = 0, sdvox;
        u8 val;
        bool ret;
@@ -1368,6 +1364,13 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
                         >> SDVO_PORT_MULTIPLY_SHIFT) + 1;
        }
 
+       dotclock = pipe_config->port_clock / pipe_config->pixel_multiplier;
+
+       if (HAS_PCH_SPLIT(dev))
+               ironlake_check_encoder_dotclock(pipe_config, dotclock);
+
+       pipe_config->adjusted_mode.crtc_clock = dotclock;
+
        /* Cross check the port pixel multiplier with the sdvo encoder state. */
        if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_CLOCK_RATE_MULT,
                                 &val, 1)) {
@@ -1770,6 +1773,9 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
 {
        struct edid *edid;
 
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+                     connector->base.id, drm_get_connector_name(connector));
+
        /* set the bus switch and get the modes */
        edid = intel_sdvo_get_edid(connector);
 
@@ -1865,6 +1871,9 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
        uint32_t reply = 0, format_map = 0;
        int i;
 
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+                     connector->base.id, drm_get_connector_name(connector));
+
        /* Read the list of supported input resolutions for the selected TV
         * format.
         */
@@ -1899,6 +1908,9 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
        struct drm_i915_private *dev_priv = connector->dev->dev_private;
        struct drm_display_mode *newmode;
 
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+                     connector->base.id, drm_get_connector_name(connector));
+
        /*
         * Fetch modes from VBT. For SDVO prefer the VBT mode since some
         * SDVO->LVDS transcoders can't cope with the EDID mode.
@@ -1930,7 +1942,6 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
                        break;
                }
        }
-
 }
 
 static int intel_sdvo_get_modes(struct drm_connector *connector)
@@ -1998,7 +2009,6 @@ static void intel_sdvo_destroy(struct drm_connector *connector)
                                     intel_sdvo_connector->tv_format);
 
        intel_sdvo_destroy_enhance_property(connector);
-       drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
        kfree(intel_sdvo_connector);
 }
@@ -2394,7 +2404,9 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
        struct intel_connector *intel_connector;
        struct intel_sdvo_connector *intel_sdvo_connector;
 
-       intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
+       DRM_DEBUG_KMS("initialising DVI device %d\n", device);
+
+       intel_sdvo_connector = kzalloc(sizeof(*intel_sdvo_connector), GFP_KERNEL);
        if (!intel_sdvo_connector)
                return false;
 
@@ -2442,7 +2454,9 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
        struct intel_connector *intel_connector;
        struct intel_sdvo_connector *intel_sdvo_connector;
 
-       intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
+       DRM_DEBUG_KMS("initialising TV type %d\n", type);
+
+       intel_sdvo_connector = kzalloc(sizeof(*intel_sdvo_connector), GFP_KERNEL);
        if (!intel_sdvo_connector)
                return false;
 
@@ -2467,6 +2481,7 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
        return true;
 
 err:
+       drm_sysfs_connector_remove(connector);
        intel_sdvo_destroy(connector);
        return false;
 }
@@ -2479,7 +2494,9 @@ intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device)
        struct intel_connector *intel_connector;
        struct intel_sdvo_connector *intel_sdvo_connector;
 
-       intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
+       DRM_DEBUG_KMS("initialising analog device %d\n", device);
+
+       intel_sdvo_connector = kzalloc(sizeof(*intel_sdvo_connector), GFP_KERNEL);
        if (!intel_sdvo_connector)
                return false;
 
@@ -2510,7 +2527,9 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
        struct intel_connector *intel_connector;
        struct intel_sdvo_connector *intel_sdvo_connector;
 
-       intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
+       DRM_DEBUG_KMS("initialising LVDS device %d\n", device);
+
+       intel_sdvo_connector = kzalloc(sizeof(*intel_sdvo_connector), GFP_KERNEL);
        if (!intel_sdvo_connector)
                return false;
 
@@ -2534,6 +2553,7 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
        return true;
 
 err:
+       drm_sysfs_connector_remove(connector);
        intel_sdvo_destroy(connector);
        return false;
 }
@@ -2605,8 +2625,10 @@ static void intel_sdvo_output_cleanup(struct intel_sdvo *intel_sdvo)
 
        list_for_each_entry_safe(connector, tmp,
                                 &dev->mode_config.connector_list, head) {
-               if (intel_attached_encoder(connector) == &intel_sdvo->base)
+               if (intel_attached_encoder(connector) == &intel_sdvo->base) {
+                       drm_sysfs_connector_remove(connector);
                        intel_sdvo_destroy(connector);
+               }
        }
 }
 
@@ -2876,7 +2898,7 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
        struct intel_encoder *intel_encoder;
        struct intel_sdvo *intel_sdvo;
        int i;
-       intel_sdvo = kzalloc(sizeof(struct intel_sdvo), GFP_KERNEL);
+       intel_sdvo = kzalloc(sizeof(*intel_sdvo), GFP_KERNEL);
        if (!intel_sdvo)
                return false;
 
index 9a0e6c5..9944d81 100644 (file)
 #include "i915_drv.h"
 #include "intel_drv.h"
 
-/* IOSF sideband */
+/*
+ * IOSF sideband, see VLV2_SidebandMsg_HAS.docx and
+ * VLV_VLV2_PUNIT_HAS_0.8.docx
+ */
 static int vlv_sideband_rw(struct drm_i915_private *dev_priv, u32 devfn,
                           u32 port, u32 opcode, u32 addr, u32 *val)
 {
@@ -101,19 +104,83 @@ u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr)
        return val;
 }
 
-u32 vlv_dpio_read(struct drm_i915_private *dev_priv, int reg)
+u32 vlv_gpio_nc_read(struct drm_i915_private *dev_priv, u32 reg)
 {
        u32 val = 0;
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPIO_NC,
+                       PUNIT_OPCODE_REG_READ, reg, &val);
+       return val;
+}
 
-       vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_DPIO,
-                       DPIO_OPCODE_REG_READ, reg, &val);
+void vlv_gpio_nc_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
+{
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPIO_NC,
+                       PUNIT_OPCODE_REG_WRITE, reg, &val);
+}
+
+u32 vlv_cck_read(struct drm_i915_private *dev_priv, u32 reg)
+{
+       u32 val = 0;
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCK,
+                       PUNIT_OPCODE_REG_READ, reg, &val);
+       return val;
+}
+
+void vlv_cck_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
+{
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCK,
+                       PUNIT_OPCODE_REG_WRITE, reg, &val);
+}
+
+u32 vlv_ccu_read(struct drm_i915_private *dev_priv, u32 reg)
+{
+       u32 val = 0;
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCU,
+                       PUNIT_OPCODE_REG_READ, reg, &val);
+       return val;
+}
 
+void vlv_ccu_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
+{
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCU,
+                       PUNIT_OPCODE_REG_WRITE, reg, &val);
+}
+
+u32 vlv_gps_core_read(struct drm_i915_private *dev_priv, u32 reg)
+{
+       u32 val = 0;
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPS_CORE,
+                       PUNIT_OPCODE_REG_READ, reg, &val);
+       return val;
+}
+
+void vlv_gps_core_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
+{
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPS_CORE,
+                       PUNIT_OPCODE_REG_WRITE, reg, &val);
+}
+
+static u32 vlv_get_phy_port(enum pipe pipe)
+{
+       u32 port = IOSF_PORT_DPIO;
+
+       WARN_ON ((pipe != PIPE_A) && (pipe != PIPE_B));
+
+       return port;
+}
+
+u32 vlv_dpio_read(struct drm_i915_private *dev_priv, enum pipe pipe, int reg)
+{
+       u32 val = 0;
+
+       vlv_sideband_rw(dev_priv, DPIO_DEVFN, vlv_get_phy_port(pipe),
+                       DPIO_OPCODE_REG_READ, reg, &val);
        return val;
 }
 
-void vlv_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val)
+void vlv_dpio_write(struct drm_i915_private *dev_priv, enum pipe pipe, int reg, u32 val)
 {
-       vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_DPIO,
+       vlv_sideband_rw(dev_priv, DPIO_DEVFN, vlv_get_phy_port(pipe),
                        DPIO_OPCODE_REG_WRITE, reg, &val);
 }
 
index ad6ec4b..b9fabf8 100644 (file)
@@ -260,14 +260,14 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        if (obj->tiling_mode != I915_TILING_NONE)
                sprctl |= SPRITE_TILED;
 
-       if (IS_HASWELL(dev))
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev))
                sprctl &= ~SPRITE_TRICKLE_FEED_DISABLE;
        else
                sprctl |= SPRITE_TRICKLE_FEED_DISABLE;
 
        sprctl |= SPRITE_ENABLE;
 
-       if (IS_HASWELL(dev))
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev))
                sprctl |= SPRITE_PIPE_CSC_ENABLE;
 
        intel_update_sprite_watermarks(plane, crtc, src_w, pixel_size, true,
@@ -288,7 +288,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                dev_priv->sprite_scaling_enabled |= 1 << pipe;
 
                if (!scaling_was_enabled) {
-                       intel_update_watermarks(dev);
+                       intel_update_watermarks(crtc);
                        intel_wait_for_vblank(dev, pipe);
                }
                sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h;
@@ -306,7 +306,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 
        /* HSW consolidates SPRTILEOFF and SPRLINOFF into a single SPROFFSET
         * register */
-       if (IS_HASWELL(dev))
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev))
                I915_WRITE(SPROFFSET(pipe), (y << 16) | x);
        else if (obj->tiling_mode != I915_TILING_NONE)
                I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x);
@@ -323,7 +323,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 
        /* potentially re-enable LP watermarks */
        if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled)
-               intel_update_watermarks(dev);
+               intel_update_watermarks(crtc);
 }
 
 static void
@@ -349,7 +349,7 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
 
        /* potentially re-enable LP watermarks */
        if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled)
-               intel_update_watermarks(dev);
+               intel_update_watermarks(crtc);
 }
 
 static int
@@ -521,13 +521,28 @@ intel_enable_primary(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int reg = DSPCNTR(intel_crtc->plane);
 
-       if (!intel_crtc->primary_disabled)
+       if (intel_crtc->primary_enabled)
                return;
 
-       intel_crtc->primary_disabled = false;
-       intel_update_fbc(dev);
+       intel_crtc->primary_enabled = true;
 
        I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE);
+       intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+       /*
+        * FIXME IPS should be fine as long as one plane is
+        * enabled, but in practice it seems to have problems
+        * when going from primary only to sprite only and vice
+        * versa.
+        */
+       if (intel_crtc->config.ips_enabled) {
+               intel_wait_for_vblank(dev, intel_crtc->pipe);
+               hsw_enable_ips(intel_crtc);
+       }
+
+       mutex_lock(&dev->struct_mutex);
+       intel_update_fbc(dev);
+       mutex_unlock(&dev->struct_mutex);
 }
 
 static void
@@ -538,13 +553,26 @@ intel_disable_primary(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int reg = DSPCNTR(intel_crtc->plane);
 
-       if (intel_crtc->primary_disabled)
+       if (!intel_crtc->primary_enabled)
                return;
 
-       I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE);
+       intel_crtc->primary_enabled = false;
 
-       intel_crtc->primary_disabled = true;
-       intel_update_fbc(dev);
+       mutex_lock(&dev->struct_mutex);
+       if (dev_priv->fbc.plane == intel_crtc->plane)
+               intel_disable_fbc(dev);
+       mutex_unlock(&dev->struct_mutex);
+
+       /*
+        * FIXME IPS should be fine as long as one plane is
+        * enabled, but in practice it seems to have problems
+        * when going from primary only to sprite only and vice
+        * versa.
+        */
+       hsw_disable_ips(intel_crtc);
+
+       I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE);
+       intel_flush_primary_plane(dev_priv, intel_crtc->plane);
 }
 
 static int
@@ -623,15 +651,12 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                   uint32_t src_w, uint32_t src_h)
 {
        struct drm_device *dev = plane->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_plane *intel_plane = to_intel_plane(plane);
-       struct intel_framebuffer *intel_fb;
-       struct drm_i915_gem_object *obj, *old_obj;
-       int pipe = intel_plane->pipe;
-       enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
-                                                                     pipe);
-       int ret = 0;
+       struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+       struct drm_i915_gem_object *obj = intel_fb->obj;
+       struct drm_i915_gem_object *old_obj = intel_plane->obj;
+       int ret;
        bool disable_primary = false;
        bool visible;
        int hscale, vscale;
@@ -652,29 +677,23 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                .y2 = crtc_y + crtc_h,
        };
        const struct drm_rect clip = {
-               .x2 = crtc->mode.hdisplay,
-               .y2 = crtc->mode.vdisplay,
+               .x2 = intel_crtc->active ? intel_crtc->config.pipe_src_w : 0,
+               .y2 = intel_crtc->active ? intel_crtc->config.pipe_src_h : 0,
+       };
+       const struct {
+               int crtc_x, crtc_y;
+               unsigned int crtc_w, crtc_h;
+               uint32_t src_x, src_y, src_w, src_h;
+       } orig = {
+               .crtc_x = crtc_x,
+               .crtc_y = crtc_y,
+               .crtc_w = crtc_w,
+               .crtc_h = crtc_h,
+               .src_x = src_x,
+               .src_y = src_y,
+               .src_w = src_w,
+               .src_h = src_h,
        };
-
-       intel_fb = to_intel_framebuffer(fb);
-       obj = intel_fb->obj;
-
-       old_obj = intel_plane->obj;
-
-       intel_plane->crtc_x = crtc_x;
-       intel_plane->crtc_y = crtc_y;
-       intel_plane->crtc_w = crtc_w;
-       intel_plane->crtc_h = crtc_h;
-       intel_plane->src_x = src_x;
-       intel_plane->src_y = src_y;
-       intel_plane->src_w = src_w;
-       intel_plane->src_h = src_h;
-
-       /* Pipe must be running... */
-       if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE)) {
-               DRM_DEBUG_KMS("Pipe disabled\n");
-               return -EINVAL;
-       }
 
        /* Don't modify another pipe's plane */
        if (intel_plane->pipe != intel_crtc->pipe) {
@@ -810,7 +829,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
         * we can disable the primary and save power.
         */
        disable_primary = drm_rect_equals(&dst, &clip);
-       WARN_ON(disable_primary && !visible);
+       WARN_ON(disable_primary && !visible && intel_crtc->active);
 
        mutex_lock(&dev->struct_mutex);
 
@@ -820,27 +839,40 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
         * the sprite planes only require 128KiB alignment and 32 PTE padding.
         */
        ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
-       if (ret)
-               goto out_unlock;
 
-       intel_plane->obj = obj;
-
-       /*
-        * Be sure to re-enable the primary before the sprite is no longer
-        * covering it fully.
-        */
-       if (!disable_primary)
-               intel_enable_primary(crtc);
+       mutex_unlock(&dev->struct_mutex);
 
-       if (visible)
-               intel_plane->update_plane(plane, crtc, fb, obj,
-                                         crtc_x, crtc_y, crtc_w, crtc_h,
-                                         src_x, src_y, src_w, src_h);
-       else
-               intel_plane->disable_plane(plane, crtc);
+       if (ret)
+               return ret;
+
+       intel_plane->crtc_x = orig.crtc_x;
+       intel_plane->crtc_y = orig.crtc_y;
+       intel_plane->crtc_w = orig.crtc_w;
+       intel_plane->crtc_h = orig.crtc_h;
+       intel_plane->src_x = orig.src_x;
+       intel_plane->src_y = orig.src_y;
+       intel_plane->src_w = orig.src_w;
+       intel_plane->src_h = orig.src_h;
+       intel_plane->obj = obj;
 
-       if (disable_primary)
-               intel_disable_primary(crtc);
+       if (intel_crtc->active) {
+               /*
+                * Be sure to re-enable the primary before the sprite is no longer
+                * covering it fully.
+                */
+               if (!disable_primary)
+                       intel_enable_primary(crtc);
+
+               if (visible)
+                       intel_plane->update_plane(plane, crtc, fb, obj,
+                                                 crtc_x, crtc_y, crtc_w, crtc_h,
+                                                 src_x, src_y, src_w, src_h);
+               else
+                       intel_plane->disable_plane(plane, crtc);
+
+               if (disable_primary)
+                       intel_disable_primary(crtc);
+       }
 
        /* Unpin old obj after new one is active to avoid ugliness */
        if (old_obj) {
@@ -850,17 +882,15 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                 * wait for vblank to avoid ugliness, we only need to
                 * do the pin & ref bookkeeping.
                 */
-               if (old_obj != obj) {
-                       mutex_unlock(&dev->struct_mutex);
-                       intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe);
-                       mutex_lock(&dev->struct_mutex);
-               }
+               if (old_obj != obj && intel_crtc->active)
+                       intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+               mutex_lock(&dev->struct_mutex);
                intel_unpin_fb_obj(old_obj);
+               mutex_unlock(&dev->struct_mutex);
        }
 
-out_unlock:
-       mutex_unlock(&dev->struct_mutex);
-       return ret;
+       return 0;
 }
 
 static int
@@ -868,7 +898,7 @@ intel_disable_plane(struct drm_plane *plane)
 {
        struct drm_device *dev = plane->dev;
        struct intel_plane *intel_plane = to_intel_plane(plane);
-       int ret = 0;
+       struct intel_crtc *intel_crtc;
 
        if (!plane->fb)
                return 0;
@@ -876,21 +906,25 @@ intel_disable_plane(struct drm_plane *plane)
        if (WARN_ON(!plane->crtc))
                return -EINVAL;
 
-       intel_enable_primary(plane->crtc);
-       intel_plane->disable_plane(plane, plane->crtc);
+       intel_crtc = to_intel_crtc(plane->crtc);
 
-       if (!intel_plane->obj)
-               goto out;
+       if (intel_crtc->active) {
+               intel_enable_primary(plane->crtc);
+               intel_plane->disable_plane(plane, plane->crtc);
+       }
 
-       intel_wait_for_vblank(dev, intel_plane->pipe);
+       if (intel_plane->obj) {
+               if (intel_crtc->active)
+                       intel_wait_for_vblank(dev, intel_plane->pipe);
 
-       mutex_lock(&dev->struct_mutex);
-       intel_unpin_fb_obj(intel_plane->obj);
-       intel_plane->obj = NULL;
-       mutex_unlock(&dev->struct_mutex);
-out:
+               mutex_lock(&dev->struct_mutex);
+               intel_unpin_fb_obj(intel_plane->obj);
+               mutex_unlock(&dev->struct_mutex);
 
-       return ret;
+               intel_plane->obj = NULL;
+       }
+
+       return 0;
 }
 
 static void intel_destroy_plane(struct drm_plane *plane)
@@ -921,7 +955,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
 
        obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out_unlock;
        }
 
@@ -950,7 +984,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
 
        obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out_unlock;
        }
 
@@ -1034,7 +1068,7 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane)
        if (INTEL_INFO(dev)->gen < 5)
                return -ENODEV;
 
-       intel_plane = kzalloc(sizeof(struct intel_plane), GFP_KERNEL);
+       intel_plane = kzalloc(sizeof(*intel_plane), GFP_KERNEL);
        if (!intel_plane)
                return -ENOMEM;
 
@@ -1058,6 +1092,7 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane)
                break;
 
        case 7:
+       case 8:
                if (IS_IVYBRIDGE(dev)) {
                        intel_plane->can_scale = true;
                        intel_plane->max_downscale = 2;
index dd6f84b..18c4062 100644 (file)
@@ -912,7 +912,7 @@ intel_tv_compute_config(struct intel_encoder *encoder,
        if (!tv_mode)
                return false;
 
-       pipe_config->adjusted_mode.clock = tv_mode->clock;
+       pipe_config->adjusted_mode.crtc_clock = tv_mode->clock;
        DRM_DEBUG_KMS("forcing bpc to 8 for TV\n");
        pipe_config->pipe_bpp = 8*3;
 
@@ -1044,7 +1044,7 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
                tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
 
        /* Enable two fixes for the chips that need them. */
-       if (dev->pci_device < 0x2772)
+       if (dev->pdev->device < 0x2772)
                tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
 
        I915_WRITE(TV_H_CTL_1, hctl1);
@@ -1094,7 +1094,7 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
                unsigned int xsize, ysize;
                /* Pipe must be off here */
                I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
-               intel_flush_display_plane(dev_priv, intel_crtc->plane);
+               intel_flush_primary_plane(dev_priv, intel_crtc->plane);
 
                /* Wait for vblank for the disable to take effect */
                if (IS_GEN2(dev))
@@ -1123,7 +1123,7 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
 
                I915_WRITE(pipeconf_reg, pipeconf);
                I915_WRITE(dspcntr_reg, dspcntr);
-               intel_flush_display_plane(dev_priv, intel_crtc->plane);
+               intel_flush_primary_plane(dev_priv, intel_crtc->plane);
        }
 
        j = 0;
@@ -1433,7 +1433,6 @@ intel_tv_get_modes(struct drm_connector *connector)
 static void
 intel_tv_destroy(struct drm_connector *connector)
 {
-       drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
        kfree(connector);
 }
@@ -1518,7 +1517,7 @@ static const struct drm_encoder_funcs intel_tv_enc_funcs = {
 static int tv_is_present_in_vbt(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct child_device_config *p_child;
+       union child_device_config *p_child;
        int i, ret;
 
        if (!dev_priv->vbt.child_dev_num)
@@ -1530,13 +1529,13 @@ static int tv_is_present_in_vbt(struct drm_device *dev)
                /*
                 * If the device type is not TV, continue.
                 */
-               if (p_child->device_type != DEVICE_TYPE_INT_TV &&
-                       p_child->device_type != DEVICE_TYPE_TV)
+               if (p_child->old.device_type != DEVICE_TYPE_INT_TV &&
+                       p_child->old.device_type != DEVICE_TYPE_TV)
                        continue;
                /* Only when the addin_offset is non-zero, it is regarded
                 * as present.
                 */
-               if (p_child->addin_offset) {
+               if (p_child->old.addin_offset) {
                        ret = 1;
                        break;
                }
@@ -1590,12 +1589,12 @@ intel_tv_init(struct drm_device *dev)
            (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
                return;
 
-       intel_tv = kzalloc(sizeof(struct intel_tv), GFP_KERNEL);
+       intel_tv = kzalloc(sizeof(*intel_tv), GFP_KERNEL);
        if (!intel_tv) {
                return;
        }
 
-       intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
+       intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
        if (!intel_connector) {
                kfree(intel_tv);
                return;
index 8649f1c..f9883ce 100644 (file)
@@ -93,7 +93,7 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv)
 {
        u32 forcewake_ack;
 
-       if (IS_HASWELL(dev_priv->dev))
+       if (IS_HASWELL(dev_priv->dev) || IS_GEN8(dev_priv->dev))
                forcewake_ack = FORCEWAKE_ACK_HSW;
        else
                forcewake_ack = FORCEWAKE_MT_ACK;
@@ -112,7 +112,8 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv)
                DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
 
        /* WaRsForcewakeWaitTC0:ivb,hsw */
-       __gen6_gt_wait_for_thread_c0(dev_priv);
+       if (INTEL_INFO(dev_priv->dev)->gen < 8)
+               __gen6_gt_wait_for_thread_c0(dev_priv);
 }
 
 static void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv)
@@ -204,60 +205,34 @@ static void vlv_force_wake_put(struct drm_i915_private *dev_priv)
        gen6_gt_check_fifodbg(dev_priv);
 }
 
-void intel_uncore_early_sanitize(struct drm_device *dev)
+static void gen6_force_wake_work(struct work_struct *work)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv =
+               container_of(work, typeof(*dev_priv), uncore.force_wake_work.work);
+       unsigned long irqflags;
 
-       if (HAS_FPGA_DBG_UNCLAIMED(dev))
-               __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
+       spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
+       if (--dev_priv->uncore.forcewake_count == 0)
+               dev_priv->uncore.funcs.force_wake_put(dev_priv);
+       spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
 }
 
-void intel_uncore_init(struct drm_device *dev)
+void intel_uncore_early_sanitize(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (IS_VALLEYVIEW(dev)) {
-               dev_priv->uncore.funcs.force_wake_get = vlv_force_wake_get;
-               dev_priv->uncore.funcs.force_wake_put = vlv_force_wake_put;
-       } else if (IS_HASWELL(dev)) {
-               dev_priv->uncore.funcs.force_wake_get = __gen6_gt_force_wake_mt_get;
-               dev_priv->uncore.funcs.force_wake_put = __gen6_gt_force_wake_mt_put;
-       } else if (IS_IVYBRIDGE(dev)) {
-               u32 ecobus;
-
-               /* IVB configs may use multi-threaded forcewake */
-
-               /* A small trick here - if the bios hasn't configured
-                * MT forcewake, and if the device is in RC6, then
-                * force_wake_mt_get will not wake the device and the
-                * ECOBUS read will return zero. Which will be
-                * (correctly) interpreted by the test below as MT
-                * forcewake being disabled.
-                */
-               mutex_lock(&dev->struct_mutex);
-               __gen6_gt_force_wake_mt_get(dev_priv);
-               ecobus = __raw_i915_read32(dev_priv, ECOBUS);
-               __gen6_gt_force_wake_mt_put(dev_priv);
-               mutex_unlock(&dev->struct_mutex);
+       if (HAS_FPGA_DBG_UNCLAIMED(dev))
+               __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
 
-               if (ecobus & FORCEWAKE_MT_ENABLE) {
-                       dev_priv->uncore.funcs.force_wake_get =
-                               __gen6_gt_force_wake_mt_get;
-                       dev_priv->uncore.funcs.force_wake_put =
-                               __gen6_gt_force_wake_mt_put;
-               } else {
-                       DRM_INFO("No MT forcewake available on Ivybridge, this can result in issues\n");
-                       DRM_INFO("when using vblank-synced partial screen updates.\n");
-                       dev_priv->uncore.funcs.force_wake_get =
-                               __gen6_gt_force_wake_get;
-                       dev_priv->uncore.funcs.force_wake_put =
-                               __gen6_gt_force_wake_put;
-               }
-       } else if (IS_GEN6(dev)) {
-               dev_priv->uncore.funcs.force_wake_get =
-                       __gen6_gt_force_wake_get;
-               dev_priv->uncore.funcs.force_wake_put =
-                       __gen6_gt_force_wake_put;
+       if (IS_HASWELL(dev) &&
+           (__raw_i915_read32(dev_priv, HSW_EDRAM_PRESENT) == 1)) {
+               /* The docs do not explain exactly how the calculation can be
+                * made. It is somewhat guessable, but for now, it's always
+                * 128MB.
+                * NB: We can't write IDICR yet because we do not have gt funcs
+                * set up */
+               dev_priv->ellc_size = 128;
+               DRM_INFO("Found %zuMB of eLLC\n", dev_priv->ellc_size);
        }
 }
 
@@ -276,10 +251,26 @@ static void intel_uncore_forcewake_reset(struct drm_device *dev)
 
 void intel_uncore_sanitize(struct drm_device *dev)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 reg_val;
+
        intel_uncore_forcewake_reset(dev);
 
        /* BIOS often leaves RC6 enabled, but disable it for hw init */
        intel_disable_gt_powersave(dev);
+
+       /* Turn off power gate, require especially for the BIOS less system */
+       if (IS_VALLEYVIEW(dev)) {
+
+               mutex_lock(&dev_priv->rps.hw_lock);
+               reg_val = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS);
+
+               if (reg_val & (RENDER_PWRGT | MEDIA_PWRGT | DISP2D_PWRGT))
+                       vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, 0x0);
+
+               mutex_unlock(&dev_priv->rps.hw_lock);
+
+       }
 }
 
 /*
@@ -292,6 +283,9 @@ void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
 {
        unsigned long irqflags;
 
+       if (!dev_priv->uncore.funcs.force_wake_get)
+               return;
+
        spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
        if (dev_priv->uncore.forcewake_count++ == 0)
                dev_priv->uncore.funcs.force_wake_get(dev_priv);
@@ -305,17 +299,22 @@ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
 {
        unsigned long irqflags;
 
+       if (!dev_priv->uncore.funcs.force_wake_put)
+               return;
+
        spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
-       if (--dev_priv->uncore.forcewake_count == 0)
-               dev_priv->uncore.funcs.force_wake_put(dev_priv);
+       if (--dev_priv->uncore.forcewake_count == 0) {
+               dev_priv->uncore.forcewake_count++;
+               mod_delayed_work(dev_priv->wq,
+                                &dev_priv->uncore.force_wake_work,
+                                1);
+       }
        spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
 }
 
 /* We give fast paths for the really cool registers */
 #define NEEDS_FORCE_WAKE(dev_priv, reg) \
-       ((HAS_FORCE_WAKE((dev_priv)->dev)) && \
-        ((reg) < 0x40000) &&            \
-        ((reg) != FORCEWAKE))
+        ((reg) < 0x40000 && (reg) != FORCEWAKE)
 
 static void
 ilk_dummy_write(struct drm_i915_private *dev_priv)
@@ -329,8 +328,7 @@ ilk_dummy_write(struct drm_i915_private *dev_priv)
 static void
 hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg)
 {
-       if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) &&
-           (__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
+       if (__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM) {
                DRM_ERROR("Unknown unclaimed register before writing to %x\n",
                          reg);
                __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
@@ -340,20 +338,43 @@ hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg)
 static void
 hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg)
 {
-       if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) &&
-           (__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
+       if (__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM) {
                DRM_ERROR("Unclaimed write to %x\n", reg);
                __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
        }
 }
 
-#define __i915_read(x) \
-u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg, bool trace) { \
+#define REG_READ_HEADER(x) \
        unsigned long irqflags; \
        u##x val = 0; \
-       spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); \
-       if (dev_priv->info->gen == 5) \
-               ilk_dummy_write(dev_priv); \
+       spin_lock_irqsave(&dev_priv->uncore.lock, irqflags)
+
+#define REG_READ_FOOTER \
+       spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); \
+       trace_i915_reg_rw(false, reg, val, sizeof(val), trace); \
+       return val
+
+#define __gen4_read(x) \
+static u##x \
+gen4_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
+       REG_READ_HEADER(x); \
+       val = __raw_i915_read##x(dev_priv, reg); \
+       REG_READ_FOOTER; \
+}
+
+#define __gen5_read(x) \
+static u##x \
+gen5_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
+       REG_READ_HEADER(x); \
+       ilk_dummy_write(dev_priv); \
+       val = __raw_i915_read##x(dev_priv, reg); \
+       REG_READ_FOOTER; \
+}
+
+#define __gen6_read(x) \
+static u##x \
+gen6_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
+       REG_READ_HEADER(x); \
        if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
                if (dev_priv->uncore.forcewake_count == 0) \
                        dev_priv->uncore.funcs.force_wake_get(dev_priv); \
@@ -363,28 +384,73 @@ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg, bool trace) { \
        } else { \
                val = __raw_i915_read##x(dev_priv, reg); \
        } \
+       REG_READ_FOOTER; \
+}
+
+__gen6_read(8)
+__gen6_read(16)
+__gen6_read(32)
+__gen6_read(64)
+__gen5_read(8)
+__gen5_read(16)
+__gen5_read(32)
+__gen5_read(64)
+__gen4_read(8)
+__gen4_read(16)
+__gen4_read(32)
+__gen4_read(64)
+
+#undef __gen6_read
+#undef __gen5_read
+#undef __gen4_read
+#undef REG_READ_FOOTER
+#undef REG_READ_HEADER
+
+#define REG_WRITE_HEADER \
+       unsigned long irqflags; \
+       trace_i915_reg_rw(true, reg, val, sizeof(val), trace); \
+       spin_lock_irqsave(&dev_priv->uncore.lock, irqflags)
+
+#define __gen4_write(x) \
+static void \
+gen4_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) { \
+       REG_WRITE_HEADER; \
+       __raw_i915_write##x(dev_priv, reg, val); \
        spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); \
-       trace_i915_reg_rw(false, reg, val, sizeof(val), trace); \
-       return val; \
 }
 
-__i915_read(8)
-__i915_read(16)
-__i915_read(32)
-__i915_read(64)
-#undef __i915_read
+#define __gen5_write(x) \
+static void \
+gen5_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) { \
+       REG_WRITE_HEADER; \
+       ilk_dummy_write(dev_priv); \
+       __raw_i915_write##x(dev_priv, reg, val); \
+       spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); \
+}
 
-#define __i915_write(x) \
-void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val, bool trace) { \
-       unsigned long irqflags; \
+#define __gen6_write(x) \
+static void \
+gen6_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) { \
        u32 __fifo_ret = 0; \
-       trace_i915_reg_rw(true, reg, val, sizeof(val), trace); \
-       spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); \
+       REG_WRITE_HEADER; \
+       if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
+               __fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
+       } \
+       __raw_i915_write##x(dev_priv, reg, val); \
+       if (unlikely(__fifo_ret)) { \
+               gen6_gt_check_fifodbg(dev_priv); \
+       } \
+       spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); \
+}
+
+#define __hsw_write(x) \
+static void \
+hsw_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) { \
+       u32 __fifo_ret = 0; \
+       REG_WRITE_HEADER; \
        if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
                __fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
        } \
-       if (dev_priv->info->gen == 5) \
-               ilk_dummy_write(dev_priv); \
        hsw_unclaimed_reg_clear(dev_priv, reg); \
        __raw_i915_write##x(dev_priv, reg, val); \
        if (unlikely(__fifo_ret)) { \
@@ -393,11 +459,185 @@ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val, bool tr
        hsw_unclaimed_reg_check(dev_priv, reg); \
        spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); \
 }
-__i915_write(8)
-__i915_write(16)
-__i915_write(32)
-__i915_write(64)
-#undef __i915_write
+
+static const u32 gen8_shadowed_regs[] = {
+       FORCEWAKE_MT,
+       GEN6_RPNSWREQ,
+       GEN6_RC_VIDEO_FREQ,
+       RING_TAIL(RENDER_RING_BASE),
+       RING_TAIL(GEN6_BSD_RING_BASE),
+       RING_TAIL(VEBOX_RING_BASE),
+       RING_TAIL(BLT_RING_BASE),
+       /* TODO: Other registers are not yet used */
+};
+
+static bool is_gen8_shadowed(struct drm_i915_private *dev_priv, u32 reg)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(gen8_shadowed_regs); i++)
+               if (reg == gen8_shadowed_regs[i])
+                       return true;
+
+       return false;
+}
+
+#define __gen8_write(x) \
+static void \
+gen8_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) { \
+       bool __needs_put = !is_gen8_shadowed(dev_priv, reg); \
+       REG_WRITE_HEADER; \
+       if (__needs_put) { \
+               dev_priv->uncore.funcs.force_wake_get(dev_priv); \
+       } \
+       __raw_i915_write##x(dev_priv, reg, val); \
+       if (__needs_put) { \
+               dev_priv->uncore.funcs.force_wake_put(dev_priv); \
+       } \
+       spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); \
+}
+
+__gen8_write(8)
+__gen8_write(16)
+__gen8_write(32)
+__gen8_write(64)
+__hsw_write(8)
+__hsw_write(16)
+__hsw_write(32)
+__hsw_write(64)
+__gen6_write(8)
+__gen6_write(16)
+__gen6_write(32)
+__gen6_write(64)
+__gen5_write(8)
+__gen5_write(16)
+__gen5_write(32)
+__gen5_write(64)
+__gen4_write(8)
+__gen4_write(16)
+__gen4_write(32)
+__gen4_write(64)
+
+#undef __gen8_write
+#undef __hsw_write
+#undef __gen6_write
+#undef __gen5_write
+#undef __gen4_write
+#undef REG_WRITE_HEADER
+
+void intel_uncore_init(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       INIT_DELAYED_WORK(&dev_priv->uncore.force_wake_work,
+                         gen6_force_wake_work);
+
+       if (IS_VALLEYVIEW(dev)) {
+               dev_priv->uncore.funcs.force_wake_get = vlv_force_wake_get;
+               dev_priv->uncore.funcs.force_wake_put = vlv_force_wake_put;
+       } else if (IS_HASWELL(dev) || IS_GEN8(dev)) {
+               dev_priv->uncore.funcs.force_wake_get = __gen6_gt_force_wake_mt_get;
+               dev_priv->uncore.funcs.force_wake_put = __gen6_gt_force_wake_mt_put;
+       } else if (IS_IVYBRIDGE(dev)) {
+               u32 ecobus;
+
+               /* IVB configs may use multi-threaded forcewake */
+
+               /* A small trick here - if the bios hasn't configured
+                * MT forcewake, and if the device is in RC6, then
+                * force_wake_mt_get will not wake the device and the
+                * ECOBUS read will return zero. Which will be
+                * (correctly) interpreted by the test below as MT
+                * forcewake being disabled.
+                */
+               mutex_lock(&dev->struct_mutex);
+               __gen6_gt_force_wake_mt_get(dev_priv);
+               ecobus = __raw_i915_read32(dev_priv, ECOBUS);
+               __gen6_gt_force_wake_mt_put(dev_priv);
+               mutex_unlock(&dev->struct_mutex);
+
+               if (ecobus & FORCEWAKE_MT_ENABLE) {
+                       dev_priv->uncore.funcs.force_wake_get =
+                               __gen6_gt_force_wake_mt_get;
+                       dev_priv->uncore.funcs.force_wake_put =
+                               __gen6_gt_force_wake_mt_put;
+               } else {
+                       DRM_INFO("No MT forcewake available on Ivybridge, this can result in issues\n");
+                       DRM_INFO("when using vblank-synced partial screen updates.\n");
+                       dev_priv->uncore.funcs.force_wake_get =
+                               __gen6_gt_force_wake_get;
+                       dev_priv->uncore.funcs.force_wake_put =
+                               __gen6_gt_force_wake_put;
+               }
+       } else if (IS_GEN6(dev)) {
+               dev_priv->uncore.funcs.force_wake_get =
+                       __gen6_gt_force_wake_get;
+               dev_priv->uncore.funcs.force_wake_put =
+                       __gen6_gt_force_wake_put;
+       }
+
+       switch (INTEL_INFO(dev)->gen) {
+       default:
+               dev_priv->uncore.funcs.mmio_writeb  = gen8_write8;
+               dev_priv->uncore.funcs.mmio_writew  = gen8_write16;
+               dev_priv->uncore.funcs.mmio_writel  = gen8_write32;
+               dev_priv->uncore.funcs.mmio_writeq  = gen8_write64;
+               dev_priv->uncore.funcs.mmio_readb  = gen6_read8;
+               dev_priv->uncore.funcs.mmio_readw  = gen6_read16;
+               dev_priv->uncore.funcs.mmio_readl  = gen6_read32;
+               dev_priv->uncore.funcs.mmio_readq  = gen6_read64;
+               break;
+       case 7:
+       case 6:
+               if (IS_HASWELL(dev)) {
+                       dev_priv->uncore.funcs.mmio_writeb  = hsw_write8;
+                       dev_priv->uncore.funcs.mmio_writew  = hsw_write16;
+                       dev_priv->uncore.funcs.mmio_writel  = hsw_write32;
+                       dev_priv->uncore.funcs.mmio_writeq  = hsw_write64;
+               } else {
+                       dev_priv->uncore.funcs.mmio_writeb  = gen6_write8;
+                       dev_priv->uncore.funcs.mmio_writew  = gen6_write16;
+                       dev_priv->uncore.funcs.mmio_writel  = gen6_write32;
+                       dev_priv->uncore.funcs.mmio_writeq  = gen6_write64;
+               }
+               dev_priv->uncore.funcs.mmio_readb  = gen6_read8;
+               dev_priv->uncore.funcs.mmio_readw  = gen6_read16;
+               dev_priv->uncore.funcs.mmio_readl  = gen6_read32;
+               dev_priv->uncore.funcs.mmio_readq  = gen6_read64;
+               break;
+       case 5:
+               dev_priv->uncore.funcs.mmio_writeb  = gen5_write8;
+               dev_priv->uncore.funcs.mmio_writew  = gen5_write16;
+               dev_priv->uncore.funcs.mmio_writel  = gen5_write32;
+               dev_priv->uncore.funcs.mmio_writeq  = gen5_write64;
+               dev_priv->uncore.funcs.mmio_readb  = gen5_read8;
+               dev_priv->uncore.funcs.mmio_readw  = gen5_read16;
+               dev_priv->uncore.funcs.mmio_readl  = gen5_read32;
+               dev_priv->uncore.funcs.mmio_readq  = gen5_read64;
+               break;
+       case 4:
+       case 3:
+       case 2:
+               dev_priv->uncore.funcs.mmio_writeb  = gen4_write8;
+               dev_priv->uncore.funcs.mmio_writew  = gen4_write16;
+               dev_priv->uncore.funcs.mmio_writel  = gen4_write32;
+               dev_priv->uncore.funcs.mmio_writeq  = gen4_write64;
+               dev_priv->uncore.funcs.mmio_readb  = gen4_read8;
+               dev_priv->uncore.funcs.mmio_readw  = gen4_read16;
+               dev_priv->uncore.funcs.mmio_readl  = gen4_read32;
+               dev_priv->uncore.funcs.mmio_readq  = gen4_read64;
+               break;
+       }
+}
+
+void intel_uncore_fini(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       flush_delayed_work(&dev_priv->uncore.force_wake_work);
+
+       /* Paranoia: make sure we have disabled everything before we exit. */
+       intel_uncore_sanitize(dev);
+}
 
 static const struct register_whitelist {
        uint64_t offset;
@@ -445,36 +685,6 @@ int i915_reg_read_ioctl(struct drm_device *dev,
        return 0;
 }
 
-static int i8xx_do_reset(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       if (IS_I85X(dev))
-               return -ENODEV;
-
-       I915_WRITE(D_STATE, I915_READ(D_STATE) | DSTATE_GFX_RESET_I830);
-       POSTING_READ(D_STATE);
-
-       if (IS_I830(dev) || IS_845G(dev)) {
-               I915_WRITE(DEBUG_RESET_I830,
-                          DEBUG_RESET_DISPLAY |
-                          DEBUG_RESET_RENDER |
-                          DEBUG_RESET_FULL);
-               POSTING_READ(DEBUG_RESET_I830);
-               msleep(1);
-
-               I915_WRITE(DEBUG_RESET_I830, 0);
-               POSTING_READ(DEBUG_RESET_I830);
-       }
-
-       msleep(1);
-
-       I915_WRITE(D_STATE, I915_READ(D_STATE) & ~DSTATE_GFX_RESET_I830);
-       POSTING_READ(D_STATE);
-
-       return 0;
-}
-
 static int i965_reset_complete(struct drm_device *dev)
 {
        u8 gdrst;
@@ -576,7 +786,6 @@ int intel_gpu_reset(struct drm_device *dev)
        case 6: return gen6_do_reset(dev);
        case 5: return ironlake_do_reset(dev);
        case 4: return i965_do_reset(dev);
-       case 2: return i8xx_do_reset(dev);
        default: return -ENODEV;
        }
 }
index cc3166d..087db33 100644 (file)
@@ -406,11 +406,6 @@ int mga_driver_load(struct drm_device *dev, unsigned long flags)
        dev_priv->mmio_base = pci_resource_start(dev->pdev, 1);
        dev_priv->mmio_size = pci_resource_len(dev->pdev, 1);
 
-       dev->counters += 3;
-       dev->types[6] = _DRM_STAT_IRQ;
-       dev->types[7] = _DRM_STAT_PRIMARY;
-       dev->types[8] = _DRM_STAT_SECONDARY;
-
        ret = drm_vblank_init(dev, 1);
 
        if (ret) {
index 598c281..2b0ceb8 100644 (file)
@@ -169,5 +169,5 @@ void mga_driver_irq_uninstall(struct drm_device *dev)
        /* Disable *all* interrupts */
        MGA_WRITE(MGA_IEN, 0);
 
-       dev->irq_enabled = 0;
+       dev->irq_enabled = false;
 }
index b487cde..3a1c5fb 100644 (file)
@@ -5,6 +5,7 @@ config DRM_MGAG200
        select FB_SYS_COPYAREA
        select FB_SYS_IMAGEBLIT
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select DRM_TTM
        help
         This is a KMS driver for the MGA G200 server chips, it
index fcce7b2..f15ea3c 100644 (file)
@@ -99,7 +99,6 @@ static struct drm_driver driver = {
        .minor = DRIVER_MINOR,
        .patchlevel = DRIVER_PATCHLEVEL,
 
-       .gem_init_object = mgag200_gem_init_object,
        .gem_free_object = mgag200_gem_free_object,
        .dumb_create = mgag200_dumb_create,
        .dumb_map_offset = mgag200_dumb_mmap_offset,
index baaae19..cf11ee6 100644 (file)
@@ -260,7 +260,6 @@ int mgag200_driver_unload(struct drm_device *dev);
 int mgag200_gem_create(struct drm_device *dev,
                   u32 size, bool iskernel,
                       struct drm_gem_object **obj);
-int mgag200_gem_init_object(struct drm_gem_object *obj);
 int mgag200_dumb_create(struct drm_file *file,
                        struct drm_device *dev,
                        struct drm_mode_create_dumb *args);
index 0f8b861..b1120cb 100644 (file)
@@ -310,12 +310,6 @@ int mgag200_dumb_create(struct drm_file *file,
        return 0;
 }
 
-int mgag200_gem_init_object(struct drm_gem_object *obj)
-{
-       BUG();
-       return 0;
-}
-
 void mgag200_bo_unref(struct mgag200_bo **bo)
 {
        struct ttm_buffer_object *tbo;
index 503a414..ee6ed63 100644 (file)
@@ -765,8 +765,6 @@ static int mga_crtc_do_set_base(struct drm_crtc *crtc,
        }
        mgag200_bo_unreserve(bo);
 
-       DRM_INFO("mga base %llx\n", gpu_addr);
-
        mga_set_start_address(crtc, (u32)gpu_addr);
 
        return 0;
index a06c19c..f39ab75 100644 (file)
@@ -14,6 +14,7 @@ config DRM_MSM
 config DRM_MSM_FBDEV
        bool "Enable legacy fbdev support for MSM modesetting driver"
        depends on DRM_MSM
+       select DRM_KMS_FB_HELPER
        select FB_SYS_FILLRECT
        select FB_SYS_COPYAREA
        select FB_SYS_IMAGEBLIT
index e179148..e5fa12b 100644 (file)
@@ -21,6 +21,7 @@ msm-y := \
        msm_drv.o \
        msm_fb.o \
        msm_gem.o \
+       msm_gem_prime.o \
        msm_gem_submit.o \
        msm_gpu.o \
        msm_ringbuffer.o
index 3546386..9588098 100644 (file)
@@ -4,16 +4,16 @@
 /* Autogenerated file, DO NOT EDIT manually!
 
 This file was generated by the rules-ng-ng headergen tool in this git repository:
-http://0x04.net/cgit/index.cgi/rules-ng-ng
-git clone git://0x04.net/rules-ng-ng
+http://github.com/freedreno/envytools/
+git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/adreno.xml              (    327 bytes, from 2013-07-05 19:21:12)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  30005 bytes, from 2013-07-19 21:30:48)
+- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  31003 bytes, from 2013-09-19 18:50:16)
 - /home/robclark/src/freedreno/envytools/rnndb/adreno_common.xml       (   8983 bytes, from 2013-07-24 01:38:36)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9712 bytes, from 2013-05-26 15:22:37)
-- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51415 bytes, from 2013-08-03 14:26:05)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9759 bytes, from 2013-09-10 00:52:33)
+- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51983 bytes, from 2013-09-10 00:52:32)
 
 Copyright (C) 2013 by the following authors:
 - Rob Clark <robdclark@gmail.com> (robclark)
@@ -317,6 +317,38 @@ static inline uint32_t A2XX_RBBM_STATUS_CMDFIFO_AVAIL(uint32_t val)
 #define A2XX_RBBM_STATUS_RB_CNTX_BUSY                          0x40000000
 #define A2XX_RBBM_STATUS_GUI_ACTIVE                            0x80000000
 
+#define REG_A2XX_MH_ARBITER_CONFIG                             0x00000a40
+#define A2XX_MH_ARBITER_CONFIG_SAME_PAGE_LIMIT__MASK           0x0000003f
+#define A2XX_MH_ARBITER_CONFIG_SAME_PAGE_LIMIT__SHIFT          0
+static inline uint32_t A2XX_MH_ARBITER_CONFIG_SAME_PAGE_LIMIT(uint32_t val)
+{
+       return ((val) << A2XX_MH_ARBITER_CONFIG_SAME_PAGE_LIMIT__SHIFT) & A2XX_MH_ARBITER_CONFIG_SAME_PAGE_LIMIT__MASK;
+}
+#define A2XX_MH_ARBITER_CONFIG_SAME_PAGE_GRANULARITY           0x00000040
+#define A2XX_MH_ARBITER_CONFIG_L1_ARB_ENABLE                   0x00000080
+#define A2XX_MH_ARBITER_CONFIG_L1_ARB_HOLD_ENABLE              0x00000100
+#define A2XX_MH_ARBITER_CONFIG_L2_ARB_CONTROL                  0x00000200
+#define A2XX_MH_ARBITER_CONFIG_PAGE_SIZE__MASK                 0x00001c00
+#define A2XX_MH_ARBITER_CONFIG_PAGE_SIZE__SHIFT                        10
+static inline uint32_t A2XX_MH_ARBITER_CONFIG_PAGE_SIZE(uint32_t val)
+{
+       return ((val) << A2XX_MH_ARBITER_CONFIG_PAGE_SIZE__SHIFT) & A2XX_MH_ARBITER_CONFIG_PAGE_SIZE__MASK;
+}
+#define A2XX_MH_ARBITER_CONFIG_TC_REORDER_ENABLE               0x00002000
+#define A2XX_MH_ARBITER_CONFIG_TC_ARB_HOLD_ENABLE              0x00004000
+#define A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT_ENABLE          0x00008000
+#define A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT__MASK           0x003f0000
+#define A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT__SHIFT          16
+static inline uint32_t A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT(uint32_t val)
+{
+       return ((val) << A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT__SHIFT) & A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT__MASK;
+}
+#define A2XX_MH_ARBITER_CONFIG_CP_CLNT_ENABLE                  0x00400000
+#define A2XX_MH_ARBITER_CONFIG_VGT_CLNT_ENABLE                 0x00800000
+#define A2XX_MH_ARBITER_CONFIG_TC_CLNT_ENABLE                  0x01000000
+#define A2XX_MH_ARBITER_CONFIG_RB_CLNT_ENABLE                  0x02000000
+#define A2XX_MH_ARBITER_CONFIG_PA_CLNT_ENABLE                  0x04000000
+
 #define REG_A2XX_A220_VSC_BIN_SIZE                             0x00000c01
 #define A2XX_A220_VSC_BIN_SIZE_WIDTH__MASK                     0x0000001f
 #define A2XX_A220_VSC_BIN_SIZE_WIDTH__SHIFT                    0
index d183516..d4afdf6 100644 (file)
@@ -4,16 +4,16 @@
 /* Autogenerated file, DO NOT EDIT manually!
 
 This file was generated by the rules-ng-ng headergen tool in this git repository:
-http://0x04.net/cgit/index.cgi/rules-ng-ng
-git clone git://0x04.net/rules-ng-ng
+http://github.com/freedreno/envytools/
+git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/adreno.xml              (    327 bytes, from 2013-07-05 19:21:12)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  30005 bytes, from 2013-07-19 21:30:48)
+- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  31003 bytes, from 2013-09-19 18:50:16)
 - /home/robclark/src/freedreno/envytools/rnndb/adreno_common.xml       (   8983 bytes, from 2013-07-24 01:38:36)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9712 bytes, from 2013-05-26 15:22:37)
-- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51415 bytes, from 2013-08-03 14:26:05)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9759 bytes, from 2013-09-10 00:52:33)
+- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51983 bytes, from 2013-09-10 00:52:32)
 
 Copyright (C) 2013 by the following authors:
 - Rob Clark <robdclark@gmail.com> (robclark)
@@ -637,11 +637,12 @@ static inline uint32_t A3XX_GRAS_SU_POLY_OFFSET_OFFSET(float val)
 #define REG_A3XX_GRAS_SU_MODE_CONTROL                          0x00002070
 #define A3XX_GRAS_SU_MODE_CONTROL_CULL_FRONT                   0x00000001
 #define A3XX_GRAS_SU_MODE_CONTROL_CULL_BACK                    0x00000002
-#define A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__MASK          0x000007fc
-#define A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__SHIFT         2
-static inline uint32_t A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH(uint32_t val)
+#define A3XX_GRAS_SU_MODE_CONTROL_FRONT_CW                     0x00000004
+#define A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__MASK          0x000007f8
+#define A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__SHIFT         3
+static inline uint32_t A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH(float val)
 {
-       return ((val) << A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__SHIFT) & A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__MASK;
+       return ((((uint32_t)(val * 4.0))) << A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__SHIFT) & A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__MASK;
 }
 #define A3XX_GRAS_SU_MODE_CONTROL_POLY_OFFSET                  0x00000800
 
@@ -745,6 +746,7 @@ static inline uint32_t A3XX_RB_RENDER_CONTROL_BIN_WIDTH(uint32_t val)
 }
 #define A3XX_RB_RENDER_CONTROL_DISABLE_COLOR_PIPE              0x00001000
 #define A3XX_RB_RENDER_CONTROL_ENABLE_GMEM                     0x00002000
+#define A3XX_RB_RENDER_CONTROL_ALPHA_TEST                      0x00400000
 #define A3XX_RB_RENDER_CONTROL_ALPHA_TEST_FUNC__MASK           0x07000000
 #define A3XX_RB_RENDER_CONTROL_ALPHA_TEST_FUNC__SHIFT          24
 static inline uint32_t A3XX_RB_RENDER_CONTROL_ALPHA_TEST_FUNC(enum adreno_compare_func val)
@@ -767,7 +769,19 @@ static inline uint32_t A3XX_RB_MSAA_CONTROL_SAMPLE_MASK(uint32_t val)
        return ((val) << A3XX_RB_MSAA_CONTROL_SAMPLE_MASK__SHIFT) & A3XX_RB_MSAA_CONTROL_SAMPLE_MASK__MASK;
 }
 
-#define REG_A3XX_UNKNOWN_20C3                                  0x000020c3
+#define REG_A3XX_RB_ALPHA_REF                                  0x000020c3
+#define A3XX_RB_ALPHA_REF_UINT__MASK                           0x0000ff00
+#define A3XX_RB_ALPHA_REF_UINT__SHIFT                          8
+static inline uint32_t A3XX_RB_ALPHA_REF_UINT(uint32_t val)
+{
+       return ((val) << A3XX_RB_ALPHA_REF_UINT__SHIFT) & A3XX_RB_ALPHA_REF_UINT__MASK;
+}
+#define A3XX_RB_ALPHA_REF_FLOAT__MASK                          0xffff0000
+#define A3XX_RB_ALPHA_REF_FLOAT__SHIFT                         16
+static inline uint32_t A3XX_RB_ALPHA_REF_FLOAT(float val)
+{
+       return ((util_float_to_half(val)) << A3XX_RB_ALPHA_REF_FLOAT__SHIFT) & A3XX_RB_ALPHA_REF_FLOAT__MASK;
+}
 
 static inline uint32_t REG_A3XX_RB_MRT(uint32_t i0) { return 0x000020c4 + 0x4*i0; }
 
@@ -1002,7 +1016,7 @@ static inline uint32_t A3XX_RB_COPY_DEST_INFO_ENDIAN(enum adreno_rb_surface_endi
 #define REG_A3XX_RB_DEPTH_CONTROL                              0x00002100
 #define A3XX_RB_DEPTH_CONTROL_Z_ENABLE                         0x00000002
 #define A3XX_RB_DEPTH_CONTROL_Z_WRITE_ENABLE                   0x00000004
-#define A3XX_RB_DEPTH_CONTROL_EARLY_Z_ENABLE                   0x00000008
+#define A3XX_RB_DEPTH_CONTROL_EARLY_Z_DISABLE                  0x00000008
 #define A3XX_RB_DEPTH_CONTROL_ZFUNC__MASK                      0x00000070
 #define A3XX_RB_DEPTH_CONTROL_ZFUNC__SHIFT                     4
 static inline uint32_t A3XX_RB_DEPTH_CONTROL_ZFUNC(enum adreno_compare_func val)
@@ -1038,7 +1052,8 @@ static inline uint32_t A3XX_RB_DEPTH_PITCH(uint32_t val)
 
 #define REG_A3XX_RB_STENCIL_CONTROL                            0x00002104
 #define A3XX_RB_STENCIL_CONTROL_STENCIL_ENABLE                 0x00000001
-#define A3XX_RB_STENCIL_CONTROL_STENCIL_ENABLE_BF              0x00000004
+#define A3XX_RB_STENCIL_CONTROL_STENCIL_ENABLE_BF              0x00000002
+#define A3XX_RB_STENCIL_CONTROL_STENCIL_READ                   0x00000004
 #define A3XX_RB_STENCIL_CONTROL_FUNC__MASK                     0x00000700
 #define A3XX_RB_STENCIL_CONTROL_FUNC__SHIFT                    8
 static inline uint32_t A3XX_RB_STENCIL_CONTROL_FUNC(enum adreno_compare_func val)
@@ -2074,6 +2089,7 @@ static inline uint32_t A3XX_UCHE_CACHE_INVALIDATE1_REG_OPCODE(enum a3xx_cache_op
 #define REG_A3XX_TP_PERFCOUNTER5_SELECT                                0x00000f09
 
 #define REG_A3XX_TEX_SAMP_0                                    0x00000000
+#define A3XX_TEX_SAMP_0_MIPFILTER_LINEAR                       0x00000002
 #define A3XX_TEX_SAMP_0_XY_MAG__MASK                           0x0000000c
 #define A3XX_TEX_SAMP_0_XY_MAG__SHIFT                          2
 static inline uint32_t A3XX_TEX_SAMP_0_XY_MAG(enum a3xx_tex_filter val)
@@ -2134,6 +2150,12 @@ static inline uint32_t A3XX_TEX_CONST_0_SWIZ_W(enum a3xx_tex_swiz val)
 {
        return ((val) << A3XX_TEX_CONST_0_SWIZ_W__SHIFT) & A3XX_TEX_CONST_0_SWIZ_W__MASK;
 }
+#define A3XX_TEX_CONST_0_MIPLVLS__MASK                         0x000f0000
+#define A3XX_TEX_CONST_0_MIPLVLS__SHIFT                                16
+static inline uint32_t A3XX_TEX_CONST_0_MIPLVLS(uint32_t val)
+{
+       return ((val) << A3XX_TEX_CONST_0_MIPLVLS__SHIFT) & A3XX_TEX_CONST_0_MIPLVLS__MASK;
+}
 #define A3XX_TEX_CONST_0_FMT__MASK                             0x1fc00000
 #define A3XX_TEX_CONST_0_FMT__SHIFT                            22
 static inline uint32_t A3XX_TEX_CONST_0_FMT(enum a3xx_tex_fmt val)
index 61979d4..33dcc60 100644 (file)
@@ -4,16 +4,16 @@
 /* Autogenerated file, DO NOT EDIT manually!
 
 This file was generated by the rules-ng-ng headergen tool in this git repository:
-http://0x04.net/cgit/index.cgi/rules-ng-ng
-git clone git://0x04.net/rules-ng-ng
+http://github.com/freedreno/envytools/
+git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/adreno.xml              (    327 bytes, from 2013-07-05 19:21:12)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  30005 bytes, from 2013-07-19 21:30:48)
+- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  31003 bytes, from 2013-09-19 18:50:16)
 - /home/robclark/src/freedreno/envytools/rnndb/adreno_common.xml       (   8983 bytes, from 2013-07-24 01:38:36)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9712 bytes, from 2013-05-26 15:22:37)
-- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51415 bytes, from 2013-08-03 14:26:05)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9759 bytes, from 2013-09-10 00:52:33)
+- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51983 bytes, from 2013-09-10 00:52:32)
 
 Copyright (C) 2013 by the following authors:
 - Rob Clark <robdclark@gmail.com> (robclark)
index 94c13f4..259ad70 100644 (file)
@@ -4,16 +4,16 @@
 /* Autogenerated file, DO NOT EDIT manually!
 
 This file was generated by the rules-ng-ng headergen tool in this git repository:
-http://0x04.net/cgit/index.cgi/rules-ng-ng
-git clone git://0x04.net/rules-ng-ng
+http://github.com/freedreno/envytools/
+git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/adreno.xml              (    327 bytes, from 2013-07-05 19:21:12)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  30005 bytes, from 2013-07-19 21:30:48)
+- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  31003 bytes, from 2013-09-19 18:50:16)
 - /home/robclark/src/freedreno/envytools/rnndb/adreno_common.xml       (   8983 bytes, from 2013-07-24 01:38:36)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9712 bytes, from 2013-05-26 15:22:37)
-- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51415 bytes, from 2013-08-03 14:26:05)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9759 bytes, from 2013-09-10 00:52:33)
+- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51983 bytes, from 2013-09-10 00:52:32)
 
 Copyright (C) 2013 by the following authors:
 - Rob Clark <robdclark@gmail.com> (robclark)
index 6f8396b..6d4c62b 100644 (file)
@@ -4,13 +4,13 @@
 /* Autogenerated file, DO NOT EDIT manually!
 
 This file was generated by the rules-ng-ng headergen tool in this git repository:
-http://0x04.net/cgit/index.cgi/rules-ng-ng
-git clone git://0x04.net/rules-ng-ng
+http://github.com/freedreno/envytools/
+git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-10-07 16:36:48)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)
index aefc1b8..d1df38b 100644 (file)
@@ -4,13 +4,13 @@
 /* Autogenerated file, DO NOT EDIT manually!
 
 This file was generated by the rules-ng-ng headergen tool in this git repository:
-http://0x04.net/cgit/index.cgi/rules-ng-ng
-git clone git://0x04.net/rules-ng-ng
+http://github.com/freedreno/envytools/
+git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-10-07 16:36:48)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)
index a225e81..0030a11 100644 (file)
@@ -4,13 +4,13 @@
 /* Autogenerated file, DO NOT EDIT manually!
 
 This file was generated by the rules-ng-ng headergen tool in this git repository:
-http://0x04.net/cgit/index.cgi/rules-ng-ng
-git clone git://0x04.net/rules-ng-ng
+http://github.com/freedreno/envytools/
+git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-10-07 16:36:48)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)
index f5fa486..4e939f8 100644 (file)
@@ -4,13 +4,13 @@
 /* Autogenerated file, DO NOT EDIT manually!
 
 This file was generated by the rules-ng-ng headergen tool in this git repository:
-http://0x04.net/cgit/index.cgi/rules-ng-ng
-git clone git://0x04.net/rules-ng-ng
+http://github.com/freedreno/envytools/
+git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-10-07 16:36:48)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)
index bee3636..dbde4f6 100644 (file)
@@ -4,13 +4,13 @@
 /* Autogenerated file, DO NOT EDIT manually!
 
 This file was generated by the rules-ng-ng headergen tool in this git repository:
-http://0x04.net/cgit/index.cgi/rules-ng-ng
-git clone git://0x04.net/rules-ng-ng
+http://github.com/freedreno/envytools/
+git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-10-07 16:36:48)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)
index bbeeebe..9908ffe 100644 (file)
@@ -4,13 +4,13 @@
 /* Autogenerated file, DO NOT EDIT manually!
 
 This file was generated by the rules-ng-ng headergen tool in this git repository:
-http://0x04.net/cgit/index.cgi/rules-ng-ng
-git clone git://0x04.net/rules-ng-ng
+http://github.com/freedreno/envytools/
+git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-10-07 16:36:48)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)
@@ -42,28 +42,28 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 
-enum mpd4_bpc {
+enum mdp4_bpc {
        BPC1 = 0,
        BPC5 = 1,
        BPC6 = 2,
        BPC8 = 3,
 };
 
-enum mpd4_bpc_alpha {
+enum mdp4_bpc_alpha {
        BPC1A = 0,
        BPC4A = 1,
        BPC6A = 2,
        BPC8A = 3,
 };
 
-enum mpd4_alpha_type {
+enum mdp4_alpha_type {
        FG_CONST = 0,
        BG_CONST = 1,
        FG_PIXEL = 2,
        BG_PIXEL = 3,
 };
 
-enum mpd4_pipe {
+enum mdp4_pipe {
        VG1 = 0,
        VG2 = 1,
        RGB1 = 2,
@@ -73,13 +73,13 @@ enum mpd4_pipe {
        VG4 = 6,
 };
 
-enum mpd4_mixer {
+enum mdp4_mixer {
        MIXER0 = 0,
        MIXER1 = 1,
        MIXER2 = 2,
 };
 
-enum mpd4_mixer_stage_id {
+enum mdp4_mixer_stage_id {
        STAGE_UNUSED = 0,
        STAGE_BASE = 1,
        STAGE0 = 2,
@@ -194,56 +194,56 @@ static inline uint32_t MDP4_DISP_INTF_SEL_EXT(enum mdp4_intf val)
 #define REG_MDP4_LAYERMIXER2_IN_CFG                            0x000100f0
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE0__MASK                    0x00000007
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE0__SHIFT                   0
-static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE0(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE0(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE0__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE0__MASK;
 }
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE0_MIXER1                   0x00000008
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE1__MASK                    0x00000070
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE1__SHIFT                   4
-static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE1(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE1(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE1__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE1__MASK;
 }
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE1_MIXER1                   0x00000080
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE2__MASK                    0x00000700
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE2__SHIFT                   8
-static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE2(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE2(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE2__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE2__MASK;
 }
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE2_MIXER1                   0x00000800
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE3__MASK                    0x00007000
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE3__SHIFT                   12
-static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE3(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE3(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE3__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE3__MASK;
 }
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE3_MIXER1                   0x00008000
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE4__MASK                    0x00070000
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE4__SHIFT                   16
-static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE4(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE4(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE4__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE4__MASK;
 }
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE4_MIXER1                   0x00080000
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE5__MASK                    0x00700000
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE5__SHIFT                   20
-static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE5(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE5(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE5__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE5__MASK;
 }
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE5_MIXER1                   0x00800000
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE6__MASK                    0x07000000
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE6__SHIFT                   24
-static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE6(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE6(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE6__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE6__MASK;
 }
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE6_MIXER1                   0x08000000
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE7__MASK                    0x70000000
 #define MDP4_LAYERMIXER2_IN_CFG_PIPE7__SHIFT                   28
-static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE7(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE7(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE7__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE7__MASK;
 }
@@ -254,56 +254,56 @@ static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE7(enum mpd4_mixer_stage_id va
 #define REG_MDP4_LAYERMIXER_IN_CFG                             0x00010100
 #define MDP4_LAYERMIXER_IN_CFG_PIPE0__MASK                     0x00000007
 #define MDP4_LAYERMIXER_IN_CFG_PIPE0__SHIFT                    0
-static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE0(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE0(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE0__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE0__MASK;
 }
 #define MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1                    0x00000008
 #define MDP4_LAYERMIXER_IN_CFG_PIPE1__MASK                     0x00000070
 #define MDP4_LAYERMIXER_IN_CFG_PIPE1__SHIFT                    4
-static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE1(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE1(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE1__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE1__MASK;
 }
 #define MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1                    0x00000080
 #define MDP4_LAYERMIXER_IN_CFG_PIPE2__MASK                     0x00000700
 #define MDP4_LAYERMIXER_IN_CFG_PIPE2__SHIFT                    8
-static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE2(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE2(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE2__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE2__MASK;
 }
 #define MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1                    0x00000800
 #define MDP4_LAYERMIXER_IN_CFG_PIPE3__MASK                     0x00007000
 #define MDP4_LAYERMIXER_IN_CFG_PIPE3__SHIFT                    12
-static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE3(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE3(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE3__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE3__MASK;
 }
 #define MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1                    0x00008000
 #define MDP4_LAYERMIXER_IN_CFG_PIPE4__MASK                     0x00070000
 #define MDP4_LAYERMIXER_IN_CFG_PIPE4__SHIFT                    16
-static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE4(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE4(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE4__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE4__MASK;
 }
 #define MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1                    0x00080000
 #define MDP4_LAYERMIXER_IN_CFG_PIPE5__MASK                     0x00700000
 #define MDP4_LAYERMIXER_IN_CFG_PIPE5__SHIFT                    20
-static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE5(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE5(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE5__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE5__MASK;
 }
 #define MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1                    0x00800000
 #define MDP4_LAYERMIXER_IN_CFG_PIPE6__MASK                     0x07000000
 #define MDP4_LAYERMIXER_IN_CFG_PIPE6__SHIFT                    24
-static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE6(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE6(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE6__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE6__MASK;
 }
 #define MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1                    0x08000000
 #define MDP4_LAYERMIXER_IN_CFG_PIPE7__MASK                     0x70000000
 #define MDP4_LAYERMIXER_IN_CFG_PIPE7__SHIFT                    28
-static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE7(enum mpd4_mixer_stage_id val)
+static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE7(enum mdp4_mixer_stage_id val)
 {
        return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE7__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE7__MASK;
 }
@@ -369,7 +369,7 @@ static inline uint32_t REG_MDP4_OVLP_STAGE(uint32_t i0, uint32_t i1) { return 0x
 static inline uint32_t REG_MDP4_OVLP_STAGE_OP(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_OVLP(i0) + __offset_STAGE(i1); }
 #define MDP4_OVLP_STAGE_OP_FG_ALPHA__MASK                      0x00000003
 #define MDP4_OVLP_STAGE_OP_FG_ALPHA__SHIFT                     0
-static inline uint32_t MDP4_OVLP_STAGE_OP_FG_ALPHA(enum mpd4_alpha_type val)
+static inline uint32_t MDP4_OVLP_STAGE_OP_FG_ALPHA(enum mdp4_alpha_type val)
 {
        return ((val) << MDP4_OVLP_STAGE_OP_FG_ALPHA__SHIFT) & MDP4_OVLP_STAGE_OP_FG_ALPHA__MASK;
 }
@@ -377,7 +377,7 @@ static inline uint32_t MDP4_OVLP_STAGE_OP_FG_ALPHA(enum mpd4_alpha_type val)
 #define MDP4_OVLP_STAGE_OP_FG_MOD_ALPHA                                0x00000008
 #define MDP4_OVLP_STAGE_OP_BG_ALPHA__MASK                      0x00000030
 #define MDP4_OVLP_STAGE_OP_BG_ALPHA__SHIFT                     4
-static inline uint32_t MDP4_OVLP_STAGE_OP_BG_ALPHA(enum mpd4_alpha_type val)
+static inline uint32_t MDP4_OVLP_STAGE_OP_BG_ALPHA(enum mdp4_alpha_type val)
 {
        return ((val) << MDP4_OVLP_STAGE_OP_BG_ALPHA__SHIFT) & MDP4_OVLP_STAGE_OP_BG_ALPHA__MASK;
 }
@@ -472,19 +472,19 @@ static inline uint32_t REG_MDP4_DMA(enum mdp4_dma i0) { return 0x00000000 + __of
 static inline uint32_t REG_MDP4_DMA_CONFIG(enum mdp4_dma i0) { return 0x00000000 + __offset_DMA(i0); }
 #define MDP4_DMA_CONFIG_G_BPC__MASK                            0x00000003
 #define MDP4_DMA_CONFIG_G_BPC__SHIFT                           0
-static inline uint32_t MDP4_DMA_CONFIG_G_BPC(enum mpd4_bpc val)
+static inline uint32_t MDP4_DMA_CONFIG_G_BPC(enum mdp4_bpc val)
 {
        return ((val) << MDP4_DMA_CONFIG_G_BPC__SHIFT) & MDP4_DMA_CONFIG_G_BPC__MASK;
 }
 #define MDP4_DMA_CONFIG_B_BPC__MASK                            0x0000000c
 #define MDP4_DMA_CONFIG_B_BPC__SHIFT                           2
-static inline uint32_t MDP4_DMA_CONFIG_B_BPC(enum mpd4_bpc val)
+static inline uint32_t MDP4_DMA_CONFIG_B_BPC(enum mdp4_bpc val)
 {
        return ((val) << MDP4_DMA_CONFIG_B_BPC__SHIFT) & MDP4_DMA_CONFIG_B_BPC__MASK;
 }
 #define MDP4_DMA_CONFIG_R_BPC__MASK                            0x00000030
 #define MDP4_DMA_CONFIG_R_BPC__SHIFT                           4
-static inline uint32_t MDP4_DMA_CONFIG_R_BPC(enum mpd4_bpc val)
+static inline uint32_t MDP4_DMA_CONFIG_R_BPC(enum mdp4_bpc val)
 {
        return ((val) << MDP4_DMA_CONFIG_R_BPC__SHIFT) & MDP4_DMA_CONFIG_R_BPC__MASK;
 }
@@ -601,9 +601,9 @@ static inline uint32_t REG_MDP4_DMA_CSC_POST_LV(enum mdp4_dma i0, uint32_t i1) {
 
 static inline uint32_t REG_MDP4_DMA_CSC_POST_LV_VAL(enum mdp4_dma i0, uint32_t i1) { return 0x00003680 + __offset_DMA(i0) + 0x4*i1; }
 
-static inline uint32_t REG_MDP4_PIPE(enum mpd4_pipe i0) { return 0x00020000 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE(enum mdp4_pipe i0) { return 0x00020000 + 0x10000*i0; }
 
-static inline uint32_t REG_MDP4_PIPE_SRC_SIZE(enum mpd4_pipe i0) { return 0x00020000 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_SRC_SIZE(enum mdp4_pipe i0) { return 0x00020000 + 0x10000*i0; }
 #define MDP4_PIPE_SRC_SIZE_HEIGHT__MASK                                0xffff0000
 #define MDP4_PIPE_SRC_SIZE_HEIGHT__SHIFT                       16
 static inline uint32_t MDP4_PIPE_SRC_SIZE_HEIGHT(uint32_t val)
@@ -617,7 +617,7 @@ static inline uint32_t MDP4_PIPE_SRC_SIZE_WIDTH(uint32_t val)
        return ((val) << MDP4_PIPE_SRC_SIZE_WIDTH__SHIFT) & MDP4_PIPE_SRC_SIZE_WIDTH__MASK;
 }
 
-static inline uint32_t REG_MDP4_PIPE_SRC_XY(enum mpd4_pipe i0) { return 0x00020004 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_SRC_XY(enum mdp4_pipe i0) { return 0x00020004 + 0x10000*i0; }
 #define MDP4_PIPE_SRC_XY_Y__MASK                               0xffff0000
 #define MDP4_PIPE_SRC_XY_Y__SHIFT                              16
 static inline uint32_t MDP4_PIPE_SRC_XY_Y(uint32_t val)
@@ -631,7 +631,7 @@ static inline uint32_t MDP4_PIPE_SRC_XY_X(uint32_t val)
        return ((val) << MDP4_PIPE_SRC_XY_X__SHIFT) & MDP4_PIPE_SRC_XY_X__MASK;
 }
 
-static inline uint32_t REG_MDP4_PIPE_DST_SIZE(enum mpd4_pipe i0) { return 0x00020008 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_DST_SIZE(enum mdp4_pipe i0) { return 0x00020008 + 0x10000*i0; }
 #define MDP4_PIPE_DST_SIZE_HEIGHT__MASK                                0xffff0000
 #define MDP4_PIPE_DST_SIZE_HEIGHT__SHIFT                       16
 static inline uint32_t MDP4_PIPE_DST_SIZE_HEIGHT(uint32_t val)
@@ -645,7 +645,7 @@ static inline uint32_t MDP4_PIPE_DST_SIZE_WIDTH(uint32_t val)
        return ((val) << MDP4_PIPE_DST_SIZE_WIDTH__SHIFT) & MDP4_PIPE_DST_SIZE_WIDTH__MASK;
 }
 
-static inline uint32_t REG_MDP4_PIPE_DST_XY(enum mpd4_pipe i0) { return 0x0002000c + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_DST_XY(enum mdp4_pipe i0) { return 0x0002000c + 0x10000*i0; }
 #define MDP4_PIPE_DST_XY_Y__MASK                               0xffff0000
 #define MDP4_PIPE_DST_XY_Y__SHIFT                              16
 static inline uint32_t MDP4_PIPE_DST_XY_Y(uint32_t val)
@@ -659,13 +659,13 @@ static inline uint32_t MDP4_PIPE_DST_XY_X(uint32_t val)
        return ((val) << MDP4_PIPE_DST_XY_X__SHIFT) & MDP4_PIPE_DST_XY_X__MASK;
 }
 
-static inline uint32_t REG_MDP4_PIPE_SRCP0_BASE(enum mpd4_pipe i0) { return 0x00020010 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_SRCP0_BASE(enum mdp4_pipe i0) { return 0x00020010 + 0x10000*i0; }
 
-static inline uint32_t REG_MDP4_PIPE_SRCP1_BASE(enum mpd4_pipe i0) { return 0x00020014 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_SRCP1_BASE(enum mdp4_pipe i0) { return 0x00020014 + 0x10000*i0; }
 
-static inline uint32_t REG_MDP4_PIPE_SRCP2_BASE(enum mpd4_pipe i0) { return 0x00020018 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_SRCP2_BASE(enum mdp4_pipe i0) { return 0x00020018 + 0x10000*i0; }
 
-static inline uint32_t REG_MDP4_PIPE_SRC_STRIDE_A(enum mpd4_pipe i0) { return 0x00020040 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_SRC_STRIDE_A(enum mdp4_pipe i0) { return 0x00020040 + 0x10000*i0; }
 #define MDP4_PIPE_SRC_STRIDE_A_P0__MASK                                0x0000ffff
 #define MDP4_PIPE_SRC_STRIDE_A_P0__SHIFT                       0
 static inline uint32_t MDP4_PIPE_SRC_STRIDE_A_P0(uint32_t val)
@@ -679,7 +679,7 @@ static inline uint32_t MDP4_PIPE_SRC_STRIDE_A_P1(uint32_t val)
        return ((val) << MDP4_PIPE_SRC_STRIDE_A_P1__SHIFT) & MDP4_PIPE_SRC_STRIDE_A_P1__MASK;
 }
 
-static inline uint32_t REG_MDP4_PIPE_SRC_STRIDE_B(enum mpd4_pipe i0) { return 0x00020044 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_SRC_STRIDE_B(enum mdp4_pipe i0) { return 0x00020044 + 0x10000*i0; }
 #define MDP4_PIPE_SRC_STRIDE_B_P2__MASK                                0x0000ffff
 #define MDP4_PIPE_SRC_STRIDE_B_P2__SHIFT                       0
 static inline uint32_t MDP4_PIPE_SRC_STRIDE_B_P2(uint32_t val)
@@ -693,7 +693,7 @@ static inline uint32_t MDP4_PIPE_SRC_STRIDE_B_P3(uint32_t val)
        return ((val) << MDP4_PIPE_SRC_STRIDE_B_P3__SHIFT) & MDP4_PIPE_SRC_STRIDE_B_P3__MASK;
 }
 
-static inline uint32_t REG_MDP4_PIPE_FRAME_SIZE(enum mpd4_pipe i0) { return 0x00020048 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_FRAME_SIZE(enum mdp4_pipe i0) { return 0x00020048 + 0x10000*i0; }
 #define MDP4_PIPE_FRAME_SIZE_HEIGHT__MASK                      0xffff0000
 #define MDP4_PIPE_FRAME_SIZE_HEIGHT__SHIFT                     16
 static inline uint32_t MDP4_PIPE_FRAME_SIZE_HEIGHT(uint32_t val)
@@ -707,28 +707,28 @@ static inline uint32_t MDP4_PIPE_FRAME_SIZE_WIDTH(uint32_t val)
        return ((val) << MDP4_PIPE_FRAME_SIZE_WIDTH__SHIFT) & MDP4_PIPE_FRAME_SIZE_WIDTH__MASK;
 }
 
-static inline uint32_t REG_MDP4_PIPE_SRC_FORMAT(enum mpd4_pipe i0) { return 0x00020050 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_SRC_FORMAT(enum mdp4_pipe i0) { return 0x00020050 + 0x10000*i0; }
 #define MDP4_PIPE_SRC_FORMAT_G_BPC__MASK                       0x00000003
 #define MDP4_PIPE_SRC_FORMAT_G_BPC__SHIFT                      0
-static inline uint32_t MDP4_PIPE_SRC_FORMAT_G_BPC(enum mpd4_bpc val)
+static inline uint32_t MDP4_PIPE_SRC_FORMAT_G_BPC(enum mdp4_bpc val)
 {
        return ((val) << MDP4_PIPE_SRC_FORMAT_G_BPC__SHIFT) & MDP4_PIPE_SRC_FORMAT_G_BPC__MASK;
 }
 #define MDP4_PIPE_SRC_FORMAT_B_BPC__MASK                       0x0000000c
 #define MDP4_PIPE_SRC_FORMAT_B_BPC__SHIFT                      2
-static inline uint32_t MDP4_PIPE_SRC_FORMAT_B_BPC(enum mpd4_bpc val)
+static inline uint32_t MDP4_PIPE_SRC_FORMAT_B_BPC(enum mdp4_bpc val)
 {
        return ((val) << MDP4_PIPE_SRC_FORMAT_B_BPC__SHIFT) & MDP4_PIPE_SRC_FORMAT_B_BPC__MASK;
 }
 #define MDP4_PIPE_SRC_FORMAT_R_BPC__MASK                       0x00000030
 #define MDP4_PIPE_SRC_FORMAT_R_BPC__SHIFT                      4
-static inline uint32_t MDP4_PIPE_SRC_FORMAT_R_BPC(enum mpd4_bpc val)
+static inline uint32_t MDP4_PIPE_SRC_FORMAT_R_BPC(enum mdp4_bpc val)
 {
        return ((val) << MDP4_PIPE_SRC_FORMAT_R_BPC__SHIFT) & MDP4_PIPE_SRC_FORMAT_R_BPC__MASK;
 }
 #define MDP4_PIPE_SRC_FORMAT_A_BPC__MASK                       0x000000c0
 #define MDP4_PIPE_SRC_FORMAT_A_BPC__SHIFT                      6
-static inline uint32_t MDP4_PIPE_SRC_FORMAT_A_BPC(enum mpd4_bpc_alpha val)
+static inline uint32_t MDP4_PIPE_SRC_FORMAT_A_BPC(enum mdp4_bpc_alpha val)
 {
        return ((val) << MDP4_PIPE_SRC_FORMAT_A_BPC__SHIFT) & MDP4_PIPE_SRC_FORMAT_A_BPC__MASK;
 }
@@ -750,7 +750,7 @@ static inline uint32_t MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT(uint32_t val)
 #define MDP4_PIPE_SRC_FORMAT_UNPACK_ALIGN_MSB                  0x00040000
 #define MDP4_PIPE_SRC_FORMAT_SOLID_FILL                                0x00400000
 
-static inline uint32_t REG_MDP4_PIPE_SRC_UNPACK(enum mpd4_pipe i0) { return 0x00020054 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_SRC_UNPACK(enum mdp4_pipe i0) { return 0x00020054 + 0x10000*i0; }
 #define MDP4_PIPE_SRC_UNPACK_ELEM0__MASK                       0x000000ff
 #define MDP4_PIPE_SRC_UNPACK_ELEM0__SHIFT                      0
 static inline uint32_t MDP4_PIPE_SRC_UNPACK_ELEM0(uint32_t val)
@@ -776,7 +776,7 @@ static inline uint32_t MDP4_PIPE_SRC_UNPACK_ELEM3(uint32_t val)
        return ((val) << MDP4_PIPE_SRC_UNPACK_ELEM3__SHIFT) & MDP4_PIPE_SRC_UNPACK_ELEM3__MASK;
 }
 
-static inline uint32_t REG_MDP4_PIPE_OP_MODE(enum mpd4_pipe i0) { return 0x00020058 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_OP_MODE(enum mdp4_pipe i0) { return 0x00020058 + 0x10000*i0; }
 #define MDP4_PIPE_OP_MODE_SCALEX_EN                            0x00000001
 #define MDP4_PIPE_OP_MODE_SCALEY_EN                            0x00000002
 #define MDP4_PIPE_OP_MODE_SRC_YCBCR                            0x00000200
@@ -789,36 +789,36 @@ static inline uint32_t REG_MDP4_PIPE_OP_MODE(enum mpd4_pipe i0) { return 0x00020
 #define MDP4_PIPE_OP_MODE_DEINT_EN                             0x00040000
 #define MDP4_PIPE_OP_MODE_DEINT_ODD_REF                                0x00080000
 
-static inline uint32_t REG_MDP4_PIPE_PHASEX_STEP(enum mpd4_pipe i0) { return 0x0002005c + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_PHASEX_STEP(enum mdp4_pipe i0) { return 0x0002005c + 0x10000*i0; }
 
-static inline uint32_t REG_MDP4_PIPE_PHASEY_STEP(enum mpd4_pipe i0) { return 0x00020060 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_PHASEY_STEP(enum mdp4_pipe i0) { return 0x00020060 + 0x10000*i0; }
 
-static inline uint32_t REG_MDP4_PIPE_FETCH_CONFIG(enum mpd4_pipe i0) { return 0x00021004 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_FETCH_CONFIG(enum mdp4_pipe i0) { return 0x00021004 + 0x10000*i0; }
 
-static inline uint32_t REG_MDP4_PIPE_SOLID_COLOR(enum mpd4_pipe i0) { return 0x00021008 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_SOLID_COLOR(enum mdp4_pipe i0) { return 0x00021008 + 0x10000*i0; }
 
-static inline uint32_t REG_MDP4_PIPE_CSC(enum mpd4_pipe i0) { return 0x00024000 + 0x10000*i0; }
+static inline uint32_t REG_MDP4_PIPE_CSC(enum mdp4_pipe i0) { return 0x00024000 + 0x10000*i0; }
 
 
-static inline uint32_t REG_MDP4_PIPE_CSC_MV(enum mpd4_pipe i0, uint32_t i1) { return 0x00024400 + 0x10000*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP4_PIPE_CSC_MV(enum mdp4_pipe i0, uint32_t i1) { return 0x00024400 + 0x10000*i0 + 0x4*i1; }
 
-static inline uint32_t REG_MDP4_PIPE_CSC_MV_VAL(enum mpd4_pipe i0, uint32_t i1) { return 0x00024400 + 0x10000*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP4_PIPE_CSC_MV_VAL(enum mdp4_pipe i0, uint32_t i1) { return 0x00024400 + 0x10000*i0 + 0x4*i1; }
 
-static inline uint32_t REG_MDP4_PIPE_CSC_PRE_BV(enum mpd4_pipe i0, uint32_t i1) { return 0x00024500 + 0x10000*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP4_PIPE_CSC_PRE_BV(enum mdp4_pipe i0, uint32_t i1) { return 0x00024500 + 0x10000*i0 + 0x4*i1; }
 
-static inline uint32_t REG_MDP4_PIPE_CSC_PRE_BV_VAL(enum mpd4_pipe i0, uint32_t i1) { return 0x00024500 + 0x10000*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP4_PIPE_CSC_PRE_BV_VAL(enum mdp4_pipe i0, uint32_t i1) { return 0x00024500 + 0x10000*i0 + 0x4*i1; }
 
-static inline uint32_t REG_MDP4_PIPE_CSC_POST_BV(enum mpd4_pipe i0, uint32_t i1) { return 0x00024580 + 0x10000*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP4_PIPE_CSC_POST_BV(enum mdp4_pipe i0, uint32_t i1) { return 0x00024580 + 0x10000*i0 + 0x4*i1; }
 
-static inline uint32_t REG_MDP4_PIPE_CSC_POST_BV_VAL(enum mpd4_pipe i0, uint32_t i1) { return 0x00024580 + 0x10000*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP4_PIPE_CSC_POST_BV_VAL(enum mdp4_pipe i0, uint32_t i1) { return 0x00024580 + 0x10000*i0 + 0x4*i1; }
 
-static inline uint32_t REG_MDP4_PIPE_CSC_PRE_LV(enum mpd4_pipe i0, uint32_t i1) { return 0x00024600 + 0x10000*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP4_PIPE_CSC_PRE_LV(enum mdp4_pipe i0, uint32_t i1) { return 0x00024600 + 0x10000*i0 + 0x4*i1; }
 
-static inline uint32_t REG_MDP4_PIPE_CSC_PRE_LV_VAL(enum mpd4_pipe i0, uint32_t i1) { return 0x00024600 + 0x10000*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP4_PIPE_CSC_PRE_LV_VAL(enum mdp4_pipe i0, uint32_t i1) { return 0x00024600 + 0x10000*i0 + 0x4*i1; }
 
-static inline uint32_t REG_MDP4_PIPE_CSC_POST_LV(enum mpd4_pipe i0, uint32_t i1) { return 0x00024680 + 0x10000*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP4_PIPE_CSC_POST_LV(enum mdp4_pipe i0, uint32_t i1) { return 0x00024680 + 0x10000*i0 + 0x4*i1; }
 
-static inline uint32_t REG_MDP4_PIPE_CSC_POST_LV_VAL(enum mpd4_pipe i0, uint32_t i1) { return 0x00024680 + 0x10000*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP4_PIPE_CSC_POST_LV_VAL(enum mdp4_pipe i0, uint32_t i1) { return 0x00024680 + 0x10000*i0 + 0x4*i1; }
 
 #define REG_MDP4_LCDC                                          0x000c0000
 
index de6bea2..019d530 100644 (file)
@@ -26,6 +26,7 @@ struct mdp4_crtc {
        struct drm_crtc base;
        char name[8];
        struct drm_plane *plane;
+       struct drm_plane *planes[8];
        int id;
        int ovlp;
        enum mdp4_dma dma;
@@ -50,7 +51,11 @@ struct mdp4_crtc {
 
        /* if there is a pending flip, these will be non-null: */
        struct drm_pending_vblank_event *event;
-       struct work_struct pageflip_work;
+       struct msm_fence_cb pageflip_cb;
+
+#define PENDING_CURSOR 0x1
+#define PENDING_FLIP   0x2
+       atomic_t pending;
 
        /* the fb that we currently hold a scanout ref to: */
        struct drm_framebuffer *fb;
@@ -92,7 +97,8 @@ static void update_fb(struct drm_crtc *crtc, bool async,
        }
 }
 
-static void complete_flip(struct drm_crtc *crtc, bool canceled)
+/* if file!=NULL, this is preclose potential cancel-flip path */
+static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
 {
        struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
        struct drm_device *dev = crtc->dev;
@@ -102,11 +108,14 @@ static void complete_flip(struct drm_crtc *crtc, bool canceled)
        spin_lock_irqsave(&dev->event_lock, flags);
        event = mdp4_crtc->event;
        if (event) {
-               mdp4_crtc->event = NULL;
-               if (canceled)
-                       event->base.destroy(&event->base);
-               else
+               /* if regular vblank case (!file) or if cancel-flip from
+                * preclose on file that requested flip, then send the
+                * event:
+                */
+               if (!file || (event->base.file_priv == file)) {
+                       mdp4_crtc->event = NULL;
                        drm_send_vblank_event(dev, mdp4_crtc->id, event);
+               }
        }
        spin_unlock_irqrestore(&dev->event_lock, flags);
 }
@@ -115,9 +124,15 @@ static void crtc_flush(struct drm_crtc *crtc)
 {
        struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
        struct mdp4_kms *mdp4_kms = get_kms(crtc);
-       uint32_t flush = 0;
+       uint32_t i, flush = 0;
 
-       flush |= pipe2flush(mdp4_plane_pipe(mdp4_crtc->plane));
+       for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) {
+               struct drm_plane *plane = mdp4_crtc->planes[i];
+               if (plane) {
+                       enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane);
+                       flush |= pipe2flush(pipe_id);
+               }
+       }
        flush |= ovlp2flush(mdp4_crtc->ovlp);
 
        DBG("%s: flush=%08x", mdp4_crtc->name, flush);
@@ -125,17 +140,29 @@ static void crtc_flush(struct drm_crtc *crtc)
        mdp4_write(mdp4_kms, REG_MDP4_OVERLAY_FLUSH, flush);
 }
 
-static void pageflip_worker(struct work_struct *work)
+static void request_pending(struct drm_crtc *crtc, uint32_t pending)
+{
+       struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
+
+       atomic_or(pending, &mdp4_crtc->pending);
+       mdp4_irq_register(get_kms(crtc), &mdp4_crtc->vblank);
+}
+
+static void pageflip_cb(struct msm_fence_cb *cb)
 {
        struct mdp4_crtc *mdp4_crtc =
-               container_of(work, struct mdp4_crtc, pageflip_work);
+               container_of(cb, struct mdp4_crtc, pageflip_cb);
        struct drm_crtc *crtc = &mdp4_crtc->base;
+       struct drm_framebuffer *fb = crtc->fb;
 
-       mdp4_plane_set_scanout(mdp4_crtc->plane, crtc->fb);
+       if (!fb)
+               return;
+
+       mdp4_plane_set_scanout(mdp4_crtc->plane, fb);
        crtc_flush(crtc);
 
        /* enable vblank to complete flip: */
-       mdp4_irq_register(get_kms(crtc), &mdp4_crtc->vblank);
+       request_pending(crtc, PENDING_FLIP);
 }
 
 static void unref_fb_worker(struct drm_flip_work *work, void *val)
@@ -205,67 +232,69 @@ static void blend_setup(struct drm_crtc *crtc)
        struct mdp4_kms *mdp4_kms = get_kms(crtc);
        int i, ovlp = mdp4_crtc->ovlp;
        uint32_t mixer_cfg = 0;
-
-       /*
-        * This probably would also need to be triggered by any attached
-        * plane when it changes.. for now since we are only using a single
-        * private plane, the configuration is hard-coded:
-        */
+       static const enum mdp4_mixer_stage_id stages[] = {
+                       STAGE_BASE, STAGE0, STAGE1, STAGE2, STAGE3,
+       };
+       /* statically (for now) map planes to mixer stage (z-order): */
+       static const int idxs[] = {
+                       [VG1]  = 1,
+                       [VG2]  = 2,
+                       [RGB1] = 0,
+                       [RGB2] = 0,
+                       [RGB3] = 0,
+                       [VG3]  = 3,
+                       [VG4]  = 4,
+
+       };
+       bool alpha[4]= { false, false, false, false };
 
        mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW0(ovlp), 0);
        mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW1(ovlp), 0);
        mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH0(ovlp), 0);
        mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH1(ovlp), 0);
 
+       /* TODO single register for all CRTCs, so this won't work properly
+        * when multiple CRTCs are active..
+        */
+       for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) {
+               struct drm_plane *plane = mdp4_crtc->planes[i];
+               if (plane) {
+                       enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane);
+                       int idx = idxs[pipe_id];
+                       if (idx > 0) {
+                               const struct mdp4_format *format =
+                                       to_mdp4_format(msm_framebuffer_format(plane->fb));
+                               alpha[idx-1] = format->alpha_enable;
+                       }
+                       mixer_cfg |= mixercfg(mdp4_crtc->mixer, pipe_id, stages[idx]);
+               }
+       }
+
+       /* this shouldn't happen.. and seems to cause underflow: */
+       WARN_ON(!mixer_cfg);
+
        for (i = 0; i < 4; i++) {
-               mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_FG_ALPHA(ovlp, i), 0);
-               mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_BG_ALPHA(ovlp, i), 0);
-               mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_OP(ovlp, i),
-                               MDP4_OVLP_STAGE_OP_FG_ALPHA(FG_CONST) |
-                               MDP4_OVLP_STAGE_OP_BG_ALPHA(BG_CONST));
-               mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_CO3(ovlp, i), 0);
+               uint32_t op;
+
+               if (alpha[i]) {
+                       op = MDP4_OVLP_STAGE_OP_FG_ALPHA(FG_PIXEL) |
+                                       MDP4_OVLP_STAGE_OP_BG_ALPHA(FG_PIXEL) |
+                                       MDP4_OVLP_STAGE_OP_BG_INV_ALPHA;
+               } else {
+                       op = MDP4_OVLP_STAGE_OP_FG_ALPHA(FG_CONST) |
+                                       MDP4_OVLP_STAGE_OP_BG_ALPHA(BG_CONST);
+               }
+
+               mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_FG_ALPHA(ovlp, i), 0xff);
+               mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_BG_ALPHA(ovlp, i), 0x00);
+               mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_OP(ovlp, i), op);
+               mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_CO3(ovlp, i), 1);
                mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_LOW0(ovlp, i), 0);
                mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_LOW1(ovlp, i), 0);
                mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH0(ovlp, i), 0);
                mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH1(ovlp, i), 0);
        }
 
-       /* TODO single register for all CRTCs, so this won't work properly
-        * when multiple CRTCs are active..
-        */
-       switch (mdp4_plane_pipe(mdp4_crtc->plane)) {
-       case VG1:
-               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE0(STAGE_BASE) |
-                       COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1);
-               break;
-       case VG2:
-               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE1(STAGE_BASE) |
-                       COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1);
-               break;
-       case RGB1:
-               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE2(STAGE_BASE) |
-                       COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1);
-               break;
-       case RGB2:
-               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE3(STAGE_BASE) |
-                       COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1);
-               break;
-       case RGB3:
-               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE4(STAGE_BASE) |
-                       COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1);
-               break;
-       case VG3:
-               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE5(STAGE_BASE) |
-                       COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1);
-               break;
-       case VG4:
-               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE6(STAGE_BASE) |
-                       COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1);
-               break;
-       default:
-               WARN_ON("invalid pipe");
-               break;
-       }
        mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, mixer_cfg);
 }
 
@@ -377,6 +406,7 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
        struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
        struct drm_device *dev = crtc->dev;
        struct drm_gem_object *obj;
+       unsigned long flags;
 
        if (mdp4_crtc->event) {
                dev_err(dev->dev, "already pending flip!\n");
@@ -385,11 +415,13 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
 
        obj = msm_framebuffer_bo(new_fb, 0);
 
+       spin_lock_irqsave(&dev->event_lock, flags);
        mdp4_crtc->event = event;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
        update_fb(crtc, true, new_fb);
 
-       return msm_gem_queue_inactive_work(obj,
-                       &mdp4_crtc->pageflip_work);
+       return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb);
 }
 
 static int mdp4_crtc_set_property(struct drm_crtc *crtc,
@@ -498,6 +530,8 @@ static int mdp4_crtc_cursor_set(struct drm_crtc *crtc,
                drm_gem_object_unreference_unlocked(old_bo);
        }
 
+       request_pending(crtc, PENDING_CURSOR);
+
        return 0;
 
 fail:
@@ -542,13 +576,21 @@ static void mdp4_crtc_vblank_irq(struct mdp4_irq *irq, uint32_t irqstatus)
        struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, vblank);
        struct drm_crtc *crtc = &mdp4_crtc->base;
        struct msm_drm_private *priv = crtc->dev->dev_private;
+       unsigned pending;
 
-       update_cursor(crtc);
-       complete_flip(crtc, false);
        mdp4_irq_unregister(get_kms(crtc), &mdp4_crtc->vblank);
 
-       drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
-       drm_flip_work_commit(&mdp4_crtc->unref_cursor_work, priv->wq);
+       pending = atomic_xchg(&mdp4_crtc->pending, 0);
+
+       if (pending & PENDING_FLIP) {
+               complete_flip(crtc, NULL);
+               drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
+       }
+
+       if (pending & PENDING_CURSOR) {
+               update_cursor(crtc);
+               drm_flip_work_commit(&mdp4_crtc->unref_cursor_work, priv->wq);
+       }
 }
 
 static void mdp4_crtc_err_irq(struct mdp4_irq *irq, uint32_t irqstatus)
@@ -565,9 +607,10 @@ uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc)
        return mdp4_crtc->vblank.irqmask;
 }
 
-void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc)
+void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file)
 {
-       complete_flip(crtc, true);
+       DBG("cancel: %p", file);
+       complete_flip(crtc, file);
 }
 
 /* set dma config, ie. the format the encoder wants. */
@@ -622,6 +665,32 @@ void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf)
        mdp4_write(mdp4_kms, REG_MDP4_DISP_INTF_SEL, intf_sel);
 }
 
+static void set_attach(struct drm_crtc *crtc, enum mdp4_pipe pipe_id,
+               struct drm_plane *plane)
+{
+       struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
+
+       BUG_ON(pipe_id >= ARRAY_SIZE(mdp4_crtc->planes));
+
+       if (mdp4_crtc->planes[pipe_id] == plane)
+               return;
+
+       mdp4_crtc->planes[pipe_id] = plane;
+       blend_setup(crtc);
+       if (mdp4_crtc->enabled && (plane != mdp4_crtc->plane))
+               crtc_flush(crtc);
+}
+
+void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
+{
+       set_attach(crtc, mdp4_plane_pipe(plane), plane);
+}
+
+void mdp4_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane)
+{
+       set_attach(crtc, mdp4_plane_pipe(plane), NULL);
+}
+
 static const char *dma_names[] = {
                "DMA_P", "DMA_S", "DMA_E",
 };
@@ -644,7 +713,6 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
        crtc = &mdp4_crtc->base;
 
        mdp4_crtc->plane = plane;
-       mdp4_crtc->plane->crtc = crtc;
 
        mdp4_crtc->ovlp = ovlp_id;
        mdp4_crtc->dma = dma_id;
@@ -668,7 +736,7 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
        ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64,
                        "unref cursor", unref_cursor_worker);
 
-       INIT_WORK(&mdp4_crtc->pageflip_work, pageflip_worker);
+       INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb);
 
        drm_crtc_init(dev, crtc, &mdp4_crtc_funcs);
        drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
index 7b645f2..17330b0 100644 (file)
@@ -44,6 +44,22 @@ static const struct mdp4_format formats[] = {
        FMT(BGR565,   0, 5, 6, 5,  2, 0, 1, 0,  false,  true,  2,  3),
 };
 
+uint32_t mdp4_get_formats(enum mdp4_pipe pipe_id, uint32_t *pixel_formats,
+               uint32_t max_formats)
+{
+       uint32_t i;
+       for (i = 0; i < ARRAY_SIZE(formats); i++) {
+               const struct mdp4_format *f = &formats[i];
+
+               if (i == max_formats)
+                       break;
+
+               pixel_formats[i] = f->base.pixel_format;
+       }
+
+       return i;
+}
+
 const struct msm_format *mdp4_get_format(struct msm_kms *kms, uint32_t format)
 {
        int i;
index bc7fd11..8972ac3 100644 (file)
@@ -135,7 +135,7 @@ static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file)
        unsigned i;
 
        for (i = 0; i < priv->num_crtcs; i++)
-               mdp4_crtc_cancel_pending_flip(priv->crtcs[i]);
+               mdp4_crtc_cancel_pending_flip(priv->crtcs[i], file);
 }
 
 static void mdp4_destroy(struct msm_kms *kms)
@@ -196,6 +196,23 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
         * for more than just RGB1->DMA_E->DTV->HDMI
         */
 
+       /* construct non-private planes: */
+       plane = mdp4_plane_init(dev, VG1, false);
+       if (IS_ERR(plane)) {
+               dev_err(dev->dev, "failed to construct plane for VG1\n");
+               ret = PTR_ERR(plane);
+               goto fail;
+       }
+       priv->planes[priv->num_planes++] = plane;
+
+       plane = mdp4_plane_init(dev, VG2, false);
+       if (IS_ERR(plane)) {
+               dev_err(dev->dev, "failed to construct plane for VG2\n");
+               ret = PTR_ERR(plane);
+               goto fail;
+       }
+       priv->planes[priv->num_planes++] = plane;
+
        /* the CRTCs get constructed with a private plane: */
        plane = mdp4_plane_init(dev, RGB1, true);
        if (IS_ERR(plane)) {
index 1e83554..eb015c8 100644 (file)
@@ -75,8 +75,8 @@ struct mdp4_platform_config {
 
 struct mdp4_format {
        struct msm_format base;
-       enum mpd4_bpc bpc_r, bpc_g, bpc_b;
-       enum mpd4_bpc_alpha bpc_a;
+       enum mdp4_bpc bpc_r, bpc_g, bpc_b;
+       enum mdp4_bpc_alpha bpc_a;
        uint8_t unpack[4];
        bool alpha_enable, unpack_tight;
        uint8_t cpp, unpack_count;
@@ -93,7 +93,7 @@ static inline u32 mdp4_read(struct mdp4_kms *mdp4_kms, u32 reg)
        return msm_readl(mdp4_kms->mmio + reg);
 }
 
-static inline uint32_t pipe2flush(enum mpd4_pipe pipe)
+static inline uint32_t pipe2flush(enum mdp4_pipe pipe)
 {
        switch (pipe) {
        case VG1:      return MDP4_OVERLAY_FLUSH_VG1;
@@ -133,6 +133,48 @@ static inline uint32_t dma2err(enum mdp4_dma dma)
        }
 }
 
+static inline uint32_t mixercfg(int mixer, enum mdp4_pipe pipe,
+               enum mdp4_mixer_stage_id stage)
+{
+       uint32_t mixer_cfg = 0;
+
+       switch (pipe) {
+       case VG1:
+               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE0(stage) |
+                       COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1);
+               break;
+       case VG2:
+               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE1(stage) |
+                       COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1);
+               break;
+       case RGB1:
+               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE2(stage) |
+                       COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1);
+               break;
+       case RGB2:
+               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE3(stage) |
+                       COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1);
+               break;
+       case RGB3:
+               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE4(stage) |
+                       COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1);
+               break;
+       case VG3:
+               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE5(stage) |
+                       COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1);
+               break;
+       case VG4:
+               mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE6(stage) |
+                       COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1);
+               break;
+       default:
+               WARN_ON("invalid pipe");
+               break;
+       }
+
+       return mixer_cfg;
+}
+
 int mdp4_disable(struct mdp4_kms *mdp4_kms);
 int mdp4_enable(struct mdp4_kms *mdp4_kms);
 
@@ -146,6 +188,8 @@ void mdp4_irq_unregister(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq);
 int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
 void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
 
+uint32_t mdp4_get_formats(enum mdp4_pipe pipe_id, uint32_t *formats,
+               uint32_t max_formats);
 const struct msm_format *mdp4_get_format(struct msm_kms *kms, uint32_t format);
 
 void mdp4_plane_install_properties(struct drm_plane *plane,
@@ -158,14 +202,16 @@ int mdp4_plane_mode_set(struct drm_plane *plane,
                unsigned int crtc_w, unsigned int crtc_h,
                uint32_t src_x, uint32_t src_y,
                uint32_t src_w, uint32_t src_h);
-enum mpd4_pipe mdp4_plane_pipe(struct drm_plane *plane);
+enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane);
 struct drm_plane *mdp4_plane_init(struct drm_device *dev,
-               enum mpd4_pipe pipe_id, bool private_plane);
+               enum mdp4_pipe pipe_id, bool private_plane);
 
 uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc);
-void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc);
+void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
 void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config);
 void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf);
+void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane);
+void mdp4_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane);
 struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
                struct drm_plane *plane, int id, int ovlp_id,
                enum mdp4_dma dma_id);
index 3468229..0f0af24 100644 (file)
@@ -22,7 +22,7 @@ struct mdp4_plane {
        struct drm_plane base;
        const char *name;
 
-       enum mpd4_pipe pipe;
+       enum mdp4_pipe pipe;
 
        uint32_t nformats;
        uint32_t formats[32];
@@ -61,7 +61,9 @@ static int mdp4_plane_update(struct drm_plane *plane,
 static int mdp4_plane_disable(struct drm_plane *plane)
 {
        struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
-       DBG("%s: TODO", mdp4_plane->name); // XXX
+       DBG("%s: disable", mdp4_plane->name);
+       if (plane->crtc)
+               mdp4_crtc_detach(plane->crtc, plane);
        return 0;
 }
 
@@ -101,7 +103,7 @@ void mdp4_plane_set_scanout(struct drm_plane *plane,
 {
        struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
        struct mdp4_kms *mdp4_kms = get_kms(plane);
-       enum mpd4_pipe pipe = mdp4_plane->pipe;
+       enum mdp4_pipe pipe = mdp4_plane->pipe;
        uint32_t iova;
 
        mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_A(pipe),
@@ -129,7 +131,7 @@ int mdp4_plane_mode_set(struct drm_plane *plane,
 {
        struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
        struct mdp4_kms *mdp4_kms = get_kms(plane);
-       enum mpd4_pipe pipe = mdp4_plane->pipe;
+       enum mdp4_pipe pipe = mdp4_plane->pipe;
        const struct mdp4_format *format;
        uint32_t op_mode = 0;
        uint32_t phasex_step = MDP4_VG_PHASE_STEP_DEFAULT;
@@ -141,6 +143,10 @@ int mdp4_plane_mode_set(struct drm_plane *plane,
        src_w = src_w >> 16;
        src_h = src_h >> 16;
 
+       DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", mdp4_plane->name,
+                       fb->base.id, src_x, src_y, src_w, src_h,
+                       crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);
+
        if (src_w != crtc_w) {
                op_mode |= MDP4_PIPE_OP_MODE_SCALEX_EN;
                /* TODO calc phasex_step */
@@ -191,7 +197,8 @@ int mdp4_plane_mode_set(struct drm_plane *plane,
        mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEX_STEP(pipe), phasex_step);
        mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEY_STEP(pipe), phasey_step);
 
-       plane->crtc = crtc;
+       /* TODO detach from old crtc (if we had more than one) */
+       mdp4_crtc_attach(crtc, plane);
 
        return 0;
 }
@@ -202,7 +209,7 @@ static const char *pipe_names[] = {
                "VG3", "VG4",
 };
 
-enum mpd4_pipe mdp4_plane_pipe(struct drm_plane *plane)
+enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane)
 {
        struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
        return mdp4_plane->pipe;
@@ -210,9 +217,8 @@ enum mpd4_pipe mdp4_plane_pipe(struct drm_plane *plane)
 
 /* initialize plane */
 struct drm_plane *mdp4_plane_init(struct drm_device *dev,
-               enum mpd4_pipe pipe_id, bool private_plane)
+               enum mdp4_pipe pipe_id, bool private_plane)
 {
-       struct msm_drm_private *priv = dev->dev_private;
        struct drm_plane *plane = NULL;
        struct mdp4_plane *mdp4_plane;
        int ret;
@@ -228,8 +234,12 @@ struct drm_plane *mdp4_plane_init(struct drm_device *dev,
        mdp4_plane->pipe = pipe_id;
        mdp4_plane->name = pipe_names[pipe_id];
 
-       drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &mdp4_plane_funcs,
-                       mdp4_plane->formats, mdp4_plane->nformats, private_plane);
+       mdp4_plane->nformats = mdp4_get_formats(pipe_id, mdp4_plane->formats,
+                       ARRAY_SIZE(mdp4_plane->formats));
+
+       drm_plane_init(dev, plane, 0xff, &mdp4_plane_funcs,
+                       mdp4_plane->formats, mdp4_plane->nformats,
+                       private_plane);
 
        mdp4_plane_install_properties(plane, &plane->base);
 
index b3a2f16..8653769 100644 (file)
@@ -187,6 +187,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
        init_waitqueue_head(&priv->fence_event);
 
        INIT_LIST_HEAD(&priv->inactive_list);
+       INIT_LIST_HEAD(&priv->fence_cbs);
 
        drm_mode_config_init(dev);
 
@@ -539,15 +540,36 @@ int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
        return ret;
 }
 
-/* call under struct_mutex */
+/* called from workqueue */
 void msm_update_fence(struct drm_device *dev, uint32_t fence)
 {
        struct msm_drm_private *priv = dev->dev_private;
 
-       if (fence > priv->completed_fence) {
-               priv->completed_fence = fence;
-               wake_up_all(&priv->fence_event);
+       mutex_lock(&dev->struct_mutex);
+       priv->completed_fence = max(fence, priv->completed_fence);
+
+       while (!list_empty(&priv->fence_cbs)) {
+               struct msm_fence_cb *cb;
+
+               cb = list_first_entry(&priv->fence_cbs,
+                               struct msm_fence_cb, work.entry);
+
+               if (cb->fence > priv->completed_fence)
+                       break;
+
+               list_del_init(&cb->work.entry);
+               queue_work(priv->wq, &cb->work);
        }
+
+       mutex_unlock(&dev->struct_mutex);
+
+       wake_up_all(&priv->fence_event);
+}
+
+void __msm_fence_worker(struct work_struct *work)
+{
+       struct msm_fence_cb *cb = container_of(work, struct msm_fence_cb, work);
+       cb->func(cb);
 }
 
 /*
@@ -650,13 +672,13 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data,
 }
 
 static const struct drm_ioctl_desc msm_ioctls[] = {
-       DRM_IOCTL_DEF_DRV(MSM_GET_PARAM,    msm_ioctl_get_param,    DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(MSM_GEM_NEW,      msm_ioctl_gem_new,      DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(MSM_GEM_INFO,     msm_ioctl_gem_info,     DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT,   msm_ioctl_gem_submit,   DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE,   msm_ioctl_wait_fence,   DRM_UNLOCKED|DRM_AUTH),
+       DRM_IOCTL_DEF_DRV(MSM_GET_PARAM,    msm_ioctl_get_param,    DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GEM_NEW,      msm_ioctl_gem_new,      DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GEM_INFO,     msm_ioctl_gem_info,     DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT,   msm_ioctl_gem_submit,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE,   msm_ioctl_wait_fence,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
 };
 
 static const struct vm_operations_struct vm_ops = {
@@ -680,7 +702,11 @@ static const struct file_operations fops = {
 };
 
 static struct drm_driver msm_driver = {
-       .driver_features    = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
+       .driver_features    = DRIVER_HAVE_IRQ |
+                               DRIVER_GEM |
+                               DRIVER_PRIME |
+                               DRIVER_RENDER |
+                               DRIVER_MODESET,
        .load               = msm_load,
        .unload             = msm_unload,
        .open               = msm_open,
@@ -698,6 +724,16 @@ static struct drm_driver msm_driver = {
        .dumb_create        = msm_gem_dumb_create,
        .dumb_map_offset    = msm_gem_dumb_map_offset,
        .dumb_destroy       = drm_gem_dumb_destroy,
+       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+       .gem_prime_export   = drm_gem_prime_export,
+       .gem_prime_import   = drm_gem_prime_import,
+       .gem_prime_pin      = msm_gem_prime_pin,
+       .gem_prime_unpin    = msm_gem_prime_unpin,
+       .gem_prime_get_sg_table = msm_gem_prime_get_sg_table,
+       .gem_prime_import_sg_table = msm_gem_prime_import_sg_table,
+       .gem_prime_vmap     = msm_gem_prime_vmap,
+       .gem_prime_vunmap   = msm_gem_prime_vunmap,
 #ifdef CONFIG_DEBUG_FS
        .debugfs_init       = msm_debugfs_init,
        .debugfs_cleanup    = msm_debugfs_cleanup,
index df8f1d0..d39f086 100644 (file)
@@ -73,10 +73,16 @@ struct msm_drm_private {
 
        struct workqueue_struct *wq;
 
+       /* callbacks deferred until bo is inactive: */
+       struct list_head fence_cbs;
+
        /* registered IOMMU domains: */
        unsigned int num_iommus;
        struct iommu_domain *iommus[NUM_DOMAINS];
 
+       unsigned int num_planes;
+       struct drm_plane *planes[8];
+
        unsigned int num_crtcs;
        struct drm_crtc *crtcs[8];
 
@@ -94,6 +100,20 @@ struct msm_format {
        uint32_t pixel_format;
 };
 
+/* callback from wq once fence has passed: */
+struct msm_fence_cb {
+       struct work_struct work;
+       uint32_t fence;
+       void (*func)(struct msm_fence_cb *cb);
+};
+
+void __msm_fence_worker(struct work_struct *work);
+
+#define INIT_FENCE_CB(_cb, _func)  do {                     \
+               INIT_WORK(&(_cb)->work, __msm_fence_worker); \
+               (_cb)->func = _func;                         \
+       } while (0)
+
 /* As there are different display controller blocks depending on the
  * snapdragon version, the kms support is split out and the appropriate
  * implementation is loaded at runtime.  The kms module is responsible
@@ -141,17 +161,24 @@ uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj);
 int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,
                uint32_t *iova);
 int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova);
+struct page **msm_gem_get_pages(struct drm_gem_object *obj);
+void msm_gem_put_pages(struct drm_gem_object *obj);
 void msm_gem_put_iova(struct drm_gem_object *obj, int id);
 int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
                struct drm_mode_create_dumb *args);
-int msm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
-               uint32_t handle);
 int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
                uint32_t handle, uint64_t *offset);
+struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj);
+void *msm_gem_prime_vmap(struct drm_gem_object *obj);
+void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
+               size_t size, struct sg_table *sg);
+int msm_gem_prime_pin(struct drm_gem_object *obj);
+void msm_gem_prime_unpin(struct drm_gem_object *obj);
 void *msm_gem_vaddr_locked(struct drm_gem_object *obj);
 void *msm_gem_vaddr(struct drm_gem_object *obj);
-int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
-               struct work_struct *work);
+int msm_gem_queue_inactive_cb(struct drm_gem_object *obj,
+               struct msm_fence_cb *cb);
 void msm_gem_move_to_active(struct drm_gem_object *obj,
                struct msm_gpu *gpu, bool write, uint32_t fence);
 void msm_gem_move_to_inactive(struct drm_gem_object *obj);
@@ -163,6 +190,8 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
                uint32_t size, uint32_t flags, uint32_t *handle);
 struct drm_gem_object *msm_gem_new(struct drm_device *dev,
                uint32_t size, uint32_t flags);
+struct drm_gem_object *msm_gem_import(struct drm_device *dev,
+               uint32_t size, struct sg_table *sgt);
 
 struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane);
 const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb);
index 2bae46c..e587d25 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <linux/spinlock.h>
 #include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
 
 #include "msm_drv.h"
 #include "msm_gem.h"
@@ -77,6 +78,21 @@ static void put_pages(struct drm_gem_object *obj)
        }
 }
 
+struct page **msm_gem_get_pages(struct drm_gem_object *obj)
+{
+       struct drm_device *dev = obj->dev;
+       struct page **p;
+       mutex_lock(&dev->struct_mutex);
+       p = get_pages(obj);
+       mutex_unlock(&dev->struct_mutex);
+       return p;
+}
+
+void msm_gem_put_pages(struct drm_gem_object *obj)
+{
+       /* when we start tracking the pin count, then do something here */
+}
+
 int msm_gem_mmap_obj(struct drm_gem_object *obj,
                struct vm_area_struct *vma)
 {
@@ -162,6 +178,11 @@ out:
        case 0:
        case -ERESTARTSYS:
        case -EINTR:
+       case -EBUSY:
+               /*
+                * EBUSY is ok: this just means that another thread
+                * already did the job.
+                */
                return VM_FAULT_NOPAGE;
        case -ENOMEM:
                return VM_FAULT_OOM;
@@ -293,7 +314,17 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,
 
 int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova)
 {
+       struct msm_gem_object *msm_obj = to_msm_bo(obj);
        int ret;
+
+       /* this is safe right now because we don't unmap until the
+        * bo is deleted:
+        */
+       if (msm_obj->domain[id].iova) {
+               *iova = msm_obj->domain[id].iova;
+               return 0;
+       }
+
        mutex_lock(&obj->dev->struct_mutex);
        ret = msm_gem_get_iova_locked(obj, id, iova);
        mutex_unlock(&obj->dev->struct_mutex);
@@ -363,8 +394,11 @@ void *msm_gem_vaddr(struct drm_gem_object *obj)
        return ret;
 }
 
-int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
-               struct work_struct *work)
+/* setup callback for when bo is no longer busy..
+ * TODO probably want to differentiate read vs write..
+ */
+int msm_gem_queue_inactive_cb(struct drm_gem_object *obj,
+               struct msm_fence_cb *cb)
 {
        struct drm_device *dev = obj->dev;
        struct msm_drm_private *priv = dev->dev_private;
@@ -372,12 +406,13 @@ int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
        int ret = 0;
 
        mutex_lock(&dev->struct_mutex);
-       if (!list_empty(&work->entry)) {
+       if (!list_empty(&cb->work.entry)) {
                ret = -EINVAL;
        } else if (is_active(msm_obj)) {
-               list_add_tail(&work->entry, &msm_obj->inactive_work);
+               cb->fence = max(msm_obj->read_fence, msm_obj->write_fence);
+               list_add_tail(&cb->work.entry, &priv->fence_cbs);
        } else {
-               queue_work(priv->wq, work);
+               queue_work(priv->wq, &cb->work);
        }
        mutex_unlock(&dev->struct_mutex);
 
@@ -410,16 +445,6 @@ void msm_gem_move_to_inactive(struct drm_gem_object *obj)
        msm_obj->write_fence = 0;
        list_del_init(&msm_obj->mm_list);
        list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
-
-       while (!list_empty(&msm_obj->inactive_work)) {
-               struct work_struct *work;
-
-               work = list_first_entry(&msm_obj->inactive_work,
-                               struct work_struct, entry);
-
-               list_del_init(&work->entry);
-               queue_work(priv->wq, work);
-       }
 }
 
 int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
@@ -510,10 +535,21 @@ void msm_gem_free_object(struct drm_gem_object *obj)
 
        drm_gem_free_mmap_offset(obj);
 
-       if (msm_obj->vaddr)
-               vunmap(msm_obj->vaddr);
+       if (obj->import_attach) {
+               if (msm_obj->vaddr)
+                       dma_buf_vunmap(obj->import_attach->dmabuf, msm_obj->vaddr);
 
-       put_pages(obj);
+               /* Don't drop the pages for imported dmabuf, as they are not
+                * ours, just free the array we allocated:
+                */
+               if (msm_obj->pages)
+                       drm_free_large(msm_obj->pages);
+
+       } else {
+               if (msm_obj->vaddr)
+                       vunmap(msm_obj->vaddr);
+               put_pages(obj);
+       }
 
        if (msm_obj->resv == &msm_obj->_resv)
                reservation_object_fini(msm_obj->resv);
@@ -549,17 +585,12 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
        return ret;
 }
 
-struct drm_gem_object *msm_gem_new(struct drm_device *dev,
-               uint32_t size, uint32_t flags)
+static int msm_gem_new_impl(struct drm_device *dev,
+               uint32_t size, uint32_t flags,
+               struct drm_gem_object **obj)
 {
        struct msm_drm_private *priv = dev->dev_private;
        struct msm_gem_object *msm_obj;
-       struct drm_gem_object *obj = NULL;
-       int ret;
-
-       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
-
-       size = PAGE_ALIGN(size);
 
        switch (flags & MSM_BO_CACHE_MASK) {
        case MSM_BO_UNCACHED:
@@ -569,21 +600,12 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev,
        default:
                dev_err(dev->dev, "invalid cache flag: %x\n",
                                (flags & MSM_BO_CACHE_MASK));
-               ret = -EINVAL;
-               goto fail;
+               return -EINVAL;
        }
 
        msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL);
-       if (!msm_obj) {
-               ret = -ENOMEM;
-               goto fail;
-       }
-
-       obj = &msm_obj->base;
-
-       ret = drm_gem_object_init(dev, obj, size);
-       if (ret)
-               goto fail;
+       if (!msm_obj)
+               return -ENOMEM;
 
        msm_obj->flags = flags;
 
@@ -591,9 +613,69 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev,
        reservation_object_init(msm_obj->resv);
 
        INIT_LIST_HEAD(&msm_obj->submit_entry);
-       INIT_LIST_HEAD(&msm_obj->inactive_work);
        list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
 
+       *obj = &msm_obj->base;
+
+       return 0;
+}
+
+struct drm_gem_object *msm_gem_new(struct drm_device *dev,
+               uint32_t size, uint32_t flags)
+{
+       struct drm_gem_object *obj;
+       int ret;
+
+       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+       size = PAGE_ALIGN(size);
+
+       ret = msm_gem_new_impl(dev, size, flags, &obj);
+       if (ret)
+               goto fail;
+
+       ret = drm_gem_object_init(dev, obj, size);
+       if (ret)
+               goto fail;
+
+       return obj;
+
+fail:
+       if (obj)
+               drm_gem_object_unreference_unlocked(obj);
+
+       return ERR_PTR(ret);
+}
+
+struct drm_gem_object *msm_gem_import(struct drm_device *dev,
+               uint32_t size, struct sg_table *sgt)
+{
+       struct msm_gem_object *msm_obj;
+       struct drm_gem_object *obj;
+       int ret, npages;
+
+       size = PAGE_ALIGN(size);
+
+       ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj);
+       if (ret)
+               goto fail;
+
+       drm_gem_private_object_init(dev, obj, size);
+
+       npages = size / PAGE_SIZE;
+
+       msm_obj = to_msm_bo(obj);
+       msm_obj->sgt = sgt;
+       msm_obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
+       if (!msm_obj->pages) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages, NULL, npages);
+       if (ret)
+               goto fail;
+
        return obj;
 
 fail:
index 0676f32..f4f23a5 100644 (file)
@@ -45,9 +45,6 @@ struct msm_gem_object {
         */
        struct list_head submit_entry;
 
-       /* work defered until bo is inactive: */
-       struct list_head inactive_work;
-
        struct page **pages;
        struct sg_table *sgt;
        void *vaddr;
diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c
new file mode 100644 (file)
index 0000000..d48f9fc
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 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 "msm_drv.h"
+#include "msm_gem.h"
+
+
+struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+       struct msm_gem_object *msm_obj = to_msm_bo(obj);
+       BUG_ON(!msm_obj->sgt);  /* should have already pinned! */
+       return msm_obj->sgt;
+}
+
+void *msm_gem_prime_vmap(struct drm_gem_object *obj)
+{
+       return msm_gem_vaddr(obj);
+}
+
+void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+       /* TODO msm_gem_vunmap() */
+}
+
+struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
+               size_t size, struct sg_table *sg)
+{
+       return msm_gem_import(dev, size, sg);
+}
+
+int msm_gem_prime_pin(struct drm_gem_object *obj)
+{
+       if (!obj->import_attach)
+               msm_gem_get_pages(obj);
+       return 0;
+}
+
+void msm_gem_prime_unpin(struct drm_gem_object *obj)
+{
+       if (!obj->import_attach)
+               msm_gem_put_pages(obj);
+}
index 3bab937..4583d61 100644 (file)
@@ -268,6 +268,8 @@ static void retire_worker(struct work_struct *work)
        struct drm_device *dev = gpu->dev;
        uint32_t fence = gpu->funcs->last_fence(gpu);
 
+       msm_update_fence(gpu->dev, fence);
+
        mutex_lock(&dev->struct_mutex);
 
        while (!list_empty(&gpu->active_list)) {
@@ -287,8 +289,6 @@ static void retire_worker(struct work_struct *work)
                }
        }
 
-       msm_update_fence(gpu->dev, fence);
-
        mutex_unlock(&dev->struct_mutex);
 }
 
index ff80f12..7cf787d 100644 (file)
@@ -3,6 +3,7 @@ config DRM_NOUVEAU
        depends on DRM && PCI
         select FW_LOADER
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select DRM_TTM
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
index d939a1d..edcf801 100644 (file)
@@ -28,7 +28,9 @@ nouveau-y += core/subdev/bar/nv50.o
 nouveau-y += core/subdev/bar/nvc0.o
 nouveau-y += core/subdev/bios/base.o
 nouveau-y += core/subdev/bios/bit.o
+nouveau-y += core/subdev/bios/boost.o
 nouveau-y += core/subdev/bios/conn.o
+nouveau-y += core/subdev/bios/cstep.o
 nouveau-y += core/subdev/bios/dcb.o
 nouveau-y += core/subdev/bios/disp.o
 nouveau-y += core/subdev/bios/dp.o
@@ -39,17 +41,26 @@ nouveau-y += core/subdev/bios/init.o
 nouveau-y += core/subdev/bios/mxm.o
 nouveau-y += core/subdev/bios/perf.o
 nouveau-y += core/subdev/bios/pll.o
+nouveau-y += core/subdev/bios/rammap.o
+nouveau-y += core/subdev/bios/timing.o
 nouveau-y += core/subdev/bios/therm.o
+nouveau-y += core/subdev/bios/vmap.o
+nouveau-y += core/subdev/bios/volt.o
 nouveau-y += core/subdev/bios/xpio.o
+nouveau-y += core/subdev/bus/hwsq.o
 nouveau-y += core/subdev/bus/nv04.o
 nouveau-y += core/subdev/bus/nv31.o
 nouveau-y += core/subdev/bus/nv50.o
+nouveau-y += core/subdev/bus/nv94.o
 nouveau-y += core/subdev/bus/nvc0.o
+nouveau-y += core/subdev/clock/base.o
 nouveau-y += core/subdev/clock/nv04.o
 nouveau-y += core/subdev/clock/nv40.o
 nouveau-y += core/subdev/clock/nv50.o
+nouveau-y += core/subdev/clock/nv84.o
 nouveau-y += core/subdev/clock/nva3.o
 nouveau-y += core/subdev/clock/nvc0.o
+nouveau-y += core/subdev/clock/nve0.o
 nouveau-y += core/subdev/clock/pllnv04.o
 nouveau-y += core/subdev/clock/pllnva3.o
 nouveau-y += core/subdev/devinit/base.o
@@ -78,7 +89,12 @@ nouveau-y += core/subdev/fb/nv47.o
 nouveau-y += core/subdev/fb/nv49.o
 nouveau-y += core/subdev/fb/nv4e.o
 nouveau-y += core/subdev/fb/nv50.o
+nouveau-y += core/subdev/fb/nv84.o
+nouveau-y += core/subdev/fb/nva3.o
+nouveau-y += core/subdev/fb/nvaa.o
+nouveau-y += core/subdev/fb/nvaf.o
 nouveau-y += core/subdev/fb/nvc0.o
+nouveau-y += core/subdev/fb/nve0.o
 nouveau-y += core/subdev/fb/ramnv04.o
 nouveau-y += core/subdev/fb/ramnv10.o
 nouveau-y += core/subdev/fb/ramnv1a.o
@@ -89,7 +105,12 @@ nouveau-y += core/subdev/fb/ramnv44.o
 nouveau-y += core/subdev/fb/ramnv49.o
 nouveau-y += core/subdev/fb/ramnv4e.o
 nouveau-y += core/subdev/fb/ramnv50.o
+nouveau-y += core/subdev/fb/ramnva3.o
+nouveau-y += core/subdev/fb/ramnvaa.o
 nouveau-y += core/subdev/fb/ramnvc0.o
+nouveau-y += core/subdev/fb/ramnve0.o
+nouveau-y += core/subdev/fb/sddr3.o
+nouveau-y += core/subdev/fb/gddr5.o
 nouveau-y += core/subdev/gpio/base.o
 nouveau-y += core/subdev/gpio/nv10.o
 nouveau-y += core/subdev/gpio/nv50.o
@@ -113,13 +134,22 @@ nouveau-y += core/subdev/instmem/nv50.o
 nouveau-y += core/subdev/ltcg/nvc0.o
 nouveau-y += core/subdev/mc/base.o
 nouveau-y += core/subdev/mc/nv04.o
+nouveau-y += core/subdev/mc/nv40.o
 nouveau-y += core/subdev/mc/nv44.o
 nouveau-y += core/subdev/mc/nv50.o
+nouveau-y += core/subdev/mc/nv94.o
 nouveau-y += core/subdev/mc/nv98.o
 nouveau-y += core/subdev/mc/nvc0.o
+nouveau-y += core/subdev/mc/nvc3.o
 nouveau-y += core/subdev/mxm/base.o
 nouveau-y += core/subdev/mxm/mxms.o
 nouveau-y += core/subdev/mxm/nv50.o
+nouveau-y += core/subdev/pwr/base.o
+nouveau-y += core/subdev/pwr/memx.o
+nouveau-y += core/subdev/pwr/nva3.o
+nouveau-y += core/subdev/pwr/nvc0.o
+nouveau-y += core/subdev/pwr/nvd0.o
+nouveau-y += core/subdev/pwr/nv108.o
 nouveau-y += core/subdev/therm/base.o
 nouveau-y += core/subdev/therm/fan.o
 nouveau-y += core/subdev/therm/fannil.o
@@ -140,6 +170,9 @@ nouveau-y += core/subdev/vm/nv41.o
 nouveau-y += core/subdev/vm/nv44.o
 nouveau-y += core/subdev/vm/nv50.o
 nouveau-y += core/subdev/vm/nvc0.o
+nouveau-y += core/subdev/volt/base.o
+nouveau-y += core/subdev/volt/gpio.o
+nouveau-y += core/subdev/volt/nv40.o
 
 nouveau-y += core/engine/falcon.o
 nouveau-y += core/engine/xtensa.o
@@ -158,6 +191,7 @@ nouveau-y += core/engine/copy/nve0.o
 nouveau-y += core/engine/crypt/nv84.o
 nouveau-y += core/engine/crypt/nv98.o
 nouveau-y += core/engine/device/base.o
+nouveau-y += core/engine/device/ctrl.o
 nouveau-y += core/engine/device/nv04.o
 nouveau-y += core/engine/device/nv10.o
 nouveau-y += core/engine/device/nv20.o
@@ -227,8 +261,18 @@ nouveau-y += core/engine/graph/nve4.o
 nouveau-y += core/engine/graph/nvf0.o
 nouveau-y += core/engine/mpeg/nv31.o
 nouveau-y += core/engine/mpeg/nv40.o
+nouveau-y += core/engine/mpeg/nv44.o
 nouveau-y += core/engine/mpeg/nv50.o
 nouveau-y += core/engine/mpeg/nv84.o
+nouveau-y += core/engine/perfmon/base.o
+nouveau-y += core/engine/perfmon/daemon.o
+nouveau-y += core/engine/perfmon/nv40.o
+nouveau-y += core/engine/perfmon/nv50.o
+nouveau-y += core/engine/perfmon/nv84.o
+nouveau-y += core/engine/perfmon/nva3.o
+nouveau-y += core/engine/perfmon/nvc0.o
+nouveau-y += core/engine/perfmon/nve0.o
+nouveau-y += core/engine/perfmon/nvf0.o
 nouveau-y += core/engine/ppp/nv98.o
 nouveau-y += core/engine/ppp/nvc0.o
 nouveau-y += core/engine/software/nv04.o
@@ -260,9 +304,7 @@ include $(src)/dispnv04/Makefile
 nouveau-y += nv50_display.o
 
 # drm/pm
-nouveau-y += nouveau_pm.o nouveau_volt.o nouveau_perf.o
-nouveau-y += nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o
-nouveau-y += nouveau_mem.o
+nouveau-y += nouveau_hwmon.o nouveau_sysfs.o
 
 # other random bits
 nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
index 7eb81c1..3f3c765 100644 (file)
 #include <core/os.h>
 #include <core/event.h>
 
-static void
-nouveau_event_put_locked(struct nouveau_event *event, int index,
-                        struct nouveau_eventh *handler)
+void
+nouveau_event_put(struct nouveau_eventh *handler)
 {
-       if (!--event->index[index].refs) {
-               if (event->disable)
-                       event->disable(event, index);
+       struct nouveau_event *event = handler->event;
+       unsigned long flags;
+       if (__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
+               spin_lock_irqsave(&event->refs_lock, flags);
+               if (!--event->index[handler->index].refs) {
+                       if (event->disable)
+                               event->disable(event, handler->index);
+               }
+               spin_unlock_irqrestore(&event->refs_lock, flags);
        }
-       list_del(&handler->head);
 }
 
 void
-nouveau_event_put(struct nouveau_event *event, int index,
-                 struct nouveau_eventh *handler)
+nouveau_event_get(struct nouveau_eventh *handler)
 {
+       struct nouveau_event *event = handler->event;
        unsigned long flags;
+       if (!__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
+               spin_lock_irqsave(&event->refs_lock, flags);
+               if (!event->index[handler->index].refs++) {
+                       if (event->enable)
+                               event->enable(event, handler->index);
+               }
+               spin_unlock_irqrestore(&event->refs_lock, flags);
+       }
+}
 
-       spin_lock_irqsave(&event->lock, flags);
-       if (index < event->index_nr)
-               nouveau_event_put_locked(event, index, handler);
-       spin_unlock_irqrestore(&event->lock, flags);
+static void
+nouveau_event_fini(struct nouveau_eventh *handler)
+{
+       struct nouveau_event *event = handler->event;
+       unsigned long flags;
+       nouveau_event_put(handler);
+       spin_lock_irqsave(&event->list_lock, flags);
+       list_del(&handler->head);
+       spin_unlock_irqrestore(&event->list_lock, flags);
 }
 
-void
-nouveau_event_get(struct nouveau_event *event, int index,
-                 struct nouveau_eventh *handler)
+static int
+nouveau_event_init(struct nouveau_event *event, int index,
+                  int (*func)(void *, int), void *priv,
+                  struct nouveau_eventh *handler)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&event->lock, flags);
-       if (index < event->index_nr) {
-               list_add(&handler->head, &event->index[index].list);
-               if (!event->index[index].refs++) {
-                       if (event->enable)
-                               event->enable(event, index);
-               }
+       if (index >= event->index_nr)
+               return -EINVAL;
+
+       handler->event = event;
+       handler->flags = 0;
+       handler->index = index;
+       handler->func = func;
+       handler->priv = priv;
+
+       spin_lock_irqsave(&event->list_lock, flags);
+       list_add_tail(&handler->head, &event->index[index].list);
+       spin_unlock_irqrestore(&event->list_lock, flags);
+       return 0;
+}
+
+int
+nouveau_event_new(struct nouveau_event *event, int index,
+                 int (*func)(void *, int), void *priv,
+                 struct nouveau_eventh **phandler)
+{
+       struct nouveau_eventh *handler;
+       int ret = -ENOMEM;
+
+       handler = *phandler = kmalloc(sizeof(*handler), GFP_KERNEL);
+       if (handler) {
+               ret = nouveau_event_init(event, index, func, priv, handler);
+               if (ret)
+                       kfree(handler);
        }
-       spin_unlock_irqrestore(&event->lock, flags);
+
+       return ret;
+}
+
+void
+nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref)
+{
+       BUG_ON(handler != NULL);
+       if (*ref) {
+               nouveau_event_fini(*ref);
+               kfree(*ref);
+       }
+       *ref = handler;
 }
 
 void
 nouveau_event_trigger(struct nouveau_event *event, int index)
 {
-       struct nouveau_eventh *handler, *temp;
+       struct nouveau_eventh *handler;
        unsigned long flags;
 
-       if (index >= event->index_nr)
+       if (WARN_ON(index >= event->index_nr))
                return;
 
-       spin_lock_irqsave(&event->lock, flags);
-       list_for_each_entry_safe(handler, temp, &event->index[index].list, head) {
-               if (handler->func(handler, index) == NVKM_EVENT_DROP) {
-                       nouveau_event_put_locked(event, index, handler);
-               }
+       spin_lock_irqsave(&event->list_lock, flags);
+       list_for_each_entry(handler, &event->index[index].list, head) {
+               if (test_bit(NVKM_EVENT_ENABLE, &handler->flags) &&
+                   handler->func(handler->priv, index) == NVKM_EVENT_DROP)
+                       nouveau_event_put(handler);
        }
-       spin_unlock_irqrestore(&event->lock, flags);
+       spin_unlock_irqrestore(&event->list_lock, flags);
 }
 
 void
@@ -102,7 +154,8 @@ nouveau_event_create(int index_nr, struct nouveau_event **pevent)
        if (!event)
                return -ENOMEM;
 
-       spin_lock_init(&event->lock);
+       spin_lock_init(&event->list_lock);
+       spin_lock_init(&event->refs_lock);
        for (i = 0; i < index_nr; i++)
                INIT_LIST_HEAD(&event->index[i].list);
        event->index_nr = index_nr;
index 62a432e..9f6fcc5 100644 (file)
 #include <core/option.h>
 #include <core/debug.h>
 
-/* compares unterminated string 'str' with zero-terminated string 'cmp' */
-static inline int
-strncasecmpz(const char *str, const char *cmp, size_t len)
-{
-       if (strlen(cmp) != len)
-               return len;
-       return strncasecmp(str, cmp, len);
-}
-
 const char *
 nouveau_stropt(const char *optstr, const char *opt, int *arglen)
 {
@@ -105,7 +96,7 @@ nouveau_dbgopt(const char *optstr, const char *sub)
                                else if (!strncasecmpz(optstr, "warn", len))
                                        level = NV_DBG_WARN;
                                else if (!strncasecmpz(optstr, "info", len))
-                                       level = NV_DBG_INFO;
+                                       level = NV_DBG_INFO_NORMAL;
                                else if (!strncasecmpz(optstr, "debug", len))
                                        level = NV_DBG_DEBUG;
                                else if (!strncasecmpz(optstr, "trace", len))
index 52fb2aa..03e0060 100644 (file)
 #include <core/subdev.h>
 #include <core/printk.h>
 
-int nv_printk_suspend_level = NV_DBG_DEBUG;
+int nv_info_debug_level = NV_DBG_INFO_NORMAL;
 
 void
-nv_printk_(struct nouveau_object *object, const char *pfx, int level,
-          const char *fmt, ...)
+nv_printk_(struct nouveau_object *object, int level, const char *fmt, ...)
 {
        static const char name[] = { '!', 'E', 'W', ' ', 'D', 'T', 'P', 'S' };
+       const char *pfx;
        char mfmt[256];
        va_list args;
 
+       switch (level) {
+       case NV_DBG_FATAL:
+               pfx = KERN_CRIT;
+               break;
+       case NV_DBG_ERROR:
+               pfx = KERN_ERR;
+               break;
+       case NV_DBG_WARN:
+               pfx = KERN_WARNING;
+               break;
+       case NV_DBG_INFO_NORMAL:
+               pfx = KERN_INFO;
+               break;
+       case NV_DBG_DEBUG:
+       case NV_DBG_PARANOIA:
+       case NV_DBG_TRACE:
+       case NV_DBG_SPAM:
+       default:
+               pfx = KERN_DEBUG;
+               break;
+       }
+
        if (object && !nv_iclass(object, NV_CLIENT_CLASS)) {
                struct nouveau_object *device = object;
                struct nouveau_object *subdev = object;
@@ -74,20 +96,3 @@ nv_printk_(struct nouveau_object *object, const char *pfx, int level,
        vprintk(mfmt, args);
        va_end(args);
 }
-
-#define CONV_LEVEL(x) case NV_DBG_##x: return NV_PRINTK_##x
-
-const char *nv_printk_level_to_pfx(int level)
-{
-       switch (level) {
-       CONV_LEVEL(FATAL);
-       CONV_LEVEL(ERROR);
-       CONV_LEVEL(WARN);
-       CONV_LEVEL(INFO);
-       CONV_LEVEL(DEBUG);
-       CONV_LEVEL(PARANOIA);
-       CONV_LEVEL(TRACE);
-       CONV_LEVEL(SPAM);
-       }
-       return NV_PRINTK_DEBUG;
-}
index 4c72571..9135b25 100644 (file)
@@ -29,7 +29,7 @@
 
 #include <core/class.h>
 
-#include <engine/device.h>
+#include "priv.h"
 
 static DEFINE_MUTEX(nv_devices_mutex);
 static LIST_HEAD(nv_devices);
@@ -75,7 +75,9 @@ static const u64 disable_map[] = {
        [NVDEV_SUBDEV_BAR]      = NV_DEVICE_DISABLE_CORE,
        [NVDEV_SUBDEV_VOLT]     = NV_DEVICE_DISABLE_CORE,
        [NVDEV_SUBDEV_THERM]    = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_PWR]      = NV_DEVICE_DISABLE_CORE,
        [NVDEV_ENGINE_DMAOBJ]   = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_ENGINE_PERFMON]  = NV_DEVICE_DISABLE_CORE,
        [NVDEV_ENGINE_FIFO]     = NV_DEVICE_DISABLE_FIFO,
        [NVDEV_ENGINE_SW]       = NV_DEVICE_DISABLE_FIFO,
        [NVDEV_ENGINE_GR]       = NV_DEVICE_DISABLE_GRAPH,
@@ -87,7 +89,7 @@ static const u64 disable_map[] = {
        [NVDEV_ENGINE_PPP]      = NV_DEVICE_DISABLE_PPP,
        [NVDEV_ENGINE_COPY0]    = NV_DEVICE_DISABLE_COPY0,
        [NVDEV_ENGINE_COPY1]    = NV_DEVICE_DISABLE_COPY1,
-       [NVDEV_ENGINE_UNK1C1]   = NV_DEVICE_DISABLE_UNK1C1,
+       [NVDEV_ENGINE_VIC]      = NV_DEVICE_DISABLE_VIC,
        [NVDEV_ENGINE_VENC]     = NV_DEVICE_DISABLE_VENC,
        [NVDEV_ENGINE_DISP]     = NV_DEVICE_DISABLE_DISP,
        [NVDEV_SUBDEV_NR]       = 0,
@@ -119,10 +121,12 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
                        return -ENODEV;
        }
 
-       ret = nouveau_parent_create(parent, nv_object(device), oclass, 0, NULL,
+       ret = nouveau_parent_create(parent, nv_object(device), oclass, 0,
+                                   nouveau_control_oclass,
                                    (1ULL << NVDEV_ENGINE_DMAOBJ) |
                                    (1ULL << NVDEV_ENGINE_FIFO) |
-                                   (1ULL << NVDEV_ENGINE_DISP), &devobj);
+                                   (1ULL << NVDEV_ENGINE_DISP) |
+                                   (1ULL << NVDEV_ENGINE_PERFMON), &devobj);
        *pobject = nv_object(devobj);
        if (ret)
                return ret;
@@ -158,22 +162,29 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
                iounmap(map);
 
                /* determine chipset and derive architecture from it */
-               if ((boot0 & 0x0f000000) > 0) {
-                       device->chipset = (boot0 & 0xff00000) >> 20;
-                       switch (device->chipset & 0xf0) {
-                       case 0x10: device->card_type = NV_10; break;
-                       case 0x20: device->card_type = NV_20; break;
-                       case 0x30: device->card_type = NV_30; break;
-                       case 0x40:
-                       case 0x60: device->card_type = NV_40; break;
-                       case 0x50:
-                       case 0x80:
-                       case 0x90:
-                       case 0xa0: device->card_type = NV_50; break;
-                       case 0xc0: device->card_type = NV_C0; break;
-                       case 0xd0: device->card_type = NV_D0; break;
-                       case 0xe0:
-                       case 0xf0: device->card_type = NV_E0; break;
+               if ((boot0 & 0x1f000000) > 0) {
+                       device->chipset = (boot0 & 0x1ff00000) >> 20;
+                       switch (device->chipset & 0x1f0) {
+                       case 0x010: {
+                               if (0x461 & (1 << (device->chipset & 0xf)))
+                                       device->card_type = NV_10;
+                               else
+                                       device->card_type = NV_11;
+                               break;
+                       }
+                       case 0x020: device->card_type = NV_20; break;
+                       case 0x030: device->card_type = NV_30; break;
+                       case 0x040:
+                       case 0x060: device->card_type = NV_40; break;
+                       case 0x050:
+                       case 0x080:
+                       case 0x090:
+                       case 0x0a0: device->card_type = NV_50; break;
+                       case 0x0c0: device->card_type = NV_C0; break;
+                       case 0x0d0: device->card_type = NV_D0; break;
+                       case 0x0e0:
+                       case 0x0f0:
+                       case 0x100: device->card_type = NV_E0; break;
                        default:
                                break;
                        }
@@ -188,7 +199,8 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
 
                switch (device->card_type) {
                case NV_04: ret = nv04_identify(device); break;
-               case NV_10: ret = nv10_identify(device); break;
+               case NV_10:
+               case NV_11: ret = nv10_identify(device); break;
                case NV_20: ret = nv20_identify(device); break;
                case NV_30: ret = nv30_identify(device); break;
                case NV_40: ret = nv40_identify(device); break;
@@ -212,7 +224,7 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
                nv_info(device, "Family : NV%02X\n", device->card_type);
 
                /* determine frequency of timing crystal */
-               if ( device->chipset < 0x17 ||
+               if ( device->card_type <= NV_10 || device->chipset < 0x17 ||
                    (device->chipset >= 0x20 && device->chipset < 0x25))
                        strap &= 0x00000040;
                else
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/ctrl.c b/drivers/gpu/drm/nouveau/core/engine/device/ctrl.c
new file mode 100644 (file)
index 0000000..4b69bf5
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <core/object.h>
+#include <core/class.h>
+
+#include <subdev/clock.h>
+
+#include "priv.h"
+
+static int
+nouveau_control_mthd_pstate_info(struct nouveau_object *object, u32 mthd,
+                               void *data, u32 size)
+{
+       struct nouveau_clock *clk = nouveau_clock(object);
+       struct nv_control_pstate_info *args = data;
+
+       if (size < sizeof(*args))
+               return -EINVAL;
+
+       if (clk) {
+               args->count  = clk->state_nr;
+               args->ustate = clk->ustate;
+               args->pstate = clk->pstate;
+       } else {
+               args->count  = 0;
+               args->ustate = NV_CONTROL_PSTATE_INFO_USTATE_DISABLE;
+               args->pstate = NV_CONTROL_PSTATE_INFO_PSTATE_UNKNOWN;
+       }
+
+       return 0;
+}
+
+static int
+nouveau_control_mthd_pstate_attr(struct nouveau_object *object, u32 mthd,
+                               void *data, u32 size)
+{
+       struct nouveau_clock *clk = nouveau_clock(object);
+       struct nv_control_pstate_attr *args = data;
+       struct nouveau_clocks *domain;
+       struct nouveau_pstate *pstate;
+       struct nouveau_cstate *cstate;
+       int i = 0, j = -1;
+       u32 lo, hi;
+
+       if ((size < sizeof(*args)) || !clk ||
+           (args->state >= 0 && args->state >= clk->state_nr))
+               return -EINVAL;
+       domain = clk->domains;
+
+       while (domain->name != nv_clk_src_max) {
+               if (domain->mname && ++j == args->index)
+                       break;
+               domain++;
+       }
+
+       if (domain->name == nv_clk_src_max)
+               return -EINVAL;
+
+       if (args->state != NV_CONTROL_PSTATE_ATTR_STATE_CURRENT) {
+               list_for_each_entry(pstate, &clk->states, head) {
+                       if (i++ == args->state)
+                               break;
+               }
+
+               lo = pstate->base.domain[domain->name];
+               hi = lo;
+               list_for_each_entry(cstate, &pstate->list, head) {
+                       lo = min(lo, cstate->domain[domain->name]);
+                       hi = max(hi, cstate->domain[domain->name]);
+               }
+
+               args->state = pstate->pstate;
+       } else {
+               lo = max(clk->read(clk, domain->name), 0);
+               hi = lo;
+       }
+
+       snprintf(args->name, sizeof(args->name), "%s", domain->mname);
+       snprintf(args->unit, sizeof(args->unit), "MHz");
+       args->min = lo / domain->mdiv;
+       args->max = hi / domain->mdiv;
+
+       args->index = 0;
+       while ((++domain)->name != nv_clk_src_max) {
+               if (domain->mname) {
+                       args->index = ++j;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int
+nouveau_control_mthd_pstate_user(struct nouveau_object *object, u32 mthd,
+                               void *data, u32 size)
+{
+       struct nouveau_clock *clk = nouveau_clock(object);
+       struct nv_control_pstate_user *args = data;
+
+       if (size < sizeof(*args) || !clk)
+               return -EINVAL;
+
+       return nouveau_clock_ustate(clk, args->state);
+}
+
+struct nouveau_oclass
+nouveau_control_oclass[] = {
+       { .handle = NV_CONTROL_CLASS,
+         .ofuncs = &nouveau_object_ofuncs,
+         .omthds = (struct nouveau_omthds[]) {
+                 { NV_CONTROL_PSTATE_INFO,
+                   NV_CONTROL_PSTATE_INFO, nouveau_control_mthd_pstate_info },
+                 { NV_CONTROL_PSTATE_ATTR,
+                   NV_CONTROL_PSTATE_ATTR, nouveau_control_mthd_pstate_attr },
+                 { NV_CONTROL_PSTATE_USER,
+                   NV_CONTROL_PSTATE_USER, nouveau_control_mthd_pstate_user },
+                 {},
+         },
+       },
+       {}
+};
index a0284cf..dbd2dde 100644 (file)
@@ -50,15 +50,15 @@ nv04_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv04_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv04_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv04_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv04_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv04_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv04_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv04_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv04_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -68,15 +68,15 @@ nv04_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv05_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv04_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv04_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv04_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv04_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv04_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv04_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv04_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
index 1b7809a..6e03dd6 100644 (file)
@@ -52,10 +52,10 @@ nv10_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
@@ -69,15 +69,15 @@ nv10_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv10_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv10_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv10_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -88,15 +88,15 @@ nv10_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv10_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv10_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv10_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -107,15 +107,15 @@ nv10_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv1a_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv1a_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv10_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv10_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv10_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -126,15 +126,15 @@ nv10_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv10_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv10_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv10_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -145,15 +145,15 @@ nv10_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv10_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -164,15 +164,15 @@ nv10_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv1a_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv1a_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv10_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -183,15 +183,15 @@ nv10_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv10_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
index 12a4005..dcde53b 100644 (file)
@@ -53,15 +53,15 @@ nv20_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv20_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv20_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv20_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -72,15 +72,15 @@ nv20_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv25_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv25_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv25_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -91,15 +91,15 @@ nv20_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv25_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv25_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv25_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -110,15 +110,15 @@ nv20_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv25_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv25_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv2a_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
index cef0f1e..7b8662e 100644 (file)
@@ -53,15 +53,15 @@ nv30_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv30_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv30_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv30_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -72,15 +72,15 @@ nv30_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv35_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv35_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv35_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
                break;
@@ -91,15 +91,15 @@ nv30_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv30_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv30_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv30_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv31_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
@@ -111,15 +111,15 @@ nv30_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv36_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv36_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv35_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv31_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
@@ -131,15 +131,15 @@ nv30_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv17_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv17_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv34_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv31_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
index 1719cb0..c8c41e9 100644 (file)
@@ -35,6 +35,7 @@
 #include <subdev/fb.h>
 #include <subdev/instmem.h>
 #include <subdev/vm.h>
+#include <subdev/volt.h>
 
 #include <engine/device.h>
 #include <engine/dmaobj.h>
@@ -43,6 +44,7 @@
 #include <engine/graph.h>
 #include <engine/mpeg.h>
 #include <engine/disp.h>
+#include <engine/perfmon.h>
 
 int
 nv40_identify(struct nouveau_device *device)
@@ -56,18 +58,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv40_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv40_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x41:
                device->cname = "NV41";
@@ -77,18 +81,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv40_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv41_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv41_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv41_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x42:
                device->cname = "NV42";
@@ -98,18 +104,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv40_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv41_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv41_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv41_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x43:
                device->cname = "NV43";
@@ -119,18 +127,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv40_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv41_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv41_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv41_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x45:
                device->cname = "NV45";
@@ -140,18 +150,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv40_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv40_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv04_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x47:
                device->cname = "G70";
@@ -161,18 +173,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv40_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv47_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv47_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv41_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x49:
                device->cname = "G71";
@@ -182,18 +196,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv40_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv49_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv49_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv41_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x4b:
                device->cname = "G73";
@@ -203,18 +219,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv40_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv49_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv49_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv41_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x44:
                device->cname = "NV44";
@@ -224,18 +242,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv44_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv44_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv44_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x46:
                device->cname = "G72";
@@ -245,18 +265,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv44_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x4a:
                device->cname = "NV44A";
@@ -266,18 +288,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv44_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv44_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv44_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x4c:
                device->cname = "C61";
@@ -287,18 +311,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv44_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x4e:
                device->cname = "C51";
@@ -308,18 +334,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv4e_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv4e_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv44_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x63:
                device->cname = "C73";
@@ -329,18 +357,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv44_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x67:
                device->cname = "C67";
@@ -350,18 +380,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv44_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        case 0x68:
                device->cname = "C68";
@@ -371,18 +403,20 @@ nv40_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv44_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv40_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv10_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv40_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv10_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv40_graph_oclass;
-               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv40_mpeg_oclass;
+               device->oclass[NVDEV_ENGINE_MPEG   ] = &nv44_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv04_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv40_perfmon_oclass;
                break;
        default:
                nv_fatal(device, "unknown Curie chipset\n");
index ffc18b8..db13982 100644 (file)
@@ -36,6 +36,8 @@
 #include <subdev/instmem.h>
 #include <subdev/vm.h>
 #include <subdev/bar.h>
+#include <subdev/pwr.h>
+#include <subdev/volt.h>
 
 #include <engine/device.h>
 #include <engine/dmaobj.h>
@@ -49,6 +51,7 @@
 #include <engine/ppp.h>
 #include <engine/copy.h>
 #include <engine/disp.h>
+#include <engine/perfmon.h>
 
 int
 nv50_identify(struct nouveau_device *device)
@@ -59,257 +62,277 @@ nv50_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv50_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv50_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv50_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv50_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv50_perfmon_oclass;
                break;
        case 0x84:
                device->cname = "G84";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv84_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv84_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv84_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv84_crypt_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv84_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv84_perfmon_oclass;
                break;
        case 0x86:
                device->cname = "G86";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv84_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv84_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv84_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv84_crypt_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv84_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv84_perfmon_oclass;
                break;
        case 0x92:
                device->cname = "G92";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv84_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv84_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv84_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv84_crypt_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv84_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv84_perfmon_oclass;
                break;
        case 0x94:
                device->cname = "G94";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv94_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv94_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv84_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv84_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv84_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv84_crypt_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv94_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv84_perfmon_oclass;
                break;
        case 0x96:
                device->cname = "G96";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv94_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv94_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv84_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv84_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv84_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv84_crypt_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv94_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv84_perfmon_oclass;
                break;
        case 0x98:
                device->cname = "G98";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv94_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv84_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv98_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv98_crypt_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv98_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nv98_ppp_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv94_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv84_perfmon_oclass;
                break;
        case 0xa0:
                device->cname = "G200";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv94_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nv84_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv84_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv84_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv84_crypt_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva0_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv84_perfmon_oclass;
                break;
        case 0xaa:
                device->cname = "MCP77/MCP78";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv94_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvaa_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv98_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv98_crypt_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv98_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nv98_ppp_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv94_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv84_perfmon_oclass;
                break;
        case 0xac:
                device->cname = "MCP79/MCP7A";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv94_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvaa_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv98_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv98_crypt_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv98_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nv98_ppp_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nv94_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nv84_perfmon_oclass;
                break;
        case 0xa3:
                device->cname = "GT215";
@@ -320,16 +343,18 @@ nv50_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv94_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nva3_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nva3_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_MPEG   ] = &nv84_mpeg_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv98_vp_oclass;
@@ -337,6 +362,7 @@ nv50_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_PPP    ] = &nv98_ppp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nva3_copy_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nva3_perfmon_oclass;
                break;
        case 0xa5:
                device->cname = "GT216";
@@ -347,22 +373,25 @@ nv50_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv94_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nva3_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nva3_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv98_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv98_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nv98_ppp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nva3_copy_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nva3_perfmon_oclass;
                break;
        case 0xa8:
                device->cname = "GT218";
@@ -373,22 +402,25 @@ nv50_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv94_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nva3_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nva3_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv98_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv98_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nv98_ppp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nva3_copy_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nva3_perfmon_oclass;
                break;
        case 0xaf:
                device->cname = "MCP89";
@@ -399,22 +431,25 @@ nv50_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nv94_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvaf_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nv50_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nv50_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nva3_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nv84_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nv50_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nv84_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nv50_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] = &nv50_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nv98_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nv98_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nv98_ppp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nva3_copy_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] =  nva3_perfmon_oclass;
                break;
        default:
                nv_fatal(device, "unknown Tesla chipset\n");
index 418f51f..8d06eef 100644 (file)
@@ -38,6 +38,8 @@
 #include <subdev/instmem.h>
 #include <subdev/vm.h>
 #include <subdev/bar.h>
+#include <subdev/pwr.h>
+#include <subdev/volt.h>
 
 #include <engine/device.h>
 #include <engine/dmaobj.h>
@@ -49,6 +51,7 @@
 #include <engine/ppp.h>
 #include <engine/copy.h>
 #include <engine/disp.h>
+#include <engine/perfmon.h>
 
 int
 nvc0_identify(struct nouveau_device *device)
@@ -63,18 +66,20 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nvc0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvc0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nvc0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nvc0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nvc0_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nvc0_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;
@@ -82,6 +87,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;
                device->oclass[NVDEV_ENGINE_COPY1  ] = &nvc0_copy1_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
                break;
        case 0xc4:
                device->cname = "GF104";
@@ -92,18 +98,20 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nvc0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvc0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nvc0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nvc0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nvc3_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nvc0_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;
@@ -111,6 +119,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;
                device->oclass[NVDEV_ENGINE_COPY1  ] = &nvc0_copy1_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
                break;
        case 0xc3:
                device->cname = "GF106";
@@ -121,24 +130,27 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nvc0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvc0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nvc0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nvc0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nvc3_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nvc0_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
                break;
        case 0xce:
                device->cname = "GF114";
@@ -149,18 +161,20 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nvc0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvc0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nvc0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nvc0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nvc3_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nvc0_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;
@@ -168,6 +182,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;
                device->oclass[NVDEV_ENGINE_COPY1  ] = &nvc0_copy1_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
                break;
        case 0xcf:
                device->cname = "GF116";
@@ -178,18 +193,20 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nvc0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvc0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nvc0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nvc0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nvc3_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nvc0_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;
@@ -197,6 +214,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;
                device->oclass[NVDEV_ENGINE_COPY1  ] = &nvc0_copy1_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
                break;
        case 0xc1:
                device->cname = "GF108";
@@ -207,24 +225,27 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nvc0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvc0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nvc0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nvc0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nvc1_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nvc0_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
                break;
        case 0xc8:
                device->cname = "GF110";
@@ -235,18 +256,20 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nvc0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvc0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nvc0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nvc0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nvc8_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nvc0_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;
@@ -254,6 +277,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;
                device->oclass[NVDEV_ENGINE_COPY1  ] = &nvc0_copy1_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nva3_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
                break;
        case 0xd9:
                device->cname = "GF119";
@@ -264,24 +288,27 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nvc0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvd0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nvc0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nvc0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nvd9_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nvc0_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nvd0_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
                break;
        case 0xd7:
                device->cname = "GF117";
@@ -292,24 +319,25 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nvc0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nvc0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nvc0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nvd7_graph_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nvc0_vp_oclass;
                device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nvd0_disp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
                break;
        default:
                nv_fatal(device, "unknown Fermi chipset\n");
index 7aca187..3900104 100644 (file)
@@ -38,6 +38,8 @@
 #include <subdev/instmem.h>
 #include <subdev/vm.h>
 #include <subdev/bar.h>
+#include <subdev/pwr.h>
+#include <subdev/volt.h>
 
 #include <engine/device.h>
 #include <engine/dmaobj.h>
@@ -49,6 +51,7 @@
 #include <engine/bsp.h>
 #include <engine/vp.h>
 #include <engine/ppp.h>
+#include <engine/perfmon.h>
 
 int
 nve0_identify(struct nouveau_device *device)
@@ -59,22 +62,24 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nve0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nve0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvd0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nve0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nve0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nve4_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nve0_disp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nve0_copy0_oclass;
@@ -83,28 +88,31 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_BSP    ] = &nve0_bsp_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nve0_vp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nve0_perfmon_oclass;
                break;
        case 0xe7:
                device->cname = "GK107";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nve0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nve0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvd0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nve0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nve0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nve4_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nve0_disp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nve0_copy0_oclass;
@@ -113,28 +121,31 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_BSP    ] = &nve0_bsp_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nve0_vp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nve0_perfmon_oclass;
                break;
        case 0xe6:
                device->cname = "GK106";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nve0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nve0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvd0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nve0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nve0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nve4_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nve0_disp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nve0_copy0_oclass;
@@ -143,28 +154,31 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_BSP    ] = &nve0_bsp_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nve0_vp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nve0_perfmon_oclass;
                break;
        case 0xf0:
                device->cname = "GK110";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
                device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
-               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
-               device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
-               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
-               device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nve0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
                device->oclass[NVDEV_SUBDEV_IBUS   ] = &nve0_ibus_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvd0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
                device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
-               device->oclass[NVDEV_ENGINE_FIFO   ] = &nve0_fifo_oclass;
-               device->oclass[NVDEV_ENGINE_SW     ] = &nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nve0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
                device->oclass[NVDEV_ENGINE_GR     ] =  nvf0_graph_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] = &nvf0_disp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nve0_copy0_oclass;
@@ -175,6 +189,43 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_VP     ] = &nve0_vp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
 #endif
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvf0_perfmon_oclass;
+               break;
+       case 0x108:
+               device->cname = "GK208";
+               device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nve0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
+               device->oclass[NVDEV_SUBDEV_IBUS   ] = &nve0_ibus_oclass;
+               device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
+               device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nv108_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
+               device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+#if 0
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nve0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_GR     ] =  nvf0_graph_oclass;
+#endif
+               device->oclass[NVDEV_ENGINE_DISP   ] = &nvf0_disp_oclass;
+#if 0
+               device->oclass[NVDEV_ENGINE_COPY0  ] = &nve0_copy0_oclass;
+               device->oclass[NVDEV_ENGINE_COPY1  ] = &nve0_copy1_oclass;
+               device->oclass[NVDEV_ENGINE_COPY2  ] = &nve0_copy2_oclass;
+               device->oclass[NVDEV_ENGINE_BSP    ] = &nve0_bsp_oclass;
+               device->oclass[NVDEV_ENGINE_VP     ] = &nve0_vp_oclass;
+               device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
+#endif
                break;
        default:
                nv_fatal(device, "unknown Kepler chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/priv.h b/drivers/gpu/drm/nouveau/core/engine/device/priv.h
new file mode 100644 (file)
index 0000000..035fd5b
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __NVKM_DEVICE_PRIV_H__
+#define __NVKM_DEVICE_PRIV_H__
+
+#include <engine/device.h>
+
+extern struct nouveau_oclass nouveau_control_oclass[];
+
+#endif
index 054d9cf..1bd4c63 100644 (file)
@@ -70,17 +70,10 @@ dp_set_link_config(struct dp_state *dp)
        };
        u32 lnkcmp;
        u8 sink[2];
+       int ret;
 
        DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
 
-       /* set desired link configuration on the sink */
-       sink[0] = dp->link_bw / 27000;
-       sink[1] = dp->link_nr;
-       if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
-               sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
-
-       nv_wraux(dp->aux, DPCD_LC00, sink, 2);
-
        /* set desired link configuration on the source */
        if ((lnkcmp = dp->info.lnkcmp)) {
                if (dp->version < 0x30) {
@@ -96,10 +89,22 @@ dp_set_link_config(struct dp_state *dp)
                nvbios_exec(&init);
        }
 
-       return dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
-                                dp->link_nr, dp->link_bw / 27000,
-                                dp->dpcd[DPCD_RC02] &
-                                         DPCD_RC02_ENHANCED_FRAME_CAP);
+       ret = dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
+                               dp->link_nr, dp->link_bw / 27000,
+                               dp->dpcd[DPCD_RC02] &
+                                        DPCD_RC02_ENHANCED_FRAME_CAP);
+       if (ret) {
+               ERR("lnk_ctl failed with %d\n", ret);
+               return ret;
+       }
+
+       /* set desired link configuration on the sink */
+       sink[0] = dp->link_bw / 27000;
+       sink[1] = dp->link_nr;
+       if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
+               sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
+
+       return nv_wraux(dp->aux, DPCD_LC00, sink, 2);
 }
 
 static void
@@ -294,8 +299,17 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
 
        ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd));
        if (ret) {
+               /* it's possible the display has been unplugged before we
+                * get here.  we still need to execute the full set of
+                * vbios scripts, and program the OR at a high enough
+                * frequency to satisfy the target mode.  failure to do
+                * so results at best in an UPDATE hanging, and at worst
+                * with PDISP running away to join the circus.
+                */
+               dp->dpcd[1] = link_bw[0] / 27000;
+               dp->dpcd[2] = 4;
+               dp->dpcd[3] = 0x00;
                ERR("failed to read DPCD\n");
-               return ret;
        }
 
        /* adjust required bandwidth for 8B/10B coding overhead */
@@ -308,7 +322,7 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
        while (*link_bw > (dp->dpcd[1] * 27000))
                link_bw++;
 
-       while (link_bw[0]) {
+       while ((ret = -EIO) && link_bw[0]) {
                /* find minimum required lane count at this link rate */
                dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT;
                while ((dp->link_nr >> 1) * link_bw[0] > datarate)
@@ -328,8 +342,10 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
                            !dp_link_train_eq(dp))
                                break;
                } else
-               if (ret >= 1) {
-                       /* dp_set_link_config() handled training */
+               if (ret) {
+                       /* dp_set_link_config() handled training, or
+                        * we failed to communicate with the sink.
+                        */
                        break;
                }
 
@@ -339,8 +355,10 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
 
        /* finish link training */
        dp_set_training_pattern(dp, 0);
+       if (ret < 0)
+               ERR("link training failed\n");
 
        /* execute post-train script from vbios */
        dp_link_train_fini(dp);
-       return true;
+       return (ret < 0) ? false : true;
 }
index 05e903f..a0bc8a8 100644 (file)
@@ -59,6 +59,7 @@ nv04_disp_intr(struct nouveau_subdev *subdev)
        struct nv04_disp_priv *priv = (void *)subdev;
        u32 crtc0 = nv_rd32(priv, 0x600100);
        u32 crtc1 = nv_rd32(priv, 0x602100);
+       u32 pvideo;
 
        if (crtc0 & 0x00000001) {
                nouveau_event_trigger(priv->base.vblank, 0);
@@ -69,6 +70,14 @@ nv04_disp_intr(struct nouveau_subdev *subdev)
                nouveau_event_trigger(priv->base.vblank, 1);
                nv_wr32(priv, 0x602100, 0x00000001);
        }
+
+       if (nv_device(priv)->chipset >= 0x10 &&
+           nv_device(priv)->chipset <= 0x40) {
+               pvideo = nv_rd32(priv, 0x8100);
+               if (pvideo & ~0x11)
+                       nv_info(priv, "PVIDEO intr: %08x\n", pvideo);
+               nv_wr32(priv, 0x8100, pvideo);
+       }
 }
 
 static int
index 52dd7a1..378a015 100644 (file)
@@ -541,6 +541,15 @@ nvd0_disp_base_init(struct nouveau_object *object)
        nv_wr32(priv, 0x6100a0, 0x00000000);
        nv_wr32(priv, 0x6100b0, 0x00000307);
 
+       /* disable underflow reporting, preventing an intermittent issue
+        * on some nve4 boards where the production vbios left this
+        * setting enabled by default.
+        *
+        * ftp://download.nvidia.com/open-gpu-doc/gk104-disable-underflow-reporting/1/gk104-disable-underflow-reporting.txt
+        */
+       for (i = 0; i < priv->head.nr; i++)
+               nv_mask(priv, 0x616308 + (i * 0x800), 0x00000111, 0x00000010);
+
        return 0;
 }
 
index 7ec4ee8..eea3ef5 100644 (file)
@@ -97,8 +97,9 @@ nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
 {
        struct nouveau_bios *bios = nouveau_bios(disp);
        struct nv50_disp_priv *priv = (void *)disp;
+       const u32 shift = nv94_sor_dp_lane_map(priv, lane);
        const u32 loff = nv94_sor_loff(outp);
-       u32 addr, shift = nv94_sor_dp_lane_map(priv, lane);
+       u32 addr, data[3];
        u8  ver, hdr, cnt, len;
        struct nvbios_dpout info;
        struct nvbios_dpcfg ocfg;
@@ -113,9 +114,12 @@ nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
        if (!addr)
                return -EINVAL;
 
-       nv_mask(priv, 0x61c118 + loff, 0x000000ff << shift, ocfg.drv << shift);
-       nv_mask(priv, 0x61c120 + loff, 0x000000ff << shift, ocfg.pre << shift);
-       nv_mask(priv, 0x61c130 + loff, 0x0000ff00, ocfg.unk << 8);
+       data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift);
+       data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift);
+       data[2] = nv_rd32(priv, 0x61c130 + loff) & ~(0x0000ff00);
+       nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.drv << shift));
+       nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pre << shift));
+       nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.unk << 8));
        return 0;
 }
 
index 9e1d435..d2df572 100644 (file)
@@ -93,8 +93,9 @@ nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
 {
        struct nouveau_bios *bios = nouveau_bios(disp);
        struct nv50_disp_priv *priv = (void *)disp;
+       const u32 shift = nvd0_sor_dp_lane_map(priv, lane);
        const u32 loff = nvd0_sor_loff(outp);
-       u32 addr, shift = nvd0_sor_dp_lane_map(priv, lane);
+       u32 addr, data[3];
        u8  ver, hdr, cnt, len;
        struct nvbios_dpout info;
        struct nvbios_dpcfg ocfg;
@@ -109,9 +110,12 @@ nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
        if (!addr)
                return -EINVAL;
 
-       nv_mask(priv, 0x61c118 + loff, 0x000000ff << shift, ocfg.drv << shift);
-       nv_mask(priv, 0x61c120 + loff, 0x000000ff << shift, ocfg.pre << shift);
-       nv_mask(priv, 0x61c130 + loff, 0x0000ff00, ocfg.unk << 8);
+       data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift);
+       data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift);
+       data[2] = nv_rd32(priv, 0x61c130 + loff) & ~(0x0000ff00);
+       nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.drv << shift));
+       nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pre << shift));
+       nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.unk << 8));
        nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000);
        return 0;
 }
index f877bd5..54f26cc 100644 (file)
@@ -632,8 +632,8 @@ nv04_fifo_init(struct nouveau_object *object)
        return 0;
 }
 
-struct nouveau_oclass
-nv04_fifo_oclass = {
+struct nouveau_oclass *
+nv04_fifo_oclass = &(struct nouveau_oclass) {
        .handle = NV_ENGINE(FIFO, 0x04),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv04_fifo_ctor,
index 2c927c1..571a22a 100644 (file)
@@ -159,8 +159,8 @@ nv10_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        return 0;
 }
 
-struct nouveau_oclass
-nv10_fifo_oclass = {
+struct nouveau_oclass *
+nv10_fifo_oclass = &(struct nouveau_oclass) {
        .handle = NV_ENGINE(FIFO, 0x10),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv10_fifo_ctor,
index a9cb51d..f257602 100644 (file)
@@ -196,8 +196,8 @@ nv17_fifo_init(struct nouveau_object *object)
        return 0;
 }
 
-struct nouveau_oclass
-nv17_fifo_oclass = {
+struct nouveau_oclass *
+nv17_fifo_oclass = &(struct nouveau_oclass) {
        .handle = NV_ENGINE(FIFO, 0x17),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv17_fifo_ctor,
index 5c7433d..343487e 100644 (file)
@@ -337,8 +337,8 @@ nv40_fifo_init(struct nouveau_object *object)
        return 0;
 }
 
-struct nouveau_oclass
-nv40_fifo_oclass = {
+struct nouveau_oclass *
+nv40_fifo_oclass = &(struct nouveau_oclass) {
        .handle = NV_ENGINE(FIFO, 0x40),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv40_fifo_ctor,
index 7e5dff5..5f55578 100644 (file)
@@ -502,8 +502,8 @@ nv50_fifo_init(struct nouveau_object *object)
        return 0;
 }
 
-struct nouveau_oclass
-nv50_fifo_oclass = {
+struct nouveau_oclass *
+nv50_fifo_oclass = &(struct nouveau_oclass) {
        .handle = NV_ENGINE(FIFO, 0x50),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv50_fifo_ctor,
index 91a87cd..0908dc8 100644 (file)
@@ -144,7 +144,7 @@ nv84_fifo_object_attach(struct nouveau_object *parent,
        case NVDEV_ENGINE_COPY0 : context |= 0x00300000; break;
        case NVDEV_ENGINE_VP    : context |= 0x00400000; break;
        case NVDEV_ENGINE_CRYPT :
-       case NVDEV_ENGINE_UNK1C1: context |= 0x00500000; break;
+       case NVDEV_ENGINE_VIC   : context |= 0x00500000; break;
        case NVDEV_ENGINE_BSP   : context |= 0x00600000; break;
        default:
                return -EINVAL;
@@ -180,7 +180,7 @@ nv84_fifo_chan_ctor_dma(struct nouveau_object *parent,
                                          (1ULL << NVDEV_ENGINE_BSP) |
                                          (1ULL << NVDEV_ENGINE_PPP) |
                                          (1ULL << NVDEV_ENGINE_COPY0) |
-                                         (1ULL << NVDEV_ENGINE_UNK1C1), &chan);
+                                         (1ULL << NVDEV_ENGINE_VIC), &chan);
        *pobject = nv_object(chan);
        if (ret)
                return ret;
@@ -243,7 +243,7 @@ nv84_fifo_chan_ctor_ind(struct nouveau_object *parent,
                                          (1ULL << NVDEV_ENGINE_BSP) |
                                          (1ULL << NVDEV_ENGINE_PPP) |
                                          (1ULL << NVDEV_ENGINE_COPY0) |
-                                         (1ULL << NVDEV_ENGINE_UNK1C1), &chan);
+                                         (1ULL << NVDEV_ENGINE_VIC), &chan);
        *pobject = nv_object(chan);
        if (ret)
                return ret;
@@ -435,8 +435,8 @@ nv84_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        return 0;
 }
 
-struct nouveau_oclass
-nv84_fifo_oclass = {
+struct nouveau_oclass *
+nv84_fifo_oclass = &(struct nouveau_oclass) {
        .handle = NV_ENGINE(FIFO, 0x84),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv84_fifo_ctor,
index ce92f28..9ac94d4 100644 (file)
@@ -494,13 +494,6 @@ nvc0_fifo_isr_subfifo_intr(struct nvc0_fifo_priv *priv, int unit)
        u32 mthd = (addr & 0x00003ffc);
        u32 show = stat;
 
-       if (stat & 0x00200000) {
-               if (mthd == 0x0054) {
-                       if (!nvc0_fifo_swmthd(priv, chid, 0x0500, 0x00000000))
-                               show &= ~0x00200000;
-               }
-       }
-
        if (stat & 0x00800000) {
                if (!nvc0_fifo_swmthd(priv, chid, mthd, data))
                        show &= ~0x00800000;
@@ -720,8 +713,8 @@ nvc0_fifo_init(struct nouveau_object *object)
        return 0;
 }
 
-struct nouveau_oclass
-nvc0_fifo_oclass = {
+struct nouveau_oclass *
+nvc0_fifo_oclass = &(struct nouveau_oclass) {
        .handle = NV_ENGINE(FIFO, 0xc0),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nvc0_fifo_ctor,
index 8e8121a..04f4129 100644 (file)
@@ -481,13 +481,6 @@ nve0_fifo_isr_subfifo_intr(struct nve0_fifo_priv *priv, int unit)
        u32 mthd = (addr & 0x00003ffc);
        u32 show = stat;
 
-       if (stat & 0x00200000) {
-               if (mthd == 0x0054) {
-                       if (!nve0_fifo_swmthd(priv, chid, 0x0500, 0x00000000))
-                               show &= ~0x00200000;
-               }
-       }
-
        if (stat & 0x00800000) {
                if (!nve0_fifo_swmthd(priv, chid, mthd, data))
                        show &= ~0x00800000;
@@ -675,8 +668,8 @@ nve0_fifo_init(struct nouveau_object *object)
        return 0;
 }
 
-struct nouveau_oclass
-nve0_fifo_oclass = {
+struct nouveau_oclass *
+nve0_fifo_oclass = &(struct nouveau_oclass) {
        .handle = NV_ENGINE(FIFO, 0xe0),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nve0_fifo_ctor,
index 64dca26..fe67415 100644 (file)
@@ -1039,7 +1039,7 @@ nvc0_grctx_generate_r406800(struct nvc0_graph_priv *priv)
                        } while (!tpcnr[gpc]);
                        tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--;
 
-                       tpc_set |= 1 << ((gpc * 8) + tpc);
+                       tpc_set |= 1ULL << ((gpc * 8) + tpc);
                }
 
                nv_wr32(priv, 0x406800 + (i * 0x20), lower_32_bits(tpc_set));
index e5be3ee..71b4283 100644 (file)
@@ -587,6 +587,7 @@ nvc1_grctx_init_unk58xx[] = {
        { 0x405870,   4, 0x04, 0x00000001 },
        { 0x405a00,   2, 0x04, 0x00000000 },
        { 0x405a18,   1, 0x04, 0x00000000 },
+       {}
 };
 
 static struct nvc0_graph_init
@@ -598,6 +599,7 @@ nvc1_grctx_init_rop[] = {
        { 0x408904,   1, 0x04, 0x62000001 },
        { 0x408908,   1, 0x04, 0x00c80929 },
        { 0x408980,   1, 0x04, 0x0000011d },
+       {}
 };
 
 static struct nvc0_graph_init
@@ -671,6 +673,7 @@ nvc1_grctx_init_gpc_0[] = {
        { 0x419000,   1, 0x04, 0x00000780 },
        { 0x419004,   2, 0x04, 0x00000000 },
        { 0x419014,   1, 0x04, 0x00000004 },
+       {}
 };
 
 static struct nvc0_graph_init
@@ -717,6 +720,7 @@ nvc1_grctx_init_tpc[] = {
        { 0x419e98,   1, 0x04, 0x00000000 },
        { 0x419ee0,   1, 0x04, 0x00011110 },
        { 0x419f30,  11, 0x04, 0x00000000 },
+       {}
 };
 
 void
index 438e784..c4740d5 100644 (file)
@@ -258,6 +258,7 @@ nvd7_grctx_init_hub[] = {
        nvc0_grctx_init_unk78xx,
        nvc0_grctx_init_unk80xx,
        nvd9_grctx_init_rop,
+       NULL
 };
 
 struct nvc0_graph_init *
index 818a475..a1102cb 100644 (file)
@@ -466,6 +466,7 @@ nvd9_grctx_init_hub[] = {
        nvc0_grctx_init_unk78xx,
        nvc0_grctx_init_unk80xx,
        nvd9_grctx_init_rop,
+       NULL
 };
 
 struct nvc0_graph_init *
index 23c143a..4532f7e 100644 (file)
@@ -945,7 +945,8 @@ nv10_graph_load_context(struct nv10_graph_chan *chan, int chid)
        for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
                nv_wr32(priv, nv10_graph_ctx_regs[i], chan->nv10[i]);
 
-       if (nv_device(priv)->chipset >= 0x17) {
+       if (nv_device(priv)->card_type >= NV_11 &&
+           nv_device(priv)->chipset >= 0x17) {
                for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++)
                        nv_wr32(priv, nv17_graph_ctx_regs[i], chan->nv17[i]);
        }
@@ -970,7 +971,8 @@ nv10_graph_unload_context(struct nv10_graph_chan *chan)
        for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
                chan->nv10[i] = nv_rd32(priv, nv10_graph_ctx_regs[i]);
 
-       if (nv_device(priv)->chipset >= 0x17) {
+       if (nv_device(priv)->card_type >= NV_11 &&
+           nv_device(priv)->chipset >= 0x17) {
                for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++)
                        chan->nv17[i] = nv_rd32(priv, nv17_graph_ctx_regs[i]);
        }
@@ -1052,7 +1054,8 @@ nv10_graph_context_ctor(struct nouveau_object *parent,
        NV_WRITE_CTX(0x00400e14, 0x00001000);
        NV_WRITE_CTX(0x00400e30, 0x00080008);
        NV_WRITE_CTX(0x00400e34, 0x00080008);
-       if (nv_device(priv)->chipset >= 0x17) {
+       if (nv_device(priv)->card_type >= NV_11 &&
+           nv_device(priv)->chipset >= 0x17) {
                /* is it really needed ??? */
                NV17_WRITE_CTX(NV10_PGRAPH_DEBUG_4,
                                        nv_rd32(priv, NV10_PGRAPH_DEBUG_4));
@@ -1231,7 +1234,7 @@ nv10_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                nv_engine(priv)->sclass = nv10_graph_sclass;
        else
        if (nv_device(priv)->chipset <  0x17 ||
-           nv_device(priv)->chipset == 0x1a)
+           nv_device(priv)->card_type < NV_11)
                nv_engine(priv)->sclass = nv15_graph_sclass;
        else
                nv_engine(priv)->sclass = nv17_graph_sclass;
@@ -1270,7 +1273,8 @@ nv10_graph_init(struct nouveau_object *object)
        nv_wr32(priv, NV04_PGRAPH_DEBUG_2, 0x25f92ad9);
        nv_wr32(priv, NV04_PGRAPH_DEBUG_3, 0x55DE0830 | (1 << 29) | (1 << 31));
 
-       if (nv_device(priv)->chipset >= 0x17) {
+       if (nv_device(priv)->card_type >= NV_11 &&
+           nv_device(priv)->chipset >= 0x17) {
                nv_wr32(priv, NV10_PGRAPH_DEBUG_4, 0x1f000000);
                nv_wr32(priv, 0x400a10, 0x03ff3fb6);
                nv_wr32(priv, 0x400838, 0x002f8684);
index 3f4f35c..434bb4b 100644 (file)
@@ -1138,7 +1138,7 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       nv_subdev(priv)->unit = 0x18001000;
+       nv_subdev(priv)->unit = 0x08001000;
        nv_subdev(priv)->intr = nvc0_graph_intr;
 
        priv->base.units = nvc0_graph_units;
index c190043..7eb6d94 100644 (file)
 
 #include <engine/fifo.h>
 #include <engine/mpeg.h>
-#include <engine/graph/nv40.h>
-
-struct nv31_mpeg_priv {
-       struct nouveau_mpeg base;
-       atomic_t refcount;
-};
-
-struct nv31_mpeg_chan {
-       struct nouveau_object base;
-};
+#include <engine/mpeg/nv31.h>
 
 /*******************************************************************************
  * MPEG object classes
@@ -89,18 +80,18 @@ nv31_mpeg_mthd_dma(struct nouveau_object *object, u32 mthd, void *arg, u32 len)
 
        if (mthd == 0x0190) {
                /* DMA_CMD */
-               nv_mask(priv, 0x00b300, 0x00030000, (dma0 & 0x00030000));
+               nv_mask(priv, 0x00b300, 0x00010000, (dma0 & 0x00030000) ? 0x00010000 : 0);
                nv_wr32(priv, 0x00b334, base);
                nv_wr32(priv, 0x00b324, size);
        } else
        if (mthd == 0x01a0) {
                /* DMA_DATA */
-               nv_mask(priv, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2);
+               nv_mask(priv, 0x00b300, 0x00020000, (dma0 & 0x00030000) ? 0x00020000 : 0);
                nv_wr32(priv, 0x00b360, base);
                nv_wr32(priv, 0x00b364, size);
        } else {
                /* DMA_IMAGE, VRAM only */
-               if (dma0 & 0x000c0000)
+               if (dma0 & 0x00030000)
                        return -EINVAL;
 
                nv_wr32(priv, 0x00b370, base);
@@ -110,7 +101,7 @@ nv31_mpeg_mthd_dma(struct nouveau_object *object, u32 mthd, void *arg, u32 len)
        return 0;
 }
 
-static struct nouveau_ofuncs
+struct nouveau_ofuncs
 nv31_mpeg_ofuncs = {
        .ctor = nv31_mpeg_object_ctor,
        .dtor = _nouveau_gpuobj_dtor,
@@ -146,16 +137,23 @@ nv31_mpeg_context_ctor(struct nouveau_object *parent,
 {
        struct nv31_mpeg_priv *priv = (void *)engine;
        struct nv31_mpeg_chan *chan;
+       unsigned long flags;
        int ret;
 
-       if (!atomic_add_unless(&priv->refcount, 1, 1))
-               return -EBUSY;
-
        ret = nouveau_object_create(parent, engine, oclass, 0, &chan);
        *pobject = nv_object(chan);
        if (ret)
                return ret;
 
+       spin_lock_irqsave(&nv_engine(priv)->lock, flags);
+       if (priv->chan) {
+               spin_unlock_irqrestore(&nv_engine(priv)->lock, flags);
+               nouveau_object_destroy(&chan->base);
+               *pobject = NULL;
+               return -EBUSY;
+       }
+       priv->chan = chan;
+       spin_unlock_irqrestore(&nv_engine(priv)->lock, flags);
        return 0;
 }
 
@@ -164,11 +162,15 @@ nv31_mpeg_context_dtor(struct nouveau_object *object)
 {
        struct nv31_mpeg_priv *priv = (void *)object->engine;
        struct nv31_mpeg_chan *chan = (void *)object;
-       atomic_dec(&priv->refcount);
+       unsigned long flags;
+
+       spin_lock_irqsave(&nv_engine(priv)->lock, flags);
+       priv->chan = NULL;
+       spin_unlock_irqrestore(&nv_engine(priv)->lock, flags);
        nouveau_object_destroy(&chan->base);
 }
 
-static struct nouveau_oclass
+struct nouveau_oclass
 nv31_mpeg_cclass = {
        .handle = NV_ENGCTX(MPEG, 0x31),
        .ofuncs = &(struct nouveau_ofuncs) {
@@ -197,21 +199,19 @@ nv31_mpeg_tile_prog(struct nouveau_engine *engine, int i)
 void
 nv31_mpeg_intr(struct nouveau_subdev *subdev)
 {
+       struct nv31_mpeg_priv *priv = (void *)subdev;
        struct nouveau_fifo *pfifo = nouveau_fifo(subdev);
-       struct nouveau_engine *engine = nv_engine(subdev);
-       struct nouveau_object *engctx;
        struct nouveau_handle *handle;
-       struct nv31_mpeg_priv *priv = (void *)subdev;
-       u32 inst = nv_rd32(priv, 0x00b318) & 0x000fffff;
+       struct nouveau_object *engctx;
        u32 stat = nv_rd32(priv, 0x00b100);
        u32 type = nv_rd32(priv, 0x00b230);
        u32 mthd = nv_rd32(priv, 0x00b234);
        u32 data = nv_rd32(priv, 0x00b238);
        u32 show = stat;
-       int chid;
+       unsigned long flags;
 
-       engctx = nouveau_engctx_get(engine, inst);
-       chid   = pfifo->chid(pfifo, engctx);
+       spin_lock_irqsave(&nv_engine(priv)->lock, flags);
+       engctx = nv_object(priv->chan);
 
        if (stat & 0x01000000) {
                /* happens on initial binding of the object */
@@ -220,7 +220,7 @@ nv31_mpeg_intr(struct nouveau_subdev *subdev)
                        show &= ~0x01000000;
                }
 
-               if (type == 0x00000010) {
+               if (type == 0x00000010 && engctx) {
                        handle = nouveau_handle_get_class(engctx, 0x3174);
                        if (handle && !nv_call(handle->object, mthd, data))
                                show &= ~0x01000000;
@@ -232,13 +232,12 @@ nv31_mpeg_intr(struct nouveau_subdev *subdev)
        nv_wr32(priv, 0x00b230, 0x00000001);
 
        if (show) {
-               nv_error(priv,
-                        "ch %d [0x%08x %s] 0x%08x 0x%08x 0x%08x 0x%08x\n",
-                        chid, inst << 4, nouveau_client_name(engctx), stat,
-                        type, mthd, data);
+               nv_error(priv, "ch %d [%s] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                        pfifo->chid(pfifo, engctx),
+                        nouveau_client_name(engctx), stat, type, mthd, data);
        }
 
-       nouveau_engctx_put(engctx);
+       spin_unlock_irqrestore(&nv_engine(priv)->lock, flags);
 }
 
 static int
@@ -284,10 +283,7 @@ nv31_mpeg_init(struct nouveau_object *object)
        /* PMPEG init */
        nv_wr32(priv, 0x00b32c, 0x00000000);
        nv_wr32(priv, 0x00b314, 0x00000100);
-       if (nv_device(priv)->chipset >= 0x40 && nv44_graph_class(priv))
-               nv_wr32(priv, 0x00b220, 0x00000044);
-       else
-               nv_wr32(priv, 0x00b220, 0x00000031);
+       nv_wr32(priv, 0x00b220, 0x00000031);
        nv_wr32(priv, 0x00b300, 0x02001ec1);
        nv_mask(priv, 0x00b32c, 0x00000001, 0x00000001);
 
diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.h b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.h
new file mode 100644 (file)
index 0000000..d08629d
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __NV31_MPEG_H__
+#define __NV31_MPEG_H__
+
+#include <engine/mpeg.h>
+
+struct nv31_mpeg_chan {
+       struct nouveau_object base;
+};
+
+struct nv31_mpeg_priv {
+       struct nouveau_mpeg base;
+       struct nv31_mpeg_chan *chan;
+};
+
+#endif
index dd61960..d4e7ec0 100644 (file)
 #include <subdev/instmem.h>
 
 #include <engine/mpeg.h>
-#include <engine/graph/nv40.h>
-
-struct nv40_mpeg_priv {
-       struct nouveau_mpeg base;
-};
-
-struct nv40_mpeg_chan {
-       struct nouveau_mpeg_chan base;
-};
+#include <engine/mpeg/nv31.h>
 
 /*******************************************************************************
- * PMPEG context
+ * MPEG object classes
  ******************************************************************************/
 
 static int
-nv40_mpeg_context_ctor(struct nouveau_object *parent,
-                      struct nouveau_object *engine,
-                      struct nouveau_oclass *oclass, void *data, u32 size,
-                      struct nouveau_object **pobject)
+nv40_mpeg_mthd_dma(struct nouveau_object *object, u32 mthd, void *arg, u32 len)
 {
-       struct nv40_mpeg_chan *chan;
-       int ret;
-
-       ret = nouveau_mpeg_context_create(parent, engine, oclass, NULL,
-                                         264 * 4, 16,
-                                         NVOBJ_FLAG_ZERO_ALLOC, &chan);
-       *pobject = nv_object(chan);
-       if (ret)
-               return ret;
+       struct nouveau_instmem *imem = nouveau_instmem(object);
+       struct nv31_mpeg_priv *priv = (void *)object->engine;
+       u32 inst = *(u32 *)arg << 4;
+       u32 dma0 = nv_ro32(imem, inst + 0);
+       u32 dma1 = nv_ro32(imem, inst + 4);
+       u32 dma2 = nv_ro32(imem, inst + 8);
+       u32 base = (dma2 & 0xfffff000) | (dma0 >> 20);
+       u32 size = dma1 + 1;
+
+       /* only allow linear DMA objects */
+       if (!(dma0 & 0x00002000))
+               return -EINVAL;
+
+       if (mthd == 0x0190) {
+               /* DMA_CMD */
+               nv_mask(priv, 0x00b300, 0x00030000, (dma0 & 0x00030000));
+               nv_wr32(priv, 0x00b334, base);
+               nv_wr32(priv, 0x00b324, size);
+       } else
+       if (mthd == 0x01a0) {
+               /* DMA_DATA */
+               nv_mask(priv, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2);
+               nv_wr32(priv, 0x00b360, base);
+               nv_wr32(priv, 0x00b364, size);
+       } else {
+               /* DMA_IMAGE, VRAM only */
+               if (dma0 & 0x00030000)
+                       return -EINVAL;
+
+               nv_wr32(priv, 0x00b370, base);
+               nv_wr32(priv, 0x00b374, size);
+       }
 
-       nv_wo32(&chan->base.base, 0x78, 0x02001ec1);
        return 0;
 }
 
-static int
-nv40_mpeg_context_fini(struct nouveau_object *object, bool suspend)
-{
-
-       struct nv40_mpeg_priv *priv = (void *)object->engine;
-       struct nv40_mpeg_chan *chan = (void *)object;
-       u32 inst = 0x80000000 | nv_gpuobj(chan)->addr >> 4;
-
-       nv_mask(priv, 0x00b32c, 0x00000001, 0x00000000);
-       if (nv_rd32(priv, 0x00b318) == inst)
-               nv_mask(priv, 0x00b318, 0x80000000, 0x00000000);
-       nv_mask(priv, 0x00b32c, 0x00000001, 0x00000001);
-       return 0;
-}
+static struct nouveau_omthds
+nv40_mpeg_omthds[] = {
+       { 0x0190, 0x0190, nv40_mpeg_mthd_dma },
+       { 0x01a0, 0x01a0, nv40_mpeg_mthd_dma },
+       { 0x01b0, 0x01b0, nv40_mpeg_mthd_dma },
+       {}
+};
 
-static struct nouveau_oclass
-nv40_mpeg_cclass = {
-       .handle = NV_ENGCTX(MPEG, 0x40),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv40_mpeg_context_ctor,
-               .dtor = _nouveau_mpeg_context_dtor,
-               .init = _nouveau_mpeg_context_init,
-               .fini = nv40_mpeg_context_fini,
-               .rd32 = _nouveau_mpeg_context_rd32,
-               .wr32 = _nouveau_mpeg_context_wr32,
-       },
+struct nouveau_oclass
+nv40_mpeg_sclass[] = {
+       { 0x3174, &nv31_mpeg_ofuncs, nv40_mpeg_omthds },
+       {}
 };
 
 /*******************************************************************************
@@ -100,7 +97,7 @@ nv40_mpeg_cclass = {
 static void
 nv40_mpeg_intr(struct nouveau_subdev *subdev)
 {
-       struct nv40_mpeg_priv *priv = (void *)subdev;
+       struct nv31_mpeg_priv *priv = (void *)subdev;
        u32 stat;
 
        if ((stat = nv_rd32(priv, 0x00b100)))
@@ -117,7 +114,7 @@ nv40_mpeg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
               struct nouveau_object **pobject)
 {
-       struct nv40_mpeg_priv *priv;
+       struct nv31_mpeg_priv *priv;
        int ret;
 
        ret = nouveau_mpeg_create(parent, engine, oclass, &priv);
@@ -127,8 +124,8 @@ nv40_mpeg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 
        nv_subdev(priv)->unit = 0x00000002;
        nv_subdev(priv)->intr = nv40_mpeg_intr;
-       nv_engine(priv)->cclass = &nv40_mpeg_cclass;
-       nv_engine(priv)->sclass = nv31_mpeg_sclass;
+       nv_engine(priv)->cclass = &nv31_mpeg_cclass;
+       nv_engine(priv)->sclass = nv40_mpeg_sclass;
        nv_engine(priv)->tile_prog = nv31_mpeg_tile_prog;
        return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv44.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv44.c
new file mode 100644 (file)
index 0000000..3d8c213
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/os.h>
+#include <core/class.h>
+#include <core/client.h>
+#include <core/engctx.h>
+#include <core/handle.h>
+
+#include <subdev/fb.h>
+#include <subdev/timer.h>
+#include <subdev/instmem.h>
+
+#include <engine/fifo.h>
+#include <engine/mpeg.h>
+
+struct nv44_mpeg_priv {
+       struct nouveau_mpeg base;
+};
+
+struct nv44_mpeg_chan {
+       struct nouveau_mpeg_chan base;
+};
+
+/*******************************************************************************
+ * PMPEG context
+ ******************************************************************************/
+
+static int
+nv44_mpeg_context_ctor(struct nouveau_object *parent,
+                      struct nouveau_object *engine,
+                      struct nouveau_oclass *oclass, void *data, u32 size,
+                      struct nouveau_object **pobject)
+{
+       struct nv44_mpeg_chan *chan;
+       int ret;
+
+       ret = nouveau_mpeg_context_create(parent, engine, oclass, NULL,
+                                         264 * 4, 16,
+                                         NVOBJ_FLAG_ZERO_ALLOC, &chan);
+       *pobject = nv_object(chan);
+       if (ret)
+               return ret;
+
+       nv_wo32(&chan->base.base, 0x78, 0x02001ec1);
+       return 0;
+}
+
+static int
+nv44_mpeg_context_fini(struct nouveau_object *object, bool suspend)
+{
+
+       struct nv44_mpeg_priv *priv = (void *)object->engine;
+       struct nv44_mpeg_chan *chan = (void *)object;
+       u32 inst = 0x80000000 | nv_gpuobj(chan)->addr >> 4;
+
+       nv_mask(priv, 0x00b32c, 0x00000001, 0x00000000);
+       if (nv_rd32(priv, 0x00b318) == inst)
+               nv_mask(priv, 0x00b318, 0x80000000, 0x00000000);
+       nv_mask(priv, 0x00b32c, 0x00000001, 0x00000001);
+       return 0;
+}
+
+static struct nouveau_oclass
+nv44_mpeg_cclass = {
+       .handle = NV_ENGCTX(MPEG, 0x44),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv44_mpeg_context_ctor,
+               .dtor = _nouveau_mpeg_context_dtor,
+               .init = _nouveau_mpeg_context_init,
+               .fini = nv44_mpeg_context_fini,
+               .rd32 = _nouveau_mpeg_context_rd32,
+               .wr32 = _nouveau_mpeg_context_wr32,
+       },
+};
+
+/*******************************************************************************
+ * PMPEG engine/subdev functions
+ ******************************************************************************/
+
+static void
+nv44_mpeg_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_fifo *pfifo = nouveau_fifo(subdev);
+       struct nouveau_engine *engine = nv_engine(subdev);
+       struct nouveau_object *engctx;
+       struct nouveau_handle *handle;
+       struct nv44_mpeg_priv *priv = (void *)subdev;
+       u32 inst = nv_rd32(priv, 0x00b318) & 0x000fffff;
+       u32 stat = nv_rd32(priv, 0x00b100);
+       u32 type = nv_rd32(priv, 0x00b230);
+       u32 mthd = nv_rd32(priv, 0x00b234);
+       u32 data = nv_rd32(priv, 0x00b238);
+       u32 show = stat;
+       int chid;
+
+       engctx = nouveau_engctx_get(engine, inst);
+       chid   = pfifo->chid(pfifo, engctx);
+
+       if (stat & 0x01000000) {
+               /* happens on initial binding of the object */
+               if (type == 0x00000020 && mthd == 0x0000) {
+                       nv_mask(priv, 0x00b308, 0x00000000, 0x00000000);
+                       show &= ~0x01000000;
+               }
+
+               if (type == 0x00000010) {
+                       handle = nouveau_handle_get_class(engctx, 0x3174);
+                       if (handle && !nv_call(handle->object, mthd, data))
+                               show &= ~0x01000000;
+                       nouveau_handle_put(handle);
+               }
+       }
+
+       nv_wr32(priv, 0x00b100, stat);
+       nv_wr32(priv, 0x00b230, 0x00000001);
+
+       if (show) {
+               nv_error(priv,
+                        "ch %d [0x%08x %s] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                        chid, inst << 4, nouveau_client_name(engctx), stat,
+                        type, mthd, data);
+       }
+
+       nouveau_engctx_put(engctx);
+}
+
+static void
+nv44_mpeg_me_intr(struct nouveau_subdev *subdev)
+{
+       struct nv44_mpeg_priv *priv = (void *)subdev;
+       u32 stat;
+
+       if ((stat = nv_rd32(priv, 0x00b100)))
+               nv44_mpeg_intr(subdev);
+
+       if ((stat = nv_rd32(priv, 0x00b800))) {
+               nv_error(priv, "PMSRCH 0x%08x\n", stat);
+               nv_wr32(priv, 0x00b800, stat);
+       }
+}
+
+static int
+nv44_mpeg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+              struct nouveau_oclass *oclass, void *data, u32 size,
+              struct nouveau_object **pobject)
+{
+       struct nv44_mpeg_priv *priv;
+       int ret;
+
+       ret = nouveau_mpeg_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       nv_subdev(priv)->unit = 0x00000002;
+       nv_subdev(priv)->intr = nv44_mpeg_me_intr;
+       nv_engine(priv)->cclass = &nv44_mpeg_cclass;
+       nv_engine(priv)->sclass = nv40_mpeg_sclass;
+       nv_engine(priv)->tile_prog = nv31_mpeg_tile_prog;
+       return 0;
+}
+
+struct nouveau_oclass
+nv44_mpeg_oclass = {
+       .handle = NV_ENGINE(MPEG, 0x44),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv44_mpeg_ctor,
+               .dtor = _nouveau_mpeg_dtor,
+               .init = nv31_mpeg_init,
+               .fini = _nouveau_mpeg_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/base.c b/drivers/gpu/drm/nouveau/core/engine/perfmon/base.c
new file mode 100644 (file)
index 0000000..e9c5e51
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/option.h>
+#include <core/class.h>
+
+#include <subdev/clock.h>
+
+#include "priv.h"
+
+#define QUAD_MASK 0x0f
+#define QUAD_FREE 0x01
+
+static struct nouveau_perfsig *
+nouveau_perfsig_find_(struct nouveau_perfdom *dom, const char *name, u32 size)
+{
+       char path[64];
+       int i;
+
+       if (name[0] != '/') {
+               for (i = 0; i < dom->signal_nr; i++) {
+                       if ( dom->signal[i].name &&
+                           !strncmp(name, dom->signal[i].name, size))
+                               return &dom->signal[i];
+               }
+       } else {
+               for (i = 0; i < dom->signal_nr; i++) {
+                       snprintf(path, sizeof(path), "/%s/%02x", dom->name, i);
+                       if (!strncmp(name, path, size))
+                               return &dom->signal[i];
+               }
+       }
+
+       return NULL;
+}
+
+struct nouveau_perfsig *
+nouveau_perfsig_find(struct nouveau_perfmon *ppm, const char *name, u32 size,
+                    struct nouveau_perfdom **pdom)
+{
+       struct nouveau_perfdom *dom = *pdom;
+       struct nouveau_perfsig *sig;
+
+       if (dom == NULL) {
+               list_for_each_entry(dom, &ppm->domains, head) {
+                       sig = nouveau_perfsig_find_(dom, name, size);
+                       if (sig) {
+                               *pdom = dom;
+                               return sig;
+                       }
+               }
+
+               return NULL;
+       }
+
+       return nouveau_perfsig_find_(dom, name, size);
+}
+
+struct nouveau_perfctr *
+nouveau_perfsig_wrap(struct nouveau_perfmon *ppm, const char *name,
+                    struct nouveau_perfdom **pdom)
+{
+       struct nouveau_perfsig *sig;
+       struct nouveau_perfctr *ctr;
+
+       sig = nouveau_perfsig_find(ppm, name, strlen(name), pdom);
+       if (!sig)
+               return NULL;
+
+       ctr = kzalloc(sizeof(*ctr), GFP_KERNEL);
+       if (ctr) {
+               ctr->signal[0] = sig;
+               ctr->logic_op = 0xaaaa;
+       }
+
+       return ctr;
+}
+
+/*******************************************************************************
+ * Perfmon object classes
+ ******************************************************************************/
+static int
+nouveau_perfctr_query(struct nouveau_object *object, u32 mthd,
+                     void *data, u32 size)
+{
+       struct nouveau_device *device = nv_device(object);
+       struct nouveau_perfmon *ppm = (void *)object->engine;
+       struct nouveau_perfdom *dom = NULL, *chk;
+       struct nv_perfctr_query *args = data;
+       const bool all = nouveau_boolopt(device->cfgopt, "NvPmShowAll", false);
+       const bool raw = nouveau_boolopt(device->cfgopt, "NvPmUnnamed", all);
+       const char *name;
+       int tmp = 0, di, si;
+       char path[64];
+
+       if (size < sizeof(*args))
+               return -EINVAL;
+
+       di = (args->iter & 0xff000000) >> 24;
+       si = (args->iter & 0x00ffffff) - 1;
+
+       list_for_each_entry(chk, &ppm->domains, head) {
+               if (tmp++ == di) {
+                       dom = chk;
+                       break;
+               }
+       }
+
+       if (dom == NULL || si >= (int)dom->signal_nr)
+               return -EINVAL;
+
+       if (si >= 0) {
+               if (raw || !(name = dom->signal[si].name)) {
+                       snprintf(path, sizeof(path), "/%s/%02x", dom->name, si);
+                       name = path;
+               }
+
+               if (args->name)
+                       strncpy(args->name, name, args->size);
+               args->size = strlen(name) + 1;
+       }
+
+       do {
+               while (++si < dom->signal_nr) {
+                       if (all || dom->signal[si].name) {
+                               args->iter = (di << 24) | ++si;
+                               return 0;
+                       }
+               }
+               si = -1;
+               di = di + 1;
+               dom = list_entry(dom->head.next, typeof(*dom), head);
+       } while (&dom->head != &ppm->domains);
+
+       args->iter = 0xffffffff;
+       return 0;
+}
+
+static int
+nouveau_perfctr_sample(struct nouveau_object *object, u32 mthd,
+                      void *data, u32 size)
+{
+       struct nouveau_perfmon *ppm = (void *)object->engine;
+       struct nouveau_perfctr *ctr, *tmp;
+       struct nouveau_perfdom *dom;
+       struct nv_perfctr_sample *args = data;
+
+       if (size < sizeof(*args))
+               return -EINVAL;
+       ppm->sequence++;
+
+       list_for_each_entry(dom, &ppm->domains, head) {
+               /* sample previous batch of counters */
+               if (dom->quad != QUAD_MASK) {
+                       dom->func->next(ppm, dom);
+                       tmp = NULL;
+                       while (!list_empty(&dom->list)) {
+                               ctr = list_first_entry(&dom->list,
+                                                       typeof(*ctr), head);
+                               if (ctr->slot < 0) break;
+                               if ( tmp && tmp == ctr) break;
+                               if (!tmp) tmp = ctr;
+                               dom->func->read(ppm, dom, ctr);
+                               ctr->slot  = -1;
+                               list_move_tail(&ctr->head, &dom->list);
+                       }
+               }
+
+               dom->quad = QUAD_MASK;
+
+               /* setup next batch of counters for sampling */
+               list_for_each_entry(ctr, &dom->list, head) {
+                       ctr->slot = ffs(dom->quad) - 1;
+                       if (ctr->slot < 0)
+                               break;
+                       dom->quad &= ~(QUAD_FREE << ctr->slot);
+                       dom->func->init(ppm, dom, ctr);
+               }
+
+               if (dom->quad != QUAD_MASK)
+                       dom->func->next(ppm, dom);
+       }
+
+       return 0;
+}
+
+static int
+nouveau_perfctr_read(struct nouveau_object *object, u32 mthd,
+                    void *data, u32 size)
+{
+       struct nouveau_perfctr *ctr = (void *)object;
+       struct nv_perfctr_read *args = data;
+
+       if (size < sizeof(*args))
+               return -EINVAL;
+       if (!ctr->clk)
+               return -EAGAIN;
+
+       args->clk = ctr->clk;
+       args->ctr = ctr->ctr;
+       return 0;
+}
+
+static void
+nouveau_perfctr_dtor(struct nouveau_object *object)
+{
+       struct nouveau_perfctr *ctr = (void *)object;
+       if (ctr->head.next)
+               list_del(&ctr->head);
+       nouveau_object_destroy(&ctr->base);
+}
+
+static int
+nouveau_perfctr_ctor(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, void *data, u32 size,
+                    struct nouveau_object **pobject)
+{
+       struct nouveau_perfmon *ppm = (void *)engine;
+       struct nouveau_perfdom *dom = NULL;
+       struct nouveau_perfsig *sig[4] = {};
+       struct nouveau_perfctr *ctr;
+       struct nv_perfctr_class *args = data;
+       int ret, i;
+
+       if (size < sizeof(*args))
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(args->signal) && args->signal[i].name; i++) {
+               sig[i] = nouveau_perfsig_find(ppm, args->signal[i].name,
+                                             args->signal[i].size, &dom);
+               if (!sig[i])
+                       return -EINVAL;
+       }
+
+       ret = nouveau_object_create(parent, engine, oclass, 0, &ctr);
+       *pobject = nv_object(ctr);
+       if (ret)
+               return ret;
+
+       ctr->slot = -1;
+       ctr->logic_op = args->logic_op;
+       ctr->signal[0] = sig[0];
+       ctr->signal[1] = sig[1];
+       ctr->signal[2] = sig[2];
+       ctr->signal[3] = sig[3];
+       if (dom)
+               list_add_tail(&ctr->head, &dom->list);
+       return 0;
+}
+
+static struct nouveau_ofuncs
+nouveau_perfctr_ofuncs = {
+       .ctor = nouveau_perfctr_ctor,
+       .dtor = nouveau_perfctr_dtor,
+       .init = nouveau_object_init,
+       .fini = nouveau_object_fini,
+};
+
+static struct nouveau_omthds
+nouveau_perfctr_omthds[] = {
+       { NV_PERFCTR_QUERY, NV_PERFCTR_QUERY, nouveau_perfctr_query },
+       { NV_PERFCTR_SAMPLE, NV_PERFCTR_SAMPLE, nouveau_perfctr_sample },
+       { NV_PERFCTR_READ, NV_PERFCTR_READ, nouveau_perfctr_read },
+       {}
+};
+
+struct nouveau_oclass
+nouveau_perfmon_sclass[] = {
+       { .handle = NV_PERFCTR_CLASS,
+         .ofuncs = &nouveau_perfctr_ofuncs,
+         .omthds =  nouveau_perfctr_omthds,
+       },
+       {},
+};
+
+/*******************************************************************************
+ * PPM context
+ ******************************************************************************/
+static void
+nouveau_perfctx_dtor(struct nouveau_object *object)
+{
+       struct nouveau_perfmon *ppm = (void *)object->engine;
+       mutex_lock(&nv_subdev(ppm)->mutex);
+       ppm->context = NULL;
+       mutex_unlock(&nv_subdev(ppm)->mutex);
+}
+
+static int
+nouveau_perfctx_ctor(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, void *data, u32 size,
+                    struct nouveau_object **pobject)
+{
+       struct nouveau_perfmon *ppm = (void *)engine;
+       struct nouveau_perfctx *ctx;
+       int ret;
+
+       ret = nouveau_engctx_create(parent, engine, oclass, NULL,
+                                   0, 0, 0, &ctx);
+       *pobject = nv_object(ctx);
+       if (ret)
+               return ret;
+
+       mutex_lock(&nv_subdev(ppm)->mutex);
+       if (ppm->context == NULL)
+               ppm->context = ctx;
+       mutex_unlock(&nv_subdev(ppm)->mutex);
+
+       if (ctx != ppm->context)
+               return -EBUSY;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nouveau_perfmon_cclass = {
+       .handle = NV_ENGCTX(PERFMON, 0x00),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nouveau_perfctx_ctor,
+               .dtor = nouveau_perfctx_dtor,
+               .init = _nouveau_engctx_init,
+               .fini = _nouveau_engctx_fini,
+       },
+};
+
+/*******************************************************************************
+ * PPM engine/subdev functions
+ ******************************************************************************/
+int
+nouveau_perfdom_new(struct nouveau_perfmon *ppm, const char *name, u32 mask,
+                   u32 base, u32 size_unit, u32 size_domain,
+                   const struct nouveau_specdom *spec)
+{
+       const struct nouveau_specdom *sdom;
+       const struct nouveau_specsig *ssig;
+       struct nouveau_perfdom *dom;
+       int i;
+
+       for (i = 0; i == 0 || mask; i++) {
+               u32 addr = base + (i * size_unit);
+               if (i && !(mask & (1 << i)))
+                       continue;
+
+               sdom = spec;
+               while (sdom->signal_nr) {
+                       dom = kzalloc(sizeof(*dom) + sdom->signal_nr *
+                                     sizeof(*dom->signal), GFP_KERNEL);
+                       if (!dom)
+                               return -ENOMEM;
+
+                       if (mask) {
+                               snprintf(dom->name, sizeof(dom->name),
+                                        "%s/%02x/%02x", name, i,
+                                        (int)(sdom - spec));
+                       } else {
+                               snprintf(dom->name, sizeof(dom->name),
+                                        "%s/%02x", name, (int)(sdom - spec));
+                       }
+
+                       list_add_tail(&dom->head, &ppm->domains);
+                       INIT_LIST_HEAD(&dom->list);
+                       dom->func = sdom->func;
+                       dom->addr = addr;
+                       dom->quad = QUAD_MASK;
+                       dom->signal_nr = sdom->signal_nr;
+
+                       ssig = (sdom++)->signal;
+                       while (ssig->name) {
+                               dom->signal[ssig->signal].name = ssig->name;
+                               ssig++;
+                       }
+
+                       addr += size_domain;
+               }
+
+               mask &= ~(1 << i);
+       }
+
+       return 0;
+}
+
+int
+_nouveau_perfmon_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_perfmon *ppm = (void *)object;
+       return nouveau_engine_fini(&ppm->base, suspend);
+}
+
+int
+_nouveau_perfmon_init(struct nouveau_object *object)
+{
+       struct nouveau_perfmon *ppm = (void *)object;
+       return nouveau_engine_init(&ppm->base);
+}
+
+void
+_nouveau_perfmon_dtor(struct nouveau_object *object)
+{
+       struct nouveau_perfmon *ppm = (void *)object;
+       struct nouveau_perfdom *dom, *tmp;
+
+       list_for_each_entry_safe(dom, tmp, &ppm->domains, head) {
+               list_del(&dom->head);
+               kfree(dom);
+       }
+
+       nouveau_engine_destroy(&ppm->base);
+}
+
+int
+nouveau_perfmon_create_(struct nouveau_object *parent,
+                       struct nouveau_object *engine,
+                       struct nouveau_oclass *oclass,
+                       int length, void **pobject)
+{
+       struct nouveau_perfmon *ppm;
+       int ret;
+
+       ret = nouveau_engine_create_(parent, engine, oclass, true, "PPM",
+                                    "perfmon", length, pobject);
+       ppm = *pobject;
+       if (ret)
+               return ret;
+
+       INIT_LIST_HEAD(&ppm->domains);
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/daemon.c b/drivers/gpu/drm/nouveau/core/engine/perfmon/daemon.c
new file mode 100644 (file)
index 0000000..50696cc
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "priv.h"
+
+static void
+pwr_perfctr_init(struct nouveau_perfmon *ppm, struct nouveau_perfdom *dom,
+                struct nouveau_perfctr *ctr)
+{
+       u32 mask = 0x00000000;
+       u32 ctrl = 0x00000001;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ctr->signal) && ctr->signal[i]; i++)
+               mask |= 1 << (ctr->signal[i] - dom->signal);
+
+       nv_wr32(ppm, 0x10a504 + (ctr->slot * 0x10), mask);
+       nv_wr32(ppm, 0x10a50c + (ctr->slot * 0x10), ctrl);
+       nv_wr32(ppm, 0x10a50c + (ppm->last * 0x10), 0x00000003);
+}
+
+static void
+pwr_perfctr_read(struct nouveau_perfmon *ppm, struct nouveau_perfdom *dom,
+                struct nouveau_perfctr *ctr)
+{
+       ctr->ctr = ppm->pwr[ctr->slot];
+       ctr->clk = ppm->pwr[ppm->last];
+}
+
+static void
+pwr_perfctr_next(struct nouveau_perfmon *ppm, struct nouveau_perfdom *dom)
+{
+       int i;
+
+       for (i = 0; i <= ppm->last; i++) {
+               ppm->pwr[i] = nv_rd32(ppm, 0x10a508 + (i * 0x10));
+               nv_wr32(ppm, 0x10a508 + (i * 0x10), 0x80000000);
+       }
+}
+
+static const struct nouveau_funcdom
+pwr_perfctr_func = {
+       .init = pwr_perfctr_init,
+       .read = pwr_perfctr_read,
+       .next = pwr_perfctr_next,
+};
+
+const struct nouveau_specdom
+nva3_perfmon_pwr[] = {
+       { 0x20, (const struct nouveau_specsig[]) {
+                       { 0x00, "pwr_gr_idle" },
+                       { 0x04, "pwr_bsp_idle" },
+                       { 0x05, "pwr_vp_idle" },
+                       { 0x06, "pwr_ppp_idle" },
+                       { 0x13, "pwr_ce0_idle" },
+                       {}
+               }, &pwr_perfctr_func },
+       {}
+};
+
+const struct nouveau_specdom
+nvc0_perfmon_pwr[] = {
+       { 0x20, (const struct nouveau_specsig[]) {
+                       { 0x00, "pwr_gr_idle" },
+                       { 0x04, "pwr_bsp_idle" },
+                       { 0x05, "pwr_vp_idle" },
+                       { 0x06, "pwr_ppp_idle" },
+                       { 0x13, "pwr_ce0_idle" },
+                       { 0x14, "pwr_ce1_idle" },
+                       {}
+               }, &pwr_perfctr_func },
+       {}
+};
+
+const struct nouveau_specdom
+nve0_perfmon_pwr[] = {
+       { 0x20, (const struct nouveau_specsig[]) {
+                       { 0x00, "pwr_gr_idle" },
+                       { 0x04, "pwr_bsp_idle" },
+                       { 0x05, "pwr_vp_idle" },
+                       { 0x06, "pwr_ppp_idle" },
+                       { 0x13, "pwr_ce0_idle" },
+                       { 0x14, "pwr_ce1_idle" },
+                       { 0x15, "pwr_ce2_idle" },
+                       {}
+               }, &pwr_perfctr_func },
+       {}
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/nv40.c b/drivers/gpu/drm/nouveau/core/engine/perfmon/nv40.c
new file mode 100644 (file)
index 0000000..b2a1078
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv40.h"
+
+/*******************************************************************************
+ * Perfmon object classes
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM context
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM engine/subdev functions
+ ******************************************************************************/
+
+static void
+nv40_perfctr_init(struct nouveau_perfmon *ppm, struct nouveau_perfdom *dom,
+                 struct nouveau_perfctr *ctr)
+{
+       struct nv40_perfmon_priv *priv = (void *)ppm;
+       struct nv40_perfmon_cntr *cntr = (void *)ctr;
+       u32 log = ctr->logic_op;
+       u32 src = 0x00000000;
+       int i;
+
+       for (i = 0; i < 4 && ctr->signal[i]; i++)
+               src |= (ctr->signal[i] - dom->signal) << (i * 8);
+
+       nv_wr32(priv, 0x00a7c0 + dom->addr, 0x00000001);
+       nv_wr32(priv, 0x00a400 + dom->addr + (cntr->base.slot * 0x40), src);
+       nv_wr32(priv, 0x00a420 + dom->addr + (cntr->base.slot * 0x40), log);
+}
+
+static void
+nv40_perfctr_read(struct nouveau_perfmon *ppm, struct nouveau_perfdom *dom,
+                 struct nouveau_perfctr *ctr)
+{
+       struct nv40_perfmon_priv *priv = (void *)ppm;
+       struct nv40_perfmon_cntr *cntr = (void *)ctr;
+
+       switch (cntr->base.slot) {
+       case 0: cntr->base.ctr = nv_rd32(priv, 0x00a700 + dom->addr); break;
+       case 1: cntr->base.ctr = nv_rd32(priv, 0x00a6c0 + dom->addr); break;
+       case 2: cntr->base.ctr = nv_rd32(priv, 0x00a680 + dom->addr); break;
+       case 3: cntr->base.ctr = nv_rd32(priv, 0x00a740 + dom->addr); break;
+       }
+       cntr->base.clk = nv_rd32(priv, 0x00a600 + dom->addr);
+}
+
+static void
+nv40_perfctr_next(struct nouveau_perfmon *ppm, struct nouveau_perfdom *dom)
+{
+       struct nv40_perfmon_priv *priv = (void *)ppm;
+       if (priv->sequence != ppm->sequence) {
+               nv_wr32(priv, 0x400084, 0x00000020);
+               priv->sequence = ppm->sequence;
+       }
+}
+
+const struct nouveau_funcdom
+nv40_perfctr_func = {
+       .init = nv40_perfctr_init,
+       .read = nv40_perfctr_read,
+       .next = nv40_perfctr_next,
+};
+
+static const struct nouveau_specdom
+nv40_perfmon[] = {
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       {}
+};
+
+int
+nv40_perfmon_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nv40_perfmon_oclass *mclass = (void *)oclass;
+       struct nv40_perfmon_priv *priv;
+       int ret;
+
+       ret = nouveau_perfmon_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       ret = nouveau_perfdom_new(&priv->base, "pm", 0, 0, 0, 4, mclass->doms);
+       if (ret)
+               return ret;
+
+       nv_engine(priv)->cclass = &nouveau_perfmon_cclass;
+       nv_engine(priv)->sclass =  nouveau_perfmon_sclass;
+       return 0;
+}
+
+struct nouveau_oclass *
+nv40_perfmon_oclass = &(struct nv40_perfmon_oclass) {
+       .base.handle = NV_ENGINE(PERFMON, 0x40),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv40_perfmon_ctor,
+               .dtor = _nouveau_perfmon_dtor,
+               .init = _nouveau_perfmon_init,
+               .fini = _nouveau_perfmon_fini,
+       },
+       .doms = nv40_perfmon,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/nv40.h b/drivers/gpu/drm/nouveau/core/engine/perfmon/nv40.h
new file mode 100644 (file)
index 0000000..1b5792d
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __NVKM_PM_NV40_H__
+#define __NVKM_PM_NV40_H__
+
+#include "priv.h"
+
+struct nv40_perfmon_oclass {
+       struct nouveau_oclass base;
+       const struct nouveau_specdom *doms;
+};
+
+struct nv40_perfmon_priv {
+       struct nouveau_perfmon base;
+       u32 sequence;
+};
+
+int nv40_perfmon_ctor(struct nouveau_object *, struct nouveau_object *,
+                     struct nouveau_oclass *, void *data, u32 size,
+                     struct nouveau_object **pobject);
+
+struct nv40_perfmon_cntr {
+       struct nouveau_perfctr base;
+};
+
+extern const struct nouveau_funcdom nv40_perfctr_func;
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/nv50.c b/drivers/gpu/drm/nouveau/core/engine/perfmon/nv50.c
new file mode 100644 (file)
index 0000000..9421769
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv40.h"
+
+/*******************************************************************************
+ * Perfmon object classes
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM context
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM engine/subdev functions
+ ******************************************************************************/
+
+static const struct nouveau_specdom
+nv50_perfmon[] = {
+       { 0x040, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x100, (const struct nouveau_specsig[]) {
+                       { 0xc8, "gr_idle" },
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x100, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x020, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x040, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       {}
+};
+
+struct nouveau_oclass *
+nv50_perfmon_oclass = &(struct nv40_perfmon_oclass) {
+       .base.handle = NV_ENGINE(PERFMON, 0x50),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv40_perfmon_ctor,
+               .dtor = _nouveau_perfmon_dtor,
+               .init = _nouveau_perfmon_init,
+               .fini = _nouveau_perfmon_fini,
+       },
+       .doms = nv50_perfmon,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/nv84.c b/drivers/gpu/drm/nouveau/core/engine/perfmon/nv84.c
new file mode 100644 (file)
index 0000000..9232c7f
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv40.h"
+
+/*******************************************************************************
+ * Perfmon object classes
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM context
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM engine/subdev functions
+ ******************************************************************************/
+
+static const struct nouveau_specdom
+nv84_perfmon[] = {
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       {}
+};
+
+struct nouveau_oclass *
+nv84_perfmon_oclass = &(struct nv40_perfmon_oclass) {
+       .base.handle = NV_ENGINE(PERFMON, 0x84),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv40_perfmon_ctor,
+               .dtor = _nouveau_perfmon_dtor,
+               .init = _nouveau_perfmon_init,
+               .fini = _nouveau_perfmon_fini,
+       },
+       .doms = nv84_perfmon,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/nva3.c b/drivers/gpu/drm/nouveau/core/engine/perfmon/nva3.c
new file mode 100644 (file)
index 0000000..6197ebd
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv40.h"
+
+/*******************************************************************************
+ * Perfmon object classes
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM context
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM engine/subdev functions
+ ******************************************************************************/
+
+static const struct nouveau_specdom
+nva3_perfmon[] = {
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       { 0x20, (const struct nouveau_specsig[]) {
+                       {}
+               }, &nv40_perfctr_func },
+       {}
+};
+
+static int
+nva3_perfmon_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **object)
+{
+       int ret = nv40_perfmon_ctor(parent, engine, oclass, data, size, object);
+       if (ret == 0) {
+               struct nv40_perfmon_priv *priv = (void *)*object;
+               ret = nouveau_perfdom_new(&priv->base, "pwr", 0, 0, 0, 0,
+                                          nva3_perfmon_pwr);
+               if (ret)
+                       return ret;
+
+               priv->base.last = 3;
+       }
+       return ret;
+}
+
+struct nouveau_oclass *
+nva3_perfmon_oclass = &(struct nv40_perfmon_oclass) {
+       .base.handle = NV_ENGINE(PERFMON, 0xa3),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nva3_perfmon_ctor,
+               .dtor = _nouveau_perfmon_dtor,
+               .init = _nouveau_perfmon_init,
+               .fini = _nouveau_perfmon_fini,
+       },
+       .doms = nva3_perfmon,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/perfmon/nvc0.c
new file mode 100644 (file)
index 0000000..74b2410
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nvc0.h"
+
+/*******************************************************************************
+ * Perfmon object classes
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM context
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM engine/subdev functions
+ ******************************************************************************/
+
+static const struct nouveau_specdom
+nvc0_perfmon_hub[] = {
+       {}
+};
+
+static const struct nouveau_specdom
+nvc0_perfmon_gpc[] = {
+       {}
+};
+
+static const struct nouveau_specdom
+nvc0_perfmon_part[] = {
+       {}
+};
+
+static void
+nvc0_perfctr_init(struct nouveau_perfmon *ppm, struct nouveau_perfdom *dom,
+                 struct nouveau_perfctr *ctr)
+{
+       struct nvc0_perfmon_priv *priv = (void *)ppm;
+       struct nvc0_perfmon_cntr *cntr = (void *)ctr;
+       u32 log = ctr->logic_op;
+       u32 src = 0x00000000;
+       int i;
+
+       for (i = 0; i < 4 && ctr->signal[i]; i++)
+               src |= (ctr->signal[i] - dom->signal) << (i * 8);
+
+       nv_wr32(priv, dom->addr + 0x09c, 0x00040002);
+       nv_wr32(priv, dom->addr + 0x100, 0x00000000);
+       nv_wr32(priv, dom->addr + 0x040 + (cntr->base.slot * 0x08), src);
+       nv_wr32(priv, dom->addr + 0x044 + (cntr->base.slot * 0x08), log);
+}
+
+static void
+nvc0_perfctr_read(struct nouveau_perfmon *ppm, struct nouveau_perfdom *dom,
+                 struct nouveau_perfctr *ctr)
+{
+       struct nvc0_perfmon_priv *priv = (void *)ppm;
+       struct nvc0_perfmon_cntr *cntr = (void *)ctr;
+
+       switch (cntr->base.slot) {
+       case 0: cntr->base.ctr = nv_rd32(priv, dom->addr + 0x08c); break;
+       case 1: cntr->base.ctr = nv_rd32(priv, dom->addr + 0x088); break;
+       case 2: cntr->base.ctr = nv_rd32(priv, dom->addr + 0x080); break;
+       case 3: cntr->base.ctr = nv_rd32(priv, dom->addr + 0x090); break;
+       }
+       cntr->base.clk = nv_rd32(priv, dom->addr + 0x070);
+}
+
+static void
+nvc0_perfctr_next(struct nouveau_perfmon *ppm, struct nouveau_perfdom *dom)
+{
+       struct nvc0_perfmon_priv *priv = (void *)ppm;
+       nv_wr32(priv, dom->addr + 0x06c, dom->signal_nr - 0x40 + 0x27);
+       nv_wr32(priv, dom->addr + 0x0ec, 0x00000011);
+}
+
+const struct nouveau_funcdom
+nvc0_perfctr_func = {
+       .init = nvc0_perfctr_init,
+       .read = nvc0_perfctr_read,
+       .next = nvc0_perfctr_next,
+};
+
+int
+nvc0_perfmon_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nvc0_perfmon_priv *priv = (void *)object;
+       nv_mask(priv, 0x000200, 0x10000000, 0x00000000);
+       nv_mask(priv, 0x000200, 0x10000000, 0x10000000);
+       return nouveau_perfmon_fini(&priv->base, suspend);
+}
+
+static int
+nvc0_perfmon_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nvc0_perfmon_priv *priv;
+       u32 mask;
+       int ret;
+
+       ret = nouveau_perfmon_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       ret = nouveau_perfdom_new(&priv->base, "pwr", 0, 0, 0, 0,
+                                  nvc0_perfmon_pwr);
+       if (ret)
+               return ret;
+
+       /* HUB */
+       ret = nouveau_perfdom_new(&priv->base, "hub", 0, 0x1b0000, 0, 0x200,
+                                  nvc0_perfmon_hub);
+       if (ret)
+               return ret;
+
+       /* GPC */
+       mask  = (1 << nv_rd32(priv, 0x022430)) - 1;
+       mask &= ~nv_rd32(priv, 0x022504);
+       mask &= ~nv_rd32(priv, 0x022584);
+
+       ret = nouveau_perfdom_new(&priv->base, "gpc", mask, 0x180000,
+                                 0x1000, 0x200, nvc0_perfmon_gpc);
+       if (ret)
+               return ret;
+
+       /* PART */
+       mask  = (1 << nv_rd32(priv, 0x022438)) - 1;
+       mask &= ~nv_rd32(priv, 0x022548);
+       mask &= ~nv_rd32(priv, 0x0225c8);
+
+       ret = nouveau_perfdom_new(&priv->base, "part", mask, 0x1a0000,
+                                 0x1000, 0x200, nvc0_perfmon_part);
+       if (ret)
+               return ret;
+
+       nv_engine(priv)->cclass = &nouveau_perfmon_cclass;
+       nv_engine(priv)->sclass =  nouveau_perfmon_sclass;
+       priv->base.last = 7;
+       return 0;
+}
+
+struct nouveau_oclass
+nvc0_perfmon_oclass = {
+       .handle = NV_ENGINE(PERFMON, 0xc0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_perfmon_ctor,
+               .dtor = _nouveau_perfmon_dtor,
+               .init = _nouveau_perfmon_init,
+               .fini = nvc0_perfmon_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/perfmon/nvc0.h
new file mode 100644 (file)
index 0000000..f66bca4
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __NVKM_PM_NVC0_H__
+#define __NVKM_PM_NVC0_H__
+
+#include "priv.h"
+
+struct nvc0_perfmon_priv {
+       struct nouveau_perfmon base;
+};
+
+struct nvc0_perfmon_cntr {
+       struct nouveau_perfctr base;
+};
+
+extern const struct nouveau_funcdom nvc0_perfctr_func;
+int nvc0_perfmon_fini(struct nouveau_object *, bool);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/nve0.c b/drivers/gpu/drm/nouveau/core/engine/perfmon/nve0.c
new file mode 100644 (file)
index 0000000..71d718c
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nvc0.h"
+
+/*******************************************************************************
+ * Perfmon object classes
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM context
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM engine/subdev functions
+ ******************************************************************************/
+
+static const struct nouveau_specdom
+nve0_perfmon_hub[] = {
+       { 0x60, (const struct nouveau_specsig[]) {
+                       { 0x47, "hub00_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       { 0x40, (const struct nouveau_specsig[]) {
+                       { 0x27, "hub01_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       { 0x60, (const struct nouveau_specsig[]) {
+                       { 0x47, "hub02_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       { 0x60, (const struct nouveau_specsig[]) {
+                       { 0x47, "hub03_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       { 0x40, (const struct nouveau_specsig[]) {
+                       { 0x03, "host_mmio_rd" },
+                       { 0x27, "hub04_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       { 0x60, (const struct nouveau_specsig[]) {
+                       { 0x47, "hub05_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       { 0xc0, (const struct nouveau_specsig[]) {
+                       { 0x74, "host_fb_rd3x" },
+                       { 0x75, "host_fb_rd3x_2" },
+                       { 0xa7, "hub06_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       { 0x60, (const struct nouveau_specsig[]) {
+                       { 0x47, "hub07_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       {}
+};
+
+static const struct nouveau_specdom
+nve0_perfmon_gpc[] = {
+       { 0xe0, (const struct nouveau_specsig[]) {
+                       { 0xc7, "gpc00_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       {}
+};
+
+static const struct nouveau_specdom
+nve0_perfmon_part[] = {
+       { 0x60, (const struct nouveau_specsig[]) {
+                       { 0x47, "part00_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       { 0x60, (const struct nouveau_specsig[]) {
+                       { 0x47, "part01_user_0" },
+                       {}
+               }, &nvc0_perfctr_func },
+       {}
+};
+
+static int
+nve0_perfmon_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nvc0_perfmon_priv *priv;
+       u32 mask;
+       int ret;
+
+       ret = nouveau_perfmon_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       /* PDAEMON */
+       ret = nouveau_perfdom_new(&priv->base, "pwr", 0, 0, 0, 0,
+                                  nve0_perfmon_pwr);
+       if (ret)
+               return ret;
+
+       /* HUB */
+       ret = nouveau_perfdom_new(&priv->base, "hub", 0, 0x1b0000, 0, 0x200,
+                                  nve0_perfmon_hub);
+       if (ret)
+               return ret;
+
+       /* GPC */
+       mask  = (1 << nv_rd32(priv, 0x022430)) - 1;
+       mask &= ~nv_rd32(priv, 0x022504);
+       mask &= ~nv_rd32(priv, 0x022584);
+
+       ret = nouveau_perfdom_new(&priv->base, "gpc", mask, 0x180000,
+                                 0x1000, 0x200, nve0_perfmon_gpc);
+       if (ret)
+               return ret;
+
+       /* PART */
+       mask  = (1 << nv_rd32(priv, 0x022438)) - 1;
+       mask &= ~nv_rd32(priv, 0x022548);
+       mask &= ~nv_rd32(priv, 0x0225c8);
+
+       ret = nouveau_perfdom_new(&priv->base, "part", mask, 0x1a0000,
+                                 0x1000, 0x200, nve0_perfmon_part);
+       if (ret)
+               return ret;
+
+       nv_engine(priv)->cclass = &nouveau_perfmon_cclass;
+       nv_engine(priv)->sclass =  nouveau_perfmon_sclass;
+       priv->base.last = 7;
+       return 0;
+}
+
+struct nouveau_oclass
+nve0_perfmon_oclass = {
+       .handle = NV_ENGINE(PERFMON, 0xe0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nve0_perfmon_ctor,
+               .dtor = _nouveau_perfmon_dtor,
+               .init = _nouveau_perfmon_init,
+               .fini = nvc0_perfmon_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/perfmon/nvf0.c
new file mode 100644 (file)
index 0000000..47256f7
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nvc0.h"
+
+/*******************************************************************************
+ * Perfmon object classes
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM context
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PPM engine/subdev functions
+ ******************************************************************************/
+
+static int
+nvf0_perfmon_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nvc0_perfmon_priv *priv;
+       int ret;
+
+       ret = nouveau_perfmon_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       ret = nouveau_perfdom_new(&priv->base, "pwr", 0, 0, 0, 0,
+                                  nve0_perfmon_pwr);
+       if (ret)
+               return ret;
+
+       nv_engine(priv)->cclass = &nouveau_perfmon_cclass;
+       nv_engine(priv)->sclass =  nouveau_perfmon_sclass;
+       return 0;
+}
+
+struct nouveau_oclass
+nvf0_perfmon_oclass = {
+       .handle = NV_ENGINE(PERFMON, 0xf0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvf0_perfmon_ctor,
+               .dtor = _nouveau_perfmon_dtor,
+               .init = _nouveau_perfmon_init,
+               .fini = nvc0_perfmon_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/priv.h b/drivers/gpu/drm/nouveau/core/engine/perfmon/priv.h
new file mode 100644 (file)
index 0000000..0ac8714
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef __NVKM_PERFMON_PRIV_H__
+#define __NVKM_PERFMON_PRIV_H__
+
+#include <engine/perfmon.h>
+
+struct nouveau_perfctr {
+       struct nouveau_object base;
+       struct list_head head;
+       struct nouveau_perfsig *signal[4];
+       int slot;
+       u32 logic_op;
+       u32 clk;
+       u32 ctr;
+};
+
+extern struct nouveau_oclass nouveau_perfmon_sclass[];
+
+struct nouveau_perfctx {
+       struct nouveau_engctx base;
+};
+
+extern struct nouveau_oclass nouveau_perfmon_cclass;
+
+struct nouveau_specsig {
+       u8 signal;
+       const char *name;
+};
+
+struct nouveau_perfsig {
+       const char *name;
+};
+
+struct nouveau_perfdom;
+struct nouveau_perfctr *
+nouveau_perfsig_wrap(struct nouveau_perfmon *, const char *,
+                    struct nouveau_perfdom **);
+
+struct nouveau_specdom {
+       u16 signal_nr;
+       const struct nouveau_specsig *signal;
+       const struct nouveau_funcdom *func;
+};
+
+extern const struct nouveau_specdom nva3_perfmon_pwr[];
+extern const struct nouveau_specdom nvc0_perfmon_pwr[];
+extern const struct nouveau_specdom nve0_perfmon_pwr[];
+
+struct nouveau_perfdom {
+       struct list_head head;
+       struct list_head list;
+       const struct nouveau_funcdom *func;
+       char name[32];
+       u32 addr;
+       u8  quad;
+       u32 signal_nr;
+       struct nouveau_perfsig signal[];
+};
+
+struct nouveau_funcdom {
+       void (*init)(struct nouveau_perfmon *, struct nouveau_perfdom *,
+                    struct nouveau_perfctr *);
+       void (*read)(struct nouveau_perfmon *, struct nouveau_perfdom *,
+                    struct nouveau_perfctr *);
+       void (*next)(struct nouveau_perfmon *, struct nouveau_perfdom *);
+};
+
+int nouveau_perfdom_new(struct nouveau_perfmon *, const char *, u32,
+                       u32, u32, u32, const struct nouveau_specdom *);
+
+#define nouveau_perfmon_create(p,e,o,d)                                        \
+       nouveau_perfmon_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_perfmon_dtor(p) ({                                             \
+       struct nouveau_perfmon *c = (p);                                       \
+       _nouveau_perfmon_dtor(nv_object(c));                                   \
+})
+#define nouveau_perfmon_init(p) ({                                             \
+       struct nouveau_perfmon *c = (p);                                       \
+       _nouveau_perfmon_init(nv_object(c));                                   \
+})
+#define nouveau_perfmon_fini(p,s) ({                                           \
+       struct nouveau_perfmon *c = (p);                                       \
+       _nouveau_perfmon_fini(nv_object(c), (s));                              \
+})
+
+int nouveau_perfmon_create_(struct nouveau_object *, struct nouveau_object *,
+                           struct nouveau_oclass *, int, void **);
+void _nouveau_perfmon_dtor(struct nouveau_object *);
+int  _nouveau_perfmon_init(struct nouveau_object *);
+int  _nouveau_perfmon_fini(struct nouveau_object *, bool);
+
+#endif
index 2a859a3..c571758 100644 (file)
@@ -135,8 +135,8 @@ nv04_software_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        return 0;
 }
 
-struct nouveau_oclass
-nv04_software_oclass = {
+struct nouveau_oclass *
+nv04_software_oclass = &(struct nouveau_oclass) {
        .handle = NV_ENGINE(SW, 0x04),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv04_software_ctor,
index a019364..a62f11a 100644 (file)
@@ -117,8 +117,8 @@ nv10_software_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        return 0;
 }
 
-struct nouveau_oclass
-nv10_software_oclass = {
+struct nouveau_oclass *
+nv10_software_oclass = &(struct nouveau_oclass) {
        .handle = NV_ENGINE(SW, 0x10),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv10_software_ctor,
index c48e749..b574dd4 100644 (file)
 
 #include <subdev/bar.h>
 
-#include <engine/software.h>
 #include <engine/disp.h>
 
-struct nv50_software_priv {
-       struct nouveau_software base;
-};
-
-struct nv50_software_chan {
-       struct nouveau_software_chan base;
-};
+#include "nv50.h"
 
 /*******************************************************************************
  * software object classes
@@ -62,7 +55,7 @@ nv50_software_mthd_dma_vblsem(struct nouveau_object *object, u32 mthd,
 
        if (nv_iclass(handle->object, NV_GPUOBJ_CLASS)) {
                struct nouveau_gpuobj *gpuobj = nv_gpuobj(handle->object);
-               chan->base.vblank.ctxdma = gpuobj->node->offset >> 4;
+               chan->vblank.ctxdma = gpuobj->node->offset >> 4;
                ret = 0;
        }
        nouveau_namedb_put(handle);
@@ -74,34 +67,33 @@ nv50_software_mthd_vblsem_offset(struct nouveau_object *object, u32 mthd,
                                 void *args, u32 size)
 {
        struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
-       chan->base.vblank.offset = *(u32 *)args;
+       chan->vblank.offset = *(u32 *)args;
        return 0;
 }
 
-static int
+int
 nv50_software_mthd_vblsem_value(struct nouveau_object *object, u32 mthd,
                                void *args, u32 size)
 {
        struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
-       chan->base.vblank.value = *(u32 *)args;
+       chan->vblank.value = *(u32 *)args;
        return 0;
 }
 
-static int
+int
 nv50_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
                                  void *args, u32 size)
 {
        struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
-       struct nouveau_disp *disp = nouveau_disp(object);
-       u32 crtc = *(u32 *)args;
-       if (crtc > 1)
+       u32 head = *(u32 *)args;
+       if (head >= chan->vblank.nr_event)
                return -EINVAL;
 
-       nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event);
+       nouveau_event_get(chan->vblank.event[head]);
        return 0;
 }
 
-static int
+int
 nv50_software_mthd_flip(struct nouveau_object *object, u32 mthd,
                        void *args, u32 size)
 {
@@ -132,10 +124,9 @@ nv50_software_sclass[] = {
  ******************************************************************************/
 
 static int
-nv50_software_vblsem_release(struct nouveau_eventh *event, int head)
+nv50_software_vblsem_release(void *data, int head)
 {
-       struct nouveau_software_chan *chan =
-               container_of(event, struct nouveau_software_chan, vblank.event);
+       struct nv50_software_chan *chan = data;
        struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
        struct nouveau_bar *bar = nouveau_bar(priv);
 
@@ -154,45 +145,76 @@ nv50_software_vblsem_release(struct nouveau_eventh *event, int head)
        return NVKM_EVENT_DROP;
 }
 
-static int
+void
+nv50_software_context_dtor(struct nouveau_object *object)
+{
+       struct nv50_software_chan *chan = (void *)object;
+       int i;
+
+       if (chan->vblank.event) {
+               for (i = 0; i < chan->vblank.nr_event; i++)
+                       nouveau_event_ref(NULL, &chan->vblank.event[i]);
+               kfree(chan->vblank.event);
+       }
+
+       nouveau_software_context_destroy(&chan->base);
+}
+
+int
 nv50_software_context_ctor(struct nouveau_object *parent,
                           struct nouveau_object *engine,
                           struct nouveau_oclass *oclass, void *data, u32 size,
                           struct nouveau_object **pobject)
 {
+       struct nouveau_disp *pdisp = nouveau_disp(parent);
+       struct nv50_software_cclass *pclass = (void *)oclass;
        struct nv50_software_chan *chan;
-       int ret;
+       int ret, i;
 
        ret = nouveau_software_context_create(parent, engine, oclass, &chan);
        *pobject = nv_object(chan);
        if (ret)
                return ret;
 
-       chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
-       chan->base.vblank.event.func = nv50_software_vblsem_release;
+       chan->vblank.nr_event = pdisp->vblank->index_nr;
+       chan->vblank.event = kzalloc(chan->vblank.nr_event *
+                                    sizeof(*chan->vblank.event), GFP_KERNEL);
+       if (!chan->vblank.event)
+               return -ENOMEM;
+
+       for (i = 0; i < chan->vblank.nr_event; i++) {
+               ret = nouveau_event_new(pdisp->vblank, i, pclass->vblank,
+                                       chan, &chan->vblank.event[i]);
+               if (ret)
+                       return ret;
+       }
+
+       chan->vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
        return 0;
 }
 
-static struct nouveau_oclass
+static struct nv50_software_cclass
 nv50_software_cclass = {
-       .handle = NV_ENGCTX(SW, 0x50),
-       .ofuncs = &(struct nouveau_ofuncs) {
+       .base.handle = NV_ENGCTX(SW, 0x50),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv50_software_context_ctor,
                .dtor = _nouveau_software_context_dtor,
                .init = _nouveau_software_context_init,
                .fini = _nouveau_software_context_fini,
        },
+       .vblank = nv50_software_vblsem_release,
 };
 
 /*******************************************************************************
  * software engine/subdev functions
  ******************************************************************************/
 
-static int
+int
 nv50_software_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                   struct nouveau_oclass *oclass, void *data, u32 size,
                   struct nouveau_object **pobject)
 {
+       struct nv50_software_oclass *pclass = (void *)oclass;
        struct nv50_software_priv *priv;
        int ret;
 
@@ -201,19 +223,21 @@ nv50_software_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       nv_engine(priv)->cclass = &nv50_software_cclass;
-       nv_engine(priv)->sclass = nv50_software_sclass;
+       nv_engine(priv)->cclass = pclass->cclass;
+       nv_engine(priv)->sclass = pclass->sclass;
        nv_subdev(priv)->intr = nv04_software_intr;
        return 0;
 }
 
-struct nouveau_oclass
-nv50_software_oclass = {
-       .handle = NV_ENGINE(SW, 0x50),
-       .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nv50_software_oclass = &(struct nv50_software_oclass) {
+       .base.handle = NV_ENGINE(SW, 0x50),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv50_software_ctor,
                .dtor = _nouveau_software_dtor,
                .init = _nouveau_software_init,
                .fini = _nouveau_software_fini,
        },
-};
+       .cclass = &nv50_software_cclass.base,
+       .sclass =  nv50_software_sclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.h b/drivers/gpu/drm/nouveau/core/engine/software/nv50.h
new file mode 100644 (file)
index 0000000..2de370c
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef __NVKM_SW_NV50_H__
+#define __NVKM_SW_NV50_H__
+
+#include <engine/software.h>
+
+struct nv50_software_oclass {
+       struct nouveau_oclass base;
+       struct nouveau_oclass *cclass;
+       struct nouveau_oclass *sclass;
+};
+
+struct nv50_software_priv {
+       struct nouveau_software base;
+};
+
+int  nv50_software_ctor(struct nouveau_object *, struct nouveau_object *,
+                       struct nouveau_oclass *, void *, u32,
+                       struct nouveau_object **);
+
+struct nv50_software_cclass {
+       struct nouveau_oclass base;
+       int (*vblank)(void *, int);
+};
+
+struct nv50_software_chan {
+       struct nouveau_software_chan base;
+       struct {
+               struct nouveau_eventh **event;
+               int nr_event;
+               u32 channel;
+               u32 ctxdma;
+               u64 offset;
+               u32 value;
+       } vblank;
+};
+
+int  nv50_software_context_ctor(struct nouveau_object *,
+                               struct nouveau_object *,
+                               struct nouveau_oclass *, void *, u32,
+                               struct nouveau_object **);
+void nv50_software_context_dtor(struct nouveau_object *);
+
+int nv50_software_mthd_vblsem_value(struct nouveau_object *, u32, void *, u32);
+int nv50_software_mthd_vblsem_release(struct nouveau_object *, u32, void *, u32);
+int nv50_software_mthd_flip(struct nouveau_object *, u32, void *, u32);
+
+#endif
index d698e71..f9430c1 100644 (file)
 #include <engine/software.h>
 #include <engine/disp.h>
 
-struct nvc0_software_priv {
-       struct nouveau_software base;
-};
-
-struct nvc0_software_chan {
-       struct nouveau_software_chan base;
-};
+#include "nv50.h"
 
 /*******************************************************************************
  * software object classes
@@ -48,58 +42,24 @@ static int
 nvc0_software_mthd_vblsem_offset(struct nouveau_object *object, u32 mthd,
                                 void *args, u32 size)
 {
-       struct nvc0_software_chan *chan = (void *)nv_engctx(object->parent);
+       struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
        u64 data = *(u32 *)args;
        if (mthd == 0x0400) {
-               chan->base.vblank.offset &= 0x00ffffffffULL;
-               chan->base.vblank.offset |= data << 32;
+               chan->vblank.offset &= 0x00ffffffffULL;
+               chan->vblank.offset |= data << 32;
        } else {
-               chan->base.vblank.offset &= 0xff00000000ULL;
-               chan->base.vblank.offset |= data;
+               chan->vblank.offset &= 0xff00000000ULL;
+               chan->vblank.offset |= data;
        }
        return 0;
 }
 
 static int
-nvc0_software_mthd_vblsem_value(struct nouveau_object *object, u32 mthd,
-                               void *args, u32 size)
-{
-       struct nvc0_software_chan *chan = (void *)nv_engctx(object->parent);
-       chan->base.vblank.value = *(u32 *)args;
-       return 0;
-}
-
-static int
-nvc0_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
-                                 void *args, u32 size)
-{
-       struct nvc0_software_chan *chan = (void *)nv_engctx(object->parent);
-       struct nouveau_disp *disp = nouveau_disp(object);
-       u32 crtc = *(u32 *)args;
-
-       if ((nv_device(object)->card_type < NV_E0 && crtc > 1) || crtc > 3)
-               return -EINVAL;
-
-       nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event);
-       return 0;
-}
-
-static int
-nvc0_software_mthd_flip(struct nouveau_object *object, u32 mthd,
-                       void *args, u32 size)
-{
-       struct nvc0_software_chan *chan = (void *)nv_engctx(object->parent);
-       if (chan->base.flip)
-               return chan->base.flip(chan->base.flip_data);
-       return -EINVAL;
-}
-
-static int
 nvc0_software_mthd_mp_control(struct nouveau_object *object, u32 mthd,
                               void *args, u32 size)
 {
-       struct nvc0_software_chan *chan = (void *)nv_engctx(object->parent);
-       struct nvc0_software_priv *priv = (void *)nv_object(chan)->engine;
+       struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
+       struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
        u32 data = *(u32 *)args;
 
        switch (mthd) {
@@ -124,9 +84,9 @@ static struct nouveau_omthds
 nvc0_software_omthds[] = {
        { 0x0400, 0x0400, nvc0_software_mthd_vblsem_offset },
        { 0x0404, 0x0404, nvc0_software_mthd_vblsem_offset },
-       { 0x0408, 0x0408, nvc0_software_mthd_vblsem_value },
-       { 0x040c, 0x040c, nvc0_software_mthd_vblsem_release },
-       { 0x0500, 0x0500, nvc0_software_mthd_flip },
+       { 0x0408, 0x0408, nv50_software_mthd_vblsem_value },
+       { 0x040c, 0x040c, nv50_software_mthd_vblsem_release },
+       { 0x0500, 0x0500, nv50_software_mthd_flip },
        { 0x0600, 0x0600, nvc0_software_mthd_mp_control },
        { 0x0644, 0x0644, nvc0_software_mthd_mp_control },
        { 0x06ac, 0x06ac, nvc0_software_mthd_mp_control },
@@ -144,11 +104,10 @@ nvc0_software_sclass[] = {
  ******************************************************************************/
 
 static int
-nvc0_software_vblsem_release(struct nouveau_eventh *event, int head)
+nvc0_software_vblsem_release(void *data, int head)
 {
-       struct nouveau_software_chan *chan =
-               container_of(event, struct nouveau_software_chan, vblank.event);
-       struct nvc0_software_priv *priv = (void *)nv_object(chan)->engine;
+       struct nv50_software_chan *chan = data;
+       struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
        struct nouveau_bar *bar = nouveau_bar(priv);
 
        nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
@@ -160,66 +119,31 @@ nvc0_software_vblsem_release(struct nouveau_eventh *event, int head)
        return NVKM_EVENT_DROP;
 }
 
-static int
-nvc0_software_context_ctor(struct nouveau_object *parent,
-                          struct nouveau_object *engine,
-                          struct nouveau_oclass *oclass, void *data, u32 size,
-                          struct nouveau_object **pobject)
-{
-       struct nvc0_software_chan *chan;
-       int ret;
-
-       ret = nouveau_software_context_create(parent, engine, oclass, &chan);
-       *pobject = nv_object(chan);
-       if (ret)
-               return ret;
-
-       chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
-       chan->base.vblank.event.func = nvc0_software_vblsem_release;
-       return 0;
-}
-
-static struct nouveau_oclass
+static struct nv50_software_cclass
 nvc0_software_cclass = {
-       .handle = NV_ENGCTX(SW, 0xc0),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nvc0_software_context_ctor,
+       .base.handle = NV_ENGCTX(SW, 0xc0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_software_context_ctor,
                .dtor = _nouveau_software_context_dtor,
                .init = _nouveau_software_context_init,
                .fini = _nouveau_software_context_fini,
        },
+       .vblank = nvc0_software_vblsem_release,
 };
 
 /*******************************************************************************
  * software engine/subdev functions
  ******************************************************************************/
 
-static int
-nvc0_software_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-                  struct nouveau_oclass *oclass, void *data, u32 size,
-                  struct nouveau_object **pobject)
-{
-       struct nvc0_software_priv *priv;
-       int ret;
-
-       ret = nouveau_software_create(parent, engine, oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       nv_engine(priv)->cclass = &nvc0_software_cclass;
-       nv_engine(priv)->sclass = nvc0_software_sclass;
-       nv_subdev(priv)->intr = nv04_software_intr;
-       return 0;
-}
-
-struct nouveau_oclass
-nvc0_software_oclass = {
-       .handle = NV_ENGINE(SW, 0xc0),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nvc0_software_ctor,
+struct nouveau_oclass *
+nvc0_software_oclass = &(struct nv50_software_oclass) {
+       .base.handle = NV_ENGINE(SW, 0xc0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_software_ctor,
                .dtor = _nouveau_software_dtor,
                .init = _nouveau_software_init,
                .fini = _nouveau_software_fini,
        },
-};
+       .cclass = &nvc0_software_cclass.base,
+       .sclass =  nvc0_software_sclass,
+}.base;
index 5a5961b..560c359 100644 (file)
@@ -22,7 +22,7 @@
 #define NV_DEVICE_DISABLE_PPP                             0x0000004000000000ULL
 #define NV_DEVICE_DISABLE_COPY0                           0x0000008000000000ULL
 #define NV_DEVICE_DISABLE_COPY1                           0x0000010000000000ULL
-#define NV_DEVICE_DISABLE_UNK1C1                          0x0000020000000000ULL
+#define NV_DEVICE_DISABLE_VIC                             0x0000020000000000ULL
 #define NV_DEVICE_DISABLE_VENC                            0x0000040000000000ULL
 
 struct nv_device_class {
@@ -98,6 +98,77 @@ struct nv_dma_class {
        u32 conf0;
 };
 
+/* Perfmon counter class
+ *
+ * XXXX: NV_PERFCTR
+ */
+#define NV_PERFCTR_CLASS                                             0x0000ffff
+#define NV_PERFCTR_QUERY                                             0x00000000
+#define NV_PERFCTR_SAMPLE                                            0x00000001
+#define NV_PERFCTR_READ                                              0x00000002
+
+struct nv_perfctr_class {
+       u16 logic_op;
+       struct {
+               char __user *name; /*XXX: use cfu when exposed to userspace */
+               u32 size;
+       } signal[4];
+};
+
+struct nv_perfctr_query {
+       u32 iter;
+       u32 size;
+       char __user *name; /*XXX: use ctu when exposed to userspace */
+};
+
+struct nv_perfctr_sample {
+};
+
+struct nv_perfctr_read {
+       u32 ctr;
+       u32 clk;
+};
+
+/* Device control class
+ *
+ * XXXX: NV_CONTROL
+ */
+#define NV_CONTROL_CLASS                                             0x0000fffe
+
+#define NV_CONTROL_PSTATE_INFO                                       0x00000000
+#define NV_CONTROL_PSTATE_INFO_USTATE_DISABLE                              (-1)
+#define NV_CONTROL_PSTATE_INFO_USTATE_PERFMON                              (-2)
+#define NV_CONTROL_PSTATE_INFO_PSTATE_UNKNOWN                              (-1)
+#define NV_CONTROL_PSTATE_INFO_PSTATE_PERFMON                              (-2)
+#define NV_CONTROL_PSTATE_ATTR                                       0x00000001
+#define NV_CONTROL_PSTATE_ATTR_STATE_CURRENT                               (-1)
+#define NV_CONTROL_PSTATE_USER                                       0x00000002
+#define NV_CONTROL_PSTATE_USER_STATE_UNKNOWN                               (-1)
+#define NV_CONTROL_PSTATE_USER_STATE_PERFMON                               (-2)
+
+struct nv_control_pstate_info {
+       u32 count; /* out: number of power states */
+       s32 ustate; /* out: current target pstate index */
+       u32 pstate; /* out: current pstate index */
+};
+
+struct nv_control_pstate_attr {
+       s32 state; /*  in: index of pstate to query
+                   * out: pstate identifier
+                   */
+       u32 index; /*  in: index of attribute to query
+                   * out: index of next attribute, or 0 if no more
+                   */
+       char name[32];
+       char unit[16];
+       u32 min;
+       u32 max;
+};
+
+struct nv_control_pstate_user {
+       s32 state; /*  in: pstate identifier */
+};
+
 /* DMA FIFO channel classes
  *
  * 006b: NV03_CHANNEL_DMA
index 9ea18df..8092e2e 100644 (file)
@@ -1,13 +1,20 @@
 #ifndef __NOUVEAU_DEBUG_H__
 #define __NOUVEAU_DEBUG_H__
 
+extern int nv_info_debug_level;
+
 #define NV_DBG_FATAL    0
 #define NV_DBG_ERROR    1
 #define NV_DBG_WARN     2
-#define NV_DBG_INFO     3
+#define NV_DBG_INFO     nv_info_debug_level
 #define NV_DBG_DEBUG    4
 #define NV_DBG_TRACE    5
 #define NV_DBG_PARANOIA 6
 #define NV_DBG_SPAM     7
 
+#define NV_DBG_INFO_NORMAL 3
+#define NV_DBG_INFO_SILENT NV_DBG_DEBUG
+
+#define nv_debug_level(a) nv_info_debug_level = NV_DBG_INFO_##a
+
 #endif
index 99b6600..ac2881d 100644 (file)
@@ -33,9 +33,10 @@ enum nv_subdev_type {
        NVDEV_SUBDEV_INSTMEM,
        NVDEV_SUBDEV_VM,
        NVDEV_SUBDEV_BAR,
+       NVDEV_SUBDEV_PWR,
        NVDEV_SUBDEV_VOLT,
-       NVDEV_SUBDEV_CLOCK,
        NVDEV_SUBDEV_THERM,
+       NVDEV_SUBDEV_CLOCK,
 
        NVDEV_ENGINE_DMAOBJ,
        NVDEV_ENGINE_FIFO,
@@ -50,9 +51,10 @@ enum nv_subdev_type {
        NVDEV_ENGINE_COPY0,
        NVDEV_ENGINE_COPY1,
        NVDEV_ENGINE_COPY2,
-       NVDEV_ENGINE_UNK1C1,
+       NVDEV_ENGINE_VIC,
        NVDEV_ENGINE_VENC,
        NVDEV_ENGINE_DISP,
+       NVDEV_ENGINE_PERFMON,
 
        NVDEV_SUBDEV_NR,
 };
@@ -72,6 +74,7 @@ struct nouveau_device {
        enum {
                NV_04    = 0x04,
                NV_10    = 0x10,
+               NV_11    = 0x11,
                NV_20    = 0x20,
                NV_30    = 0x30,
                NV_40    = 0x40,
index 9e09440..5d539eb 100644 (file)
@@ -5,13 +5,21 @@
 #define NVKM_EVENT_DROP 0
 #define NVKM_EVENT_KEEP 1
 
+/* nouveau_eventh.flags bit #s */
+#define NVKM_EVENT_ENABLE 0
+
 struct nouveau_eventh {
+       struct nouveau_event *event;
        struct list_head head;
-       int (*func)(struct nouveau_eventh *, int index);
+       unsigned long flags;
+       int index;
+       int (*func)(void *, int);
+       void *priv;
 };
 
 struct nouveau_event {
-       spinlock_t lock;
+       spinlock_t list_lock;
+       spinlock_t refs_lock;
 
        void *priv;
        void (*enable)(struct nouveau_event *, int index);
@@ -28,9 +36,11 @@ int  nouveau_event_create(int index_nr, struct nouveau_event **);
 void nouveau_event_destroy(struct nouveau_event **);
 void nouveau_event_trigger(struct nouveau_event *, int index);
 
-void nouveau_event_get(struct nouveau_event *, int index,
-                      struct nouveau_eventh *);
-void nouveau_event_put(struct nouveau_event *, int index,
-                      struct nouveau_eventh *);
+int  nouveau_event_new(struct nouveau_event *, int index,
+                      int (*func)(void *, int), void *,
+                      struct nouveau_eventh **);
+void nouveau_event_ref(struct nouveau_eventh *, struct nouveau_eventh **);
+void nouveau_event_get(struct nouveau_eventh *);
+void nouveau_event_put(struct nouveau_eventh *);
 
 #endif
index 2707495..ed05584 100644 (file)
@@ -8,4 +8,13 @@ bool nouveau_boolopt(const char *optstr, const char *opt, bool value);
 
 int nouveau_dbgopt(const char *optstr, const char *sub);
 
+/* compares unterminated string 'str' with zero-terminated string 'cmp' */
+static inline int
+strncasecmpz(const char *str, const char *cmp, size_t len)
+{
+       if (strlen(cmp) != len)
+               return len;
+       return strncasecmp(str, cmp, len);
+}
+
 #endif
index d87836e..0f9a37b 100644 (file)
@@ -6,27 +6,12 @@
 
 struct nouveau_object;
 
-#define NV_PRINTK_FATAL    KERN_CRIT
-#define NV_PRINTK_ERROR    KERN_ERR
-#define NV_PRINTK_WARN     KERN_WARNING
-#define NV_PRINTK_INFO     KERN_INFO
-#define NV_PRINTK_DEBUG    KERN_DEBUG
-#define NV_PRINTK_PARANOIA KERN_DEBUG
-#define NV_PRINTK_TRACE    KERN_DEBUG
-#define NV_PRINTK_SPAM     KERN_DEBUG
-
-extern int nv_printk_suspend_level;
-
-#define NV_DBG_SUSPEND (nv_printk_suspend_level)
-#define NV_PRINTK_SUSPEND  (nv_printk_level_to_pfx(nv_printk_suspend_level))
-
-const char *nv_printk_level_to_pfx(int level);
-void __printf(4, 5)
-nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
+void __printf(3, 4)
+nv_printk_(struct nouveau_object *, int, const char *, ...);
 
 #define nv_printk(o,l,f,a...) do {                                             \
        if (NV_DBG_##l <= CONFIG_NOUVEAU_DEBUG)                                \
-               nv_printk_(nv_object(o), NV_PRINTK_##l, NV_DBG_##l, f, ##a);   \
+               nv_printk_(nv_object(o), NV_DBG_##l, f, ##a);                  \
 } while(0)
 
 #define nv_fatal(o,f,a...) nv_printk((o), FATAL, f, ##a)
@@ -37,16 +22,9 @@ nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
 #define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a)
 #define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a)
 
-#define nv_suspend(o,f,a...) nv_printk((o), SUSPEND, f, ##a)
-
-static inline void nv_suspend_set_printk_level(int level)
-{
-       nv_printk_suspend_level = level;
-}
-
 #define nv_assert(f,a...) do {                                                 \
        if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG)                              \
-               nv_printk_(NULL, NV_PRINTK_FATAL, NV_DBG_FATAL, f "\n", ##a);  \
+               nv_printk_(NULL, NV_DBG_FATAL, f "\n", ##a);                   \
        BUG_ON(1);                                                             \
 } while(0)
 
index 633c2f8..8c32cf4 100644 (file)
@@ -101,14 +101,14 @@ nouveau_client_name_for_fifo_chid(struct nouveau_fifo *fifo, u32 chid);
 #define _nouveau_fifo_init _nouveau_engine_init
 #define _nouveau_fifo_fini _nouveau_engine_fini
 
-extern struct nouveau_oclass nv04_fifo_oclass;
-extern struct nouveau_oclass nv10_fifo_oclass;
-extern struct nouveau_oclass nv17_fifo_oclass;
-extern struct nouveau_oclass nv40_fifo_oclass;
-extern struct nouveau_oclass nv50_fifo_oclass;
-extern struct nouveau_oclass nv84_fifo_oclass;
-extern struct nouveau_oclass nvc0_fifo_oclass;
-extern struct nouveau_oclass nve0_fifo_oclass;
+extern struct nouveau_oclass *nv04_fifo_oclass;
+extern struct nouveau_oclass *nv10_fifo_oclass;
+extern struct nouveau_oclass *nv17_fifo_oclass;
+extern struct nouveau_oclass *nv40_fifo_oclass;
+extern struct nouveau_oclass *nv50_fifo_oclass;
+extern struct nouveau_oclass *nv84_fifo_oclass;
+extern struct nouveau_oclass *nvc0_fifo_oclass;
+extern struct nouveau_oclass *nve0_fifo_oclass;
 
 void nv04_fifo_intr(struct nouveau_subdev *);
 int  nv04_fifo_context_attach(struct nouveau_object *, struct nouveau_object *);
index 1d1a89a..9b0d938 100644 (file)
@@ -42,10 +42,13 @@ struct nouveau_mpeg {
 
 extern struct nouveau_oclass nv31_mpeg_oclass;
 extern struct nouveau_oclass nv40_mpeg_oclass;
+extern struct nouveau_oclass nv44_mpeg_oclass;
 extern struct nouveau_oclass nv50_mpeg_oclass;
 extern struct nouveau_oclass nv84_mpeg_oclass;
-
+extern struct nouveau_ofuncs nv31_mpeg_ofuncs;
+extern struct nouveau_oclass nv31_mpeg_cclass;
 extern struct nouveau_oclass nv31_mpeg_sclass[];
+extern struct nouveau_oclass nv40_mpeg_sclass[];
 void nv31_mpeg_intr(struct nouveau_subdev *);
 void nv31_mpeg_tile_prog(struct nouveau_engine *, int);
 int  nv31_mpeg_init(struct nouveau_object *);
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/perfmon.h b/drivers/gpu/drm/nouveau/core/include/engine/perfmon.h
new file mode 100644 (file)
index 0000000..49b0024
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef __NVKM_PERFMON_H__
+#define __NVKM_PERFMON_H__
+
+#include <core/device.h>
+#include <core/engine.h>
+#include <core/engctx.h>
+#include <core/class.h>
+
+struct nouveau_perfdom;
+struct nouveau_perfctr;
+struct nouveau_perfmon {
+       struct nouveau_engine base;
+
+       struct nouveau_perfctx *context;
+       void *profile_data;
+
+       struct list_head domains;
+       u32 sequence;
+
+       /*XXX: temp for daemon backend */
+       u32 pwr[8];
+       u32 last;
+};
+
+static inline struct nouveau_perfmon *
+nouveau_perfmon(void *obj)
+{
+       return (void *)nv_device(obj)->subdev[NVDEV_ENGINE_PERFMON];
+}
+
+extern struct nouveau_oclass *nv40_perfmon_oclass;
+extern struct nouveau_oclass *nv50_perfmon_oclass;
+extern struct nouveau_oclass *nv84_perfmon_oclass;
+extern struct nouveau_oclass *nva3_perfmon_oclass;
+extern struct nouveau_oclass nvc0_perfmon_oclass;
+extern struct nouveau_oclass nve0_perfmon_oclass;
+extern struct nouveau_oclass nvf0_perfmon_oclass;
+
+#endif
index 4579948..23a462b 100644 (file)
@@ -3,19 +3,10 @@
 
 #include <core/engine.h>
 #include <core/engctx.h>
-#include <core/event.h>
 
 struct nouveau_software_chan {
        struct nouveau_engctx base;
 
-       struct {
-               struct nouveau_eventh event;
-               u32 channel;
-               u32 ctxdma;
-               u64 offset;
-               u32 value;
-       } vblank;
-
        int (*flip)(void *);
        void *flip_data;
 };
@@ -50,10 +41,10 @@ struct nouveau_software {
 #define _nouveau_software_init _nouveau_engine_init
 #define _nouveau_software_fini _nouveau_engine_fini
 
-extern struct nouveau_oclass nv04_software_oclass;
-extern struct nouveau_oclass nv10_software_oclass;
-extern struct nouveau_oclass nv50_software_oclass;
-extern struct nouveau_oclass nvc0_software_oclass;
+extern struct nouveau_oclass *nv04_software_oclass;
+extern struct nouveau_oclass *nv10_software_oclass;
+extern struct nouveau_oclass *nv50_software_oclass;
+extern struct nouveau_oclass *nvc0_software_oclass;
 
 void nv04_software_intr(struct nouveau_subdev *);
 
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/boost.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/boost.h
new file mode 100644 (file)
index 0000000..662b207
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __NVBIOS_BOOST_H__
+#define __NVBIOS_BOOST_H__
+
+u16 nvbios_boostTe(struct nouveau_bios *, u8 *, u8 *, u8 *, u8 *, u8 *, u8 *);
+
+struct nvbios_boostE {
+       u8  pstate;
+       u32 min;
+       u32 max;
+};
+
+u16 nvbios_boostEe(struct nouveau_bios *, int idx, u8 *, u8 *, u8 *, u8 *);
+u16 nvbios_boostEp(struct nouveau_bios *, int idx, u8 *, u8 *, u8 *, u8 *,
+                  struct nvbios_boostE *);
+u16 nvbios_boostEm(struct nouveau_bios *, u8, u8 *, u8 *, u8 *, u8 *,
+                  struct nvbios_boostE *);
+
+struct nvbios_boostS {
+       u8  domain;
+       u8  percent;
+       u32 min;
+       u32 max;
+};
+
+u16 nvbios_boostSe(struct nouveau_bios *, int, u16, u8 *, u8 *, u8, u8);
+u16 nvbios_boostSp(struct nouveau_bios *, int, u16, u8 *, u8 *, u8, u8,
+                  struct nvbios_boostS *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/cstep.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/cstep.h
new file mode 100644 (file)
index 0000000..a80a438
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __NVBIOS_CSTEP_H__
+#define __NVBIOS_CSTEP_H__
+
+u16 nvbios_cstepTe(struct nouveau_bios *,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz);
+
+struct nvbios_cstepE {
+       u8  pstate;
+       u8  index;
+};
+
+u16 nvbios_cstepEe(struct nouveau_bios *, int idx, u8 *ver, u8 *hdr);
+u16 nvbios_cstepEp(struct nouveau_bios *, int idx, u8 *ver, u8 *hdr,
+                  struct nvbios_cstepE *);
+u16 nvbios_cstepEm(struct nouveau_bios *, u8 pstate, u8 *ver, u8 *hdr,
+                  struct nvbios_cstepE *);
+
+struct nvbios_cstepX {
+       u32 freq;
+       u8  unkn[2];
+       u8  voltage;
+};
+
+u16 nvbios_cstepXe(struct nouveau_bios *, int idx, u8 *ver, u8 *hdr);
+u16 nvbios_cstepXp(struct nouveau_bios *, int idx, u8 *ver, u8 *hdr,
+                  struct nvbios_cstepX *);
+
+#endif
index 96d3364..c7b2e58 100644 (file)
@@ -7,7 +7,15 @@ enum dcb_gpio_func_name {
        DCB_GPIO_TVDAC1 = 0x2d,
        DCB_GPIO_FAN = 0x09,
        DCB_GPIO_FAN_SENSE = 0x3d,
-       DCB_GPIO_UNUSED = 0xff
+       DCB_GPIO_UNUSED = 0xff,
+       DCB_GPIO_VID0 = 0x04,
+       DCB_GPIO_VID1 = 0x05,
+       DCB_GPIO_VID2 = 0x06,
+       DCB_GPIO_VID3 = 0x1a,
+       DCB_GPIO_VID4 = 0x73,
+       DCB_GPIO_VID5 = 0x74,
+       DCB_GPIO_VID6 = 0x75,
+       DCB_GPIO_VID7 = 0x76,
 };
 
 #define DCB_GPIO_LOG_DIR     0x02
index 0b285e9..16ff06e 100644 (file)
@@ -3,6 +3,39 @@
 
 struct nouveau_bios;
 
+u16 nvbios_perf_table(struct nouveau_bios *, u8 *ver, u8 *hdr,
+                     u8 *cnt, u8 *len, u8 *snr, u8 *ssz);
+
+struct nvbios_perfE {
+       u8  pstate;
+       u8  fanspeed;
+       u8  voltage;
+       u32 core;
+       u32 shader;
+       u32 memory;
+       u32 vdec;
+       u32 disp;
+       u32 script;
+};
+
+u16 nvbios_perf_entry(struct nouveau_bios *, int idx,
+                     u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u16 nvbios_perfEp(struct nouveau_bios *, int idx,
+                 u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_perfE *);
+
+struct nvbios_perfS {
+       union {
+               struct {
+                       u32 freq;
+               } v40;
+       };
+};
+
+u32 nvbios_perfSe(struct nouveau_bios *, u32 data, int idx,
+                 u8 *ver, u8 *hdr, u8 cnt, u8 len);
+u32 nvbios_perfSp(struct nouveau_bios *, u32 data, int idx,
+                 u8 *ver, u8 *hdr, u8 cnt, u8 len, struct nvbios_perfS *);
+
 struct nvbios_perf_fan {
        u32 pwm_divisor;
 };
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h
new file mode 100644 (file)
index 0000000..bc15e03
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __NVBIOS_RAMMAP_H__
+#define __NVBIOS_RAMMAP_H__
+
+u16 nvbios_rammap_table(struct nouveau_bios *, u8 *ver, u8 *hdr,
+                       u8 *cnt, u8 *len, u8 *snr, u8 *ssz);
+u16 nvbios_rammap_entry(struct nouveau_bios *, int idx,
+                       u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u16 nvbios_rammap_match(struct nouveau_bios *, u16 khz,
+                       u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/timing.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/timing.h
new file mode 100644 (file)
index 0000000..963694b
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __NVBIOS_TIMING_H__
+#define __NVBIOS_TIMING_H__
+
+u16 nvbios_timing_table(struct nouveau_bios *,
+                       u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u16 nvbios_timing_entry(struct nouveau_bios *, int idx, u8 *ver, u8 *hdr);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/vmap.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/vmap.h
new file mode 100644 (file)
index 0000000..ad5a8f2
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef __NVBIOS_VMAP_H__
+#define __NVBIOS_VMAP_H__
+
+struct nouveau_bios;
+
+struct nvbios_vmap {
+};
+
+u16 nvbios_vmap_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u16 nvbios_vmap_parse(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                     struct nvbios_vmap *);
+
+struct nvbios_vmap_entry {
+       u8  unk0;
+       u8  link;
+       u32 min;
+       u32 max;
+       s32 arg[6];
+};
+
+u16 nvbios_vmap_entry(struct nouveau_bios *, int idx, u8 *ver, u8 *len);
+u16 nvbios_vmap_entry_parse(struct nouveau_bios *, int idx, u8 *ver, u8 *len,
+                           struct nvbios_vmap_entry *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/volt.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/volt.h
new file mode 100644 (file)
index 0000000..6a11dcd
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __NVBIOS_VOLT_H__
+#define __NVBIOS_VOLT_H__
+
+struct nouveau_bios;
+
+struct nvbios_volt {
+       u8  vidmask;
+       u32 min;
+       u32 max;
+       u32 base;
+       s16 step;
+};
+
+u16 nvbios_volt_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u16 nvbios_volt_parse(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                     struct nvbios_volt *);
+
+struct nvbios_volt_entry {
+       u32 voltage;
+       u8  vid;
+};
+
+u16 nvbios_volt_entry(struct nouveau_bios *, int idx, u8 *ver, u8 *len);
+u16 nvbios_volt_entry_parse(struct nouveau_bios *, int idx, u8 *ver, u8 *len,
+                           struct nvbios_volt_entry *);
+
+#endif
index 7d88ec4..697f7ce 100644 (file)
@@ -11,6 +11,8 @@ struct nouveau_bus_intr {
 
 struct nouveau_bus {
        struct nouveau_subdev base;
+       int (*hwsq_exec)(struct nouveau_bus *, u32 *, u32);
+       u32 hwsq_size;
 };
 
 static inline struct nouveau_bus *
@@ -33,9 +35,19 @@ nouveau_bus(void *obj)
 #define _nouveau_bus_init _nouveau_subdev_init
 #define _nouveau_bus_fini _nouveau_subdev_fini
 
-extern struct nouveau_oclass nv04_bus_oclass;
-extern struct nouveau_oclass nv31_bus_oclass;
-extern struct nouveau_oclass nv50_bus_oclass;
-extern struct nouveau_oclass nvc0_bus_oclass;
+extern struct nouveau_oclass *nv04_bus_oclass;
+extern struct nouveau_oclass *nv31_bus_oclass;
+extern struct nouveau_oclass *nv50_bus_oclass;
+extern struct nouveau_oclass *nv94_bus_oclass;
+extern struct nouveau_oclass *nvc0_bus_oclass;
+
+/* interface to sequencer */
+struct nouveau_hwsq;
+int  nouveau_hwsq_init(struct nouveau_bus *, struct nouveau_hwsq **);
+int  nouveau_hwsq_fini(struct nouveau_hwsq **, bool exec);
+void nouveau_hwsq_wr32(struct nouveau_hwsq *, u32 addr, u32 data);
+void nouveau_hwsq_setf(struct nouveau_hwsq *, u8 flag, int data);
+void nouveau_hwsq_wait(struct nouveau_hwsq *, u8 flag, u8 data);
+void nouveau_hwsq_nsec(struct nouveau_hwsq *, u32 nsec);
 
 #endif
index 89ee289..e2675bc 100644 (file)
@@ -7,9 +7,78 @@
 struct nouveau_pll_vals;
 struct nvbios_pll;
 
+enum nv_clk_src {
+       nv_clk_src_crystal,
+       nv_clk_src_href,
+
+       nv_clk_src_hclk,
+       nv_clk_src_hclkm3,
+       nv_clk_src_hclkm3d2,
+
+       nv_clk_src_host,
+
+       nv_clk_src_sppll0,
+       nv_clk_src_sppll1,
+
+       nv_clk_src_mpllsrcref,
+       nv_clk_src_mpllsrc,
+       nv_clk_src_mpll,
+       nv_clk_src_mdiv,
+
+       nv_clk_src_core,
+       nv_clk_src_shader,
+
+       nv_clk_src_mem,
+
+       nv_clk_src_gpc,
+       nv_clk_src_rop,
+       nv_clk_src_hubk01,
+       nv_clk_src_hubk06,
+       nv_clk_src_hubk07,
+       nv_clk_src_copy,
+       nv_clk_src_daemon,
+       nv_clk_src_disp,
+       nv_clk_src_vdec,
+
+       nv_clk_src_dom6,
+
+       nv_clk_src_max,
+};
+
+struct nouveau_cstate {
+       struct list_head head;
+       u8  voltage;
+       u32 domain[nv_clk_src_max];
+};
+
+struct nouveau_pstate {
+       struct list_head head;
+       struct list_head list; /* c-states */
+       struct nouveau_cstate base;
+       u8 pstate;
+       u8 fanspeed;
+};
+
 struct nouveau_clock {
        struct nouveau_subdev base;
 
+       struct nouveau_clocks *domains;
+       struct nouveau_pstate bstate;
+
+       struct list_head states;
+       int state_nr;
+
+       int pstate; /* current */
+       int ustate; /* user-requested (-1 disabled, -2 perfmon) */
+       int astate; /* perfmon adjustment (base) */
+       int tstate; /* thermal adjustment (max-) */
+       int dstate; /* display adjustment (min+) */
+
+       int  (*read)(struct nouveau_clock *, enum nv_clk_src);
+       int  (*calc)(struct nouveau_clock *, struct nouveau_cstate *);
+       int  (*prog)(struct nouveau_clock *);
+       void (*tidy)(struct nouveau_clock *);
+
        /*XXX: die, these are here *only* to support the completely
         *     bat-shit insane what-was-nouveau_hw.c code
         */
@@ -25,27 +94,42 @@ nouveau_clock(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_CLOCK];
 }
 
-#define nouveau_clock_create(p,e,o,d)                                          \
-       nouveau_subdev_create((p), (e), (o), 0, "CLOCK", "clock", d)
-#define nouveau_clock_destroy(p)                                               \
-       nouveau_subdev_destroy(&(p)->base)
-#define nouveau_clock_init(p)                                                  \
-       nouveau_subdev_init(&(p)->base)
+struct nouveau_clocks {
+       enum nv_clk_src name;
+       u8 bios; /* 0xff for none */
+#define NVKM_CLK_DOM_FLAG_CORE 0x01
+       u8 flags;
+       const char *mname;
+       int mdiv;
+};
+
+#define nouveau_clock_create(p,e,o,i,d)                                        \
+       nouveau_clock_create_((p), (e), (o), (i), sizeof(**d), (void **)d)
+#define nouveau_clock_destroy(p) ({                                            \
+       struct nouveau_clock *clk = (p);                                       \
+       _nouveau_clock_dtor(nv_object(clk));                                   \
+})
+#define nouveau_clock_init(p) ({                                               \
+       struct nouveau_clock *clk = (p);                                       \
+       _nouveau_clock_init(nv_object(clk));                                   \
+})
 #define nouveau_clock_fini(p,s)                                                \
        nouveau_subdev_fini(&(p)->base, (s))
 
 int  nouveau_clock_create_(struct nouveau_object *, struct nouveau_object *,
-                          struct nouveau_oclass *, void *, u32, int, void **);
-
-#define _nouveau_clock_dtor _nouveau_subdev_dtor
-#define _nouveau_clock_init _nouveau_subdev_init
+                          struct nouveau_oclass *,
+                          struct nouveau_clocks *, int, void **);
+void _nouveau_clock_dtor(struct nouveau_object *);
+int _nouveau_clock_init(struct nouveau_object *);
 #define _nouveau_clock_fini _nouveau_subdev_fini
 
 extern struct nouveau_oclass nv04_clock_oclass;
 extern struct nouveau_oclass nv40_clock_oclass;
-extern struct nouveau_oclass nv50_clock_oclass;
+extern struct nouveau_oclass *nv50_clock_oclass;
+extern struct nouveau_oclass *nv84_clock_oclass;
 extern struct nouveau_oclass nva3_clock_oclass;
 extern struct nouveau_oclass nvc0_clock_oclass;
+extern struct nouveau_oclass nve0_clock_oclass;
 
 int nv04_clock_pll_set(struct nouveau_clock *, u32 type, u32 freq);
 int nv04_clock_pll_calc(struct nouveau_clock *, struct nvbios_pll *,
@@ -55,4 +139,9 @@ int nv04_clock_pll_prog(struct nouveau_clock *, u32 reg1,
 int nva3_clock_pll_calc(struct nouveau_clock *, struct nvbios_pll *,
                        int clk, struct nouveau_pll_vals *);
 
+int nouveau_clock_ustate(struct nouveau_clock *, int req);
+int nouveau_clock_astate(struct nouveau_clock *, int req, int rel);
+int nouveau_clock_dstate(struct nouveau_clock *, int req, int rel);
+int nouveau_clock_tstate(struct nouveau_clock *, int req, int rel);
+
 #endif
index 2e74050..8541aa3 100644 (file)
@@ -78,23 +78,28 @@ nouveau_fb(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_FB];
 }
 
-extern struct nouveau_oclass nv04_fb_oclass;
-extern struct nouveau_oclass nv10_fb_oclass;
-extern struct nouveau_oclass nv1a_fb_oclass;
-extern struct nouveau_oclass nv20_fb_oclass;
-extern struct nouveau_oclass nv25_fb_oclass;
-extern struct nouveau_oclass nv30_fb_oclass;
-extern struct nouveau_oclass nv35_fb_oclass;
-extern struct nouveau_oclass nv36_fb_oclass;
-extern struct nouveau_oclass nv40_fb_oclass;
-extern struct nouveau_oclass nv41_fb_oclass;
-extern struct nouveau_oclass nv44_fb_oclass;
-extern struct nouveau_oclass nv46_fb_oclass;
-extern struct nouveau_oclass nv47_fb_oclass;
-extern struct nouveau_oclass nv49_fb_oclass;
-extern struct nouveau_oclass nv4e_fb_oclass;
-extern struct nouveau_oclass nv50_fb_oclass;
-extern struct nouveau_oclass nvc0_fb_oclass;
+extern struct nouveau_oclass *nv04_fb_oclass;
+extern struct nouveau_oclass *nv10_fb_oclass;
+extern struct nouveau_oclass *nv1a_fb_oclass;
+extern struct nouveau_oclass *nv20_fb_oclass;
+extern struct nouveau_oclass *nv25_fb_oclass;
+extern struct nouveau_oclass *nv30_fb_oclass;
+extern struct nouveau_oclass *nv35_fb_oclass;
+extern struct nouveau_oclass *nv36_fb_oclass;
+extern struct nouveau_oclass *nv40_fb_oclass;
+extern struct nouveau_oclass *nv41_fb_oclass;
+extern struct nouveau_oclass *nv44_fb_oclass;
+extern struct nouveau_oclass *nv46_fb_oclass;
+extern struct nouveau_oclass *nv47_fb_oclass;
+extern struct nouveau_oclass *nv49_fb_oclass;
+extern struct nouveau_oclass *nv4e_fb_oclass;
+extern struct nouveau_oclass *nv50_fb_oclass;
+extern struct nouveau_oclass *nv84_fb_oclass;
+extern struct nouveau_oclass *nva3_fb_oclass;
+extern struct nouveau_oclass *nvaa_fb_oclass;
+extern struct nouveau_oclass *nvaf_fb_oclass;
+extern struct nouveau_oclass *nvc0_fb_oclass;
+extern struct nouveau_oclass *nve0_fb_oclass;
 
 struct nouveau_ram {
        struct nouveau_object base;
@@ -121,6 +126,17 @@ struct nouveau_ram {
        int  (*get)(struct nouveau_fb *, u64 size, u32 align,
                    u32 size_nc, u32 type, struct nouveau_mem **);
        void (*put)(struct nouveau_fb *, struct nouveau_mem **);
+
+       int  (*calc)(struct nouveau_fb *, u32 freq);
+       int  (*prog)(struct nouveau_fb *);
+       void (*tidy)(struct nouveau_fb *);
+       struct {
+               u8  version;
+               u32 data;
+               u8  size;
+       } rammap, ramcfg, timing;
+       u32 freq;
+       u32 mr[16];
 };
 
 #endif
index 7e4e277..9fa5da7 100644 (file)
@@ -60,13 +60,18 @@ void _nouveau_i2c_port_dtor(struct nouveau_object *);
 #define _nouveau_i2c_port_init nouveau_object_init
 #define _nouveau_i2c_port_fini nouveau_object_fini
 
+struct nouveau_i2c_board_info {
+       struct i2c_board_info dev;
+       u8 udelay; /* set to 0 to use the standard delay */
+};
+
 struct nouveau_i2c {
        struct nouveau_subdev base;
 
        struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
        struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
        int (*identify)(struct nouveau_i2c *, int index,
-                       const char *what, struct i2c_board_info *,
+                       const char *what, struct nouveau_i2c_board_info *,
                        bool (*match)(struct nouveau_i2c_port *,
                                      struct i2c_board_info *));
        struct list_head ports;
index ce6569f..adc88b7 100644 (file)
@@ -11,7 +11,6 @@ struct nouveau_mc_intr {
 
 struct nouveau_mc {
        struct nouveau_subdev base;
-       const struct nouveau_mc_intr *intr_map;
        bool use_msi;
 };
 
@@ -21,8 +20,8 @@ nouveau_mc(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_MC];
 }
 
-#define nouveau_mc_create(p,e,o,m,d)                                           \
-       nouveau_mc_create_((p), (e), (o), (m), sizeof(**d), (void **)d)
+#define nouveau_mc_create(p,e,o,d)                                             \
+       nouveau_mc_create_((p), (e), (o), sizeof(**d), (void **)d)
 #define nouveau_mc_destroy(p) ({                                               \
        struct nouveau_mc *pmc = (p); _nouveau_mc_dtor(nv_object(pmc));        \
 })
@@ -34,20 +33,24 @@ nouveau_mc(void *obj)
 })
 
 int  nouveau_mc_create_(struct nouveau_object *, struct nouveau_object *,
-                       struct nouveau_oclass *, const struct nouveau_mc_intr *,
-                       int, void **);
+                       struct nouveau_oclass *, int, void **);
 void _nouveau_mc_dtor(struct nouveau_object *);
 int  _nouveau_mc_init(struct nouveau_object *);
 int  _nouveau_mc_fini(struct nouveau_object *, bool);
 
-extern struct nouveau_oclass nv04_mc_oclass;
-extern struct nouveau_oclass nv44_mc_oclass;
-extern struct nouveau_oclass nv50_mc_oclass;
-extern struct nouveau_oclass nv98_mc_oclass;
-extern struct nouveau_oclass nvc0_mc_oclass;
+struct nouveau_mc_oclass {
+       struct nouveau_oclass base;
+       const struct nouveau_mc_intr *intr;
+       void (*msi_rearm)(struct nouveau_mc *);
+};
 
-extern const struct nouveau_mc_intr nv04_mc_intr[];
-int nv04_mc_init(struct nouveau_object *);
-int nv50_mc_init(struct nouveau_object *);
+extern struct nouveau_oclass *nv04_mc_oclass;
+extern struct nouveau_oclass *nv40_mc_oclass;
+extern struct nouveau_oclass *nv44_mc_oclass;
+extern struct nouveau_oclass *nv50_mc_oclass;
+extern struct nouveau_oclass *nv94_mc_oclass;
+extern struct nouveau_oclass *nv98_mc_oclass;
+extern struct nouveau_oclass *nvc0_mc_oclass;
+extern struct nouveau_oclass *nvc3_mc_oclass;
 
 #endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/pwr.h b/drivers/gpu/drm/nouveau/core/include/subdev/pwr.h
new file mode 100644 (file)
index 0000000..c5c92cb
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef __NOUVEAU_PWR_H__
+#define __NOUVEAU_PWR_H__
+
+#include <core/subdev.h>
+#include <core/device.h>
+
+struct nouveau_pwr {
+       struct nouveau_subdev base;
+
+       struct {
+               u32 limit;
+               u32 *data;
+               u32  size;
+       } code;
+
+       struct {
+               u32 limit;
+               u32 *data;
+               u32  size;
+       } data;
+
+       struct {
+               u32 base;
+               u32 size;
+       } send;
+
+       struct {
+               u32 base;
+               u32 size;
+
+               struct work_struct work;
+               wait_queue_head_t wait;
+               u32 process;
+               u32 message;
+               u32 data[2];
+       } recv;
+
+       int (*message)(struct nouveau_pwr *, u32[2], u32, u32, u32, u32);
+};
+
+static inline struct nouveau_pwr *
+nouveau_pwr(void *obj)
+{
+       return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_PWR];
+}
+
+#define nouveau_pwr_create(p, e, o, d)                                         \
+       nouveau_pwr_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_pwr_destroy(p)                                                 \
+       nouveau_subdev_destroy(&(p)->base)
+#define nouveau_pwr_init(p) ({                                                 \
+       struct nouveau_pwr *ppwr = (p);                                        \
+       _nouveau_pwr_init(nv_object(ppwr));                                    \
+})
+#define nouveau_pwr_fini(p,s) ({                                               \
+       struct nouveau_pwr *ppwr = (p);                                        \
+       _nouveau_pwr_fini(nv_object(ppwr), (s));                               \
+})
+
+int nouveau_pwr_create_(struct nouveau_object *, struct nouveau_object *,
+                          struct nouveau_oclass *, int, void **);
+#define _nouveau_pwr_dtor _nouveau_subdev_dtor
+int _nouveau_pwr_init(struct nouveau_object *);
+int _nouveau_pwr_fini(struct nouveau_object *, bool);
+
+extern struct nouveau_oclass nva3_pwr_oclass;
+extern struct nouveau_oclass nvc0_pwr_oclass;
+extern struct nouveau_oclass nvd0_pwr_oclass;
+extern struct nouveau_oclass nv108_pwr_oclass;
+
+/* interface to MEMX process running on PPWR */
+struct nouveau_memx;
+int  nouveau_memx_init(struct nouveau_pwr *, struct nouveau_memx **);
+int  nouveau_memx_fini(struct nouveau_memx **, bool exec);
+void nouveau_memx_wr32(struct nouveau_memx *, u32 addr, u32 data);
+void nouveau_memx_wait(struct nouveau_memx *,
+                      u32 addr, u32 mask, u32 data, u32 nsec);
+void nouveau_memx_nsec(struct nouveau_memx *, u32 nsec);
+
+#endif
index c075998..69891d4 100644 (file)
@@ -71,6 +71,8 @@ void _nouveau_therm_dtor(struct nouveau_object *);
 int  _nouveau_therm_init(struct nouveau_object *);
 int  _nouveau_therm_fini(struct nouveau_object *, bool);
 
+int  nouveau_therm_cstate(struct nouveau_therm *, int, int);
+
 extern struct nouveau_oclass nv40_therm_oclass;
 extern struct nouveau_oclass nv50_therm_oclass;
 extern struct nouveau_oclass nv84_therm_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/volt.h b/drivers/gpu/drm/nouveau/core/include/subdev/volt.h
new file mode 100644 (file)
index 0000000..820b62f
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef __NOUVEAU_VOLT_H__
+#define __NOUVEAU_VOLT_H__
+
+#include <core/subdev.h>
+#include <core/device.h>
+
+struct nouveau_voltage {
+       u32 uv;
+       u8  id;
+};
+
+struct nouveau_volt {
+       struct nouveau_subdev base;
+
+       int (*vid_get)(struct nouveau_volt *);
+       int (*get)(struct nouveau_volt *);
+       int (*vid_set)(struct nouveau_volt *, u8 vid);
+       int (*set)(struct nouveau_volt *, u32 uv);
+       int (*set_id)(struct nouveau_volt *, u8 id, int condition);
+
+       u8 vid_mask;
+       u8 vid_nr;
+       struct {
+               u32 uv;
+               u8 vid;
+       } vid[256];
+};
+
+static inline struct nouveau_volt *
+nouveau_volt(void *obj)
+{
+       return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_VOLT];
+}
+
+#define nouveau_volt_create(p, e, o, d)                                        \
+       nouveau_volt_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_volt_destroy(p) ({                                             \
+       struct nouveau_volt *v = (p);                                          \
+       _nouveau_volt_dtor(nv_object(v));                                      \
+})
+#define nouveau_volt_init(p) ({                                                \
+       struct nouveau_volt *v = (p);                                          \
+       _nouveau_volt_init(nv_object(v));                                      \
+})
+#define nouveau_volt_fini(p,s)                                                 \
+       nouveau_subdev_fini((p), (s))
+
+int  nouveau_volt_create_(struct nouveau_object *, struct nouveau_object *,
+                         struct nouveau_oclass *, int, void **);
+void _nouveau_volt_dtor(struct nouveau_object *);
+int  _nouveau_volt_init(struct nouveau_object *);
+#define _nouveau_volt_fini _nouveau_subdev_fini
+
+extern struct nouveau_oclass nv40_volt_oclass;
+
+int nouveau_voltgpio_init(struct nouveau_volt *);
+int nouveau_voltgpio_get(struct nouveau_volt *);
+int nouveau_voltgpio_set(struct nouveau_volt *, u8);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/boost.c b/drivers/gpu/drm/nouveau/core/subdev/bios/boost.c
new file mode 100644 (file)
index 0000000..c1835e5
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/boost.h>
+
+u16
+nvbios_boostTe(struct nouveau_bios *bios,
+              u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
+{
+       struct bit_entry bit_P;
+       u16 boost = 0x0000;
+
+       if (!bit_entry(bios, 'P', &bit_P)) {
+               if (bit_P.version == 2)
+                       boost = nv_ro16(bios, bit_P.offset + 0x30);
+
+               if (boost) {
+                       *ver = nv_ro08(bios, boost + 0);
+                       switch (*ver) {
+                       case 0x11:
+                               *hdr = nv_ro08(bios, boost + 1);
+                               *cnt = nv_ro08(bios, boost + 5);
+                               *len = nv_ro08(bios, boost + 2);
+                               *snr = nv_ro08(bios, boost + 4);
+                               *ssz = nv_ro08(bios, boost + 3);
+                               return boost;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       return 0x0000;
+}
+
+u16
+nvbios_boostEe(struct nouveau_bios *bios, int idx,
+              u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       u8  snr, ssz;
+       u16 data = nvbios_boostTe(bios, ver, hdr, cnt, len, &snr, &ssz);
+       if (data && idx < *cnt) {
+               data = data + *hdr + (idx * (*len + (snr * ssz)));
+               *hdr = *len;
+               *cnt = snr;
+               *len = ssz;
+               return data;
+       }
+       return 0x0000;
+}
+
+u16
+nvbios_boostEp(struct nouveau_bios *bios, int idx,
+              u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_boostE *info)
+{
+       u16 data = nvbios_boostEe(bios, idx, ver, hdr, cnt, len);
+       memset(info, 0x00, sizeof(*info));
+       if (data) {
+               info->pstate = (nv_ro16(bios, data + 0x00) & 0x01e0) >> 5;
+               info->min    =  nv_ro16(bios, data + 0x02) * 1000;
+               info->max    =  nv_ro16(bios, data + 0x04) * 1000;
+       }
+       return data;
+}
+
+u16
+nvbios_boostEm(struct nouveau_bios *bios, u8 pstate,
+              u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_boostE *info)
+{
+       u32 data, idx = 0;
+       while ((data = nvbios_boostEp(bios, idx++, ver, hdr, cnt, len, info))) {
+               if (info->pstate == pstate)
+                       break;
+       }
+       return data;
+}
+
+u16
+nvbios_boostSe(struct nouveau_bios *bios, int idx,
+              u16 data, u8 *ver, u8 *hdr, u8 cnt, u8 len)
+{
+       if (data && idx < cnt) {
+               data = data + *hdr + (idx * len);
+               *hdr = len;
+               return data;
+       }
+       return 0x0000;
+}
+
+u16
+nvbios_boostSp(struct nouveau_bios *bios, int idx,
+              u16 data, u8 *ver, u8 *hdr, u8 cnt, u8 len,
+              struct nvbios_boostS *info)
+{
+       data = nvbios_boostSe(bios, idx, data, ver, hdr, cnt, len);
+       memset(info, 0x00, sizeof(*info));
+       if (data) {
+               info->domain  = nv_ro08(bios, data + 0x00);
+               info->percent = nv_ro08(bios, data + 0x01);
+               info->min     = nv_ro16(bios, data + 0x02) * 1000;
+               info->max     = nv_ro16(bios, data + 0x04) * 1000;
+       }
+       return data;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/cstep.c b/drivers/gpu/drm/nouveau/core/subdev/bios/cstep.c
new file mode 100644 (file)
index 0000000..d3b1532
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/cstep.h>
+
+u16
+nvbios_cstepTe(struct nouveau_bios *bios,
+              u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz)
+{
+       struct bit_entry bit_P;
+       u16 cstep = 0x0000;
+
+       if (!bit_entry(bios, 'P', &bit_P)) {
+               if (bit_P.version == 2)
+                       cstep = nv_ro16(bios, bit_P.offset + 0x34);
+
+               if (cstep) {
+                       *ver = nv_ro08(bios, cstep + 0);
+                       switch (*ver) {
+                       case 0x10:
+                               *hdr = nv_ro08(bios, cstep + 1);
+                               *cnt = nv_ro08(bios, cstep + 3);
+                               *len = nv_ro08(bios, cstep + 2);
+                               *xnr = nv_ro08(bios, cstep + 5);
+                               *xsz = nv_ro08(bios, cstep + 4);
+                               return cstep;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       return 0x0000;
+}
+
+u16
+nvbios_cstepEe(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr)
+{
+       u8  cnt, len, xnr, xsz;
+       u16 data = nvbios_cstepTe(bios, ver, hdr, &cnt, &len, &xnr, &xsz);
+       if (data && idx < cnt) {
+               data = data + *hdr + (idx * len);
+               *hdr = len;
+               return data;
+       }
+       return 0x0000;
+}
+
+u16
+nvbios_cstepEp(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr,
+              struct nvbios_cstepE *info)
+{
+       u16 data = nvbios_cstepEe(bios, idx, ver, hdr);
+       memset(info, 0x00, sizeof(*info));
+       if (data) {
+               info->pstate = (nv_ro16(bios, data + 0x00) & 0x01e0) >> 5;
+               info->index   = nv_ro08(bios, data + 0x03);
+       }
+       return data;
+}
+
+u16
+nvbios_cstepEm(struct nouveau_bios *bios, u8 pstate, u8 *ver, u8 *hdr,
+              struct nvbios_cstepE *info)
+{
+       u32 data, idx = 0;
+       while ((data = nvbios_cstepEp(bios, idx++, ver, hdr, info))) {
+               if (info->pstate == pstate)
+                       break;
+       }
+       return data;
+}
+
+u16
+nvbios_cstepXe(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr)
+{
+       u8  cnt, len, xnr, xsz;
+       u16 data = nvbios_cstepTe(bios, ver, hdr, &cnt, &len, &xnr, &xsz);
+       if (data && idx < xnr) {
+               data = data + *hdr + (cnt * len) + (idx * xsz);
+               *hdr = xsz;
+               return data;
+       }
+       return 0x0000;
+}
+
+u16
+nvbios_cstepXp(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr,
+              struct nvbios_cstepX *info)
+{
+       u16 data = nvbios_cstepXe(bios, idx, ver, hdr);
+       memset(info, 0x00, sizeof(*info));
+       if (data) {
+               info->freq    = nv_ro16(bios, data + 0x00) * 1000;
+               info->unkn[0] = nv_ro08(bios, data + 0x02);
+               info->unkn[1] = nv_ro08(bios, data + 0x03);
+               info->voltage = nv_ro08(bios, data + 0x04);
+       }
+       return data;
+}
index 663853b..7628fe7 100644 (file)
@@ -89,6 +89,7 @@ nvbios_dpout_parse(struct nouveau_bios *bios, u8 idx,
                   struct nvbios_dpout *info)
 {
        u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len);
+       memset(info, 0x00, sizeof(*info));
        if (data && *ver) {
                info->type = nv_ro16(bios, data + 0x00);
                info->mask = nv_ro16(bios, data + 0x02);
@@ -99,9 +100,12 @@ nvbios_dpout_parse(struct nouveau_bios *bios, u8 idx,
                        info->script[0] = nv_ro16(bios, data + 0x06);
                        info->script[1] = nv_ro16(bios, data + 0x08);
                        info->lnkcmp    = nv_ro16(bios, data + 0x0a);
-                       info->script[2] = nv_ro16(bios, data + 0x0c);
-                       info->script[3] = nv_ro16(bios, data + 0x0e);
-                       info->script[4] = nv_ro16(bios, data + 0x10);
+                       if (*len >= 0x0f) {
+                               info->script[2] = nv_ro16(bios, data + 0x0c);
+                               info->script[3] = nv_ro16(bios, data + 0x0e);
+                       }
+                       if (*len >= 0x11)
+                               info->script[4] = nv_ro16(bios, data + 0x10);
                        break;
                case 0x40:
                        info->flags     = nv_ro08(bios, data + 0x04);
index 57cda2a..420908c 100644 (file)
@@ -2180,7 +2180,7 @@ nvbios_init(struct nouveau_subdev *subdev, bool execute)
        u16 data;
 
        if (execute)
-               nv_suspend(bios, "running init tables\n");
+               nv_info(bios, "running init tables\n");
        while (!ret && (data = (init_script(bios, ++i)))) {
                struct nvbios_init init = {
                        .subdev = subdev,
@@ -2210,5 +2210,5 @@ nvbios_init(struct nouveau_subdev *subdev, bool execute)
                ret = nvbios_exec(&init);
        }
 
-       return 0;
+       return ret;
 }
index bcbb056..675e221 100644 (file)
@@ -26,8 +26,9 @@
 #include <subdev/bios/bit.h>
 #include <subdev/bios/perf.h>
 
-static u16
-perf_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+u16
+nvbios_perf_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr,
+                 u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
 {
        struct bit_entry bit_P;
        u16 perf = 0x0000;
@@ -38,10 +39,22 @@ perf_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
                        if (perf) {
                                *ver = nv_ro08(bios, perf + 0);
                                *hdr = nv_ro08(bios, perf + 1);
+                               if (*ver >= 0x40 && *ver < 0x41) {
+                                       *cnt = nv_ro08(bios, perf + 5);
+                                       *len = nv_ro08(bios, perf + 2);
+                                       *snr = nv_ro08(bios, perf + 4);
+                                       *ssz = nv_ro08(bios, perf + 3);
+                                       return perf;
+                               } else
+                               if (*ver >= 0x20 && *ver < 0x40) {
+                                       *cnt = nv_ro08(bios, perf + 2);
+                                       *len = nv_ro08(bios, perf + 3);
+                                       *snr = nv_ro08(bios, perf + 4);
+                                       *ssz = nv_ro08(bios, perf + 5);
+                                       return perf;
+                               }
                        }
-               } else
-                       nv_error(bios, "unknown offset for perf in BIT P %d\n",
-                               bit_P.version);
+               }
        }
 
        if (bios->bmp_offset) {
@@ -50,19 +63,132 @@ perf_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
                        if (perf) {
                                *hdr = nv_ro08(bios, perf + 0);
                                *ver = nv_ro08(bios, perf + 1);
+                               *cnt = nv_ro08(bios, perf + 2);
+                               *len = nv_ro08(bios, perf + 3);
+                               *snr = 0;
+                               *ssz = 0;
+                               return perf;
                        }
                }
        }
 
+       return 0x0000;
+}
+
+u16
+nvbios_perf_entry(struct nouveau_bios *bios, int idx,
+                 u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       u8  snr, ssz;
+       u16 perf = nvbios_perf_table(bios, ver, hdr, cnt, len, &snr, &ssz);
+       if (perf && idx < *cnt) {
+               perf = perf + *hdr + (idx * (*len + (snr * ssz)));
+               *hdr = *len;
+               *cnt = snr;
+               *len = ssz;
+               return perf;
+       }
+       return 0x0000;
+}
+
+u16
+nvbios_perfEp(struct nouveau_bios *bios, int idx,
+             u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+             struct nvbios_perfE *info)
+{
+       u16 perf = nvbios_perf_entry(bios, idx, ver, hdr, cnt, len);
+       memset(info, 0x00, sizeof(*info));
+       info->pstate = nv_ro08(bios, perf + 0x00);
+       switch (!!perf * *ver) {
+       case 0x12:
+       case 0x13:
+       case 0x14:
+               info->core     = nv_ro32(bios, perf + 0x01) * 10;
+               info->memory   = nv_ro32(bios, perf + 0x05) * 20;
+               info->fanspeed = nv_ro08(bios, perf + 0x37);
+               if (*hdr > 0x38)
+                       info->voltage = nv_ro08(bios, perf + 0x38);
+               break;
+       case 0x21:
+       case 0x23:
+       case 0x24:
+               info->fanspeed = nv_ro08(bios, perf + 0x04);
+               info->voltage  = nv_ro08(bios, perf + 0x05);
+               info->shader   = nv_ro16(bios, perf + 0x06) * 1000;
+               info->core     = info->shader + (signed char)
+                                nv_ro08(bios, perf + 0x08) * 1000;
+               switch (nv_device(bios)->chipset) {
+               case 0x49:
+               case 0x4b:
+                       info->memory = nv_ro16(bios, perf + 0x0b) * 1000;
+                       break;
+               default:
+                       info->memory = nv_ro16(bios, perf + 0x0b) * 2000;
+                       break;
+               }
+               break;
+       case 0x25:
+               info->fanspeed = nv_ro08(bios, perf + 0x04);
+               info->voltage  = nv_ro08(bios, perf + 0x05);
+               info->core     = nv_ro16(bios, perf + 0x06) * 1000;
+               info->shader   = nv_ro16(bios, perf + 0x0a) * 1000;
+               info->memory   = nv_ro16(bios, perf + 0x0c) * 1000;
+               break;
+       case 0x30:
+               info->script   = nv_ro16(bios, perf + 0x02);
+       case 0x35:
+               info->fanspeed = nv_ro08(bios, perf + 0x06);
+               info->voltage  = nv_ro08(bios, perf + 0x07);
+               info->core     = nv_ro16(bios, perf + 0x08) * 1000;
+               info->shader   = nv_ro16(bios, perf + 0x0a) * 1000;
+               info->memory   = nv_ro16(bios, perf + 0x0c) * 1000;
+               info->vdec     = nv_ro16(bios, perf + 0x10) * 1000;
+               info->disp     = nv_ro16(bios, perf + 0x14) * 1000;
+               break;
+       case 0x40:
+               info->voltage  = nv_ro08(bios, perf + 0x02);
+               break;
+       default:
+               return 0x0000;
+       }
        return perf;
 }
 
+u32
+nvbios_perfSe(struct nouveau_bios *bios, u32 perfE, int idx,
+             u8 *ver, u8 *hdr, u8 cnt, u8 len)
+{
+       u32 data = 0x00000000;
+       if (idx < cnt) {
+               data = perfE + *hdr + (idx * len);
+               *hdr = len;
+       }
+       return data;
+}
+
+u32
+nvbios_perfSp(struct nouveau_bios *bios, u32 perfE, int idx,
+             u8 *ver, u8 *hdr, u8 cnt, u8 len,
+             struct nvbios_perfS *info)
+{
+       u32 data = nvbios_perfSe(bios, perfE, idx, ver, hdr, cnt, len);
+       memset(info, 0x00, sizeof(*info));
+       switch (!!data * *ver) {
+       case 0x40:
+               info->v40.freq = (nv_ro16(bios, data + 0x00) & 0x3fff) * 1000;
+               break;
+       default:
+               break;
+       }
+       return data;
+}
+
 int
 nvbios_perf_fan_parse(struct nouveau_bios *bios,
                      struct nvbios_perf_fan *fan)
 {
-       u8 ver = 0, hdr = 0, cnt = 0, len = 0;
-       u16 perf = perf_table(bios, &ver, &hdr, &cnt, &len);
+       u8  ver, hdr, cnt, len, snr, ssz;
+       u16 perf = nvbios_perf_table(bios, &ver, &hdr, &cnt, &len, &snr, &ssz);
        if (!perf)
                return -ENODEV;
 
index f835501..1f76de5 100644 (file)
@@ -114,6 +114,7 @@ pll_map(struct nouveau_bios *bios)
        switch (nv_device(bios)->card_type) {
        case NV_04:
        case NV_10:
+       case NV_11:
        case NV_20:
        case NV_30:
                return nv04_pll_mapping;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c b/drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c
new file mode 100644 (file)
index 0000000..916fa9d
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/rammap.h>
+
+u16
+nvbios_rammap_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr,
+                   u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
+{
+       struct bit_entry bit_P;
+       u16 rammap = 0x0000;
+
+       if (!bit_entry(bios, 'P', &bit_P)) {
+               if (bit_P.version == 2)
+                       rammap = nv_ro16(bios, bit_P.offset + 4);
+
+               if (rammap) {
+                       *ver = nv_ro08(bios, rammap + 0);
+                       switch (*ver) {
+                       case 0x10:
+                       case 0x11:
+                               *hdr = nv_ro08(bios, rammap + 1);
+                               *cnt = nv_ro08(bios, rammap + 5);
+                               *len = nv_ro08(bios, rammap + 2);
+                               *snr = nv_ro08(bios, rammap + 4);
+                               *ssz = nv_ro08(bios, rammap + 3);
+                               return rammap;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       return 0x0000;
+}
+
+u16
+nvbios_rammap_entry(struct nouveau_bios *bios, int idx,
+                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       u8  snr, ssz;
+       u16 rammap = nvbios_rammap_table(bios, ver, hdr, cnt, len, &snr, &ssz);
+       if (rammap && idx < *cnt) {
+               rammap = rammap + *hdr + (idx * (*len + (snr * ssz)));
+               *hdr = *len;
+               *cnt = snr;
+               *len = ssz;
+               return rammap;
+       }
+       return 0x0000;
+}
+
+u16
+nvbios_rammap_match(struct nouveau_bios *bios, u16 khz,
+                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       int idx = 0;
+       u32 data;
+       while ((data = nvbios_rammap_entry(bios, idx++, ver, hdr, cnt, len))) {
+               if (khz >= nv_ro16(bios, data + 0x00) &&
+                   khz <= nv_ro16(bios, data + 0x02))
+                       break;
+       }
+       return data;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/timing.c b/drivers/gpu/drm/nouveau/core/subdev/bios/timing.c
new file mode 100644 (file)
index 0000000..151c2d6
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/timing.h>
+
+u16
+nvbios_timing_table(struct nouveau_bios *bios,
+                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       struct bit_entry bit_P;
+       u16 timing = 0x0000;
+
+       if (!bit_entry(bios, 'P', &bit_P)) {
+               if (bit_P.version == 1)
+                       timing = nv_ro16(bios, bit_P.offset + 4);
+               else
+               if (bit_P.version == 2)
+                       timing = nv_ro16(bios, bit_P.offset + 8);
+
+               if (timing) {
+                       *ver = nv_ro08(bios, timing + 0);
+                       switch (*ver) {
+                       case 0x10:
+                               *hdr = nv_ro08(bios, timing + 1);
+                               *cnt = nv_ro08(bios, timing + 2);
+                               *len = nv_ro08(bios, timing + 3);
+                               return timing;
+                       case 0x20:
+                               *hdr = nv_ro08(bios, timing + 1);
+                               *cnt = nv_ro08(bios, timing + 3);
+                               *len = nv_ro08(bios, timing + 2);
+                               return timing;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       return 0x0000;
+}
+
+u16
+nvbios_timing_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len)
+{
+       u8  hdr, cnt;
+       u16 timing = nvbios_timing_table(bios, ver, &hdr, &cnt, len);
+       if (timing && idx < cnt)
+               return timing + hdr + (idx * *len);
+       return 0x0000;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/vmap.c b/drivers/gpu/drm/nouveau/core/subdev/bios/vmap.c
new file mode 100644 (file)
index 0000000..f343a1b
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/vmap.h>
+
+u16
+nvbios_vmap_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       struct bit_entry bit_P;
+       u16 vmap = 0x0000;
+
+       if (!bit_entry(bios, 'P', &bit_P)) {
+               if (bit_P.version == 2) {
+                       vmap = nv_ro16(bios, bit_P.offset + 0x20);
+                       if (vmap) {
+                               *ver = nv_ro08(bios, vmap + 0);
+                               switch (*ver) {
+                               case 0x10:
+                               case 0x20:
+                                       *hdr = nv_ro08(bios, vmap + 1);
+                                       *cnt = nv_ro08(bios, vmap + 3);
+                                       *len = nv_ro08(bios, vmap + 2);
+                                       return vmap;
+                               default:
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       return 0x0000;
+}
+
+u16
+nvbios_vmap_parse(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                 struct nvbios_vmap *info)
+{
+       u16 vmap = nvbios_vmap_table(bios, ver, hdr, cnt, len);
+       memset(info, 0x00, sizeof(*info));
+       switch (!!vmap * *ver) {
+       case 0x10:
+       case 0x20:
+               break;
+       }
+       return vmap;
+}
+
+u16
+nvbios_vmap_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len)
+{
+       u8  hdr, cnt;
+       u16 vmap = nvbios_vmap_table(bios, ver, &hdr, &cnt, len);
+       if (vmap && idx < cnt) {
+               vmap = vmap + hdr + (idx * *len);
+               return vmap;
+       }
+       return 0x0000;
+}
+
+u16
+nvbios_vmap_entry_parse(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len,
+                       struct nvbios_vmap_entry *info)
+{
+       u16 vmap = nvbios_vmap_entry(bios, idx, ver, len);
+       memset(info, 0x00, sizeof(*info));
+       switch (!!vmap * *ver) {
+       case 0x10:
+               info->link   = 0xff;
+               info->min    = nv_ro32(bios, vmap + 0x00);
+               info->max    = nv_ro32(bios, vmap + 0x04);
+               info->arg[0] = nv_ro32(bios, vmap + 0x08);
+               info->arg[1] = nv_ro32(bios, vmap + 0x0c);
+               info->arg[2] = nv_ro32(bios, vmap + 0x10);
+               break;
+       case 0x20:
+               info->unk0   = nv_ro08(bios, vmap + 0x00);
+               info->link   = nv_ro08(bios, vmap + 0x01);
+               info->min    = nv_ro32(bios, vmap + 0x02);
+               info->max    = nv_ro32(bios, vmap + 0x06);
+               info->arg[0] = nv_ro32(bios, vmap + 0x0a);
+               info->arg[1] = nv_ro32(bios, vmap + 0x0e);
+               info->arg[2] = nv_ro32(bios, vmap + 0x12);
+               info->arg[3] = nv_ro32(bios, vmap + 0x16);
+               info->arg[4] = nv_ro32(bios, vmap + 0x1a);
+               info->arg[5] = nv_ro32(bios, vmap + 0x1e);
+               break;
+       }
+       return vmap;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/volt.c b/drivers/gpu/drm/nouveau/core/subdev/bios/volt.c
new file mode 100644 (file)
index 0000000..bb590de
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/volt.h>
+
+u16
+nvbios_volt_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       struct bit_entry bit_P;
+       u16 volt = 0x0000;
+
+       if (!bit_entry(bios, 'P', &bit_P)) {
+               if (bit_P.version == 2)
+                       volt = nv_ro16(bios, bit_P.offset + 0x0c);
+               else
+               if (bit_P.version == 1)
+                       volt = nv_ro16(bios, bit_P.offset + 0x10);
+
+               if (volt) {
+                       *ver = nv_ro08(bios, volt + 0);
+                       switch (*ver) {
+                       case 0x12:
+                               *hdr = 5;
+                               *cnt = nv_ro08(bios, volt + 2);
+                               *len = nv_ro08(bios, volt + 1);
+                               return volt;
+                       case 0x20:
+                               *hdr = nv_ro08(bios, volt + 1);
+                               *cnt = nv_ro08(bios, volt + 2);
+                               *len = nv_ro08(bios, volt + 3);
+                               return volt;
+                       case 0x30:
+                       case 0x40:
+                       case 0x50:
+                               *hdr = nv_ro08(bios, volt + 1);
+                               *cnt = nv_ro08(bios, volt + 3);
+                               *len = nv_ro08(bios, volt + 2);
+                               return volt;
+                       }
+               }
+       }
+
+       return 0x0000;
+}
+
+u16
+nvbios_volt_parse(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                 struct nvbios_volt *info)
+{
+       u16 volt = nvbios_volt_table(bios, ver, hdr, cnt, len);
+       memset(info, 0x00, sizeof(*info));
+       switch (!!volt * *ver) {
+       case 0x12:
+               info->vidmask = nv_ro08(bios, volt + 0x04);
+               break;
+       case 0x20:
+               info->vidmask = nv_ro08(bios, volt + 0x05);
+               break;
+       case 0x30:
+               info->vidmask = nv_ro08(bios, volt + 0x04);
+               break;
+       case 0x40:
+               info->base    = nv_ro32(bios, volt + 0x04);
+               info->step    = nv_ro16(bios, volt + 0x08);
+               info->vidmask = nv_ro08(bios, volt + 0x0b);
+               /*XXX*/
+               info->min     = 0;
+               info->max     = info->base;
+               break;
+       case 0x50:
+               info->vidmask = nv_ro08(bios, volt + 0x06);
+               info->min     = nv_ro32(bios, volt + 0x0a);
+               info->max     = nv_ro32(bios, volt + 0x0e);
+               info->base    = nv_ro32(bios, volt + 0x12) & 0x00ffffff;
+               info->step    = nv_ro16(bios, volt + 0x16);
+               break;
+       }
+       return volt;
+}
+
+u16
+nvbios_volt_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len)
+{
+       u8  hdr, cnt;
+       u16 volt = nvbios_volt_table(bios, ver, &hdr, &cnt, len);
+       if (volt && idx < cnt) {
+               volt = volt + hdr + (idx * *len);
+               return volt;
+       }
+       return 0x0000;
+}
+
+u16
+nvbios_volt_entry_parse(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len,
+                       struct nvbios_volt_entry *info)
+{
+       u16 volt = nvbios_volt_entry(bios, idx, ver, len);
+       memset(info, 0x00, sizeof(*info));
+       switch (!!volt * *ver) {
+       case 0x12:
+       case 0x20:
+               info->voltage = nv_ro08(bios, volt + 0x00) * 10000;
+               info->vid     = nv_ro08(bios, volt + 0x01);
+               break;
+       case 0x30:
+               info->voltage = nv_ro08(bios, volt + 0x00) * 10000;
+               info->vid     = nv_ro08(bios, volt + 0x01) >> 2;
+               break;
+       case 0x40:
+       case 0x50:
+               break;
+       }
+       return volt;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/hwsq.c b/drivers/gpu/drm/nouveau/core/subdev/bus/hwsq.c
new file mode 100644 (file)
index 0000000..f757470
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <subdev/timer.h>
+#include <subdev/bus.h>
+
+struct nouveau_hwsq {
+       struct nouveau_bus *pbus;
+       u32 addr;
+       u32 data;
+       struct {
+               u8 data[512];
+               u8 size;
+       } c;
+};
+
+static void
+hwsq_cmd(struct nouveau_hwsq *hwsq, int size, u8 data[])
+{
+       memcpy(&hwsq->c.data[hwsq->c.size], data, size * sizeof(data[0]));
+       hwsq->c.size += size;
+}
+
+int
+nouveau_hwsq_init(struct nouveau_bus *pbus, struct nouveau_hwsq **phwsq)
+{
+       struct nouveau_hwsq *hwsq;
+
+       hwsq = *phwsq = kmalloc(sizeof(*hwsq), GFP_KERNEL);
+       if (hwsq) {
+               hwsq->pbus = pbus;
+               hwsq->addr = ~0;
+               hwsq->data = ~0;
+               memset(hwsq->c.data, 0x7f, sizeof(hwsq->c.data));
+               hwsq->c.size = 0;
+       }
+
+       return hwsq ? 0 : -ENOMEM;
+}
+
+int
+nouveau_hwsq_fini(struct nouveau_hwsq **phwsq, bool exec)
+{
+       struct nouveau_hwsq *hwsq = *phwsq;
+       int ret = 0, i;
+       if (hwsq) {
+               struct nouveau_bus *pbus = hwsq->pbus;
+               hwsq->c.size = (hwsq->c.size + 4) / 4;
+               if (hwsq->c.size <= pbus->hwsq_size) {
+                       if (exec)
+                               ret = pbus->hwsq_exec(pbus, (u32 *)hwsq->c.data,
+                                                     hwsq->c.size);
+                       if (ret)
+                               nv_error(pbus, "hwsq exec failed: %d\n", ret);
+               } else {
+                       nv_error(pbus, "hwsq ucode too large\n");
+                       ret = -ENOSPC;
+               }
+
+               for (i = 0; ret && i < hwsq->c.size; i++)
+                       nv_error(pbus, "\t0x%08x\n", ((u32 *)hwsq->c.data)[i]);
+
+               *phwsq = NULL;
+               kfree(hwsq);
+       }
+       return ret;
+}
+
+void
+nouveau_hwsq_wr32(struct nouveau_hwsq *hwsq, u32 addr, u32 data)
+{
+       nv_debug(hwsq->pbus, "R[%06x] = 0x%08x\n", addr, data);
+
+       if (hwsq->data != data) {
+               if ((data & 0xffff0000) != (hwsq->data & 0xffff0000)) {
+                       hwsq_cmd(hwsq, 5, (u8[]){ 0xe2, data, data >> 8,
+                                                 data >> 16, data >> 24 });
+               } else {
+                       hwsq_cmd(hwsq, 3, (u8[]){ 0x42, data, data >> 8 });
+               }
+       }
+
+       if ((addr & 0xffff0000) != (hwsq->addr & 0xffff0000)) {
+               hwsq_cmd(hwsq, 5, (u8[]){ 0xe0, addr, addr >> 8,
+                                         addr >> 16, addr >> 24 });
+       } else {
+               hwsq_cmd(hwsq, 3, (u8[]){ 0x40, addr, addr >> 8 });
+       }
+
+       hwsq->addr = addr;
+       hwsq->data = data;
+}
+
+void
+nouveau_hwsq_setf(struct nouveau_hwsq *hwsq, u8 flag, int data)
+{
+       nv_debug(hwsq->pbus, " FLAG[%02x] = %d\n", flag, data);
+       flag += 0x80;
+       if (data >= 0)
+               flag += 0x20;
+       if (data >= 1)
+               flag += 0x20;
+       hwsq_cmd(hwsq, 1, (u8[]){ flag });
+}
+
+void
+nouveau_hwsq_wait(struct nouveau_hwsq *hwsq, u8 flag, u8 data)
+{
+       nv_debug(hwsq->pbus, " WAIT[%02x] = %d\n", flag, data);
+       hwsq_cmd(hwsq, 3, (u8[]){ 0x5f, flag, data });
+}
+
+void
+nouveau_hwsq_nsec(struct nouveau_hwsq *hwsq, u32 nsec)
+{
+       u8 shift = 0, usec = nsec / 1000;
+       while (usec & ~3) {
+               usec >>= 2;
+               shift++;
+       }
+
+       nv_debug(hwsq->pbus, "    DELAY = %d ns\n", nsec);
+       hwsq_cmd(hwsq, 1, (u8[]){ 0x00 | (shift << 2) | usec });
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/hwsq.h b/drivers/gpu/drm/nouveau/core/subdev/bus/hwsq.h
new file mode 100644 (file)
index 0000000..12176f9
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef __NVKM_BUS_HWSQ_H__
+#define __NVKM_BUS_HWSQ_H__
+
+#include <subdev/bus.h>
+
+struct hwsq {
+       struct nouveau_subdev *subdev;
+       struct nouveau_hwsq *hwsq;
+       int sequence;
+};
+
+struct hwsq_reg {
+       int sequence;
+       bool force;
+       u32 addr[2];
+       u32 data;
+};
+
+static inline struct hwsq_reg
+hwsq_reg2(u32 addr1, u32 addr2)
+{
+       return (struct hwsq_reg) {
+               .sequence = 0,
+               .force = 0,
+               .addr = { addr1, addr2 },
+               .data = 0xdeadbeef,
+       };
+}
+
+static inline struct hwsq_reg
+hwsq_reg(u32 addr)
+{
+       return hwsq_reg2(addr, addr);
+}
+
+static inline int
+hwsq_init(struct hwsq *ram, struct nouveau_subdev *subdev)
+{
+       struct nouveau_bus *pbus = nouveau_bus(subdev);
+       int ret;
+
+       ret = nouveau_hwsq_init(pbus, &ram->hwsq);
+       if (ret)
+               return ret;
+
+       ram->sequence++;
+       ram->subdev = subdev;
+       return 0;
+}
+
+static inline int
+hwsq_exec(struct hwsq *ram, bool exec)
+{
+       int ret = 0;
+       if (ram->subdev) {
+               ret = nouveau_hwsq_fini(&ram->hwsq, exec);
+               ram->subdev = NULL;
+       }
+       return ret;
+}
+
+static inline u32
+hwsq_rd32(struct hwsq *ram, struct hwsq_reg *reg)
+{
+       if (reg->sequence != ram->sequence)
+               reg->data = nv_rd32(ram->subdev, reg->addr[0]);
+       return reg->data;
+}
+
+static inline void
+hwsq_wr32(struct hwsq *ram, struct hwsq_reg *reg, u32 data)
+{
+       reg->sequence = ram->sequence;
+       reg->data = data;
+       if (reg->addr[0] != reg->addr[1])
+               nouveau_hwsq_wr32(ram->hwsq, reg->addr[1], reg->data);
+       nouveau_hwsq_wr32(ram->hwsq, reg->addr[0], reg->data);
+}
+
+static inline void
+hwsq_nuke(struct hwsq *ram, struct hwsq_reg *reg)
+{
+       reg->force = true;
+}
+
+static inline u32
+hwsq_mask(struct hwsq *ram, struct hwsq_reg *reg, u32 mask, u32 data)
+{
+       u32 temp = hwsq_rd32(ram, reg);
+       if (temp != ((temp & ~mask) | data) || reg->force)
+               hwsq_wr32(ram, reg, (temp & ~mask) | data);
+       return temp;
+}
+
+static inline void
+hwsq_setf(struct hwsq *ram, u8 flag, int data)
+{
+       nouveau_hwsq_setf(ram->hwsq, flag, data);
+}
+
+static inline void
+hwsq_wait(struct hwsq *ram, u8 flag, u8 data)
+{
+       nouveau_hwsq_wait(ram->hwsq, flag, data);
+}
+
+static inline void
+hwsq_nsec(struct hwsq *ram, u32 nsec)
+{
+       nouveau_hwsq_nsec(ram->hwsq, nsec);
+}
+
+#endif
index 8c7f805..23921b5 100644 (file)
  *          Ben Skeggs
  */
 
-#include <subdev/bus.h>
-
-struct nv04_bus_priv {
-       struct nouveau_bus base;
-};
+#include "nv04.h"
 
 static void
 nv04_bus_intr(struct nouveau_subdev *subdev)
@@ -56,10 +52,22 @@ nv04_bus_intr(struct nouveau_subdev *subdev)
 }
 
 static int
+nv04_bus_init(struct nouveau_object *object)
+{
+       struct nv04_bus_priv *priv = (void *)object;
+
+       nv_wr32(priv, 0x001100, 0xffffffff);
+       nv_wr32(priv, 0x001140, 0x00000111);
+
+       return nouveau_bus_init(&priv->base);
+}
+
+int
 nv04_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
              struct nouveau_oclass *oclass, void *data, u32 size,
              struct nouveau_object **pobject)
 {
+       struct nv04_bus_impl *impl = (void *)oclass;
        struct nv04_bus_priv *priv;
        int ret;
 
@@ -68,28 +76,20 @@ nv04_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       nv_subdev(priv)->intr = nv04_bus_intr;
+       nv_subdev(priv)->intr = impl->intr;
+       priv->base.hwsq_exec = impl->hwsq_exec;
+       priv->base.hwsq_size = impl->hwsq_size;
        return 0;
 }
 
-static int
-nv04_bus_init(struct nouveau_object *object)
-{
-       struct nv04_bus_priv *priv = (void *)object;
-
-       nv_wr32(priv, 0x001100, 0xffffffff);
-       nv_wr32(priv, 0x001140, 0x00000111);
-
-       return nouveau_bus_init(&priv->base);
-}
-
-struct nouveau_oclass
-nv04_bus_oclass = {
-       .handle = NV_SUBDEV(BUS, 0x04),
-       .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nv04_bus_oclass = &(struct nv04_bus_impl) {
+       .base.handle = NV_SUBDEV(BUS, 0x04),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv04_bus_ctor,
                .dtor = _nouveau_bus_dtor,
                .init = nv04_bus_init,
                .fini = _nouveau_bus_fini,
        },
-};
+       .intr = nv04_bus_intr,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv04.h b/drivers/gpu/drm/nouveau/core/subdev/bus/nv04.h
new file mode 100644 (file)
index 0000000..4d76024
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef __NVKM_BUS_NV04_H__
+#define __NVKM_BUS_NV04_H__
+
+#include <subdev/bus.h>
+
+struct nv04_bus_priv {
+       struct nouveau_bus base;
+};
+
+int  nv04_bus_ctor(struct nouveau_object *, struct nouveau_object *,
+                  struct nouveau_oclass *, void *, u32,
+                  struct nouveau_object **);
+int  nv50_bus_init(struct nouveau_object *);
+void nv50_bus_intr(struct nouveau_subdev *);
+
+struct nv04_bus_impl {
+       struct nouveau_oclass base;
+       void (*intr)(struct nouveau_subdev *);
+       int  (*hwsq_exec)(struct nouveau_bus *, u32 *, u32);
+       u32  hwsq_size;
+};
+
+#endif
index 34132ae..94da46f 100644 (file)
  *          Ben Skeggs
  */
 
-#include <subdev/bus.h>
-
-struct nv31_bus_priv {
-       struct nouveau_bus base;
-};
+#include "nv04.h"
 
 static void
 nv31_bus_intr(struct nouveau_subdev *subdev)
@@ -71,7 +67,7 @@ nv31_bus_intr(struct nouveau_subdev *subdev)
 static int
 nv31_bus_init(struct nouveau_object *object)
 {
-       struct nv31_bus_priv *priv = (void *)object;
+       struct nv04_bus_priv *priv = (void *)object;
        int ret;
 
        ret = nouveau_bus_init(&priv->base);
@@ -83,30 +79,14 @@ nv31_bus_init(struct nouveau_object *object)
        return 0;
 }
 
-static int
-nv31_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
-{
-       struct nv31_bus_priv *priv;
-       int ret;
-
-       ret = nouveau_bus_create(parent, engine, oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       nv_subdev(priv)->intr = nv31_bus_intr;
-       return 0;
-}
-
-struct nouveau_oclass
-nv31_bus_oclass = {
-       .handle = NV_SUBDEV(BUS, 0x31),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv31_bus_ctor,
+struct nouveau_oclass *
+nv31_bus_oclass = &(struct nv04_bus_impl) {
+       .base.handle = NV_SUBDEV(BUS, 0x31),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_bus_ctor,
                .dtor = _nouveau_bus_dtor,
                .init = nv31_bus_init,
                .fini = _nouveau_bus_fini,
        },
-};
+       .intr = nv31_bus_intr,
+}.base;
index f5b2117..11918f7 100644 (file)
  *          Ben Skeggs
  */
 
-#include <subdev/bus.h>
+#include <subdev/timer.h>
 
-struct nv50_bus_priv {
-       struct nouveau_bus base;
-};
+#include "nv04.h"
 
-static void
+static int
+nv50_bus_hwsq_exec(struct nouveau_bus *pbus, u32 *data, u32 size)
+{
+       struct nv50_bus_priv *priv = (void *)pbus;
+       int i;
+
+       nv_mask(pbus, 0x001098, 0x00000008, 0x00000000);
+       nv_wr32(pbus, 0x001304, 0x00000000);
+       for (i = 0; i < size; i++)
+               nv_wr32(priv, 0x001400 + (i * 4), data[i]);
+       nv_mask(pbus, 0x001098, 0x00000018, 0x00000018);
+       nv_wr32(pbus, 0x00130c, 0x00000003);
+
+       return nv_wait(pbus, 0x001308, 0x00000100, 0x00000000) ? 0 : -ETIMEDOUT;
+}
+
+void
 nv50_bus_intr(struct nouveau_subdev *subdev)
 {
        struct nouveau_bus *pbus = nouveau_bus(subdev);
@@ -61,10 +75,10 @@ nv50_bus_intr(struct nouveau_subdev *subdev)
        }
 }
 
-static int
+int
 nv50_bus_init(struct nouveau_object *object)
 {
-       struct nv50_bus_priv *priv = (void *)object;
+       struct nv04_bus_priv *priv = (void *)object;
        int ret;
 
        ret = nouveau_bus_init(&priv->base);
@@ -76,30 +90,16 @@ nv50_bus_init(struct nouveau_object *object)
        return 0;
 }
 
-static int
-nv50_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
-{
-       struct nv50_bus_priv *priv;
-       int ret;
-
-       ret = nouveau_bus_create(parent, engine, oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       nv_subdev(priv)->intr = nv50_bus_intr;
-       return 0;
-}
-
-struct nouveau_oclass
-nv50_bus_oclass = {
-       .handle = NV_SUBDEV(BUS, 0x50),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv50_bus_ctor,
+struct nouveau_oclass *
+nv50_bus_oclass = &(struct nv04_bus_impl) {
+       .base.handle = NV_SUBDEV(BUS, 0x50),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_bus_ctor,
                .dtor = _nouveau_bus_dtor,
                .init = nv50_bus_init,
                .fini = _nouveau_bus_fini,
        },
-};
+       .intr = nv50_bus_intr,
+       .hwsq_exec = nv50_bus_hwsq_exec,
+       .hwsq_size = 64,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv94.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nv94.c
new file mode 100644 (file)
index 0000000..d365905
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres <martin.peres@labri.fr>
+ *          Ben Skeggs
+ */
+
+#include <subdev/timer.h>
+
+#include "nv04.h"
+
+static int
+nv94_bus_hwsq_exec(struct nouveau_bus *pbus, u32 *data, u32 size)
+{
+       struct nv50_bus_priv *priv = (void *)pbus;
+       int i;
+
+       nv_mask(pbus, 0x001098, 0x00000008, 0x00000000);
+       nv_wr32(pbus, 0x001304, 0x00000000);
+       nv_wr32(pbus, 0x001318, 0x00000000);
+       for (i = 0; i < size; i++)
+               nv_wr32(priv, 0x080000 + (i * 4), data[i]);
+       nv_mask(pbus, 0x001098, 0x00000018, 0x00000018);
+       nv_wr32(pbus, 0x00130c, 0x00000001);
+
+       return nv_wait(pbus, 0x001308, 0x00000100, 0x00000000) ? 0 : -ETIMEDOUT;
+}
+
+struct nouveau_oclass *
+nv94_bus_oclass = &(struct nv04_bus_impl) {
+       .base.handle = NV_SUBDEV(BUS, 0x94),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_bus_ctor,
+               .dtor = _nouveau_bus_dtor,
+               .init = nv50_bus_init,
+               .fini = _nouveau_bus_fini,
+       },
+       .intr = nv50_bus_intr,
+       .hwsq_exec = nv94_bus_hwsq_exec,
+       .hwsq_size = 128,
+}.base;
index b192d62..73839d7 100644 (file)
  *          Ben Skeggs
  */
 
-#include <subdev/bus.h>
-
-struct nvc0_bus_priv {
-       struct nouveau_bus base;
-};
+#include "nv04.h"
 
 static void
 nvc0_bus_intr(struct nouveau_subdev *subdev)
@@ -60,7 +56,7 @@ nvc0_bus_intr(struct nouveau_subdev *subdev)
 static int
 nvc0_bus_init(struct nouveau_object *object)
 {
-       struct nvc0_bus_priv *priv = (void *)object;
+       struct nv04_bus_priv *priv = (void *)object;
        int ret;
 
        ret = nouveau_bus_init(&priv->base);
@@ -72,30 +68,14 @@ nvc0_bus_init(struct nouveau_object *object)
        return 0;
 }
 
-static int
-nvc0_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
-{
-       struct nvc0_bus_priv *priv;
-       int ret;
-
-       ret = nouveau_bus_create(parent, engine, oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       nv_subdev(priv)->intr = nvc0_bus_intr;
-       return 0;
-}
-
-struct nouveau_oclass
-nvc0_bus_oclass = {
-       .handle = NV_SUBDEV(BUS, 0xc0),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nvc0_bus_ctor,
+struct nouveau_oclass *
+nvc0_bus_oclass = &(struct nv04_bus_impl) {
+       .base.handle = NV_SUBDEV(BUS, 0xc0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_bus_ctor,
                .dtor = _nouveau_bus_dtor,
                .init = nvc0_bus_init,
                .fini = _nouveau_bus_fini,
        },
-};
+       .intr = nvc0_bus_intr,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c
new file mode 100644 (file)
index 0000000..e2938a2
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/option.h>
+
+#include <subdev/clock.h>
+#include <subdev/therm.h>
+#include <subdev/volt.h>
+#include <subdev/fb.h>
+
+#include <subdev/bios.h>
+#include <subdev/bios/boost.h>
+#include <subdev/bios/cstep.h>
+#include <subdev/bios/perf.h>
+
+/******************************************************************************
+ * misc
+ *****************************************************************************/
+static u32
+nouveau_clock_adjust(struct nouveau_clock *clk, bool adjust,
+                    u8 pstate, u8 domain, u32 input)
+{
+       struct nouveau_bios *bios = nouveau_bios(clk);
+       struct nvbios_boostE boostE;
+       u8  ver, hdr, cnt, len;
+       u16 data;
+
+       data = nvbios_boostEm(bios, pstate, &ver, &hdr, &cnt, &len, &boostE);
+       if (data) {
+               struct nvbios_boostS boostS;
+               u8  idx = 0, sver, shdr;
+               u16 subd;
+
+               input = max(boostE.min, input);
+               input = min(boostE.max, input);
+               do {
+                       sver = ver;
+                       shdr = hdr;
+                       subd = nvbios_boostSp(bios, idx++, data, &sver, &shdr,
+                                             cnt, len, &boostS);
+                       if (subd && boostS.domain == domain) {
+                               if (adjust)
+                                       input = input * boostS.percent / 100;
+                               input = max(boostS.min, input);
+                               input = min(boostS.max, input);
+                               break;
+                       }
+               } while (subd);
+       }
+
+       return input;
+}
+
+/******************************************************************************
+ * C-States
+ *****************************************************************************/
+static int
+nouveau_cstate_prog(struct nouveau_clock *clk,
+                   struct nouveau_pstate *pstate, int cstatei)
+{
+       struct nouveau_therm *ptherm = nouveau_therm(clk);
+       struct nouveau_volt *volt = nouveau_volt(clk);
+       struct nouveau_cstate *cstate;
+       int ret;
+
+       if (!list_empty(&pstate->list)) {
+               cstate = list_entry(pstate->list.prev, typeof(*cstate), head);
+       } else {
+               cstate = &pstate->base;
+       }
+
+       ret = nouveau_therm_cstate(ptherm, pstate->fanspeed, +1);
+       if (ret && ret != -ENODEV) {
+               nv_error(clk, "failed to raise fan speed: %d\n", ret);
+               return ret;
+       }
+
+       ret = volt->set_id(volt, cstate->voltage, +1);
+       if (ret && ret != -ENODEV) {
+               nv_error(clk, "failed to raise voltage: %d\n", ret);
+               return ret;
+       }
+
+       ret = clk->calc(clk, cstate);
+       if (ret == 0) {
+               ret = clk->prog(clk);
+               clk->tidy(clk);
+       }
+
+       ret = volt->set_id(volt, cstate->voltage, -1);
+       if (ret && ret != -ENODEV)
+               nv_error(clk, "failed to lower voltage: %d\n", ret);
+
+       ret = nouveau_therm_cstate(ptherm, pstate->fanspeed, -1);
+       if (ret && ret != -ENODEV)
+               nv_error(clk, "failed to lower fan speed: %d\n", ret);
+
+       return 0;
+}
+
+static void
+nouveau_cstate_del(struct nouveau_cstate *cstate)
+{
+       list_del(&cstate->head);
+       kfree(cstate);
+}
+
+static int
+nouveau_cstate_new(struct nouveau_clock *clk, int idx,
+                  struct nouveau_pstate *pstate)
+{
+       struct nouveau_bios *bios = nouveau_bios(clk);
+       struct nouveau_clocks *domain = clk->domains;
+       struct nouveau_cstate *cstate = NULL;
+       struct nvbios_cstepX cstepX;
+       u8  ver, hdr;
+       u16 data;
+
+       data = nvbios_cstepXp(bios, idx, &ver, &hdr, &cstepX);
+       if (!data)
+               return -ENOENT;
+
+       cstate = kzalloc(sizeof(*cstate), GFP_KERNEL);
+       if (!cstate)
+               return -ENOMEM;
+
+       *cstate = pstate->base;
+       cstate->voltage = cstepX.voltage;
+
+       while (domain && domain->name != nv_clk_src_max) {
+               if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) {
+                       u32 freq = nouveau_clock_adjust(clk, true,
+                                                       pstate->pstate,
+                                                       domain->bios,
+                                                       cstepX.freq);
+                       cstate->domain[domain->name] = freq;
+               }
+               domain++;
+       }
+
+       list_add(&cstate->head, &pstate->list);
+       return 0;
+}
+
+/******************************************************************************
+ * P-States
+ *****************************************************************************/
+static int
+nouveau_pstate_prog(struct nouveau_clock *clk, int pstatei)
+{
+       struct nouveau_fb *pfb = nouveau_fb(clk);
+       struct nouveau_pstate *pstate;
+       int ret, idx = 0;
+
+       list_for_each_entry(pstate, &clk->states, head) {
+               if (idx++ == pstatei)
+                       break;
+       }
+
+       nv_debug(clk, "setting performance state %d\n", pstatei);
+       clk->pstate = pstatei;
+
+       if (pfb->ram->calc) {
+               ret = pfb->ram->calc(pfb, pstate->base.domain[nv_clk_src_mem]);
+               if (ret == 0)
+                       ret = pfb->ram->prog(pfb);
+               pfb->ram->tidy(pfb);
+       }
+
+       return nouveau_cstate_prog(clk, pstate, 0);
+}
+
+static int
+nouveau_pstate_calc(struct nouveau_clock *clk)
+{
+       int pstate, ret = 0;
+
+       nv_trace(clk, "P %d U %d A %d T %d D %d\n", clk->pstate,
+                clk->ustate, clk->astate, clk->tstate, clk->dstate);
+
+       if (clk->state_nr && clk->ustate != -1) {
+               pstate = (clk->ustate < 0) ? clk->astate : clk->ustate;
+               pstate = min(pstate, clk->state_nr - 1 - clk->tstate);
+               pstate = max(pstate, clk->dstate);
+       } else {
+               pstate = clk->pstate = -1;
+       }
+
+       nv_trace(clk, "-> %d\n", pstate);
+       if (pstate != clk->pstate)
+               ret = nouveau_pstate_prog(clk, pstate);
+       return ret;
+}
+
+static void
+nouveau_pstate_info(struct nouveau_clock *clk, struct nouveau_pstate *pstate)
+{
+       struct nouveau_clocks *clock = clk->domains - 1;
+       struct nouveau_cstate *cstate;
+       char info[3][32] = { "", "", "" };
+       char name[4] = "--";
+       int i = -1;
+
+       if (pstate->pstate != 0xff)
+               snprintf(name, sizeof(name), "%02x", pstate->pstate);
+
+       while ((++clock)->name != nv_clk_src_max) {
+               u32 lo = pstate->base.domain[clock->name];
+               u32 hi = lo;
+               if (hi == 0)
+                       continue;
+
+               nv_debug(clk, "%02x: %10d KHz\n", clock->name, lo);
+               list_for_each_entry(cstate, &pstate->list, head) {
+                       u32 freq = cstate->domain[clock->name];
+                       lo = min(lo, freq);
+                       hi = max(hi, freq);
+                       nv_debug(clk, "%10d KHz\n", freq);
+               }
+
+               if (clock->mname && ++i < ARRAY_SIZE(info)) {
+                       lo /= clock->mdiv;
+                       hi /= clock->mdiv;
+                       if (lo == hi) {
+                               snprintf(info[i], sizeof(info[i]), "%s %d MHz",
+                                        clock->mname, lo);
+                       } else {
+                               snprintf(info[i], sizeof(info[i]),
+                                        "%s %d-%d MHz", clock->mname, lo, hi);
+                       }
+               }
+       }
+
+       nv_info(clk, "%s: %s %s %s\n", name, info[0], info[1], info[2]);
+}
+
+static void
+nouveau_pstate_del(struct nouveau_pstate *pstate)
+{
+       struct nouveau_cstate *cstate, *temp;
+
+       list_for_each_entry_safe(cstate, temp, &pstate->list, head) {
+               nouveau_cstate_del(cstate);
+       }
+
+       list_del(&pstate->head);
+       kfree(pstate);
+}
+
+static int
+nouveau_pstate_new(struct nouveau_clock *clk, int idx)
+{
+       struct nouveau_bios *bios = nouveau_bios(clk);
+       struct nouveau_clocks *domain = clk->domains - 1;
+       struct nouveau_pstate *pstate;
+       struct nouveau_cstate *cstate;
+       struct nvbios_cstepE cstepE;
+       struct nvbios_perfE perfE;
+       u8  ver, hdr, cnt, len;
+       u16 data;
+
+       data = nvbios_perfEp(bios, idx, &ver, &hdr, &cnt, &len, &perfE);
+       if (!data)
+               return -EINVAL;
+       if (perfE.pstate == 0xff)
+               return 0;
+
+       pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
+       cstate = &pstate->base;
+       if (!pstate)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&pstate->list);
+
+       pstate->pstate = perfE.pstate;
+       pstate->fanspeed = perfE.fanspeed;
+       cstate->voltage = perfE.voltage;
+       cstate->domain[nv_clk_src_core] = perfE.core;
+       cstate->domain[nv_clk_src_shader] = perfE.shader;
+       cstate->domain[nv_clk_src_mem] = perfE.memory;
+       cstate->domain[nv_clk_src_vdec] = perfE.vdec;
+       cstate->domain[nv_clk_src_dom6] = perfE.disp;
+
+       while (ver >= 0x40 && (++domain)->name != nv_clk_src_max) {
+               struct nvbios_perfS perfS;
+               u8  sver = ver, shdr = hdr;
+               u32 perfSe = nvbios_perfSp(bios, data, domain->bios,
+                                         &sver, &shdr, cnt, len, &perfS);
+               if (perfSe == 0 || sver != 0x40)
+                       continue;
+
+               if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) {
+                       perfS.v40.freq = nouveau_clock_adjust(clk, false,
+                                                             pstate->pstate,
+                                                             domain->bios,
+                                                             perfS.v40.freq);
+               }
+
+               cstate->domain[domain->name] = perfS.v40.freq;
+       }
+
+       data = nvbios_cstepEm(bios, pstate->pstate, &ver, &hdr, &cstepE);
+       if (data) {
+               int idx = cstepE.index;
+               do {
+                       nouveau_cstate_new(clk, idx, pstate);
+               } while(idx--);
+       }
+
+       nouveau_pstate_info(clk, pstate);
+       list_add_tail(&pstate->head, &clk->states);
+       clk->state_nr++;
+       return 0;
+}
+
+/******************************************************************************
+ * Adjustment triggers
+ *****************************************************************************/
+static int
+nouveau_clock_ustate_update(struct nouveau_clock *clk, int req)
+{
+       struct nouveau_pstate *pstate;
+       int i = 0;
+
+       /* YKW repellant */
+       return -ENOSYS;
+
+       if (req != -1 && req != -2) {
+               list_for_each_entry(pstate, &clk->states, head) {
+                       if (pstate->pstate == req)
+                               break;
+                       i++;
+               }
+
+               if (pstate->pstate != req)
+                       return -EINVAL;
+               req = i;
+       }
+
+       clk->ustate = req;
+       return 0;
+}
+
+int
+nouveau_clock_ustate(struct nouveau_clock *clk, int req)
+{
+       int ret = nouveau_clock_ustate_update(clk, req);
+       if (ret)
+               return ret;
+       return nouveau_pstate_calc(clk);
+}
+
+int
+nouveau_clock_astate(struct nouveau_clock *clk, int req, int rel)
+{
+       if (!rel) clk->astate  = req;
+       if ( rel) clk->astate += rel;
+       clk->astate = min(clk->astate, clk->state_nr - 1);
+       clk->astate = max(clk->astate, 0);
+       return nouveau_pstate_calc(clk);
+}
+
+int
+nouveau_clock_tstate(struct nouveau_clock *clk, int req, int rel)
+{
+       if (!rel) clk->tstate  = req;
+       if ( rel) clk->tstate += rel;
+       clk->tstate = min(clk->tstate, 0);
+       clk->tstate = max(clk->tstate, -(clk->state_nr - 1));
+       return nouveau_pstate_calc(clk);
+}
+
+int
+nouveau_clock_dstate(struct nouveau_clock *clk, int req, int rel)
+{
+       if (!rel) clk->dstate  = req;
+       if ( rel) clk->dstate += rel;
+       clk->dstate = min(clk->dstate, clk->state_nr - 1);
+       clk->dstate = max(clk->dstate, 0);
+       return nouveau_pstate_calc(clk);
+}
+
+/******************************************************************************
+ * subdev base class implementation
+ *****************************************************************************/
+int
+_nouveau_clock_init(struct nouveau_object *object)
+{
+       struct nouveau_clock *clk = (void *)object;
+       struct nouveau_clocks *clock = clk->domains;
+       int ret;
+
+       memset(&clk->bstate, 0x00, sizeof(clk->bstate));
+       INIT_LIST_HEAD(&clk->bstate.list);
+       clk->bstate.pstate = 0xff;
+
+       while (clock->name != nv_clk_src_max) {
+               ret = clk->read(clk, clock->name);
+               if (ret < 0) {
+                       nv_error(clk, "%02x freq unknown\n", clock->name);
+                       return ret;
+               }
+               clk->bstate.base.domain[clock->name] = ret;
+               clock++;
+       }
+
+       nouveau_pstate_info(clk, &clk->bstate);
+
+       clk->astate = clk->state_nr - 1;
+       clk->tstate = 0;
+       clk->dstate = 0;
+       clk->pstate = -1;
+       nouveau_pstate_calc(clk);
+       return 0;
+}
+
+void
+_nouveau_clock_dtor(struct nouveau_object *object)
+{
+       struct nouveau_clock *clk = (void *)object;
+       struct nouveau_pstate *pstate, *temp;
+
+       list_for_each_entry_safe(pstate, temp, &clk->states, head) {
+               nouveau_pstate_del(pstate);
+       }
+
+       nouveau_subdev_destroy(&clk->base);
+}
+
+int
+nouveau_clock_create_(struct nouveau_object *parent,
+                     struct nouveau_object *engine,
+                     struct nouveau_oclass *oclass,
+                     struct nouveau_clocks *clocks,
+                     int length, void **object)
+{
+       struct nouveau_device *device = nv_device(parent);
+       struct nouveau_clock *clk;
+       int ret, idx, arglen;
+       const char *mode;
+
+       ret = nouveau_subdev_create_(parent, engine, oclass, 0, "CLK",
+                                    "clock", length, object);
+       clk = *object;
+       if (ret)
+               return ret;
+
+       INIT_LIST_HEAD(&clk->states);
+       clk->domains = clocks;
+       clk->ustate = -1;
+
+       idx = 0;
+       do {
+               ret = nouveau_pstate_new(clk, idx++);
+       } while (ret == 0);
+
+       mode = nouveau_stropt(device->cfgopt, "NvClkMode", &arglen);
+       if (mode) {
+               if (!strncasecmpz(mode, "disabled", arglen)) {
+                       clk->ustate = -1;
+               } else {
+                       char save = mode[arglen];
+                       long v;
+
+                       ((char *)mode)[arglen] = '\0';
+                       if (!kstrtol(mode, 0, &v))
+                               nouveau_clock_ustate_update(clk, v);
+                       ((char *)mode)[arglen] = save;
+               }
+       }
+
+       return 0;
+}
index a142775..da50c1b 100644 (file)
@@ -77,7 +77,7 @@ nv04_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv04_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, NULL, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
index 0db5dbf..db7346f 100644 (file)
  */
 
 #include <subdev/clock.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+#include "pll.h"
 
 struct nv40_clock_priv {
        struct nouveau_clock base;
+       u32 ctrl;
+       u32 npll_ctrl;
+       u32 npll_coef;
+       u32 spll;
+};
+
+static struct nouveau_clocks
+nv40_domain[] = {
+       { nv_clk_src_crystal, 0xff },
+       { nv_clk_src_href   , 0xff },
+       { nv_clk_src_core   , 0xff, 0, "core", 1000 },
+       { nv_clk_src_shader , 0xff, 0, "shader", 1000 },
+       { nv_clk_src_mem    , 0xff, 0, "memory", 1000 },
+       { nv_clk_src_max }
 };
 
+static u32
+read_pll_1(struct nv40_clock_priv *priv, u32 reg)
+{
+       u32 ctrl = nv_rd32(priv, reg + 0x00);
+       int P = (ctrl & 0x00070000) >> 16;
+       int N = (ctrl & 0x0000ff00) >> 8;
+       int M = (ctrl & 0x000000ff) >> 0;
+       u32 ref = 27000, clk = 0;
+
+       if (ctrl & 0x80000000)
+               clk = ref * N / M;
+
+       return clk >> P;
+}
+
+static u32
+read_pll_2(struct nv40_clock_priv *priv, u32 reg)
+{
+       u32 ctrl = nv_rd32(priv, reg + 0x00);
+       u32 coef = nv_rd32(priv, reg + 0x04);
+       int N2 = (coef & 0xff000000) >> 24;
+       int M2 = (coef & 0x00ff0000) >> 16;
+       int N1 = (coef & 0x0000ff00) >> 8;
+       int M1 = (coef & 0x000000ff) >> 0;
+       int P = (ctrl & 0x00070000) >> 16;
+       u32 ref = 27000, clk = 0;
+
+       if ((ctrl & 0x80000000) && M1) {
+               clk = ref * N1 / M1;
+               if ((ctrl & 0x40000100) == 0x40000000) {
+                       if (M2)
+                               clk = clk * N2 / M2;
+                       else
+                               clk = 0;
+               }
+       }
+
+       return clk >> P;
+}
+
+static u32
+read_clk(struct nv40_clock_priv *priv, u32 src)
+{
+       switch (src) {
+       case 3:
+               return read_pll_2(priv, 0x004000);
+       case 2:
+               return read_pll_1(priv, 0x004008);
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int
+nv40_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
+{
+       struct nv40_clock_priv *priv = (void *)clk;
+       u32 mast = nv_rd32(priv, 0x00c040);
+
+       switch (src) {
+       case nv_clk_src_crystal:
+               return nv_device(priv)->crystal;
+       case nv_clk_src_href:
+               return 100000; /*XXX: PCIE/AGP differ*/
+       case nv_clk_src_core:
+               return read_clk(priv, (mast & 0x00000003) >> 0);
+       case nv_clk_src_shader:
+               return read_clk(priv, (mast & 0x00000030) >> 4);
+       case nv_clk_src_mem:
+               return read_pll_2(priv, 0x4020);
+       default:
+               break;
+       }
+
+       nv_debug(priv, "unknown clock source %d 0x%08x\n", src, mast);
+       return -EINVAL;
+}
+
+static int
+nv40_clock_calc_pll(struct nv40_clock_priv *priv, u32 reg, u32 clk,
+                   int *N1, int *M1, int *N2, int *M2, int *log2P)
+{
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll pll;
+       int ret;
+
+       ret = nvbios_pll_parse(bios, reg, &pll);
+       if (ret)
+               return ret;
+
+       if (clk < pll.vco1.max_freq)
+               pll.vco2.max_freq = 0;
+
+       ret = nv04_pll_calc(nv_subdev(priv), &pll, clk, N1, M1, N2, M2, log2P);
+       if (ret == 0)
+               return -ERANGE;
+       return ret;
+}
+
+static int
+nv40_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate)
+{
+       struct nv40_clock_priv *priv = (void *)clk;
+       int gclk = cstate->domain[nv_clk_src_core];
+       int sclk = cstate->domain[nv_clk_src_shader];
+       int N1, M1, N2, M2, log2P;
+       int ret;
+
+       /* core/geometric clock */
+       ret = nv40_clock_calc_pll(priv, 0x004000, gclk,
+                                &N1, &M1, &N2, &M2, &log2P);
+       if (ret < 0)
+               return ret;
+
+       if (N2 == M2) {
+               priv->npll_ctrl = 0x80000100 | (log2P << 16);
+               priv->npll_coef = (N1 << 8) | M1;
+       } else {
+               priv->npll_ctrl = 0xc0000000 | (log2P << 16);
+               priv->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
+       }
+
+       /* use the second pll for shader/rop clock, if it differs from core */
+       if (sclk && sclk != gclk) {
+               ret = nv40_clock_calc_pll(priv, 0x004008, sclk,
+                                        &N1, &M1, NULL, NULL, &log2P);
+               if (ret < 0)
+                       return ret;
+
+               priv->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1;
+               priv->ctrl = 0x00000223;
+       } else {
+               priv->spll = 0x00000000;
+               priv->ctrl = 0x00000333;
+       }
+
+       return 0;
+}
+
+static int
+nv40_clock_prog(struct nouveau_clock *clk)
+{
+       struct nv40_clock_priv *priv = (void *)clk;
+       nv_mask(priv, 0x00c040, 0x00000333, 0x00000000);
+       nv_wr32(priv, 0x004004, priv->npll_coef);
+       nv_mask(priv, 0x004000, 0xc0070100, priv->npll_ctrl);
+       nv_mask(priv, 0x004008, 0xc007ffff, priv->spll);
+       mdelay(5);
+       nv_mask(priv, 0x00c040, 0x00000333, priv->ctrl);
+       return 0;
+}
+
+static void
+nv40_clock_tidy(struct nouveau_clock *clk)
+{
+}
+
 static int
 nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_oclass *oclass, void *data, u32 size,
@@ -36,13 +213,17 @@ nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv40_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, nv40_domain, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
        priv->base.pll_calc = nv04_clock_pll_calc;
        priv->base.pll_prog = nv04_clock_pll_prog;
+       priv->base.read = nv40_clock_read;
+       priv->base.calc = nv40_clock_calc;
+       priv->base.prog = nv40_clock_prog;
+       priv->base.tidy = nv40_clock_tidy;
        return 0;
 }
 
index d09d3e7..250a6d9 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/clock.h>
 #include <subdev/bios.h>
 #include <subdev/bios/pll.h>
 
+#include "nv50.h"
 #include "pll.h"
+#include "seq.h"
 
-struct nv50_clock_priv {
-       struct nouveau_clock base;
-};
+static u32
+read_div(struct nv50_clock_priv *priv)
+{
+       switch (nv_device(priv)->chipset) {
+       case 0x50: /* it exists, but only has bit 31, not the dividers.. */
+       case 0x84:
+       case 0x86:
+       case 0x98:
+       case 0xa0:
+               return nv_rd32(priv, 0x004700);
+       case 0x92:
+       case 0x94:
+       case 0x96:
+               return nv_rd32(priv, 0x004800);
+       default:
+               return 0x00000000;
+       }
+}
+
+static u32
+read_pll_src(struct nv50_clock_priv *priv, u32 base)
+{
+       struct nouveau_clock *clk = &priv->base;
+       u32 coef, ref = clk->read(clk, nv_clk_src_crystal);
+       u32 rsel = nv_rd32(priv, 0x00e18c);
+       int P, N, M, id;
+
+       switch (nv_device(priv)->chipset) {
+       case 0x50:
+       case 0xa0:
+               switch (base) {
+               case 0x4020:
+               case 0x4028: id = !!(rsel & 0x00000004); break;
+               case 0x4008: id = !!(rsel & 0x00000008); break;
+               case 0x4030: id = 0; break;
+               default:
+                       nv_error(priv, "ref: bad pll 0x%06x\n", base);
+                       return 0;
+               }
+
+               coef = nv_rd32(priv, 0x00e81c + (id * 0x0c));
+               ref *=  (coef & 0x01000000) ? 2 : 4;
+               P    =  (coef & 0x00070000) >> 16;
+               N    = ((coef & 0x0000ff00) >> 8) + 1;
+               M    = ((coef & 0x000000ff) >> 0) + 1;
+               break;
+       case 0x84:
+       case 0x86:
+       case 0x92:
+               coef = nv_rd32(priv, 0x00e81c);
+               P    = (coef & 0x00070000) >> 16;
+               N    = (coef & 0x0000ff00) >> 8;
+               M    = (coef & 0x000000ff) >> 0;
+               break;
+       case 0x94:
+       case 0x96:
+       case 0x98:
+               rsel = nv_rd32(priv, 0x00c050);
+               switch (base) {
+               case 0x4020: rsel = (rsel & 0x00000003) >> 0; break;
+               case 0x4008: rsel = (rsel & 0x0000000c) >> 2; break;
+               case 0x4028: rsel = (rsel & 0x00001800) >> 11; break;
+               case 0x4030: rsel = 3; break;
+               default:
+                       nv_error(priv, "ref: bad pll 0x%06x\n", base);
+                       return 0;
+               }
+
+               switch (rsel) {
+               case 0: id = 1; break;
+               case 1: return clk->read(clk, nv_clk_src_crystal);
+               case 2: return clk->read(clk, nv_clk_src_href);
+               case 3: id = 0; break;
+               }
+
+               coef =  nv_rd32(priv, 0x00e81c + (id * 0x28));
+               P    = (nv_rd32(priv, 0x00e824 + (id * 0x28)) >> 16) & 7;
+               P   += (coef & 0x00070000) >> 16;
+               N    = (coef & 0x0000ff00) >> 8;
+               M    = (coef & 0x000000ff) >> 0;
+               break;
+       default:
+               BUG_ON(1);
+       }
+
+       if (M)
+               return (ref * N / M) >> P;
+       return 0;
+}
+
+static u32
+read_pll_ref(struct nv50_clock_priv *priv, u32 base)
+{
+       struct nouveau_clock *clk = &priv->base;
+       u32 src, mast = nv_rd32(priv, 0x00c040);
+
+       switch (base) {
+       case 0x004028:
+               src = !!(mast & 0x00200000);
+               break;
+       case 0x004020:
+               src = !!(mast & 0x00400000);
+               break;
+       case 0x004008:
+               src = !!(mast & 0x00010000);
+               break;
+       case 0x004030:
+               src = !!(mast & 0x02000000);
+               break;
+       case 0x00e810:
+               return clk->read(clk, nv_clk_src_crystal);
+       default:
+               nv_error(priv, "bad pll 0x%06x\n", base);
+               return 0;
+       }
+
+       if (src)
+               return clk->read(clk, nv_clk_src_href);
+       return read_pll_src(priv, base);
+}
+
+static u32
+read_pll(struct nv50_clock_priv *priv, u32 base)
+{
+       struct nouveau_clock *clk = &priv->base;
+       u32 mast = nv_rd32(priv, 0x00c040);
+       u32 ctrl = nv_rd32(priv, base + 0);
+       u32 coef = nv_rd32(priv, base + 4);
+       u32 ref = read_pll_ref(priv, base);
+       u32 freq = 0;
+       int N1, N2, M1, M2;
+
+       if (base == 0x004028 && (mast & 0x00100000)) {
+               /* wtf, appears to only disable post-divider on nva0 */
+               if (nv_device(priv)->chipset != 0xa0)
+                       return clk->read(clk, nv_clk_src_dom6);
+       }
+
+       N2 = (coef & 0xff000000) >> 24;
+       M2 = (coef & 0x00ff0000) >> 16;
+       N1 = (coef & 0x0000ff00) >> 8;
+       M1 = (coef & 0x000000ff);
+       if ((ctrl & 0x80000000) && M1) {
+               freq = ref * N1 / M1;
+               if ((ctrl & 0x40000100) == 0x40000000) {
+                       if (M2)
+                               freq = freq * N2 / M2;
+                       else
+                               freq = 0;
+               }
+       }
+
+       return freq;
+}
 
 static int
+nv50_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
+{
+       struct nv50_clock_priv *priv = (void *)clk;
+       u32 mast = nv_rd32(priv, 0x00c040);
+       u32 P = 0;
+
+       switch (src) {
+       case nv_clk_src_crystal:
+               return nv_device(priv)->crystal;
+       case nv_clk_src_href:
+               return 100000; /* PCIE reference clock */
+       case nv_clk_src_hclk:
+               return div_u64((u64)clk->read(clk, nv_clk_src_href) * 27778, 10000);
+       case nv_clk_src_hclkm3:
+               return clk->read(clk, nv_clk_src_hclk) * 3;
+       case nv_clk_src_hclkm3d2:
+               return clk->read(clk, nv_clk_src_hclk) * 3 / 2;
+       case nv_clk_src_host:
+               switch (mast & 0x30000000) {
+               case 0x00000000: return clk->read(clk, nv_clk_src_href);
+               case 0x10000000: break;
+               case 0x20000000: /* !0x50 */
+               case 0x30000000: return clk->read(clk, nv_clk_src_hclk);
+               }
+               break;
+       case nv_clk_src_core:
+               if (!(mast & 0x00100000))
+                       P = (nv_rd32(priv, 0x004028) & 0x00070000) >> 16;
+               switch (mast & 0x00000003) {
+               case 0x00000000: return clk->read(clk, nv_clk_src_crystal) >> P;
+               case 0x00000001: return clk->read(clk, nv_clk_src_dom6);
+               case 0x00000002: return read_pll(priv, 0x004020) >> P;
+               case 0x00000003: return read_pll(priv, 0x004028) >> P;
+               }
+               break;
+       case nv_clk_src_shader:
+               P = (nv_rd32(priv, 0x004020) & 0x00070000) >> 16;
+               switch (mast & 0x00000030) {
+               case 0x00000000:
+                       if (mast & 0x00000080)
+                               return clk->read(clk, nv_clk_src_host) >> P;
+                       return clk->read(clk, nv_clk_src_crystal) >> P;
+               case 0x00000010: break;
+               case 0x00000020: return read_pll(priv, 0x004028) >> P;
+               case 0x00000030: return read_pll(priv, 0x004020) >> P;
+               }
+               break;
+       case nv_clk_src_mem:
+               P = (nv_rd32(priv, 0x004008) & 0x00070000) >> 16;
+               if (nv_rd32(priv, 0x004008) & 0x00000200) {
+                       switch (mast & 0x0000c000) {
+                       case 0x00000000:
+                               return clk->read(clk, nv_clk_src_crystal) >> P;
+                       case 0x00008000:
+                       case 0x0000c000:
+                               return clk->read(clk, nv_clk_src_href) >> P;
+                       }
+               } else {
+                       return read_pll(priv, 0x004008) >> P;
+               }
+               break;
+       case nv_clk_src_vdec:
+               P = (read_div(priv) & 0x00000700) >> 8;
+               switch (nv_device(priv)->chipset) {
+               case 0x84:
+               case 0x86:
+               case 0x92:
+               case 0x94:
+               case 0x96:
+               case 0xa0:
+                       switch (mast & 0x00000c00) {
+                       case 0x00000000:
+                               if (nv_device(priv)->chipset == 0xa0) /* wtf?? */
+                                       return clk->read(clk, nv_clk_src_core) >> P;
+                               return clk->read(clk, nv_clk_src_crystal) >> P;
+                       case 0x00000400:
+                               return 0;
+                       case 0x00000800:
+                               if (mast & 0x01000000)
+                                       return read_pll(priv, 0x004028) >> P;
+                               return read_pll(priv, 0x004030) >> P;
+                       case 0x00000c00:
+                               return clk->read(clk, nv_clk_src_core) >> P;
+                       }
+                       break;
+               case 0x98:
+                       switch (mast & 0x00000c00) {
+                       case 0x00000000:
+                               return clk->read(clk, nv_clk_src_core) >> P;
+                       case 0x00000400:
+                               return 0;
+                       case 0x00000800:
+                               return clk->read(clk, nv_clk_src_hclkm3d2) >> P;
+                       case 0x00000c00:
+                               return clk->read(clk, nv_clk_src_mem) >> P;
+                       }
+                       break;
+               }
+               break;
+       case nv_clk_src_dom6:
+               switch (nv_device(priv)->chipset) {
+               case 0x50:
+               case 0xa0:
+                       return read_pll(priv, 0x00e810) >> 2;
+               case 0x84:
+               case 0x86:
+               case 0x92:
+               case 0x94:
+               case 0x96:
+               case 0x98:
+                       P = (read_div(priv) & 0x00000007) >> 0;
+                       switch (mast & 0x0c000000) {
+                       case 0x00000000: return clk->read(clk, nv_clk_src_href);
+                       case 0x04000000: break;
+                       case 0x08000000: return clk->read(clk, nv_clk_src_hclk);
+                       case 0x0c000000:
+                               return clk->read(clk, nv_clk_src_hclkm3) >> P;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       default:
+               break;
+       }
+
+       nv_debug(priv, "unknown clock source %d 0x%08x\n", src, mast);
+       return -EINVAL;
+}
+
+static u32
+calc_pll(struct nv50_clock_priv *priv, u32 reg, u32 clk, int *N, int *M, int *P)
+{
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll pll;
+       int ret;
+
+       ret = nvbios_pll_parse(bios, reg, &pll);
+       if (ret)
+               return 0;
+
+       pll.vco2.max_freq = 0;
+       pll.refclk = read_pll_ref(priv, reg);
+       if (!pll.refclk)
+               return 0;
+
+       return nv04_pll_calc(nv_subdev(priv), &pll, clk, N, M, NULL, NULL, P);
+}
+
+static inline u32
+calc_div(u32 src, u32 target, int *div)
+{
+       u32 clk0 = src, clk1 = src;
+       for (*div = 0; *div <= 7; (*div)++) {
+               if (clk0 <= target) {
+                       clk1 = clk0 << (*div ? 1 : 0);
+                       break;
+               }
+               clk0 >>= 1;
+       }
+
+       if (target - clk0 <= clk1 - target)
+               return clk0;
+       (*div)--;
+       return clk1;
+}
+
+static inline u32
+clk_same(u32 a, u32 b)
+{
+       return ((a / 1000) == (b / 1000));
+}
+
+static int
+nv50_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate)
+{
+       struct nv50_clock_priv *priv = (void *)clk;
+       struct nv50_clock_hwsq *hwsq = &priv->hwsq;
+       const int shader = cstate->domain[nv_clk_src_shader];
+       const int core = cstate->domain[nv_clk_src_core];
+       const int vdec = cstate->domain[nv_clk_src_vdec];
+       const int dom6 = cstate->domain[nv_clk_src_dom6];
+       u32 mastm = 0, mastv = 0;
+       u32 divsm = 0, divsv = 0;
+       int N, M, P1, P2;
+       int freq, out;
+
+       /* prepare a hwsq script from which we'll perform the reclock */
+       out = clk_init(hwsq, nv_subdev(clk));
+       if (out)
+               return out;
+
+       clk_wr32(hwsq, fifo, 0x00000001); /* block fifo */
+       clk_nsec(hwsq, 8000);
+       clk_setf(hwsq, 0x10, 0x00); /* disable fb */
+       clk_wait(hwsq, 0x00, 0x01); /* wait for fb disabled */
+
+       /* vdec: avoid modifying xpll until we know exactly how the other
+        * clock domains work, i suspect at least some of them can also be
+        * tied to xpll...
+        */
+       if (vdec) {
+               /* see how close we can get using nvclk as a source */
+               freq = calc_div(core, vdec, &P1);
+
+               /* see how close we can get using xpll/hclk as a source */
+               if (nv_device(priv)->chipset != 0x98)
+                       out = read_pll(priv, 0x004030);
+               else
+                       out = clk->read(clk, nv_clk_src_hclkm3d2);
+               out = calc_div(out, vdec, &P2);
+
+               /* select whichever gets us closest */
+               if (abs(vdec - freq) <= abs(vdec - out)) {
+                       if (nv_device(priv)->chipset != 0x98)
+                               mastv |= 0x00000c00;
+                       divsv |= P1 << 8;
+               } else {
+                       mastv |= 0x00000800;
+                       divsv |= P2 << 8;
+               }
+
+               mastm |= 0x00000c00;
+               divsm |= 0x00000700;
+       }
+
+       /* dom6: nfi what this is, but we're limited to various combinations
+        * of the host clock frequency
+        */
+       if (dom6) {
+               if (clk_same(dom6, clk->read(clk, nv_clk_src_href))) {
+                       mastv |= 0x00000000;
+               } else
+               if (clk_same(dom6, clk->read(clk, nv_clk_src_hclk))) {
+                       mastv |= 0x08000000;
+               } else {
+                       freq = clk->read(clk, nv_clk_src_hclk) * 3;
+                       freq = calc_div(freq, dom6, &P1);
+
+                       mastv |= 0x0c000000;
+                       divsv |= P1;
+               }
+
+               mastm |= 0x0c000000;
+               divsm |= 0x00000007;
+       }
+
+       /* vdec/dom6: switch to "safe" clocks temporarily, update dividers
+        * and then switch to target clocks
+        */
+       clk_mask(hwsq, mast, mastm, 0x00000000);
+       clk_mask(hwsq, divs, divsm, divsv);
+       clk_mask(hwsq, mast, mastm, mastv);
+
+       /* core/shader: disconnect nvclk/sclk from their PLLs (nvclk to dom6,
+        * sclk to hclk) before reprogramming
+        */
+       if (nv_device(priv)->chipset < 0x92)
+               clk_mask(hwsq, mast, 0x001000b0, 0x00100080);
+       else
+               clk_mask(hwsq, mast, 0x000000b3, 0x00000081);
+
+       /* core: for the moment at least, always use nvpll */
+       freq = calc_pll(priv, 0x4028, core, &N, &M, &P1);
+       if (freq == 0)
+               return -ERANGE;
+
+       clk_mask(hwsq, nvpll[0], 0xc03f0100,
+                                0x80000000 | (P1 << 19) | (P1 << 16));
+       clk_mask(hwsq, nvpll[1], 0x0000ffff, (N << 8) | M);
+
+       /* shader: tie to nvclk if possible, otherwise use spll.  have to be
+        * very careful that the shader clock is at least twice the core, or
+        * some chipsets will be very unhappy.  i expect most or all of these
+        * cases will be handled by tying to nvclk, but it's possible there's
+        * corners
+        */
+       if (P1-- && shader == (core << 1)) {
+               clk_mask(hwsq, spll[0], 0xc03f0100, (P1 << 19) | (P1 << 16));
+               clk_mask(hwsq, mast, 0x00100033, 0x00000023);
+       } else {
+               freq = calc_pll(priv, 0x4020, shader, &N, &M, &P1);
+               if (freq == 0)
+                       return -ERANGE;
+
+               clk_mask(hwsq, spll[0], 0xc03f0100,
+                                       0x80000000 | (P1 << 19) | (P1 << 16));
+               clk_mask(hwsq, spll[1], 0x0000ffff, (N << 8) | M);
+               clk_mask(hwsq, mast, 0x00100033, 0x00000033);
+       }
+
+       /* restore normal operation */
+       clk_setf(hwsq, 0x10, 0x01); /* enable fb */
+       clk_wait(hwsq, 0x00, 0x00); /* wait for fb enabled */
+       clk_wr32(hwsq, fifo, 0x00000000); /* un-block fifo */
+       return 0;
+}
+
+static int
+nv50_clock_prog(struct nouveau_clock *clk)
+{
+       struct nv50_clock_priv *priv = (void *)clk;
+       return clk_exec(&priv->hwsq, true);
+}
+
+static void
+nv50_clock_tidy(struct nouveau_clock *clk)
+{
+       struct nv50_clock_priv *priv = (void *)clk;
+       clk_exec(&priv->hwsq, false);
+}
+
+int
 nv50_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_oclass *oclass, void *data, u32 size,
                struct nouveau_object **pobject)
 {
+       struct nv50_clock_oclass *pclass = (void *)oclass;
        struct nv50_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, pclass->domains,
+                                 &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       priv->base.pll_calc = nv04_clock_pll_calc;
+       priv->hwsq.r_fifo = hwsq_reg(0x002504);
+       priv->hwsq.r_spll[0] = hwsq_reg(0x004020);
+       priv->hwsq.r_spll[1] = hwsq_reg(0x004024);
+       priv->hwsq.r_nvpll[0] = hwsq_reg(0x004028);
+       priv->hwsq.r_nvpll[1] = hwsq_reg(0x00402c);
+       switch (nv_device(priv)->chipset) {
+       case 0x92:
+       case 0x94:
+       case 0x96:
+               priv->hwsq.r_divs = hwsq_reg(0x004800);
+               break;
+       default:
+               priv->hwsq.r_divs = hwsq_reg(0x004700);
+               break;
+       }
+       priv->hwsq.r_mast = hwsq_reg(0x00c040);
+
+       priv->base.read = nv50_clock_read;
+       priv->base.calc = nv50_clock_calc;
+       priv->base.prog = nv50_clock_prog;
+       priv->base.tidy = nv50_clock_tidy;
        return 0;
 }
 
-struct nouveau_oclass
-nv50_clock_oclass = {
-       .handle = NV_SUBDEV(CLOCK, 0x50),
-       .ofuncs = &(struct nouveau_ofuncs) {
+static struct nouveau_clocks
+nv50_domains[] = {
+       { nv_clk_src_crystal, 0xff },
+       { nv_clk_src_href   , 0xff },
+       { nv_clk_src_core   , 0xff, 0, "core", 1000 },
+       { nv_clk_src_shader , 0xff, 0, "shader", 1000 },
+       { nv_clk_src_mem    , 0xff, 0, "memory", 1000 },
+       { nv_clk_src_max }
+};
+
+struct nouveau_oclass *
+nv50_clock_oclass = &(struct nv50_clock_oclass) {
+       .base.handle = NV_SUBDEV(CLOCK, 0x50),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv50_clock_ctor,
                .dtor = _nouveau_clock_dtor,
                .init = _nouveau_clock_init,
                .fini = _nouveau_clock_fini,
        },
-};
+       .domains = nv50_domains,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.h b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.h
new file mode 100644 (file)
index 0000000..f10917d
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __NVKM_CLK_NV50_H__
+#define __NVKM_CLK_NV50_H__
+
+#include <subdev/bus.h>
+#include <subdev/bus/hwsq.h>
+#include <subdev/clock.h>
+
+struct nv50_clock_hwsq {
+       struct hwsq base;
+       struct hwsq_reg r_fifo;
+       struct hwsq_reg r_spll[2];
+       struct hwsq_reg r_nvpll[2];
+       struct hwsq_reg r_divs;
+       struct hwsq_reg r_mast;
+};
+
+struct nv50_clock_priv {
+       struct nouveau_clock base;
+       struct nv50_clock_hwsq hwsq;
+};
+
+int  nv50_clock_ctor(struct nouveau_object *, struct nouveau_object *,
+                    struct nouveau_oclass *, void *, u32,
+                    struct nouveau_object **);
+
+struct nv50_clock_oclass {
+       struct nouveau_oclass base;
+       struct nouveau_clocks *domains;
+};
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv84.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv84.c
new file mode 100644 (file)
index 0000000..b0b7c14
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "nv50.h"
+
+static struct nouveau_clocks
+nv84_domains[] = {
+       { nv_clk_src_crystal, 0xff },
+       { nv_clk_src_href   , 0xff },
+       { nv_clk_src_core   , 0xff, 0, "core", 1000 },
+       { nv_clk_src_shader , 0xff, 0, "shader", 1000 },
+       { nv_clk_src_mem    , 0xff, 0, "memory", 1000 },
+       { nv_clk_src_vdec   , 0xff },
+       { nv_clk_src_max }
+};
+
+struct nouveau_oclass *
+nv84_clock_oclass = &(struct nv50_clock_oclass) {
+       .base.handle = NV_SUBDEV(CLOCK, 0x84),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_clock_ctor,
+               .dtor = _nouveau_clock_dtor,
+               .init = _nouveau_clock_init,
+               .fini = _nouveau_clock_fini,
+       },
+       .domains = nv84_domains,
+}.base;
index f074cd2..4f5a137 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/clock.h>
 #include <subdev/bios.h>
 #include <subdev/bios/pll.h>
+#include <subdev/timer.h>
 
 #include "pll.h"
 
+#include "nva3.h"
+
 struct nva3_clock_priv {
        struct nouveau_clock base;
+       struct nva3_clock_info eng[nv_clk_src_max];
 };
 
+static u32 read_clk(struct nva3_clock_priv *, int, bool);
+static u32 read_pll(struct nva3_clock_priv *, int, u32);
+
+static u32
+read_vco(struct nva3_clock_priv *priv, int clk)
+{
+       u32 sctl = nv_rd32(priv, 0x4120 + (clk * 4));
+       if ((sctl & 0x00000030) != 0x00000030)
+               return read_pll(priv, 0x41, 0x00e820);
+       return read_pll(priv, 0x42, 0x00e8a0);
+}
+
+static u32
+read_clk(struct nva3_clock_priv *priv, int clk, bool ignore_en)
+{
+       u32 sctl, sdiv, sclk;
+
+       /* refclk for the 0xe8xx plls is a fixed frequency */
+       if (clk >= 0x40) {
+               if (nv_device(priv)->chipset == 0xaf) {
+                       /* no joke.. seriously.. sigh.. */
+                       return nv_rd32(priv, 0x00471c) * 1000;
+               }
+
+               return nv_device(priv)->crystal;
+       }
+
+       sctl = nv_rd32(priv, 0x4120 + (clk * 4));
+       if (!ignore_en && !(sctl & 0x00000100))
+               return 0;
+
+       switch (sctl & 0x00003000) {
+       case 0x00000000:
+               return nv_device(priv)->crystal;
+       case 0x00002000:
+               if (sctl & 0x00000040)
+                       return 108000;
+               return 100000;
+       case 0x00003000:
+               sclk = read_vco(priv, clk);
+               sdiv = ((sctl & 0x003f0000) >> 16) + 2;
+               return (sclk * 2) / sdiv;
+       default:
+               return 0;
+       }
+}
+
+static u32
+read_pll(struct nva3_clock_priv *priv, int clk, u32 pll)
+{
+       u32 ctrl = nv_rd32(priv, pll + 0);
+       u32 sclk = 0, P = 1, N = 1, M = 1;
+
+       if (!(ctrl & 0x00000008)) {
+               if (ctrl & 0x00000001) {
+                       u32 coef = nv_rd32(priv, pll + 4);
+                       M = (coef & 0x000000ff) >> 0;
+                       N = (coef & 0x0000ff00) >> 8;
+                       P = (coef & 0x003f0000) >> 16;
+
+                       /* no post-divider on these.. */
+                       if ((pll & 0x00ff00) == 0x00e800)
+                               P = 1;
+
+                       sclk = read_clk(priv, 0x00 + clk, false);
+               }
+       } else {
+               sclk = read_clk(priv, 0x10 + clk, false);
+       }
+
+       if (M * P)
+               return sclk * N / (M * P);
+       return 0;
+}
+
+static int
+nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
+{
+       struct nva3_clock_priv *priv = (void *)clk;
+
+       switch (src) {
+       case nv_clk_src_crystal:
+               return nv_device(priv)->crystal;
+       case nv_clk_src_href:
+               return 100000;
+       case nv_clk_src_core:
+               return read_pll(priv, 0x00, 0x4200);
+       case nv_clk_src_shader:
+               return read_pll(priv, 0x01, 0x4220);
+       case nv_clk_src_mem:
+               return read_pll(priv, 0x02, 0x4000);
+       case nv_clk_src_disp:
+               return read_clk(priv, 0x20, false);
+       case nv_clk_src_vdec:
+               return read_clk(priv, 0x21, false);
+       case nv_clk_src_daemon:
+               return read_clk(priv, 0x25, false);
+       default:
+               nv_error(clk, "invalid clock source %d\n", src);
+               return -EINVAL;
+       }
+}
+
 int
-nva3_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
-                   int clk, struct nouveau_pll_vals *pv)
+nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz,
+               struct nva3_clock_info *info)
 {
-       int ret, N, M, P;
+       struct nouveau_bios *bios = nouveau_bios(clock);
+       struct nva3_clock_priv *priv = (void *)clock;
+       struct nvbios_pll limits;
+       u32 oclk, sclk, sdiv;
+       int P, N, M, diff;
+       int ret;
+
+       info->pll = 0;
+       info->clk = 0;
+
+       switch (khz) {
+       case 27000:
+               info->clk = 0x00000100;
+               return khz;
+       case 100000:
+               info->clk = 0x00002100;
+               return khz;
+       case 108000:
+               info->clk = 0x00002140;
+               return khz;
+       default:
+               sclk = read_vco(priv, clk);
+               sdiv = min((sclk * 2) / (khz - 2999), (u32)65);
+               /* if the clock has a PLL attached, and we can get a within
+                * [-2, 3) MHz of a divider, we'll disable the PLL and use
+                * the divider instead.
+                *
+                * divider can go as low as 2, limited here because NVIDIA
+                * and the VBIOS on my NVA8 seem to prefer using the PLL
+                * for 810MHz - is there a good reason?
+                */
+               if (sdiv > 4) {
+                       oclk = (sclk * 2) / sdiv;
+                       diff = khz - oclk;
+                       if (!pll || (diff >= -2000 && diff < 3000)) {
+                               info->clk = (((sdiv - 2) << 16) | 0x00003100);
+                               return oclk;
+                       }
+               }
+
+               if (!pll)
+                       return -ERANGE;
+               break;
+       }
 
-       ret = nva3_pll_calc(nv_subdev(clock), info, clk, &N, NULL, &M, &P);
+       ret = nvbios_pll_parse(bios, pll, &limits);
+       if (ret)
+               return ret;
+
+       limits.refclk = read_clk(priv, clk - 0x10, true);
+       if (!limits.refclk)
+               return -EINVAL;
 
-       if (ret > 0) {
-               pv->refclk = info->refclk;
-               pv->N1 = N;
-               pv->M1 = M;
-               pv->log2P = P;
+       ret = nva3_pll_calc(nv_subdev(priv), &limits, khz, &N, NULL, &M, &P);
+       if (ret >= 0) {
+               info->clk = nv_rd32(priv, 0x4120 + (clk * 4));
+               info->pll = (P << 16) | (N << 8) | M;
        }
+
+       return ret ? ret : -ERANGE;
+}
+
+static int
+calc_clk(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate,
+        int clk, u32 pll, int idx)
+{
+       int ret = nva3_clock_info(&priv->base, clk, pll, cstate->domain[idx],
+                                 &priv->eng[idx]);
+       if (ret >= 0)
+               return 0;
        return ret;
 }
 
+static void
+prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx)
+{
+       struct nva3_clock_info *info = &priv->eng[idx];
+       const u32 src0 = 0x004120 + (clk * 4);
+       const u32 src1 = 0x004160 + (clk * 4);
+       const u32 ctrl = pll + 0;
+       const u32 coef = pll + 4;
+
+       if (info->pll) {
+               nv_mask(priv, src0, 0x00000101, 0x00000101);
+               nv_wr32(priv, coef, info->pll);
+               nv_mask(priv, ctrl, 0x00000015, 0x00000015);
+               nv_mask(priv, ctrl, 0x00000010, 0x00000000);
+               nv_wait(priv, ctrl, 0x00020000, 0x00020000);
+               nv_mask(priv, ctrl, 0x00000010, 0x00000010);
+               nv_mask(priv, ctrl, 0x00000008, 0x00000000);
+               nv_mask(priv, src1, 0x00000100, 0x00000000);
+               nv_mask(priv, src1, 0x00000001, 0x00000000);
+       } else {
+               nv_mask(priv, src1, 0x003f3141, 0x00000101 | info->clk);
+               nv_mask(priv, ctrl, 0x00000018, 0x00000018);
+               udelay(20);
+               nv_mask(priv, ctrl, 0x00000001, 0x00000000);
+               nv_mask(priv, src0, 0x00000100, 0x00000000);
+               nv_mask(priv, src0, 0x00000001, 0x00000000);
+       }
+}
+
+static void
+prog_clk(struct nva3_clock_priv *priv, int clk, int idx)
+{
+       struct nva3_clock_info *info = &priv->eng[idx];
+       nv_mask(priv, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | info->clk);
+}
+
+static int
+nva3_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate)
+{
+       struct nva3_clock_priv *priv = (void *)clk;
+       int ret;
+
+       if ((ret = calc_clk(priv, cstate, 0x10, 0x4200, nv_clk_src_core)) ||
+           (ret = calc_clk(priv, cstate, 0x11, 0x4220, nv_clk_src_shader)) ||
+           (ret = calc_clk(priv, cstate, 0x20, 0x0000, nv_clk_src_disp)) ||
+           (ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec)))
+               return ret;
+
+       return 0;
+}
+
+static int
+nva3_clock_prog(struct nouveau_clock *clk)
+{
+       struct nva3_clock_priv *priv = (void *)clk;
+       prog_pll(priv, 0x00, 0x004200, nv_clk_src_core);
+       prog_pll(priv, 0x01, 0x004220, nv_clk_src_shader);
+       prog_clk(priv, 0x20, nv_clk_src_disp);
+       prog_clk(priv, 0x21, nv_clk_src_vdec);
+       return 0;
+}
+
+static void
+nva3_clock_tidy(struct nouveau_clock *clk)
+{
+}
+
+static struct nouveau_clocks
+nva3_domain[] = {
+       { nv_clk_src_crystal, 0xff },
+       { nv_clk_src_href   , 0xff },
+       { nv_clk_src_core   , 0x00, 0, "core", 1000 },
+       { nv_clk_src_shader , 0x01, 0, "shader", 1000 },
+       { nv_clk_src_mem    , 0x02, 0, "memory", 1000 },
+       { nv_clk_src_vdec   , 0x03 },
+       { nv_clk_src_disp   , 0x04 },
+       { nv_clk_src_max }
+};
 
 static int
 nva3_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
@@ -58,12 +302,15 @@ nva3_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nva3_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, nva3_domain, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       priv->base.pll_calc = nva3_clock_pll_calc;
+       priv->base.read = nva3_clock_read;
+       priv->base.calc = nva3_clock_calc;
+       priv->base.prog = nva3_clock_prog;
+       priv->base.tidy = nva3_clock_tidy;
        return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h
new file mode 100644 (file)
index 0000000..6229a50
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __NVKM_CLK_NVA3_H__
+#define __NVKM_CLK_NVA3_H__
+
+#include <subdev/clock.h>
+
+struct nva3_clock_info {
+       u32 clk;
+       u32 pll;
+};
+
+int nva3_clock_info(struct nouveau_clock *, int, u32, u32,
+                   struct nva3_clock_info *);
+
+#endif
index 439d81c..c310572 100644 (file)
 #include <subdev/clock.h>
 #include <subdev/bios.h>
 #include <subdev/bios/pll.h>
+#include <subdev/timer.h>
 
 #include "pll.h"
 
+struct nvc0_clock_info {
+       u32 freq;
+       u32 ssel;
+       u32 mdiv;
+       u32 dsrc;
+       u32 ddiv;
+       u32 coef;
+};
+
 struct nvc0_clock_priv {
        struct nouveau_clock base;
+       struct nvc0_clock_info eng[16];
+};
+
+static u32 read_div(struct nvc0_clock_priv *, int, u32, u32);
+
+static u32
+read_vco(struct nvc0_clock_priv *priv, u32 dsrc)
+{
+       struct nouveau_clock *clk = &priv->base;
+       u32 ssrc = nv_rd32(priv, dsrc);
+       if (!(ssrc & 0x00000100))
+               return clk->read(clk, nv_clk_src_sppll0);
+       return clk->read(clk, nv_clk_src_sppll1);
+}
+
+static u32
+read_pll(struct nvc0_clock_priv *priv, u32 pll)
+{
+       struct nouveau_clock *clk = &priv->base;
+       u32 ctrl = nv_rd32(priv, pll + 0x00);
+       u32 coef = nv_rd32(priv, pll + 0x04);
+       u32 P = (coef & 0x003f0000) >> 16;
+       u32 N = (coef & 0x0000ff00) >> 8;
+       u32 M = (coef & 0x000000ff) >> 0;
+       u32 sclk;
+
+       if (!(ctrl & 0x00000001))
+               return 0;
+
+       switch (pll) {
+       case 0x00e800:
+       case 0x00e820:
+               sclk = nv_device(priv)->crystal;
+               P = 1;
+               break;
+       case 0x132000:
+               sclk = clk->read(clk, nv_clk_src_mpllsrc);
+               break;
+       case 0x132020:
+               sclk = clk->read(clk, nv_clk_src_mpllsrcref);
+               break;
+       case 0x137000:
+       case 0x137020:
+       case 0x137040:
+       case 0x1370e0:
+               sclk = read_div(priv, (pll & 0xff) / 0x20, 0x137120, 0x137140);
+               break;
+       default:
+               return 0;
+       }
+
+       return sclk * N / M / P;
+}
+
+static u32
+read_div(struct nvc0_clock_priv *priv, int doff, u32 dsrc, u32 dctl)
+{
+       u32 ssrc = nv_rd32(priv, dsrc + (doff * 4));
+       u32 sctl = nv_rd32(priv, dctl + (doff * 4));
+
+       switch (ssrc & 0x00000003) {
+       case 0:
+               if ((ssrc & 0x00030000) != 0x00030000)
+                       return nv_device(priv)->crystal;
+               return 108000;
+       case 2:
+               return 100000;
+       case 3:
+               if (sctl & 0x80000000) {
+                       u32 sclk = read_vco(priv, dsrc + (doff * 4));
+                       u32 sdiv = (sctl & 0x0000003f) + 2;
+                       return (sclk * 2) / sdiv;
+               }
+
+               return read_vco(priv, dsrc + (doff * 4));
+       default:
+               return 0;
+       }
+}
+
+static u32
+read_clk(struct nvc0_clock_priv *priv, int clk)
+{
+       u32 sctl = nv_rd32(priv, 0x137250 + (clk * 4));
+       u32 ssel = nv_rd32(priv, 0x137100);
+       u32 sclk, sdiv;
+
+       if (ssel & (1 << clk)) {
+               if (clk < 7)
+                       sclk = read_pll(priv, 0x137000 + (clk * 0x20));
+               else
+                       sclk = read_pll(priv, 0x1370e0);
+               sdiv = ((sctl & 0x00003f00) >> 8) + 2;
+       } else {
+               sclk = read_div(priv, clk, 0x137160, 0x1371d0);
+               sdiv = ((sctl & 0x0000003f) >> 0) + 2;
+       }
+
+       if (sctl & 0x80000000)
+               return (sclk * 2) / sdiv;
+
+       return sclk;
+}
+
+static int
+nvc0_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
+{
+       struct nouveau_device *device = nv_device(clk);
+       struct nvc0_clock_priv *priv = (void *)clk;
+
+       switch (src) {
+       case nv_clk_src_crystal:
+               return device->crystal;
+       case nv_clk_src_href:
+               return 100000;
+       case nv_clk_src_sppll0:
+               return read_pll(priv, 0x00e800);
+       case nv_clk_src_sppll1:
+               return read_pll(priv, 0x00e820);
+
+       case nv_clk_src_mpllsrcref:
+               return read_div(priv, 0, 0x137320, 0x137330);
+       case nv_clk_src_mpllsrc:
+               return read_pll(priv, 0x132020);
+       case nv_clk_src_mpll:
+               return read_pll(priv, 0x132000);
+       case nv_clk_src_mdiv:
+               return read_div(priv, 0, 0x137300, 0x137310);
+       case nv_clk_src_mem:
+               if (nv_rd32(priv, 0x1373f0) & 0x00000002)
+                       return clk->read(clk, nv_clk_src_mpll);
+               return clk->read(clk, nv_clk_src_mdiv);
+
+       case nv_clk_src_gpc:
+               return read_clk(priv, 0x00);
+       case nv_clk_src_rop:
+               return read_clk(priv, 0x01);
+       case nv_clk_src_hubk07:
+               return read_clk(priv, 0x02);
+       case nv_clk_src_hubk06:
+               return read_clk(priv, 0x07);
+       case nv_clk_src_hubk01:
+               return read_clk(priv, 0x08);
+       case nv_clk_src_copy:
+               return read_clk(priv, 0x09);
+       case nv_clk_src_daemon:
+               return read_clk(priv, 0x0c);
+       case nv_clk_src_vdec:
+               return read_clk(priv, 0x0e);
+       default:
+               nv_error(clk, "invalid clock source %d\n", src);
+               return -EINVAL;
+       }
+}
+
+static u32
+calc_div(struct nvc0_clock_priv *priv, int clk, u32 ref, u32 freq, u32 *ddiv)
+{
+       u32 div = min((ref * 2) / freq, (u32)65);
+       if (div < 2)
+               div = 2;
+
+       *ddiv = div - 2;
+       return (ref * 2) / div;
+}
+
+static u32
+calc_src(struct nvc0_clock_priv *priv, int clk, u32 freq, u32 *dsrc, u32 *ddiv)
+{
+       u32 sclk;
+
+       /* use one of the fixed frequencies if possible */
+       *ddiv = 0x00000000;
+       switch (freq) {
+       case  27000:
+       case 108000:
+               *dsrc = 0x00000000;
+               if (freq == 108000)
+                       *dsrc |= 0x00030000;
+               return freq;
+       case 100000:
+               *dsrc = 0x00000002;
+               return freq;
+       default:
+               *dsrc = 0x00000003;
+               break;
+       }
+
+       /* otherwise, calculate the closest divider */
+       sclk = read_vco(priv, 0x137160 + (clk * 4));
+       if (clk < 7)
+               sclk = calc_div(priv, clk, sclk, freq, ddiv);
+       return sclk;
+}
+
+static u32
+calc_pll(struct nvc0_clock_priv *priv, int clk, u32 freq, u32 *coef)
+{
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll limits;
+       int N, M, P, ret;
+
+       ret = nvbios_pll_parse(bios, 0x137000 + (clk * 0x20), &limits);
+       if (ret)
+               return 0;
+
+       limits.refclk = read_div(priv, clk, 0x137120, 0x137140);
+       if (!limits.refclk)
+               return 0;
+
+       ret = nva3_pll_calc(nv_subdev(priv), &limits, freq, &N, NULL, &M, &P);
+       if (ret <= 0)
+               return 0;
+
+       *coef = (P << 16) | (N << 8) | M;
+       return ret;
+}
+
+static int
+calc_clk(struct nvc0_clock_priv *priv,
+        struct nouveau_cstate *cstate, int clk, int dom)
+{
+       struct nvc0_clock_info *info = &priv->eng[clk];
+       u32 freq = cstate->domain[dom];
+       u32 src0, div0, div1D, div1P = 0;
+       u32 clk0, clk1 = 0;
+
+       /* invalid clock domain */
+       if (!freq)
+               return 0;
+
+       /* first possible path, using only dividers */
+       clk0 = calc_src(priv, clk, freq, &src0, &div0);
+       clk0 = calc_div(priv, clk, clk0, freq, &div1D);
+
+       /* see if we can get any closer using PLLs */
+       if (clk0 != freq && (0x00004387 & (1 << clk))) {
+               if (clk <= 7)
+                       clk1 = calc_pll(priv, clk, freq, &info->coef);
+               else
+                       clk1 = cstate->domain[nv_clk_src_hubk06];
+               clk1 = calc_div(priv, clk, clk1, freq, &div1P);
+       }
+
+       /* select the method which gets closest to target freq */
+       if (abs((int)freq - clk0) <= abs((int)freq - clk1)) {
+               info->dsrc = src0;
+               if (div0) {
+                       info->ddiv |= 0x80000000;
+                       info->ddiv |= div0 << 8;
+                       info->ddiv |= div0;
+               }
+               if (div1D) {
+                       info->mdiv |= 0x80000000;
+                       info->mdiv |= div1D;
+               }
+               info->ssel = info->coef = 0;
+               info->freq = clk0;
+       } else {
+               if (div1P) {
+                       info->mdiv |= 0x80000000;
+                       info->mdiv |= div1P << 8;
+               }
+               info->ssel = (1 << clk);
+               info->freq = clk1;
+       }
+
+       return 0;
+}
+
+static int
+nvc0_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate)
+{
+       struct nvc0_clock_priv *priv = (void *)clk;
+       int ret;
+
+       if ((ret = calc_clk(priv, cstate, 0x00, nv_clk_src_gpc)) ||
+           (ret = calc_clk(priv, cstate, 0x01, nv_clk_src_rop)) ||
+           (ret = calc_clk(priv, cstate, 0x02, nv_clk_src_hubk07)) ||
+           (ret = calc_clk(priv, cstate, 0x07, nv_clk_src_hubk06)) ||
+           (ret = calc_clk(priv, cstate, 0x08, nv_clk_src_hubk01)) ||
+           (ret = calc_clk(priv, cstate, 0x09, nv_clk_src_copy)) ||
+           (ret = calc_clk(priv, cstate, 0x0c, nv_clk_src_daemon)) ||
+           (ret = calc_clk(priv, cstate, 0x0e, nv_clk_src_vdec)))
+               return ret;
+
+       return 0;
+}
+
+static void
+nvc0_clock_prog_0(struct nvc0_clock_priv *priv, int clk)
+{
+       struct nvc0_clock_info *info = &priv->eng[clk];
+       if (clk < 7 && !info->ssel) {
+               nv_mask(priv, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv);
+               nv_wr32(priv, 0x137160 + (clk * 0x04), info->dsrc);
+       }
+}
+
+static void
+nvc0_clock_prog_1(struct nvc0_clock_priv *priv, int clk)
+{
+       nv_mask(priv, 0x137100, (1 << clk), 0x00000000);
+       nv_wait(priv, 0x137100, (1 << clk), 0x00000000);
+}
+
+static void
+nvc0_clock_prog_2(struct nvc0_clock_priv *priv, int clk)
+{
+       struct nvc0_clock_info *info = &priv->eng[clk];
+       const u32 addr = 0x137000 + (clk * 0x20);
+       if (clk <= 7) {
+               nv_mask(priv, addr + 0x00, 0x00000004, 0x00000000);
+               nv_mask(priv, addr + 0x00, 0x00000001, 0x00000000);
+               if (info->coef) {
+                       nv_wr32(priv, addr + 0x04, info->coef);
+                       nv_mask(priv, addr + 0x00, 0x00000001, 0x00000001);
+                       nv_wait(priv, addr + 0x00, 0x00020000, 0x00020000);
+                       nv_mask(priv, addr + 0x00, 0x00020004, 0x00000004);
+               }
+       }
+}
+
+static void
+nvc0_clock_prog_3(struct nvc0_clock_priv *priv, int clk)
+{
+       struct nvc0_clock_info *info = &priv->eng[clk];
+       if (info->ssel) {
+               nv_mask(priv, 0x137100, (1 << clk), info->ssel);
+               nv_wait(priv, 0x137100, (1 << clk), info->ssel);
+       }
+}
+
+static void
+nvc0_clock_prog_4(struct nvc0_clock_priv *priv, int clk)
+{
+       struct nvc0_clock_info *info = &priv->eng[clk];
+       nv_mask(priv, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv);
+}
+
+static int
+nvc0_clock_prog(struct nouveau_clock *clk)
+{
+       struct nvc0_clock_priv *priv = (void *)clk;
+       struct {
+               void (*exec)(struct nvc0_clock_priv *, int);
+       } stage[] = {
+               { nvc0_clock_prog_0 }, /* div programming */
+               { nvc0_clock_prog_1 }, /* select div mode */
+               { nvc0_clock_prog_2 }, /* (maybe) program pll */
+               { nvc0_clock_prog_3 }, /* (maybe) select pll mode */
+               { nvc0_clock_prog_4 }, /* final divider */
+       };
+       int i, j;
+
+       for (i = 0; i < ARRAY_SIZE(stage); i++) {
+               for (j = 0; j < ARRAY_SIZE(priv->eng); j++) {
+                       if (!priv->eng[j].freq)
+                               continue;
+                       stage[i].exec(priv, j);
+               }
+       }
+
+       return 0;
+}
+
+static void
+nvc0_clock_tidy(struct nouveau_clock *clk)
+{
+       struct nvc0_clock_priv *priv = (void *)clk;
+       memset(priv->eng, 0x00, sizeof(priv->eng));
+}
+
+static struct nouveau_clocks
+nvc0_domain[] = {
+       { nv_clk_src_crystal, 0xff },
+       { nv_clk_src_href   , 0xff },
+       { nv_clk_src_hubk06 , 0x00 },
+       { nv_clk_src_hubk01 , 0x01 },
+       { nv_clk_src_copy   , 0x02 },
+       { nv_clk_src_gpc    , 0x03, 0, "core", 2000 },
+       { nv_clk_src_rop    , 0x04 },
+       { nv_clk_src_mem    , 0x05, 0, "memory", 1000 },
+       { nv_clk_src_vdec   , 0x06 },
+       { nv_clk_src_daemon , 0x0a },
+       { nv_clk_src_hubk07 , 0x0b },
+       { nv_clk_src_max }
 };
 
 static int
@@ -40,12 +437,15 @@ nvc0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nvc0_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, nvc0_domain, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       priv->base.pll_calc = nva3_clock_pll_calc;
+       priv->base.read = nvc0_clock_read;
+       priv->base.calc = nvc0_clock_calc;
+       priv->base.prog = nvc0_clock_prog;
+       priv->base.tidy = nvc0_clock_tidy;
        return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c
new file mode 100644 (file)
index 0000000..4c62e84
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/clock.h>
+#include <subdev/timer.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+#include "pll.h"
+
+struct nve0_clock_info {
+       u32 freq;
+       u32 ssel;
+       u32 mdiv;
+       u32 dsrc;
+       u32 ddiv;
+       u32 coef;
+};
+
+struct nve0_clock_priv {
+       struct nouveau_clock base;
+       struct nve0_clock_info eng[16];
+};
+
+static u32 read_div(struct nve0_clock_priv *, int, u32, u32);
+static u32 read_pll(struct nve0_clock_priv *, u32);
+
+static u32
+read_vco(struct nve0_clock_priv *priv, u32 dsrc)
+{
+       u32 ssrc = nv_rd32(priv, dsrc);
+       if (!(ssrc & 0x00000100))
+               return read_pll(priv, 0x00e800);
+       return read_pll(priv, 0x00e820);
+}
+
+static u32
+read_pll(struct nve0_clock_priv *priv, u32 pll)
+{
+       u32 ctrl = nv_rd32(priv, pll + 0x00);
+       u32 coef = nv_rd32(priv, pll + 0x04);
+       u32 P = (coef & 0x003f0000) >> 16;
+       u32 N = (coef & 0x0000ff00) >> 8;
+       u32 M = (coef & 0x000000ff) >> 0;
+       u32 sclk;
+       u16 fN = 0xf000;
+
+       if (!(ctrl & 0x00000001))
+               return 0;
+
+       switch (pll) {
+       case 0x00e800:
+       case 0x00e820:
+               sclk = nv_device(priv)->crystal;
+               P = 1;
+               break;
+       case 0x132000:
+               sclk = read_pll(priv, 0x132020);
+               P = (coef & 0x10000000) ? 2 : 1;
+               break;
+       case 0x132020:
+               sclk = read_div(priv, 0, 0x137320, 0x137330);
+               fN   = nv_rd32(priv, pll + 0x10) >> 16;
+               break;
+       case 0x137000:
+       case 0x137020:
+       case 0x137040:
+       case 0x1370e0:
+               sclk = read_div(priv, (pll & 0xff) / 0x20, 0x137120, 0x137140);
+               break;
+       default:
+               return 0;
+       }
+
+       if (P == 0)
+               P = 1;
+
+       sclk = (sclk * N) + (((u16)(fN + 4096) * sclk) >> 13);
+       return sclk / (M * P);
+}
+
+static u32
+read_div(struct nve0_clock_priv *priv, int doff, u32 dsrc, u32 dctl)
+{
+       u32 ssrc = nv_rd32(priv, dsrc + (doff * 4));
+       u32 sctl = nv_rd32(priv, dctl + (doff * 4));
+
+       switch (ssrc & 0x00000003) {
+       case 0:
+               if ((ssrc & 0x00030000) != 0x00030000)
+                       return nv_device(priv)->crystal;
+               return 108000;
+       case 2:
+               return 100000;
+       case 3:
+               if (sctl & 0x80000000) {
+                       u32 sclk = read_vco(priv, dsrc + (doff * 4));
+                       u32 sdiv = (sctl & 0x0000003f) + 2;
+                       return (sclk * 2) / sdiv;
+               }
+
+               return read_vco(priv, dsrc + (doff * 4));
+       default:
+               return 0;
+       }
+}
+
+static u32
+read_mem(struct nve0_clock_priv *priv)
+{
+       switch (nv_rd32(priv, 0x1373f4) & 0x0000000f) {
+       case 1: return read_pll(priv, 0x132020);
+       case 2: return read_pll(priv, 0x132000);
+       default:
+               return 0;
+       }
+}
+
+static u32
+read_clk(struct nve0_clock_priv *priv, int clk)
+{
+       u32 sctl = nv_rd32(priv, 0x137250 + (clk * 4));
+       u32 sclk, sdiv;
+
+       if (clk < 7) {
+               u32 ssel = nv_rd32(priv, 0x137100);
+               if (ssel & (1 << clk)) {
+                       sclk = read_pll(priv, 0x137000 + (clk * 0x20));
+                       sdiv = 1;
+               } else {
+                       sclk = read_div(priv, clk, 0x137160, 0x1371d0);
+                       sdiv = 0;
+               }
+       } else {
+               u32 ssrc = nv_rd32(priv, 0x137160 + (clk * 0x04));
+               if ((ssrc & 0x00000003) == 0x00000003) {
+                       sclk = read_div(priv, clk, 0x137160, 0x1371d0);
+                       if (ssrc & 0x00000100) {
+                               if (ssrc & 0x40000000)
+                                       sclk = read_pll(priv, 0x1370e0);
+                               sdiv = 1;
+                       } else {
+                               sdiv = 0;
+                       }
+               } else {
+                       sclk = read_div(priv, clk, 0x137160, 0x1371d0);
+                       sdiv = 0;
+               }
+       }
+
+       if (sctl & 0x80000000) {
+               if (sdiv)
+                       sdiv = ((sctl & 0x00003f00) >> 8) + 2;
+               else
+                       sdiv = ((sctl & 0x0000003f) >> 0) + 2;
+               return (sclk * 2) / sdiv;
+       }
+
+       return sclk;
+}
+
+static int
+nve0_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
+{
+       struct nouveau_device *device = nv_device(clk);
+       struct nve0_clock_priv *priv = (void *)clk;
+
+       switch (src) {
+       case nv_clk_src_crystal:
+               return device->crystal;
+       case nv_clk_src_href:
+               return 100000;
+       case nv_clk_src_mem:
+               return read_mem(priv);
+       case nv_clk_src_gpc:
+               return read_clk(priv, 0x00);
+       case nv_clk_src_rop:
+               return read_clk(priv, 0x01);
+       case nv_clk_src_hubk07:
+               return read_clk(priv, 0x02);
+       case nv_clk_src_hubk06:
+               return read_clk(priv, 0x07);
+       case nv_clk_src_hubk01:
+               return read_clk(priv, 0x08);
+       case nv_clk_src_daemon:
+               return read_clk(priv, 0x0c);
+       case nv_clk_src_vdec:
+               return read_clk(priv, 0x0e);
+       default:
+               nv_error(clk, "invalid clock source %d\n", src);
+               return -EINVAL;
+       }
+}
+
+static u32
+calc_div(struct nve0_clock_priv *priv, int clk, u32 ref, u32 freq, u32 *ddiv)
+{
+       u32 div = min((ref * 2) / freq, (u32)65);
+       if (div < 2)
+               div = 2;
+
+       *ddiv = div - 2;
+       return (ref * 2) / div;
+}
+
+static u32
+calc_src(struct nve0_clock_priv *priv, int clk, u32 freq, u32 *dsrc, u32 *ddiv)
+{
+       u32 sclk;
+
+       /* use one of the fixed frequencies if possible */
+       *ddiv = 0x00000000;
+       switch (freq) {
+       case  27000:
+       case 108000:
+               *dsrc = 0x00000000;
+               if (freq == 108000)
+                       *dsrc |= 0x00030000;
+               return freq;
+       case 100000:
+               *dsrc = 0x00000002;
+               return freq;
+       default:
+               *dsrc = 0x00000003;
+               break;
+       }
+
+       /* otherwise, calculate the closest divider */
+       sclk = read_vco(priv, 0x137160 + (clk * 4));
+       if (clk < 7)
+               sclk = calc_div(priv, clk, sclk, freq, ddiv);
+       return sclk;
+}
+
+static u32
+calc_pll(struct nve0_clock_priv *priv, int clk, u32 freq, u32 *coef)
+{
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll limits;
+       int N, M, P, ret;
+
+       ret = nvbios_pll_parse(bios, 0x137000 + (clk * 0x20), &limits);
+       if (ret)
+               return 0;
+
+       limits.refclk = read_div(priv, clk, 0x137120, 0x137140);
+       if (!limits.refclk)
+               return 0;
+
+       ret = nva3_pll_calc(nv_subdev(priv), &limits, freq, &N, NULL, &M, &P);
+       if (ret <= 0)
+               return 0;
+
+       *coef = (P << 16) | (N << 8) | M;
+       return ret;
+}
+
+static int
+calc_clk(struct nve0_clock_priv *priv,
+        struct nouveau_cstate *cstate, int clk, int dom)
+{
+       struct nve0_clock_info *info = &priv->eng[clk];
+       u32 freq = cstate->domain[dom];
+       u32 src0, div0, div1D, div1P = 0;
+       u32 clk0, clk1 = 0;
+
+       /* invalid clock domain */
+       if (!freq)
+               return 0;
+
+       /* first possible path, using only dividers */
+       clk0 = calc_src(priv, clk, freq, &src0, &div0);
+       clk0 = calc_div(priv, clk, clk0, freq, &div1D);
+
+       /* see if we can get any closer using PLLs */
+       if (clk0 != freq && (0x0000ff87 & (1 << clk))) {
+               if (clk <= 7)
+                       clk1 = calc_pll(priv, clk, freq, &info->coef);
+               else
+                       clk1 = cstate->domain[nv_clk_src_hubk06];
+               clk1 = calc_div(priv, clk, clk1, freq, &div1P);
+       }
+
+       /* select the method which gets closest to target freq */
+       if (abs((int)freq - clk0) <= abs((int)freq - clk1)) {
+               info->dsrc = src0;
+               if (div0) {
+                       info->ddiv |= 0x80000000;
+                       info->ddiv |= div0 << 8;
+                       info->ddiv |= div0;
+               }
+               if (div1D) {
+                       info->mdiv |= 0x80000000;
+                       info->mdiv |= div1D;
+               }
+               info->ssel = 0;
+               info->freq = clk0;
+       } else {
+               if (div1P) {
+                       info->mdiv |= 0x80000000;
+                       info->mdiv |= div1P << 8;
+               }
+               info->ssel = (1 << clk);
+               info->dsrc = 0x40000100;
+               info->freq = clk1;
+       }
+
+       return 0;
+}
+
+static int
+nve0_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate)
+{
+       struct nve0_clock_priv *priv = (void *)clk;
+       int ret;
+
+       if ((ret = calc_clk(priv, cstate, 0x00, nv_clk_src_gpc)) ||
+           (ret = calc_clk(priv, cstate, 0x01, nv_clk_src_rop)) ||
+           (ret = calc_clk(priv, cstate, 0x02, nv_clk_src_hubk07)) ||
+           (ret = calc_clk(priv, cstate, 0x07, nv_clk_src_hubk06)) ||
+           (ret = calc_clk(priv, cstate, 0x08, nv_clk_src_hubk01)) ||
+           (ret = calc_clk(priv, cstate, 0x0c, nv_clk_src_daemon)) ||
+           (ret = calc_clk(priv, cstate, 0x0e, nv_clk_src_vdec)))
+               return ret;
+
+       return 0;
+}
+
+static void
+nve0_clock_prog_0(struct nve0_clock_priv *priv, int clk)
+{
+       struct nve0_clock_info *info = &priv->eng[clk];
+       if (!info->ssel) {
+               nv_mask(priv, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv);
+               nv_wr32(priv, 0x137160 + (clk * 0x04), info->dsrc);
+       }
+}
+
+static void
+nve0_clock_prog_1_0(struct nve0_clock_priv *priv, int clk)
+{
+       nv_mask(priv, 0x137100, (1 << clk), 0x00000000);
+       nv_wait(priv, 0x137100, (1 << clk), 0x00000000);
+}
+
+static void
+nve0_clock_prog_1_1(struct nve0_clock_priv *priv, int clk)
+{
+       nv_mask(priv, 0x137160 + (clk * 0x04), 0x00000100, 0x00000000);
+}
+
+static void
+nve0_clock_prog_2(struct nve0_clock_priv *priv, int clk)
+{
+       struct nve0_clock_info *info = &priv->eng[clk];
+       const u32 addr = 0x137000 + (clk * 0x20);
+       nv_mask(priv, addr + 0x00, 0x00000004, 0x00000000);
+       nv_mask(priv, addr + 0x00, 0x00000001, 0x00000000);
+       if (info->coef) {
+               nv_wr32(priv, addr + 0x04, info->coef);
+               nv_mask(priv, addr + 0x00, 0x00000001, 0x00000001);
+               nv_wait(priv, addr + 0x00, 0x00020000, 0x00020000);
+               nv_mask(priv, addr + 0x00, 0x00020004, 0x00000004);
+       }
+}
+
+static void
+nve0_clock_prog_3(struct nve0_clock_priv *priv, int clk)
+{
+       struct nve0_clock_info *info = &priv->eng[clk];
+       nv_mask(priv, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv);
+}
+
+static void
+nve0_clock_prog_4_0(struct nve0_clock_priv *priv, int clk)
+{
+       struct nve0_clock_info *info = &priv->eng[clk];
+       if (info->ssel) {
+               nv_mask(priv, 0x137100, (1 << clk), info->ssel);
+               nv_wait(priv, 0x137100, (1 << clk), info->ssel);
+       }
+}
+
+static void
+nve0_clock_prog_4_1(struct nve0_clock_priv *priv, int clk)
+{
+       struct nve0_clock_info *info = &priv->eng[clk];
+       if (info->ssel) {
+               nv_mask(priv, 0x137160 + (clk * 0x04), 0x40000000, 0x40000000);
+               nv_mask(priv, 0x137160 + (clk * 0x04), 0x00000100, 0x00000100);
+       }
+}
+
+static int
+nve0_clock_prog(struct nouveau_clock *clk)
+{
+       struct nve0_clock_priv *priv = (void *)clk;
+       struct {
+               u32 mask;
+               void (*exec)(struct nve0_clock_priv *, int);
+       } stage[] = {
+               { 0x007f, nve0_clock_prog_0   }, /* div programming */
+               { 0x007f, nve0_clock_prog_1_0 }, /* select div mode */
+               { 0xff80, nve0_clock_prog_1_1 },
+               { 0x00ff, nve0_clock_prog_2   }, /* (maybe) program pll */
+               { 0xff80, nve0_clock_prog_3   }, /* final divider */
+               { 0x007f, nve0_clock_prog_4_0 }, /* (maybe) select pll mode */
+               { 0xff80, nve0_clock_prog_4_1 },
+       };
+       int i, j;
+
+       for (i = 0; i < ARRAY_SIZE(stage); i++) {
+               for (j = 0; j < ARRAY_SIZE(priv->eng); j++) {
+                       if (!(stage[i].mask & (1 << j)))
+                               continue;
+                       if (!priv->eng[j].freq)
+                               continue;
+                       stage[i].exec(priv, j);
+               }
+       }
+
+       return 0;
+}
+
+static void
+nve0_clock_tidy(struct nouveau_clock *clk)
+{
+       struct nve0_clock_priv *priv = (void *)clk;
+       memset(priv->eng, 0x00, sizeof(priv->eng));
+}
+
+static struct nouveau_clocks
+nve0_domain[] = {
+       { nv_clk_src_crystal, 0xff },
+       { nv_clk_src_href   , 0xff },
+       { nv_clk_src_gpc    , 0x00, NVKM_CLK_DOM_FLAG_CORE, "core", 2000 },
+       { nv_clk_src_hubk07 , 0x01, NVKM_CLK_DOM_FLAG_CORE },
+       { nv_clk_src_rop    , 0x02, NVKM_CLK_DOM_FLAG_CORE },
+       { nv_clk_src_mem    , 0x03, 0, "memory", 1000 },
+       { nv_clk_src_hubk06 , 0x04, NVKM_CLK_DOM_FLAG_CORE },
+       { nv_clk_src_hubk01 , 0x05 },
+       { nv_clk_src_vdec   , 0x06 },
+       { nv_clk_src_daemon , 0x07 },
+       { nv_clk_src_max }
+};
+
+static int
+nve0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+               struct nouveau_oclass *oclass, void *data, u32 size,
+               struct nouveau_object **pobject)
+{
+       struct nve0_clock_priv *priv;
+       int ret;
+
+       ret = nouveau_clock_create(parent, engine, oclass, nve0_domain, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.read = nve0_clock_read;
+       priv->base.calc = nve0_clock_calc;
+       priv->base.prog = nve0_clock_prog;
+       priv->base.tidy = nve0_clock_tidy;
+       return 0;
+}
+
+struct nouveau_oclass
+nve0_clock_oclass = {
+       .handle = NV_SUBDEV(CLOCK, 0xe0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nve0_clock_ctor,
+               .dtor = _nouveau_clock_dtor,
+               .init = _nouveau_clock_init,
+               .fini = _nouveau_clock_fini,
+       },
+};
index cf1ed0d..b47d543 100644 (file)
@@ -38,7 +38,7 @@ getMNP_single(struct nouveau_subdev *subdev, struct nvbios_pll *info, int clk,
         * "clk" parameter in kHz
         * returns calculated clock
         */
-       int cv = nouveau_bios(subdev)->version.chip;
+       struct nouveau_bios *bios = nouveau_bios(subdev);
        int minvco = info->vco1.min_freq, maxvco = info->vco1.max_freq;
        int minM = info->vco1.min_m, maxM = info->vco1.max_m;
        int minN = info->vco1.min_n, maxN = info->vco1.max_n;
@@ -54,18 +54,21 @@ getMNP_single(struct nouveau_subdev *subdev, struct nvbios_pll *info, int clk,
 
        /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
        /* possibly correlated with introduction of 27MHz crystal */
-       if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
-               if (clk > 250000)
-                       maxM = 6;
-               if (clk > 340000)
-                       maxM = 2;
-       } else if (cv < 0x40) {
-               if (clk > 150000)
-                       maxM = 6;
-               if (clk > 200000)
-                       maxM = 4;
-               if (clk > 340000)
-                       maxM = 2;
+       if (bios->version.major < 0x60) {
+               int cv = bios->version.chip;
+               if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
+                       if (clk > 250000)
+                               maxM = 6;
+                       if (clk > 340000)
+                               maxM = 2;
+               } else if (cv < 0x40) {
+                       if (clk > 150000)
+                               maxM = 6;
+                       if (clk > 200000)
+                               maxM = 4;
+                       if (clk > 340000)
+                               maxM = 2;
+               }
        }
 
        P = 1 << maxP;
@@ -227,10 +230,12 @@ nv04_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info, u32 freq,
 {
        int ret;
 
-       if (!info->vco2.max_freq) {
+       if (!info->vco2.max_freq || !N2) {
                ret = getMNP_single(subdev, info, freq, N1, M1, P);
-               *N2 = 1;
-               *M2 = 1;
+               if (N2) {
+                       *N2 = 1;
+                       *M2 = 1;
+               }
        } else {
                ret = getMNP_double(subdev, info, freq, N1, M1, N2, M2, P);
        }
index 2fe1f71..8eca457 100644 (file)
@@ -45,6 +45,7 @@ nva3_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info,
        lM = max(lM, (int)info->vco1.min_m);
        hM = (info->refclk + info->vco1.min_inputfreq) / info->vco1.min_inputfreq;
        hM = min(hM, (int)info->vco1.max_m);
+       lM = min(lM, hM);
 
        for (M = lM; M <= hM; M++) {
                u32 tmp = freq * *P * M;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/seq.h b/drivers/gpu/drm/nouveau/core/subdev/clock/seq.h
new file mode 100644 (file)
index 0000000..fb33f06
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __NVKM_CLK_SEQ_H__
+#define __NVKM_CLK_SEQ_H__
+
+#include <subdev/bus.h>
+#include <subdev/bus/hwsq.h>
+
+#define clk_init(s,p)       hwsq_init(&(s)->base, (p))
+#define clk_exec(s,e)       hwsq_exec(&(s)->base, (e))
+#define clk_have(s,r)       ((s)->r_##r.addr != 0x000000)
+#define clk_rd32(s,r)       hwsq_rd32(&(s)->base, &(s)->r_##r)
+#define clk_wr32(s,r,d)     hwsq_wr32(&(s)->base, &(s)->r_##r, (d))
+#define clk_mask(s,r,m,d)   hwsq_mask(&(s)->base, &(s)->r_##r, (m), (d))
+#define clk_setf(s,f,d)     hwsq_setf(&(s)->base, (f), (d))
+#define clk_wait(s,f,d)     hwsq_wait(&(s)->base, (f), (d))
+#define clk_nsec(s,n)       hwsq_nsec(&(s)->base, (n))
+
+#endif
index b22357d..27c8235 100644 (file)
@@ -168,7 +168,8 @@ setPLL_single(struct nouveau_devinit *devinit, u32 reg,
                /* downclock -- write new NM first */
                nv_wr32(devinit, reg, (oldpll & 0xffff0000) | pv->NM1);
 
-       if (chip_version < 0x17 && chip_version != 0x11)
+       if ((chip_version < 0x17 || chip_version == 0x1a) &&
+           chip_version != 0x11)
                /* wait a bit on older chips */
                msleep(64);
        nv_rd32(devinit, reg);
index 463b08f..8d274db 100644 (file)
@@ -38,12 +38,18 @@ static void
 nv10_devinit_meminit(struct nouveau_devinit *devinit)
 {
        struct nv10_devinit_priv *priv = (void *)devinit;
-       const int mem_width[] = { 0x10, 0x00, 0x20 };
-       const int mem_width_count = nv_device(priv)->chipset >= 0x17 ? 3 : 2;
+       static const int mem_width[] = { 0x10, 0x00, 0x20 };
+       int mem_width_count;
        uint32_t patt = 0xdeadbeef;
        struct io_mapping *fb;
        int i, j, k;
 
+       if (nv_device(priv)->card_type >= NV_11 &&
+           nv_device(priv)->chipset >= 0x17)
+               mem_width_count = 3;
+       else
+               mem_width_count = 2;
+
        /* Map the framebuffer aperture */
        fb = fbmem_init(nv_device(priv)->pdev);
        if (!fb) {
index 821cd75..f009d8a 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include "subdev/fb.h"
-#include "subdev/bios.h"
-#include "subdev/bios/bit.h"
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+
+#include "priv.h"
 
 int
 nouveau_fb_bios_memtype(struct nouveau_bios *bios)
@@ -106,9 +107,9 @@ _nouveau_fb_dtor(struct nouveau_object *object)
 
 int
 nouveau_fb_create_(struct nouveau_object *parent, struct nouveau_object *engine,
-                  struct nouveau_oclass *oclass, struct nouveau_oclass *ramcls,
-                  int length, void **pobject)
+                  struct nouveau_oclass *oclass, int length, void **pobject)
 {
+       struct nouveau_fb_impl *impl = (void *)oclass;
        static const char *name[] = {
                [NV_MEM_TYPE_UNKNOWN] = "unknown",
                [NV_MEM_TYPE_STOLEN ] = "stolen system memory",
@@ -132,8 +133,10 @@ nouveau_fb_create_(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       pfb->memtype_valid = impl->memtype;
+
        ret = nouveau_object_ctor(nv_object(pfb), nv_object(pfb),
-                                 ramcls, NULL, 0, &ram);
+                                 impl->ram, NULL, 0, &ram);
        if (ret) {
                nv_fatal(pfb, "error detecting memory configuration!!\n");
                return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c b/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c
new file mode 100644 (file)
index 0000000..34f9605
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <subdev/bios.h>
+#include "priv.h"
+
+int
+nouveau_gddr5_calc(struct nouveau_ram *ram)
+{
+       struct nouveau_bios *bios = nouveau_bios(ram);
+       int pd, lf, xd, vh, vr, vo;
+       int WL, CL, WR, at, dt, ds;
+       int rq = ram->freq < 1000000; /* XXX */
+
+       switch (!!ram->ramcfg.data * ram->ramcfg.version) {
+       case 0x11:
+               pd =  (nv_ro08(bios, ram->ramcfg.data + 0x01) & 0x80) >> 7;
+               lf =  (nv_ro08(bios, ram->ramcfg.data + 0x01) & 0x40) >> 6;
+               xd = !(nv_ro08(bios, ram->ramcfg.data + 0x01) & 0x20);
+               vh =  (nv_ro08(bios, ram->ramcfg.data + 0x02) & 0x10) >> 4;
+               vr =  (nv_ro08(bios, ram->ramcfg.data + 0x02) & 0x04) >> 2;
+               vo =   nv_ro08(bios, ram->ramcfg.data + 0x06) & 0xff;
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       switch (!!ram->timing.data * ram->timing.version) {
+       case 0x20:
+               WL = (nv_ro16(bios, ram->timing.data + 0x04) & 0x0f80) >> 7;
+               CL =  nv_ro08(bios, ram->timing.data + 0x04) & 0x1f;
+               WR =  nv_ro08(bios, ram->timing.data + 0x0a) & 0x7f;
+               at = (nv_ro08(bios, ram->timing.data + 0x2e) & 0xc0) >> 6;
+               dt =  nv_ro08(bios, ram->timing.data + 0x2e) & 0x03;
+               ds =  nv_ro08(bios, ram->timing.data + 0x2f) & 0x03;
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       if (WL < 1 || WL > 7 || CL < 5 || CL > 36 || WR < 4 || WR > 35)
+               return -EINVAL;
+       CL -= 5;
+       WR -= 4;
+
+       ram->mr[0] &= ~0xf7f;
+       ram->mr[0] |= (WR & 0x0f) << 8;
+       ram->mr[0] |= (CL & 0x0f) << 3;
+       ram->mr[0] |= (WL & 0x07) << 0;
+
+       ram->mr[1] &= ~0x0bf;
+       ram->mr[1] |= (xd & 0x01) << 7;
+       ram->mr[1] |= (at & 0x03) << 4;
+       ram->mr[1] |= (dt & 0x03) << 2;
+       ram->mr[1] |= (ds & 0x03) << 0;
+
+       ram->mr[3] &= ~0x020;
+       ram->mr[3] |= (rq & 0x01) << 5;
+
+       if (!vo)
+               vo = (ram->mr[6] & 0xff0) >> 4;
+       if (ram->mr[6] & 0x001)
+               pd = 1; /* binary driver does this.. bug? */
+       ram->mr[6] &= ~0xff1;
+       ram->mr[6] |= (vo & 0xff) << 4;
+       ram->mr[6] |= (pd & 0x01) << 0;
+
+       if (!(ram->mr[7] & 0x100))
+               vr = 0; /* binary driver does this.. bug? */
+       ram->mr[7] &= ~0x188;
+       ram->mr[7] |= (vr & 0x01) << 8;
+       ram->mr[7] |= (vh & 0x01) << 7;
+       ram->mr[7] |= (lf & 0x01) << 3;
+       return 0;
+}
index 1f103c7..8309fe3 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include "priv.h"
+#include "nv04.h"
 
 #define NV04_PFB_CFG0                                          0x00100200
 
-struct nv04_fb_priv {
-       struct nouveau_fb base;
-};
-
 bool
 nv04_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags)
 {
@@ -57,30 +53,37 @@ nv04_fb_init(struct nouveau_object *object)
        return 0;
 }
 
-static int
+int
 nv04_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
             struct nouveau_oclass *oclass, void *data, u32 size,
             struct nouveau_object **pobject)
 {
+       struct nv04_fb_impl *impl = (void *)oclass;
        struct nv04_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &nv04_ram_oclass, &priv);
+       ret = nouveau_fb_create(parent, engine, oclass, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
+       priv->base.tile.regions = impl->tile.regions;
+       priv->base.tile.init = impl->tile.init;
+       priv->base.tile.comp = impl->tile.comp;
+       priv->base.tile.fini = impl->tile.fini;
+       priv->base.tile.prog = impl->tile.prog;
        return 0;
 }
 
-struct nouveau_oclass
-nv04_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x04),
-       .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nv04_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x04),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv04_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv04_ram_oclass,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.h b/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.h
new file mode 100644 (file)
index 0000000..06ce71f
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __NVKM_FB_NV04_H__
+#define __NVKM_FB_NV04_H__
+
+#include "priv.h"
+
+struct nv04_fb_priv {
+       struct nouveau_fb base;
+};
+
+int  nv04_fb_ctor(struct nouveau_object *, struct nouveau_object *,
+                 struct nouveau_oclass *, void *, u32,
+                 struct nouveau_object **);
+
+struct nv04_fb_impl {
+       struct nouveau_fb_impl base;
+       struct {
+               int regions;
+               void (*init)(struct nouveau_fb *, int i, u32 addr, u32 size,
+                            u32 pitch, u32 flags, struct nouveau_fb_tile *);
+               void (*comp)(struct nouveau_fb *, int i, u32 size, u32 flags,
+                            struct nouveau_fb_tile *);
+               void (*fini)(struct nouveau_fb *, int i,
+                            struct nouveau_fb_tile *);
+               void (*prog)(struct nouveau_fb *, int i,
+                            struct nouveau_fb_tile *);
+       } tile;
+};
+
+void nv10_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size,
+                      u32 pitch, u32 flags, struct nouveau_fb_tile *);
+void nv10_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *);
+void nv10_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
+
+void nv20_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size,
+                      u32 pitch, u32 flags, struct nouveau_fb_tile *);
+void nv20_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *);
+void nv20_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
+
+int  nv30_fb_init(struct nouveau_object *);
+void nv30_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size,
+                      u32 pitch, u32 flags, struct nouveau_fb_tile *);
+
+void nv40_fb_tile_comp(struct nouveau_fb *, int i, u32 size, u32 flags,
+                      struct nouveau_fb_tile *);
+
+int  nv41_fb_init(struct nouveau_object *);
+void nv41_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
+
+int  nv44_fb_init(struct nouveau_object *);
+void nv44_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
+
+void nv46_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size,
+                      u32 pitch, u32 flags, struct nouveau_fb_tile *);
+
+#endif
index be069b5..ffb7ec6 100644 (file)
  *
  */
 
-#include "priv.h"
-
-struct nv10_fb_priv {
-       struct nouveau_fb base;
-};
+#include "nv04.h"
 
 void
 nv10_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
@@ -57,34 +53,19 @@ nv10_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
        nv_rd32(pfb, 0x100240 + (i * 0x10));
 }
 
-static int
-nv10_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv10_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv10_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 8;
-       priv->base.tile.init = nv10_fb_tile_init;
-       priv->base.tile.fini = nv10_fb_tile_fini;
-       priv->base.tile.prog = nv10_fb_tile_prog;
-       return 0;
-}
-
-struct nouveau_oclass
-nv10_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x10),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv10_fb_ctor,
+struct nouveau_oclass *
+nv10_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x10),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = _nouveau_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv10_ram_oclass,
+       .tile.regions = 8,
+       .tile.init = nv10_fb_tile_init,
+       .tile.fini = nv10_fb_tile_fini,
+       .tile.prog = nv10_fb_tile_prog,
+}.base.base;
index 57a2af0..9159a5c 100644 (file)
  *
  */
 
-#include "priv.h"
+#include "nv04.h"
 
-struct nv1a_fb_priv {
-       struct nouveau_fb base;
-};
-
-static int
-nv1a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv1a_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv1a_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 8;
-       priv->base.tile.init = nv10_fb_tile_init;
-       priv->base.tile.fini = nv10_fb_tile_fini;
-       priv->base.tile.prog = nv10_fb_tile_prog;
-       return 0;
-}
-
-struct nouveau_oclass
-nv1a_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x1a),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv1a_fb_ctor,
+struct nouveau_oclass *
+nv1a_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x1a),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = _nouveau_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv10_ram_oclass,
+       .tile.regions = 8,
+       .tile.init = nv10_fb_tile_init,
+       .tile.fini = nv10_fb_tile_fini,
+       .tile.prog = nv10_fb_tile_prog,
+}.base.base;
index b18c4e6..f003c1b 100644 (file)
  *
  */
 
-#include "priv.h"
-
-struct nv20_fb_priv {
-       struct nouveau_fb base;
-};
+#include "nv04.h"
 
 void
 nv20_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
@@ -80,35 +76,20 @@ nv20_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
        nv_wr32(pfb, 0x100300 + (i * 0x04), tile->zcomp);
 }
 
-static int
-nv20_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv20_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv20_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 8;
-       priv->base.tile.init = nv20_fb_tile_init;
-       priv->base.tile.comp = nv20_fb_tile_comp;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv20_fb_tile_prog;
-       return 0;
-}
-
-struct nouveau_oclass
-nv20_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x20),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv20_fb_ctor,
+struct nouveau_oclass *
+nv20_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x20),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = _nouveau_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv20_ram_oclass,
+       .tile.regions = 8,
+       .tile.init = nv20_fb_tile_init,
+       .tile.comp = nv20_fb_tile_comp,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv20_fb_tile_prog,
+}.base.base;
index 32ccabf..f34f422 100644 (file)
  *
  */
 
-#include "priv.h"
-
-struct nv25_fb_priv {
-       struct nouveau_fb base;
-};
+#include "nv04.h"
 
 static void
 nv25_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
@@ -46,35 +42,20 @@ nv25_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
        }
 }
 
-static int
-nv25_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv25_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv20_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 8;
-       priv->base.tile.init = nv20_fb_tile_init;
-       priv->base.tile.comp = nv25_fb_tile_comp;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv20_fb_tile_prog;
-       return 0;
-}
-
-struct nouveau_oclass
-nv25_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x25),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv25_fb_ctor,
+struct nouveau_oclass *
+nv25_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x25),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = _nouveau_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv20_ram_oclass,
+       .tile.regions = 8,
+       .tile.init = nv20_fb_tile_init,
+       .tile.comp = nv25_fb_tile_comp,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv20_fb_tile_prog,
+}.base.base;
index bef756d..69093f7 100644 (file)
  *
  */
 
-#include "priv.h"
-
-struct nv30_fb_priv {
-       struct nouveau_fb base;
-};
+#include "nv04.h"
 
 void
 nv30_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
@@ -67,7 +63,7 @@ nv30_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
 }
 
 static int
-calc_bias(struct nv30_fb_priv *priv, int k, int i, int j)
+calc_bias(struct nv04_fb_priv *priv, int k, int i, int j)
 {
        struct nouveau_device *device = nv_device(priv);
        int b = (device->chipset > 0x30 ?
@@ -78,7 +74,7 @@ calc_bias(struct nv30_fb_priv *priv, int k, int i, int j)
 }
 
 static int
-calc_ref(struct nv30_fb_priv *priv, int l, int k, int i)
+calc_ref(struct nv04_fb_priv *priv, int l, int k, int i)
 {
        int j, x = 0;
 
@@ -95,7 +91,7 @@ int
 nv30_fb_init(struct nouveau_object *object)
 {
        struct nouveau_device *device = nv_device(object);
-       struct nv30_fb_priv *priv = (void *)object;
+       struct nv04_fb_priv *priv = (void *)object;
        int ret, i, j;
 
        ret = nouveau_fb_init(&priv->base);
@@ -124,35 +120,20 @@ nv30_fb_init(struct nouveau_object *object)
        return 0;
 }
 
-static int
-nv30_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv30_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv20_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 8;
-       priv->base.tile.init = nv30_fb_tile_init;
-       priv->base.tile.comp = nv30_fb_tile_comp;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv20_fb_tile_prog;
-       return 0;
-}
-
-struct nouveau_oclass
-nv30_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x30),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv30_fb_ctor,
+struct nouveau_oclass *
+nv30_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x30),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv30_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv20_ram_oclass,
+       .tile.regions = 8,
+       .tile.init = nv30_fb_tile_init,
+       .tile.comp = nv30_fb_tile_comp,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv20_fb_tile_prog,
+}.base.base;
index 097d8e3..161b06e 100644 (file)
  *
  */
 
-#include "priv.h"
-
-struct nv35_fb_priv {
-       struct nouveau_fb base;
-};
+#include "nv04.h"
 
 static void
 nv35_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
@@ -47,35 +43,20 @@ nv35_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
        }
 }
 
-static int
-nv35_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv35_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv20_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 8;
-       priv->base.tile.init = nv30_fb_tile_init;
-       priv->base.tile.comp = nv35_fb_tile_comp;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv20_fb_tile_prog;
-       return 0;
-}
-
-struct nouveau_oclass
-nv35_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x35),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv35_fb_ctor,
+struct nouveau_oclass *
+nv35_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x35),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv30_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv20_ram_oclass,
+       .tile.regions = 8,
+       .tile.init = nv30_fb_tile_init,
+       .tile.comp = nv35_fb_tile_comp,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv20_fb_tile_prog,
+}.base.base;
index 9d6d9df..2dd3d0a 100644 (file)
  *
  */
 
-#include "priv.h"
-
-struct nv36_fb_priv {
-       struct nouveau_fb base;
-};
+#include "nv04.h"
 
 static void
 nv36_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
@@ -47,35 +43,20 @@ nv36_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
        }
 }
 
-static int
-nv36_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv36_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv20_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 8;
-       priv->base.tile.init = nv30_fb_tile_init;
-       priv->base.tile.comp = nv36_fb_tile_comp;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv20_fb_tile_prog;
-       return 0;
-}
-
-struct nouveau_oclass
-nv36_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x36),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv36_fb_ctor,
+struct nouveau_oclass *
+nv36_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x36),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv30_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv20_ram_oclass,
+       .tile.regions = 8,
+       .tile.init = nv30_fb_tile_init,
+       .tile.comp = nv36_fb_tile_comp,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv20_fb_tile_prog,
+}.base.base;
index 33b4393..95a115a 100644 (file)
  *
  */
 
-#include "priv.h"
-
-struct nv40_fb_priv {
-       struct nouveau_fb base;
-};
+#include "nv04.h"
 
 void
 nv40_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
@@ -50,7 +46,7 @@ nv40_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
 static int
 nv40_fb_init(struct nouveau_object *object)
 {
-       struct nv40_fb_priv *priv = (void *)object;
+       struct nv04_fb_priv *priv = (void *)object;
        int ret;
 
        ret = nouveau_fb_init(&priv->base);
@@ -61,36 +57,20 @@ nv40_fb_init(struct nouveau_object *object)
        return 0;
 }
 
-static int
-nv40_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv40_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv40_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 8;
-       priv->base.tile.init = nv30_fb_tile_init;
-       priv->base.tile.comp = nv40_fb_tile_comp;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv20_fb_tile_prog;
-       return 0;
-}
-
-
-struct nouveau_oclass
-nv40_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x40),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv40_fb_ctor,
+struct nouveau_oclass *
+nv40_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x40),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv40_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv40_ram_oclass,
+       .tile.regions = 8,
+       .tile.init = nv30_fb_tile_init,
+       .tile.comp = nv40_fb_tile_comp,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv20_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.h b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.h
new file mode 100644 (file)
index 0000000..581f808
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __NVKM_FB_NV40_H__
+#define __NVKM_FB_NV40_H__
+
+#include "priv.h"
+
+struct nv40_ram {
+       struct nouveau_ram base;
+       u32 ctrl;
+       u32 coef;
+};
+
+
+int  nv40_ram_calc(struct nouveau_fb *, u32);
+int  nv40_ram_prog(struct nouveau_fb *);
+void nv40_ram_tidy(struct nouveau_fb *);
+
+#endif
index 02cd837..b239a86 100644 (file)
  *
  */
 
-#include "priv.h"
-
-struct nv41_fb_priv {
-       struct nouveau_fb base;
-};
+#include "nv04.h"
 
 void
 nv41_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
@@ -43,7 +39,7 @@ nv41_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
 int
 nv41_fb_init(struct nouveau_object *object)
 {
-       struct nv41_fb_priv *priv = (void *)object;
+       struct nv04_fb_priv *priv = (void *)object;
        int ret;
 
        ret = nouveau_fb_init(&priv->base);
@@ -54,36 +50,20 @@ nv41_fb_init(struct nouveau_object *object)
        return 0;
 }
 
-static int
-nv41_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv41_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv41_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 12;
-       priv->base.tile.init = nv30_fb_tile_init;
-       priv->base.tile.comp = nv40_fb_tile_comp;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv41_fb_tile_prog;
-       return 0;
-}
-
-
-struct nouveau_oclass
-nv41_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x41),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv41_fb_ctor,
+struct nouveau_oclass *
+nv41_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x41),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv41_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv41_ram_oclass,
+       .tile.regions = 12,
+       .tile.init = nv30_fb_tile_init,
+       .tile.comp = nv40_fb_tile_comp,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv41_fb_tile_prog,
+}.base.base;
index c5246c2..d847820 100644 (file)
  *
  */
 
-#include "priv.h"
-
-struct nv44_fb_priv {
-       struct nouveau_fb base;
-};
+#include "nv04.h"
 
 static void
 nv44_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
@@ -52,7 +48,7 @@ nv44_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
 int
 nv44_fb_init(struct nouveau_object *object)
 {
-       struct nv44_fb_priv *priv = (void *)object;
+       struct nv04_fb_priv *priv = (void *)object;
        int ret;
 
        ret = nouveau_fb_init(&priv->base);
@@ -64,35 +60,19 @@ nv44_fb_init(struct nouveau_object *object)
        return 0;
 }
 
-static int
-nv44_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv44_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv44_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 12;
-       priv->base.tile.init = nv44_fb_tile_init;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv44_fb_tile_prog;
-       return 0;
-}
-
-
-struct nouveau_oclass
-nv44_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x44),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv44_fb_ctor,
+struct nouveau_oclass *
+nv44_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x44),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv44_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv44_ram_oclass,
+       .tile.regions = 12,
+       .tile.init = nv44_fb_tile_init,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv44_fb_tile_prog,
+}.base.base;
index e2b5790..a5b7751 100644 (file)
  *
  */
 
-#include "priv.h"
-
-struct nv46_fb_priv {
-       struct nouveau_fb base;
-};
+#include "nv04.h"
 
 void
 nv46_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
@@ -44,35 +40,19 @@ nv46_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
        tile->pitch = pitch;
 }
 
-static int
-nv46_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv46_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv44_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 15;
-       priv->base.tile.init = nv46_fb_tile_init;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv44_fb_tile_prog;
-       return 0;
-}
-
-
-struct nouveau_oclass
-nv46_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x46),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv46_fb_ctor,
+struct nouveau_oclass *
+nv46_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x46),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv44_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv44_ram_oclass,
+       .tile.regions = 15,
+       .tile.init = nv46_fb_tile_init,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv44_fb_tile_prog,
+}.base.base;
index fe6a227..3bea142 100644 (file)
  *
  */
 
-#include "priv.h"
+#include "nv04.h"
 
-struct nv47_fb_priv {
-       struct nouveau_fb base;
-};
-
-static int
-nv47_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv47_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv41_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 15;
-       priv->base.tile.init = nv30_fb_tile_init;
-       priv->base.tile.comp = nv40_fb_tile_comp;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv41_fb_tile_prog;
-       return 0;
-}
-
-
-struct nouveau_oclass
-nv47_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x47),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv47_fb_ctor,
+struct nouveau_oclass *
+nv47_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x47),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv41_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv41_ram_oclass,
+       .tile.regions = 15,
+       .tile.init = nv30_fb_tile_init,
+       .tile.comp = nv40_fb_tile_comp,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv41_fb_tile_prog,
+}.base.base;
index 5eca99b..666cbd5 100644 (file)
  *
  */
 
-#include "priv.h"
+#include "nv04.h"
 
-struct nv49_fb_priv {
-       struct nouveau_fb base;
-};
-
-static int
-nv49_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv49_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv49_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 15;
-       priv->base.tile.init = nv30_fb_tile_init;
-       priv->base.tile.comp = nv40_fb_tile_comp;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv41_fb_tile_prog;
-       return 0;
-}
-
-
-struct nouveau_oclass
-nv49_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x49),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv49_fb_ctor,
+struct nouveau_oclass *
+nv49_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x49),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv41_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv49_ram_oclass,
+       .tile.regions = 15,
+       .tile.init = nv30_fb_tile_init,
+       .tile.comp = nv40_fb_tile_comp,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv41_fb_tile_prog,
+}.base.base;
index 1190b78..42e64f3 100644 (file)
  *
  */
 
-#include "priv.h"
+#include "nv04.h"
 
-struct nv4e_fb_priv {
-       struct nouveau_fb base;
-};
-
-static int
-nv4e_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv4e_fb_priv *priv;
-       int ret;
-
-       ret = nouveau_fb_create(parent, engine, oclass, &nv4e_ram_oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.tile.regions = 12;
-       priv->base.tile.init = nv46_fb_tile_init;
-       priv->base.tile.fini = nv20_fb_tile_fini;
-       priv->base.tile.prog = nv44_fb_tile_prog;
-       return 0;
-}
-
-struct nouveau_oclass
-nv4e_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x4e),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv4e_fb_ctor,
+struct nouveau_oclass *
+nv4e_fb_oclass = &(struct nv04_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x4e),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_fb_ctor,
                .dtor = _nouveau_fb_dtor,
                .init = nv44_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv04_fb_memtype_valid,
+       .base.ram = &nv4e_ram_oclass,
+       .tile.regions = 12,
+       .tile.init = nv46_fb_tile_init,
+       .tile.fini = nv20_fb_tile_fini,
+       .tile.prog = nv44_fb_tile_prog,
+}.base.base;
index da614ec..cbc7f00 100644 (file)
 #include <core/engctx.h>
 #include <core/object.h>
 
-#include "priv.h"
 #include <subdev/bios.h>
 
-struct nv50_fb_priv {
-       struct nouveau_fb base;
-       struct page *r100c08_page;
-       dma_addr_t r100c08;
-};
+#include "nv50.h"
 
 int
 nv50_fb_memtype[0x80] = {
@@ -48,7 +43,7 @@ nv50_fb_memtype[0x80] = {
        1, 0, 2, 0, 1, 0, 2, 0, 1, 1, 2, 2, 1, 1, 0, 0
 };
 
-static bool
+bool
 nv50_fb_memtype_valid(struct nouveau_fb *pfb, u32 memtype)
 {
        return nv50_fb_memtype[(memtype & 0xff00) >> 8] != 0;
@@ -239,7 +234,7 @@ nv50_fb_intr(struct nouveau_subdev *subdev)
                pr_cont("0x%08x\n", st1);
 }
 
-static int
+int
 nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
             struct nouveau_oclass *oclass, void *data, u32 size,
             struct nouveau_object **pobject)
@@ -248,7 +243,7 @@ nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv50_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &nv50_ram_oclass, &priv);
+       ret = nouveau_fb_create(parent, engine, oclass, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -264,12 +259,11 @@ nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                nv_warn(priv, "failed 0x100c08 page alloc\n");
        }
 
-       priv->base.memtype_valid = nv50_fb_memtype_valid;
        nv_subdev(priv)->intr = nv50_fb_intr;
        return 0;
 }
 
-static void
+void
 nv50_fb_dtor(struct nouveau_object *object)
 {
        struct nouveau_device *device = nv_device(object);
@@ -284,10 +278,10 @@ nv50_fb_dtor(struct nouveau_object *object)
        nouveau_fb_destroy(&priv->base);
 }
 
-static int
+int
 nv50_fb_init(struct nouveau_object *object)
 {
-       struct nouveau_device *device = nv_device(object);
+       struct nv50_fb_impl *impl = (void *)object->oclass;
        struct nv50_fb_priv *priv = (void *)object;
        int ret;
 
@@ -303,33 +297,20 @@ nv50_fb_init(struct nouveau_object *object)
 
        /* This is needed to get meaningful information from 100c90
         * on traps. No idea what these values mean exactly. */
-       switch (device->chipset) {
-       case 0x50:
-               nv_wr32(priv, 0x100c90, 0x000707ff);
-               break;
-       case 0xa3:
-       case 0xa5:
-       case 0xa8:
-               nv_wr32(priv, 0x100c90, 0x000d0fff);
-               break;
-       case 0xaf:
-               nv_wr32(priv, 0x100c90, 0x089d1fff);
-               break;
-       default:
-               nv_wr32(priv, 0x100c90, 0x001d07ff);
-               break;
-       }
-
+       nv_wr32(priv, 0x100c90, impl->trap);
        return 0;
 }
 
-struct nouveau_oclass
-nv50_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0x50),
-       .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nv50_fb_oclass = &(struct nv50_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x50),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv50_fb_ctor,
                .dtor = nv50_fb_dtor,
                .init = nv50_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .base.memtype = nv50_fb_memtype_valid,
+       .base.ram = &nv50_ram_oclass,
+       .trap = 0x000707ff,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.h b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.h
new file mode 100644 (file)
index 0000000..c5e5a88
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __NVKM_FB_NV50_H__
+#define __NVKM_FB_NV50_H__
+
+#include "priv.h"
+
+struct nv50_fb_priv {
+       struct nouveau_fb base;
+       struct page *r100c08_page;
+       dma_addr_t r100c08;
+};
+
+int  nv50_fb_ctor(struct nouveau_object *, struct nouveau_object *,
+                 struct nouveau_oclass *, void *, u32,
+                 struct nouveau_object **);
+void nv50_fb_dtor(struct nouveau_object *);
+int  nv50_fb_init(struct nouveau_object *);
+
+struct nv50_fb_impl {
+       struct nouveau_fb_impl base;
+       u32 trap;
+};
+
+#define nv50_ram_create(p,e,o,d)                                               \
+       nv50_ram_create_((p), (e), (o), sizeof(**d), (void **)d)
+int  nv50_ram_create_(struct nouveau_object *, struct nouveau_object *,
+                     struct nouveau_oclass *, int, void **);
+int  nv50_ram_get(struct nouveau_fb *, u64 size, u32 align, u32 ncmin,
+                 u32 memtype, struct nouveau_mem **);
+void nv50_ram_put(struct nouveau_fb *, struct nouveau_mem **);
+void __nv50_ram_put(struct nouveau_fb *, struct nouveau_mem *);
+extern int nv50_fb_memtype[0x80];
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv84.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv84.c
new file mode 100644 (file)
index 0000000..cf0e767
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv50.h"
+
+struct nouveau_oclass *
+nv84_fb_oclass = &(struct nv50_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0x84),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_fb_ctor,
+               .dtor = nv50_fb_dtor,
+               .init = nv50_fb_init,
+               .fini = _nouveau_fb_fini,
+       },
+       .base.memtype = nv50_fb_memtype_valid,
+       .base.ram = &nv50_ram_oclass,
+       .trap = 0x001d07ff,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nva3.c
new file mode 100644 (file)
index 0000000..dab6e1c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv50.h"
+
+struct nouveau_oclass *
+nva3_fb_oclass = &(struct nv50_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0xa3),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_fb_ctor,
+               .dtor = nv50_fb_dtor,
+               .init = nv50_fb_init,
+               .fini = _nouveau_fb_fini,
+       },
+       .base.memtype = nv50_fb_memtype_valid,
+       .base.ram = &nva3_ram_oclass,
+       .trap = 0x000d0fff,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvaa.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvaa.c
new file mode 100644 (file)
index 0000000..cba8e68
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv50.h"
+
+struct nouveau_oclass *
+nvaa_fb_oclass = &(struct nv50_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0xaa),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_fb_ctor,
+               .dtor = nv50_fb_dtor,
+               .init = nv50_fb_init,
+               .fini = _nouveau_fb_fini,
+       },
+       .base.memtype = nv50_fb_memtype_valid,
+       .base.ram = &nvaa_ram_oclass,
+       .trap = 0x001d07ff,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvaf.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvaf.c
new file mode 100644 (file)
index 0000000..5423faa
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv50.h"
+
+struct nouveau_oclass *
+nvaf_fb_oclass = &(struct nv50_fb_impl) {
+       .base.base.handle = NV_SUBDEV(FB, 0xaf),
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_fb_ctor,
+               .dtor = nv50_fb_dtor,
+               .init = nv50_fb_init,
+               .fini = _nouveau_fb_fini,
+       },
+       .base.memtype = nv50_fb_memtype_valid,
+       .base.ram = &nvaa_ram_oclass,
+       .trap = 0x089d1fff,
+}.base.base;
index f35d76f..e5fc37c 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include "priv.h"
-
-struct nvc0_fb_priv {
-       struct nouveau_fb base;
-       struct page *r100c10_page;
-       dma_addr_t r100c10;
-};
+#include "nvc0.h"
 
 extern const u8 nvc0_pte_storage_type_map[256];
 
-static bool
+bool
 nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags)
 {
        u8 memtype = (tile_flags & 0x0000ff00) >> 8;
        return likely((nvc0_pte_storage_type_map[memtype] != 0xff));
 }
 
-static int
+int
 nvc0_fb_init(struct nouveau_object *object)
 {
        struct nvc0_fb_priv *priv = (void *)object;
@@ -54,7 +48,7 @@ nvc0_fb_init(struct nouveau_object *object)
        return 0;
 }
 
-static void
+void
 nvc0_fb_dtor(struct nouveau_object *object)
 {
        struct nouveau_device *device = nv_device(object);
@@ -69,7 +63,7 @@ nvc0_fb_dtor(struct nouveau_object *object)
        nouveau_fb_destroy(&priv->base);
 }
 
-static int
+int
 nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
             struct nouveau_oclass *oclass, void *data, u32 size,
             struct nouveau_object **pobject)
@@ -78,13 +72,11 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nvc0_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &nvc0_ram_oclass, &priv);
+       ret = nouveau_fb_create(parent, engine, oclass, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       priv->base.memtype_valid = nvc0_fb_memtype_valid;
-
        priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
        if (priv->r100c10_page) {
                priv->r100c10 = pci_map_page(device->pdev, priv->r100c10_page,
@@ -97,14 +89,15 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        return 0;
 }
 
-
-struct nouveau_oclass
-nvc0_fb_oclass = {
-       .handle = NV_SUBDEV(FB, 0xc0),
-       .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nvc0_fb_oclass = &(struct nouveau_fb_impl) {
+       .base.handle = NV_SUBDEV(FB, 0xc0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nvc0_fb_ctor,
                .dtor = nvc0_fb_dtor,
                .init = nvc0_fb_init,
                .fini = _nouveau_fb_fini,
        },
-};
+       .memtype = nvc0_fb_memtype_valid,
+       .ram = &nvc0_ram_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h
new file mode 100644 (file)
index 0000000..9e1931e
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __NVKM_RAM_NVC0_H__
+#define __NVKM_RAM_NVC0_H__
+
+#include "priv.h"
+#include "nv50.h"
+
+struct nvc0_fb_priv {
+       struct nouveau_fb base;
+       struct page *r100c10_page;
+       dma_addr_t r100c10;
+};
+
+int  nvc0_fb_ctor(struct nouveau_object *, struct nouveau_object *,
+                 struct nouveau_oclass *, void *, u32,
+                 struct nouveau_object **);
+void nvc0_fb_dtor(struct nouveau_object *);
+int  nvc0_fb_init(struct nouveau_object *);
+bool nvc0_fb_memtype_valid(struct nouveau_fb *, u32);
+
+
+#define nvc0_ram_create(p,e,o,d)                                               \
+       nvc0_ram_create_((p), (e), (o), sizeof(**d), (void **)d)
+int  nvc0_ram_create_(struct nouveau_object *, struct nouveau_object *,
+                     struct nouveau_oclass *, int, void **);
+int  nvc0_ram_get(struct nouveau_fb *, u64, u32, u32, u32,
+                 struct nouveau_mem **);
+void nvc0_ram_put(struct nouveau_fb *, struct nouveau_mem **);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nve0.c
new file mode 100644 (file)
index 0000000..595db50
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nvc0.h"
+
+struct nouveau_oclass *
+nve0_fb_oclass = &(struct nouveau_fb_impl) {
+       .base.handle = NV_SUBDEV(FB, 0xe0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_fb_ctor,
+               .dtor = nvc0_fb_dtor,
+               .init = nvc0_fb_init,
+               .fini = _nouveau_fb_fini,
+       },
+       .memtype = nvc0_fb_memtype_valid,
+       .ram = &nve0_ram_oclass,
+}.base;
index db9d6dd..4931252 100644 (file)
@@ -12,6 +12,8 @@
 #define nouveau_ram_fini(p,s)                                                  \
        nouveau_object_fini(&(p)->base, (s))
 
+#define nouveau_ram_create_(p,e,o,s,d)                                         \
+       nouveau_object_create_((p), (e), (o), 0, (s), (void **)d)
 #define _nouveau_ram_dtor nouveau_object_destroy
 #define _nouveau_ram_init nouveau_object_init
 #define _nouveau_ram_fini nouveau_object_fini
@@ -26,10 +28,16 @@ extern struct nouveau_oclass nv44_ram_oclass;
 extern struct nouveau_oclass nv49_ram_oclass;
 extern struct nouveau_oclass nv4e_ram_oclass;
 extern struct nouveau_oclass nv50_ram_oclass;
+extern struct nouveau_oclass nva3_ram_oclass;
+extern struct nouveau_oclass nvaa_ram_oclass;
 extern struct nouveau_oclass nvc0_ram_oclass;
+extern struct nouveau_oclass nve0_ram_oclass;
 
-#define nouveau_fb_create(p,e,c,r,d)                                           \
-       nouveau_fb_create_((p), (e), (c), (r), sizeof(**d), (void **)d)
+int nouveau_sddr3_calc(struct nouveau_ram *ram);
+int nouveau_gddr5_calc(struct nouveau_ram *ram);
+
+#define nouveau_fb_create(p,e,c,d)                                             \
+       nouveau_fb_create_((p), (e), (c), sizeof(**d), (void **)d)
 #define nouveau_fb_destroy(p) ({                                               \
        struct nouveau_fb *pfb = (p);                                          \
        _nouveau_fb_dtor(nv_object(pfb));                                      \
@@ -44,44 +52,21 @@ extern struct nouveau_oclass nvc0_ram_oclass;
 })
 
 int nouveau_fb_create_(struct nouveau_object *, struct nouveau_object *,
-                      struct nouveau_oclass *, struct nouveau_oclass *,
-                      int length, void **pobject);
+                      struct nouveau_oclass *, int, void **);
 void _nouveau_fb_dtor(struct nouveau_object *);
 int  _nouveau_fb_init(struct nouveau_object *);
 int  _nouveau_fb_fini(struct nouveau_object *, bool);
 
-struct nouveau_bios;
-int  nouveau_fb_bios_memtype(struct nouveau_bios *);
+struct nouveau_fb_impl {
+       struct nouveau_oclass base;
+       struct nouveau_oclass *ram;
+       bool (*memtype)(struct nouveau_fb *, u32);
+};
 
 bool nv04_fb_memtype_valid(struct nouveau_fb *, u32 memtype);
+bool nv50_fb_memtype_valid(struct nouveau_fb *, u32 memtype);
 
-void nv10_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size,
-                      u32 pitch, u32 flags, struct nouveau_fb_tile *);
-void nv10_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *);
-void nv10_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
-
-void nv20_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size,
-                      u32 pitch, u32 flags, struct nouveau_fb_tile *);
-void nv20_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *);
-void nv20_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
-
-int  nv30_fb_init(struct nouveau_object *);
-void nv30_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size,
-                      u32 pitch, u32 flags, struct nouveau_fb_tile *);
-
-void nv40_fb_tile_comp(struct nouveau_fb *, int i, u32 size, u32 flags,
-                      struct nouveau_fb_tile *);
-
-int  nv41_fb_init(struct nouveau_object *);
-void nv41_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
-
-int  nv44_fb_init(struct nouveau_object *);
-void nv44_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
-
-void nv46_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size,
-                      u32 pitch, u32 flags, struct nouveau_fb_tile *);
-
-void __nv50_ram_put(struct nouveau_fb *, struct nouveau_mem *);
-extern int nv50_fb_memtype[0x80];
+struct nouveau_bios;
+int  nouveau_fb_bios_memtype(struct nouveau_bios *);
 
 #endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h b/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h
new file mode 100644 (file)
index 0000000..0f57fcf
--- /dev/null
@@ -0,0 +1,118 @@
+#ifndef __NVKM_FBRAM_FUC_H__
+#define __NVKM_FBRAM_FUC_H__
+
+#include <subdev/pwr.h>
+
+struct ramfuc {
+       struct nouveau_memx *memx;
+       struct nouveau_fb *pfb;
+       int sequence;
+};
+
+struct ramfuc_reg {
+       int sequence;
+       bool force;
+       u32 addr[2];
+       u32 data;
+};
+
+static inline struct ramfuc_reg
+ramfuc_reg2(u32 addr1, u32 addr2)
+{
+       return (struct ramfuc_reg) {
+               .sequence = 0,
+               .addr = { addr1, addr2 },
+               .data = 0xdeadbeef,
+       };
+}
+
+static inline struct ramfuc_reg
+ramfuc_reg(u32 addr)
+{
+       return ramfuc_reg2(addr, addr);
+}
+
+static inline int
+ramfuc_init(struct ramfuc *ram, struct nouveau_fb *pfb)
+{
+       struct nouveau_pwr *ppwr = nouveau_pwr(pfb);
+       int ret;
+
+       ret = nouveau_memx_init(ppwr, &ram->memx);
+       if (ret)
+               return ret;
+
+       ram->sequence++;
+       ram->pfb = pfb;
+       return 0;
+}
+
+static inline int
+ramfuc_exec(struct ramfuc *ram, bool exec)
+{
+       int ret = 0;
+       if (ram->pfb) {
+               ret = nouveau_memx_fini(&ram->memx, exec);
+               ram->pfb = NULL;
+       }
+       return ret;
+}
+
+static inline u32
+ramfuc_rd32(struct ramfuc *ram, struct ramfuc_reg *reg)
+{
+       if (reg->sequence != ram->sequence)
+               reg->data = nv_rd32(ram->pfb, reg->addr[0]);
+       return reg->data;
+}
+
+static inline void
+ramfuc_wr32(struct ramfuc *ram, struct ramfuc_reg *reg, u32 data)
+{
+       reg->sequence = ram->sequence;
+       reg->data = data;
+       if (reg->addr[0] != reg->addr[1])
+               nouveau_memx_wr32(ram->memx, reg->addr[1], reg->data);
+       nouveau_memx_wr32(ram->memx, reg->addr[0], reg->data);
+}
+
+static inline void
+ramfuc_nuke(struct ramfuc *ram, struct ramfuc_reg *reg)
+{
+       reg->force = true;
+}
+
+static inline u32
+ramfuc_mask(struct ramfuc *ram, struct ramfuc_reg *reg, u32 mask, u32 data)
+{
+       u32 temp = ramfuc_rd32(ram, reg);
+       if (temp != ((temp & ~mask) | data) || reg->force) {
+               ramfuc_wr32(ram, reg, (temp & ~mask) | data);
+               reg->force = false;
+       }
+       return temp;
+}
+
+static inline void
+ramfuc_wait(struct ramfuc *ram, u32 addr, u32 mask, u32 data, u32 nsec)
+{
+       nouveau_memx_wait(ram->memx, addr, mask, data, nsec);
+}
+
+static inline void
+ramfuc_nsec(struct ramfuc *ram, u32 nsec)
+{
+       nouveau_memx_nsec(ram->memx, nsec);
+}
+
+#define ram_init(s,p)       ramfuc_init(&(s)->base, (p))
+#define ram_exec(s,e)       ramfuc_exec(&(s)->base, (e))
+#define ram_have(s,r)       ((s)->r_##r.addr != 0x000000)
+#define ram_rd32(s,r)       ramfuc_rd32(&(s)->base, &(s)->r_##r)
+#define ram_wr32(s,r,d)     ramfuc_wr32(&(s)->base, &(s)->r_##r, (d))
+#define ram_nuke(s,r)       ramfuc_nuke(&(s)->base, &(s)->r_##r)
+#define ram_mask(s,r,m,d)   ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d))
+#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n))
+#define ram_nsec(s,n)       ramfuc_nsec(&(s)->base, (n))
+
+#endif
index ee49ac4..7648beb 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include "priv.h"
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/pll.h>
+#include <subdev/bios/init.h>
+#include <subdev/clock.h>
+#include <subdev/clock/pll.h>
+#include <subdev/timer.h>
+
+#include <engine/fifo.h>
+
+#include "nv40.h"
+
+int
+nv40_ram_calc(struct nouveau_fb *pfb, u32 freq)
+{
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nv40_ram *ram = (void *)pfb->ram;
+       struct nvbios_pll pll;
+       int N1, M1, N2, M2;
+       int log2P, ret;
+
+       ret = nvbios_pll_parse(bios, 0x04, &pll);
+       if (ret) {
+               nv_error(pfb, "mclk pll data not found\n");
+               return ret;
+       }
+
+       ret = nv04_pll_calc(nv_subdev(pfb), &pll, freq,
+                           &N1, &M1, &N2, &M2, &log2P);
+       if (ret < 0)
+               return ret;
+
+       ram->ctrl  = 0x80000000 | (log2P << 16);
+       ram->ctrl |= min(pll.bias_p + log2P, (int)pll.max_p) << 20;
+       if (N2 == M2) {
+               ram->ctrl |= 0x00000100;
+               ram->coef  = (N1 << 8) | M1;
+       } else {
+               ram->ctrl |= 0x40000000;
+               ram->coef  = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
+       }
+
+       return 0;
+}
+
+int
+nv40_ram_prog(struct nouveau_fb *pfb)
+{
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nv40_ram *ram = (void *)pfb->ram;
+       struct bit_entry M;
+       u32 crtc_mask = 0;
+       u8  sr1[2];
+       int i;
+
+       /* determine which CRTCs are active, fetch VGA_SR1 for each */
+       for (i = 0; i < 2; i++) {
+               u32 vbl = nv_rd32(pfb, 0x600808 + (i * 0x2000));
+               u32 cnt = 0;
+               do {
+                       if (vbl != nv_rd32(pfb, 0x600808 + (i * 0x2000))) {
+                               nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01);
+                               sr1[i] = nv_rd08(pfb, 0x0c03c5 + (i * 0x2000));
+                               if (!(sr1[i] & 0x20))
+                                       crtc_mask |= (1 << i);
+                               break;
+                       }
+                       udelay(1);
+               } while (cnt++ < 32);
+       }
+
+       /* wait for vblank start on active crtcs, disable memory access */
+       for (i = 0; i < 2; i++) {
+               if (!(crtc_mask & (1 << i)))
+                       continue;
+               nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00000000);
+               nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
+               nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01);
+               nv_wr08(pfb, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20);
+       }
+
+       /* prepare ram for reclocking */
+       nv_wr32(pfb, 0x1002d4, 0x00000001); /* precharge */
+       nv_wr32(pfb, 0x1002d0, 0x00000001); /* refresh */
+       nv_wr32(pfb, 0x1002d0, 0x00000001); /* refresh */
+       nv_mask(pfb, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */
+       nv_wr32(pfb, 0x1002dc, 0x00000001); /* enable self-refresh */
+
+       /* change the PLL of each memory partition */
+       nv_mask(pfb, 0x00c040, 0x0000c000, 0x00000000);
+       switch (nv_device(pfb)->chipset) {
+       case 0x40:
+       case 0x45:
+       case 0x41:
+       case 0x42:
+       case 0x47:
+               nv_mask(pfb, 0x004044, 0xc0771100, ram->ctrl);
+               nv_mask(pfb, 0x00402c, 0xc0771100, ram->ctrl);
+               nv_wr32(pfb, 0x004048, ram->coef);
+               nv_wr32(pfb, 0x004030, ram->coef);
+       case 0x43:
+       case 0x49:
+       case 0x4b:
+               nv_mask(pfb, 0x004038, 0xc0771100, ram->ctrl);
+               nv_wr32(pfb, 0x00403c, ram->coef);
+       default:
+               nv_mask(pfb, 0x004020, 0xc0771100, ram->ctrl);
+               nv_wr32(pfb, 0x004024, ram->coef);
+               break;
+       }
+       udelay(100);
+       nv_mask(pfb, 0x00c040, 0x0000c000, 0x0000c000);
+
+       /* re-enable normal operation of memory controller */
+       nv_wr32(pfb, 0x1002dc, 0x00000000);
+       nv_mask(pfb, 0x100210, 0x80000000, 0x80000000);
+       udelay(100);
+
+       /* execute memory reset script from vbios */
+       if (!bit_entry(bios, 'M', &M)) {
+               struct nvbios_init init = {
+                       .subdev = nv_subdev(pfb),
+                       .bios = bios,
+                       .offset = nv_ro16(bios, M.offset + 0x00),
+                       .execute = 1,
+               };
+
+               nvbios_exec(&init);
+       }
+
+       /* make sure we're in vblank (hopefully the same one as before), and
+        * then re-enable crtc memory access
+        */
+       for (i = 0; i < 2; i++) {
+               if (!(crtc_mask & (1 << i)))
+                       continue;
+               nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
+               nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01);
+               nv_wr08(pfb, 0x0c03c5 + (i * 0x2000), sr1[i]);
+       }
+
+       return 0;
+}
+
+void
+nv40_ram_tidy(struct nouveau_fb *pfb)
+{
+}
 
 static int
 nv40_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
@@ -30,7 +177,7 @@ nv40_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_object **pobject)
 {
        struct nouveau_fb *pfb = nouveau_fb(parent);
-       struct nouveau_ram *ram;
+       struct nv40_ram *ram;
        u32 pbus1218 = nv_rd32(pfb, 0x001218);
        int ret;
 
@@ -40,15 +187,18 @@ nv40_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        switch (pbus1218 & 0x00000300) {
-       case 0x00000000: ram->type = NV_MEM_TYPE_SDRAM; break;
-       case 0x00000100: ram->type = NV_MEM_TYPE_DDR1; break;
-       case 0x00000200: ram->type = NV_MEM_TYPE_GDDR3; break;
-       case 0x00000300: ram->type = NV_MEM_TYPE_DDR2; break;
+       case 0x00000000: ram->base.type = NV_MEM_TYPE_SDRAM; break;
+       case 0x00000100: ram->base.type = NV_MEM_TYPE_DDR1; break;
+       case 0x00000200: ram->base.type = NV_MEM_TYPE_GDDR3; break;
+       case 0x00000300: ram->base.type = NV_MEM_TYPE_DDR2; break;
        }
 
-       ram->size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
-       ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
-       ram->tags  =  nv_rd32(pfb, 0x100320);
+       ram->base.size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
+       ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+       ram->base.tags  =  nv_rd32(pfb, 0x100320);
+       ram->base.calc = nv40_ram_calc;
+       ram->base.prog = nv40_ram_prog;
+       ram->base.tidy = nv40_ram_tidy;
        return 0;
 }
 
index 1dab7e1..d64498a 100644 (file)
@@ -22,7 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include "priv.h"
+#include "nv40.h"
 
 static int
 nv41_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
@@ -30,7 +30,7 @@ nv41_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_object **pobject)
 {
        struct nouveau_fb *pfb = nouveau_fb(parent);
-       struct nouveau_ram *ram;
+       struct nv40_ram *ram;
        u32 pfb474 = nv_rd32(pfb, 0x100474);
        int ret;
 
@@ -40,15 +40,18 @@ nv41_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        if (pfb474 & 0x00000004)
-               ram->type = NV_MEM_TYPE_GDDR3;
+               ram->base.type = NV_MEM_TYPE_GDDR3;
        if (pfb474 & 0x00000002)
-               ram->type = NV_MEM_TYPE_DDR2;
+               ram->base.type = NV_MEM_TYPE_DDR2;
        if (pfb474 & 0x00000001)
-               ram->type = NV_MEM_TYPE_DDR1;
+               ram->base.type = NV_MEM_TYPE_DDR1;
 
-       ram->size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
-       ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
-       ram->tags  =  nv_rd32(pfb, 0x100320);
+       ram->base.size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
+       ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+       ram->base.tags  =  nv_rd32(pfb, 0x100320);
+       ram->base.calc = nv40_ram_calc;
+       ram->base.prog = nv40_ram_prog;
+       ram->base.tidy = nv40_ram_tidy;
        return 0;
 }
 
index 25fff84..089acac 100644 (file)
@@ -22,7 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include "priv.h"
+#include "nv40.h"
 
 static int
 nv44_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
@@ -30,7 +30,7 @@ nv44_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_object **pobject)
 {
        struct nouveau_fb *pfb = nouveau_fb(parent);
-       struct nouveau_ram *ram;
+       struct nv40_ram *ram;
        u32 pfb474 = nv_rd32(pfb, 0x100474);
        int ret;
 
@@ -40,13 +40,16 @@ nv44_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        if (pfb474 & 0x00000004)
-               ram->type = NV_MEM_TYPE_GDDR3;
+               ram->base.type = NV_MEM_TYPE_GDDR3;
        if (pfb474 & 0x00000002)
-               ram->type = NV_MEM_TYPE_DDR2;
+               ram->base.type = NV_MEM_TYPE_DDR2;
        if (pfb474 & 0x00000001)
-               ram->type = NV_MEM_TYPE_DDR1;
+               ram->base.type = NV_MEM_TYPE_DDR1;
 
-       ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+       ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+       ram->base.calc = nv40_ram_calc;
+       ram->base.prog = nv40_ram_prog;
+       ram->base.tidy = nv40_ram_tidy;
        return 0;
 }
 
index ab7ef0a..baa013a 100644 (file)
@@ -22,7 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include "priv.h"
+#include "nv40.h"
 
 static int
 nv49_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
@@ -30,7 +30,7 @@ nv49_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_object **pobject)
 {
        struct nouveau_fb *pfb = nouveau_fb(parent);
-       struct nouveau_ram *ram;
+       struct nv40_ram *ram;
        u32 pfb914 = nv_rd32(pfb, 0x100914);
        int ret;
 
@@ -40,15 +40,18 @@ nv49_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        switch (pfb914 & 0x00000003) {
-       case 0x00000000: ram->type = NV_MEM_TYPE_DDR1; break;
-       case 0x00000001: ram->type = NV_MEM_TYPE_DDR2; break;
-       case 0x00000002: ram->type = NV_MEM_TYPE_GDDR3; break;
+       case 0x00000000: ram->base.type = NV_MEM_TYPE_DDR1; break;
+       case 0x00000001: ram->base.type = NV_MEM_TYPE_DDR2; break;
+       case 0x00000002: ram->base.type = NV_MEM_TYPE_GDDR3; break;
        case 0x00000003: break;
        }
 
-       ram->size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
-       ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
-       ram->tags  =  nv_rd32(pfb, 0x100320);
+       ram->base.size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
+       ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+       ram->base.tags  =  nv_rd32(pfb, 0x100320);
+       ram->base.calc = nv40_ram_calc;
+       ram->base.prog = nv40_ram_prog;
+       ram->base.tidy = nv40_ram_tidy;
        return 0;
 }
 
index 903baff..76762a1 100644 (file)
  */
 
 #include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/pll.h>
+#include <subdev/bios/perf.h>
+#include <subdev/bios/timing.h>
+#include <subdev/clock/pll.h>
+#include <subdev/fb.h>
+
+#include <core/option.h>
 #include <core/mm.h>
-#include "priv.h"
+
+#include "ramseq.h"
+
+#include "nv50.h"
+
+struct nv50_ramseq {
+       struct hwsq base;
+       struct hwsq_reg r_0x002504;
+       struct hwsq_reg r_0x004008;
+       struct hwsq_reg r_0x00400c;
+       struct hwsq_reg r_0x00c040;
+       struct hwsq_reg r_0x100210;
+       struct hwsq_reg r_0x1002d0;
+       struct hwsq_reg r_0x1002d4;
+       struct hwsq_reg r_0x1002dc;
+       struct hwsq_reg r_0x100da0[8];
+       struct hwsq_reg r_0x100e20;
+       struct hwsq_reg r_0x100e24;
+       struct hwsq_reg r_0x611200;
+       struct hwsq_reg r_timing[9];
+       struct hwsq_reg r_mr[4];
+};
+
+struct nv50_ram {
+       struct nouveau_ram base;
+       struct nv50_ramseq hwsq;
+};
+
+#define QFX5800NVA0 1
+
+static int
+nv50_ram_calc(struct nouveau_fb *pfb, u32 freq)
+{
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nv50_ram *ram = (void *)pfb->ram;
+       struct nv50_ramseq *hwsq = &ram->hwsq;
+       struct nvbios_perfE perfE;
+       struct nvbios_pll mpll;
+       struct bit_entry M;
+       struct {
+               u32 data;
+               u8  size;
+       } ramcfg, timing;
+       u8  ver, hdr, cnt, strap;
+       u32 data;
+       int N1, M1, N2, M2, P;
+       int ret, i;
+
+       /* lookup closest matching performance table entry for frequency */
+       i = 0;
+       do {
+               ramcfg.data = nvbios_perfEp(bios, i++, &ver, &hdr, &cnt,
+                                          &ramcfg.size, &perfE);
+               if (!ramcfg.data || (ver < 0x25 || ver >= 0x40) ||
+                   (ramcfg.size < 2)) {
+                       nv_error(pfb, "invalid/missing perftab entry\n");
+                       return -EINVAL;
+               }
+       } while (perfE.memory < freq);
+
+       /* locate specific data set for the attached memory */
+       if (bit_entry(bios, 'M', &M) || M.version != 1 || M.length < 5) {
+               nv_error(pfb, "invalid/missing memory table\n");
+               return -EINVAL;
+       }
+
+       strap = (nv_rd32(pfb, 0x101000) & 0x0000003c) >> 2;
+       data = nv_ro16(bios, M.offset + 3);
+       if (data)
+               strap = nv_ro08(bios, data + strap);
+
+       if (strap >= cnt) {
+               nv_error(pfb, "invalid ramcfg strap\n");
+               return -EINVAL;
+       }
+
+       ramcfg.data += hdr + (strap * ramcfg.size);
+
+       /* lookup memory timings, if bios says they're present */
+       strap = nv_ro08(bios, ramcfg.data + 0x01);
+       if (strap != 0xff) {
+               timing.data = nvbios_timing_entry(bios, strap, &ver, &hdr);
+               if (!timing.data || ver != 0x10 || hdr < 0x12) {
+                       nv_error(pfb, "invalid/missing timing entry "
+                                "%02x %04x %02x %02x\n",
+                                strap, timing.data, ver, hdr);
+                       return -EINVAL;
+               }
+       } else {
+               timing.data = 0;
+       }
+
+       ret = ram_init(hwsq, nv_subdev(pfb));
+       if (ret)
+               return ret;
+
+       ram_wait(hwsq, 0x01, 0x00); /* wait for !vblank */
+       ram_wait(hwsq, 0x01, 0x01); /* wait for vblank */
+       ram_wr32(hwsq, 0x611200, 0x00003300);
+       ram_wr32(hwsq, 0x002504, 0x00000001); /* block fifo */
+       ram_nsec(hwsq, 8000);
+       ram_setf(hwsq, 0x10, 0x00); /* disable fb */
+       ram_wait(hwsq, 0x00, 0x01); /* wait for fb disabled */
+
+       ram_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge */
+       ram_wr32(hwsq, 0x1002d0, 0x00000001); /* refresh */
+       ram_wr32(hwsq, 0x1002d0, 0x00000001); /* refresh */
+       ram_wr32(hwsq, 0x100210, 0x00000000); /* disable auto-refresh */
+       ram_wr32(hwsq, 0x1002dc, 0x00000001); /* enable self-refresh */
+
+       ret = nvbios_pll_parse(bios, 0x004008, &mpll);
+       mpll.vco2.max_freq = 0;
+       if (ret == 0) {
+               ret = nv04_pll_calc(nv_subdev(pfb), &mpll, freq,
+                                  &N1, &M1, &N2, &M2, &P);
+               if (ret == 0)
+                       ret = -EINVAL;
+       }
+
+       if (ret < 0)
+               return ret;
+
+       ram_mask(hwsq, 0x00c040, 0xc000c000, 0x0000c000);
+       ram_mask(hwsq, 0x004008, 0x00000200, 0x00000200);
+       ram_mask(hwsq, 0x00400c, 0x0000ffff, (N1 << 8) | M1);
+       ram_mask(hwsq, 0x004008, 0x81ff0000, 0x80000000 | (mpll.bias_p << 19) |
+                                            (P << 22) | (P << 16));
+#if QFX5800NVA0
+       for (i = 0; i < 8; i++)
+               ram_mask(hwsq, 0x100da0[i], 0x00000000, 0x00000000); /*XXX*/
+#endif
+       ram_nsec(hwsq, 96000); /*XXX*/
+       ram_mask(hwsq, 0x004008, 0x00002200, 0x00002000);
+
+       ram_wr32(hwsq, 0x1002dc, 0x00000000); /* disable self-refresh */
+       ram_wr32(hwsq, 0x100210, 0x80000000); /* enable auto-refresh */
+
+       ram_nsec(hwsq, 12000);
+
+       switch (ram->base.type) {
+       case NV_MEM_TYPE_DDR2:
+               ram_nuke(hwsq, mr[0]); /* force update */
+               ram_mask(hwsq, mr[0], 0x000, 0x000);
+               break;
+       case NV_MEM_TYPE_GDDR3:
+               ram_mask(hwsq, mr[2], 0x000, 0x000);
+               ram_nuke(hwsq, mr[0]); /* force update */
+               ram_mask(hwsq, mr[0], 0x000, 0x000);
+               break;
+       default:
+               break;
+       }
+
+       ram_mask(hwsq, timing[3], 0x00000000, 0x00000000); /*XXX*/
+       ram_mask(hwsq, timing[1], 0x00000000, 0x00000000); /*XXX*/
+       ram_mask(hwsq, timing[6], 0x00000000, 0x00000000); /*XXX*/
+       ram_mask(hwsq, timing[7], 0x00000000, 0x00000000); /*XXX*/
+       ram_mask(hwsq, timing[8], 0x00000000, 0x00000000); /*XXX*/
+       ram_mask(hwsq, timing[0], 0x00000000, 0x00000000); /*XXX*/
+       ram_mask(hwsq, timing[2], 0x00000000, 0x00000000); /*XXX*/
+       ram_mask(hwsq, timing[4], 0x00000000, 0x00000000); /*XXX*/
+       ram_mask(hwsq, timing[5], 0x00000000, 0x00000000); /*XXX*/
+
+       ram_mask(hwsq, timing[0], 0x00000000, 0x00000000); /*XXX*/
+
+#if QFX5800NVA0
+       ram_nuke(hwsq, 0x100e24);
+       ram_mask(hwsq, 0x100e24, 0x00000000, 0x00000000);
+       ram_nuke(hwsq, 0x100e20);
+       ram_mask(hwsq, 0x100e20, 0x00000000, 0x00000000);
+#endif
+
+       ram_mask(hwsq, mr[0], 0x100, 0x100);
+       ram_mask(hwsq, mr[0], 0x100, 0x000);
+
+       ram_setf(hwsq, 0x10, 0x01); /* enable fb */
+       ram_wait(hwsq, 0x00, 0x00); /* wait for fb enabled */
+       ram_wr32(hwsq, 0x611200, 0x00003330);
+       ram_wr32(hwsq, 0x002504, 0x00000000); /* un-block fifo */
+       return 0;
+}
+
+static int
+nv50_ram_prog(struct nouveau_fb *pfb)
+{
+       struct nouveau_device *device = nv_device(pfb);
+       struct nv50_ram *ram = (void *)pfb->ram;
+       struct nv50_ramseq *hwsq = &ram->hwsq;
+
+       ram_exec(hwsq, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+       return 0;
+}
+
+static void
+nv50_ram_tidy(struct nouveau_fb *pfb)
+{
+       struct nv50_ram *ram = (void *)pfb->ram;
+       struct nv50_ramseq *hwsq = &ram->hwsq;
+       ram_exec(hwsq, false);
+}
 
 void
 __nv50_ram_put(struct nouveau_fb *pfb, struct nouveau_mem *mem)
@@ -57,7 +264,7 @@ nv50_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
        kfree(mem);
 }
 
-static int
+int
 nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
             u32 memtype, struct nouveau_mem **pmem)
 {
@@ -160,77 +367,114 @@ nv50_fb_vram_rblock(struct nouveau_fb *pfb, struct nouveau_ram *ram)
        return rblock_size;
 }
 
-static int
-nv50_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
-               struct nouveau_oclass *oclass, void *data, u32 datasize,
-               struct nouveau_object **pobject)
+int
+nv50_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine,
+                struct nouveau_oclass *oclass, int length, void **pobject)
 {
-       struct nouveau_fb *pfb = nouveau_fb(parent);
-       struct nouveau_device *device = nv_device(pfb);
-       struct nouveau_bios *bios = nouveau_bios(device);
-       struct nouveau_ram *ram;
        const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
        const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
-       u32 size;
+       struct nouveau_bios *bios = nouveau_bios(parent);
+       struct nouveau_fb *pfb = nouveau_fb(parent);
+       struct nouveau_ram *ram;
        int ret;
 
-       ret = nouveau_ram_create(parent, engine, oclass, &ram);
-       *pobject = nv_object(ram);
+       ret = nouveau_ram_create_(parent, engine, oclass, length, pobject);
+       ram = *pobject;
        if (ret)
                return ret;
 
        ram->size = nv_rd32(pfb, 0x10020c);
-       ram->size = (ram->size & 0xffffff00) |
-                      ((ram->size & 0x000000ff) << 32);
-
-       size = (ram->size >> 12) - rsvd_head - rsvd_tail;
-       switch (device->chipset) {
-       case 0xaa:
-       case 0xac:
-       case 0xaf: /* IGPs, no reordering, no real VRAM */
-               ret = nouveau_mm_init(&pfb->vram, rsvd_head, size, 1);
-               if (ret)
-                       return ret;
+       ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32);
 
-               ram->type   = NV_MEM_TYPE_STOLEN;
-               ram->stolen = (u64)nv_rd32(pfb, 0x100e10) << 12;
+       switch (nv_rd32(pfb, 0x100714) & 0x00000007) {
+       case 0: ram->type = NV_MEM_TYPE_DDR1; break;
+       case 1:
+               if (nouveau_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3)
+                       ram->type = NV_MEM_TYPE_DDR3;
+               else
+                       ram->type = NV_MEM_TYPE_DDR2;
                break;
+       case 2: ram->type = NV_MEM_TYPE_GDDR3; break;
+       case 3: ram->type = NV_MEM_TYPE_GDDR4; break;
+       case 4: ram->type = NV_MEM_TYPE_GDDR5; break;
        default:
-               switch (nv_rd32(pfb, 0x100714) & 0x00000007) {
-               case 0: ram->type = NV_MEM_TYPE_DDR1; break;
-               case 1:
-                       if (nouveau_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3)
-                               ram->type = NV_MEM_TYPE_DDR3;
-                       else
-                               ram->type = NV_MEM_TYPE_DDR2;
-                       break;
-               case 2: ram->type = NV_MEM_TYPE_GDDR3; break;
-               case 3: ram->type = NV_MEM_TYPE_GDDR4; break;
-               case 4: ram->type = NV_MEM_TYPE_GDDR5; break;
-               default:
-                       break;
-               }
-
-               ret = nouveau_mm_init(&pfb->vram, rsvd_head, size,
-                                     nv50_fb_vram_rblock(pfb, ram) >> 12);
-               if (ret)
-                       return ret;
-
-               ram->ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1;
-               ram->tags  =  nv_rd32(pfb, 0x100320);
                break;
        }
 
+       ret = nouveau_mm_init(&pfb->vram, rsvd_head, (ram->size >> 12) -
+                             (rsvd_head + rsvd_tail),
+                             nv50_fb_vram_rblock(pfb, ram) >> 12);
+       if (ret)
+               return ret;
+
+       ram->ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1;
+       ram->tags  =  nv_rd32(pfb, 0x100320);
        ram->get = nv50_ram_get;
        ram->put = nv50_ram_put;
        return 0;
 }
 
+static int
+nv50_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 datasize,
+             struct nouveau_object **pobject)
+{
+       struct nv50_ram *ram;
+       int ret, i;
+
+       ret = nv50_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       switch (ram->base.type) {
+       case NV_MEM_TYPE_DDR2:
+       case NV_MEM_TYPE_GDDR3:
+               ram->base.calc = nv50_ram_calc;
+               ram->base.prog = nv50_ram_prog;
+               ram->base.tidy = nv50_ram_tidy;
+               break;
+       default:
+               nv_warn(ram, "reclocking of this ram type unsupported\n");
+               return 0;
+       }
+
+       ram->hwsq.r_0x002504 = hwsq_reg(0x002504);
+       ram->hwsq.r_0x00c040 = hwsq_reg(0x00c040);
+       ram->hwsq.r_0x004008 = hwsq_reg(0x004008);
+       ram->hwsq.r_0x00400c = hwsq_reg(0x00400c);
+       ram->hwsq.r_0x100210 = hwsq_reg(0x100210);
+       ram->hwsq.r_0x1002d0 = hwsq_reg(0x1002d0);
+       ram->hwsq.r_0x1002d4 = hwsq_reg(0x1002d4);
+       ram->hwsq.r_0x1002dc = hwsq_reg(0x1002dc);
+       for (i = 0; i < 8; i++)
+               ram->hwsq.r_0x100da0[i] = hwsq_reg(0x100da0 + (i * 0x04));
+       ram->hwsq.r_0x100e20 = hwsq_reg(0x100e20);
+       ram->hwsq.r_0x100e24 = hwsq_reg(0x100e24);
+       ram->hwsq.r_0x611200 = hwsq_reg(0x611200);
+
+       for (i = 0; i < 9; i++)
+               ram->hwsq.r_timing[i] = hwsq_reg(0x100220 + (i * 0x04));
+
+       if (ram->base.ranks > 1) {
+               ram->hwsq.r_mr[0] = hwsq_reg2(0x1002c0, 0x1002c8);
+               ram->hwsq.r_mr[1] = hwsq_reg2(0x1002c4, 0x1002cc);
+               ram->hwsq.r_mr[2] = hwsq_reg2(0x1002e0, 0x1002e8);
+               ram->hwsq.r_mr[3] = hwsq_reg2(0x1002e4, 0x1002ec);
+       } else {
+               ram->hwsq.r_mr[0] = hwsq_reg(0x1002c0);
+               ram->hwsq.r_mr[1] = hwsq_reg(0x1002c4);
+               ram->hwsq.r_mr[2] = hwsq_reg(0x1002e0);
+               ram->hwsq.r_mr[3] = hwsq_reg(0x1002e4);
+       }
+
+       return 0;
+}
+
 struct nouveau_oclass
 nv50_ram_oclass = {
-       .handle = 0,
        .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv50_ram_create,
+               .ctor = nv50_ram_ctor,
                .dtor = _nouveau_ram_dtor,
                .init = _nouveau_ram_init,
                .fini = _nouveau_ram_fini,
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
new file mode 100644 (file)
index 0000000..f6292cd
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/pll.h>
+#include <subdev/bios/rammap.h>
+#include <subdev/bios/timing.h>
+
+#include <subdev/clock/nva3.h>
+#include <subdev/clock/pll.h>
+
+#include <core/option.h>
+
+#include "ramfuc.h"
+
+#include "nv50.h"
+
+struct nva3_ramfuc {
+       struct ramfuc base;
+       struct ramfuc_reg r_0x004000;
+       struct ramfuc_reg r_0x004004;
+       struct ramfuc_reg r_0x004018;
+       struct ramfuc_reg r_0x004128;
+       struct ramfuc_reg r_0x004168;
+       struct ramfuc_reg r_0x100200;
+       struct ramfuc_reg r_0x100210;
+       struct ramfuc_reg r_0x100220[9];
+       struct ramfuc_reg r_0x1002d0;
+       struct ramfuc_reg r_0x1002d4;
+       struct ramfuc_reg r_0x1002dc;
+       struct ramfuc_reg r_0x10053c;
+       struct ramfuc_reg r_0x1005a0;
+       struct ramfuc_reg r_0x1005a4;
+       struct ramfuc_reg r_0x100714;
+       struct ramfuc_reg r_0x100718;
+       struct ramfuc_reg r_0x10071c;
+       struct ramfuc_reg r_0x100760;
+       struct ramfuc_reg r_0x1007a0;
+       struct ramfuc_reg r_0x1007e0;
+       struct ramfuc_reg r_0x10f804;
+       struct ramfuc_reg r_0x1110e0;
+       struct ramfuc_reg r_0x111100;
+       struct ramfuc_reg r_0x111104;
+       struct ramfuc_reg r_0x611200;
+       struct ramfuc_reg r_mr[4];
+};
+
+struct nva3_ram {
+       struct nouveau_ram base;
+       struct nva3_ramfuc fuc;
+};
+
+static int
+nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
+{
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nva3_ram *ram = (void *)pfb->ram;
+       struct nva3_ramfuc *fuc = &ram->fuc;
+       struct nva3_clock_info mclk;
+       struct bit_entry M;
+       u8  ver, cnt, strap;
+       u32 data;
+       struct {
+               u32 data;
+               u8  size;
+       } rammap, ramcfg, timing;
+       u32 r004018, r100760, ctrl;
+       u32 unk714, unk718, unk71c;
+       int ret;
+
+       /* lookup memory config data relevant to the target frequency */
+       rammap.data = nvbios_rammap_match(bios, freq / 1000, &ver, &rammap.size,
+                                        &cnt, &ramcfg.size);
+       if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) {
+               nv_error(pfb, "invalid/missing rammap entry\n");
+               return -EINVAL;
+       }
+
+       /* locate specific data set for the attached memory */
+       if (bit_entry(bios, 'M', &M) || M.version != 2 || M.length < 3) {
+               nv_error(pfb, "invalid/missing memory table\n");
+               return -EINVAL;
+       }
+
+       strap = (nv_rd32(pfb, 0x101000) & 0x0000003c) >> 2;
+       data = nv_ro16(bios, M.offset + 1);
+       if (data)
+               strap = nv_ro08(bios, data + strap);
+
+       if (strap >= cnt) {
+               nv_error(pfb, "invalid ramcfg strap\n");
+               return -EINVAL;
+       }
+
+       ramcfg.data = rammap.data + rammap.size + (strap * ramcfg.size);
+       if (!ramcfg.data || ver != 0x10 || ramcfg.size < 0x0e) {
+               nv_error(pfb, "invalid/missing ramcfg entry\n");
+               return -EINVAL;
+       }
+
+       /* lookup memory timings, if bios says they're present */
+       strap = nv_ro08(bios, ramcfg.data + 0x01);
+       if (strap != 0xff) {
+               timing.data = nvbios_timing_entry(bios, strap, &ver,
+                                                &timing.size);
+               if (!timing.data || ver != 0x10 || timing.size < 0x19) {
+                       nv_error(pfb, "invalid/missing timing entry\n");
+                       return -EINVAL;
+               }
+       } else {
+               timing.data = 0;
+       }
+
+       ret = nva3_clock_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk);
+       if (ret < 0) {
+               nv_error(pfb, "failed mclk calculation\n");
+               return ret;
+       }
+
+       ret = ram_init(fuc, pfb);
+       if (ret)
+               return ret;
+
+       /* XXX: where the fuck does 750MHz come from? */
+       if (freq <= 750000) {
+               r004018 = 0x10000000;
+               r100760 = 0x22222222;
+       } else {
+               r004018 = 0x00000000;
+               r100760 = 0x00000000;
+       }
+
+       ctrl = ram_rd32(fuc, 0x004000);
+       if (ctrl & 0x00000008) {
+               if (mclk.pll) {
+                       ram_mask(fuc, 0x004128, 0x00000101, 0x00000101);
+                       ram_wr32(fuc, 0x004004, mclk.pll);
+                       ram_wr32(fuc, 0x004000, (ctrl |= 0x00000001));
+                       ram_wr32(fuc, 0x004000, (ctrl &= 0xffffffef));
+                       ram_wait(fuc, 0x004000, 0x00020000, 0x00020000, 64000);
+                       ram_wr32(fuc, 0x004000, (ctrl |= 0x00000010));
+                       ram_wr32(fuc, 0x004018, 0x00005000 | r004018);
+                       ram_wr32(fuc, 0x004000, (ctrl |= 0x00000004));
+               }
+       } else {
+               u32 ssel = 0x00000101;
+               if (mclk.clk)
+                       ssel |= mclk.clk;
+               else
+                       ssel |= 0x00080000; /* 324MHz, shouldn't matter... */
+               ram_mask(fuc, 0x004168, 0x003f3141, ctrl);
+       }
+
+       if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) {
+               ram_mask(fuc, 0x111104, 0x00000600, 0x00000000);
+       } else {
+               ram_mask(fuc, 0x111100, 0x40000000, 0x40000000);
+               ram_mask(fuc, 0x111104, 0x00000180, 0x00000000);
+       }
+
+       if (!(nv_ro08(bios, rammap.data + 0x04) & 0x02))
+               ram_mask(fuc, 0x100200, 0x00000800, 0x00000000);
+       ram_wr32(fuc, 0x611200, 0x00003300);
+       if (!(nv_ro08(bios, ramcfg.data + 0x02) & 0x10))
+               ram_wr32(fuc, 0x111100, 0x4c020000); /*XXX*/
+
+       ram_wr32(fuc, 0x1002d4, 0x00000001);
+       ram_wr32(fuc, 0x1002d0, 0x00000001);
+       ram_wr32(fuc, 0x1002d0, 0x00000001);
+       ram_wr32(fuc, 0x100210, 0x00000000);
+       ram_wr32(fuc, 0x1002dc, 0x00000001);
+       ram_nsec(fuc, 2000);
+
+       ctrl = ram_rd32(fuc, 0x004000);
+       if (!(ctrl & 0x00000008) && mclk.pll) {
+               ram_wr32(fuc, 0x004000, (ctrl |=  0x00000008));
+               ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000);
+               ram_wr32(fuc, 0x004018, 0x00001000);
+               ram_wr32(fuc, 0x004000, (ctrl &= ~0x00000001));
+               ram_wr32(fuc, 0x004004, mclk.pll);
+               ram_wr32(fuc, 0x004000, (ctrl |=  0x00000001));
+               udelay(64);
+               ram_wr32(fuc, 0x004018, 0x00005000 | r004018);
+               udelay(20);
+       } else
+       if (!mclk.pll) {
+               ram_mask(fuc, 0x004168, 0x003f3040, mclk.clk);
+               ram_wr32(fuc, 0x004000, (ctrl |= 0x00000008));
+               ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000);
+               ram_wr32(fuc, 0x004018, 0x0000d000 | r004018);
+       }
+
+       if ( (nv_ro08(bios, rammap.data + 0x04) & 0x08)) {
+               u32 unk5a0 = (nv_ro16(bios, ramcfg.data + 0x05) << 8) |
+                             nv_ro08(bios, ramcfg.data + 0x05);
+               u32 unk5a4 = (nv_ro16(bios, ramcfg.data + 0x07));
+               u32 unk804 = (nv_ro08(bios, ramcfg.data + 0x09) & 0xf0) << 16 |
+                            (nv_ro08(bios, ramcfg.data + 0x03) & 0x0f) << 16 |
+                            (nv_ro08(bios, ramcfg.data + 0x09) & 0x0f) |
+                            0x80000000;
+               ram_wr32(fuc, 0x1005a0, unk5a0);
+               ram_wr32(fuc, 0x1005a4, unk5a4);
+               ram_wr32(fuc, 0x10f804, unk804);
+               ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000);
+       } else {
+               ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000);
+               ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000);
+               ram_mask(fuc, 0x100760, 0x22222222, r100760);
+               ram_mask(fuc, 0x1007a0, 0x22222222, r100760);
+               ram_mask(fuc, 0x1007e0, 0x22222222, r100760);
+       }
+
+       if (mclk.pll) {
+               ram_mask(fuc, 0x1110e0, 0x00088000, 0x00011000);
+               ram_wr32(fuc, 0x004000, (ctrl &= ~0x00000008));
+       }
+
+       /*XXX: LEAVE */
+       ram_wr32(fuc, 0x1002dc, 0x00000000);
+       ram_wr32(fuc, 0x1002d4, 0x00000001);
+       ram_wr32(fuc, 0x100210, 0x80000000);
+       ram_nsec(fuc, 1000);
+       ram_nsec(fuc, 1000);
+
+       ram_mask(fuc, mr[2], 0x00000000, 0x00000000);
+       ram_nsec(fuc, 1000);
+       ram_nuke(fuc, mr[0]);
+       ram_mask(fuc, mr[0], 0x00000000, 0x00000000);
+       ram_nsec(fuc, 1000);
+
+       ram_mask(fuc, 0x100220[3], 0x00000000, 0x00000000);
+       ram_mask(fuc, 0x100220[1], 0x00000000, 0x00000000);
+       ram_mask(fuc, 0x100220[6], 0x00000000, 0x00000000);
+       ram_mask(fuc, 0x100220[7], 0x00000000, 0x00000000);
+       ram_mask(fuc, 0x100220[2], 0x00000000, 0x00000000);
+       ram_mask(fuc, 0x100220[4], 0x00000000, 0x00000000);
+       ram_mask(fuc, 0x100220[5], 0x00000000, 0x00000000);
+       ram_mask(fuc, 0x100220[0], 0x00000000, 0x00000000);
+       ram_mask(fuc, 0x100220[8], 0x00000000, 0x00000000);
+
+       data = (nv_ro08(bios, ramcfg.data + 0x02) & 0x08) ? 0x00000000 : 0x00001000;
+       ram_mask(fuc, 0x100200, 0x00001000, data);
+
+       unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000010;
+       unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100;
+       unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100;
+       if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x20))
+               unk714 |= 0xf0000000;
+       if (!(nv_ro08(bios, ramcfg.data + 0x02) & 0x04))
+               unk714 |= 0x00000010;
+       ram_wr32(fuc, 0x100714, unk714);
+
+       if (nv_ro08(bios, ramcfg.data + 0x02) & 0x01)
+               unk71c |= 0x00000100;
+       ram_wr32(fuc, 0x10071c, unk71c);
+
+       if (nv_ro08(bios, ramcfg.data + 0x02) & 0x02)
+               unk718 |= 0x00000100;
+       ram_wr32(fuc, 0x100718, unk718);
+
+       if (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)
+               ram_wr32(fuc, 0x111100, 0x48000000); /*XXX*/
+
+       ram_mask(fuc, mr[0], 0x100, 0x100);
+       ram_nsec(fuc, 1000);
+       ram_mask(fuc, mr[0], 0x100, 0x000);
+       ram_nsec(fuc, 1000);
+
+       ram_nsec(fuc, 2000);
+       ram_nsec(fuc, 12000);
+
+       ram_wr32(fuc, 0x611200, 0x00003330);
+       if ( (nv_ro08(bios, rammap.data + 0x04) & 0x02))
+               ram_mask(fuc, 0x100200, 0x00000800, 0x00000800);
+       if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) {
+               ram_mask(fuc, 0x111104, 0x00000180, 0x00000180);
+               ram_mask(fuc, 0x111100, 0x40000000, 0x00000000);
+       } else {
+               ram_mask(fuc, 0x111104, 0x00000600, 0x00000600);
+       }
+
+       if (mclk.pll) {
+               ram_mask(fuc, 0x004168, 0x00000001, 0x00000000);
+               ram_mask(fuc, 0x004168, 0x00000100, 0x00000000);
+       } else {
+               ram_mask(fuc, 0x004000, 0x00000001, 0x00000000);
+               ram_mask(fuc, 0x004128, 0x00000001, 0x00000000);
+               ram_mask(fuc, 0x004128, 0x00000100, 0x00000000);
+       }
+
+       return 0;
+}
+
+static int
+nva3_ram_prog(struct nouveau_fb *pfb)
+{
+       struct nouveau_device *device = nv_device(pfb);
+       struct nva3_ram *ram = (void *)pfb->ram;
+       struct nva3_ramfuc *fuc = &ram->fuc;
+       ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+       return 0;
+}
+
+static void
+nva3_ram_tidy(struct nouveau_fb *pfb)
+{
+       struct nva3_ram *ram = (void *)pfb->ram;
+       struct nva3_ramfuc *fuc = &ram->fuc;
+       ram_exec(fuc, false);
+}
+
+static int
+nva3_ram_init(struct nouveau_object *object)
+{
+       struct nouveau_fb *pfb = (void *)object->parent;
+       struct nva3_ram   *ram = (void *)object;
+       int ret, i;
+
+       ret = nouveau_ram_init(&ram->base);
+       if (ret)
+               return ret;
+
+       /* prepare for ddr link training, and load training patterns */
+       switch (ram->base.type) {
+       case NV_MEM_TYPE_DDR3: {
+               static const u32 pattern[16] = {
+                       0xaaaaaaaa, 0xcccccccc, 0xdddddddd, 0xeeeeeeee,
+                       0x00000000, 0x11111111, 0x44444444, 0xdddddddd,
+                       0x33333333, 0x55555555, 0x77777777, 0x66666666,
+                       0x99999999, 0x88888888, 0xeeeeeeee, 0xbbbbbbbb,
+               };
+
+               nv_wr32(pfb, 0x100538, 0x10001ff6); /*XXX*/
+               nv_wr32(pfb, 0x1005a8, 0x0000ffff);
+               nv_mask(pfb, 0x10f800, 0x00000001, 0x00000001);
+               for (i = 0; i < 0x30; i++) {
+                       nv_wr32(pfb, 0x10f8c0, (i << 8) | i);
+                       nv_wr32(pfb, 0x10f8e0, (i << 8) | i);
+                       nv_wr32(pfb, 0x10f900, pattern[i % 16]);
+                       nv_wr32(pfb, 0x10f920, pattern[i % 16]);
+               }
+       }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int
+nva3_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 datasize,
+             struct nouveau_object **pobject)
+{
+       struct nva3_ram *ram;
+       int ret, i;
+
+       ret = nv50_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       switch (ram->base.type) {
+       case NV_MEM_TYPE_DDR3:
+               ram->base.calc = nva3_ram_calc;
+               ram->base.prog = nva3_ram_prog;
+               ram->base.tidy = nva3_ram_tidy;
+               break;
+       default:
+               nv_warn(ram, "reclocking of this ram type unsupported\n");
+               return 0;
+       }
+
+       ram->fuc.r_0x004000 = ramfuc_reg(0x004000);
+       ram->fuc.r_0x004004 = ramfuc_reg(0x004004);
+       ram->fuc.r_0x004018 = ramfuc_reg(0x004018);
+       ram->fuc.r_0x004128 = ramfuc_reg(0x004128);
+       ram->fuc.r_0x004168 = ramfuc_reg(0x004168);
+       ram->fuc.r_0x100200 = ramfuc_reg(0x100200);
+       ram->fuc.r_0x100210 = ramfuc_reg(0x100210);
+       for (i = 0; i < 9; i++)
+               ram->fuc.r_0x100220[i] = ramfuc_reg(0x100220 + (i * 4));
+       ram->fuc.r_0x1002d0 = ramfuc_reg(0x1002d0);
+       ram->fuc.r_0x1002d4 = ramfuc_reg(0x1002d4);
+       ram->fuc.r_0x1002dc = ramfuc_reg(0x1002dc);
+       ram->fuc.r_0x10053c = ramfuc_reg(0x10053c);
+       ram->fuc.r_0x1005a0 = ramfuc_reg(0x1005a0);
+       ram->fuc.r_0x1005a4 = ramfuc_reg(0x1005a4);
+       ram->fuc.r_0x100714 = ramfuc_reg(0x100714);
+       ram->fuc.r_0x100718 = ramfuc_reg(0x100718);
+       ram->fuc.r_0x10071c = ramfuc_reg(0x10071c);
+       ram->fuc.r_0x100760 = ramfuc_reg(0x100760);
+       ram->fuc.r_0x1007a0 = ramfuc_reg(0x1007a0);
+       ram->fuc.r_0x1007e0 = ramfuc_reg(0x1007e0);
+       ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804);
+       ram->fuc.r_0x1110e0 = ramfuc_reg(0x1110e0);
+       ram->fuc.r_0x111100 = ramfuc_reg(0x111100);
+       ram->fuc.r_0x111104 = ramfuc_reg(0x111104);
+       ram->fuc.r_0x611200 = ramfuc_reg(0x611200);
+
+       if (ram->base.ranks > 1) {
+               ram->fuc.r_mr[0] = ramfuc_reg2(0x1002c0, 0x1002c8);
+               ram->fuc.r_mr[1] = ramfuc_reg2(0x1002c4, 0x1002cc);
+               ram->fuc.r_mr[2] = ramfuc_reg2(0x1002e0, 0x1002e8);
+               ram->fuc.r_mr[3] = ramfuc_reg2(0x1002e4, 0x1002ec);
+       } else {
+               ram->fuc.r_mr[0] = ramfuc_reg(0x1002c0);
+               ram->fuc.r_mr[1] = ramfuc_reg(0x1002c4);
+               ram->fuc.r_mr[2] = ramfuc_reg(0x1002e0);
+               ram->fuc.r_mr[3] = ramfuc_reg(0x1002e4);
+       }
+
+       return 0;
+}
+
+struct nouveau_oclass
+nva3_ram_oclass = {
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nva3_ram_ctor,
+               .dtor = _nouveau_ram_dtor,
+               .init = nva3_ram_init,
+               .fini = _nouveau_ram_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvaa.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvaa.c
new file mode 100644 (file)
index 0000000..00f2ca7
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv50.h"
+
+static int
+nvaa_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 datasize,
+             struct nouveau_object **pobject)
+{
+       const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+       const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+       struct nouveau_fb *pfb = nouveau_fb(parent);
+       struct nouveau_ram *ram;
+       int ret;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       ram->size = nv_rd32(pfb, 0x10020c);
+       ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32);
+
+       ret = nouveau_mm_init(&pfb->vram, rsvd_head, (ram->size >> 12) -
+                             (rsvd_head + rsvd_tail), 1);
+       if (ret)
+               return ret;
+
+       ram->type   = NV_MEM_TYPE_STOLEN;
+       ram->stolen = (u64)nv_rd32(pfb, 0x100e10) << 12;
+       ram->get = nv50_ram_get;
+       ram->put = nv50_ram_put;
+       return 0;
+}
+
+struct nouveau_oclass
+nvaa_ram_oclass = {
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvaa_ram_ctor,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       },
+};
index cf97c4d..f464547 100644 (file)
  */
 
 #include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/pll.h>
+#include <subdev/bios/rammap.h>
+#include <subdev/bios/timing.h>
 #include <subdev/ltcg.h>
 
-#include "priv.h"
+#include <subdev/clock.h>
+#include <subdev/clock/pll.h>
+
+#include <core/option.h>
+
+#include "ramfuc.h"
+
+#include "nvc0.h"
+
+struct nvc0_ramfuc {
+       struct ramfuc base;
+
+       struct ramfuc_reg r_0x10fe20;
+       struct ramfuc_reg r_0x10fe24;
+       struct ramfuc_reg r_0x137320;
+       struct ramfuc_reg r_0x137330;
+
+       struct ramfuc_reg r_0x132000;
+       struct ramfuc_reg r_0x132004;
+       struct ramfuc_reg r_0x132100;
+
+       struct ramfuc_reg r_0x137390;
+
+       struct ramfuc_reg r_0x10f290;
+       struct ramfuc_reg r_0x10f294;
+       struct ramfuc_reg r_0x10f298;
+       struct ramfuc_reg r_0x10f29c;
+       struct ramfuc_reg r_0x10f2a0;
+
+       struct ramfuc_reg r_0x10f300;
+       struct ramfuc_reg r_0x10f338;
+       struct ramfuc_reg r_0x10f340;
+       struct ramfuc_reg r_0x10f344;
+       struct ramfuc_reg r_0x10f348;
+
+       struct ramfuc_reg r_0x10f910;
+       struct ramfuc_reg r_0x10f914;
+
+       struct ramfuc_reg r_0x100b0c;
+       struct ramfuc_reg r_0x10f050;
+       struct ramfuc_reg r_0x10f090;
+       struct ramfuc_reg r_0x10f200;
+       struct ramfuc_reg r_0x10f210;
+       struct ramfuc_reg r_0x10f310;
+       struct ramfuc_reg r_0x10f314;
+       struct ramfuc_reg r_0x10f610;
+       struct ramfuc_reg r_0x10f614;
+       struct ramfuc_reg r_0x10f800;
+       struct ramfuc_reg r_0x10f808;
+       struct ramfuc_reg r_0x10f824;
+       struct ramfuc_reg r_0x10f830;
+       struct ramfuc_reg r_0x10f988;
+       struct ramfuc_reg r_0x10f98c;
+       struct ramfuc_reg r_0x10f990;
+       struct ramfuc_reg r_0x10f998;
+       struct ramfuc_reg r_0x10f9b0;
+       struct ramfuc_reg r_0x10f9b4;
+       struct ramfuc_reg r_0x10fb04;
+       struct ramfuc_reg r_0x10fb08;
+       struct ramfuc_reg r_0x137300;
+       struct ramfuc_reg r_0x137310;
+       struct ramfuc_reg r_0x137360;
+       struct ramfuc_reg r_0x1373ec;
+       struct ramfuc_reg r_0x1373f0;
+       struct ramfuc_reg r_0x1373f8;
+
+       struct ramfuc_reg r_0x61c140;
+       struct ramfuc_reg r_0x611200;
+
+       struct ramfuc_reg r_0x13d8f4;
+};
+
+struct nvc0_ram {
+       struct nouveau_ram base;
+       struct nvc0_ramfuc fuc;
+       struct nvbios_pll refpll;
+       struct nvbios_pll mempll;
+};
+
+static void
+nvc0_ram_train(struct nvc0_ramfuc *fuc, u32 magic)
+{
+       struct nvc0_ram *ram = container_of(fuc, typeof(*ram), fuc);
+       struct nouveau_fb *pfb = nouveau_fb(ram);
+       u32 part = nv_rd32(pfb, 0x022438), i;
+       u32 mask = nv_rd32(pfb, 0x022554);
+       u32 addr = 0x110974;
+
+       ram_wr32(fuc, 0x10f910, magic);
+       ram_wr32(fuc, 0x10f914, magic);
+
+       for (i = 0; (magic & 0x80000000) && i < part; addr += 0x1000, i++) {
+               if (mask & (1 << i))
+                       continue;
+               ram_wait(fuc, addr, 0x0000000f, 0x00000000, 500000);
+       }
+}
+
+static int
+nvc0_ram_calc(struct nouveau_fb *pfb, u32 freq)
+{
+       struct nouveau_clock *clk = nouveau_clock(pfb);
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nvc0_ram *ram = (void *)pfb->ram;
+       struct nvc0_ramfuc *fuc = &ram->fuc;
+       struct bit_entry M;
+       u8  ver, cnt, strap;
+       u32 data;
+       struct {
+               u32 data;
+               u8  size;
+       } rammap, ramcfg, timing;
+       int ref, div, out;
+       int from, mode;
+       int N1, M1, P;
+       int ret;
+
+       /* lookup memory config data relevant to the target frequency */
+       rammap.data = nvbios_rammap_match(bios, freq / 1000, &ver, &rammap.size,
+                                        &cnt, &ramcfg.size);
+       if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) {
+               nv_error(pfb, "invalid/missing rammap entry\n");
+               return -EINVAL;
+       }
+
+       /* locate specific data set for the attached memory */
+       if (bit_entry(bios, 'M', &M) || M.version != 2 || M.length < 3) {
+               nv_error(pfb, "invalid/missing memory table\n");
+               return -EINVAL;
+       }
+
+       strap = (nv_rd32(pfb, 0x101000) & 0x0000003c) >> 2;
+       data = nv_ro16(bios, M.offset + 1);
+       if (data)
+               strap = nv_ro08(bios, data + strap);
+
+       if (strap >= cnt) {
+               nv_error(pfb, "invalid ramcfg strap\n");
+               return -EINVAL;
+       }
+
+       ramcfg.data = rammap.data + rammap.size + (strap * ramcfg.size);
+       if (!ramcfg.data || ver != 0x10 || ramcfg.size < 0x0e) {
+               nv_error(pfb, "invalid/missing ramcfg entry\n");
+               return -EINVAL;
+       }
+
+       /* lookup memory timings, if bios says they're present */
+       strap = nv_ro08(bios, ramcfg.data + 0x01);
+       if (strap != 0xff) {
+               timing.data = nvbios_timing_entry(bios, strap, &ver,
+                                                &timing.size);
+               if (!timing.data || ver != 0x10 || timing.size < 0x19) {
+                       nv_error(pfb, "invalid/missing timing entry\n");
+                       return -EINVAL;
+               }
+       } else {
+               timing.data = 0;
+       }
+
+       ret = ram_init(fuc, pfb);
+       if (ret)
+               return ret;
+
+       /* determine current mclk configuration */
+       from = !!(ram_rd32(fuc, 0x1373f0) & 0x00000002); /*XXX: ok? */
+
+       /* determine target mclk configuration */
+       if (!(ram_rd32(fuc, 0x137300) & 0x00000100))
+               ref = clk->read(clk, nv_clk_src_sppll0);
+       else
+               ref = clk->read(clk, nv_clk_src_sppll1);
+       div = max(min((ref * 2) / freq, (u32)65), (u32)2) - 2;
+       out = (ref * 2) / (div + 2);
+       mode = freq != out;
+
+       ram_mask(fuc, 0x137360, 0x00000002, 0x00000000);
+
+       if ((ram_rd32(fuc, 0x132000) & 0x00000002) || 0 /*XXX*/) {
+               ram_nuke(fuc, 0x132000);
+               ram_mask(fuc, 0x132000, 0x00000002, 0x00000002);
+               ram_mask(fuc, 0x132000, 0x00000002, 0x00000000);
+       }
+
+       if (mode == 1) {
+               ram_nuke(fuc, 0x10fe20);
+               ram_mask(fuc, 0x10fe20, 0x00000002, 0x00000002);
+               ram_mask(fuc, 0x10fe20, 0x00000002, 0x00000000);
+       }
+
+// 0x00020034 // 0x0000000a
+       ram_wr32(fuc, 0x132100, 0x00000001);
+
+       if (mode == 1 && from == 0) {
+               /* calculate refpll */
+               ret = nva3_pll_calc(nv_subdev(pfb), &ram->refpll,
+                                   ram->mempll.refclk, &N1, NULL, &M1, &P);
+               if (ret <= 0) {
+                       nv_error(pfb, "unable to calc refpll\n");
+                       return ret ? ret : -ERANGE;
+               }
+
+               ram_wr32(fuc, 0x10fe20, 0x20010000);
+               ram_wr32(fuc, 0x137320, 0x00000003);
+               ram_wr32(fuc, 0x137330, 0x81200006);
+               ram_wr32(fuc, 0x10fe24, (P << 16) | (N1 << 8) | M1);
+               ram_wr32(fuc, 0x10fe20, 0x20010001);
+               ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000);
+
+               /* calculate mempll */
+               ret = nva3_pll_calc(nv_subdev(pfb), &ram->mempll, freq,
+                                  &N1, NULL, &M1, &P);
+               if (ret <= 0) {
+                       nv_error(pfb, "unable to calc refpll\n");
+                       return ret ? ret : -ERANGE;
+               }
+
+               ram_wr32(fuc, 0x10fe20, 0x20010005);
+               ram_wr32(fuc, 0x132004, (P << 16) | (N1 << 8) | M1);
+               ram_wr32(fuc, 0x132000, 0x18010101);
+               ram_wait(fuc, 0x137390, 0x00000002, 0x00000002, 64000);
+       } else
+       if (mode == 0) {
+               ram_wr32(fuc, 0x137300, 0x00000003);
+       }
+
+       if (from == 0) {
+               ram_nuke(fuc, 0x10fb04);
+               ram_mask(fuc, 0x10fb04, 0x0000ffff, 0x00000000);
+               ram_nuke(fuc, 0x10fb08);
+               ram_mask(fuc, 0x10fb08, 0x0000ffff, 0x00000000);
+               ram_wr32(fuc, 0x10f988, 0x2004ff00);
+               ram_wr32(fuc, 0x10f98c, 0x003fc040);
+               ram_wr32(fuc, 0x10f990, 0x20012001);
+               ram_wr32(fuc, 0x10f998, 0x00011a00);
+               ram_wr32(fuc, 0x13d8f4, 0x00000000);
+       } else {
+               ram_wr32(fuc, 0x10f988, 0x20010000);
+               ram_wr32(fuc, 0x10f98c, 0x00000000);
+               ram_wr32(fuc, 0x10f990, 0x20012001);
+               ram_wr32(fuc, 0x10f998, 0x00010a00);
+       }
+
+       if (from == 0) {
+// 0x00020039 // 0x000000ba
+       }
+
+// 0x0002003a // 0x00000002
+       ram_wr32(fuc, 0x100b0c, 0x00080012);
+// 0x00030014 // 0x00000000 // 0x02b5f070
+// 0x00030014 // 0x00010000 // 0x02b5f070
+       ram_wr32(fuc, 0x611200, 0x00003300);
+// 0x00020034 // 0x0000000a
+// 0x00030020 // 0x00000001 // 0x00000000
+
+       ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000);
+       ram_wr32(fuc, 0x10f210, 0x00000000);
+       ram_nsec(fuc, 1000);
+       if (mode == 0)
+               nvc0_ram_train(fuc, 0x000c1001);
+       ram_wr32(fuc, 0x10f310, 0x00000001);
+       ram_nsec(fuc, 1000);
+       ram_wr32(fuc, 0x10f090, 0x00000061);
+       ram_wr32(fuc, 0x10f090, 0xc000007f);
+       ram_nsec(fuc, 1000);
+
+       if (from == 0) {
+               ram_wr32(fuc, 0x10f824, 0x00007fd4);
+       } else {
+               ram_wr32(fuc, 0x1373ec, 0x00020404);
+       }
+
+       if (mode == 0) {
+               ram_mask(fuc, 0x10f808, 0x00080000, 0x00000000);
+               ram_mask(fuc, 0x10f200, 0x00008000, 0x00008000);
+               ram_wr32(fuc, 0x10f830, 0x41500010);
+               ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000);
+               ram_mask(fuc, 0x132100, 0x00000100, 0x00000100);
+               ram_wr32(fuc, 0x10f050, 0xff000090);
+               ram_wr32(fuc, 0x1373ec, 0x00020f0f);
+               ram_wr32(fuc, 0x1373f0, 0x00000003);
+               ram_wr32(fuc, 0x137310, 0x81201616);
+               ram_wr32(fuc, 0x132100, 0x00000001);
+// 0x00020039 // 0x000000ba
+               ram_wr32(fuc, 0x10f830, 0x00300017);
+               ram_wr32(fuc, 0x1373f0, 0x00000001);
+               ram_wr32(fuc, 0x10f824, 0x00007e77);
+               ram_wr32(fuc, 0x132000, 0x18030001);
+               ram_wr32(fuc, 0x10f090, 0x4000007e);
+               ram_nsec(fuc, 2000);
+               ram_wr32(fuc, 0x10f314, 0x00000001);
+               ram_wr32(fuc, 0x10f210, 0x80000000);
+               ram_wr32(fuc, 0x10f338, 0x00300220);
+               ram_wr32(fuc, 0x10f300, 0x0000011d);
+               ram_nsec(fuc, 1000);
+               ram_wr32(fuc, 0x10f290, 0x02060505);
+               ram_wr32(fuc, 0x10f294, 0x34208288);
+               ram_wr32(fuc, 0x10f298, 0x44050411);
+               ram_wr32(fuc, 0x10f29c, 0x0000114c);
+               ram_wr32(fuc, 0x10f2a0, 0x42e10069);
+               ram_wr32(fuc, 0x10f614, 0x40044f77);
+               ram_wr32(fuc, 0x10f610, 0x40044f77);
+               ram_wr32(fuc, 0x10f344, 0x00600009);
+               ram_nsec(fuc, 1000);
+               ram_wr32(fuc, 0x10f348, 0x00700008);
+               ram_wr32(fuc, 0x61c140, 0x19240000);
+               ram_wr32(fuc, 0x10f830, 0x00300017);
+               nvc0_ram_train(fuc, 0x80021001);
+               nvc0_ram_train(fuc, 0x80081001);
+               ram_wr32(fuc, 0x10f340, 0x00500004);
+               ram_nsec(fuc, 1000);
+               ram_wr32(fuc, 0x10f830, 0x01300017);
+               ram_wr32(fuc, 0x10f830, 0x00300017);
+// 0x00030020 // 0x00000000 // 0x00000000
+// 0x00020034 // 0x0000000b
+               ram_wr32(fuc, 0x100b0c, 0x00080028);
+               ram_wr32(fuc, 0x611200, 0x00003330);
+       } else {
+               ram_wr32(fuc, 0x10f800, 0x00001800);
+               ram_wr32(fuc, 0x13d8f4, 0x00000000);
+               ram_wr32(fuc, 0x1373ec, 0x00020404);
+               ram_wr32(fuc, 0x1373f0, 0x00000003);
+               ram_wr32(fuc, 0x10f830, 0x40700010);
+               ram_wr32(fuc, 0x10f830, 0x40500010);
+               ram_wr32(fuc, 0x13d8f4, 0x00000000);
+               ram_wr32(fuc, 0x1373f8, 0x00000000);
+               ram_wr32(fuc, 0x132100, 0x00000101);
+               ram_wr32(fuc, 0x137310, 0x89201616);
+               ram_wr32(fuc, 0x10f050, 0xff000090);
+               ram_wr32(fuc, 0x1373ec, 0x00030404);
+               ram_wr32(fuc, 0x1373f0, 0x00000002);
+       // 0x00020039 // 0x00000011
+               ram_wr32(fuc, 0x132100, 0x00000001);
+               ram_wr32(fuc, 0x1373f8, 0x00002000);
+               ram_nsec(fuc, 2000);
+               ram_wr32(fuc, 0x10f808, 0x7aaa0050);
+               ram_wr32(fuc, 0x10f830, 0x00500010);
+               ram_wr32(fuc, 0x10f200, 0x00ce1000);
+               ram_wr32(fuc, 0x10f090, 0x4000007e);
+               ram_nsec(fuc, 2000);
+               ram_wr32(fuc, 0x10f314, 0x00000001);
+               ram_wr32(fuc, 0x10f210, 0x80000000);
+               ram_wr32(fuc, 0x10f338, 0x00300200);
+               ram_wr32(fuc, 0x10f300, 0x0000084d);
+               ram_nsec(fuc, 1000);
+               ram_wr32(fuc, 0x10f290, 0x0b343825);
+               ram_wr32(fuc, 0x10f294, 0x3483028e);
+               ram_wr32(fuc, 0x10f298, 0x440c0600);
+               ram_wr32(fuc, 0x10f29c, 0x0000214c);
+               ram_wr32(fuc, 0x10f2a0, 0x42e20069);
+               ram_wr32(fuc, 0x10f200, 0x00ce0000);
+               ram_wr32(fuc, 0x10f614, 0x60044e77);
+               ram_wr32(fuc, 0x10f610, 0x60044e77);
+               ram_wr32(fuc, 0x10f340, 0x00500000);
+               ram_nsec(fuc, 1000);
+               ram_wr32(fuc, 0x10f344, 0x00600228);
+               ram_nsec(fuc, 1000);
+               ram_wr32(fuc, 0x10f348, 0x00700000);
+               ram_wr32(fuc, 0x13d8f4, 0x00000000);
+               ram_wr32(fuc, 0x61c140, 0x09a40000);
+
+               nvc0_ram_train(fuc, 0x800e1008);
+
+               ram_nsec(fuc, 1000);
+               ram_wr32(fuc, 0x10f800, 0x00001804);
+       // 0x00030020 // 0x00000000 // 0x00000000
+       // 0x00020034 // 0x0000000b
+               ram_wr32(fuc, 0x13d8f4, 0x00000000);
+               ram_wr32(fuc, 0x100b0c, 0x00080028);
+               ram_wr32(fuc, 0x611200, 0x00003330);
+               ram_nsec(fuc, 100000);
+               ram_wr32(fuc, 0x10f9b0, 0x05313f41);
+               ram_wr32(fuc, 0x10f9b4, 0x00002f50);
+
+               nvc0_ram_train(fuc, 0x010c1001);
+       }
+
+       ram_mask(fuc, 0x10f200, 0x00000800, 0x00000800);
+// 0x00020016 // 0x00000000
+
+       if (mode == 0)
+               ram_mask(fuc, 0x132000, 0x00000001, 0x00000000);
+       return 0;
+}
+
+static int
+nvc0_ram_prog(struct nouveau_fb *pfb)
+{
+       struct nouveau_device *device = nv_device(pfb);
+       struct nvc0_ram *ram = (void *)pfb->ram;
+       struct nvc0_ramfuc *fuc = &ram->fuc;
+       ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+       return 0;
+}
+
+static void
+nvc0_ram_tidy(struct nouveau_fb *pfb)
+{
+       struct nvc0_ram *ram = (void *)pfb->ram;
+       struct nvc0_ramfuc *fuc = &ram->fuc;
+       ram_exec(fuc, false);
+}
 
 extern const u8 nvc0_pte_storage_type_map[256];
 
@@ -110,10 +515,9 @@ nvc0_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
        return 0;
 }
 
-static int
-nvc0_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
-               struct nouveau_oclass *oclass, void *data, u32 size,
-               struct nouveau_object **pobject)
+int
+nvc0_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine,
+                struct nouveau_oclass *oclass, int size, void **pobject)
 {
        struct nouveau_fb *pfb = nouveau_fb(parent);
        struct nouveau_bios *bios = nouveau_bios(pfb);
@@ -127,8 +531,8 @@ nvc0_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
        bool uniform = true;
        int ret, part;
 
-       ret = nouveau_ram_create(parent, engine, oclass, &ram);
-       *pobject = nv_object(ram);
+       ret = nouveau_ram_create_(parent, engine, oclass, size, pobject);
+       ram = *pobject;
        if (ret)
                return ret;
 
@@ -182,13 +586,158 @@ nvc0_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
        return 0;
 }
 
+static int
+nvc0_ram_init(struct nouveau_object *object)
+{
+       struct nouveau_fb *pfb = (void *)object->parent;
+       struct nvc0_ram   *ram = (void *)object;
+       int ret, i;
+
+       ret = nouveau_ram_init(&ram->base);
+       if (ret)
+               return ret;
+
+       /* prepare for ddr link training, and load training patterns */
+       switch (ram->base.type) {
+       case NV_MEM_TYPE_GDDR5: {
+               static const u8  train0[] = {
+                       0x00, 0xff, 0x55, 0xaa, 0x33, 0xcc,
+                       0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
+               };
+               static const u32 train1[] = {
+                       0x00000000, 0xffffffff,
+                       0x55555555, 0xaaaaaaaa,
+                       0x33333333, 0xcccccccc,
+                       0xf0f0f0f0, 0x0f0f0f0f,
+                       0x00ff00ff, 0xff00ff00,
+                       0x0000ffff, 0xffff0000,
+               };
+
+               for (i = 0; i < 0x30; i++) {
+                       nv_wr32(pfb, 0x10f968, 0x00000000 | (i << 8));
+                       nv_wr32(pfb, 0x10f96c, 0x00000000 | (i << 8));
+                       nv_wr32(pfb, 0x10f920, 0x00000100 | train0[i % 12]);
+                       nv_wr32(pfb, 0x10f924, 0x00000100 | train0[i % 12]);
+                       nv_wr32(pfb, 0x10f918,              train1[i % 12]);
+                       nv_wr32(pfb, 0x10f91c,              train1[i % 12]);
+                       nv_wr32(pfb, 0x10f920, 0x00000000 | train0[i % 12]);
+                       nv_wr32(pfb, 0x10f924, 0x00000000 | train0[i % 12]);
+                       nv_wr32(pfb, 0x10f918,              train1[i % 12]);
+                       nv_wr32(pfb, 0x10f91c,              train1[i % 12]);
+               }
+       }       break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int
+nvc0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nouveau_bios *bios = nouveau_bios(parent);
+       struct nvc0_ram *ram;
+       int ret;
+
+       ret = nvc0_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       ret = nvbios_pll_parse(bios, 0x0c, &ram->refpll);
+       if (ret) {
+               nv_error(ram, "mclk refpll data not found\n");
+               return ret;
+       }
+
+       ret = nvbios_pll_parse(bios, 0x04, &ram->mempll);
+       if (ret) {
+               nv_error(ram, "mclk pll data not found\n");
+               return ret;
+       }
+
+       switch (ram->base.type) {
+       case NV_MEM_TYPE_GDDR5:
+               ram->base.calc = nvc0_ram_calc;
+               ram->base.prog = nvc0_ram_prog;
+               ram->base.tidy = nvc0_ram_tidy;
+               break;
+       default:
+               nv_warn(ram, "reclocking of this ram type unsupported\n");
+               return 0;
+       }
+
+       ram->fuc.r_0x10fe20 = ramfuc_reg(0x10fe20);
+       ram->fuc.r_0x10fe24 = ramfuc_reg(0x10fe24);
+       ram->fuc.r_0x137320 = ramfuc_reg(0x137320);
+       ram->fuc.r_0x137330 = ramfuc_reg(0x137330);
+
+       ram->fuc.r_0x132000 = ramfuc_reg(0x132000);
+       ram->fuc.r_0x132004 = ramfuc_reg(0x132004);
+       ram->fuc.r_0x132100 = ramfuc_reg(0x132100);
+
+       ram->fuc.r_0x137390 = ramfuc_reg(0x137390);
+
+       ram->fuc.r_0x10f290 = ramfuc_reg(0x10f290);
+       ram->fuc.r_0x10f294 = ramfuc_reg(0x10f294);
+       ram->fuc.r_0x10f298 = ramfuc_reg(0x10f298);
+       ram->fuc.r_0x10f29c = ramfuc_reg(0x10f29c);
+       ram->fuc.r_0x10f2a0 = ramfuc_reg(0x10f2a0);
+
+       ram->fuc.r_0x10f300 = ramfuc_reg(0x10f300);
+       ram->fuc.r_0x10f338 = ramfuc_reg(0x10f338);
+       ram->fuc.r_0x10f340 = ramfuc_reg(0x10f340);
+       ram->fuc.r_0x10f344 = ramfuc_reg(0x10f344);
+       ram->fuc.r_0x10f348 = ramfuc_reg(0x10f348);
+
+       ram->fuc.r_0x10f910 = ramfuc_reg(0x10f910);
+       ram->fuc.r_0x10f914 = ramfuc_reg(0x10f914);
+
+       ram->fuc.r_0x100b0c = ramfuc_reg(0x100b0c);
+       ram->fuc.r_0x10f050 = ramfuc_reg(0x10f050);
+       ram->fuc.r_0x10f090 = ramfuc_reg(0x10f090);
+       ram->fuc.r_0x10f200 = ramfuc_reg(0x10f200);
+       ram->fuc.r_0x10f210 = ramfuc_reg(0x10f210);
+       ram->fuc.r_0x10f310 = ramfuc_reg(0x10f310);
+       ram->fuc.r_0x10f314 = ramfuc_reg(0x10f314);
+       ram->fuc.r_0x10f610 = ramfuc_reg(0x10f610);
+       ram->fuc.r_0x10f614 = ramfuc_reg(0x10f614);
+       ram->fuc.r_0x10f800 = ramfuc_reg(0x10f800);
+       ram->fuc.r_0x10f808 = ramfuc_reg(0x10f808);
+       ram->fuc.r_0x10f824 = ramfuc_reg(0x10f824);
+       ram->fuc.r_0x10f830 = ramfuc_reg(0x10f830);
+       ram->fuc.r_0x10f988 = ramfuc_reg(0x10f988);
+       ram->fuc.r_0x10f98c = ramfuc_reg(0x10f98c);
+       ram->fuc.r_0x10f990 = ramfuc_reg(0x10f990);
+       ram->fuc.r_0x10f998 = ramfuc_reg(0x10f998);
+       ram->fuc.r_0x10f9b0 = ramfuc_reg(0x10f9b0);
+       ram->fuc.r_0x10f9b4 = ramfuc_reg(0x10f9b4);
+       ram->fuc.r_0x10fb04 = ramfuc_reg(0x10fb04);
+       ram->fuc.r_0x10fb08 = ramfuc_reg(0x10fb08);
+       ram->fuc.r_0x137310 = ramfuc_reg(0x137300);
+       ram->fuc.r_0x137310 = ramfuc_reg(0x137310);
+       ram->fuc.r_0x137360 = ramfuc_reg(0x137360);
+       ram->fuc.r_0x1373ec = ramfuc_reg(0x1373ec);
+       ram->fuc.r_0x1373f0 = ramfuc_reg(0x1373f0);
+       ram->fuc.r_0x1373f8 = ramfuc_reg(0x1373f8);
+
+       ram->fuc.r_0x61c140 = ramfuc_reg(0x61c140);
+       ram->fuc.r_0x611200 = ramfuc_reg(0x611200);
+
+       ram->fuc.r_0x13d8f4 = ramfuc_reg(0x13d8f4);
+       return 0;
+}
+
 struct nouveau_oclass
 nvc0_ram_oclass = {
        .handle = 0,
        .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nvc0_ram_create,
+               .ctor = nvc0_ram_ctor,
                .dtor = _nouveau_ram_dtor,
-               .init = _nouveau_ram_init,
+               .init = nvc0_ram_init,
                .fini = _nouveau_ram_fini,
        }
 };
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
new file mode 100644 (file)
index 0000000..bc86cfd
--- /dev/null
@@ -0,0 +1,1264 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/gpio.h>
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/pll.h>
+#include <subdev/bios/init.h>
+#include <subdev/bios/rammap.h>
+#include <subdev/bios/timing.h>
+
+#include <subdev/clock.h>
+#include <subdev/clock/pll.h>
+
+#include <subdev/timer.h>
+
+#include <core/option.h>
+
+#include "nvc0.h"
+
+#include "ramfuc.h"
+
+struct nve0_ramfuc {
+       struct ramfuc base;
+
+       struct nvbios_pll refpll;
+       struct nvbios_pll mempll;
+
+       struct ramfuc_reg r_gpioMV;
+       u32 r_funcMV[2];
+       struct ramfuc_reg r_gpio2E;
+       u32 r_func2E[2];
+       struct ramfuc_reg r_gpiotrig;
+
+       struct ramfuc_reg r_0x132020;
+       struct ramfuc_reg r_0x132028;
+       struct ramfuc_reg r_0x132024;
+       struct ramfuc_reg r_0x132030;
+       struct ramfuc_reg r_0x132034;
+       struct ramfuc_reg r_0x132000;
+       struct ramfuc_reg r_0x132004;
+       struct ramfuc_reg r_0x132040;
+
+       struct ramfuc_reg r_0x10f248;
+       struct ramfuc_reg r_0x10f290;
+       struct ramfuc_reg r_0x10f294;
+       struct ramfuc_reg r_0x10f298;
+       struct ramfuc_reg r_0x10f29c;
+       struct ramfuc_reg r_0x10f2a0;
+       struct ramfuc_reg r_0x10f2a4;
+       struct ramfuc_reg r_0x10f2a8;
+       struct ramfuc_reg r_0x10f2ac;
+       struct ramfuc_reg r_0x10f2cc;
+       struct ramfuc_reg r_0x10f2e8;
+       struct ramfuc_reg r_0x10f250;
+       struct ramfuc_reg r_0x10f24c;
+       struct ramfuc_reg r_0x10fec4;
+       struct ramfuc_reg r_0x10fec8;
+       struct ramfuc_reg r_0x10f604;
+       struct ramfuc_reg r_0x10f614;
+       struct ramfuc_reg r_0x10f610;
+       struct ramfuc_reg r_0x100770;
+       struct ramfuc_reg r_0x100778;
+       struct ramfuc_reg r_0x10f224;
+
+       struct ramfuc_reg r_0x10f870;
+       struct ramfuc_reg r_0x10f698;
+       struct ramfuc_reg r_0x10f694;
+       struct ramfuc_reg r_0x10f6b8;
+       struct ramfuc_reg r_0x10f808;
+       struct ramfuc_reg r_0x10f670;
+       struct ramfuc_reg r_0x10f60c;
+       struct ramfuc_reg r_0x10f830;
+       struct ramfuc_reg r_0x1373ec;
+       struct ramfuc_reg r_0x10f800;
+       struct ramfuc_reg r_0x10f82c;
+
+       struct ramfuc_reg r_0x10f978;
+       struct ramfuc_reg r_0x10f910;
+       struct ramfuc_reg r_0x10f914;
+
+       struct ramfuc_reg r_mr[16]; /* MR0 - MR8, MR15 */
+
+       struct ramfuc_reg r_0x62c000;
+       struct ramfuc_reg r_0x10f200;
+       struct ramfuc_reg r_0x10f210;
+       struct ramfuc_reg r_0x10f310;
+       struct ramfuc_reg r_0x10f314;
+       struct ramfuc_reg r_0x10f318;
+       struct ramfuc_reg r_0x10f090;
+       struct ramfuc_reg r_0x10f69c;
+       struct ramfuc_reg r_0x10f824;
+       struct ramfuc_reg r_0x1373f0;
+       struct ramfuc_reg r_0x1373f4;
+       struct ramfuc_reg r_0x137320;
+       struct ramfuc_reg r_0x10f65c;
+       struct ramfuc_reg r_0x10f6bc;
+       struct ramfuc_reg r_0x100710;
+       struct ramfuc_reg r_0x10f750;
+};
+
+struct nve0_ram {
+       struct nouveau_ram base;
+       struct nve0_ramfuc fuc;
+       int from;
+       int mode;
+       int N1, fN1, M1, P1;
+       int N2, M2, P2;
+};
+
+/*******************************************************************************
+ * GDDR5
+ ******************************************************************************/
+static void
+train(struct nve0_ramfuc *fuc, u32 magic)
+{
+       struct nve0_ram *ram = container_of(fuc, typeof(*ram), fuc);
+       struct nouveau_fb *pfb = nouveau_fb(ram);
+       const int mc = nv_rd32(pfb, 0x02243c);
+       int i;
+
+       ram_mask(fuc, 0x10f910, 0xbc0e0000, magic);
+       ram_mask(fuc, 0x10f914, 0xbc0e0000, magic);
+       for (i = 0; i < mc; i++) {
+               const u32 addr = 0x110974 + (i * 0x1000);
+               ram_wait(fuc, addr, 0x0000000f, 0x00000000, 500000);
+       }
+}
+
+static void
+r1373f4_init(struct nve0_ramfuc *fuc)
+{
+       struct nve0_ram *ram = container_of(fuc, typeof(*ram), fuc);
+       const u32 mcoef = ((--ram->P2 << 28) | (ram->N2 << 8) | ram->M2);
+       const u32 rcoef = ((  ram->P1 << 16) | (ram->N1 << 8) | ram->M1);
+       const u32 runk0 = ram->fN1 << 16;
+       const u32 runk1 = ram->fN1;
+
+       if (ram->from == 2) {
+               ram_mask(fuc, 0x1373f4, 0x00000000, 0x00001100);
+               ram_mask(fuc, 0x1373f4, 0x00000000, 0x00000010);
+       } else {
+               ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010);
+       }
+
+       ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000);
+       ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000);
+
+       /* (re)program refpll, if required */
+       if ((ram_rd32(fuc, 0x132024) & 0xffffffff) != rcoef ||
+           (ram_rd32(fuc, 0x132034) & 0x0000ffff) != runk1) {
+               ram_mask(fuc, 0x132000, 0x00000001, 0x00000000);
+               ram_mask(fuc, 0x132020, 0x00000001, 0x00000000);
+               ram_wr32(fuc, 0x137320, 0x00000000);
+               ram_mask(fuc, 0x132030, 0xffff0000, runk0);
+               ram_mask(fuc, 0x132034, 0x0000ffff, runk1);
+               ram_wr32(fuc, 0x132024, rcoef);
+               ram_mask(fuc, 0x132028, 0x00080000, 0x00080000);
+               ram_mask(fuc, 0x132020, 0x00000001, 0x00000001);
+               ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000);
+               ram_mask(fuc, 0x132028, 0x00080000, 0x00000000);
+       }
+
+       /* (re)program mempll, if required */
+       if (ram->mode == 2) {
+               ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000);
+               ram_mask(fuc, 0x132000, 0x00000001, 0x00000000);
+               ram_mask(fuc, 0x132004, 0x103fffff, mcoef);
+               ram_mask(fuc, 0x132000, 0x00000001, 0x00000001);
+               ram_wait(fuc, 0x137390, 0x00000002, 0x00000002, 64000);
+               ram_mask(fuc, 0x1373f4, 0x00000000, 0x00001100);
+       } else {
+               ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010100);
+       }
+
+       ram_mask(fuc, 0x1373f4, 0x00000000, 0x00000010);
+}
+
+static void
+r1373f4_fini(struct nve0_ramfuc *fuc, u32 ramcfg)
+{
+       struct nve0_ram *ram = container_of(fuc, typeof(*ram), fuc);
+       struct nouveau_bios *bios = nouveau_bios(ram);
+       u8 v0 = (nv_ro08(bios, ramcfg + 0x03) & 0xc0) >> 6;
+       u8 v1 = (nv_ro08(bios, ramcfg + 0x03) & 0x30) >> 4;
+       u32 tmp;
+
+       tmp = ram_rd32(fuc, 0x1373ec) & ~0x00030000;
+       ram_wr32(fuc, 0x1373ec, tmp | (v1 << 16));
+       ram_mask(fuc, 0x1373f0, (~ram->mode & 3), 0x00000000);
+       if (ram->mode == 2) {
+               ram_mask(fuc, 0x1373f4, 0x00000003, 0x000000002);
+               ram_mask(fuc, 0x1373f4, 0x00001100, 0x000000000);
+       } else {
+               ram_mask(fuc, 0x1373f4, 0x00000003, 0x000000001);
+               ram_mask(fuc, 0x1373f4, 0x00010000, 0x000000000);
+       }
+       ram_mask(fuc, 0x10f800, 0x00000030, (v0 ^ v1) << 4);
+}
+
+static int
+nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
+{
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nve0_ram *ram = (void *)pfb->ram;
+       struct nve0_ramfuc *fuc = &ram->fuc;
+       const u32 rammap = ram->base.rammap.data;
+       const u32 ramcfg = ram->base.ramcfg.data;
+       const u32 timing = ram->base.timing.data;
+       int vc = !(nv_ro08(bios, ramcfg + 0x02) & 0x08);
+       int mv = 1; /*XXX*/
+       u32 mask, data;
+
+       ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000);
+       ram_wr32(fuc, 0x62c000, 0x0f0f0000);
+
+       /* MR1: turn termination on early, for some reason.. */
+       if ((ram->base.mr[1] & 0x03c) != 0x030)
+               ram_mask(fuc, mr[1], 0x03c, ram->base.mr[1] & 0x03c);
+
+       if (vc == 1 && ram_have(fuc, gpio2E)) {
+               u32 temp  = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]);
+               if (temp != ram_rd32(fuc, gpio2E)) {
+                       ram_wr32(fuc, gpiotrig, 1);
+                       ram_nsec(fuc, 20000);
+               }
+       }
+
+       ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000);
+
+       ram_mask(fuc, 0x10f914, 0x01020000, 0x000c0000);
+       ram_mask(fuc, 0x10f910, 0x01020000, 0x000c0000);
+
+       ram_wr32(fuc, 0x10f210, 0x00000000); /* REFRESH_AUTO = 0 */
+       ram_nsec(fuc, 1000);
+       ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */
+       ram_nsec(fuc, 1000);
+
+       ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000);
+       ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */
+       ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000);
+       ram_wr32(fuc, 0x10f090, 0x00000061);
+       ram_wr32(fuc, 0x10f090, 0xc000007f);
+       ram_nsec(fuc, 1000);
+
+       ram_wr32(fuc, 0x10f698, 0x00000000);
+       ram_wr32(fuc, 0x10f69c, 0x00000000);
+
+       /*XXX: there does appear to be some kind of condition here, simply
+        *     modifying these bits in the vbios from the default pl0
+        *     entries shows no change.  however, the data does appear to
+        *     be correct and may be required for the transition back
+        */
+       mask = 0x800f07e0;
+       data = 0x00030000;
+       if (ram_rd32(fuc, 0x10f978) & 0x00800000)
+               data |= 0x00040000;
+
+       if (1) {
+               data |= 0x800807e0;
+               switch (nv_ro08(bios, ramcfg + 0x03) & 0xc0) {
+               case 0xc0: data &= ~0x00000040; break;
+               case 0x80: data &= ~0x00000100; break;
+               case 0x40: data &= ~0x80000000; break;
+               case 0x00: data &= ~0x00000400; break;
+               }
+
+               switch (nv_ro08(bios, ramcfg + 0x03) & 0x30) {
+               case 0x30: data &= ~0x00000020; break;
+               case 0x20: data &= ~0x00000080; break;
+               case 0x10: data &= ~0x00080000; break;
+               case 0x00: data &= ~0x00000200; break;
+               }
+       }
+
+       if (nv_ro08(bios, ramcfg + 0x02) & 0x80)
+               mask |= 0x03000000;
+       if (nv_ro08(bios, ramcfg + 0x02) & 0x40)
+               mask |= 0x00002000;
+       if (nv_ro08(bios, ramcfg + 0x07) & 0x10)
+               mask |= 0x00004000;
+       if (nv_ro08(bios, ramcfg + 0x07) & 0x08)
+               mask |= 0x00000003;
+       else {
+               mask |= 0x34000000;
+               if (ram_rd32(fuc, 0x10f978) & 0x00800000)
+                       mask |= 0x40000000;
+       }
+       ram_mask(fuc, 0x10f824, mask, data);
+
+       ram_mask(fuc, 0x132040, 0x00010000, 0x00000000);
+
+       if (ram->from == 2 && ram->mode != 2) {
+               ram_mask(fuc, 0x10f808, 0x00080000, 0x00000000);
+               ram_mask(fuc, 0x10f200, 0x00008000, 0x00008000);
+               ram_mask(fuc, 0x10f800, 0x00000000, 0x00000004);
+               ram_mask(fuc, 0x10f830, 0x00008000, 0x01040010);
+               ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000);
+               r1373f4_init(fuc);
+               ram_mask(fuc, 0x1373f0, 0x00000002, 0x00000001);
+               r1373f4_fini(fuc, ramcfg);
+               ram_mask(fuc, 0x10f830, 0x00c00000, 0x00240001);
+       } else
+       if (ram->from != 2 && ram->mode != 2) {
+               r1373f4_init(fuc);
+               r1373f4_fini(fuc, ramcfg);
+       }
+
+       if (ram_have(fuc, gpioMV)) {
+               u32 temp  = ram_mask(fuc, gpioMV, 0x3000, fuc->r_funcMV[mv]);
+               if (temp != ram_rd32(fuc, gpioMV)) {
+                       ram_wr32(fuc, gpiotrig, 1);
+                       ram_nsec(fuc, 64000);
+               }
+       }
+
+       if ( (nv_ro08(bios, ramcfg + 0x02) & 0x40) ||
+            (nv_ro08(bios, ramcfg + 0x07) & 0x10)) {
+               ram_mask(fuc, 0x132040, 0x00010000, 0x00010000);
+               ram_nsec(fuc, 20000);
+       }
+
+       if (ram->from != 2 && ram->mode == 2) {
+               ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000);
+               ram_mask(fuc, 0x1373f0, 0x00000000, 0x00000002);
+               ram_mask(fuc, 0x10f830, 0x00800001, 0x00408010);
+               r1373f4_init(fuc);
+               r1373f4_fini(fuc, ramcfg);
+               ram_mask(fuc, 0x10f808, 0x00000000, 0x00080000);
+               ram_mask(fuc, 0x10f200, 0x00808000, 0x00800000);
+       } else
+       if (ram->from == 2 && ram->mode == 2) {
+               ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000);
+               r1373f4_init(fuc);
+               r1373f4_fini(fuc, ramcfg);
+       }
+
+       if (ram->mode != 2) /*XXX*/ {
+               if (nv_ro08(bios, ramcfg + 0x07) & 0x40)
+                       ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000);
+       }
+
+       data = (nv_ro08(bios, rammap + 0x11) & 0x0c) >> 2;
+       ram_wr32(fuc, 0x10f65c, 0x00000011 * data);
+       ram_wr32(fuc, 0x10f6b8, 0x01010101 * nv_ro08(bios, ramcfg + 0x09));
+       ram_wr32(fuc, 0x10f6bc, 0x01010101 * nv_ro08(bios, ramcfg + 0x09));
+
+       data = nv_ro08(bios, ramcfg + 0x04);
+       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x08)) {
+               ram_wr32(fuc, 0x10f698, 0x01010101 * data);
+               ram_wr32(fuc, 0x10f69c, 0x01010101 * data);
+       }
+
+       if (ram->mode != 2) {
+               u32 temp = ram_rd32(fuc, 0x10f694) & ~0xff00ff00;
+               ram_wr32(fuc, 0x10f694, temp | (0x01000100 * data));
+       }
+
+       if (ram->mode == 2 && (nv_ro08(bios, ramcfg + 0x08) & 0x10))
+               data = 0x00000080;
+       else
+               data = 0x00000000;
+       ram_mask(fuc, 0x10f60c, 0x00000080, data);
+
+       mask = 0x00070000;
+       data = 0x00000000;
+       if (!(nv_ro08(bios, ramcfg + 0x02) & 0x80))
+               data |= 0x03000000;
+       if (!(nv_ro08(bios, ramcfg + 0x02) & 0x40))
+               data |= 0x00002000;
+       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x10))
+               data |= 0x00004000;
+       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x08))
+               data |= 0x00000003;
+       else
+               data |= 0x74000000;
+       ram_mask(fuc, 0x10f824, mask, data);
+
+       if (nv_ro08(bios, ramcfg + 0x01) & 0x08)
+               data = 0x00000000;
+       else
+               data = 0x00001000;
+       ram_mask(fuc, 0x10f200, 0x00001000, data);
+
+       if (ram_rd32(fuc, 0x10f670) & 0x80000000) {
+               ram_nsec(fuc, 10000);
+               ram_mask(fuc, 0x10f670, 0x80000000, 0x00000000);
+       }
+
+       if (nv_ro08(bios, ramcfg + 0x08) & 0x01)
+               data = 0x00100000;
+       else
+               data = 0x00000000;
+       ram_mask(fuc, 0x10f82c, 0x00100000, data);
+
+       data = 0x00000000;
+       if (nv_ro08(bios, ramcfg + 0x08) & 0x08)
+               data |= 0x00002000;
+       if (nv_ro08(bios, ramcfg + 0x08) & 0x04)
+               data |= 0x00001000;
+       if (nv_ro08(bios, ramcfg + 0x08) & 0x02)
+               data |= 0x00004000;
+       ram_mask(fuc, 0x10f830, 0x00007000, data);
+
+       /* PFB timing */
+       ram_mask(fuc, 0x10f248, 0xffffffff, nv_ro32(bios, timing + 0x28));
+       ram_mask(fuc, 0x10f290, 0xffffffff, nv_ro32(bios, timing + 0x00));
+       ram_mask(fuc, 0x10f294, 0xffffffff, nv_ro32(bios, timing + 0x04));
+       ram_mask(fuc, 0x10f298, 0xffffffff, nv_ro32(bios, timing + 0x08));
+       ram_mask(fuc, 0x10f29c, 0xffffffff, nv_ro32(bios, timing + 0x0c));
+       ram_mask(fuc, 0x10f2a0, 0xffffffff, nv_ro32(bios, timing + 0x10));
+       ram_mask(fuc, 0x10f2a4, 0xffffffff, nv_ro32(bios, timing + 0x14));
+       ram_mask(fuc, 0x10f2a8, 0xffffffff, nv_ro32(bios, timing + 0x18));
+       ram_mask(fuc, 0x10f2ac, 0xffffffff, nv_ro32(bios, timing + 0x1c));
+       ram_mask(fuc, 0x10f2cc, 0xffffffff, nv_ro32(bios, timing + 0x20));
+       ram_mask(fuc, 0x10f2e8, 0xffffffff, nv_ro32(bios, timing + 0x24));
+
+       data = (nv_ro08(bios, ramcfg + 0x02) & 0x03) << 8;
+       if (nv_ro08(bios, ramcfg + 0x01) & 0x10)
+               data |= 0x70000000;
+       ram_mask(fuc, 0x10f604, 0x70000300, data);
+
+       data = (nv_ro08(bios, timing + 0x30) & 0x07) << 28;
+       if (nv_ro08(bios, ramcfg + 0x01) & 0x01)
+               data |= 0x00000100;
+       ram_mask(fuc, 0x10f614, 0x70000000, data);
+
+       data = (nv_ro08(bios, timing + 0x30) & 0x07) << 28;
+       if (nv_ro08(bios, ramcfg + 0x01) & 0x02)
+               data |= 0x00000100;
+       ram_mask(fuc, 0x10f610, 0x70000000, data);
+
+       mask = 0x33f00000;
+       data = 0x00000000;
+       if (!(nv_ro08(bios, ramcfg + 0x01) & 0x04))
+               data |= 0x20200000;
+       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x80))
+               data |= 0x12800000;
+       /*XXX: see note above about there probably being some condition
+        *     for the 10f824 stuff that uses ramcfg 3...
+        */
+       if ( (nv_ro08(bios, ramcfg + 0x03) & 0xf0)) {
+               if (nv_ro08(bios, rammap + 0x08) & 0x0c) {
+                       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x80))
+                               mask |= 0x00000020;
+                       else
+                               data |= 0x00000020;
+                       mask |= 0x00000004;
+               }
+       } else {
+               mask |= 0x40000020;
+               data |= 0x00000004;
+       }
+
+       ram_mask(fuc, 0x10f808, mask, data);
+
+       data = nv_ro08(bios, ramcfg + 0x03) & 0x0f;
+       ram_wr32(fuc, 0x10f870, 0x11111111 * data);
+
+       data = nv_ro08(bios, ramcfg + 0x02) & 0x03;
+       if (nv_ro08(bios, ramcfg + 0x01) & 0x10)
+               data |= 0x00000004;
+       if ((nv_rd32(bios, 0x100770) & 0x00000004) != (data & 0x00000004)) {
+               ram_wr32(fuc, 0x10f750, 0x04000009);
+               ram_wr32(fuc, 0x100710, 0x00000000);
+               ram_wait(fuc, 0x100710, 0x80000000, 0x80000000, 200000);
+       }
+       ram_mask(fuc, 0x100770, 0x00000007, data);
+
+       data = (nv_ro08(bios, timing + 0x30) & 0x07) << 8;
+       if (nv_ro08(bios, ramcfg + 0x01) & 0x01)
+               data |= 0x80000000;
+       ram_mask(fuc, 0x100778, 0x00000700, data);
+
+       data = nv_ro16(bios, timing + 0x2c);
+       ram_mask(fuc, 0x10f250, 0x000003f0, (data & 0x003f) <<  4);
+       ram_mask(fuc, 0x10f24c, 0x7f000000, (data & 0x1fc0) << 18);
+
+       data = nv_ro08(bios, timing + 0x30);
+       ram_mask(fuc, 0x10f224, 0x001f0000, (data & 0xf8) << 13);
+
+       data = nv_ro16(bios, timing + 0x31);
+       ram_mask(fuc, 0x10fec4, 0x041e0f07, (data & 0x0800) << 15 |
+                                           (data & 0x0780) << 10 |
+                                           (data & 0x0078) <<  5 |
+                                           (data & 0x0007));
+       ram_mask(fuc, 0x10fec8, 0x00000027, (data & 0x8000) >> 10 |
+                                           (data & 0x7000) >> 12);
+
+       ram_wr32(fuc, 0x10f090, 0x4000007e);
+       ram_nsec(fuc, 1000);
+       ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */
+       ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */
+       ram_nsec(fuc, 2000);
+       ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */
+
+       if ((nv_ro08(bios, ramcfg + 0x08) & 0x10) && (ram->mode == 2) /*XXX*/) {
+               u32 temp = ram_mask(fuc, 0x10f294, 0xff000000, 0x24000000);
+               train(fuc, 0xa4010000); /*XXX*/
+               ram_nsec(fuc, 1000);
+               ram_wr32(fuc, 0x10f294, temp);
+       }
+
+       ram_mask(fuc, mr[3], 0xfff, ram->base.mr[3]);
+       ram_wr32(fuc, mr[0], ram->base.mr[0]);
+       ram_mask(fuc, mr[8], 0xfff, ram->base.mr[8]);
+       ram_nsec(fuc, 1000);
+       ram_mask(fuc, mr[1], 0xfff, ram->base.mr[1]);
+       ram_mask(fuc, mr[5], 0xfff, ram->base.mr[5]);
+       ram_mask(fuc, mr[6], 0xfff, ram->base.mr[6]);
+       ram_mask(fuc, mr[7], 0xfff, ram->base.mr[7]);
+
+       if (vc == 0 && ram_have(fuc, gpio2E)) {
+               u32 temp  = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[0]);
+               if (temp != ram_rd32(fuc, gpio2E)) {
+                       ram_wr32(fuc, gpiotrig, 1);
+                       ram_nsec(fuc, 20000);
+               }
+       }
+
+       ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000);
+       ram_wr32(fuc, 0x10f318, 0x00000001); /* NOP? */
+       ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000);
+       ram_nsec(fuc, 1000);
+
+       data  = ram_rd32(fuc, 0x10f978);
+       data &= ~0x00046144;
+       data |=  0x0000000b;
+       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x08)) {
+               if (!(nv_ro08(bios, ramcfg + 0x07) & 0x04))
+                       data |= 0x0000200c;
+               else
+                       data |= 0x00000000;
+       } else {
+               data |= 0x00040044;
+       }
+       ram_wr32(fuc, 0x10f978, data);
+
+       if (ram->mode == 1) {
+               data = ram_rd32(fuc, 0x10f830) | 0x00000001;
+               ram_wr32(fuc, 0x10f830, data);
+       }
+
+       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x08)) {
+               data = 0x88020000;
+               if ( (nv_ro08(bios, ramcfg + 0x07) & 0x04))
+                       data |= 0x10000000;
+               if (!(nv_ro08(bios, rammap + 0x08) & 0x10))
+                       data |= 0x00080000;
+       } else {
+               data = 0xa40e0000;
+       }
+       train(fuc, data);
+       ram_nsec(fuc, 1000);
+
+       if (ram->mode == 2) { /*XXX*/
+               ram_mask(fuc, 0x10f800, 0x00000004, 0x00000004);
+       }
+
+       /* MR5: (re)enable LP3 if necessary
+        * XXX: need to find the switch, keeping off for now
+        */
+       ram_mask(fuc, mr[5], 0x00000004, 0x00000000);
+
+       if (ram->mode != 2) {
+               ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000);
+               ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000);
+       }
+
+       if (nv_ro08(bios, ramcfg + 0x07) & 0x02) {
+               ram_mask(fuc, 0x10f910, 0x80020000, 0x01000000);
+               ram_mask(fuc, 0x10f914, 0x80020000, 0x01000000);
+       }
+
+       ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
+
+       if (nv_ro08(bios, rammap + 0x08) & 0x01)
+               data = 0x00000800;
+       else
+               data = 0x00000000;
+       ram_mask(fuc, 0x10f200, 0x00000800, data);
+       return 0;
+}
+
+/*******************************************************************************
+ * DDR3
+ ******************************************************************************/
+
+static int
+nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq)
+{
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nve0_ram *ram = (void *)pfb->ram;
+       struct nve0_ramfuc *fuc = &ram->fuc;
+       const u32 rcoef = ((  ram->P1 << 16) | (ram->N1 << 8) | ram->M1);
+       const u32 runk0 = ram->fN1 << 16;
+       const u32 runk1 = ram->fN1;
+       const u32 rammap = ram->base.rammap.data;
+       const u32 ramcfg = ram->base.ramcfg.data;
+       const u32 timing = ram->base.timing.data;
+       int vc = !(nv_ro08(bios, ramcfg + 0x02) & 0x08);
+       int mv = 1; /*XXX*/
+       u32 mask, data;
+
+       ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000);
+       ram_wr32(fuc, 0x62c000, 0x0f0f0000);
+
+       if (vc == 1 && ram_have(fuc, gpio2E)) {
+               u32 temp  = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]);
+               if (temp != ram_rd32(fuc, gpio2E)) {
+                       ram_wr32(fuc, gpiotrig, 1);
+                       ram_nsec(fuc, 20000);
+               }
+       }
+
+       ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000);
+       if ((nv_ro08(bios, ramcfg + 0x03) & 0xf0))
+               ram_mask(fuc, 0x10f808, 0x04000000, 0x04000000);
+
+       ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */
+       ram_wr32(fuc, 0x10f210, 0x00000000); /* REFRESH_AUTO = 0 */
+       ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */
+       ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000);
+       ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */
+       ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000);
+       ram_nsec(fuc, 1000);
+
+       ram_wr32(fuc, 0x10f090, 0x00000060);
+       ram_wr32(fuc, 0x10f090, 0xc000007e);
+
+       /*XXX: there does appear to be some kind of condition here, simply
+        *     modifying these bits in the vbios from the default pl0
+        *     entries shows no change.  however, the data does appear to
+        *     be correct and may be required for the transition back
+        */
+       mask = 0x00010000;
+       data = 0x00010000;
+
+       if (1) {
+               mask |= 0x800807e0;
+               data |= 0x800807e0;
+               switch (nv_ro08(bios, ramcfg + 0x03) & 0xc0) {
+               case 0xc0: data &= ~0x00000040; break;
+               case 0x80: data &= ~0x00000100; break;
+               case 0x40: data &= ~0x80000000; break;
+               case 0x00: data &= ~0x00000400; break;
+               }
+
+               switch (nv_ro08(bios, ramcfg + 0x03) & 0x30) {
+               case 0x30: data &= ~0x00000020; break;
+               case 0x20: data &= ~0x00000080; break;
+               case 0x10: data &= ~0x00080000; break;
+               case 0x00: data &= ~0x00000200; break;
+               }
+       }
+
+       if (nv_ro08(bios, ramcfg + 0x02) & 0x80)
+               mask |= 0x03000000;
+       if (nv_ro08(bios, ramcfg + 0x02) & 0x40)
+               mask |= 0x00002000;
+       if (nv_ro08(bios, ramcfg + 0x07) & 0x10)
+               mask |= 0x00004000;
+       if (nv_ro08(bios, ramcfg + 0x07) & 0x08)
+               mask |= 0x00000003;
+       else
+               mask |= 0x14000000;
+       ram_mask(fuc, 0x10f824, mask, data);
+
+       ram_mask(fuc, 0x132040, 0x00010000, 0x00000000);
+
+       ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010);
+       data  = ram_rd32(fuc, 0x1373ec) & ~0x00030000;
+       data |= (nv_ro08(bios, ramcfg + 0x03) & 0x30) << 12;
+       ram_wr32(fuc, 0x1373ec, data);
+       ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000);
+       ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000);
+
+       /* (re)program refpll, if required */
+       if ((ram_rd32(fuc, 0x132024) & 0xffffffff) != rcoef ||
+           (ram_rd32(fuc, 0x132034) & 0x0000ffff) != runk1) {
+               ram_mask(fuc, 0x132000, 0x00000001, 0x00000000);
+               ram_mask(fuc, 0x132020, 0x00000001, 0x00000000);
+               ram_wr32(fuc, 0x137320, 0x00000000);
+               ram_mask(fuc, 0x132030, 0xffff0000, runk0);
+               ram_mask(fuc, 0x132034, 0x0000ffff, runk1);
+               ram_wr32(fuc, 0x132024, rcoef);
+               ram_mask(fuc, 0x132028, 0x00080000, 0x00080000);
+               ram_mask(fuc, 0x132020, 0x00000001, 0x00000001);
+               ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000);
+               ram_mask(fuc, 0x132028, 0x00080000, 0x00000000);
+       }
+
+       ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000010);
+       ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000001);
+       ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000);
+
+       if (ram_have(fuc, gpioMV)) {
+               u32 temp  = ram_mask(fuc, gpioMV, 0x3000, fuc->r_funcMV[mv]);
+               if (temp != ram_rd32(fuc, gpioMV)) {
+                       ram_wr32(fuc, gpiotrig, 1);
+                       ram_nsec(fuc, 64000);
+               }
+       }
+
+       if ( (nv_ro08(bios, ramcfg + 0x02) & 0x40) ||
+            (nv_ro08(bios, ramcfg + 0x07) & 0x10)) {
+               ram_mask(fuc, 0x132040, 0x00010000, 0x00010000);
+               ram_nsec(fuc, 20000);
+       }
+
+       if (ram->mode != 2) /*XXX*/ {
+               if (nv_ro08(bios, ramcfg + 0x07) & 0x40)
+                       ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000);
+       }
+
+       data = (nv_ro08(bios, rammap + 0x11) & 0x0c) >> 2;
+       ram_wr32(fuc, 0x10f65c, 0x00000011 * data);
+       ram_wr32(fuc, 0x10f6b8, 0x01010101 * nv_ro08(bios, ramcfg + 0x09));
+       ram_wr32(fuc, 0x10f6bc, 0x01010101 * nv_ro08(bios, ramcfg + 0x09));
+
+       mask = 0x00010000;
+       data = 0x00000000;
+       if (!(nv_ro08(bios, ramcfg + 0x02) & 0x80))
+               data |= 0x03000000;
+       if (!(nv_ro08(bios, ramcfg + 0x02) & 0x40))
+               data |= 0x00002000;
+       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x10))
+               data |= 0x00004000;
+       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x08))
+               data |= 0x00000003;
+       else
+               data |= 0x14000000;
+       ram_mask(fuc, 0x10f824, mask, data);
+       ram_nsec(fuc, 1000);
+
+       if (nv_ro08(bios, ramcfg + 0x08) & 0x01)
+               data = 0x00100000;
+       else
+               data = 0x00000000;
+       ram_mask(fuc, 0x10f82c, 0x00100000, data);
+
+       /* PFB timing */
+       ram_mask(fuc, 0x10f248, 0xffffffff, nv_ro32(bios, timing + 0x28));
+       ram_mask(fuc, 0x10f290, 0xffffffff, nv_ro32(bios, timing + 0x00));
+       ram_mask(fuc, 0x10f294, 0xffffffff, nv_ro32(bios, timing + 0x04));
+       ram_mask(fuc, 0x10f298, 0xffffffff, nv_ro32(bios, timing + 0x08));
+       ram_mask(fuc, 0x10f29c, 0xffffffff, nv_ro32(bios, timing + 0x0c));
+       ram_mask(fuc, 0x10f2a0, 0xffffffff, nv_ro32(bios, timing + 0x10));
+       ram_mask(fuc, 0x10f2a4, 0xffffffff, nv_ro32(bios, timing + 0x14));
+       ram_mask(fuc, 0x10f2a8, 0xffffffff, nv_ro32(bios, timing + 0x18));
+       ram_mask(fuc, 0x10f2ac, 0xffffffff, nv_ro32(bios, timing + 0x1c));
+       ram_mask(fuc, 0x10f2cc, 0xffffffff, nv_ro32(bios, timing + 0x20));
+       ram_mask(fuc, 0x10f2e8, 0xffffffff, nv_ro32(bios, timing + 0x24));
+
+       mask = 0x33f00000;
+       data = 0x00000000;
+       if (!(nv_ro08(bios, ramcfg + 0x01) & 0x04))
+               data |= 0x20200000;
+       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x80))
+               data |= 0x12800000;
+       /*XXX: see note above about there probably being some condition
+        *     for the 10f824 stuff that uses ramcfg 3...
+        */
+       if ( (nv_ro08(bios, ramcfg + 0x03) & 0xf0)) {
+               if (nv_ro08(bios, rammap + 0x08) & 0x0c) {
+                       if (!(nv_ro08(bios, ramcfg + 0x07) & 0x80))
+                               mask |= 0x00000020;
+                       else
+                               data |= 0x00000020;
+                       mask |= 0x08000004;
+               }
+               data |= 0x04000000;
+       } else {
+               mask |= 0x44000020;
+               data |= 0x08000004;
+       }
+
+       ram_mask(fuc, 0x10f808, mask, data);
+
+       data = nv_ro08(bios, ramcfg + 0x03) & 0x0f;
+       ram_wr32(fuc, 0x10f870, 0x11111111 * data);
+
+       data = nv_ro16(bios, timing + 0x2c);
+       ram_mask(fuc, 0x10f250, 0x000003f0, (data & 0x003f) <<  4);
+
+       if (((nv_ro32(bios, timing + 0x2c) & 0x00001fc0) >>  6) >
+           ((nv_ro32(bios, timing + 0x28) & 0x7f000000) >> 24))
+               data = (nv_ro32(bios, timing + 0x2c) & 0x00001fc0) >>  6;
+       else
+               data = (nv_ro32(bios, timing + 0x28) & 0x1f000000) >> 24;
+       ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24);
+
+       data = nv_ro08(bios, timing + 0x30);
+       ram_mask(fuc, 0x10f224, 0x001f0000, (data & 0xf8) << 13);
+
+       ram_wr32(fuc, 0x10f090, 0x4000007f);
+       ram_nsec(fuc, 1000);
+
+       ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */
+       ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */
+       ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */
+       ram_nsec(fuc, 1000);
+
+       ram_nuke(fuc, mr[0]);
+       ram_mask(fuc, mr[0], 0x100, 0x100);
+       ram_mask(fuc, mr[0], 0x100, 0x000);
+
+       ram_mask(fuc, mr[2], 0xfff, ram->base.mr[2]);
+       ram_wr32(fuc, mr[0], ram->base.mr[0]);
+       ram_nsec(fuc, 1000);
+
+       ram_nuke(fuc, mr[0]);
+       ram_mask(fuc, mr[0], 0x100, 0x100);
+       ram_mask(fuc, mr[0], 0x100, 0x000);
+
+       if (vc == 0 && ram_have(fuc, gpio2E)) {
+               u32 temp  = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[0]);
+               if (temp != ram_rd32(fuc, gpio2E)) {
+                       ram_wr32(fuc, gpiotrig, 1);
+                       ram_nsec(fuc, 20000);
+               }
+       }
+
+       if (ram->mode != 2) {
+               ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000);
+               ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000);
+       }
+
+       ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000);
+       ram_wr32(fuc, 0x10f318, 0x00000001); /* NOP? */
+       ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000);
+       ram_nsec(fuc, 1000);
+
+       ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
+
+       if (nv_ro08(bios, rammap + 0x08) & 0x01)
+               data = 0x00000800;
+       else
+               data = 0x00000000;
+       ram_mask(fuc, 0x10f200, 0x00000800, data);
+       return 0;
+}
+
+/*******************************************************************************
+ * main hooks
+ ******************************************************************************/
+
+static int
+nve0_ram_calc(struct nouveau_fb *pfb, u32 freq)
+{
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nve0_ram *ram = (void *)pfb->ram;
+       struct nve0_ramfuc *fuc = &ram->fuc;
+       struct bit_entry M;
+       int ret, refclk, strap, i;
+       u32 data;
+       u8  cnt;
+
+       /* lookup memory config data relevant to the target frequency */
+       ram->base.rammap.data = nvbios_rammap_match(bios, freq / 1000,
+                                                  &ram->base.rammap.version,
+                                                  &ram->base.rammap.size, &cnt,
+                                                  &ram->base.ramcfg.size);
+       if (!ram->base.rammap.data || ram->base.rammap.version != 0x11 ||
+            ram->base.rammap.size < 0x09) {
+               nv_error(pfb, "invalid/missing rammap entry\n");
+               return -EINVAL;
+       }
+
+       /* locate specific data set for the attached memory */
+       if (bit_entry(bios, 'M', &M) || M.version != 2 || M.length < 3) {
+               nv_error(pfb, "invalid/missing memory table\n");
+               return -EINVAL;
+       }
+
+       strap = (nv_rd32(pfb, 0x101000) & 0x0000003c) >> 2;
+       data = nv_ro16(bios, M.offset + 1);
+       if (data)
+               strap = nv_ro08(bios, data + strap);
+
+       if (strap >= cnt) {
+               nv_error(pfb, "invalid ramcfg strap\n");
+               return -EINVAL;
+       }
+
+       ram->base.ramcfg.version = ram->base.rammap.version;
+       ram->base.ramcfg.data = ram->base.rammap.data + ram->base.rammap.size +
+                              (ram->base.ramcfg.size * strap);
+       if (!ram->base.ramcfg.data || ram->base.ramcfg.version != 0x11 ||
+            ram->base.ramcfg.size < 0x08) {
+               nv_error(pfb, "invalid/missing ramcfg entry\n");
+               return -EINVAL;
+       }
+
+       /* lookup memory timings, if bios says they're present */
+       strap = nv_ro08(bios, ram->base.ramcfg.data + 0x00);
+       if (strap != 0xff) {
+               ram->base.timing.data =
+                       nvbios_timing_entry(bios, strap,
+                                          &ram->base.timing.version,
+                                          &ram->base.timing.size);
+               if (!ram->base.timing.data ||
+                    ram->base.timing.version != 0x20 ||
+                    ram->base.timing.size < 0x33) {
+                       nv_error(pfb, "invalid/missing timing entry\n");
+                       return -EINVAL;
+               }
+       } else {
+               ram->base.timing.data = 0;
+       }
+
+       ret = ram_init(fuc, pfb);
+       if (ret)
+               return ret;
+
+       ram->mode = (freq > fuc->refpll.vco1.max_freq) ? 2 : 1;
+       ram->from = ram_rd32(fuc, 0x1373f4) & 0x0000000f;
+
+       /* XXX: this is *not* what nvidia do.  on fermi nvidia generally
+        * select, based on some unknown condition, one of the two possible
+        * reference frequencies listed in the vbios table for mempll and
+        * program refpll to that frequency.
+        *
+        * so far, i've seen very weird values being chosen by nvidia on
+        * kepler boards, no idea how/why they're chosen.
+        */
+       refclk = freq;
+       if (ram->mode == 2)
+               refclk = fuc->mempll.refclk;
+
+       /* calculate refpll coefficients */
+       ret = nva3_pll_calc(nv_subdev(pfb), &fuc->refpll, refclk, &ram->N1,
+                          &ram->fN1, &ram->M1, &ram->P1);
+       fuc->mempll.refclk = ret;
+       if (ret <= 0) {
+               nv_error(pfb, "unable to calc refpll\n");
+               return -EINVAL;
+       }
+
+       /* calculate mempll coefficients, if we're using it */
+       if (ram->mode == 2) {
+               /* post-divider doesn't work... the reg takes the values but
+                * appears to completely ignore it.  there *is* a bit at
+                * bit 28 that appears to divide the clock by 2 if set.
+                */
+               fuc->mempll.min_p = 1;
+               fuc->mempll.max_p = 2;
+
+               ret = nva3_pll_calc(nv_subdev(pfb), &fuc->mempll, freq,
+                                  &ram->N2, NULL, &ram->M2, &ram->P2);
+               if (ret <= 0) {
+                       nv_error(pfb, "unable to calc mempll\n");
+                       return -EINVAL;
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(fuc->r_mr); i++) {
+               if (ram_have(fuc, mr[i]))
+                       ram->base.mr[i] = ram_rd32(fuc, mr[i]);
+       }
+
+       switch (ram->base.type) {
+       case NV_MEM_TYPE_DDR3:
+               ret = nouveau_sddr3_calc(&ram->base);
+               if (ret == 0)
+                       ret = nve0_ram_calc_sddr3(pfb, freq);
+               break;
+       case NV_MEM_TYPE_GDDR5:
+               ret = nouveau_gddr5_calc(&ram->base);
+               if (ret == 0)
+                       ret = nve0_ram_calc_gddr5(pfb, freq);
+               break;
+       default:
+               ret = -ENOSYS;
+               break;
+       }
+
+       return ret;
+}
+
+static int
+nve0_ram_prog(struct nouveau_fb *pfb)
+{
+       struct nouveau_device *device = nv_device(pfb);
+       struct nve0_ram *ram = (void *)pfb->ram;
+       struct nve0_ramfuc *fuc = &ram->fuc;
+       ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+       return 0;
+}
+
+static void
+nve0_ram_tidy(struct nouveau_fb *pfb)
+{
+       struct nve0_ram *ram = (void *)pfb->ram;
+       struct nve0_ramfuc *fuc = &ram->fuc;
+       ram_exec(fuc, false);
+}
+
+static int
+nve0_ram_init(struct nouveau_object *object)
+{
+       struct nouveau_fb *pfb = (void *)object->parent;
+       struct nve0_ram *ram   = (void *)object;
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       static const u8  train0[] = {
+               0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
+               0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
+       };
+       static const u32 train1[] = {
+               0x00000000, 0xffffffff,
+               0x55555555, 0xaaaaaaaa,
+               0x33333333, 0xcccccccc,
+               0xf0f0f0f0, 0x0f0f0f0f,
+               0x00ff00ff, 0xff00ff00,
+               0x0000ffff, 0xffff0000,
+       };
+       u8  ver, hdr, cnt, len, snr, ssz;
+       u32 data, save;
+       int ret, i;
+
+       ret = nouveau_ram_init(&ram->base);
+       if (ret)
+               return ret;
+
+       /* run a bunch of tables from rammap table.  there's actually
+        * individual pointers for each rammap entry too, but, nvidia
+        * seem to just run the last two entries' scripts early on in
+        * their init, and never again.. we'll just run 'em all once
+        * for now.
+        *
+        * i strongly suspect that each script is for a separate mode
+        * (likely selected by 0x10f65c's lower bits?), and the
+        * binary driver skips the one that's already been setup by
+        * the init tables.
+        */
+       data = nvbios_rammap_table(bios, &ver, &hdr, &cnt, &len, &snr, &ssz);
+       if (!data || hdr < 0x15)
+               return -EINVAL;
+
+       cnt  = nv_ro08(bios, data + 0x14); /* guess at count */
+       data = nv_ro32(bios, data + 0x10); /* guess u32... */
+       save = nv_rd32(pfb, 0x10f65c);
+       for (i = 0; i < cnt; i++) {
+               nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4);
+               nvbios_exec(&(struct nvbios_init) {
+                               .subdev = nv_subdev(pfb),
+                               .bios = bios,
+                               .offset = nv_ro32(bios, data), /* guess u32 */
+                               .execute = 1,
+                           });
+               data += 4;
+       }
+       nv_wr32(pfb, 0x10f65c, save);
+
+       switch (ram->base.type) {
+       case NV_MEM_TYPE_GDDR5:
+               for (i = 0; i < 0x30; i++) {
+                       nv_wr32(pfb, 0x10f968, 0x00000000 | (i << 8));
+                       nv_wr32(pfb, 0x10f920, 0x00000000 | train0[i % 12]);
+                       nv_wr32(pfb, 0x10f918,              train1[i % 12]);
+                       nv_wr32(pfb, 0x10f920, 0x00000100 | train0[i % 12]);
+                       nv_wr32(pfb, 0x10f918,              train1[i % 12]);
+
+                       nv_wr32(pfb, 0x10f96c, 0x00000000 | (i << 8));
+                       nv_wr32(pfb, 0x10f924, 0x00000000 | train0[i % 12]);
+                       nv_wr32(pfb, 0x10f91c,              train1[i % 12]);
+                       nv_wr32(pfb, 0x10f924, 0x00000100 | train0[i % 12]);
+                       nv_wr32(pfb, 0x10f91c,              train1[i % 12]);
+               }
+
+               for (i = 0; i < 0x100; i++) {
+                       nv_wr32(pfb, 0x10f968, i);
+                       nv_wr32(pfb, 0x10f900, train1[2 + (i & 1)]);
+               }
+
+               for (i = 0; i < 0x100; i++) {
+                       nv_wr32(pfb, 0x10f96c, i);
+                       nv_wr32(pfb, 0x10f900, train1[2 + (i & 1)]);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int
+nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nouveau_fb *pfb = nouveau_fb(parent);
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nouveau_gpio *gpio = nouveau_gpio(pfb);
+       struct dcb_gpio_func func;
+       struct nve0_ram *ram;
+       int ret;
+
+       ret = nvc0_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       switch (ram->base.type) {
+       case NV_MEM_TYPE_DDR3:
+       case NV_MEM_TYPE_GDDR5:
+               ram->base.calc = nve0_ram_calc;
+               ram->base.prog = nve0_ram_prog;
+               ram->base.tidy = nve0_ram_tidy;
+               break;
+       default:
+               nv_warn(pfb, "reclocking of this RAM type is unsupported\n");
+               break;
+       }
+
+       // parse bios data for both pll's
+       ret = nvbios_pll_parse(bios, 0x0c, &ram->fuc.refpll);
+       if (ret) {
+               nv_error(pfb, "mclk refpll data not found\n");
+               return ret;
+       }
+
+       ret = nvbios_pll_parse(bios, 0x04, &ram->fuc.mempll);
+       if (ret) {
+               nv_error(pfb, "mclk pll data not found\n");
+               return ret;
+       }
+
+       ret = gpio->find(gpio, 0, 0x18, DCB_GPIO_UNUSED, &func);
+       if (ret == 0) {
+               ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (func.line * 0x04));
+               ram->fuc.r_funcMV[0] = (func.log[0] ^ 2) << 12;
+               ram->fuc.r_funcMV[1] = (func.log[1] ^ 2) << 12;
+       }
+
+       ret = gpio->find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func);
+       if (ret == 0) {
+               ram->fuc.r_gpio2E = ramfuc_reg(0x00d610 + (func.line * 0x04));
+               ram->fuc.r_func2E[0] = (func.log[0] ^ 2) << 12;
+               ram->fuc.r_func2E[1] = (func.log[1] ^ 2) << 12;
+       }
+
+       ram->fuc.r_gpiotrig = ramfuc_reg(0x00d604);
+
+       ram->fuc.r_0x132020 = ramfuc_reg(0x132020);
+       ram->fuc.r_0x132028 = ramfuc_reg(0x132028);
+       ram->fuc.r_0x132024 = ramfuc_reg(0x132024);
+       ram->fuc.r_0x132030 = ramfuc_reg(0x132030);
+       ram->fuc.r_0x132034 = ramfuc_reg(0x132034);
+       ram->fuc.r_0x132000 = ramfuc_reg(0x132000);
+       ram->fuc.r_0x132004 = ramfuc_reg(0x132004);
+       ram->fuc.r_0x132040 = ramfuc_reg(0x132040);
+
+       ram->fuc.r_0x10f248 = ramfuc_reg(0x10f248);
+       ram->fuc.r_0x10f290 = ramfuc_reg(0x10f290);
+       ram->fuc.r_0x10f294 = ramfuc_reg(0x10f294);
+       ram->fuc.r_0x10f298 = ramfuc_reg(0x10f298);
+       ram->fuc.r_0x10f29c = ramfuc_reg(0x10f29c);
+       ram->fuc.r_0x10f2a0 = ramfuc_reg(0x10f2a0);
+       ram->fuc.r_0x10f2a4 = ramfuc_reg(0x10f2a4);
+       ram->fuc.r_0x10f2a8 = ramfuc_reg(0x10f2a8);
+       ram->fuc.r_0x10f2ac = ramfuc_reg(0x10f2ac);
+       ram->fuc.r_0x10f2cc = ramfuc_reg(0x10f2cc);
+       ram->fuc.r_0x10f2e8 = ramfuc_reg(0x10f2e8);
+       ram->fuc.r_0x10f250 = ramfuc_reg(0x10f250);
+       ram->fuc.r_0x10f24c = ramfuc_reg(0x10f24c);
+       ram->fuc.r_0x10fec4 = ramfuc_reg(0x10fec4);
+       ram->fuc.r_0x10fec8 = ramfuc_reg(0x10fec8);
+       ram->fuc.r_0x10f604 = ramfuc_reg(0x10f604);
+       ram->fuc.r_0x10f614 = ramfuc_reg(0x10f614);
+       ram->fuc.r_0x10f610 = ramfuc_reg(0x10f610);
+       ram->fuc.r_0x100770 = ramfuc_reg(0x100770);
+       ram->fuc.r_0x100778 = ramfuc_reg(0x100778);
+       ram->fuc.r_0x10f224 = ramfuc_reg(0x10f224);
+
+       ram->fuc.r_0x10f870 = ramfuc_reg(0x10f870);
+       ram->fuc.r_0x10f698 = ramfuc_reg(0x10f698);
+       ram->fuc.r_0x10f694 = ramfuc_reg(0x10f694);
+       ram->fuc.r_0x10f6b8 = ramfuc_reg(0x10f6b8);
+       ram->fuc.r_0x10f808 = ramfuc_reg(0x10f808);
+       ram->fuc.r_0x10f670 = ramfuc_reg(0x10f670);
+       ram->fuc.r_0x10f60c = ramfuc_reg(0x10f60c);
+       ram->fuc.r_0x10f830 = ramfuc_reg(0x10f830);
+       ram->fuc.r_0x1373ec = ramfuc_reg(0x1373ec);
+       ram->fuc.r_0x10f800 = ramfuc_reg(0x10f800);
+       ram->fuc.r_0x10f82c = ramfuc_reg(0x10f82c);
+
+       ram->fuc.r_0x10f978 = ramfuc_reg(0x10f978);
+       ram->fuc.r_0x10f910 = ramfuc_reg(0x10f910);
+       ram->fuc.r_0x10f914 = ramfuc_reg(0x10f914);
+
+       switch (ram->base.type) {
+       case NV_MEM_TYPE_GDDR5:
+               ram->fuc.r_mr[0] = ramfuc_reg(0x10f300);
+               ram->fuc.r_mr[1] = ramfuc_reg(0x10f330);
+               ram->fuc.r_mr[2] = ramfuc_reg(0x10f334);
+               ram->fuc.r_mr[3] = ramfuc_reg(0x10f338);
+               ram->fuc.r_mr[4] = ramfuc_reg(0x10f33c);
+               ram->fuc.r_mr[5] = ramfuc_reg(0x10f340);
+               ram->fuc.r_mr[6] = ramfuc_reg(0x10f344);
+               ram->fuc.r_mr[7] = ramfuc_reg(0x10f348);
+               ram->fuc.r_mr[8] = ramfuc_reg(0x10f354);
+               ram->fuc.r_mr[15] = ramfuc_reg(0x10f34c);
+               break;
+       case NV_MEM_TYPE_DDR3:
+               ram->fuc.r_mr[0] = ramfuc_reg(0x10f300);
+               ram->fuc.r_mr[2] = ramfuc_reg(0x10f320);
+               break;
+       default:
+               break;
+       }
+
+       ram->fuc.r_0x62c000 = ramfuc_reg(0x62c000);
+       ram->fuc.r_0x10f200 = ramfuc_reg(0x10f200);
+       ram->fuc.r_0x10f210 = ramfuc_reg(0x10f210);
+       ram->fuc.r_0x10f310 = ramfuc_reg(0x10f310);
+       ram->fuc.r_0x10f314 = ramfuc_reg(0x10f314);
+       ram->fuc.r_0x10f318 = ramfuc_reg(0x10f318);
+       ram->fuc.r_0x10f090 = ramfuc_reg(0x10f090);
+       ram->fuc.r_0x10f69c = ramfuc_reg(0x10f69c);
+       ram->fuc.r_0x10f824 = ramfuc_reg(0x10f824);
+       ram->fuc.r_0x1373f0 = ramfuc_reg(0x1373f0);
+       ram->fuc.r_0x1373f4 = ramfuc_reg(0x1373f4);
+       ram->fuc.r_0x137320 = ramfuc_reg(0x137320);
+       ram->fuc.r_0x10f65c = ramfuc_reg(0x10f65c);
+       ram->fuc.r_0x10f6bc = ramfuc_reg(0x10f6bc);
+       ram->fuc.r_0x100710 = ramfuc_reg(0x100710);
+       ram->fuc.r_0x10f750 = ramfuc_reg(0x10f750);
+       return 0;
+}
+
+struct nouveau_oclass
+nve0_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nve0_ram_ctor,
+               .dtor = _nouveau_ram_dtor,
+               .init = nve0_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramseq.h b/drivers/gpu/drm/nouveau/core/subdev/fb/ramseq.h
new file mode 100644 (file)
index 0000000..571077e
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __NVKM_FBRAM_SEQ_H__
+#define __NVKM_FBRAM_SEQ_H__
+
+#include <subdev/bus.h>
+#include <subdev/bus/hwsq.h>
+
+#define ram_init(s,p)       hwsq_init(&(s)->base, (p))
+#define ram_exec(s,e)       hwsq_exec(&(s)->base, (e))
+#define ram_have(s,r)       ((s)->r_##r.addr != 0x000000)
+#define ram_rd32(s,r)       hwsq_rd32(&(s)->base, &(s)->r_##r)
+#define ram_wr32(s,r,d)     hwsq_wr32(&(s)->base, &(s)->r_##r, (d))
+#define ram_nuke(s,r)       hwsq_nuke(&(s)->base, &(s)->r_##r)
+#define ram_mask(s,r,m,d)   hwsq_mask(&(s)->base, &(s)->r_##r, (m), (d))
+#define ram_setf(s,f,d)     hwsq_setf(&(s)->base, (f), (d))
+#define ram_wait(s,f,d)     hwsq_wait(&(s)->base, (f), (d))
+#define ram_nsec(s,n)       hwsq_nsec(&(s)->base, (n))
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c
new file mode 100644 (file)
index 0000000..ebd4cd9
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <subdev/bios.h>
+#include "priv.h"
+
+struct ramxlat {
+       int id;
+       u8 enc;
+};
+
+static inline int
+ramxlat(const struct ramxlat *xlat, int id)
+{
+       while (xlat->id >= 0) {
+               if (xlat->id == id)
+                       return xlat->enc;
+               xlat++;
+       }
+       return -EINVAL;
+}
+
+static const struct ramxlat
+ramddr3_cl[] = {
+       { 5, 2 }, { 6, 4 }, { 7, 6 }, { 8, 8 }, { 9, 10 }, { 10, 12 },
+       { 11, 14 },
+       /* the below are mentioned in some, but not all, ddr3 docs */
+       { 12, 1 }, { 13, 3 }, { 14, 5 },
+       { -1 }
+};
+
+static const struct ramxlat
+ramddr3_wr[] = {
+       { 5, 1 }, { 6, 2 }, { 7, 3 }, { 8, 4 }, { 10, 5 }, { 12, 6 },
+       /* the below are mentioned in some, but not all, ddr3 docs */
+       { 14, 7 }, { 16, 0 },
+       { -1 }
+};
+
+static const struct ramxlat
+ramddr3_cwl[] = {
+       { 5, 0 }, { 6, 1 }, { 7, 2 }, { 8, 3 },
+       /* the below are mentioned in some, but not all, ddr3 docs */
+       { 9, 4 },
+       { -1 }
+};
+
+int
+nouveau_sddr3_calc(struct nouveau_ram *ram)
+{
+       struct nouveau_bios *bios = nouveau_bios(ram);
+       int WL, CL, WR;
+
+       switch (!!ram->timing.data * ram->timing.version) {
+       case 0x20:
+               WL = (nv_ro16(bios, ram->timing.data + 0x04) & 0x0f80) >> 7;
+               CL =  nv_ro08(bios, ram->timing.data + 0x04) & 0x1f;
+               WR =  nv_ro08(bios, ram->timing.data + 0x0a) & 0x7f;
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       WL = ramxlat(ramddr3_cwl, WL);
+       CL = ramxlat(ramddr3_cl, CL);
+       WR = ramxlat(ramddr3_wr, WR);
+       if (WL < 0 || CL < 0 || WR < 0)
+               return -EINVAL;
+
+       ram->mr[0] &= ~0xe74;
+       ram->mr[0] |= (WR & 0x07) << 9;
+       ram->mr[0] |= (CL & 0x0e) << 3;
+       ram->mr[0] |= (CL & 0x01) << 2;
+
+       ram->mr[2] &= ~0x038;
+       ram->mr[2] |= (WL & 0x07) << 3;
+       return 0;
+}
index d422acc..f572c28 100644 (file)
@@ -67,7 +67,7 @@ nouveau_gpio_find(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
                }
        }
 
-       return -EINVAL;
+       return -ENOENT;
 }
 
 static int
index 2895c19..041fd5e 100644 (file)
@@ -195,7 +195,7 @@ nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type)
 
 static int
 nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
-                    struct i2c_board_info *info,
+                    struct nouveau_i2c_board_info *info,
                     bool (*match)(struct nouveau_i2c_port *,
                                   struct i2c_board_info *))
 {
@@ -208,12 +208,29 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
        }
 
        nv_debug(i2c, "probing %ss on bus: %d\n", what, port->index);
-       for (i = 0; info[i].addr; i++) {
-               if (nv_probe_i2c(port, info[i].addr) &&
-                   (!match || match(port, &info[i]))) {
-                       nv_info(i2c, "detected %s: %s\n", what, info[i].type);
+       for (i = 0; info[i].dev.addr; i++) {
+               u8 orig_udelay = 0;
+
+               if ((port->adapter.algo == &i2c_bit_algo) &&
+                   (info[i].udelay != 0)) {
+                       struct i2c_algo_bit_data *algo = port->adapter.algo_data;
+                       nv_debug(i2c, "using custom udelay %d instead of %d\n",
+                                info[i].udelay, algo->udelay);
+                       orig_udelay = algo->udelay;
+                       algo->udelay = info[i].udelay;
+               }
+
+               if (nv_probe_i2c(port, info[i].dev.addr) &&
+                   (!match || match(port, &info[i].dev))) {
+                       nv_info(i2c, "detected %s: %s\n", what,
+                               info[i].dev.type);
                        return i;
                }
+
+               if (orig_udelay) {
+                       struct i2c_algo_bit_data *algo = port->adapter.algo_data;
+                       algo->udelay = orig_udelay;
+               }
        }
 
        nv_debug(i2c, "no devices found.\n");
index e290cfa..b4b9943 100644 (file)
 #include <subdev/mc.h>
 #include <core/option.h>
 
+static inline u32
+nouveau_mc_intr_mask(struct nouveau_mc *pmc)
+{
+       u32 intr = nv_rd32(pmc, 0x000100);
+       if (intr == 0xffffffff) /* likely fallen off the bus */
+               intr = 0x00000000;
+       return intr;
+}
+
 static irqreturn_t
 nouveau_mc_intr(int irq, void *arg)
 {
        struct nouveau_mc *pmc = arg;
-       const struct nouveau_mc_intr *map = pmc->intr_map;
-       struct nouveau_device *device = nv_device(pmc);
+       const struct nouveau_mc_oclass *oclass = (void *)nv_object(pmc)->oclass;
+       const struct nouveau_mc_intr *map = oclass->intr;
        struct nouveau_subdev *unit;
-       u32 stat, intr;
-
-       intr = stat = nv_rd32(pmc, 0x000100);
-       if (intr == 0xffffffff)
-               return IRQ_NONE;
-       while (stat && map->stat) {
-               if (stat & map->stat) {
-                       unit = nouveau_subdev(pmc, map->unit);
-                       if (unit && unit->intr)
-                               unit->intr(unit);
-                       intr &= ~map->stat;
-               }
-               map++;
-       }
+       u32 intr;
 
+       nv_wr32(pmc, 0x000140, 0x00000000);
+       nv_rd32(pmc, 0x000140);
+       intr = nouveau_mc_intr_mask(pmc);
        if (pmc->use_msi)
-               nv_wr08(pmc->base.base.parent, 0x00088068, 0xff);
+               oclass->msi_rearm(pmc);
 
        if (intr) {
-               nv_error(pmc, "unknown intr 0x%08x\n", stat);
+               u32 stat = intr = nouveau_mc_intr_mask(pmc);
+               while (map->stat) {
+                       if (intr & map->stat) {
+                               unit = nouveau_subdev(pmc, map->unit);
+                               if (unit && unit->intr)
+                                       unit->intr(unit);
+                               stat &= ~map->stat;
+                       }
+                       map++;
+               }
+
+               if (stat)
+                       nv_error(pmc, "unknown intr 0x%08x\n", stat);
        }
 
-       if (stat == IRQ_HANDLED)
-               pm_runtime_mark_last_busy(&device->pdev->dev);
-       return stat ? IRQ_HANDLED : IRQ_NONE;
+       nv_wr32(pmc, 0x000140, 0x00000001);
+       return intr ? IRQ_HANDLED : IRQ_NONE;
 }
 
 int
@@ -91,37 +101,42 @@ _nouveau_mc_dtor(struct nouveau_object *object)
 
 int
 nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine,
-                  struct nouveau_oclass *oclass,
-                  const struct nouveau_mc_intr *intr_map,
-                  int length, void **pobject)
+                  struct nouveau_oclass *bclass, int length, void **pobject)
 {
+       const struct nouveau_mc_oclass *oclass = (void *)bclass;
        struct nouveau_device *device = nv_device(parent);
        struct nouveau_mc *pmc;
        int ret;
 
-       ret = nouveau_subdev_create_(parent, engine, oclass, 0, "PMC",
+       ret = nouveau_subdev_create_(parent, engine, bclass, 0, "PMC",
                                     "master", length, pobject);
        pmc = *pobject;
        if (ret)
                return ret;
 
-       pmc->intr_map = intr_map;
-
        switch (device->pdev->device & 0x0ff0) {
-       case 0x00f0: /* BR02? */
-       case 0x02e0: /* BR02? */
-               pmc->use_msi = false;
+       case 0x00f0:
+       case 0x02e0:
+               /* BR02? NFI how these would be handled yet exactly */
                break;
        default:
-               pmc->use_msi = nouveau_boolopt(device->cfgopt, "NvMSI", false);
+               switch (device->chipset) {
+               case 0xaa: break; /* reported broken, nv also disable it */
+               default:
+                       pmc->use_msi = true;
+                       break;
+               }
+       }
+
+       pmc->use_msi = nouveau_boolopt(device->cfgopt, "NvMSI", pmc->use_msi);
+       if (pmc->use_msi && oclass->msi_rearm) {
+               pmc->use_msi = pci_enable_msi(device->pdev) == 0;
                if (pmc->use_msi) {
-                       pmc->use_msi = pci_enable_msi(device->pdev) == 0;
-                       if (pmc->use_msi) {
-                               nv_info(pmc, "MSI interrupts enabled\n");
-                               nv_wr08(device, 0x00088068, 0xff);
-                       }
+                       nv_info(pmc, "MSI interrupts enabled\n");
+                       oclass->msi_rearm(pmc);
                }
-               break;
+       } else {
+               pmc->use_msi = false;
        }
 
        ret = request_irq(device->pdev->irq, nouveau_mc_intr,
index 64aa4ed..2d787e4 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/mc.h>
-
-struct nv04_mc_priv {
-       struct nouveau_mc base;
-};
+#include "nv04.h"
 
 const struct nouveau_mc_intr
 nv04_mc_intr[] = {
        { 0x00000001, NVDEV_ENGINE_MPEG },      /* NV17- MPEG/ME */
        { 0x00000100, NVDEV_ENGINE_FIFO },
        { 0x00001000, NVDEV_ENGINE_GR },
+       { 0x00010000, NVDEV_ENGINE_DISP },
        { 0x00020000, NVDEV_ENGINE_VP },        /* NV40- */
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x01000000, NVDEV_ENGINE_DISP },      /* NV04- PCRTC0 */
@@ -42,7 +39,18 @@ nv04_mc_intr[] = {
        {}
 };
 
-static int
+int
+nv04_mc_init(struct nouveau_object *object)
+{
+       struct nv04_mc_priv *priv = (void *)object;
+
+       nv_wr32(priv, 0x000200, 0xffffffff); /* everything enabled */
+       nv_wr32(priv, 0x001850, 0x00000001); /* disable rom access */
+
+       return nouveau_mc_init(&priv->base);
+}
+
+int
 nv04_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
             struct nouveau_oclass *oclass, void *data, u32 size,
             struct nouveau_object **pobject)
@@ -50,7 +58,7 @@ nv04_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv04_mc_priv *priv;
        int ret;
 
-       ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv);
+       ret = nouveau_mc_create(parent, engine, oclass, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -58,24 +66,14 @@ nv04_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        return 0;
 }
 
-int
-nv04_mc_init(struct nouveau_object *object)
-{
-       struct nv04_mc_priv *priv = (void *)object;
-
-       nv_wr32(priv, 0x000200, 0xffffffff); /* everything enabled */
-       nv_wr32(priv, 0x001850, 0x00000001); /* disable rom access */
-
-       return nouveau_mc_init(&priv->base);
-}
-
-struct nouveau_oclass
-nv04_mc_oclass = {
-       .handle = NV_SUBDEV(MC, 0x04),
-       .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nv04_mc_oclass = &(struct nouveau_mc_oclass) {
+       .base.handle = NV_SUBDEV(MC, 0x04),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv04_mc_ctor,
                .dtor = _nouveau_mc_dtor,
                .init = nv04_mc_init,
                .fini = _nouveau_mc_fini,
        },
-};
+       .intr = nv04_mc_intr,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.h b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.h
new file mode 100644 (file)
index 0000000..b0d5c31
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef __NVKM_MC_NV04_H__
+#define __NVKM_MC_NV04_H__
+
+#include <subdev/mc.h>
+
+struct nv04_mc_priv {
+       struct nouveau_mc base;
+};
+
+int  nv04_mc_ctor(struct nouveau_object *, struct nouveau_object *,
+                 struct nouveau_oclass *, void *, u32,
+                 struct nouveau_object **);
+
+extern const struct nouveau_mc_intr nv04_mc_intr[];
+int  nv04_mc_init(struct nouveau_object *);
+void nv40_mc_msi_rearm(struct nouveau_mc *);
+int  nv50_mc_init(struct nouveau_object *);
+extern const struct nouveau_mc_intr nv50_mc_intr[];
+extern const struct nouveau_mc_intr nvc0_mc_intr[];
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv40.c
new file mode 100644 (file)
index 0000000..5b1faec
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv04.h"
+
+void
+nv40_mc_msi_rearm(struct nouveau_mc *pmc)
+{
+       struct nv04_mc_priv *priv = (void *)pmc;
+       nv_wr08(priv, 0x088068, 0xff);
+}
+
+struct nouveau_oclass *
+nv40_mc_oclass = &(struct nouveau_mc_oclass) {
+       .base.handle = NV_SUBDEV(MC, 0x40),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_mc_ctor,
+               .dtor = _nouveau_mc_dtor,
+               .init = nv04_mc_init,
+               .fini = _nouveau_mc_fini,
+       },
+       .intr = nv04_mc_intr,
+       .msi_rearm = nv40_mc_msi_rearm,
+}.base;
index d989178..3bfee5c 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/mc.h>
-
-struct nv44_mc_priv {
-       struct nouveau_mc base;
-};
-
-static int
-nv44_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv44_mc_priv *priv;
-       int ret;
-
-       ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       return 0;
-}
+#include "nv04.h"
 
 static int
 nv44_mc_init(struct nouveau_object *object)
 {
-       struct nv44_mc_priv *priv = (void *)object;
+       struct nv04_mc_priv *priv = (void *)object;
        u32 tmp = nv_rd32(priv, 0x10020c);
 
        nv_wr32(priv, 0x000200, 0xffffffff); /* everything enabled */
@@ -60,13 +40,15 @@ nv44_mc_init(struct nouveau_object *object)
        return nouveau_mc_init(&priv->base);
 }
 
-struct nouveau_oclass
-nv44_mc_oclass = {
-       .handle = NV_SUBDEV(MC, 0x44),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv44_mc_ctor,
+struct nouveau_oclass *
+nv44_mc_oclass = &(struct nouveau_mc_oclass) {
+       .base.handle = NV_SUBDEV(MC, 0x44),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_mc_ctor,
                .dtor = _nouveau_mc_dtor,
                .init = nv44_mc_init,
                .fini = _nouveau_mc_fini,
        },
-};
+       .intr = nv04_mc_intr,
+       .msi_rearm = nv40_mc_msi_rearm,
+}.base;
index 2b1afe2..e8822a9 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/mc.h>
+#include "nv04.h"
 
-struct nv50_mc_priv {
-       struct nouveau_mc base;
-};
-
-static const struct nouveau_mc_intr
+const struct nouveau_mc_intr
 nv50_mc_intr[] = {
        { 0x00000001, NVDEV_ENGINE_MPEG },
        { 0x00000100, NVDEV_ENGINE_FIFO },
@@ -45,37 +41,30 @@ nv50_mc_intr[] = {
        {},
 };
 
-static int
-nv50_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
+static void
+nv50_mc_msi_rearm(struct nouveau_mc *pmc)
 {
-       struct nv50_mc_priv *priv;
-       int ret;
-
-       ret = nouveau_mc_create(parent, engine, oclass, nv50_mc_intr, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       return 0;
+       struct nouveau_device *device = nv_device(pmc);
+       pci_write_config_byte(device->pdev, 0x68, 0xff);
 }
 
 int
 nv50_mc_init(struct nouveau_object *object)
 {
-       struct nv50_mc_priv *priv = (void *)object;
+       struct nv04_mc_priv *priv = (void *)object;
        nv_wr32(priv, 0x000200, 0xffffffff); /* everything on */
        return nouveau_mc_init(&priv->base);
 }
 
-struct nouveau_oclass
-nv50_mc_oclass = {
-       .handle = NV_SUBDEV(MC, 0x50),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv50_mc_ctor,
+struct nouveau_oclass *
+nv50_mc_oclass = &(struct nouveau_mc_oclass) {
+       .base.handle = NV_SUBDEV(MC, 0x50),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_mc_ctor,
                .dtor = _nouveau_mc_dtor,
                .init = nv50_mc_init,
                .fini = _nouveau_mc_fini,
        },
-};
+       .intr = nv50_mc_intr,
+       .msi_rearm = nv50_mc_msi_rearm,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv94.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv94.c
new file mode 100644 (file)
index 0000000..5f45411
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv04.h"
+
+struct nouveau_oclass *
+nv94_mc_oclass = &(struct nouveau_mc_oclass) {
+       .base.handle = NV_SUBDEV(MC, 0x94),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_mc_ctor,
+               .dtor = _nouveau_mc_dtor,
+               .init = nv50_mc_init,
+               .fini = _nouveau_mc_fini,
+       },
+       .intr = nv50_mc_intr,
+       .msi_rearm = nv40_mc_msi_rearm,
+}.base;
index 0671041..f8a6f18 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/mc.h>
-
-struct nv98_mc_priv {
-       struct nouveau_mc base;
-};
+#include "nv04.h"
 
 static const struct nouveau_mc_intr
 nv98_mc_intr[] = {
@@ -36,6 +32,7 @@ nv98_mc_intr[] = {
        { 0x00004000, NVDEV_ENGINE_CRYPT },     /* NV84:NVA3 */
        { 0x00008000, NVDEV_ENGINE_BSP },
        { 0x00020000, NVDEV_ENGINE_VP },
+       { 0x00040000, NVDEV_SUBDEV_PWR },       /* NVA3:NVC0 */
        { 0x00080000, NVDEV_SUBDEV_THERM },     /* NVA3:NVC0 */
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x00200000, NVDEV_SUBDEV_GPIO },
@@ -47,29 +44,15 @@ nv98_mc_intr[] = {
        {},
 };
 
-static int
-nv98_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
-{
-       struct nv98_mc_priv *priv;
-       int ret;
-
-       ret = nouveau_mc_create(parent, engine, oclass, nv98_mc_intr, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-struct nouveau_oclass
-nv98_mc_oclass = {
-       .handle = NV_SUBDEV(MC, 0x98),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv98_mc_ctor,
+struct nouveau_oclass *
+nv98_mc_oclass = &(struct nouveau_mc_oclass) {
+       .base.handle = NV_SUBDEV(MC, 0x98),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_mc_ctor,
                .dtor = _nouveau_mc_dtor,
                .init = nv50_mc_init,
                .fini = _nouveau_mc_fini,
        },
-};
+       .intr = nv98_mc_intr,
+       .msi_rearm = nv40_mc_msi_rearm,
+}.base;
index 104175c..c02b476 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/mc.h>
+#include "nv04.h"
 
-struct nvc0_mc_priv {
-       struct nouveau_mc base;
-};
-
-static const struct nouveau_mc_intr
+const struct nouveau_mc_intr
 nvc0_mc_intr[] = {
        { 0x00000001, NVDEV_ENGINE_PPP },
        { 0x00000020, NVDEV_ENGINE_COPY0 },
@@ -41,6 +37,7 @@ nvc0_mc_intr[] = {
        { 0x00020000, NVDEV_ENGINE_VP },
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x00200000, NVDEV_SUBDEV_GPIO },
+       { 0x01000000, NVDEV_SUBDEV_PWR },
        { 0x02000000, NVDEV_SUBDEV_LTCG },
        { 0x04000000, NVDEV_ENGINE_DISP },
        { 0x10000000, NVDEV_SUBDEV_BUS },
@@ -49,29 +46,22 @@ nvc0_mc_intr[] = {
        {},
 };
 
-static int
-nvc0_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-            struct nouveau_oclass *oclass, void *data, u32 size,
-            struct nouveau_object **pobject)
+static void
+nvc0_mc_msi_rearm(struct nouveau_mc *pmc)
 {
-       struct nvc0_mc_priv *priv;
-       int ret;
-
-       ret = nouveau_mc_create(parent, engine, oclass, nvc0_mc_intr, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       return 0;
+       struct nv04_mc_priv *priv = (void *)pmc;
+       nv_wr32(priv, 0x088704, 0x00000000);
 }
 
-struct nouveau_oclass
-nvc0_mc_oclass = {
-       .handle = NV_SUBDEV(MC, 0xc0),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nvc0_mc_ctor,
+struct nouveau_oclass *
+nvc0_mc_oclass = &(struct nouveau_mc_oclass) {
+       .base.handle = NV_SUBDEV(MC, 0xc0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_mc_ctor,
                .dtor = _nouveau_mc_dtor,
                .init = nv50_mc_init,
                .fini = _nouveau_mc_fini,
        },
-};
+       .intr = nvc0_mc_intr,
+       .msi_rearm = nvc0_mc_msi_rearm,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc3.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc3.c
new file mode 100644 (file)
index 0000000..837e545
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv04.h"
+
+struct nouveau_oclass *
+nvc3_mc_oclass = &(struct nouveau_mc_oclass) {
+       .base.handle = NV_SUBDEV(MC, 0xc3),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_mc_ctor,
+               .dtor = _nouveau_mc_dtor,
+               .init = nv50_mc_init,
+               .fini = _nouveau_mc_fini,
+       },
+       .intr = nvc0_mc_intr,
+       .msi_rearm = nv40_mc_msi_rearm,
+}.base;
index e286e13..1291204 100644 (file)
@@ -116,7 +116,7 @@ mxm_shadow_dsm(struct nouveau_mxm *mxm, u8 version)
        acpi_handle handle;
        int ret;
 
-       handle = DEVICE_ACPI_HANDLE(&device->pdev->dev);
+       handle = ACPI_HANDLE(&device->pdev->dev);
        if (!handle)
                return false;
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/base.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/base.c
new file mode 100644 (file)
index 0000000..d4fd3bc
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/pwr.h>
+#include <subdev/timer.h>
+
+static int
+nouveau_pwr_send(struct nouveau_pwr *ppwr, u32 reply[2],
+                u32 process, u32 message, u32 data0, u32 data1)
+{
+       struct nouveau_subdev *subdev = nv_subdev(ppwr);
+       u32 addr;
+
+       /* wait for a free slot in the fifo */
+       addr  = nv_rd32(ppwr, 0x10a4a0);
+       if (!nv_wait_ne(ppwr, 0x10a4b0, 0xffffffff, addr ^ 8))
+               return -EBUSY;
+
+       /* we currently only support a single process at a time waiting
+        * on a synchronous reply, take the PPWR mutex and tell the
+        * receive handler what we're waiting for
+        */
+       if (reply) {
+               mutex_lock(&subdev->mutex);
+               ppwr->recv.message = message;
+               ppwr->recv.process = process;
+       }
+
+       /* acquire data segment access */
+       do {
+               nv_wr32(ppwr, 0x10a580, 0x00000001);
+       } while (nv_rd32(ppwr, 0x10a580) != 0x00000001);
+
+       /* write the packet */
+       nv_wr32(ppwr, 0x10a1c0, 0x01000000 | (((addr & 0x07) << 4) +
+                               ppwr->send.base));
+       nv_wr32(ppwr, 0x10a1c4, process);
+       nv_wr32(ppwr, 0x10a1c4, message);
+       nv_wr32(ppwr, 0x10a1c4, data0);
+       nv_wr32(ppwr, 0x10a1c4, data1);
+       nv_wr32(ppwr, 0x10a4a0, (addr + 1) & 0x0f);
+
+       /* release data segment access */
+       nv_wr32(ppwr, 0x10a580, 0x00000000);
+
+       /* wait for reply, if requested */
+       if (reply) {
+               wait_event(ppwr->recv.wait, (ppwr->recv.process == 0));
+               reply[0] = ppwr->recv.data[0];
+               reply[1] = ppwr->recv.data[1];
+               mutex_unlock(&subdev->mutex);
+       }
+
+       return 0;
+}
+
+static void
+nouveau_pwr_recv(struct work_struct *work)
+{
+       struct nouveau_pwr *ppwr =
+               container_of(work, struct nouveau_pwr, recv.work);
+       u32 process, message, data0, data1;
+
+       /* nothing to do if GET == PUT */
+       u32 addr =  nv_rd32(ppwr, 0x10a4cc);
+       if (addr == nv_rd32(ppwr, 0x10a4c8))
+               return;
+
+       /* acquire data segment access */
+       do {
+               nv_wr32(ppwr, 0x10a580, 0x00000002);
+       } while (nv_rd32(ppwr, 0x10a580) != 0x00000002);
+
+       /* read the packet */
+       nv_wr32(ppwr, 0x10a1c0, 0x02000000 | (((addr & 0x07) << 4) +
+                               ppwr->recv.base));
+       process = nv_rd32(ppwr, 0x10a1c4);
+       message = nv_rd32(ppwr, 0x10a1c4);
+       data0   = nv_rd32(ppwr, 0x10a1c4);
+       data1   = nv_rd32(ppwr, 0x10a1c4);
+       nv_wr32(ppwr, 0x10a4cc, (addr + 1) & 0x0f);
+
+       /* release data segment access */
+       nv_wr32(ppwr, 0x10a580, 0x00000000);
+
+       /* wake process if it's waiting on a synchronous reply */
+       if (ppwr->recv.process) {
+               if (process == ppwr->recv.process &&
+                   message == ppwr->recv.message) {
+                       ppwr->recv.data[0] = data0;
+                       ppwr->recv.data[1] = data1;
+                       ppwr->recv.process = 0;
+                       wake_up(&ppwr->recv.wait);
+                       return;
+               }
+       }
+
+       /* right now there's no other expected responses from the engine,
+        * so assume that any unexpected message is an error.
+        */
+       nv_warn(ppwr, "%c%c%c%c 0x%08x 0x%08x 0x%08x 0x%08x\n",
+               (char)((process & 0x000000ff) >>  0),
+               (char)((process & 0x0000ff00) >>  8),
+               (char)((process & 0x00ff0000) >> 16),
+               (char)((process & 0xff000000) >> 24),
+               process, message, data0, data1);
+}
+
+static void
+nouveau_pwr_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_pwr *ppwr = (void *)subdev;
+       u32 disp = nv_rd32(ppwr, 0x10a01c);
+       u32 intr = nv_rd32(ppwr, 0x10a008) & disp & ~(disp >> 16);
+
+       if (intr & 0x00000020) {
+               u32 stat = nv_rd32(ppwr, 0x10a16c);
+               if (stat & 0x80000000) {
+                       nv_error(ppwr, "UAS fault at 0x%06x addr 0x%08x\n",
+                                stat & 0x00ffffff, nv_rd32(ppwr, 0x10a168));
+                       nv_wr32(ppwr, 0x10a16c, 0x00000000);
+                       intr &= ~0x00000020;
+               }
+       }
+
+       if (intr & 0x00000040) {
+               schedule_work(&ppwr->recv.work);
+               nv_wr32(ppwr, 0x10a004, 0x00000040);
+               intr &= ~0x00000040;
+       }
+
+       if (intr & 0x00000080) {
+               nv_info(ppwr, "wr32 0x%06x 0x%08x\n", nv_rd32(ppwr, 0x10a7a0),
+                                                     nv_rd32(ppwr, 0x10a7a4));
+               nv_wr32(ppwr, 0x10a004, 0x00000080);
+               intr &= ~0x00000080;
+       }
+
+       if (intr) {
+               nv_error(ppwr, "intr 0x%08x\n", intr);
+               nv_wr32(ppwr, 0x10a004, intr);
+       }
+}
+
+int
+_nouveau_pwr_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_pwr *ppwr = (void *)object;
+
+       nv_wr32(ppwr, 0x10a014, 0x00000060);
+       flush_work(&ppwr->recv.work);
+
+       return nouveau_subdev_fini(&ppwr->base, suspend);
+}
+
+int
+_nouveau_pwr_init(struct nouveau_object *object)
+{
+       struct nouveau_pwr *ppwr = (void *)object;
+       int ret, i;
+
+       ret = nouveau_subdev_init(&ppwr->base);
+       if (ret)
+               return ret;
+
+       nv_subdev(ppwr)->intr = nouveau_pwr_intr;
+       ppwr->message = nouveau_pwr_send;
+
+       /* prevent previous ucode from running, wait for idle, reset */
+       nv_wr32(ppwr, 0x10a014, 0x0000ffff); /* INTR_EN_CLR = ALL */
+       nv_wait(ppwr, 0x10a04c, 0xffffffff, 0x00000000);
+       nv_mask(ppwr, 0x000200, 0x00002000, 0x00000000);
+       nv_mask(ppwr, 0x000200, 0x00002000, 0x00002000);
+
+       /* upload data segment */
+       nv_wr32(ppwr, 0x10a1c0, 0x01000000);
+       for (i = 0; i < ppwr->data.size / 4; i++)
+               nv_wr32(ppwr, 0x10a1c4, ppwr->data.data[i]);
+
+       /* upload code segment */
+       nv_wr32(ppwr, 0x10a180, 0x01000000);
+       for (i = 0; i < ppwr->code.size / 4; i++) {
+               if ((i & 0x3f) == 0)
+                       nv_wr32(ppwr, 0x10a188, i >> 6);
+               nv_wr32(ppwr, 0x10a184, ppwr->code.data[i]);
+       }
+
+       /* start it running */
+       nv_wr32(ppwr, 0x10a10c, 0x00000000);
+       nv_wr32(ppwr, 0x10a104, 0x00000000);
+       nv_wr32(ppwr, 0x10a100, 0x00000002);
+
+       /* wait for valid host->pwr ring configuration */
+       if (!nv_wait_ne(ppwr, 0x10a4d0, 0xffffffff, 0x00000000))
+               return -EBUSY;
+       ppwr->send.base = nv_rd32(ppwr, 0x10a4d0) & 0x0000ffff;
+       ppwr->send.size = nv_rd32(ppwr, 0x10a4d0) >> 16;
+
+       /* wait for valid pwr->host ring configuration */
+       if (!nv_wait_ne(ppwr, 0x10a4dc, 0xffffffff, 0x00000000))
+               return -EBUSY;
+       ppwr->recv.base = nv_rd32(ppwr, 0x10a4dc) & 0x0000ffff;
+       ppwr->recv.size = nv_rd32(ppwr, 0x10a4dc) >> 16;
+
+       nv_wr32(ppwr, 0x10a010, 0x000000e0);
+       return 0;
+}
+
+int
+nouveau_pwr_create_(struct nouveau_object *parent,
+                   struct nouveau_object *engine,
+                   struct nouveau_oclass *oclass, int length, void **pobject)
+{
+       struct nouveau_pwr *ppwr;
+       int ret;
+
+       ret = nouveau_subdev_create_(parent, engine, oclass, 0, "PPWR",
+                                    "pwr", length, pobject);
+       ppwr = *pobject;
+       if (ret)
+               return ret;
+
+       INIT_WORK(&ppwr->recv.work, nouveau_pwr_recv);
+       init_waitqueue_head(&ppwr->recv.wait);
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/host.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/host.fuc
new file mode 100644 (file)
index 0000000..2284ecb
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifdef INCLUDE_PROC
+process(PROC_HOST, #host_init, #host_recv)
+#endif
+
+/******************************************************************************
+ * HOST data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+// HOST (R)FIFO packet format
+.equ #fifo_process 0x00
+.equ #fifo_message 0x04
+.equ #fifo_data0   0x08
+.equ #fifo_data1   0x0c
+
+// HOST HOST->PWR queue description
+.equ #fifo_qlen 4 // log2(size of queue entry in bytes)
+.equ #fifo_qnum 3 // log2(max number of entries in queue)
+.equ #fifo_qmaskb (1 << #fifo_qnum) // max number of entries in queue
+.equ #fifo_qmaskp (#fifo_qmaskb - 1)
+.equ #fifo_qmaskf ((#fifo_qmaskb << 1) - 1)
+.equ #fifo_qsize  (1 << (#fifo_qlen + #fifo_qnum))
+fifo_queue: .skip 128 // #fifo_qsize
+
+// HOST PWR->HOST queue description
+.equ #rfifo_qlen 4 // log2(size of queue entry in bytes)
+.equ #rfifo_qnum 3 // log2(max number of entries in queue)
+.equ #rfifo_qmaskb (1 << #rfifo_qnum) // max number of entries in queue
+.equ #rfifo_qmaskp (#rfifo_qmaskb - 1)
+.equ #rfifo_qmaskf ((#rfifo_qmaskb << 1) - 1)
+.equ #rfifo_qsize  (1 << (#rfifo_qlen + #rfifo_qnum))
+rfifo_queue: .skip 128 // #rfifo_qsize
+#endif
+
+/******************************************************************************
+ * HOST code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+// HOST->PWR comms - dequeue message(s) for process(es) from FIFO
+//
+// $r15 - current (host)
+// $r0  - zero
+host_send:
+       nv_iord($r1, NV_PPWR_FIFO_GET(0))
+       nv_iord($r2, NV_PPWR_FIFO_PUT(0))
+       cmp b32 $r1 $r2
+       bra e #host_send_done
+               // calculate address of message
+               and $r14 $r1 #fifo_qmaskp
+               shl b32 $r14 $r14 #fifo_qlen
+               add b32 $r14 #fifo_queue
+
+               // read message data, and pass to appropriate process
+               ld b32 $r11 D[$r14 + #fifo_data1]
+               ld b32 $r12 D[$r14 + #fifo_data0]
+               ld b32 $r13 D[$r14 + #fifo_message]
+               ld b32 $r14 D[$r14 + #fifo_process]
+               call(send)
+
+               // increment GET
+               add b32 $r1 0x1
+               and $r14 $r1 #fifo_qmaskf
+               nv_iowr(NV_PPWR_FIFO_GET(0), $r1)
+               bra #host_send
+       host_send_done:
+       ret
+
+// PWR->HOST comms - enqueue message for HOST to RFIFO
+//
+// $r15 - current (host)
+// $r14 - process
+// $r13 - message
+// $r12 - message data 0
+// $r11 - message data 1
+// $r0  - zero
+host_recv:
+       // message from intr handler == HOST->PWR comms pending
+       mov $r1 (PROC_KERN & 0x0000ffff)
+       sethi $r1 (PROC_KERN & 0xffff0000)
+       cmp b32 $r14 $r1
+       bra e #host_send
+
+       // wait for space in RFIFO
+       host_recv_wait:
+       nv_iord($r1, NV_PPWR_RFIFO_GET)
+       nv_iord($r2, NV_PPWR_RFIFO_PUT)
+       xor $r1 #rfifo_qmaskb
+       cmp b32 $r1 $r2
+       bra e #host_recv_wait
+
+       and $r3 $r2 #rfifo_qmaskp
+       shl b32 $r3 #rfifo_qlen
+       add b32 $r3 #rfifo_queue
+
+       // enqueue message
+       st b32 D[$r3 + #fifo_data1] $r11
+       st b32 D[$r3 + #fifo_data0] $r12
+       st b32 D[$r3 + #fifo_message] $r13
+       st b32 D[$r3 + #fifo_process] $r14
+
+       add b32 $r2 0x1
+       and $r2 #rfifo_qmaskf
+       nv_iowr(NV_PPWR_RFIFO_PUT, $r2)
+
+       // notify host of pending message
+       mov $r2 NV_PPWR_INTR_TRIGGER_USER0
+       nv_iowr(NV_PPWR_INTR_TRIGGER, $r2)
+       ret
+
+// $r15 - current (host)
+// $r0  - zero
+host_init:
+       // store each fifo's base/size in H2D/D2H scratch regs
+       mov $r1 #fifo_qsize
+       shl b32 $r1 16
+       or $r1 #fifo_queue
+       nv_iowr(NV_PPWR_H2D, $r1);
+
+       mov $r1 #rfifo_qsize
+       shl b32 $r1 16
+       or $r1 #rfifo_queue
+       nv_iowr(NV_PPWR_D2H, $r1);
+
+       // enable fifo subintr for first fifo
+       mov $r1 1
+       nv_iowr(NV_PPWR_FIFO_INTR_EN, $r1)
+       ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/idle.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/idle.fuc
new file mode 100644 (file)
index 0000000..98f1c37
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifdef INCLUDE_PROC
+process(PROC_IDLE, #idle, #idle_recv)
+#endif
+
+/******************************************************************************
+ * IDLE data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+#endif
+
+/******************************************************************************
+ * IDLE code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+// description
+//
+// $r15 - current (idle)
+// $r14 - message
+// $r0  - zero
+idle_recv:
+       ret
+
+// description
+//
+// $r15 - current (idle)
+// $r0  - zero
+idle:
+       // set our "no interrupt has occurred during our execution" flag
+       bset $flags $p0
+
+       // count IDLE invocations for debugging purposes
+       nv_iord($r1, NV_PPWR_DSCRATCH(1))
+       add b32 $r1 1
+       nv_iowr(NV_PPWR_DSCRATCH(1), $r1)
+
+       // keep looping while there's pending messages for any process
+       idle_loop:
+       mov $r1 #proc_list_head
+       bclr $flags $p2
+       idle_proc:
+               // process the process' messages until there's none left
+               idle_proc_exec:
+                       push $r1
+                       mov b32 $r14 $r1
+                       call(recv)
+                       pop $r1
+                       bra not $p1 #idle_proc_next
+                       bset $flags $p2
+                       bra #idle_proc_exec
+               // next process!
+               idle_proc_next:
+               add b32 $r1 #proc_size
+               cmp b32 $r1 $r15
+               bra ne #idle_proc
+       bra $p2 #idle_loop
+
+       // sleep if no interrupts have occurred
+       sleep $p0
+       bra #idle
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc
new file mode 100644 (file)
index 0000000..0a7b05f
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+/******************************************************************************
+ * kernel data segment
+ *****************************************************************************/
+#ifdef INCLUDE_PROC
+proc_kern:
+process(PROC_KERN, 0, 0)
+proc_list_head:
+#endif
+
+#ifdef INCLUDE_DATA
+proc_list_tail:
+time_prev: .b32 0
+time_next: .b32 0
+#endif
+
+/******************************************************************************
+ * kernel code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+       bra #init
+
+// read nv register
+//
+// $r15 - current
+// $r14 - addr
+// $r13 - data (return)
+// $r0  - zero
+rd32:
+       nv_iowr(NV_PPWR_MMIO_ADDR, $r14)
+       mov $r14 NV_PPWR_MMIO_CTRL_OP_RD
+       sethi $r14 NV_PPWR_MMIO_CTRL_TRIGGER
+       nv_iowr(NV_PPWR_MMIO_CTRL, $r14)
+       rd32_wait:
+               nv_iord($r14, NV_PPWR_MMIO_CTRL)
+               and $r14 NV_PPWR_MMIO_CTRL_STATUS
+               bra nz #rd32_wait
+       nv_iord($r13, NV_PPWR_MMIO_DATA)
+       ret
+
+// write nv register
+//
+// $r15 - current
+// $r14 - addr
+// $r13 - data
+// $r0  - zero
+wr32:
+       nv_iowr(NV_PPWR_MMIO_ADDR, $r14)
+       nv_iowr(NV_PPWR_MMIO_DATA, $r13)
+       mov $r14 NV_PPWR_MMIO_CTRL_OP_WR
+       or $r14 NV_PPWR_MMIO_CTRL_MASK_B32_0
+       sethi $r14 NV_PPWR_MMIO_CTRL_TRIGGER
+
+#ifdef NVKM_FALCON_MMIO_TRAP
+       mov $r8 NV_PPWR_INTR_TRIGGER_USER1
+       nv_iowr(NV_PPWR_INTR_TRIGGER, $r8)
+       wr32_host:
+               nv_iord($r8, NV_PPWR_INTR)
+               and $r8 NV_PPWR_INTR_USER1
+               bra nz #wr32_host
+#endif
+
+       nv_iowr(NV_PPWR_MMIO_CTRL, $r14)
+       wr32_wait:
+               nv_iord($r14, NV_PPWR_MMIO_CTRL)
+               and $r14 NV_PPWR_MMIO_CTRL_STATUS
+               bra nz #wr32_wait
+       ret
+
+// busy-wait for a period of time
+//
+// $r15 - current
+// $r14 - ns
+// $r0  - zero
+nsec:
+       nv_iord($r8, NV_PPWR_TIMER_LOW)
+       nsec_loop:
+               nv_iord($r9, NV_PPWR_TIMER_LOW)
+               sub b32 $r9 $r8
+               cmp b32 $r9 $r14
+               bra l #nsec_loop
+       ret
+
+// busy-wait for a period of time
+//
+// $r15 - current
+// $r14 - addr
+// $r13 - mask
+// $r12 - data
+// $r11 - timeout (ns)
+// $r0  - zero
+wait:
+       nv_iord($r8, NV_PPWR_TIMER_LOW)
+       wait_loop:
+               nv_rd32($r10, $r14)
+               and $r10 $r13
+               cmp b32 $r10 $r12
+               bra e #wait_done
+               nv_iord($r9, NV_PPWR_TIMER_LOW)
+               sub b32 $r9 $r8
+               cmp b32 $r9 $r11
+               bra l #wait_loop
+       wait_done:
+       ret
+
+// $r15 - current (kern)
+// $r14 - process
+// $r8  - NV_PPWR_INTR
+intr_watchdog:
+       // read process' timer status, skip if not enabled
+       ld b32 $r9 D[$r14 + #proc_time]
+       cmp b32 $r9 0
+       bra z #intr_watchdog_next_proc
+
+       // subtract last timer's value from process' timer,
+       // if it's <= 0 then the timer has expired
+       ld b32 $r10 D[$r0 + #time_prev]
+       sub b32 $r9 $r10
+       bra g #intr_watchdog_next_time
+               mov $r13 KMSG_ALARM
+               call(send_proc)
+               clear b32 $r9
+               bra #intr_watchdog_next_proc
+
+       // otherwise, update the next timer's value if this
+       // process' timer is the soonest
+       intr_watchdog_next_time:
+               // ... or if there's no next timer yet
+               ld b32 $r10 D[$r0 + #time_next]
+               cmp b32 $r10 0
+               bra z #intr_watchdog_next_time_set
+
+               cmp b32 $r9 $r10
+               bra g #intr_watchdog_next_proc
+               intr_watchdog_next_time_set:
+               st b32 D[$r0 + #time_next] $r9
+
+       // update process' timer status, and advance
+       intr_watchdog_next_proc:
+       st b32 D[$r14 + #proc_time] $r9
+       add b32 $r14 #proc_size
+       cmp b32 $r14 #proc_list_tail
+       bra ne #intr_watchdog
+       ret
+
+intr:
+       push $r0
+       clear b32 $r0
+       push $r8
+       push $r9
+       push $r10
+       push $r11
+       push $r12
+       push $r13
+       push $r14
+       push $r15
+       mov $r15 #proc_kern
+       mov $r8 $flags
+       push $r8
+
+       nv_iord($r8, NV_PPWR_DSCRATCH(0))
+       add b32 $r8 1
+       nv_iowr(NV_PPWR_DSCRATCH(0), $r8)
+
+       nv_iord($r8, NV_PPWR_INTR)
+       and $r9 $r8 NV_PPWR_INTR_WATCHDOG
+       bra z #intr_skip_watchdog
+               st b32 D[$r0 + #time_next] $r0
+               mov $r14 #proc_list_head
+               call(intr_watchdog)
+               ld b32 $r9 D[$r0 + #time_next]
+               cmp b32 $r9 0
+               bra z #intr_skip_watchdog
+                       nv_iowr(NV_PPWR_WATCHDOG_TIME, $r9)
+                       st b32 D[$r0 + #time_prev] $r9
+
+       intr_skip_watchdog:
+       and $r9 $r8 NV_PPWR_INTR_SUBINTR
+       bra z #intr_skip_subintr
+               nv_iord($r9, NV_PPWR_SUBINTR)
+               and $r10 $r9 NV_PPWR_SUBINTR_FIFO
+               bra z #intr_subintr_skip_fifo
+                       nv_iord($r12, NV_PPWR_FIFO_INTR)
+                       push $r12
+                       mov $r14 (PROC_HOST & 0x0000ffff)
+                       sethi $r14 (PROC_HOST & 0xffff0000)
+                       mov $r13 KMSG_FIFO
+                       call(send)
+                       pop $r12
+                       nv_iowr(NV_PPWR_FIFO_INTR, $r12)
+               intr_subintr_skip_fifo:
+               nv_iowr(NV_PPWR_SUBINTR, $r9)
+
+       intr_skip_subintr:
+       and $r9 $r8 NV_PPWR_INTR_PAUSE
+       bra z #intr_skip_pause
+               and $r10 0xffbf
+
+       intr_skip_pause:
+       and $r9 $r8 NV_PPWR_INTR_USER0
+       bra z #intr_skip_user0
+               and $r10 0xffbf
+
+       intr_skip_user0:
+       nv_iowr(NV_PPWR_INTR_ACK, $r8)
+       pop $r8
+       mov $flags $r8
+       pop $r15
+       pop $r14
+       pop $r13
+       pop $r12
+       pop $r11
+       pop $r10
+       pop $r9
+       pop $r8
+       pop $r0
+       bclr $flags $p0
+       iret
+
+// request the current process be sent a message after a timeout expires
+//
+// $r15 - current
+// $r14 - ticks
+// $r0  - zero
+timer:
+       // interrupts off to prevent racing with timer isr
+       bclr $flags ie0
+
+       // if current process already has a timer set, bail
+       ld b32 $r8 D[$r15 + #proc_time]
+       cmp b32 $r8 0
+       bra g #timer_done
+       st b32 D[$r15 + #proc_time] $r14
+
+       // halt watchdog timer temporarily and check for a pending
+       // interrupt.  if there's one already pending, we can just
+       // bail since the timer isr will queue the next soonest
+       // right after it's done
+       nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
+       nv_iord($r8, NV_PPWR_INTR)
+       and $r8 NV_PPWR_INTR_WATCHDOG
+       bra nz #timer_enable
+
+       // update the watchdog if this timer should expire first,
+       // or if there's no timeout already set
+       nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
+       cmp b32 $r14 $r0
+       bra e #timer_reset
+       cmp b32 $r14 $r8
+       bra l #timer_done
+       timer_reset:
+       nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14)
+       st b32 D[$r0 + #time_prev] $r14
+
+       // re-enable the watchdog timer
+       timer_enable:
+       mov $r8 1
+       nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
+
+       // interrupts back on
+       timer_done:
+       bset $flags ie0
+       ret
+
+// send message to another process
+//
+// $r15 - current
+// $r14 - process
+// $r13 - message
+// $r12 - message data 0
+// $r11 - message data 1
+// $r0  - zero
+send_proc:
+       push $r8
+       push $r9
+       // check for space in queue
+       ld b32 $r8 D[$r14 + #proc_qget]
+       ld b32 $r9 D[$r14 + #proc_qput]
+       xor $r8 #proc_qmaskb
+       cmp b32 $r8 $r9
+       bra e #send_done
+
+       // enqueue message
+       and $r8 $r9 #proc_qmaskp
+       shl b32 $r8 $r8 #proc_qlen
+       add b32 $r8 #proc_queue
+       add b32 $r8 $r14
+
+       ld b32 $r10 D[$r15 + #proc_id]
+       st b32 D[$r8 + #msg_process] $r10
+       st b32 D[$r8 + #msg_message] $r13
+       st b32 D[$r8 + #msg_data0] $r12
+       st b32 D[$r8 + #msg_data1] $r11
+
+       // increment PUT
+       add b32 $r9 1
+       and $r9 #proc_qmaskf
+       st b32 D[$r14 + #proc_qput] $r9
+       bset $flags $p2
+       send_done:
+       pop $r9
+       pop $r8
+       ret
+
+// lookup process structure by its name
+//
+// $r15 - current
+// $r14 - process name
+// $r0  - zero
+//
+// $r14 - process
+// $p1  - success
+find:
+       push $r8
+       mov $r8 #proc_list_head
+       bset $flags $p1
+       find_loop:
+               ld b32 $r10 D[$r8 + #proc_id]
+               cmp b32 $r10 $r14
+               bra e #find_done
+               add b32 $r8 #proc_size
+               cmp b32 $r8 #proc_list_tail
+               bra ne #find_loop
+               bclr $flags $p1
+       find_done:
+       mov b32 $r14 $r8
+       pop $r8
+       ret
+
+// send message to another process
+//
+// $r15 - current
+// $r14 - process id
+// $r13 - message
+// $r12 - message data 0
+// $r11 - message data 1
+// $r0  - zero
+send:
+       call(find)
+       bra $p1 #send_proc
+       ret
+
+// process single message for a given process
+//
+// $r15 - current
+// $r14 - process
+// $r0  - zero
+recv:
+       ld b32 $r8 D[$r14 + #proc_qget]
+       ld b32 $r9 D[$r14 + #proc_qput]
+       bclr $flags $p1
+       cmp b32 $r8 $r9
+       bra e #recv_done
+               // dequeue message
+               and $r9 $r8 #proc_qmaskp
+               add b32 $r8 1
+               and $r8 #proc_qmaskf
+               st b32 D[$r14 + #proc_qget] $r8
+               ld b32 $r10 D[$r14 + #proc_recv]
+
+               push $r15
+               mov $r15 $flags
+               push $r15
+               mov b32 $r15 $r14
+
+               shl b32 $r9 $r9 #proc_qlen
+               add b32 $r14 $r9
+               add b32 $r14 #proc_queue
+               ld b32 $r11 D[$r14 + #msg_data1]
+               ld b32 $r12 D[$r14 + #msg_data0]
+               ld b32 $r13 D[$r14 + #msg_message]
+               ld b32 $r14 D[$r14 + #msg_process]
+
+               // process it
+               call $r10
+               pop $r15
+               mov $flags $r15
+               bset $flags $p1
+               pop $r15
+       recv_done:
+       ret
+
+init:
+       // setup stack
+       nv_iord($r1, NV_PPWR_CAPS)
+       extr $r1 $r1 9:17
+       shl b32 $r1 8
+       mov $sp $r1
+
+#ifdef NVKM_FALCON_MMIO_UAS
+       // somehow allows the magic "access mmio via D[]" stuff that's
+       // used by the nv_rd32/nv_wr32 macros to work
+       mov $r1 0x0010
+       sethi $r1 NV_PPWR_UAS_CONFIG_ENABLE
+       nv_iowrs(NV_PPWR_UAS_CONFIG, $r1)
+#endif
+
+       // route all interrupts except user0/1 and pause to fuc
+       mov $r1 0x00e0
+       sethi $r1 0x00000000
+       nv_iowr(NV_PPWR_INTR_ROUTE, $r1)
+
+       // enable watchdog and subintr intrs
+       mov $r1 NV_PPWR_INTR_EN_CLR_MASK
+       nv_iowr(NV_PPWR_INTR_EN_CLR, $r1)
+       mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG
+       or $r1 NV_PPWR_INTR_EN_SET_SUBINTR
+       nv_iowr(NV_PPWR_INTR_EN_SET, $r1)
+
+       // enable interrupts globally
+       mov $r1 #intr
+       sethi $r1 0x00000000
+       mov $iv0 $r1
+       bset $flags ie0
+
+       // enable watchdog timer
+       mov $r1 1
+       nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1)
+
+       // bootstrap processes, idle process will be last, and not return
+       mov $r15 #proc_list_head
+       init_proc:
+               ld b32 $r1 D[$r15 + #proc_init]
+               cmp b32 $r1 0
+               bra z #init_proc
+               call $r1
+               add b32 $r15 #proc_size
+               bra #init_proc
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc
new file mode 100644 (file)
index 0000000..2a74ea9
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define GT215 0xa3
+#define GF100 0xc0
+#define GF119 0xd9
+#define GK208 0x108
+
+#include "os.h"
+
+// IO addresses
+#define NV_PPWR_INTR_TRIGGER                                             0x0000
+#define NV_PPWR_INTR_TRIGGER_USER1                                   0x00000080
+#define NV_PPWR_INTR_TRIGGER_USER0                                   0x00000040
+#define NV_PPWR_INTR_ACK                                                 0x0004
+#define NV_PPWR_INTR_ACK_SUBINTR                                     0x00000800
+#define NV_PPWR_INTR_ACK_WATCHDOG                                    0x00000002
+#define NV_PPWR_INTR                                                     0x0008
+#define NV_PPWR_INTR_SUBINTR                                         0x00000800
+#define NV_PPWR_INTR_USER1                                           0x00000080
+#define NV_PPWR_INTR_USER0                                           0x00000040
+#define NV_PPWR_INTR_PAUSE                                           0x00000020
+#define NV_PPWR_INTR_WATCHDOG                                        0x00000002
+#define NV_PPWR_INTR_EN_SET                                              0x0010
+#define NV_PPWR_INTR_EN_SET_SUBINTR                                  0x00000800
+#define NV_PPWR_INTR_EN_SET_WATCHDOG                                 0x00000002
+#define NV_PPWR_INTR_EN_CLR                                              0x0014
+#define NV_PPWR_INTR_EN_CLR_MASK                    /* fuck i hate envyas */ -1
+#define NV_PPWR_INTR_ROUTE                                               0x001c
+#define NV_PPWR_TIMER_LOW                                                0x002c
+#define NV_PPWR_WATCHDOG_TIME                                            0x0034
+#define NV_PPWR_WATCHDOG_ENABLE                                          0x0038
+#define NV_PPWR_CAPS                                                     0x0108
+#define NV_PPWR_UAS_CONFIG                                               0x0164
+#define NV_PPWR_UAS_CONFIG_ENABLE                                    0x00010000
+#if NVKM_PPWR_CHIPSET >= GK208
+#define NV_PPWR_DSCRATCH(i)                                   (4 * (i) + 0x0450)
+#endif
+#define NV_PPWR_FIFO_PUT(i)                                   (4 * (i) + 0x04a0)
+#define NV_PPWR_FIFO_GET(i)                                   (4 * (i) + 0x04b0)
+#define NV_PPWR_FIFO_INTR                                                0x04c0
+#define NV_PPWR_FIFO_INTR_EN                                             0x04c4
+#define NV_PPWR_RFIFO_PUT                                                0x04c8
+#define NV_PPWR_RFIFO_GET                                                0x04cc
+#define NV_PPWR_H2D                                                      0x04d0
+#define NV_PPWR_D2H                                                      0x04dc
+#if NVKM_PPWR_CHIPSET < GK208
+#define NV_PPWR_DSCRATCH(i)                                   (4 * (i) + 0x05d0)
+#endif
+#define NV_PPWR_SUBINTR                                                  0x0688
+#define NV_PPWR_SUBINTR_FIFO                                         0x00000002
+#define NV_PPWR_MMIO_ADDR                                                0x07a0
+#define NV_PPWR_MMIO_DATA                                                0x07a4
+#define NV_PPWR_MMIO_CTRL                                                0x07ac
+#define NV_PPWR_MMIO_CTRL_TRIGGER                                    0x00010000
+#define NV_PPWR_MMIO_CTRL_STATUS                                     0x00007000
+#define NV_PPWR_MMIO_CTRL_STATUS_IDLE                                0x00000000
+#define NV_PPWR_MMIO_CTRL_MASK                                       0x000000f0
+#define NV_PPWR_MMIO_CTRL_MASK_B32_0                                 0x000000f0
+#define NV_PPWR_MMIO_CTRL_OP                                         0x00000003
+#define NV_PPWR_MMIO_CTRL_OP_RD                                      0x00000001
+#define NV_PPWR_MMIO_CTRL_OP_WR                                      0x00000002
+#define NV_PPWR_OUTPUT                                                   0x07c0
+#define NV_PPWR_OUTPUT_FB_PAUSE                                      0x00000004
+#define NV_PPWR_OUTPUT_SET                                               0x07e0
+#define NV_PPWR_OUTPUT_SET_FB_PAUSE                                  0x00000004
+#define NV_PPWR_OUTPUT_CLR                                               0x07e4
+#define NV_PPWR_OUTPUT_CLR_FB_PAUSE                                  0x00000004
+
+// Inter-process message format
+.equ #msg_process 0x00 /* send() target, recv() sender */
+.equ #msg_message 0x04
+.equ #msg_data0   0x08
+.equ #msg_data1   0x0c
+
+// Kernel message IDs
+#define KMSG_FIFO  0x00000000
+#define KMSG_ALARM 0x00000001
+
+// Process message queue description
+.equ #proc_qlen 4 // log2(size of queue entry in bytes)
+.equ #proc_qnum 2 // log2(max number of entries in queue)
+.equ #proc_qmaskb (1 << #proc_qnum) // max number of entries in queue
+.equ #proc_qmaskp (#proc_qmaskb - 1)
+.equ #proc_qmaskf ((#proc_qmaskb << 1) - 1)
+.equ #proc_qsize  (1 << (#proc_qlen + #proc_qnum))
+
+// Process table entry
+.equ #proc_id    0x00
+.equ #proc_init  0x04
+.equ #proc_recv  0x08
+.equ #proc_time  0x0c
+.equ #proc_qput  0x10
+.equ #proc_qget  0x14
+.equ #proc_queue 0x18
+.equ #proc_size (0x18 + #proc_qsize)
+
+#define process(id,init,recv) /*
+*/     .b32 id /*
+*/     .b32 init /*
+*/     .b32 recv /*
+*/     .b32 0 /*
+*/     .b32 0 /*
+*/     .b32 0 /*
+*/     .skip 64
+
+#ifndef NVKM_FALCON_UNSHIFTED_IO
+#define nv_iord(reg,ior) /*
+*/     mov reg ior /*
+*/     shl b32 reg 6 /*
+*/     iord reg I[reg + 0x000]
+#else
+#define nv_iord(reg,ior) /*
+*/     mov reg ior /*
+*/     iord reg I[reg + 0x000]
+#endif
+
+#ifndef NVKM_FALCON_UNSHIFTED_IO
+#define nv_iowr(ior,reg) /*
+*/     mov $r0 ior /*
+*/     shl b32 $r0 6 /*
+*/     iowr I[$r0 + 0x000] reg /*
+*/     clear b32 $r0
+#else
+#define nv_iowr(ior,reg) /*
+*/     mov $r0 ior /*
+*/     iowr I[$r0 + 0x000] reg /*
+*/     clear b32 $r0
+#endif
+
+#ifndef NVKM_FALCON_UNSHIFTED_IO
+#define nv_iowrs(ior,reg) /*
+*/     mov $r0 ior /*
+*/     shl b32 $r0 6 /*
+*/     iowrs I[$r0 + 0x000] reg /*
+*/     clear b32 $r0
+#else
+#define nv_iowrs(ior,reg) /*
+*/     mov $r0 ior /*
+*/     iowrs I[$r0 + 0x000] reg /*
+*/     clear b32 $r0
+#endif
+
+#define hash #
+#define fn(a) a
+#ifndef NVKM_FALCON_PC24
+#define call(a) call fn(hash)a
+#else
+#define call(a) lcall fn(hash)a
+#endif
+
+#ifndef NVKM_FALCON_MMIO_UAS
+#define nv_rd32(reg,addr) /*
+*/     mov b32 $r14 addr /*
+*/     call(rd32) /*
+*/     mov b32 reg $r13
+#else
+#define nv_rd32(reg,addr) /*
+*/     sethi $r0 0x14000000 /*
+*/     or $r0 addr /*
+*/     ld b32 reg D[$r0] /*
+*/     clear b32 $r0
+#endif
+
+#if !defined(NVKM_FALCON_MMIO_UAS) || defined(NVKM_FALCON_MMIO_TRAP)
+#define nv_wr32(addr,reg) /*
+*/     push addr /*
+*/     push reg /*
+*/     pop $r13 /*
+*/     pop $r14 /*
+*/     call(wr32) /*
+#else
+#define nv_wr32(addr,reg) /*
+*/     sethi $r0 0x14000000 /*
+*/     or $r0 addr /*
+*/     st b32 D[$r0] reg /*
+*/     clear b32 $r0
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc
new file mode 100644 (file)
index 0000000..d43741e
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifdef INCLUDE_PROC
+process(PROC_MEMX, #memx_init, #memx_recv)
+#endif
+
+/******************************************************************************
+ * MEMX data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+.equ #memx_opcode 0
+.equ #memx_header 2
+.equ #memx_length 4
+.equ #memx_func   8
+
+#define handler(cmd,hdr,len,func) /*
+*/     .b16 MEMX_##cmd /*
+*/     .b16 hdr /*
+*/     .b16 len /*
+*/      .b16 0 /*
+*/     .b32 func
+
+memx_func_head:
+handler(ENTER , 0x0001, 0x0000, #memx_func_enter)
+memx_func_next:
+handler(LEAVE , 0x0000, 0x0000, #memx_func_leave)
+handler(WR32  , 0x0000, 0x0002, #memx_func_wr32)
+handler(WAIT  , 0x0004, 0x0000, #memx_func_wait)
+handler(DELAY , 0x0001, 0x0000, #memx_func_delay)
+memx_func_tail:
+
+.equ #memx_func_size #memx_func_next - #memx_func_head
+.equ #memx_func_num (#memx_func_tail - #memx_func_head) / #memx_func_size
+
+memx_data_head:
+.skip 0x0800
+memx_data_tail:
+#endif
+
+/******************************************************************************
+ * MEMX code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+// description
+//
+// $r15 - current (memx)
+// $r4  - packet length
+//     +00: bitmask of heads to wait for vblank on
+// $r3  - opcode desciption
+// $r0  - zero
+memx_func_enter:
+       mov $r6 NV_PPWR_OUTPUT_SET_FB_PAUSE
+       nv_iowr(NV_PPWR_OUTPUT_SET, $r6)
+       memx_func_enter_wait:
+               nv_iord($r6, NV_PPWR_OUTPUT)
+               and $r6 NV_PPWR_OUTPUT_FB_PAUSE
+               bra z #memx_func_enter_wait
+       //XXX: TODO
+       ld b32 $r6 D[$r1 + 0x00]
+       add b32 $r1 0x04
+       ret
+
+// description
+//
+// $r15 - current (memx)
+// $r4  - packet length
+// $r3  - opcode desciption
+// $r0  - zero
+memx_func_leave:
+       mov $r6 NV_PPWR_OUTPUT_CLR_FB_PAUSE
+       nv_iowr(NV_PPWR_OUTPUT_CLR, $r6)
+       memx_func_leave_wait:
+               nv_iord($r6, NV_PPWR_OUTPUT)
+               and $r6 NV_PPWR_OUTPUT_FB_PAUSE
+               bra nz #memx_func_leave_wait
+       ret
+
+// description
+//
+// $r15 - current (memx)
+// $r4  - packet length
+//     +00*n: addr
+//     +04*n: data
+// $r3  - opcode desciption
+// $r0  - zero
+memx_func_wr32:
+       ld b32 $r6 D[$r1 + 0x00]
+       ld b32 $r5 D[$r1 + 0x04]
+       add b32 $r1 0x08
+       nv_wr32($r6, $r5)
+       sub b32 $r4 0x02
+       bra nz #memx_func_wr32
+       ret
+
+// description
+//
+// $r15 - current (memx)
+// $r4  - packet length
+//     +00: addr
+//     +04: mask
+//     +08: data
+//     +0c: timeout (ns)
+// $r3  - opcode desciption
+// $r0  - zero
+memx_func_wait:
+       nv_iord($r8, NV_PPWR_TIMER_LOW)
+       ld b32 $r14 D[$r1 + 0x00]
+       ld b32 $r13 D[$r1 + 0x04]
+       ld b32 $r12 D[$r1 + 0x08]
+       ld b32 $r11 D[$r1 + 0x0c]
+       add b32 $r1 0x10
+       call(wait)
+       ret
+
+// description
+//
+// $r15 - current (memx)
+// $r4  - packet length
+//     +00: time (ns)
+// $r3  - opcode desciption
+// $r0  - zero
+memx_func_delay:
+       ld b32 $r14 D[$r1 + 0x00]
+       add b32 $r1 0x04
+       call(nsec)
+       ret
+
+// description
+//
+// $r15 - current (memx)
+// $r14 - sender process name
+// $r13 - message (exec)
+// $r12 - head of script
+// $r11 - tail of script
+// $r0  - zero
+memx_exec:
+       push $r14
+       push $r13
+       mov b32 $r1 $r12
+       mov b32 $r2 $r11
+       memx_exec_next:
+               // fetch the packet header, and locate opcode info
+               ld b32 $r3 D[$r1]
+               add b32 $r1 4
+               shr b32 $r4 $r3 16
+               mulu $r3 #memx_func_size
+
+               // execute the opcode handler
+               ld b32 $r5 D[$r3 + #memx_func_head + #memx_func]
+               call $r5
+
+               // keep going, if we haven't reached the end
+               cmp b32 $r1 $r2
+               bra l #memx_exec_next
+
+       // send completion reply
+       pop $r13
+       pop $r14
+       call(send)
+       ret
+
+// description
+//
+// $r15 - current (memx)
+// $r14 - sender process name
+// $r13 - message
+// $r12 - data0
+// $r11 - data1
+// $r0  - zero
+memx_info:
+       mov $r12 #memx_data_head
+       mov $r11 #memx_data_tail - #memx_data_head
+       call(send)
+       ret
+
+// description
+//
+// $r15 - current (memx)
+// $r14 - sender process name
+// $r13 - message
+// $r12 - data0
+// $r11 - data1
+// $r0  - zero
+memx_recv:
+       cmp b32 $r13 MEMX_MSG_EXEC
+       bra e #memx_exec
+       cmp b32 $r13 MEMX_MSG_INFO
+       bra e #memx_info
+       ret
+
+// description
+//
+// $r15 - current (memx)
+// $r0  - zero
+memx_init:
+       ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc
new file mode 100644 (file)
index 0000000..947be53
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define NVKM_PPWR_CHIPSET GK208
+
+#define NVKM_FALCON_PC24
+#define NVKM_FALCON_UNSHIFTED_IO
+//#define NVKM_FALCON_MMIO_UAS
+//#define NVKM_FALCON_MMIO_TRAP
+
+#include "macros.fuc"
+
+.section #nv108_pwr_data
+#define INCLUDE_PROC
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_PROC
+
+#define INCLUDE_DATA
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_DATA
+.align 256
+
+.section #nv108_pwr_code
+#define INCLUDE_CODE
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_CODE
+.align 256
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h
new file mode 100644 (file)
index 0000000..9342e2d
--- /dev/null
@@ -0,0 +1,1165 @@
+uint32_t nv108_pwr_data[] = {
+/* 0x0000: proc_kern */
+       0x52544e49,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0058: proc_list_head */
+       0x54534f48,
+       0x00000379,
+       0x0000032a,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x584d454d,
+       0x0000046f,
+       0x00000461,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x46524550,
+       0x00000473,
+       0x00000471,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x54534554,
+       0x00000494,
+       0x00000475,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x454c4449,
+       0x0000049f,
+       0x0000049d,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0210: proc_list_tail */
+/* 0x0210: time_prev */
+       0x00000000,
+/* 0x0214: time_next */
+       0x00000000,
+/* 0x0218: fifo_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0298: rfifo_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0318: memx_func_head */
+       0x00010000,
+       0x00000000,
+       0x000003a9,
+/* 0x0324: memx_func_next */
+       0x00000001,
+       0x00000000,
+       0x000003c7,
+       0x00000002,
+       0x00000002,
+       0x000003df,
+       0x00040003,
+       0x00000000,
+       0x00000407,
+       0x00010004,
+       0x00000000,
+       0x00000421,
+/* 0x0354: memx_func_tail */
+/* 0x0354: memx_data_head */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0b54: memx_data_tail */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
+
+uint32_t nv108_pwr_code[] = {
+       0x02910ef5,
+/* 0x0004: rd32 */
+       0xf607a040,
+       0x04bd000e,
+       0xe3f0010e,
+       0x07ac4001,
+       0xbd000ef6,
+/* 0x0019: rd32_wait */
+       0x07ac4e04,
+       0xf100eecf,
+       0xf47000e4,
+       0xa44df61b,
+       0x00ddcf07,
+/* 0x002e: wr32 */
+       0xa04000f8,
+       0x000ef607,
+       0xa44004bd,
+       0x000df607,
+       0x020e04bd,
+       0xf0f0e5f0,
+       0xac4001e3,
+       0x000ef607,
+/* 0x004e: wr32_wait */
+       0xac4e04bd,
+       0x00eecf07,
+       0x7000e4f1,
+       0xf8f61bf4,
+/* 0x005d: nsec */
+       0xcf2c0800,
+/* 0x0062: nsec_loop */
+       0x2c090088,
+       0xbb0099cf,
+       0x9ea60298,
+       0xf8f61ef4,
+/* 0x0071: wait */
+       0xcf2c0800,
+/* 0x0076: wait_loop */
+       0xeeb20088,
+       0x0000047e,
+       0xadfddab2,
+       0xf4aca604,
+       0x2c09100b,
+       0xbb0099cf,
+       0x9ba60298,
+/* 0x0093: wait_done */
+       0xf8e61ef4,
+/* 0x0095: intr_watchdog */
+       0x03e99800,
+       0xf40096b0,
+       0x0a98280b,
+       0x029abb84,
+       0x0d0e1cf4,
+       0x01de7e01,
+       0xf494bd00,
+/* 0x00b2: intr_watchdog_next_time */
+       0x0a98140e,
+       0x00a6b085,
+       0xa6080bf4,
+       0x061cf49a,
+/* 0x00c0: intr_watchdog_next_time_set */
+/* 0x00c3: intr_watchdog_next_proc */
+       0xb58509b5,
+       0xe0b603e9,
+       0x10e6b158,
+       0xc81bf402,
+/* 0x00d2: intr */
+       0x00f900f8,
+       0x80f904bd,
+       0xa0f990f9,
+       0xc0f9b0f9,
+       0xe0f9d0f9,
+       0x000ff0f9,
+       0xf90188fe,
+       0x04504880,
+       0xb60088cf,
+       0x50400180,
+       0x0008f604,
+       0x080804bd,
+       0xc40088cf,
+       0x0bf40289,
+       0x8500b51f,
+       0x957e580e,
+       0x09980000,
+       0x0096b085,
+       0x000d0bf4,
+       0x0009f634,
+       0x09b504bd,
+/* 0x0125: intr_skip_watchdog */
+       0x0089e484,
+       0x360bf408,
+       0xcf068849,
+       0x9ac40099,
+       0x220bf402,
+       0xcf04c04c,
+       0xc0f900cc,
+       0xf14f484e,
+       0x0d5453e3,
+       0x023f7e00,
+       0x40c0fc00,
+       0x0cf604c0,
+/* 0x0157: intr_subintr_skip_fifo */
+       0x4004bd00,
+       0x09f60688,
+/* 0x015f: intr_skip_subintr */
+       0xc404bd00,
+       0x0bf42089,
+       0xbfa4f107,
+/* 0x0169: intr_skip_pause */
+       0x4089c4ff,
+       0xf1070bf4,
+/* 0x0173: intr_skip_user0 */
+       0x00ffbfa4,
+       0x0008f604,
+       0x80fc04bd,
+       0xfc0088fe,
+       0xfce0fcf0,
+       0xfcc0fcd0,
+       0xfca0fcb0,
+       0xfc80fc90,
+       0x0032f400,
+/* 0x0196: timer */
+       0x32f401f8,
+       0x03f89810,
+       0xf40086b0,
+       0xfeb53a1c,
+       0xf6380003,
+       0x04bd0008,
+       0x88cf0808,
+       0x0284f000,
+       0x081c1bf4,
+       0x0088cf34,
+       0x0bf4e0a6,
+       0xf4e8a608,
+/* 0x01c6: timer_reset */
+       0x3400161e,
+       0xbd000ef6,
+       0x840eb504,
+/* 0x01d0: timer_enable */
+       0x38000108,
+       0xbd0008f6,
+/* 0x01d9: timer_done */
+       0x1031f404,
+/* 0x01de: send_proc */
+       0x80f900f8,
+       0xe89890f9,
+       0x04e99805,
+       0xa60486f0,
+       0x2a0bf489,
+       0x940398c4,
+       0x80b60488,
+       0x008ebb18,
+       0xb500fa98,
+       0x8db5008a,
+       0x028cb501,
+       0xb6038bb5,
+       0x94f00190,
+       0x04e9b507,
+/* 0x0217: send_done */
+       0xfc0231f4,
+       0xf880fc90,
+/* 0x021d: find */
+       0x0880f900,
+       0x0131f458,
+/* 0x0224: find_loop */
+       0xa6008a98,
+       0x100bf4ae,
+       0xb15880b6,
+       0xf4021086,
+       0x32f4f11b,
+/* 0x0239: find_done */
+       0xfc8eb201,
+/* 0x023f: send */
+       0x7e00f880,
+       0xf400021d,
+       0x00f89b01,
+/* 0x0248: recv */
+       0x9805e898,
+       0x32f404e9,
+       0xf489a601,
+       0x89c43c0b,
+       0x0180b603,
+       0xb50784f0,
+       0xea9805e8,
+       0xfef0f902,
+       0xf0f9018f,
+       0x9994efb2,
+       0x00e9bb04,
+       0x9818e0b6,
+       0xec9803eb,
+       0x01ed9802,
+       0xf900ee98,
+       0xfef0fca5,
+       0x31f400f8,
+/* 0x028f: recv_done */
+       0xf8f0fc01,
+/* 0x0291: init */
+       0x01084100,
+       0xe70011cf,
+       0xb6010911,
+       0x14fe0814,
+       0x00e04100,
+       0x000013f0,
+       0x0001f61c,
+       0xff0104bd,
+       0x01f61400,
+       0x0104bd00,
+       0x0015f102,
+       0xf6100008,
+       0x04bd0001,
+       0xf000d241,
+       0x10fe0013,
+       0x1031f400,
+       0x38000101,
+       0xbd0001f6,
+/* 0x02db: init_proc */
+       0x98580f04,
+       0x16b001f1,
+       0xfa0bf400,
+       0xf0b615f9,
+       0xf20ef458,
+/* 0x02ec: host_send */
+       0xcf04b041,
+       0xa0420011,
+       0x0022cf04,
+       0x0bf412a6,
+       0x071ec42e,
+       0xb704ee94,
+       0x980218e0,
+       0xec9803eb,
+       0x01ed9802,
+       0x7e00ee98,
+       0xb600023f,
+       0x1ec40110,
+       0x04b0400f,
+       0xbd0001f6,
+       0xc70ef404,
+/* 0x0328: host_send_done */
+/* 0x032a: host_recv */
+       0x494100f8,
+       0x5413f14e,
+       0xf4e1a652,
+/* 0x0336: host_recv_wait */
+       0xcc41b90b,
+       0x0011cf04,
+       0xcf04c842,
+       0x16f00022,
+       0xf412a608,
+       0x23c4ef0b,
+       0x0434b607,
+       0x029830b7,
+       0xb5033bb5,
+       0x3db5023c,
+       0x003eb501,
+       0xf00120b6,
+       0xc8400f24,
+       0x0002f604,
+       0x400204bd,
+       0x02f60000,
+       0xf804bd00,
+/* 0x0379: host_init */
+       0x00804100,
+       0xf11014b6,
+       0x40021815,
+       0x01f604d0,
+       0x4104bd00,
+       0x14b60080,
+       0x9815f110,
+       0x04dc4002,
+       0xbd0001f6,
+       0x40010104,
+       0x01f604c4,
+       0xf804bd00,
+/* 0x03a9: memx_func_enter */
+       0x40040600,
+       0x06f607e0,
+/* 0x03b3: memx_func_enter_wait */
+       0x4604bd00,
+       0x66cf07c0,
+       0x0464f000,
+       0x98f70bf4,
+       0x10b60016,
+/* 0x03c7: memx_func_leave */
+       0x0600f804,
+       0x07e44004,
+       0xbd0006f6,
+/* 0x03d1: memx_func_leave_wait */
+       0x07c04604,
+       0xf00066cf,
+       0x1bf40464,
+/* 0x03df: memx_func_wr32 */
+       0x9800f8f7,
+       0x15980016,
+       0x0810b601,
+       0x50f960f9,
+       0xe0fcd0fc,
+       0x00002e7e,
+       0x140003f1,
+       0xa00506fd,
+       0xb604bd05,
+       0x1bf40242,
+/* 0x0407: memx_func_wait */
+       0x0800f8dd,
+       0x0088cf2c,
+       0x98001e98,
+       0x1c98011d,
+       0x031b9802,
+       0x7e1010b6,
+       0xf8000071,
+/* 0x0421: memx_func_delay */
+       0x001e9800,
+       0x7e0410b6,
+       0xf800005d,
+/* 0x042d: memx_exec */
+       0xf9e0f900,
+       0xb2c1b2d0,
+/* 0x0435: memx_exec_next */
+       0x001398b2,
+       0x950410b6,
+       0x30f01034,
+       0xc835980c,
+       0x12a655f9,
+       0xfced1ef4,
+       0x7ee0fcd0,
+       0xf800023f,
+/* 0x0455: memx_info */
+       0x03544c00,
+       0x7e08004b,
+       0xf800023f,
+/* 0x0461: memx_recv */
+       0x01d6b000,
+       0xb0c90bf4,
+       0x0bf400d6,
+/* 0x046f: memx_init */
+       0xf800f8eb,
+/* 0x0471: perf_recv */
+/* 0x0473: perf_init */
+       0xf800f800,
+/* 0x0475: test_recv */
+       0x04584100,
+       0xb60011cf,
+       0x58400110,
+       0x0001f604,
+       0xe7f104bd,
+       0xe3f1d900,
+       0x967e134f,
+       0x00f80001,
+/* 0x0494: test_init */
+       0x7e08004e,
+       0xf8000196,
+/* 0x049d: idle_recv */
+/* 0x049f: idle */
+       0xf400f800,
+       0x54410031,
+       0x0011cf04,
+       0x400110b6,
+       0x01f60454,
+/* 0x04b3: idle_loop */
+       0x0104bd00,
+       0x0232f458,
+/* 0x04b8: idle_proc */
+/* 0x04b8: idle_proc_exec */
+       0x1eb210f9,
+       0x0002487e,
+       0x11f410fc,
+       0x0231f409,
+/* 0x04cb: idle_proc_next */
+       0xb6f00ef4,
+       0x1fa65810,
+       0xf4e81bf4,
+       0x28f4e002,
+       0xc60ef400,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc
new file mode 100644 (file)
index 0000000..6fde0b8
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define NVKM_PPWR_CHIPSET GT215
+
+//#define NVKM_FALCON_PC24
+//#define NVKM_FALCON_UNSHIFTED_IO
+//#define NVKM_FALCON_MMIO_UAS
+//#define NVKM_FALCON_MMIO_TRAP
+
+#include "macros.fuc"
+
+.section #nva3_pwr_data
+#define INCLUDE_PROC
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_PROC
+
+#define INCLUDE_DATA
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_DATA
+.align 256
+
+.section #nva3_pwr_code
+#define INCLUDE_CODE
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_CODE
+.align 256
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h
new file mode 100644 (file)
index 0000000..0fa4d7d
--- /dev/null
@@ -0,0 +1,1229 @@
+uint32_t nva3_pwr_data[] = {
+/* 0x0000: proc_kern */
+       0x52544e49,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0058: proc_list_head */
+       0x54534f48,
+       0x00000430,
+       0x000003cd,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x584d454d,
+       0x0000054e,
+       0x00000540,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x46524550,
+       0x00000552,
+       0x00000550,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x54534554,
+       0x0000057b,
+       0x00000554,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x454c4449,
+       0x00000587,
+       0x00000585,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0210: proc_list_tail */
+/* 0x0210: time_prev */
+       0x00000000,
+/* 0x0214: time_next */
+       0x00000000,
+/* 0x0218: fifo_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0298: rfifo_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0318: memx_func_head */
+       0x00010000,
+       0x00000000,
+       0x0000046f,
+/* 0x0324: memx_func_next */
+       0x00000001,
+       0x00000000,
+       0x00000496,
+       0x00000002,
+       0x00000002,
+       0x000004b7,
+       0x00040003,
+       0x00000000,
+       0x000004df,
+       0x00010004,
+       0x00000000,
+       0x000004fc,
+/* 0x0354: memx_func_tail */
+/* 0x0354: memx_data_head */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0b54: memx_data_tail */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
+
+uint32_t nva3_pwr_code[] = {
+       0x030d0ef5,
+/* 0x0004: rd32 */
+       0x07a007f1,
+       0xd00604b6,
+       0x04bd000e,
+       0xf001e7f0,
+       0x07f101e3,
+       0x04b607ac,
+       0x000ed006,
+/* 0x0022: rd32_wait */
+       0xe7f104bd,
+       0xe4b607ac,
+       0x00eecf06,
+       0x7000e4f1,
+       0xf1f21bf4,
+       0xb607a4d7,
+       0xddcf06d4,
+/* 0x003f: wr32 */
+       0xf100f800,
+       0xb607a007,
+       0x0ed00604,
+       0xf104bd00,
+       0xb607a407,
+       0x0dd00604,
+       0xf004bd00,
+       0xe5f002e7,
+       0x01e3f0f0,
+       0x07ac07f1,
+       0xd00604b6,
+       0x04bd000e,
+/* 0x006c: wr32_wait */
+       0x07ace7f1,
+       0xcf06e4b6,
+       0xe4f100ee,
+       0x1bf47000,
+/* 0x007f: nsec */
+       0xf000f8f2,
+       0x84b62c87,
+       0x0088cf06,
+/* 0x0088: nsec_loop */
+       0xb62c97f0,
+       0x99cf0694,
+       0x0298bb00,
+       0xf4069eb8,
+       0x00f8f11e,
+/* 0x009c: wait */
+       0xb62c87f0,
+       0x88cf0684,
+/* 0x00a5: wait_loop */
+       0x02eeb900,
+       0xb90421f4,
+       0xadfd02da,
+       0x06acb804,
+       0xf0150bf4,
+       0x94b62c97,
+       0x0099cf06,
+       0xb80298bb,
+       0x1ef4069b,
+/* 0x00c9: wait_done */
+/* 0x00cb: intr_watchdog */
+       0x9800f8df,
+       0x96b003e9,
+       0x2a0bf400,
+       0xbb840a98,
+       0x1cf4029a,
+       0x01d7f00f,
+       0x025421f5,
+       0x0ef494bd,
+/* 0x00e9: intr_watchdog_next_time */
+       0x850a9815,
+       0xf400a6b0,
+       0x9ab8090b,
+       0x061cf406,
+/* 0x00f8: intr_watchdog_next_time_set */
+/* 0x00fb: intr_watchdog_next_proc */
+       0x80850980,
+       0xe0b603e9,
+       0x10e6b158,
+       0xc61bf402,
+/* 0x010a: intr */
+       0x00f900f8,
+       0x80f904bd,
+       0xa0f990f9,
+       0xc0f9b0f9,
+       0xe0f9d0f9,
+       0xf7f0f0f9,
+       0x0188fe00,
+       0x87f180f9,
+       0x84b605d0,
+       0x0088cf06,
+       0xf10180b6,
+       0xb605d007,
+       0x08d00604,
+       0xf004bd00,
+       0x84b60887,
+       0x0088cf06,
+       0xf40289c4,
+       0x0080230b,
+       0x58e7f085,
+       0x98cb21f4,
+       0x96b08509,
+       0x110bf400,
+       0xb63407f0,
+       0x09d00604,
+       0x8004bd00,
+/* 0x016e: intr_skip_watchdog */
+       0x89e48409,
+       0x0bf40800,
+       0x8897f148,
+       0x0694b606,
+       0xc40099cf,
+       0x0bf4029a,
+       0xc0c7f12c,
+       0x06c4b604,
+       0xf900cccf,
+       0x48e7f1c0,
+       0x53e3f14f,
+       0x00d7f054,
+       0x02b921f5,
+       0x07f1c0fc,
+       0x04b604c0,
+       0x000cd006,
+/* 0x01ae: intr_subintr_skip_fifo */
+       0x07f104bd,
+       0x04b60688,
+       0x0009d006,
+/* 0x01ba: intr_skip_subintr */
+       0x89c404bd,
+       0x070bf420,
+       0xffbfa4f1,
+/* 0x01c4: intr_skip_pause */
+       0xf44089c4,
+       0xa4f1070b,
+/* 0x01ce: intr_skip_user0 */
+       0x07f0ffbf,
+       0x0604b604,
+       0xbd0008d0,
+       0xfe80fc04,
+       0xf0fc0088,
+       0xd0fce0fc,
+       0xb0fcc0fc,
+       0x90fca0fc,
+       0x00fc80fc,
+       0xf80032f4,
+/* 0x01f5: timer */
+       0x1032f401,
+       0xb003f898,
+       0x1cf40086,
+       0x03fe8051,
+       0xb63807f0,
+       0x08d00604,
+       0xf004bd00,
+       0x84b60887,
+       0x0088cf06,
+       0xf40284f0,
+       0x87f0261b,
+       0x0684b634,
+       0xb80088cf,
+       0x0bf406e0,
+       0x06e8b809,
+/* 0x0233: timer_reset */
+       0xf01f1ef4,
+       0x04b63407,
+       0x000ed006,
+       0x0e8004bd,
+/* 0x0241: timer_enable */
+       0x0187f084,
+       0xb63807f0,
+       0x08d00604,
+/* 0x024f: timer_done */
+       0xf404bd00,
+       0x00f81031,
+/* 0x0254: send_proc */
+       0x90f980f9,
+       0x9805e898,
+       0x86f004e9,
+       0x0689b804,
+       0xc42a0bf4,
+       0x88940398,
+       0x1880b604,
+       0x98008ebb,
+       0x8a8000fa,
+       0x018d8000,
+       0x80028c80,
+       0x90b6038b,
+       0x0794f001,
+       0xf404e980,
+/* 0x028e: send_done */
+       0x90fc0231,
+       0x00f880fc,
+/* 0x0294: find */
+       0x87f080f9,
+       0x0131f458,
+/* 0x029c: find_loop */
+       0xb8008a98,
+       0x0bf406ae,
+       0x5880b610,
+       0x021086b1,
+       0xf4f01bf4,
+/* 0x02b2: find_done */
+       0x8eb90132,
+       0xf880fc02,
+/* 0x02b9: send */
+       0x9421f500,
+       0x9701f402,
+/* 0x02c2: recv */
+       0xe89800f8,
+       0x04e99805,
+       0xb80132f4,
+       0x0bf40689,
+       0x0389c43d,
+       0xf00180b6,
+       0xe8800784,
+       0x02ea9805,
+       0x8ffef0f9,
+       0xb9f0f901,
+       0x999402ef,
+       0x00e9bb04,
+       0x9818e0b6,
+       0xec9803eb,
+       0x01ed9802,
+       0xf900ee98,
+       0xfef0fca5,
+       0x31f400f8,
+/* 0x030b: recv_done */
+       0xf8f0fc01,
+/* 0x030d: init */
+       0x0817f100,
+       0x0614b601,
+       0xe70011cf,
+       0xb6010911,
+       0x14fe0814,
+       0xe017f100,
+       0x0013f000,
+       0xb61c07f0,
+       0x01d00604,
+       0xf004bd00,
+       0x07f0ff17,
+       0x0604b614,
+       0xbd0001d0,
+       0x0217f004,
+       0x080015f1,
+       0xb61007f0,
+       0x01d00604,
+       0xf104bd00,
+       0xf0010a17,
+       0x10fe0013,
+       0x1031f400,
+       0xf00117f0,
+       0x04b63807,
+       0x0001d006,
+       0xf7f004bd,
+/* 0x0371: init_proc */
+       0x01f19858,
+       0xf40016b0,
+       0x15f9fa0b,
+       0xf458f0b6,
+/* 0x0382: host_send */
+       0x17f1f20e,
+       0x14b604b0,
+       0x0011cf06,
+       0x04a027f1,
+       0xcf0624b6,
+       0x12b80022,
+       0x320bf406,
+       0x94071ec4,
+       0xe0b704ee,
+       0xeb980218,
+       0x02ec9803,
+       0x9801ed98,
+       0x21f500ee,
+       0x10b602b9,
+       0x0f1ec401,
+       0x04b007f1,
+       0xd00604b6,
+       0x04bd0001,
+/* 0x03cb: host_send_done */
+       0xf8ba0ef4,
+/* 0x03cd: host_recv */
+       0x4917f100,
+       0x5413f14e,
+       0x06e1b852,
+/* 0x03db: host_recv_wait */
+       0xf1aa0bf4,
+       0xb604cc17,
+       0x11cf0614,
+       0xc827f100,
+       0x0624b604,
+       0xf00022cf,
+       0x12b80816,
+       0xe60bf406,
+       0xb60723c4,
+       0x30b70434,
+       0x3b800298,
+       0x023c8003,
+       0x80013d80,
+       0x20b6003e,
+       0x0f24f001,
+       0x04c807f1,
+       0xd00604b6,
+       0x04bd0002,
+       0xf04027f0,
+       0x04b60007,
+       0x0002d006,
+       0x00f804bd,
+/* 0x0430: host_init */
+       0x008017f1,
+       0xf11014b6,
+       0xf1021815,
+       0xb604d007,
+       0x01d00604,
+       0xf104bd00,
+       0xb6008017,
+       0x15f11014,
+       0x07f10298,
+       0x04b604dc,
+       0x0001d006,
+       0x17f004bd,
+       0xc407f101,
+       0x0604b604,
+       0xbd0001d0,
+/* 0x046f: memx_func_enter */
+       0xf000f804,
+       0x07f10467,
+       0x04b607e0,
+       0x0006d006,
+/* 0x047e: memx_func_enter_wait */
+       0x67f104bd,
+       0x64b607c0,
+       0x0066cf06,
+       0xf40464f0,
+       0x1698f30b,
+       0x0410b600,
+/* 0x0496: memx_func_leave */
+       0x67f000f8,
+       0xe407f104,
+       0x0604b607,
+       0xbd0006d0,
+/* 0x04a5: memx_func_leave_wait */
+       0xc067f104,
+       0x0664b607,
+       0xf00066cf,
+       0x1bf40464,
+/* 0x04b7: memx_func_wr32 */
+       0x9800f8f3,
+       0x15980016,
+       0x0810b601,
+       0x50f960f9,
+       0xe0fcd0fc,
+       0xf13f21f4,
+       0xfd140003,
+       0x05800506,
+       0xb604bd00,
+       0x1bf40242,
+/* 0x04df: memx_func_wait */
+       0xf000f8dd,
+       0x84b62c87,
+       0x0088cf06,
+       0x98001e98,
+       0x1c98011d,
+       0x031b9802,
+       0xf41010b6,
+       0x00f89c21,
+/* 0x04fc: memx_func_delay */
+       0xb6001e98,
+       0x21f40410,
+/* 0x0507: memx_exec */
+       0xf900f87f,
+       0xb9d0f9e0,
+       0xb2b902c1,
+/* 0x0511: memx_exec_next */
+       0x00139802,
+       0x950410b6,
+       0x30f01034,
+       0xc835980c,
+       0x12b855f9,
+       0xec1ef406,
+       0xe0fcd0fc,
+       0x02b921f5,
+/* 0x0532: memx_info */
+       0xc7f100f8,
+       0xb7f10354,
+       0x21f50800,
+       0x00f802b9,
+/* 0x0540: memx_recv */
+       0xf401d6b0,
+       0xd6b0c40b,
+       0xe90bf400,
+/* 0x054e: memx_init */
+       0x00f800f8,
+/* 0x0550: perf_recv */
+/* 0x0552: perf_init */
+       0x00f800f8,
+/* 0x0554: test_recv */
+       0x05d817f1,
+       0xcf0614b6,
+       0x10b60011,
+       0xd807f101,
+       0x0604b605,
+       0xbd0001d0,
+       0x00e7f104,
+       0x4fe3f1d9,
+       0xf521f513,
+/* 0x057b: test_init */
+       0xf100f801,
+       0xf50800e7,
+       0xf801f521,
+/* 0x0585: idle_recv */
+/* 0x0587: idle */
+       0xf400f800,
+       0x17f10031,
+       0x14b605d4,
+       0x0011cf06,
+       0xf10110b6,
+       0xb605d407,
+       0x01d00604,
+/* 0x05a3: idle_loop */
+       0xf004bd00,
+       0x32f45817,
+/* 0x05a9: idle_proc */
+/* 0x05a9: idle_proc_exec */
+       0xb910f902,
+       0x21f5021e,
+       0x10fc02c2,
+       0xf40911f4,
+       0x0ef40231,
+/* 0x05bd: idle_proc_next */
+       0x5810b6ef,
+       0xf4061fb8,
+       0x02f4e61b,
+       0x0028f4dd,
+       0x00bb0ef4,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc
new file mode 100644 (file)
index 0000000..eaa64da
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define NVKM_PPWR_CHIPSET GF100
+
+//#define NVKM_FALCON_PC24
+//#define NVKM_FALCON_UNSHIFTED_IO
+//#define NVKM_FALCON_MMIO_UAS
+//#define NVKM_FALCON_MMIO_TRAP
+
+#include "macros.fuc"
+
+.section #nvc0_pwr_data
+#define INCLUDE_PROC
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_PROC
+
+#define INCLUDE_DATA
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_DATA
+.align 256
+
+.section #nvc0_pwr_code
+#define INCLUDE_CODE
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_CODE
+.align 256
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h
new file mode 100644 (file)
index 0000000..82c8e8b
--- /dev/null
@@ -0,0 +1,1229 @@
+uint32_t nvc0_pwr_data[] = {
+/* 0x0000: proc_kern */
+       0x52544e49,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0058: proc_list_head */
+       0x54534f48,
+       0x00000430,
+       0x000003cd,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x584d454d,
+       0x0000054e,
+       0x00000540,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x46524550,
+       0x00000552,
+       0x00000550,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x54534554,
+       0x0000057b,
+       0x00000554,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x454c4449,
+       0x00000587,
+       0x00000585,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0210: proc_list_tail */
+/* 0x0210: time_prev */
+       0x00000000,
+/* 0x0214: time_next */
+       0x00000000,
+/* 0x0218: fifo_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0298: rfifo_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0318: memx_func_head */
+       0x00010000,
+       0x00000000,
+       0x0000046f,
+/* 0x0324: memx_func_next */
+       0x00000001,
+       0x00000000,
+       0x00000496,
+       0x00000002,
+       0x00000002,
+       0x000004b7,
+       0x00040003,
+       0x00000000,
+       0x000004df,
+       0x00010004,
+       0x00000000,
+       0x000004fc,
+/* 0x0354: memx_func_tail */
+/* 0x0354: memx_data_head */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0b54: memx_data_tail */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
+
+uint32_t nvc0_pwr_code[] = {
+       0x030d0ef5,
+/* 0x0004: rd32 */
+       0x07a007f1,
+       0xd00604b6,
+       0x04bd000e,
+       0xf001e7f0,
+       0x07f101e3,
+       0x04b607ac,
+       0x000ed006,
+/* 0x0022: rd32_wait */
+       0xe7f104bd,
+       0xe4b607ac,
+       0x00eecf06,
+       0x7000e4f1,
+       0xf1f21bf4,
+       0xb607a4d7,
+       0xddcf06d4,
+/* 0x003f: wr32 */
+       0xf100f800,
+       0xb607a007,
+       0x0ed00604,
+       0xf104bd00,
+       0xb607a407,
+       0x0dd00604,
+       0xf004bd00,
+       0xe5f002e7,
+       0x01e3f0f0,
+       0x07ac07f1,
+       0xd00604b6,
+       0x04bd000e,
+/* 0x006c: wr32_wait */
+       0x07ace7f1,
+       0xcf06e4b6,
+       0xe4f100ee,
+       0x1bf47000,
+/* 0x007f: nsec */
+       0xf000f8f2,
+       0x84b62c87,
+       0x0088cf06,
+/* 0x0088: nsec_loop */
+       0xb62c97f0,
+       0x99cf0694,
+       0x0298bb00,
+       0xf4069eb8,
+       0x00f8f11e,
+/* 0x009c: wait */
+       0xb62c87f0,
+       0x88cf0684,
+/* 0x00a5: wait_loop */
+       0x02eeb900,
+       0xb90421f4,
+       0xadfd02da,
+       0x06acb804,
+       0xf0150bf4,
+       0x94b62c97,
+       0x0099cf06,
+       0xb80298bb,
+       0x1ef4069b,
+/* 0x00c9: wait_done */
+/* 0x00cb: intr_watchdog */
+       0x9800f8df,
+       0x96b003e9,
+       0x2a0bf400,
+       0xbb840a98,
+       0x1cf4029a,
+       0x01d7f00f,
+       0x025421f5,
+       0x0ef494bd,
+/* 0x00e9: intr_watchdog_next_time */
+       0x850a9815,
+       0xf400a6b0,
+       0x9ab8090b,
+       0x061cf406,
+/* 0x00f8: intr_watchdog_next_time_set */
+/* 0x00fb: intr_watchdog_next_proc */
+       0x80850980,
+       0xe0b603e9,
+       0x10e6b158,
+       0xc61bf402,
+/* 0x010a: intr */
+       0x00f900f8,
+       0x80f904bd,
+       0xa0f990f9,
+       0xc0f9b0f9,
+       0xe0f9d0f9,
+       0xf7f0f0f9,
+       0x0188fe00,
+       0x87f180f9,
+       0x84b605d0,
+       0x0088cf06,
+       0xf10180b6,
+       0xb605d007,
+       0x08d00604,
+       0xf004bd00,
+       0x84b60887,
+       0x0088cf06,
+       0xf40289c4,
+       0x0080230b,
+       0x58e7f085,
+       0x98cb21f4,
+       0x96b08509,
+       0x110bf400,
+       0xb63407f0,
+       0x09d00604,
+       0x8004bd00,
+/* 0x016e: intr_skip_watchdog */
+       0x89e48409,
+       0x0bf40800,
+       0x8897f148,
+       0x0694b606,
+       0xc40099cf,
+       0x0bf4029a,
+       0xc0c7f12c,
+       0x06c4b604,
+       0xf900cccf,
+       0x48e7f1c0,
+       0x53e3f14f,
+       0x00d7f054,
+       0x02b921f5,
+       0x07f1c0fc,
+       0x04b604c0,
+       0x000cd006,
+/* 0x01ae: intr_subintr_skip_fifo */
+       0x07f104bd,
+       0x04b60688,
+       0x0009d006,
+/* 0x01ba: intr_skip_subintr */
+       0x89c404bd,
+       0x070bf420,
+       0xffbfa4f1,
+/* 0x01c4: intr_skip_pause */
+       0xf44089c4,
+       0xa4f1070b,
+/* 0x01ce: intr_skip_user0 */
+       0x07f0ffbf,
+       0x0604b604,
+       0xbd0008d0,
+       0xfe80fc04,
+       0xf0fc0088,
+       0xd0fce0fc,
+       0xb0fcc0fc,
+       0x90fca0fc,
+       0x00fc80fc,
+       0xf80032f4,
+/* 0x01f5: timer */
+       0x1032f401,
+       0xb003f898,
+       0x1cf40086,
+       0x03fe8051,
+       0xb63807f0,
+       0x08d00604,
+       0xf004bd00,
+       0x84b60887,
+       0x0088cf06,
+       0xf40284f0,
+       0x87f0261b,
+       0x0684b634,
+       0xb80088cf,
+       0x0bf406e0,
+       0x06e8b809,
+/* 0x0233: timer_reset */
+       0xf01f1ef4,
+       0x04b63407,
+       0x000ed006,
+       0x0e8004bd,
+/* 0x0241: timer_enable */
+       0x0187f084,
+       0xb63807f0,
+       0x08d00604,
+/* 0x024f: timer_done */
+       0xf404bd00,
+       0x00f81031,
+/* 0x0254: send_proc */
+       0x90f980f9,
+       0x9805e898,
+       0x86f004e9,
+       0x0689b804,
+       0xc42a0bf4,
+       0x88940398,
+       0x1880b604,
+       0x98008ebb,
+       0x8a8000fa,
+       0x018d8000,
+       0x80028c80,
+       0x90b6038b,
+       0x0794f001,
+       0xf404e980,
+/* 0x028e: send_done */
+       0x90fc0231,
+       0x00f880fc,
+/* 0x0294: find */
+       0x87f080f9,
+       0x0131f458,
+/* 0x029c: find_loop */
+       0xb8008a98,
+       0x0bf406ae,
+       0x5880b610,
+       0x021086b1,
+       0xf4f01bf4,
+/* 0x02b2: find_done */
+       0x8eb90132,
+       0xf880fc02,
+/* 0x02b9: send */
+       0x9421f500,
+       0x9701f402,
+/* 0x02c2: recv */
+       0xe89800f8,
+       0x04e99805,
+       0xb80132f4,
+       0x0bf40689,
+       0x0389c43d,
+       0xf00180b6,
+       0xe8800784,
+       0x02ea9805,
+       0x8ffef0f9,
+       0xb9f0f901,
+       0x999402ef,
+       0x00e9bb04,
+       0x9818e0b6,
+       0xec9803eb,
+       0x01ed9802,
+       0xf900ee98,
+       0xfef0fca5,
+       0x31f400f8,
+/* 0x030b: recv_done */
+       0xf8f0fc01,
+/* 0x030d: init */
+       0x0817f100,
+       0x0614b601,
+       0xe70011cf,
+       0xb6010911,
+       0x14fe0814,
+       0xe017f100,
+       0x0013f000,
+       0xb61c07f0,
+       0x01d00604,
+       0xf004bd00,
+       0x07f0ff17,
+       0x0604b614,
+       0xbd0001d0,
+       0x0217f004,
+       0x080015f1,
+       0xb61007f0,
+       0x01d00604,
+       0xf104bd00,
+       0xf0010a17,
+       0x10fe0013,
+       0x1031f400,
+       0xf00117f0,
+       0x04b63807,
+       0x0001d006,
+       0xf7f004bd,
+/* 0x0371: init_proc */
+       0x01f19858,
+       0xf40016b0,
+       0x15f9fa0b,
+       0xf458f0b6,
+/* 0x0382: host_send */
+       0x17f1f20e,
+       0x14b604b0,
+       0x0011cf06,
+       0x04a027f1,
+       0xcf0624b6,
+       0x12b80022,
+       0x320bf406,
+       0x94071ec4,
+       0xe0b704ee,
+       0xeb980218,
+       0x02ec9803,
+       0x9801ed98,
+       0x21f500ee,
+       0x10b602b9,
+       0x0f1ec401,
+       0x04b007f1,
+       0xd00604b6,
+       0x04bd0001,
+/* 0x03cb: host_send_done */
+       0xf8ba0ef4,
+/* 0x03cd: host_recv */
+       0x4917f100,
+       0x5413f14e,
+       0x06e1b852,
+/* 0x03db: host_recv_wait */
+       0xf1aa0bf4,
+       0xb604cc17,
+       0x11cf0614,
+       0xc827f100,
+       0x0624b604,
+       0xf00022cf,
+       0x12b80816,
+       0xe60bf406,
+       0xb60723c4,
+       0x30b70434,
+       0x3b800298,
+       0x023c8003,
+       0x80013d80,
+       0x20b6003e,
+       0x0f24f001,
+       0x04c807f1,
+       0xd00604b6,
+       0x04bd0002,
+       0xf04027f0,
+       0x04b60007,
+       0x0002d006,
+       0x00f804bd,
+/* 0x0430: host_init */
+       0x008017f1,
+       0xf11014b6,
+       0xf1021815,
+       0xb604d007,
+       0x01d00604,
+       0xf104bd00,
+       0xb6008017,
+       0x15f11014,
+       0x07f10298,
+       0x04b604dc,
+       0x0001d006,
+       0x17f004bd,
+       0xc407f101,
+       0x0604b604,
+       0xbd0001d0,
+/* 0x046f: memx_func_enter */
+       0xf000f804,
+       0x07f10467,
+       0x04b607e0,
+       0x0006d006,
+/* 0x047e: memx_func_enter_wait */
+       0x67f104bd,
+       0x64b607c0,
+       0x0066cf06,
+       0xf40464f0,
+       0x1698f30b,
+       0x0410b600,
+/* 0x0496: memx_func_leave */
+       0x67f000f8,
+       0xe407f104,
+       0x0604b607,
+       0xbd0006d0,
+/* 0x04a5: memx_func_leave_wait */
+       0xc067f104,
+       0x0664b607,
+       0xf00066cf,
+       0x1bf40464,
+/* 0x04b7: memx_func_wr32 */
+       0x9800f8f3,
+       0x15980016,
+       0x0810b601,
+       0x50f960f9,
+       0xe0fcd0fc,
+       0xf13f21f4,
+       0xfd140003,
+       0x05800506,
+       0xb604bd00,
+       0x1bf40242,
+/* 0x04df: memx_func_wait */
+       0xf000f8dd,
+       0x84b62c87,
+       0x0088cf06,
+       0x98001e98,
+       0x1c98011d,
+       0x031b9802,
+       0xf41010b6,
+       0x00f89c21,
+/* 0x04fc: memx_func_delay */
+       0xb6001e98,
+       0x21f40410,
+/* 0x0507: memx_exec */
+       0xf900f87f,
+       0xb9d0f9e0,
+       0xb2b902c1,
+/* 0x0511: memx_exec_next */
+       0x00139802,
+       0x950410b6,
+       0x30f01034,
+       0xc835980c,
+       0x12b855f9,
+       0xec1ef406,
+       0xe0fcd0fc,
+       0x02b921f5,
+/* 0x0532: memx_info */
+       0xc7f100f8,
+       0xb7f10354,
+       0x21f50800,
+       0x00f802b9,
+/* 0x0540: memx_recv */
+       0xf401d6b0,
+       0xd6b0c40b,
+       0xe90bf400,
+/* 0x054e: memx_init */
+       0x00f800f8,
+/* 0x0550: perf_recv */
+/* 0x0552: perf_init */
+       0x00f800f8,
+/* 0x0554: test_recv */
+       0x05d817f1,
+       0xcf0614b6,
+       0x10b60011,
+       0xd807f101,
+       0x0604b605,
+       0xbd0001d0,
+       0x00e7f104,
+       0x4fe3f1d9,
+       0xf521f513,
+/* 0x057b: test_init */
+       0xf100f801,
+       0xf50800e7,
+       0xf801f521,
+/* 0x0585: idle_recv */
+/* 0x0587: idle */
+       0xf400f800,
+       0x17f10031,
+       0x14b605d4,
+       0x0011cf06,
+       0xf10110b6,
+       0xb605d407,
+       0x01d00604,
+/* 0x05a3: idle_loop */
+       0xf004bd00,
+       0x32f45817,
+/* 0x05a9: idle_proc */
+/* 0x05a9: idle_proc_exec */
+       0xb910f902,
+       0x21f5021e,
+       0x10fc02c2,
+       0xf40911f4,
+       0x0ef40231,
+/* 0x05bd: idle_proc_next */
+       0x5810b6ef,
+       0xf4061fb8,
+       0x02f4e61b,
+       0x0028f4dd,
+       0x00bb0ef4,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc
new file mode 100644 (file)
index 0000000..32d65ea
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define NVKM_PPWR_CHIPSET GF119
+
+//#define NVKM_FALCON_PC24
+#define NVKM_FALCON_UNSHIFTED_IO
+//#define NVKM_FALCON_MMIO_UAS
+//#define NVKM_FALCON_MMIO_TRAP
+
+#include "macros.fuc"
+
+.section #nvd0_pwr_data
+#define INCLUDE_PROC
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_PROC
+
+#define INCLUDE_DATA
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_DATA
+.align 256
+
+.section #nvd0_pwr_code
+#define INCLUDE_CODE
+#include "kernel.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_CODE
+.align 256
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h
new file mode 100644 (file)
index 0000000..ce65e2a
--- /dev/null
@@ -0,0 +1,1229 @@
+uint32_t nvd0_pwr_data[] = {
+/* 0x0000: proc_kern */
+       0x52544e49,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0058: proc_list_head */
+       0x54534f48,
+       0x000003be,
+       0x00000367,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x584d454d,
+       0x000004c4,
+       0x000004b6,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x46524550,
+       0x000004c8,
+       0x000004c6,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x54534554,
+       0x000004eb,
+       0x000004ca,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x454c4449,
+       0x000004f7,
+       0x000004f5,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0210: proc_list_tail */
+/* 0x0210: time_prev */
+       0x00000000,
+/* 0x0214: time_next */
+       0x00000000,
+/* 0x0218: fifo_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0298: rfifo_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0318: memx_func_head */
+       0x00010000,
+       0x00000000,
+       0x000003f4,
+/* 0x0324: memx_func_next */
+       0x00000001,
+       0x00000000,
+       0x00000415,
+       0x00000002,
+       0x00000002,
+       0x00000430,
+       0x00040003,
+       0x00000000,
+       0x00000458,
+       0x00010004,
+       0x00000000,
+       0x00000472,
+/* 0x0354: memx_func_tail */
+/* 0x0354: memx_data_head */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0b54: memx_data_tail */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
+
+uint32_t nvd0_pwr_code[] = {
+       0x02bf0ef5,
+/* 0x0004: rd32 */
+       0x07a007f1,
+       0xbd000ed0,
+       0x01e7f004,
+       0xf101e3f0,
+       0xd007ac07,
+       0x04bd000e,
+/* 0x001c: rd32_wait */
+       0x07ace7f1,
+       0xf100eecf,
+       0xf47000e4,
+       0xd7f1f51b,
+       0xddcf07a4,
+/* 0x0033: wr32 */
+       0xf100f800,
+       0xd007a007,
+       0x04bd000e,
+       0x07a407f1,
+       0xbd000dd0,
+       0x02e7f004,
+       0xf0f0e5f0,
+       0x07f101e3,
+       0x0ed007ac,
+/* 0x0057: wr32_wait */
+       0xf104bd00,
+       0xcf07ace7,
+       0xe4f100ee,
+       0x1bf47000,
+/* 0x0067: nsec */
+       0xf000f8f5,
+       0x88cf2c87,
+/* 0x006d: nsec_loop */
+       0x2c97f000,
+       0xbb0099cf,
+       0x9eb80298,
+       0xf41ef406,
+/* 0x007e: wait */
+       0x87f000f8,
+       0x0088cf2c,
+/* 0x0084: wait_loop */
+       0xf402eeb9,
+       0xdab90421,
+       0x04adfd02,
+       0xf406acb8,
+       0x97f0120b,
+       0x0099cf2c,
+       0xb80298bb,
+       0x1ef4069b,
+/* 0x00a5: wait_done */
+/* 0x00a7: intr_watchdog */
+       0x9800f8e2,
+       0x96b003e9,
+       0x2a0bf400,
+       0xbb840a98,
+       0x1cf4029a,
+       0x01d7f00f,
+       0x020621f5,
+       0x0ef494bd,
+/* 0x00c5: intr_watchdog_next_time */
+       0x850a9815,
+       0xf400a6b0,
+       0x9ab8090b,
+       0x061cf406,
+/* 0x00d4: intr_watchdog_next_time_set */
+/* 0x00d7: intr_watchdog_next_proc */
+       0x80850980,
+       0xe0b603e9,
+       0x10e6b158,
+       0xc61bf402,
+/* 0x00e6: intr */
+       0x00f900f8,
+       0x80f904bd,
+       0xa0f990f9,
+       0xc0f9b0f9,
+       0xe0f9d0f9,
+       0xf7f0f0f9,
+       0x0188fe00,
+       0x87f180f9,
+       0x88cf05d0,
+       0x0180b600,
+       0x05d007f1,
+       0xbd0008d0,
+       0x0887f004,
+       0xc40088cf,
+       0x0bf40289,
+       0x85008020,
+       0xf458e7f0,
+       0x0998a721,
+       0x0096b085,
+       0xf00e0bf4,
+       0x09d03407,
+       0x8004bd00,
+/* 0x013e: intr_skip_watchdog */
+       0x89e48409,
+       0x0bf40800,
+       0x8897f13c,
+       0x0099cf06,
+       0xf4029ac4,
+       0xc7f1260b,
+       0xcccf04c0,
+       0xf1c0f900,
+       0xf14f48e7,
+       0xf05453e3,
+       0x21f500d7,
+       0xc0fc026b,
+       0x04c007f1,
+       0xbd000cd0,
+/* 0x0175: intr_subintr_skip_fifo */
+       0x8807f104,
+       0x0009d006,
+/* 0x017e: intr_skip_subintr */
+       0x89c404bd,
+       0x070bf420,
+       0xffbfa4f1,
+/* 0x0188: intr_skip_pause */
+       0xf44089c4,
+       0xa4f1070b,
+/* 0x0192: intr_skip_user0 */
+       0x07f0ffbf,
+       0x0008d004,
+       0x80fc04bd,
+       0xfc0088fe,
+       0xfce0fcf0,
+       0xfcc0fcd0,
+       0xfca0fcb0,
+       0xfc80fc90,
+       0x0032f400,
+/* 0x01b6: timer */
+       0x32f401f8,
+       0x03f89810,
+       0xf40086b0,
+       0xfe80421c,
+       0x3807f003,
+       0xbd0008d0,
+       0x0887f004,
+       0xf00088cf,
+       0x1bf40284,
+       0x3487f020,
+       0xb80088cf,
+       0x0bf406e0,
+       0x06e8b809,
+/* 0x01eb: timer_reset */
+       0xf0191ef4,
+       0x0ed03407,
+       0x8004bd00,
+/* 0x01f6: timer_enable */
+       0x87f0840e,
+       0x3807f001,
+       0xbd0008d0,
+/* 0x0201: timer_done */
+       0x1031f404,
+/* 0x0206: send_proc */
+       0x80f900f8,
+       0xe89890f9,
+       0x04e99805,
+       0xb80486f0,
+       0x0bf40689,
+       0x0398c42a,
+       0xb6048894,
+       0x8ebb1880,
+       0x00fa9800,
+       0x80008a80,
+       0x8c80018d,
+       0x038b8002,
+       0xf00190b6,
+       0xe9800794,
+       0x0231f404,
+/* 0x0240: send_done */
+       0x80fc90fc,
+/* 0x0246: find */
+       0x80f900f8,
+       0xf45887f0,
+/* 0x024e: find_loop */
+       0x8a980131,
+       0x06aeb800,
+       0xb6100bf4,
+       0x86b15880,
+       0x1bf40210,
+       0x0132f4f0,
+/* 0x0264: find_done */
+       0xfc028eb9,
+/* 0x026b: send */
+       0xf500f880,
+       0xf4024621,
+       0x00f89701,
+/* 0x0274: recv */
+       0x9805e898,
+       0x32f404e9,
+       0x0689b801,
+       0xc43d0bf4,
+       0x80b60389,
+       0x0784f001,
+       0x9805e880,
+       0xf0f902ea,
+       0xf9018ffe,
+       0x02efb9f0,
+       0xbb049994,
+       0xe0b600e9,
+       0x03eb9818,
+       0x9802ec98,
+       0xee9801ed,
+       0xfca5f900,
+       0x00f8fef0,
+       0xfc0131f4,
+/* 0x02bd: recv_done */
+/* 0x02bf: init */
+       0xf100f8f0,
+       0xcf010817,
+       0x11e70011,
+       0x14b60109,
+       0x0014fe08,
+       0x00e017f1,
+       0xf00013f0,
+       0x01d01c07,
+       0xf004bd00,
+       0x07f0ff17,
+       0x0001d014,
+       0x17f004bd,
+       0x0015f102,
+       0x1007f008,
+       0xbd0001d0,
+       0xe617f104,
+       0x0013f000,
+       0xf40010fe,
+       0x17f01031,
+       0x3807f001,
+       0xbd0001d0,
+       0x58f7f004,
+/* 0x0314: init_proc */
+       0xb001f198,
+       0x0bf40016,
+       0xb615f9fa,
+       0x0ef458f0,
+/* 0x0325: host_send */
+       0xb017f1f2,
+       0x0011cf04,
+       0x04a027f1,
+       0xb80022cf,
+       0x0bf40612,
+       0x071ec42f,
+       0xb704ee94,
+       0x980218e0,
+       0xec9803eb,
+       0x01ed9802,
+       0xf500ee98,
+       0xb6026b21,
+       0x1ec40110,
+       0xb007f10f,
+       0x0001d004,
+       0x0ef404bd,
+/* 0x0365: host_send_done */
+/* 0x0367: host_recv */
+       0xf100f8c3,
+       0xf14e4917,
+       0xb8525413,
+       0x0bf406e1,
+/* 0x0375: host_recv_wait */
+       0xcc17f1b3,
+       0x0011cf04,
+       0x04c827f1,
+       0xf00022cf,
+       0x12b80816,
+       0xec0bf406,
+       0xb60723c4,
+       0x30b70434,
+       0x3b800298,
+       0x023c8003,
+       0x80013d80,
+       0x20b6003e,
+       0x0f24f001,
+       0x04c807f1,
+       0xbd0002d0,
+       0x4027f004,
+       0xd00007f0,
+       0x04bd0002,
+/* 0x03be: host_init */
+       0x17f100f8,
+       0x14b60080,
+       0x1815f110,
+       0xd007f102,
+       0x0001d004,
+       0x17f104bd,
+       0x14b60080,
+       0x9815f110,
+       0xdc07f102,
+       0x0001d004,
+       0x17f004bd,
+       0xc407f101,
+       0x0001d004,
+       0x00f804bd,
+/* 0x03f4: memx_func_enter */
+       0xf10467f0,
+       0xd007e007,
+       0x04bd0006,
+/* 0x0400: memx_func_enter_wait */
+       0x07c067f1,
+       0xf00066cf,
+       0x0bf40464,
+       0x001698f6,
+       0xf80410b6,
+/* 0x0415: memx_func_leave */
+       0x0467f000,
+       0x07e407f1,
+       0xbd0006d0,
+/* 0x0421: memx_func_leave_wait */
+       0xc067f104,
+       0x0066cf07,
+       0xf40464f0,
+       0x00f8f61b,
+/* 0x0430: memx_func_wr32 */
+       0x98001698,
+       0x10b60115,
+       0xf960f908,
+       0xfcd0fc50,
+       0x3321f4e0,
+       0x140003f1,
+       0x800506fd,
+       0x04bd0005,
+       0xf40242b6,
+       0x00f8dd1b,
+/* 0x0458: memx_func_wait */
+       0xcf2c87f0,
+       0x1e980088,
+       0x011d9800,
+       0x98021c98,
+       0x10b6031b,
+       0x7e21f410,
+/* 0x0472: memx_func_delay */
+       0x1e9800f8,
+       0x0410b600,
+       0xf86721f4,
+/* 0x047d: memx_exec */
+       0xf9e0f900,
+       0x02c1b9d0,
+/* 0x0487: memx_exec_next */
+       0x9802b2b9,
+       0x10b60013,
+       0x10349504,
+       0x980c30f0,
+       0x55f9c835,
+       0xf40612b8,
+       0xd0fcec1e,
+       0x21f5e0fc,
+       0x00f8026b,
+/* 0x04a8: memx_info */
+       0x0354c7f1,
+       0x0800b7f1,
+       0x026b21f5,
+/* 0x04b6: memx_recv */
+       0xd6b000f8,
+       0xc40bf401,
+       0xf400d6b0,
+       0x00f8e90b,
+/* 0x04c4: memx_init */
+/* 0x04c6: perf_recv */
+       0x00f800f8,
+/* 0x04c8: perf_init */
+/* 0x04ca: test_recv */
+       0x17f100f8,
+       0x11cf05d8,
+       0x0110b600,
+       0x05d807f1,
+       0xbd0001d0,
+       0x00e7f104,
+       0x4fe3f1d9,
+       0xb621f513,
+/* 0x04eb: test_init */
+       0xf100f801,
+       0xf50800e7,
+       0xf801b621,
+/* 0x04f5: idle_recv */
+/* 0x04f7: idle */
+       0xf400f800,
+       0x17f10031,
+       0x11cf05d4,
+       0x0110b600,
+       0x05d407f1,
+       0xbd0001d0,
+/* 0x050d: idle_loop */
+       0x5817f004,
+/* 0x0513: idle_proc */
+/* 0x0513: idle_proc_exec */
+       0xf90232f4,
+       0x021eb910,
+       0x027421f5,
+       0x11f410fc,
+       0x0231f409,
+/* 0x0527: idle_proc_next */
+       0xb6ef0ef4,
+       0x1fb85810,
+       0xe61bf406,
+       0xf4dd02f4,
+       0x0ef40028,
+       0x000000c1,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h
new file mode 100644 (file)
index 0000000..5fb0ccc
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __NVKM_PWR_OS_H__
+#define __NVKM_PWR_OS_H__
+
+/* Process names */
+#define PROC_KERN 0x52544e49
+#define PROC_IDLE 0x454c4449
+#define PROC_HOST 0x54534f48
+#define PROC_MEMX 0x584d454d
+#define PROC_PERF 0x46524550
+#define PROC_TEST 0x54534554
+
+/* KERN: message identifiers */
+#define KMSG_FIFO   0x00000000
+#define KMSG_ALARM  0x00000001
+
+/* MEMX: message identifiers */
+#define MEMX_MSG_INFO 0
+#define MEMX_MSG_EXEC 1
+
+/* MEMX: script opcode definitions */
+#define MEMX_ENTER  0
+#define MEMX_LEAVE  1
+#define MEMX_WR32   2
+#define MEMX_WAIT   3
+#define MEMX_DELAY  4
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/perf.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/perf.fuc
new file mode 100644 (file)
index 0000000..38eadf7
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifdef INCLUDE_PROC
+process(PROC_PERF, #perf_init, #perf_recv)
+#endif
+
+/******************************************************************************
+ * PERF data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+#endif
+
+/******************************************************************************
+ * PERF code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+
+// description
+//
+// $r15 - current (perf)
+// $r14 - sender process name
+// $r13 - message
+// $r12 - data0
+// $r11 - data1
+// $r0  - zero
+perf_recv:
+       ret
+
+// description
+//
+// $r15 - current (perf)
+// $r0  - zero
+perf_init:
+       ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/test.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/test.fuc
new file mode 100644 (file)
index 0000000..0c3a71b
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifdef INCLUDE_PROC
+process(PROC_TEST, #test_init, #test_recv)
+#endif
+
+/******************************************************************************
+ * TEST data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+#endif
+
+/******************************************************************************
+ * TEST code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+// description
+//
+// $r15 - current (test)
+// $r14 - sender process name
+// $r13 - message
+// $r12 - data0
+// $r11 - data1
+// $r0  - zero
+test_recv:
+       nv_iord($r1, NV_PPWR_DSCRATCH(2))
+       add b32 $r1 1
+       nv_iowr(NV_PPWR_DSCRATCH(2), $r1)
+       mov $r14 -0x2700 /* 0xd900, envyas grrr! */
+       sethi $r14 0x134f0000
+       call(timer)
+       ret
+
+// description
+//
+// $r15 - current (test)
+// $r0  - zero
+test_init:
+       mov $r14 0x800
+       call(timer)
+       ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c
new file mode 100644 (file)
index 0000000..03de310
--- /dev/null
@@ -0,0 +1,121 @@
+#ifndef __NVKM_PWR_MEMX_H__
+#define __NVKM_PWR_MEMX_H__
+
+#include <subdev/pwr.h>
+#include <subdev/pwr/fuc/os.h>
+
+struct nouveau_memx {
+       struct nouveau_pwr *ppwr;
+       u32 base;
+       u32 size;
+       struct {
+               u32 mthd;
+               u32 size;
+               u32 data[64];
+       } c;
+};
+
+static void
+memx_out(struct nouveau_memx *memx)
+{
+       struct nouveau_pwr *ppwr = memx->ppwr;
+       int i;
+
+       if (memx->c.size) {
+               nv_wr32(ppwr, 0x10a1c4, (memx->c.size << 16) | memx->c.mthd);
+               for (i = 0; i < memx->c.size; i++)
+                       nv_wr32(ppwr, 0x10a1c4, memx->c.data[i]);
+               memx->c.size = 0;
+       }
+}
+
+static void
+memx_cmd(struct nouveau_memx *memx, u32 mthd, u32 size, u32 data[])
+{
+       if ((memx->c.size + size >= ARRAY_SIZE(memx->c.data)) ||
+           (memx->c.size && memx->c.mthd != mthd))
+               memx_out(memx);
+       memcpy(&memx->c.data[memx->c.size], data, size * sizeof(data[0]));
+       memx->c.size += size;
+       memx->c.mthd  = mthd;
+}
+
+int
+nouveau_memx_init(struct nouveau_pwr *ppwr, struct nouveau_memx **pmemx)
+{
+       struct nouveau_memx *memx;
+       u32 reply[2];
+       int ret;
+
+       ret = ppwr->message(ppwr, reply, PROC_MEMX, MEMX_MSG_INFO, 0, 0);
+       if (ret)
+               return ret;
+
+       memx = *pmemx = kzalloc(sizeof(*memx), GFP_KERNEL);
+       if (!memx)
+               return -ENOMEM;
+       memx->ppwr = ppwr;
+       memx->base = reply[0];
+       memx->size = reply[1];
+
+       /* acquire data segment access */
+       do {
+               nv_wr32(ppwr, 0x10a580, 0x00000003);
+       } while (nv_rd32(ppwr, 0x10a580) != 0x00000003);
+       nv_wr32(ppwr, 0x10a1c0, 0x01000000 | memx->base);
+       nv_wr32(ppwr, 0x10a1c4, 0x00010000 | MEMX_ENTER);
+       nv_wr32(ppwr, 0x10a1c4, 0x00000000);
+       return 0;
+}
+
+int
+nouveau_memx_fini(struct nouveau_memx **pmemx, bool exec)
+{
+       struct nouveau_memx *memx = *pmemx;
+       struct nouveau_pwr *ppwr = memx->ppwr;
+       u32 finish, reply[2];
+
+       /* flush the cache... */
+       memx_out(memx);
+
+       /* release data segment access */
+       nv_wr32(ppwr, 0x10a1c4, 0x00000000 | MEMX_LEAVE);
+       finish = nv_rd32(ppwr, 0x10a1c0) & 0x00ffffff;
+       nv_wr32(ppwr, 0x10a580, 0x00000000);
+
+       /* call MEMX process to execute the script, and wait for reply */
+       if (exec) {
+               ppwr->message(ppwr, reply, PROC_MEMX, MEMX_MSG_EXEC,
+                                memx->base, finish);
+       }
+
+       kfree(memx);
+       return 0;
+}
+
+void
+nouveau_memx_wr32(struct nouveau_memx *memx, u32 addr, u32 data)
+{
+       nv_debug(memx->ppwr, "R[%06x] = 0x%08x\n", addr, data);
+       memx_cmd(memx, MEMX_WR32, 2, (u32[]){ addr, data });
+}
+
+void
+nouveau_memx_wait(struct nouveau_memx *memx,
+                 u32 addr, u32 mask, u32 data, u32 nsec)
+{
+       nv_debug(memx->ppwr, "R[%06x] & 0x%08x == 0x%08x, %d us\n",
+                               addr, mask, data, nsec);
+       memx_cmd(memx, MEMX_WAIT, 4, (u32[]){ addr, ~mask, data, nsec });
+       memx_out(memx); /* fuc can't handle multiple */
+}
+
+void
+nouveau_memx_nsec(struct nouveau_memx *memx, u32 nsec)
+{
+       nv_debug(memx->ppwr, "    DELAY = %d ns\n", nsec);
+       memx_cmd(memx, MEMX_DELAY, 1, (u32[]){ nsec });
+       memx_out(memx); /* fuc can't handle multiple */
+}
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/nv108.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/nv108.c
new file mode 100644 (file)
index 0000000..52c8541
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/pwr.h>
+
+#include "fuc/nv108.fuc.h"
+
+struct nv108_pwr_priv {
+       struct nouveau_pwr base;
+};
+
+static int
+nv108_pwr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+              struct nouveau_oclass *oclass, void *data, u32 size,
+              struct nouveau_object **pobject)
+{
+       struct nv108_pwr_priv *priv;
+       int ret;
+
+       ret = nouveau_pwr_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.code.data = nv108_pwr_code;
+       priv->base.code.size = sizeof(nv108_pwr_code);
+       priv->base.data.data = nv108_pwr_data;
+       priv->base.data.size = sizeof(nv108_pwr_data);
+       return 0;
+}
+
+struct nouveau_oclass
+nv108_pwr_oclass = {
+       .handle = NV_SUBDEV(PWR, 0x00),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv108_pwr_ctor,
+               .dtor = _nouveau_pwr_dtor,
+               .init = _nouveau_pwr_init,
+               .fini = _nouveau_pwr_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/nva3.c
new file mode 100644 (file)
index 0000000..c132b7c
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/pwr.h>
+
+#include "fuc/nva3.fuc.h"
+
+struct nva3_pwr_priv {
+       struct nouveau_pwr base;
+};
+
+static int
+nva3_pwr_init(struct nouveau_object *object)
+{
+       struct nva3_pwr_priv *priv = (void *)object;
+       nv_mask(priv, 0x022210, 0x00000001, 0x00000000);
+       nv_mask(priv, 0x022210, 0x00000001, 0x00000001);
+       return nouveau_pwr_init(&priv->base);
+}
+
+static int
+nva3_pwr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nva3_pwr_priv *priv;
+       int ret;
+
+       ret = nouveau_pwr_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.code.data = nva3_pwr_code;
+       priv->base.code.size = sizeof(nva3_pwr_code);
+       priv->base.data.data = nva3_pwr_data;
+       priv->base.data.size = sizeof(nva3_pwr_data);
+       return 0;
+}
+
+struct nouveau_oclass
+nva3_pwr_oclass = {
+       .handle = NV_SUBDEV(PWR, 0xa3),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nva3_pwr_ctor,
+               .dtor = _nouveau_pwr_dtor,
+               .init = nva3_pwr_init,
+               .fini = _nouveau_pwr_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/nvc0.c
new file mode 100644 (file)
index 0000000..495f685
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/pwr.h>
+
+#include "fuc/nvc0.fuc.h"
+
+struct nvc0_pwr_priv {
+       struct nouveau_pwr base;
+};
+
+static int
+nvc0_pwr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nvc0_pwr_priv *priv;
+       int ret;
+
+       ret = nouveau_pwr_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.code.data = nvc0_pwr_code;
+       priv->base.code.size = sizeof(nvc0_pwr_code);
+       priv->base.data.data = nvc0_pwr_data;
+       priv->base.data.size = sizeof(nvc0_pwr_data);
+       return 0;
+}
+
+struct nouveau_oclass
+nvc0_pwr_oclass = {
+       .handle = NV_SUBDEV(PWR, 0xc0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_pwr_ctor,
+               .dtor = _nouveau_pwr_dtor,
+               .init = _nouveau_pwr_init,
+               .fini = _nouveau_pwr_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/nvd0.c
new file mode 100644 (file)
index 0000000..043aa14
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/pwr.h>
+
+#include "fuc/nvd0.fuc.h"
+
+struct nvd0_pwr_priv {
+       struct nouveau_pwr base;
+};
+
+static int
+nvd0_pwr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nvd0_pwr_priv *priv;
+       int ret;
+
+       ret = nouveau_pwr_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.code.data = nvd0_pwr_code;
+       priv->base.code.size = sizeof(nvd0_pwr_code);
+       priv->base.data.data = nvd0_pwr_data;
+       priv->base.data.size = sizeof(nvd0_pwr_data);
+       return 0;
+}
+
+struct nouveau_oclass
+nvd0_pwr_oclass = {
+       .handle = NV_SUBDEV(PWR, 0xd0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvd0_pwr_ctor,
+               .dtor = _nouveau_pwr_dtor,
+               .init = _nouveau_pwr_init,
+               .fini = _nouveau_pwr_fini,
+       },
+};
index f1de7a9..80e584a 100644 (file)
@@ -92,10 +92,11 @@ nouveau_therm_update(struct nouveau_therm *therm, int mode)
        struct nouveau_timer *ptimer = nouveau_timer(therm);
        struct nouveau_therm_priv *priv = (void *)therm;
        unsigned long flags;
-       int duty;
+       bool immd = true;
+       bool poll = true;
+       int duty = -1;
 
        spin_lock_irqsave(&priv->lock, flags);
-       nv_debug(therm, "FAN speed check\n");
        if (mode < 0)
                mode = priv->mode;
        priv->mode = mode;
@@ -106,28 +107,49 @@ nouveau_therm_update(struct nouveau_therm *therm, int mode)
                duty = nouveau_therm_fan_get(therm);
                if (duty < 0)
                        duty = 100;
+               poll = false;
                break;
        case NOUVEAU_THERM_CTRL_AUTO:
-               if (priv->fan->bios.nr_fan_trip)
+               if (priv->fan->bios.nr_fan_trip) {
                        duty = nouveau_therm_update_trip(therm);
-               else
+               } else
+               if (priv->fan->bios.linear_min_temp ||
+                   priv->fan->bios.linear_max_temp) {
                        duty = nouveau_therm_update_linear(therm);
+               } else {
+                       if (priv->cstate)
+                               duty = priv->cstate;
+                       poll = false;
+               }
+               immd = false;
                break;
        case NOUVEAU_THERM_CTRL_NONE:
        default:
                ptimer->alarm_cancel(ptimer, &priv->alarm);
-               goto done;
+               poll = false;
        }
 
-       nv_debug(therm, "FAN target request: %d%%\n", duty);
-       nouveau_therm_fan_set(therm, (mode != NOUVEAU_THERM_CTRL_AUTO), duty);
-
-done:
-       if (list_empty(&priv->alarm.head) && (mode == NOUVEAU_THERM_CTRL_AUTO))
+       if (list_empty(&priv->alarm.head) && poll)
                ptimer->alarm(ptimer, 1000000000ULL, &priv->alarm);
-       else if (!list_empty(&priv->alarm.head))
-               nv_debug(therm, "therm fan alarm list is not empty\n");
        spin_unlock_irqrestore(&priv->lock, flags);
+
+       if (duty >= 0) {
+               nv_debug(therm, "FAN target request: %d%%\n", duty);
+               nouveau_therm_fan_set(therm, immd, duty);
+       }
+}
+
+int
+nouveau_therm_cstate(struct nouveau_therm *ptherm, int fan, int dir)
+{
+       struct nouveau_therm_priv *priv = (void *)ptherm;
+       if (!dir || (dir < 0 && fan < priv->cstate) ||
+                   (dir > 0 && fan > priv->cstate)) {
+               nv_debug(ptherm, "default fan speed -> %d%%\n", fan);
+               priv->cstate = fan;
+               nouveau_therm_update(ptherm, -1);
+       }
+       return 0;
 }
 
 static void
@@ -149,14 +171,15 @@ nouveau_therm_fan_mode(struct nouveau_therm *therm, int mode)
                "automatic"
        };
 
-       /* The default PDAEMON ucode interferes with fan management */
+       /* The default PPWR ucode on fermi interferes with fan management */
        if ((mode >= ARRAY_SIZE(name)) ||
-           (mode != NOUVEAU_THERM_CTRL_NONE && device->card_type >= NV_C0))
+           (mode != NOUVEAU_THERM_CTRL_NONE && device->card_type >= NV_C0 &&
+            !nouveau_subdev(device, NVDEV_SUBDEV_PWR)))
                return -EINVAL;
 
        /* do not allow automatic fan management if the thermal sensor is
         * not available */
-       if (priv->mode == 2 && therm->temp_get(therm) < 0)
+       if (priv->mode == NOUVEAU_THERM_CTRL_AUTO && therm->temp_get(therm) < 0)
                return -EINVAL;
 
        if (priv->mode == mode)
@@ -335,7 +358,7 @@ nouveau_therm_preinit(struct nouveau_therm *therm)
        nouveau_therm_ic_ctor(therm);
        nouveau_therm_fan_ctor(therm);
 
-       nouveau_therm_fan_mode(therm, NOUVEAU_THERM_CTRL_NONE);
+       nouveau_therm_fan_mode(therm, NOUVEAU_THERM_CTRL_AUTO);
        nouveau_therm_sensor_preinit(therm);
        return 0;
 }
index 39f47b9..95f6129 100644 (file)
@@ -185,8 +185,11 @@ nouveau_therm_fan_set_defaults(struct nouveau_therm *therm)
        priv->fan->bios.max_duty = 100;
        priv->fan->bios.bump_period = 500;
        priv->fan->bios.slow_down_period = 2000;
+/*XXX: talk to mupuf */
+#if 0
        priv->fan->bios.linear_min_temp = 40;
        priv->fan->bios.linear_max_temp = 85;
+#endif
 }
 
 static void
index e601773..f69dab1 100644 (file)
@@ -97,6 +97,13 @@ nouveau_fantog_create(struct nouveau_therm *therm, struct dcb_gpio_func *func)
 {
        struct nouveau_therm_priv *tpriv = (void *)therm;
        struct nouveau_fantog_priv *priv;
+       int ret;
+
+       if (therm->pwm_ctrl) {
+               ret = therm->pwm_ctrl(therm, func->line, false);
+               if (ret)
+                       return ret;
+       }
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        tpriv->fan = &priv->base;
index 8b3adec..e44ed7b 100644 (file)
@@ -41,7 +41,8 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c,
        if (!client)
                return false;
 
-       if (!client->driver || client->driver->detect(client, info)) {
+       if (!client->dev.driver ||
+           to_i2c_driver(client->dev.driver)->detect(client, info)) {
                i2c_unregister_device(client);
                return false;
        }
@@ -55,28 +56,28 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c,
        return true;
 }
 
-static struct i2c_board_info
+static struct nouveau_i2c_board_info
 nv_board_infos[] = {
-       { I2C_BOARD_INFO("w83l785ts", 0x2d) },
-       { I2C_BOARD_INFO("w83781d", 0x2d) },
-       { I2C_BOARD_INFO("adt7473", 0x2e) },
-       { I2C_BOARD_INFO("adt7473", 0x2d) },
-       { I2C_BOARD_INFO("adt7473", 0x2c) },
-       { I2C_BOARD_INFO("f75375", 0x2e) },
-       { I2C_BOARD_INFO("lm99", 0x4c) },
-       { I2C_BOARD_INFO("lm90", 0x4c) },
-       { I2C_BOARD_INFO("lm90", 0x4d) },
-       { I2C_BOARD_INFO("adm1021", 0x18) },
-       { I2C_BOARD_INFO("adm1021", 0x19) },
-       { I2C_BOARD_INFO("adm1021", 0x1a) },
-       { I2C_BOARD_INFO("adm1021", 0x29) },
-       { I2C_BOARD_INFO("adm1021", 0x2a) },
-       { I2C_BOARD_INFO("adm1021", 0x2b) },
-       { I2C_BOARD_INFO("adm1021", 0x4c) },
-       { I2C_BOARD_INFO("adm1021", 0x4d) },
-       { I2C_BOARD_INFO("adm1021", 0x4e) },
-       { I2C_BOARD_INFO("lm63", 0x18) },
-       { I2C_BOARD_INFO("lm63", 0x4e) },
+       { { I2C_BOARD_INFO("w83l785ts", 0x2d) }, 0 },
+       { { I2C_BOARD_INFO("w83781d", 0x2d) }, 0  },
+       { { I2C_BOARD_INFO("adt7473", 0x2e) }, 20  },
+       { { I2C_BOARD_INFO("adt7473", 0x2d) }, 20  },
+       { { I2C_BOARD_INFO("adt7473", 0x2c) }, 20  },
+       { { I2C_BOARD_INFO("f75375", 0x2e) }, 0  },
+       { { I2C_BOARD_INFO("lm99", 0x4c) }, 0  },
+       { { I2C_BOARD_INFO("lm90", 0x4c) }, 0  },
+       { { I2C_BOARD_INFO("lm90", 0x4d) }, 0  },
+       { { I2C_BOARD_INFO("adm1021", 0x18) }, 0  },
+       { { I2C_BOARD_INFO("adm1021", 0x19) }, 0  },
+       { { I2C_BOARD_INFO("adm1021", 0x1a) }, 0  },
+       { { I2C_BOARD_INFO("adm1021", 0x29) }, 0  },
+       { { I2C_BOARD_INFO("adm1021", 0x2a) }, 0  },
+       { { I2C_BOARD_INFO("adm1021", 0x2b) }, 0  },
+       { { I2C_BOARD_INFO("adm1021", 0x4c) }, 0  },
+       { { I2C_BOARD_INFO("adm1021", 0x4d) }, 0  },
+       { { I2C_BOARD_INFO("adm1021", 0x4e) }, 0  },
+       { { I2C_BOARD_INFO("lm63", 0x18) }, 0  },
+       { { I2C_BOARD_INFO("lm63", 0x4e) }, 0  },
        { }
 };
 
@@ -89,9 +90,9 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm)
        struct nvbios_extdev_func extdev_entry;
 
        if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_LM89, &extdev_entry)) {
-               struct i2c_board_info board[] = {
-                       { I2C_BOARD_INFO("lm90", extdev_entry.addr >> 1) },
-                       { }
+               struct nouveau_i2c_board_info board[] = {
+                 { { I2C_BOARD_INFO("lm90", extdev_entry.addr >> 1) }, 0},
+                 { }
                };
 
                i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
@@ -101,9 +102,9 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm)
        }
 
        if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_ADT7473, &extdev_entry)) {
-               struct i2c_board_info board[] = {
-                       { I2C_BOARD_INFO("adt7473", extdev_entry.addr >> 1) },
-                       { }
+               struct nouveau_i2c_board_info board[] = {
+                 { { I2C_BOARD_INFO("adt7473", extdev_entry.addr >> 1) }, 20 },
+                 { }
                };
 
                i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
index 42ba633..1d15c52 100644 (file)
@@ -126,7 +126,7 @@ nv84_therm_intr(struct nouveau_subdev *subdev)
 
        spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
 
-       intr = nv_rd32(therm, 0x20100);
+       intr = nv_rd32(therm, 0x20100) & 0x3ff;
 
        /* THRS_4: downclock */
        if (intr & 0x002) {
@@ -209,6 +209,19 @@ nv84_therm_ctor(struct nouveau_object *parent,
        return nouveau_therm_preinit(&priv->base.base);
 }
 
+int
+nv84_therm_fini(struct nouveau_object *object, bool suspend)
+{
+       /* Disable PTherm IRQs */
+       nv_wr32(object, 0x20000, 0x00000000);
+
+       /* ACK all PTherm IRQs */
+       nv_wr32(object, 0x20100, 0xffffffff);
+       nv_wr32(object, 0x1100, 0x10000); /* PBUS */
+
+       return _nouveau_therm_fini(object, suspend);
+}
+
 struct nouveau_oclass
 nv84_therm_oclass = {
        .handle = NV_SUBDEV(THERM, 0x84),
@@ -216,6 +229,6 @@ nv84_therm_oclass = {
                .ctor = nv84_therm_ctor,
                .dtor = _nouveau_therm_dtor,
                .init = _nouveau_therm_init,
-               .fini = _nouveau_therm_fini,
+               .fini = nv84_therm_fini,
        },
 };
index d11a7c4..3b2c458 100644 (file)
@@ -94,6 +94,6 @@ nva3_therm_oclass = {
                .ctor = nva3_therm_ctor,
                .dtor = _nouveau_therm_dtor,
                .init = nva3_therm_init,
-               .fini = _nouveau_therm_fini,
+               .fini = nv84_therm_fini,
        },
 };
index 54c28bd..4dd4f81 100644 (file)
@@ -148,6 +148,6 @@ nvd0_therm_oclass = {
                .ctor = nvd0_therm_ctor,
                .dtor = _nouveau_therm_dtor,
                .init = nvd0_therm_init,
-               .fini = _nouveau_therm_fini,
+               .fini = nv84_therm_fini,
        },
 };
index dd38529..96f8f95 100644 (file)
@@ -76,6 +76,7 @@ struct nouveau_therm_priv {
        spinlock_t lock;
        struct nouveau_therm_trip_point *last_trip;
        int mode;
+       int cstate;
        int suspend;
 
        /* bios */
@@ -144,6 +145,7 @@ int nv50_fan_pwm_get(struct nouveau_therm *, int, u32 *, u32 *);
 int nv50_fan_pwm_set(struct nouveau_therm *, int, u32, u32);
 int nv50_fan_pwm_clock(struct nouveau_therm *);
 int nv84_temp_get(struct nouveau_therm *therm);
+int nv84_therm_fini(struct nouveau_object *object, bool suspend);
 
 int nva3_therm_fan_sense(struct nouveau_therm *);
 
index b80a330..cfde9eb 100644 (file)
@@ -180,8 +180,6 @@ alarm_timer_callback(struct nouveau_alarm *alarm)
 
        spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
 
-       nv_debug(therm, "polling the internal temperature\n");
-
        nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_fan_boost,
                                             NOUVEAU_THERM_THRS_FANBOOST);
 
index 57711ec..c0bdd10 100644 (file)
@@ -119,16 +119,8 @@ nv04_timer_alarm_cancel(struct nouveau_timer *ptimer,
 {
        struct nv04_timer_priv *priv = (void *)ptimer;
        unsigned long flags;
-
-       /* avoid deleting an entry while the alarm intr is running */
        spin_lock_irqsave(&priv->lock, flags);
-
-       /* delete the alarm from the list */
-       list_del(&alarm->head);
-
-       /* reset the head so as list_empty returns 1 */
-       INIT_LIST_HEAD(&alarm->head);
-
+       list_del_init(&alarm->head);
        spin_unlock_irqrestore(&priv->lock, flags);
 }
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/volt/base.c b/drivers/gpu/drm/nouveau/core/subdev/volt/base.c
new file mode 100644 (file)
index 0000000..32794a9
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/volt.h>
+
+#include <subdev/bios.h>
+#include <subdev/bios/vmap.h>
+#include <subdev/bios/volt.h>
+
+static int
+nouveau_volt_get(struct nouveau_volt *volt)
+{
+       if (volt->vid_get) {
+               int ret = volt->vid_get(volt), i;
+               if (ret >= 0) {
+                       for (i = 0; i < volt->vid_nr; i++) {
+                               if (volt->vid[i].vid == ret)
+                                       return volt->vid[i].uv;
+                       }
+                       ret = -EINVAL;
+               }
+               return ret;
+       }
+       return -ENODEV;
+}
+
+static int
+nouveau_volt_set(struct nouveau_volt *volt, u32 uv)
+{
+       if (volt->vid_set) {
+               int i, ret = -EINVAL;
+               for (i = 0; i < volt->vid_nr; i++) {
+                       if (volt->vid[i].uv == uv) {
+                               ret = volt->vid_set(volt, volt->vid[i].vid);
+                               nv_debug(volt, "set %duv: %d\n", uv, ret);
+                               break;
+                       }
+               }
+               return ret;
+       }
+       return -ENODEV;
+}
+
+static int
+nouveau_volt_map(struct nouveau_volt *volt, u8 id)
+{
+       struct nouveau_bios *bios = nouveau_bios(volt);
+       struct nvbios_vmap_entry info;
+       u8  ver, len;
+       u16 vmap;
+
+       vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
+       if (vmap) {
+               if (info.link != 0xff) {
+                       int ret = nouveau_volt_map(volt, info.link);
+                       if (ret < 0)
+                               return ret;
+                       info.min += ret;
+               }
+               return info.min;
+       }
+
+       return id ? id * 10000 : -ENODEV;
+}
+
+static int
+nouveau_volt_set_id(struct nouveau_volt *volt, u8 id, int condition)
+{
+       int ret = nouveau_volt_map(volt, id);
+       if (ret >= 0) {
+               int prev = nouveau_volt_get(volt);
+               if (!condition || prev < 0 ||
+                   (condition < 0 && ret < prev) ||
+                   (condition > 0 && ret > prev)) {
+                       ret = nouveau_volt_set(volt, ret);
+               } else {
+                       ret = 0;
+               }
+       }
+       return ret;
+}
+
+int
+_nouveau_volt_init(struct nouveau_object *object)
+{
+       struct nouveau_volt *volt = (void *)object;
+       int ret;
+
+       ret = nouveau_subdev_init(&volt->base);
+       if (ret)
+               return ret;
+
+       ret = volt->get(volt);
+       if (ret < 0) {
+               if (ret != -ENODEV)
+                       nv_debug(volt, "current voltage unknown\n");
+               return 0;
+       }
+
+       nv_info(volt, "GPU voltage: %duv\n", ret);
+       return 0;
+}
+
+void
+_nouveau_volt_dtor(struct nouveau_object *object)
+{
+       struct nouveau_volt *volt = (void *)object;
+       nouveau_subdev_destroy(&volt->base);
+}
+
+int
+nouveau_volt_create_(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, int length, void **pobject)
+{
+       struct nouveau_bios *bios = nouveau_bios(parent);
+       struct nouveau_volt *volt;
+       struct nvbios_volt_entry ivid;
+       struct nvbios_volt info;
+       u8  ver, hdr, cnt, len;
+       u16 data;
+       int ret, i;
+
+       ret = nouveau_subdev_create_(parent, engine, oclass, 0, "VOLT",
+                                    "voltage", length, pobject);
+       volt = *pobject;
+       if (ret)
+               return ret;
+
+       volt->get = nouveau_volt_get;
+       volt->set = nouveau_volt_set;
+       volt->set_id = nouveau_volt_set_id;
+
+       data = nvbios_volt_parse(bios, &ver, &hdr, &cnt, &len, &info);
+       if (data && info.vidmask && info.base && info.step) {
+               for (i = 0; i < info.vidmask + 1; i++) {
+                       if (info.base >= info.min &&
+                           info.base <= info.max) {
+                               volt->vid[volt->vid_nr].uv = info.base;
+                               volt->vid[volt->vid_nr].vid = i;
+                               volt->vid_nr++;
+                       }
+                       info.base += info.step;
+               }
+               volt->vid_mask = info.vidmask;
+       } else
+       if (data && info.vidmask) {
+               for (i = 0; i < cnt; i++) {
+                       data = nvbios_volt_entry_parse(bios, i, &ver, &hdr,
+                                                     &ivid);
+                       if (data) {
+                               volt->vid[volt->vid_nr].uv = ivid.voltage;
+                               volt->vid[volt->vid_nr].vid = ivid.vid;
+                               volt->vid_nr++;
+                       }
+               }
+               volt->vid_mask = info.vidmask;
+       }
+
+       if (volt->vid_nr) {
+               for (i = 0; i < volt->vid_nr; i++) {
+                       nv_debug(volt, "VID %02x: %duv\n",
+                                volt->vid[i].vid, volt->vid[i].uv);
+               }
+
+               /*XXX: this is an assumption.. there probably exists boards
+                * out there with i2c-connected voltage controllers too..
+                */
+               ret = nouveau_voltgpio_init(volt);
+               if (ret == 0) {
+                       volt->vid_get = nouveau_voltgpio_get;
+                       volt->vid_set = nouveau_voltgpio_set;
+               }
+       }
+
+       return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/volt/gpio.c b/drivers/gpu/drm/nouveau/core/subdev/volt/gpio.c
new file mode 100644 (file)
index 0000000..755fa91
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/volt.h>
+#include <subdev/gpio.h>
+#include <subdev/bios/gpio.h>
+
+static const u8 tags[] = {
+       DCB_GPIO_VID0, DCB_GPIO_VID1, DCB_GPIO_VID2, DCB_GPIO_VID3,
+       DCB_GPIO_VID4, DCB_GPIO_VID5, DCB_GPIO_VID6, DCB_GPIO_VID7,
+};
+
+int
+nouveau_voltgpio_get(struct nouveau_volt *volt)
+{
+       struct nouveau_gpio *gpio = nouveau_gpio(volt);
+       u8 vid = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tags); i++) {
+               if (volt->vid_mask & (1 << i)) {
+                       int ret = gpio->get(gpio, 0, tags[i], 0xff);
+                       if (ret < 0)
+                               return ret;
+                       vid |= ret << i;
+               }
+       }
+
+       return vid;
+}
+
+int
+nouveau_voltgpio_set(struct nouveau_volt *volt, u8 vid)
+{
+       struct nouveau_gpio *gpio = nouveau_gpio(volt);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tags); i++, vid >>= 1) {
+               if (volt->vid_mask & (1 << i)) {
+                       int ret = gpio->set(gpio, 0, tags[i], 0xff, vid & 1);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+int
+nouveau_voltgpio_init(struct nouveau_volt *volt)
+{
+       struct nouveau_gpio *gpio = nouveau_gpio(volt);
+       struct dcb_gpio_func func;
+       int i;
+
+       /* check we have gpio function info for each vid bit.  on some
+        * boards (ie. nvs295) the vid mask has more bits than there
+        * are valid gpio functions... from traces, nvidia appear to
+        * just touch the existing ones, so let's mask off the invalid
+        * bits and continue with life
+        */
+       for (i = 0; i < ARRAY_SIZE(tags); i++) {
+               if (volt->vid_mask & (1 << i)) {
+                       int ret = gpio->find(gpio, 0, tags[i], 0xff, &func);
+                       if (ret) {
+                               if (ret != -ENOENT)
+                                       return ret;
+                               nv_debug(volt, "VID bit %d has no GPIO\n", i);
+                               volt->vid_mask &= ~(1 << i);
+                       }
+               }
+       }
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/volt/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/volt/nv40.c
new file mode 100644 (file)
index 0000000..87d5358
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/volt.h>
+
+struct nv40_volt_priv {
+       struct nouveau_volt base;
+};
+
+static int
+nv40_volt_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+              struct nouveau_oclass *oclass, void *data, u32 size,
+              struct nouveau_object **pobject)
+{
+       struct nv40_volt_priv *priv;
+       int ret;
+
+       ret = nouveau_volt_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nv40_volt_oclass = {
+       .handle = NV_SUBDEV(VOLT, 0x40),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv40_volt_ctor,
+               .dtor = _nouveau_volt_dtor,
+               .init = _nouveau_volt_init,
+               .fini = _nouveau_volt_fini,
+       },
+};
index ea3f5b8..424a489 100644 (file)
@@ -5,6 +5,7 @@ nouveau-y += dispnv04/dac.o
 nouveau-y += dispnv04/dfp.o
 nouveau-y += dispnv04/disp.o
 nouveau-y += dispnv04/hw.o
+nouveau-y += dispnv04/overlay.o
 nouveau-y += dispnv04/tvmodesnv17.o
 nouveau-y += dispnv04/tvnv04.o
 nouveau-y += dispnv04/tvnv17.o
index 2e70462..2a15b98 100644 (file)
@@ -210,8 +210,8 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
        sim_data.nvclk_khz = NVClk;
        sim_data.bpp = bpp;
        sim_data.two_heads = nv_two_heads(dev);
-       if ((dev->pci_device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ ||
-           (dev->pci_device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) {
+       if ((dev->pdev->device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ ||
+           (dev->pdev->device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) {
                uint32_t type;
 
                pci_read_config_dword(pci_get_bus_and_slot(0, 1), 0x7c, &type);
@@ -256,8 +256,8 @@ nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm
 
        if (nv_device(drm->device)->card_type < NV_20)
                nv04_update_arb(dev, vclk, bpp, burst, lwm);
-       else if ((dev->pci_device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ ||
-                (dev->pci_device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) {
+       else if ((dev->pdev->device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ ||
+                (dev->pdev->device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) {
                *burst = 128;
                *lwm = 0x0480;
        } else
index d4fbf11..0e3270c 100644 (file)
@@ -326,8 +326,6 @@ nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
                        regp->MiscOutReg = 0x23;        /* +hsync +vsync */
        }
 
-       regp->MiscOutReg |= (mode->clock_index & 0x03) << 2;
-
        /*
         * Time Sequencer
         */
index 93dd23f..936a71c 100644 (file)
@@ -490,10 +490,10 @@ static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode)
        /* BIOS scripts usually take care of the backlight, thanks
         * Apple for your consistency.
         */
-       if (dev->pci_device == 0x0174 || dev->pci_device == 0x0179 ||
-           dev->pci_device == 0x0189 || dev->pci_device == 0x0329) {
+       if (dev->pdev->device == 0x0174 || dev->pdev->device == 0x0179 ||
+           dev->pdev->device == 0x0189 || dev->pdev->device == 0x0329) {
                if (mode == DRM_MODE_DPMS_ON) {
-                       nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 0, 1 << 31);
+                       nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 1 << 31, 1 << 31);
                        nv_mask(device, NV_PCRTC_GPIO_EXT, 3, 1);
                } else {
                        nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 1 << 31, 0);
@@ -625,13 +625,15 @@ static void nv04_tmds_slave_init(struct drm_encoder *encoder)
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct nouveau_i2c_port *port = i2c->find(i2c, 2);
-       struct i2c_board_info info[] = {
+       struct nouveau_i2c_board_info info[] = {
                {
-                       .type = "sil164",
-                       .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38),
-                       .platform_data = &(struct sil164_encoder_params) {
-                               SIL164_INPUT_EDGE_RISING
-                       }
+                   {
+                       .type = "sil164",
+                       .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38),
+                       .platform_data = &(struct sil164_encoder_params) {
+                           SIL164_INPUT_EDGE_RISING
+                        }
+                   }, 0
                },
                { }
        };
@@ -646,7 +648,7 @@ static void nv04_tmds_slave_init(struct drm_encoder *encoder)
                return;
 
        drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
-                            &port->adapter, &info[type]);
+                            &port->adapter, &info[type].dev);
 }
 
 static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
index 4908d3f..b13ff0f 100644 (file)
@@ -140,6 +140,8 @@ nv04_display_create(struct drm_device *dev)
                func->save(encoder);
        }
 
+       nouveau_overlay_init(dev);
+
        return 0;
 }
 
index 9928187..56a28db 100644 (file)
@@ -123,11 +123,14 @@ int nv04_tv_create(struct drm_connector *, struct dcb_output *);
 /* nv17_tv.c */
 int nv17_tv_create(struct drm_connector *, struct dcb_output *);
 
+/* overlay.c */
+void nouveau_overlay_init(struct drm_device *dev);
+
 static inline bool
 nv_two_heads(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
-       const int impl = dev->pci_device & 0x0ff0;
+       const int impl = dev->pdev->device & 0x0ff0;
 
        if (nv_device(drm->device)->card_type >= NV_10 && impl != 0x0100 &&
            impl != 0x0150 && impl != 0x01a0 && impl != 0x0200)
@@ -139,14 +142,14 @@ nv_two_heads(struct drm_device *dev)
 static inline bool
 nv_gf4_disp_arch(struct drm_device *dev)
 {
-       return nv_two_heads(dev) && (dev->pci_device & 0x0ff0) != 0x0110;
+       return nv_two_heads(dev) && (dev->pdev->device & 0x0ff0) != 0x0110;
 }
 
 static inline bool
 nv_two_reg_pll(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
-       const int impl = dev->pci_device & 0x0ff0;
+       const int impl = dev->pdev->device & 0x0ff0;
 
        if (impl == 0x0310 || impl == 0x0340 || nv_device(drm->device)->card_type >= NV_40)
                return true;
index 973056b..aca76af 100644 (file)
@@ -27,6 +27,7 @@
 #include "hw.h"
 
 #include <subdev/bios/pll.h>
+#include <subdev/fb.h>
 #include <subdev/clock.h>
 #include <subdev/timer.h>
 
@@ -220,7 +221,7 @@ nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype)
        int ret;
 
        if (plltype == PLL_MEMORY &&
-           (dev->pci_device & 0x0ff0) == CHIPSET_NFORCE) {
+           (dev->pdev->device & 0x0ff0) == CHIPSET_NFORCE) {
                uint32_t mpllP;
 
                pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP);
@@ -230,7 +231,7 @@ nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype)
                return 400000 / mpllP;
        } else
        if (plltype == PLL_MEMORY &&
-           (dev->pci_device & 0xff0) == CHIPSET_NFORCE2) {
+           (dev->pdev->device & 0xff0) == CHIPSET_NFORCE2) {
                uint32_t clock;
 
                pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock);
@@ -664,6 +665,7 @@ nv_load_state_ext(struct drm_device *dev, int head,
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_device *device = nv_device(drm->device);
        struct nouveau_timer *ptimer = nouveau_timer(device);
+       struct nouveau_fb *pfb = nouveau_fb(device);
        struct nv04_crtc_reg *regp = &state->crtc_reg[head];
        uint32_t reg900;
        int i;
@@ -680,10 +682,10 @@ nv_load_state_ext(struct drm_device *dev, int head,
                nv_wr32(device, NV_PVIDEO_INTR_EN, 0);
                nv_wr32(device, NV_PVIDEO_OFFSET_BUFF(0), 0);
                nv_wr32(device, NV_PVIDEO_OFFSET_BUFF(1), 0);
-               nv_wr32(device, NV_PVIDEO_LIMIT(0), 0); //drm->fb_available_size - 1);
-               nv_wr32(device, NV_PVIDEO_LIMIT(1), 0); //drm->fb_available_size - 1);
-               nv_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(0), 0); //drm->fb_available_size - 1);
-               nv_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(1), 0); //drm->fb_available_size - 1);
+               nv_wr32(device, NV_PVIDEO_LIMIT(0), pfb->ram->size - 1);
+               nv_wr32(device, NV_PVIDEO_LIMIT(1), pfb->ram->size - 1);
+               nv_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(0), pfb->ram->size - 1);
+               nv_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(1), pfb->ram->size - 1);
                nv_wr32(device, NV_PBUS_POWERCTRL_2, 0);
 
                NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg);
@@ -740,7 +742,7 @@ nv_load_state_ext(struct drm_device *dev, int head,
        }
        /* NV11 and NV20 stop at 0x52. */
        if (nv_gf4_disp_arch(dev)) {
-               if (nv_device(drm->device)->card_type == NV_10) {
+               if (nv_device(drm->device)->card_type < NV_20) {
                        /* Not waiting for vertical retrace before modifying
                           CRE_53/CRE_54 causes lockups. */
                        nouveau_timer_wait_eq(ptimer, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x8);
diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
new file mode 100644 (file)
index 0000000..3618ac6
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2013 Ilia Mirkin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Implementation based on the pre-KMS implementation in xf86-video-nouveau,
+ * written by Arthur Huillet.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fourcc.h>
+
+#include "nouveau_drm.h"
+
+#include "nouveau_bo.h"
+#include "nouveau_connector.h"
+#include "nouveau_display.h"
+#include "nvreg.h"
+
+
+struct nouveau_plane {
+       struct drm_plane base;
+       bool flip;
+       struct nouveau_bo *cur;
+
+       struct {
+               struct drm_property *colorkey;
+               struct drm_property *contrast;
+               struct drm_property *brightness;
+               struct drm_property *hue;
+               struct drm_property *saturation;
+               struct drm_property *iturbt_709;
+       } props;
+
+       int colorkey;
+       int contrast;
+       int brightness;
+       int hue;
+       int saturation;
+       int iturbt_709;
+};
+
+static uint32_t formats[] = {
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_UYVY,
+};
+
+/* Sine can be approximated with
+ * http://en.wikipedia.org/wiki/Bhaskara_I's_sine_approximation_formula
+ * sin(x degrees) ~= 4 x (180 - x) / (40500 - x (180 - x) )
+ * Note that this only works for the range [0, 180].
+ * Also note that sin(x) == -sin(x - 180)
+ */
+static inline int
+sin_mul(int degrees, int factor)
+{
+       if (degrees > 180) {
+               degrees -= 180;
+               factor *= -1;
+       }
+       return factor * 4 * degrees * (180 - degrees) /
+               (40500 - degrees * (180 - degrees));
+}
+
+/* cos(x) = sin(x + 90) */
+static inline int
+cos_mul(int degrees, int factor)
+{
+       return sin_mul((degrees + 90) % 360, factor);
+}
+
+static int
+nv10_update_plane(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 nouveau_device *dev = nouveau_dev(plane->dev);
+       struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
+       struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb);
+       struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+       struct nouveau_bo *cur = nv_plane->cur;
+       bool flip = nv_plane->flip;
+       int format = ALIGN(src_w * 4, 0x100);
+       int soff = NV_PCRTC0_SIZE * nv_crtc->index;
+       int soff2 = NV_PCRTC0_SIZE * !nv_crtc->index;
+       int ret;
+
+       if (format > 0xffff)
+               return -EINVAL;
+
+       ret = nouveau_bo_pin(nv_fb->nvbo, TTM_PL_FLAG_VRAM);
+       if (ret)
+               return ret;
+
+       nv_plane->cur = nv_fb->nvbo;
+
+       /* Source parameters given in 16.16 fixed point, ignore fractional. */
+       src_x = src_x >> 16;
+       src_y = src_y >> 16;
+       src_w = src_w >> 16;
+       src_h = src_h >> 16;
+
+       nv_mask(dev, NV_PCRTC_ENGINE_CTRL + soff, NV_CRTC_FSEL_OVERLAY, NV_CRTC_FSEL_OVERLAY);
+       nv_mask(dev, NV_PCRTC_ENGINE_CTRL + soff2, NV_CRTC_FSEL_OVERLAY, 0);
+
+       nv_wr32(dev, NV_PVIDEO_BASE(flip), 0);
+       nv_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nv_fb->nvbo->bo.offset);
+       nv_wr32(dev, NV_PVIDEO_SIZE_IN(flip), src_h << 16 | src_w);
+       nv_wr32(dev, NV_PVIDEO_POINT_IN(flip), src_y << 16 | src_x);
+       nv_wr32(dev, NV_PVIDEO_DS_DX(flip), (src_w << 20) / crtc_w);
+       nv_wr32(dev, NV_PVIDEO_DT_DY(flip), (src_h << 20) / crtc_h);
+       nv_wr32(dev, NV_PVIDEO_POINT_OUT(flip), crtc_y << 16 | crtc_x);
+       nv_wr32(dev, NV_PVIDEO_SIZE_OUT(flip), crtc_h << 16 | crtc_w);
+
+       if (fb->pixel_format == DRM_FORMAT_NV12) {
+               format |= NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8;
+               format |= NV_PVIDEO_FORMAT_PLANAR;
+       }
+       if (nv_plane->iturbt_709)
+               format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709;
+       if (nv_plane->colorkey & (1 << 24))
+               format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY;
+
+       if (fb->pixel_format == DRM_FORMAT_NV12) {
+               nv_wr32(dev, NV_PVIDEO_UVPLANE_BASE(flip), 0);
+               nv_wr32(dev, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip),
+                       nv_fb->nvbo->bo.offset + fb->offsets[1]);
+       }
+       nv_wr32(dev, NV_PVIDEO_FORMAT(flip), format);
+       nv_wr32(dev, NV_PVIDEO_STOP, 0);
+       /* TODO: wait for vblank? */
+       nv_wr32(dev, NV_PVIDEO_BUFFER, flip ? 0x10 : 0x1);
+       nv_plane->flip = !flip;
+
+       if (cur)
+               nouveau_bo_unpin(cur);
+
+       return 0;
+}
+
+static int
+nv10_disable_plane(struct drm_plane *plane)
+{
+       struct nouveau_device *dev = nouveau_dev(plane->dev);
+       struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
+
+       nv_wr32(dev, NV_PVIDEO_STOP, 1);
+       if (nv_plane->cur) {
+               nouveau_bo_unpin(nv_plane->cur);
+               nv_plane->cur = NULL;
+       }
+
+       return 0;
+}
+
+static void
+nv10_destroy_plane(struct drm_plane *plane)
+{
+       nv10_disable_plane(plane);
+       drm_plane_cleanup(plane);
+       kfree(plane);
+}
+
+static void
+nv10_set_params(struct nouveau_plane *plane)
+{
+       struct nouveau_device *dev = nouveau_dev(plane->base.dev);
+       u32 luma = (plane->brightness - 512) << 16 | plane->contrast;
+       u32 chroma = ((sin_mul(plane->hue, plane->saturation) & 0xffff) << 16) |
+               (cos_mul(plane->hue, plane->saturation) & 0xffff);
+       u32 format = 0;
+
+       nv_wr32(dev, NV_PVIDEO_LUMINANCE(0), luma);
+       nv_wr32(dev, NV_PVIDEO_LUMINANCE(1), luma);
+       nv_wr32(dev, NV_PVIDEO_CHROMINANCE(0), chroma);
+       nv_wr32(dev, NV_PVIDEO_CHROMINANCE(1), chroma);
+       nv_wr32(dev, NV_PVIDEO_COLOR_KEY, plane->colorkey & 0xffffff);
+
+       if (plane->cur) {
+               if (plane->iturbt_709)
+                       format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709;
+               if (plane->colorkey & (1 << 24))
+                       format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY;
+               nv_mask(dev, NV_PVIDEO_FORMAT(plane->flip),
+                       NV_PVIDEO_FORMAT_MATRIX_ITURBT709 |
+                       NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY,
+                       format);
+       }
+}
+
+static int
+nv10_set_property(struct drm_plane *plane,
+                 struct drm_property *property,
+                 uint64_t value)
+{
+       struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
+
+       if (property == nv_plane->props.colorkey)
+               nv_plane->colorkey = value;
+       else if (property == nv_plane->props.contrast)
+               nv_plane->contrast = value;
+       else if (property == nv_plane->props.brightness)
+               nv_plane->brightness = value;
+       else if (property == nv_plane->props.hue)
+               nv_plane->hue = value;
+       else if (property == nv_plane->props.saturation)
+               nv_plane->saturation = value;
+       else if (property == nv_plane->props.iturbt_709)
+               nv_plane->iturbt_709 = value;
+       else
+               return -EINVAL;
+
+       nv10_set_params(nv_plane);
+       return 0;
+}
+
+static const struct drm_plane_funcs nv10_plane_funcs = {
+       .update_plane = nv10_update_plane,
+       .disable_plane = nv10_disable_plane,
+       .set_property = nv10_set_property,
+       .destroy = nv10_destroy_plane,
+};
+
+static void
+nv10_overlay_init(struct drm_device *device)
+{
+       struct nouveau_device *dev = nouveau_dev(device);
+       struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL);
+       int ret;
+
+       if (!plane)
+               return;
+
+       ret = drm_plane_init(device, &plane->base, 3 /* both crtc's */,
+                            &nv10_plane_funcs,
+                            formats, ARRAY_SIZE(formats), false);
+       if (ret)
+               goto err;
+
+       /* Set up the plane properties */
+       plane->props.colorkey = drm_property_create_range(
+                       device, 0, "colorkey", 0, 0x01ffffff);
+       plane->props.contrast = drm_property_create_range(
+                       device, 0, "contrast", 0, 8192 - 1);
+       plane->props.brightness = drm_property_create_range(
+                       device, 0, "brightness", 0, 1024);
+       plane->props.hue = drm_property_create_range(
+                       device, 0, "hue", 0, 359);
+       plane->props.saturation = drm_property_create_range(
+                       device, 0, "saturation", 0, 8192 - 1);
+       plane->props.iturbt_709 = drm_property_create_range(
+                       device, 0, "iturbt_709", 0, 1);
+       if (!plane->props.colorkey ||
+           !plane->props.contrast ||
+           !plane->props.brightness ||
+           !plane->props.hue ||
+           !plane->props.saturation ||
+           !plane->props.iturbt_709)
+               goto cleanup;
+
+       plane->colorkey = 0;
+       drm_object_attach_property(&plane->base.base,
+                                  plane->props.colorkey, plane->colorkey);
+
+       plane->contrast = 0x1000;
+       drm_object_attach_property(&plane->base.base,
+                                  plane->props.contrast, plane->contrast);
+
+       plane->brightness = 512;
+       drm_object_attach_property(&plane->base.base,
+                                  plane->props.brightness, plane->brightness);
+
+       plane->hue = 0;
+       drm_object_attach_property(&plane->base.base,
+                                  plane->props.hue, plane->hue);
+
+       plane->saturation = 0x1000;
+       drm_object_attach_property(&plane->base.base,
+                                  plane->props.saturation, plane->saturation);
+
+       plane->iturbt_709 = 0;
+       drm_object_attach_property(&plane->base.base,
+                                  plane->props.iturbt_709, plane->iturbt_709);
+
+       nv10_set_params(plane);
+       nv_wr32(dev, NV_PVIDEO_STOP, 1);
+       return;
+cleanup:
+       drm_plane_cleanup(&plane->base);
+err:
+       kfree(plane);
+       nv_error(dev, "Failed to create plane\n");
+}
+
+void
+nouveau_overlay_init(struct drm_device *device)
+{
+       struct nouveau_device *dev = nouveau_dev(device);
+       if (dev->chipset >= 0x10 && dev->chipset <= 0x40)
+               nv10_overlay_init(device);
+}
index bf13db4..cc4b208 100644 (file)
 
 #include <subdev/i2c.h>
 
-static struct i2c_board_info nv04_tv_encoder_info[] = {
+static struct nouveau_i2c_board_info nv04_tv_encoder_info[] = {
        {
-               I2C_BOARD_INFO("ch7006", 0x75),
-               .platform_data = &(struct ch7006_encoder_params) {
-                       CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
-                       0, 0, 0,
-                       CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
-                       CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
-               }
+               {
+                       I2C_BOARD_INFO("ch7006", 0x75),
+                       .platform_data = &(struct ch7006_encoder_params) {
+                               CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
+                               0, 0, 0,
+                               CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
+                               CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
+                       }
+               },
+               0
        },
        { }
 };
@@ -229,7 +232,8 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry)
 
        /* Run the slave-specific initialization */
        ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
-                                  &port->adapter, &nv04_tv_encoder_info[type]);
+                                  &port->adapter,
+                                  &nv04_tv_encoder_info[type].dev);
        if (ret < 0)
                goto fail_cleanup;
 
index 8f467e7..6828d81 100644 (file)
@@ -87,6 +87,7 @@ nouveau_abi16_swclass(struct nouveau_drm *drm)
        case NV_04:
                return 0x006e;
        case NV_10:
+       case NV_11:
        case NV_20:
        case NV_30:
        case NV_40:
@@ -130,7 +131,7 @@ nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
        if (chan->ntfy) {
                nouveau_bo_vma_del(chan->ntfy, &chan->ntfy_vma);
                nouveau_bo_unpin(chan->ntfy);
-               drm_gem_object_unreference_unlocked(chan->ntfy->gem);
+               drm_gem_object_unreference_unlocked(&chan->ntfy->gem);
        }
 
        if (chan->heap.block_size)
@@ -178,10 +179,10 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
                getparam->value = device->chipset;
                break;
        case NOUVEAU_GETPARAM_PCI_VENDOR:
-               getparam->value = dev->pci_vendor;
+               getparam->value = dev->pdev->vendor;
                break;
        case NOUVEAU_GETPARAM_PCI_DEVICE:
-               getparam->value = dev->pci_device;
+               getparam->value = dev->pdev->device;
                break;
        case NOUVEAU_GETPARAM_BUS_TYPE:
                if (drm_pci_device_is_agp(dev))
@@ -297,7 +298,7 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
        else
                init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
 
-       if (device->card_type < NV_C0) {
+       if (device->card_type < NV_10) {
                init->subchan[0].handle = 0x00000000;
                init->subchan[0].grclass = 0x0000;
                init->subchan[1].handle = NvSw;
@@ -320,7 +321,7 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
                        goto done;
        }
 
-       ret = drm_gem_handle_create(file_priv, chan->ntfy->gem,
+       ret = drm_gem_handle_create(file_priv, &chan->ntfy->gem,
                                    &init->notifier_handle);
        if (ret)
                goto done;
index cfbeee6..95c7404 100644 (file)
@@ -256,7 +256,7 @@ static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
        acpi_handle dhandle;
        int retval = 0;
 
-       dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
+       dhandle = ACPI_HANDLE(&pdev->dev);
        if (!dhandle)
                return false;
 
@@ -314,6 +314,16 @@ static bool nouveau_dsm_detect(void)
                        has_optimus = 1;
        }
 
+       while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) {
+               vga_count++;
+
+               retval = nouveau_dsm_pci_probe(pdev);
+               if (retval & NOUVEAU_DSM_HAS_MUX)
+                       has_dsm |= 1;
+               if (retval & NOUVEAU_DSM_HAS_OPT)
+                       has_optimus = 1;
+       }
+
        /* find the optimus DSM or the old v1 DSM */
        if (has_optimus == 1) {
                acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
@@ -404,7 +414,7 @@ bool nouveau_acpi_rom_supported(struct pci_dev *pdev)
        if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected)
                return false;
 
-       dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
+       dhandle = ACPI_HANDLE(&pdev->dev);
        if (!dhandle)
                return false;
 
@@ -438,7 +448,7 @@ nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
                return NULL;
        }
 
-       handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
+       handle = ACPI_HANDLE(&dev->pdev->dev);
        if (!handle)
                return NULL;
 
index 6e7a55f..2953c4e 100644 (file)
@@ -11,10 +11,28 @@ MODULE_PARM_DESC(agpmode, "AGP mode (0 to disable AGP)");
 static int nouveau_agpmode = -1;
 module_param_named(agpmode, nouveau_agpmode, int, 0400);
 
+struct nouveau_agpmode_quirk {
+       u16 hostbridge_vendor;
+       u16 hostbridge_device;
+       u16 chip_vendor;
+       u16 chip_device;
+       int mode;
+};
+
+static struct nouveau_agpmode_quirk nouveau_agpmode_quirk_list[] = {
+       /* VIA Apollo PRO133x / GeForce FX 5600 Ultra, max agpmode 2, fdo #20341 */
+       { PCI_VENDOR_ID_VIA, 0x0691, PCI_VENDOR_ID_NVIDIA, 0x0311, 2 },
+
+       {},
+};
+
 static unsigned long
-get_agp_mode(struct nouveau_drm *drm, unsigned long mode)
+get_agp_mode(struct nouveau_drm *drm, const struct drm_agp_info *info)
 {
        struct nouveau_device *device = nv_device(drm->device);
+       struct nouveau_agpmode_quirk *quirk = nouveau_agpmode_quirk_list;
+       int agpmode = nouveau_agpmode;
+       unsigned long mode = info->mode;
 
        /*
         * FW seems to be broken on nv18, it makes the card lock up
@@ -24,11 +42,27 @@ get_agp_mode(struct nouveau_drm *drm, unsigned long mode)
                mode &= ~PCI_AGP_COMMAND_FW;
 
        /*
+        * Go through the quirks list and adjust the agpmode accordingly.
+        */
+       while (agpmode == -1 && quirk->hostbridge_vendor) {
+               if (info->id_vendor == quirk->hostbridge_vendor &&
+                   info->id_device == quirk->hostbridge_device &&
+                   device->pdev->vendor == quirk->chip_vendor &&
+                   device->pdev->device == quirk->chip_device) {
+                       agpmode = quirk->mode;
+                       nv_info(device, "Forcing agp mode to %dX. Use agpmode to override.\n",
+                               agpmode);
+                       break;
+               }
+               ++quirk;
+       }
+
+       /*
         * AGP mode set in the command line.
         */
-       if (nouveau_agpmode > 0) {
+       if (agpmode > 0) {
                bool agpv3 = mode & 0x8;
-               int rate = agpv3 ? nouveau_agpmode / 4 : nouveau_agpmode;
+               int rate = agpv3 ? agpmode / 4 : agpmode;
 
                mode = (mode & ~0x7) | (rate & 0x7);
        }
@@ -90,7 +124,7 @@ nouveau_agp_reset(struct nouveau_drm *drm)
                if (ret)
                        return;
 
-               mode.mode  = get_agp_mode(drm, info.mode);
+               mode.mode  = get_agp_mode(drm, &info);
                mode.mode &= ~PCI_AGP_COMMAND_FW;
 
                ret = drm_agp_enable(dev, mode);
@@ -139,7 +173,7 @@ nouveau_agp_init(struct nouveau_drm *drm)
        }
 
        /* see agp.h for the AGPSTAT_* modes available */
-       mode.mode = get_agp_mode(drm, info.mode);
+       mode.mode = get_agp_mode(drm, &info);
 
        ret = drm_agp_enable(dev, mode);
        if (ret) {
index 2ffad21..630f6e8 100644 (file)
@@ -82,7 +82,7 @@ nv40_backlight_init(struct drm_connector *connector)
        memset(&props, 0, sizeof(struct backlight_properties));
        props.type = BACKLIGHT_RAW;
        props.max_brightness = 31;
-       bd = backlight_device_register("nv_backlight", &connector->kdev, drm,
+       bd = backlight_device_register("nv_backlight", connector->kdev, drm,
                                       &nv40_bl_ops, &props);
        if (IS_ERR(bd))
                return PTR_ERR(bd);
@@ -204,7 +204,7 @@ nv50_backlight_init(struct drm_connector *connector)
        memset(&props, 0, sizeof(struct backlight_properties));
        props.type = BACKLIGHT_RAW;
        props.max_brightness = 100;
-       bd = backlight_device_register("nv_backlight", &connector->kdev,
+       bd = backlight_device_register("nv_backlight", connector->kdev,
                                       nv_encoder, ops, &props);
        if (IS_ERR(bd))
                return PTR_ERR(bd);
index 3e72876..4c3feaa 100644 (file)
@@ -127,8 +127,8 @@ static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_outp
 #ifdef __powerpc__
        /* Powerbook specific quirks */
        if (script == LVDS_RESET &&
-           (dev->pci_device == 0x0179 || dev->pci_device == 0x0189 ||
-            dev->pci_device == 0x0329))
+           (dev->pdev->device == 0x0179 || dev->pdev->device == 0x0189 ||
+            dev->pdev->device == 0x0329))
                nv_write_tmds(dev, dcbent->or, 0, 0x02, 0x72);
 #endif
 
index 755c38d..c0fde6b 100644 (file)
@@ -98,12 +98,7 @@ nv10_bo_put_tile_region(struct drm_device *dev, struct nouveau_drm_tile *tile,
 
        if (tile) {
                spin_lock(&drm->tile.lock);
-               if (fence) {
-                       /* Mark it as pending. */
-                       tile->fence = fence;
-                       nouveau_fence_ref(fence);
-               }
-
+               tile->fence = nouveau_fence_ref(fence);
                tile->used = false;
                spin_unlock(&drm->tile.lock);
        }
@@ -146,7 +141,7 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
        struct drm_device *dev = drm->dev;
        struct nouveau_bo *nvbo = nouveau_bo(bo);
 
-       if (unlikely(nvbo->gem))
+       if (unlikely(nvbo->gem.filp))
                DRM_ERROR("bo %p still attached to GEM object\n", bo);
        WARN_ON(nvbo->pin_refcnt > 0);
        nv10_bo_put_tile_region(dev, nvbo->tile, NULL);
@@ -269,7 +264,8 @@ set_placement_range(struct nouveau_bo *nvbo, uint32_t type)
        struct nouveau_fb *pfb = nouveau_fb(drm->device);
        u32 vram_pages = pfb->ram->size >> PAGE_SHIFT;
 
-       if (nv_device(drm->device)->card_type == NV_10 &&
+       if ((nv_device(drm->device)->card_type == NV_10 ||
+            nv_device(drm->device)->card_type == NV_11) &&
            nvbo->tile_mode && (type & TTM_PL_FLAG_VRAM) &&
            nvbo->bo.mem.num_pages < vram_pages / 4) {
                /*
@@ -982,7 +978,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
                     bool no_wait_gpu, struct ttm_mem_reg *new_mem)
 {
        struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
-       struct nouveau_channel *chan = chan = drm->ttm.chan;
+       struct nouveau_channel *chan = drm->ttm.chan;
        struct nouveau_bo *nvbo = nouveau_bo(bo);
        struct ttm_mem_reg *old_mem = &bo->mem;
        int ret;
@@ -1267,7 +1263,7 @@ nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
 {
        struct nouveau_bo *nvbo = nouveau_bo(bo);
 
-       return drm_vma_node_verify_access(&nvbo->gem->vma_node, filp);
+       return drm_vma_node_verify_access(&nvbo->gem.vma_node, filp);
 }
 
 static int
@@ -1461,14 +1457,12 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
 void
 nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence)
 {
+       struct nouveau_fence *new_fence = nouveau_fence_ref(fence);
        struct nouveau_fence *old_fence = NULL;
 
-       if (likely(fence))
-               nouveau_fence_ref(fence);
-
        spin_lock(&nvbo->bo.bdev->fence_lock);
        old_fence = nvbo->bo.sync_obj;
-       nvbo->bo.sync_obj = fence;
+       nvbo->bo.sync_obj = new_fence;
        spin_unlock(&nvbo->bo.bdev->fence_lock);
 
        nouveau_fence_unref(&old_fence);
@@ -1551,7 +1545,8 @@ nouveau_bo_vma_add(struct nouveau_bo *nvbo, struct nouveau_vm *vm,
 
        if (nvbo->bo.mem.mem_type == TTM_PL_VRAM)
                nouveau_vm_map(vma, nvbo->bo.mem.mm_node);
-       else if (nvbo->bo.mem.mem_type == TTM_PL_TT) {
+       else if (nvbo->bo.mem.mem_type == TTM_PL_TT &&
+                nvbo->page_shift == vma->vm->vmm->spg_shift) {
                if (node->sg)
                        nouveau_vm_map_sg_table(vma, 0, size, node);
                else
index 653dbbb..ff17c1f 100644 (file)
@@ -27,7 +27,10 @@ struct nouveau_bo {
        u32 tile_flags;
        struct nouveau_drm_tile *tile;
 
-       struct drm_gem_object *gem;
+       /* Only valid if allocated via nouveau_gem_new() and iff you hold a
+        * gem reference to it! For debugging, use gem.filp != NULL to test
+        * whether it is valid. */
+       struct drm_gem_object gem;
 
        /* protect by the ttm reservation lock */
        int pin_refcnt;
index e84f4c3..cc5152b 100644 (file)
@@ -346,22 +346,17 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
        for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
                OUT_RING(chan, 0x00000000);
 
-       /* allocate software object class (used for fences on <= nv05, and
-        * to signal flip completion), bind it to a subchannel.
-        */
-       if ((device->card_type < NV_E0) || gart /* nve0: want_nvsw */) {
+       /* allocate software object class (used for fences on <= nv05) */
+       if (device->card_type < NV_10) {
                ret = nouveau_object_new(nv_object(client), chan->handle,
-                                        NvSw, nouveau_abi16_swclass(chan->drm),
-                                        NULL, 0, &object);
+                                        NvSw, 0x006e, NULL, 0, &object);
                if (ret)
                        return ret;
 
                swch = (void *)object->parent;
                swch->flip = nouveau_flip_complete;
                swch->flip_data = chan;
-       }
 
-       if (device->card_type < NV_C0) {
                ret = RING_SPACE(chan, 2);
                if (ret)
                        return ret;
index c5b36f9..1674882 100644 (file)
@@ -100,6 +100,7 @@ static void
 nouveau_connector_destroy(struct drm_connector *connector)
 {
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
+       nouveau_event_ref(NULL, &nv_connector->hpd_func);
        kfree(nv_connector->edid);
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
@@ -214,9 +215,10 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
        } else {
                connector->doublescan_allowed = true;
                if (nv_device(drm->device)->card_type == NV_20 ||
-                  (nv_device(drm->device)->card_type == NV_10 &&
-                   (dev->pci_device & 0x0ff0) != 0x0100 &&
-                   (dev->pci_device & 0x0ff0) != 0x0150))
+                   ((nv_device(drm->device)->card_type == NV_10 ||
+                     nv_device(drm->device)->card_type == NV_11) &&
+                    (dev->pdev->device & 0x0ff0) != 0x0100 &&
+                    (dev->pdev->device & 0x0ff0) != 0x0150))
                        /* HW is broken */
                        connector->interlace_allowed = false;
                else
@@ -932,10 +934,9 @@ nouveau_connector_hotplug_work(struct work_struct *work)
 }
 
 static int
-nouveau_connector_hotplug(struct nouveau_eventh *event, int index)
+nouveau_connector_hotplug(void *data, int index)
 {
-       struct nouveau_connector *nv_connector =
-               container_of(event, struct nouveau_connector, hpd_func);
+       struct nouveau_connector *nv_connector = data;
        schedule_work(&nv_connector->hpd_work);
        return NVKM_EVENT_KEEP;
 }
@@ -1007,10 +1008,16 @@ nouveau_connector_create(struct drm_device *dev, int index)
 
                ret = gpio->find(gpio, 0, hpd[ffs((entry & 0x07033000) >> 12)],
                                 DCB_GPIO_UNUSED, &nv_connector->hpd);
-               nv_connector->hpd_func.func = nouveau_connector_hotplug;
                if (ret)
                        nv_connector->hpd.func = DCB_GPIO_UNUSED;
 
+               if (nv_connector->hpd.func != DCB_GPIO_UNUSED) {
+                       nouveau_event_new(gpio->events, nv_connector->hpd.line,
+                                         nouveau_connector_hotplug,
+                                         nv_connector,
+                                        &nv_connector->hpd_func);
+               }
+
                nv_connector->type = nv_connector->dcb[0];
                if (drm_conntype_from_dcb(nv_connector->type) ==
                                          DRM_MODE_CONNECTOR_Unknown) {
index 6e399aa..264a778 100644 (file)
@@ -69,7 +69,7 @@ struct nouveau_connector {
 
        struct dcb_gpio_func hpd;
        struct work_struct hpd_work;
-       struct nouveau_eventh hpd_func;
+       struct nouveau_eventh *hpd_func;
 
        int dithering_mode;
        int dithering_depth;
@@ -107,7 +107,4 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
 struct drm_connector *
 nouveau_connector_create(struct drm_device *, int index);
 
-int
-nouveau_connector_bpp(struct drm_connector *);
-
 #endif /* __NOUVEAU_CONNECTOR_H__ */
index 7848590..7809d92 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
-#include <drm/ttm/ttm_execbuf_util.h>
 
 #include "nouveau_fbcon.h"
 #include "dispnv04/hw.h"
 
 #include "nouveau_fence.h"
 
-#include <subdev/bios/gpio.h>
-#include <subdev/gpio.h>
 #include <engine/disp.h>
 
 #include <core/class.h>
 
+static int
+nouveau_display_vblank_handler(void *data, int head)
+{
+       struct nouveau_drm *drm = data;
+       drm_handle_vblank(drm->dev, head);
+       return NVKM_EVENT_KEEP;
+}
+
+int
+nouveau_display_vblank_enable(struct drm_device *dev, int head)
+{
+       struct nouveau_display *disp = nouveau_display(dev);
+       if (disp) {
+               nouveau_event_get(disp->vblank[head]);
+               return 0;
+       }
+       return -EIO;
+}
+
+void
+nouveau_display_vblank_disable(struct drm_device *dev, int head)
+{
+       struct nouveau_display *disp = nouveau_display(dev);
+       if (disp)
+               nouveau_event_put(disp->vblank[head]);
+}
+
+static void
+nouveau_display_vblank_fini(struct drm_device *dev)
+{
+       struct nouveau_display *disp = nouveau_display(dev);
+       int i;
+
+       if (disp->vblank) {
+               for (i = 0; i < dev->mode_config.num_crtc; i++)
+                       nouveau_event_ref(NULL, &disp->vblank[i]);
+               kfree(disp->vblank);
+               disp->vblank = NULL;
+       }
+
+       drm_vblank_cleanup(dev);
+}
+
+static int
+nouveau_display_vblank_init(struct drm_device *dev)
+{
+       struct nouveau_display *disp = nouveau_display(dev);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
+       int ret, i;
+
+       disp->vblank = kzalloc(dev->mode_config.num_crtc *
+                              sizeof(*disp->vblank), GFP_KERNEL);
+       if (!disp->vblank)
+               return -ENOMEM;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               ret = nouveau_event_new(pdisp->vblank, i,
+                                       nouveau_display_vblank_handler,
+                                       drm, &disp->vblank[i]);
+               if (ret) {
+                       nouveau_display_vblank_fini(dev);
+                       return ret;
+               }
+       }
+
+       ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+       if (ret) {
+               nouveau_display_vblank_fini(dev);
+               return ret;
+       }
+
+       return 0;
+}
+
 static void
 nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
 {
        struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
 
        if (fb->nvbo)
-               drm_gem_object_unreference_unlocked(fb->nvbo->gem);
+               drm_gem_object_unreference_unlocked(&fb->nvbo->gem);
 
        drm_framebuffer_cleanup(drm_fb);
        kfree(fb);
@@ -63,7 +135,7 @@ nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb,
 {
        struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
 
-       return drm_gem_handle_create(file_priv, fb->nvbo->gem, handle);
+       return drm_gem_handle_create(file_priv, &fb->nvbo->gem, handle);
 }
 
 static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {
@@ -227,9 +299,7 @@ static struct nouveau_drm_prop_enum_list dither_depth[] = {
 int
 nouveau_display_init(struct drm_device *dev)
 {
-       struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_display *disp = nouveau_display(dev);
-       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
        struct drm_connector *connector;
        int ret;
 
@@ -243,10 +313,7 @@ nouveau_display_init(struct drm_device *dev)
        /* enable hotplug interrupts */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct nouveau_connector *conn = nouveau_connector(connector);
-               if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) {
-                       nouveau_event_get(gpio->events, conn->hpd.line,
-                                        &conn->hpd_func);
-               }
+               if (conn->hpd_func) nouveau_event_get(conn->hpd_func);
        }
 
        return ret;
@@ -255,18 +322,13 @@ nouveau_display_init(struct drm_device *dev)
 void
 nouveau_display_fini(struct drm_device *dev)
 {
-       struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_display *disp = nouveau_display(dev);
-       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
        struct drm_connector *connector;
 
        /* disable hotplug interrupts */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct nouveau_connector *conn = nouveau_connector(connector);
-               if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) {
-                       nouveau_event_put(gpio->events, conn->hpd.line,
-                                        &conn->hpd_func);
-               }
+               if (conn->hpd_func) nouveau_event_put(conn->hpd_func);
        }
 
        drm_kms_helper_poll_disable(dev);
@@ -336,6 +398,11 @@ nouveau_display_create(struct drm_device *dev)
        dev->mode_config.preferred_depth = 24;
        dev->mode_config.prefer_shadow = 1;
 
+       if (nv_device(drm->device)->chipset < 0x11)
+               dev->mode_config.async_page_flip = false;
+       else
+               dev->mode_config.async_page_flip = true;
+
        drm_kms_helper_poll_init(dev);
        drm_kms_helper_poll_disable(dev);
 
@@ -352,7 +419,7 @@ nouveau_display_create(struct drm_device *dev)
                goto disp_create_err;
 
        if (dev->mode_config.num_crtc) {
-               ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+               ret = nouveau_display_vblank_init(dev);
                if (ret)
                        goto vblank_err;
        }
@@ -374,7 +441,7 @@ nouveau_display_destroy(struct drm_device *dev)
        struct nouveau_display *disp = nouveau_display(dev);
 
        nouveau_backlight_exit(dev);
-       drm_vblank_cleanup(dev);
+       nouveau_display_vblank_fini(dev);
 
        drm_kms_helper_poll_fini(dev);
        drm_mode_config_cleanup(dev);
@@ -394,7 +461,7 @@ nouveau_display_suspend(struct drm_device *dev)
 
        nouveau_display_fini(dev);
 
-       NV_SUSPEND(drm, "unpinning framebuffer(s)...\n");
+       NV_INFO(drm, "unpinning framebuffer(s)...\n");
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                struct nouveau_framebuffer *nouveau_fb;
 
@@ -492,19 +559,15 @@ nouveau_page_flip_emit(struct nouveau_channel *chan,
                goto fail;
 
        /* Emit the pageflip */
-       ret = RING_SPACE(chan, 3);
+       ret = RING_SPACE(chan, 2);
        if (ret)
                goto fail;
 
-       if (nv_device(drm->device)->card_type < NV_C0) {
+       if (nv_device(drm->device)->card_type < NV_C0)
                BEGIN_NV04(chan, NvSubSw, NV_SW_PAGE_FLIP, 1);
-               OUT_RING  (chan, 0x00000000);
-               OUT_RING  (chan, 0x00000000);
-       } else {
-               BEGIN_NVC0(chan, 0, NV10_SUBCHAN_REF_CNT, 1);
-               OUT_RING  (chan, 0);
-               BEGIN_IMC0(chan, 0, NVSW_SUBCHAN_PAGE_FLIP, 0x0000);
-       }
+       else
+               BEGIN_NVC0(chan, FermiSw, NV_SW_PAGE_FLIP, 1);
+       OUT_RING  (chan, 0x00000000);
        FIRE_RING (chan);
 
        ret = nouveau_fence_new(chan, false, pfence);
@@ -521,22 +584,16 @@ fail:
 
 int
 nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
-                      struct drm_pending_vblank_event *event,
-                      uint32_t page_flip_flags)
+                      struct drm_pending_vblank_event *event, u32 flags)
 {
+       const int swap_interval = (flags & DRM_MODE_PAGE_FLIP_ASYNC) ? 0 : 1;
        struct drm_device *dev = crtc->dev;
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->fb)->nvbo;
        struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo;
        struct nouveau_page_flip_state *s;
-       struct nouveau_channel *chan = NULL;
+       struct nouveau_channel *chan = drm->channel;
        struct nouveau_fence *fence;
-       struct ttm_validate_buffer resv[2] = {
-               { .bo = &old_bo->bo },
-               { .bo = &new_bo->bo },
-       };
-       struct ww_acquire_ctx ticket;
-       LIST_HEAD(res);
        int ret;
 
        if (!drm->channel)
@@ -546,26 +603,22 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        if (!s)
                return -ENOMEM;
 
-       /* Choose the channel the flip will be handled in */
-       spin_lock(&old_bo->bo.bdev->fence_lock);
-       fence = new_bo->bo.sync_obj;
-       if (fence)
-               chan = fence->channel;
-       if (!chan)
-               chan = drm->channel;
-       spin_unlock(&old_bo->bo.bdev->fence_lock);
+       /* synchronise rendering channel with the kernel's channel */
+       spin_lock(&new_bo->bo.bdev->fence_lock);
+       fence = nouveau_fence_ref(new_bo->bo.sync_obj);
+       spin_unlock(&new_bo->bo.bdev->fence_lock);
+       ret = nouveau_fence_sync(fence, chan);
+       if (ret)
+               return ret;
 
        if (new_bo != old_bo) {
                ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM);
                if (ret)
                        goto fail_free;
-
-               list_add(&resv[1].head, &res);
        }
-       list_add(&resv[0].head, &res);
 
        mutex_lock(&chan->cli->mutex);
-       ret = ttm_eu_reserve_buffers(&ticket, &res);
+       ret = ttm_bo_reserve(&old_bo->bo, true, false, false, NULL);
        if (ret)
                goto fail_unpin;
 
@@ -577,12 +630,29 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 
        /* Emit a page flip */
        if (nv_device(drm->device)->card_type >= NV_50) {
-               ret = nv50_display_flip_next(crtc, fb, chan, 0);
+               ret = nv50_display_flip_next(crtc, fb, chan, swap_interval);
                if (ret)
                        goto fail_unreserve;
        } else {
                struct nv04_display *dispnv04 = nv04_display(dev);
-               nouveau_bo_ref(new_bo, &dispnv04->image[nouveau_crtc(crtc)->index]);
+               int head = nouveau_crtc(crtc)->index;
+
+               if (swap_interval) {
+                       ret = RING_SPACE(chan, 8);
+                       if (ret)
+                               goto fail_unreserve;
+
+                       BEGIN_NV04(chan, NvSubImageBlit, 0x012c, 1);
+                       OUT_RING  (chan, 0);
+                       BEGIN_NV04(chan, NvSubImageBlit, 0x0134, 1);
+                       OUT_RING  (chan, head);
+                       BEGIN_NV04(chan, NvSubImageBlit, 0x0100, 1);
+                       OUT_RING  (chan, 0);
+                       BEGIN_NV04(chan, NvSubImageBlit, 0x0130, 1);
+                       OUT_RING  (chan, 0);
+               }
+
+               nouveau_bo_ref(new_bo, &dispnv04->image[head]);
        }
 
        ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence);
@@ -593,14 +663,15 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        /* Update the crtc struct and cleanup */
        crtc->fb = fb;
 
-       ttm_eu_fence_buffer_objects(&ticket, &res, fence);
+       nouveau_bo_fence(old_bo, fence);
+       ttm_bo_unreserve(&old_bo->bo);
        if (old_bo != new_bo)
                nouveau_bo_unpin(old_bo);
        nouveau_fence_unref(&fence);
        return 0;
 
 fail_unreserve:
-       ttm_eu_backoff_reservation(&ticket, &res);
+       ttm_bo_unreserve(&old_bo->bo);
 fail_unpin:
        mutex_unlock(&chan->cli->mutex);
        if (old_bo != new_bo)
@@ -674,8 +745,8 @@ nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
        if (ret)
                return ret;
 
-       ret = drm_gem_handle_create(file_priv, bo->gem, &args->handle);
-       drm_gem_object_unreference_unlocked(bo->gem);
+       ret = drm_gem_handle_create(file_priv, &bo->gem, &args->handle);
+       drm_gem_object_unreference_unlocked(&bo->gem);
        return ret;
 }
 
@@ -688,7 +759,7 @@ nouveau_display_dumb_map_offset(struct drm_file *file_priv,
 
        gem = drm_gem_object_lookup(dev, file_priv, handle);
        if (gem) {
-               struct nouveau_bo *bo = gem->driver_private;
+               struct nouveau_bo *bo = nouveau_gem_object(gem);
                *poffset = drm_vma_node_offset_addr(&bo->bo.vma_node);
                drm_gem_object_unreference_unlocked(gem);
                return 0;
index 025c66f..8bc8bab 100644 (file)
@@ -36,6 +36,8 @@ struct nouveau_display {
        int  (*init)(struct drm_device *);
        void (*fini)(struct drm_device *);
 
+       struct nouveau_eventh **vblank;
+
        struct drm_property *dithering_mode;
        struct drm_property *dithering_depth;
        struct drm_property *underscan_property;
@@ -59,6 +61,8 @@ void nouveau_display_fini(struct drm_device *dev);
 int  nouveau_display_suspend(struct drm_device *dev);
 void nouveau_display_repin(struct drm_device *dev);
 void nouveau_display_resume(struct drm_device *dev);
+int  nouveau_display_vblank_enable(struct drm_device *, int);
+void nouveau_display_vblank_disable(struct drm_device *, int);
 
 int  nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                            struct drm_pending_vblank_event *event,
index 690d593..984004d 100644 (file)
@@ -51,9 +51,11 @@ enum {
        NvSubCtxSurf2D  = 0,
        NvSubSw         = 1,
        NvSubImageBlit  = 2,
-       NvSub2D         = 3,
        NvSubGdiRect    = 3,
-       NvSubCopy       = 4,
+
+       NvSub2D         = 3, /* DO NOT CHANGE - hardcoded for kepler gr fifo */
+       NvSubCopy       = 4, /* DO NOT CHANGE - hardcoded for kepler gr fifo */
+       FermiSw         = 5, /* DO NOT CHANGE (well.. 6/7 will work...) */
 };
 
 /* Object handles. */
@@ -194,7 +196,6 @@ WIND_RING(struct nouveau_channel *chan)
 #define NV84_SUBCHAN_UEVENT                                          0x00000020
 #define NV84_SUBCHAN_WRCACHE_FLUSH                                   0x00000024
 #define NV10_SUBCHAN_REF_CNT                                         0x00000050
-#define NVSW_SUBCHAN_PAGE_FLIP                                       0x00000054
 #define NV11_SUBCHAN_DMA_SEMAPHORE                                   0x00000060
 #define NV11_SUBCHAN_SEMAPHORE_OFFSET                                0x00000064
 #define NV11_SUBCHAN_SEMAPHORE_ACQUIRE                               0x00000068
index e893c53..7a3759f 100644 (file)
@@ -37,6 +37,7 @@
 #include <engine/device.h>
 #include <engine/disp.h>
 #include <engine/fifo.h>
+#include <engine/software.h>
 
 #include <subdev/vm.h>
 
@@ -46,7 +47,8 @@
 #include "nouveau_gem.h"
 #include "nouveau_agp.h"
 #include "nouveau_vga.h"
-#include "nouveau_pm.h"
+#include "nouveau_sysfs.h"
+#include "nouveau_hwmon.h"
 #include "nouveau_acpi.h"
 #include "nouveau_bios.h"
 #include "nouveau_ioctl.h"
@@ -78,41 +80,6 @@ module_param_named(runpm, nouveau_runtime_pm, int, 0400);
 
 static struct drm_driver driver;
 
-static int
-nouveau_drm_vblank_handler(struct nouveau_eventh *event, int head)
-{
-       struct nouveau_drm *drm =
-               container_of(event, struct nouveau_drm, vblank[head]);
-       drm_handle_vblank(drm->dev, head);
-       return NVKM_EVENT_KEEP;
-}
-
-static int
-nouveau_drm_vblank_enable(struct drm_device *dev, int head)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
-
-       if (WARN_ON_ONCE(head > ARRAY_SIZE(drm->vblank)))
-               return -EIO;
-       WARN_ON_ONCE(drm->vblank[head].func);
-       drm->vblank[head].func = nouveau_drm_vblank_handler;
-       nouveau_event_get(pdisp->vblank, head, &drm->vblank[head]);
-       return 0;
-}
-
-static void
-nouveau_drm_vblank_disable(struct drm_device *dev, int head)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
-       if (drm->vblank[head].func)
-               nouveau_event_put(pdisp->vblank, head, &drm->vblank[head]);
-       else
-               WARN_ON_ONCE(1);
-       drm->vblank[head].func = NULL;
-}
-
 static u64
 nouveau_name(struct pci_dev *pdev)
 {
@@ -177,7 +144,8 @@ nouveau_accel_init(struct nouveau_drm *drm)
 
        /* initialise synchronisation routines */
        if      (device->card_type < NV_10) ret = nv04_fence_create(drm);
-       else if (device->chipset   <  0x17) ret = nv10_fence_create(drm);
+       else if (device->card_type < NV_11 ||
+                device->chipset   <  0x17) ret = nv10_fence_create(drm);
        else if (device->card_type < NV_50) ret = nv17_fence_create(drm);
        else if (device->chipset   <  0x84) ret = nv50_fence_create(drm);
        else if (device->card_type < NV_C0) ret = nv84_fence_create(drm);
@@ -224,6 +192,32 @@ nouveau_accel_init(struct nouveau_drm *drm)
                return;
        }
 
+       ret = nouveau_object_new(nv_object(drm), NVDRM_CHAN, NVDRM_NVSW,
+                                nouveau_abi16_swclass(drm), NULL, 0, &object);
+       if (ret == 0) {
+               struct nouveau_software_chan *swch = (void *)object->parent;
+               ret = RING_SPACE(drm->channel, 2);
+               if (ret == 0) {
+                       if (device->card_type < NV_C0) {
+                               BEGIN_NV04(drm->channel, NvSubSw, 0, 1);
+                               OUT_RING  (drm->channel, NVDRM_NVSW);
+                       } else
+                       if (device->card_type < NV_E0) {
+                               BEGIN_NVC0(drm->channel, FermiSw, 0, 1);
+                               OUT_RING  (drm->channel, 0x001f0000);
+                       }
+               }
+               swch = (void *)object->parent;
+               swch->flip = nouveau_flip_complete;
+               swch->flip_data = drm->channel;
+       }
+
+       if (ret) {
+               NV_ERROR(drm, "failed to allocate software object, %d\n", ret);
+               nouveau_accel_fini(drm);
+               return;
+       }
+
        if (device->card_type < NV_C0) {
                ret = nouveau_gpuobj_new(drm->device, NULL, 32, 0, 0,
                                        &drm->notify);
@@ -418,8 +412,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
                        goto fail_dispinit;
        }
 
-       nouveau_pm_init(dev);
-
+       nouveau_sysfs_init(dev);
+       nouveau_hwmon_init(dev);
        nouveau_accel_init(drm);
        nouveau_fbcon_init(dev);
 
@@ -455,8 +449,8 @@ nouveau_drm_unload(struct drm_device *dev)
        pm_runtime_get_sync(dev->dev);
        nouveau_fbcon_fini(dev);
        nouveau_accel_fini(drm);
-
-       nouveau_pm_fini(dev);
+       nouveau_hwmon_fini(dev);
+       nouveau_sysfs_fini(dev);
 
        if (dev->mode_config.num_crtc)
                nouveau_display_fini(dev);
@@ -496,16 +490,16 @@ nouveau_do_suspend(struct drm_device *dev)
        int ret;
 
        if (dev->mode_config.num_crtc) {
-               NV_SUSPEND(drm, "suspending display...\n");
+               NV_INFO(drm, "suspending display...\n");
                ret = nouveau_display_suspend(dev);
                if (ret)
                        return ret;
        }
 
-       NV_SUSPEND(drm, "evicting buffers...\n");
+       NV_INFO(drm, "evicting buffers...\n");
        ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM);
 
-       NV_SUSPEND(drm, "waiting for kernel channels to go idle...\n");
+       NV_INFO(drm, "waiting for kernel channels to go idle...\n");
        if (drm->cechan) {
                ret = nouveau_channel_idle(drm->cechan);
                if (ret)
@@ -518,7 +512,7 @@ nouveau_do_suspend(struct drm_device *dev)
                        return ret;
        }
 
-       NV_SUSPEND(drm, "suspending client object trees...\n");
+       NV_INFO(drm, "suspending client object trees...\n");
        if (drm->fence && nouveau_fence(drm)->suspend) {
                if (!nouveau_fence(drm)->suspend(drm))
                        return -ENOMEM;
@@ -530,7 +524,7 @@ nouveau_do_suspend(struct drm_device *dev)
                        goto fail_client;
        }
 
-       NV_SUSPEND(drm, "suspending kernel object tree...\n");
+       NV_INFO(drm, "suspending kernel object tree...\n");
        ret = nouveau_client_fini(&drm->client.base, true);
        if (ret)
                goto fail_client;
@@ -544,7 +538,7 @@ fail_client:
        }
 
        if (dev->mode_config.num_crtc) {
-               NV_SUSPEND(drm, "resuming display...\n");
+               NV_INFO(drm, "resuming display...\n");
                nouveau_display_resume(dev);
        }
        return ret;
@@ -563,7 +557,6 @@ int nouveau_pmops_suspend(struct device *dev)
        if (drm_dev->mode_config.num_crtc)
                nouveau_fbcon_set_suspend(drm_dev, 1);
 
-       nv_suspend_set_printk_level(NV_DBG_INFO);
        ret = nouveau_do_suspend(drm_dev);
        if (ret)
                return ret;
@@ -571,8 +564,6 @@ int nouveau_pmops_suspend(struct device *dev)
        pci_save_state(pdev);
        pci_disable_device(pdev);
        pci_set_power_state(pdev, PCI_D3hot);
-       nv_suspend_set_printk_level(NV_DBG_DEBUG);
-
        return 0;
 }
 
@@ -582,15 +573,15 @@ nouveau_do_resume(struct drm_device *dev)
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_cli *cli;
 
-       NV_SUSPEND(drm, "re-enabling device...\n");
+       NV_INFO(drm, "re-enabling device...\n");
 
        nouveau_agp_reset(drm);
 
-       NV_SUSPEND(drm, "resuming kernel object tree...\n");
+       NV_INFO(drm, "resuming kernel object tree...\n");
        nouveau_client_init(&drm->client.base);
        nouveau_agp_init(drm);
 
-       NV_SUSPEND(drm, "resuming client object trees...\n");
+       NV_INFO(drm, "resuming client object trees...\n");
        if (drm->fence && nouveau_fence(drm)->resume)
                nouveau_fence(drm)->resume(drm);
 
@@ -599,10 +590,9 @@ nouveau_do_resume(struct drm_device *dev)
        }
 
        nouveau_run_vbios_init(dev);
-       nouveau_pm_resume(dev);
 
        if (dev->mode_config.num_crtc) {
-               NV_SUSPEND(drm, "resuming display...\n");
+               NV_INFO(drm, "resuming display...\n");
                nouveau_display_repin(dev);
        }
 
@@ -626,19 +616,15 @@ int nouveau_pmops_resume(struct device *dev)
                return ret;
        pci_set_master(pdev);
 
-       nv_suspend_set_printk_level(NV_DBG_INFO);
        ret = nouveau_do_resume(drm_dev);
-       if (ret) {
-               nv_suspend_set_printk_level(NV_DBG_DEBUG);
+       if (ret)
                return ret;
-       }
        if (drm_dev->mode_config.num_crtc)
                nouveau_fbcon_set_suspend(drm_dev, 0);
 
        nouveau_fbcon_zfill_all(drm_dev);
        if (drm_dev->mode_config.num_crtc)
                nouveau_display_resume(drm_dev);
-       nv_suspend_set_printk_level(NV_DBG_DEBUG);
        return 0;
 }
 
@@ -648,12 +634,10 @@ static int nouveau_pmops_freeze(struct device *dev)
        struct drm_device *drm_dev = pci_get_drvdata(pdev);
        int ret;
 
-       nv_suspend_set_printk_level(NV_DBG_INFO);
        if (drm_dev->mode_config.num_crtc)
                nouveau_fbcon_set_suspend(drm_dev, 1);
 
        ret = nouveau_do_suspend(drm_dev);
-       nv_suspend_set_printk_level(NV_DBG_DEBUG);
        return ret;
 }
 
@@ -663,18 +647,14 @@ static int nouveau_pmops_thaw(struct device *dev)
        struct drm_device *drm_dev = pci_get_drvdata(pdev);
        int ret;
 
-       nv_suspend_set_printk_level(NV_DBG_INFO);
        ret = nouveau_do_resume(drm_dev);
-       if (ret) {
-               nv_suspend_set_printk_level(NV_DBG_DEBUG);
+       if (ret)
                return ret;
-       }
        if (drm_dev->mode_config.num_crtc)
                nouveau_fbcon_set_suspend(drm_dev, 0);
        nouveau_fbcon_zfill_all(drm_dev);
        if (drm_dev->mode_config.num_crtc)
                nouveau_display_resume(drm_dev);
-       nv_suspend_set_printk_level(NV_DBG_DEBUG);
        return 0;
 }
 
@@ -816,8 +796,8 @@ driver = {
 #endif
 
        .get_vblank_counter = drm_vblank_count,
-       .enable_vblank = nouveau_drm_vblank_enable,
-       .disable_vblank = nouveau_drm_vblank_disable,
+       .enable_vblank = nouveau_display_vblank_enable,
+       .disable_vblank = nouveau_display_vblank_disable,
 
        .ioctls = nouveau_ioctls,
        .num_ioctls = ARRAY_SIZE(nouveau_ioctls),
@@ -834,7 +814,6 @@ driver = {
        .gem_prime_vmap = nouveau_gem_prime_vmap,
        .gem_prime_vunmap = nouveau_gem_prime_vunmap,
 
-       .gem_init_object = nouveau_gem_object_new,
        .gem_free_object = nouveau_gem_object_del,
        .gem_open_object = nouveau_gem_object_open,
        .gem_close_object = nouveau_gem_object_close,
@@ -879,6 +858,7 @@ static int nouveau_pmops_runtime_suspend(struct device *dev)
        if (nouveau_runtime_pm == 0)
                return -EINVAL;
 
+       nv_debug_level(SILENT);
        drm_kms_helper_poll_disable(drm_dev);
        vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
        nouveau_switcheroo_optimus_dsm();
@@ -915,6 +895,7 @@ static int nouveau_pmops_runtime_resume(struct device *dev)
        nv_mask(device, 0x88488, (1 << 25), (1 << 25));
        vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
        drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
+       nv_debug_level(NORMAL);
        return ret;
 }
 
index 994fd6e..4b0fb6c 100644 (file)
@@ -51,10 +51,12 @@ struct nouveau_drm_tile {
 };
 
 enum nouveau_drm_handle {
-       NVDRM_CLIENT = 0xffffffff,
-       NVDRM_DEVICE = 0xdddddddd,
-       NVDRM_PUSH   = 0xbbbb0000, /* |= client chid */
-       NVDRM_CHAN   = 0xcccc0000, /* |= client chid */
+       NVDRM_CLIENT  = 0xffffffff,
+       NVDRM_DEVICE  = 0xdddddddd,
+       NVDRM_CONTROL = 0xdddddddc,
+       NVDRM_PUSH    = 0xbbbb0000, /* |= client chid */
+       NVDRM_CHAN    = 0xcccc0000, /* |= client chid */
+       NVDRM_NVSW    = 0x55550000,
 };
 
 struct nouveau_cli {
@@ -127,10 +129,10 @@ struct nouveau_drm {
        struct nvbios vbios;
        struct nouveau_display *display;
        struct backlight_device *backlight;
-       struct nouveau_eventh vblank[4];
 
        /* power management */
-       struct nouveau_pm *pm;
+       struct nouveau_hwmon *hwmon;
+       struct nouveau_sysfs *sysfs;
 
        /* display power reference */
        bool have_disp_power_ref;
@@ -154,7 +156,6 @@ nouveau_dev(struct drm_device *dev)
 int nouveau_pmops_suspend(struct device *);
 int nouveau_pmops_resume(struct device *);
 
-#define NV_SUSPEND(cli, fmt, args...) nv_suspend((cli), fmt, ##args)
 #define NV_FATAL(cli, fmt, args...) nv_fatal((cli), fmt, ##args)
 #define NV_ERROR(cli, fmt, args...) nv_error((cli), fmt, ##args)
 #define NV_WARN(cli, fmt, args...) nv_warn((cli), fmt, ##args)
index a86ecf6..7903e0e 100644 (file)
@@ -420,7 +420,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon)
                nouveau_bo_unmap(nouveau_fb->nvbo);
                nouveau_bo_vma_del(nouveau_fb->nvbo, &nouveau_fb->vma);
                nouveau_bo_unpin(nouveau_fb->nvbo);
-               drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem);
+               drm_gem_object_unreference_unlocked(&nouveau_fb->nvbo->gem);
                nouveau_fb->nvbo = NULL;
        }
        drm_fb_helper_fini(&fbcon->helper);
@@ -503,34 +503,45 @@ nouveau_fbcon_fini(struct drm_device *dev)
        drm->fbcon = NULL;
 }
 
-void nouveau_fbcon_save_disable_accel(struct drm_device *dev)
+void
+nouveau_fbcon_save_disable_accel(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
-
-       drm->fbcon->saved_flags = drm->fbcon->helper.fbdev->flags;
-       drm->fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED;
+       if (drm->fbcon) {
+               drm->fbcon->saved_flags = drm->fbcon->helper.fbdev->flags;
+               drm->fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED;
+       }
 }
 
-void nouveau_fbcon_restore_accel(struct drm_device *dev)
+void
+nouveau_fbcon_restore_accel(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
-       drm->fbcon->helper.fbdev->flags = drm->fbcon->saved_flags;
+       if (drm->fbcon) {
+               drm->fbcon->helper.fbdev->flags = drm->fbcon->saved_flags;
+       }
 }
 
-void nouveau_fbcon_set_suspend(struct drm_device *dev, int state)
+void
+nouveau_fbcon_set_suspend(struct drm_device *dev, int state)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
-       console_lock();
-       if (state == 0)
-               nouveau_fbcon_save_disable_accel(dev);
-       fb_set_suspend(drm->fbcon->helper.fbdev, state);
-       if (state == 1)
-               nouveau_fbcon_restore_accel(dev);
-       console_unlock();
+       if (drm->fbcon) {
+               console_lock();
+               if (state == 0)
+                       nouveau_fbcon_save_disable_accel(dev);
+               fb_set_suspend(drm->fbcon->helper.fbdev, state);
+               if (state == 1)
+                       nouveau_fbcon_restore_accel(dev);
+               console_unlock();
+       }
 }
 
-void nouveau_fbcon_zfill_all(struct drm_device *dev)
+void
+nouveau_fbcon_zfill_all(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
-       nouveau_fbcon_zfill(dev, drm->fbcon);
+       if (drm->fbcon) {
+               nouveau_fbcon_zfill(dev, drm->fbcon);
+       }
 }
index be31499..40cf52e 100644 (file)
@@ -165,17 +165,11 @@ nouveau_fence_done(struct nouveau_fence *fence)
        return !fence->channel;
 }
 
-struct nouveau_fence_uevent {
-       struct nouveau_eventh handler;
-       struct nouveau_fence_priv *priv;
-};
-
 static int
-nouveau_fence_wait_uevent_handler(struct nouveau_eventh *event, int index)
+nouveau_fence_wait_uevent_handler(void *data, int index)
 {
-       struct nouveau_fence_uevent *uevent =
-               container_of(event, struct nouveau_fence_uevent, handler);
-       wake_up_all(&uevent->priv->waiting);
+       struct nouveau_fence_priv *priv = data;
+       wake_up_all(&priv->waiting);
        return NVKM_EVENT_KEEP;
 }
 
@@ -186,13 +180,16 @@ nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
        struct nouveau_channel *chan = fence->channel;
        struct nouveau_fifo *pfifo = nouveau_fifo(chan->drm->device);
        struct nouveau_fence_priv *priv = chan->drm->fence;
-       struct nouveau_fence_uevent uevent = {
-               .handler.func = nouveau_fence_wait_uevent_handler,
-               .priv = priv,
-       };
+       struct nouveau_eventh *handler;
        int ret = 0;
 
-       nouveau_event_get(pfifo->uevent, 0, &uevent.handler);
+       ret = nouveau_event_new(pfifo->uevent, 0,
+                               nouveau_fence_wait_uevent_handler,
+                               priv, &handler);
+       if (ret)
+               return ret;
+
+       nouveau_event_get(handler);
 
        if (fence->timeout) {
                unsigned long timeout = fence->timeout - jiffies;
@@ -224,7 +221,7 @@ nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
                }
        }
 
-       nouveau_event_put(pfifo->uevent, 0, &uevent.handler);
+       nouveau_event_ref(NULL, &handler);
        if (unlikely(ret < 0))
                return ret;
 
@@ -309,7 +306,8 @@ nouveau_fence_unref(struct nouveau_fence **pfence)
 struct nouveau_fence *
 nouveau_fence_ref(struct nouveau_fence *fence)
 {
-       kref_get(&fence->kref);
+       if (fence)
+               kref_get(&fence->kref);
        return fence;
 }
 
index f32b712..78a27f8 100644 (file)
 #include "nouveau_ttm.h"
 #include "nouveau_gem.h"
 
-int
-nouveau_gem_object_new(struct drm_gem_object *gem)
-{
-       return 0;
-}
-
 void
 nouveau_gem_object_del(struct drm_gem_object *gem)
 {
-       struct nouveau_bo *nvbo = gem->driver_private;
+       struct nouveau_bo *nvbo = nouveau_gem_object(gem);
        struct ttm_buffer_object *bo = &nvbo->bo;
 
-       if (!nvbo)
-               return;
-       nvbo->gem = NULL;
-
        if (gem->import_attach)
                drm_prime_gem_destroy(gem, nvbo->bo.sg);
 
-       ttm_bo_unref(&bo);
-
        drm_gem_object_release(gem);
-       kfree(gem);
+
+       /* reset filp so nouveau_bo_del_ttm() can test for it */
+       gem->filp = NULL;
+       ttm_bo_unref(&bo);
 }
 
 int
@@ -115,8 +106,7 @@ nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
 
        if (mapped) {
                spin_lock(&nvbo->bo.bdev->fence_lock);
-               if (nvbo->bo.sync_obj)
-                       fence = nouveau_fence_ref(nvbo->bo.sync_obj);
+               fence = nouveau_fence_ref(nvbo->bo.sync_obj);
                spin_unlock(&nvbo->bo.bdev->fence_lock);
        }
 
@@ -186,14 +176,15 @@ nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain,
        if (nv_device(drm->device)->card_type >= NV_50)
                nvbo->valid_domains &= domain;
 
-       nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size);
-       if (!nvbo->gem) {
+       /* Initialize the embedded gem-object. We return a single gem-reference
+        * to the caller, instead of a normal nouveau_bo ttm reference. */
+       ret = drm_gem_object_init(dev, &nvbo->gem, nvbo->bo.mem.size);
+       if (ret) {
                nouveau_bo_ref(NULL, pnvbo);
                return -ENOMEM;
        }
 
-       nvbo->bo.persistent_swap_storage = nvbo->gem->filp;
-       nvbo->gem->driver_private = nvbo;
+       nvbo->bo.persistent_swap_storage = nvbo->gem.filp;
        return 0;
 }
 
@@ -250,15 +241,15 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
        if (ret)
                return ret;
 
-       ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle);
+       ret = drm_gem_handle_create(file_priv, &nvbo->gem, &req->info.handle);
        if (ret == 0) {
-               ret = nouveau_gem_info(file_priv, nvbo->gem, &req->info);
+               ret = nouveau_gem_info(file_priv, &nvbo->gem, &req->info);
                if (ret)
                        drm_gem_handle_delete(file_priv, req->info.handle);
        }
 
        /* drop reference from allocate - handle holds it now */
-       drm_gem_object_unreference_unlocked(nvbo->gem);
+       drm_gem_object_unreference_unlocked(&nvbo->gem);
        return ret;
 }
 
@@ -266,7 +257,7 @@ static int
 nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains,
                       uint32_t write_domains, uint32_t valid_domains)
 {
-       struct nouveau_bo *nvbo = gem->driver_private;
+       struct nouveau_bo *nvbo = nouveau_gem_object(gem);
        struct ttm_buffer_object *bo = &nvbo->bo;
        uint32_t domains = valid_domains & nvbo->valid_domains &
                (write_domains ? write_domains : read_domains);
@@ -317,7 +308,8 @@ validate_fini_list(struct list_head *list, struct nouveau_fence *fence,
        list_for_each_safe(entry, tmp, list) {
                nvbo = list_entry(entry, struct nouveau_bo, entry);
 
-               nouveau_bo_fence(nvbo, fence);
+               if (likely(fence))
+                       nouveau_bo_fence(nvbo, fence);
 
                if (unlikely(nvbo->validate_mapped)) {
                        ttm_bo_kunmap(&nvbo->kmap);
@@ -327,7 +319,7 @@ validate_fini_list(struct list_head *list, struct nouveau_fence *fence,
                list_del(&nvbo->entry);
                nvbo->reserved_by = NULL;
                ttm_bo_unreserve_ticket(&nvbo->bo, ticket);
-               drm_gem_object_unreference_unlocked(nvbo->gem);
+               drm_gem_object_unreference_unlocked(&nvbo->gem);
        }
 }
 
@@ -376,7 +368,7 @@ retry:
                        validate_fini(op, NULL);
                        return -ENOENT;
                }
-               nvbo = gem->driver_private;
+               nvbo = nouveau_gem_object(gem);
                if (nvbo == res_bo) {
                        res_bo = NULL;
                        drm_gem_object_unreference_unlocked(gem);
@@ -446,8 +438,7 @@ validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo)
        int ret = 0;
 
        spin_lock(&nvbo->bo.bdev->fence_lock);
-       if (nvbo->bo.sync_obj)
-               fence = nouveau_fence_ref(nvbo->bo.sync_obj);
+       fence = nouveau_fence_ref(nvbo->bo.sync_obj);
        spin_unlock(&nvbo->bo.bdev->fence_lock);
 
        if (fence) {
@@ -478,7 +469,7 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli,
                        return ret;
                }
 
-               ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains,
+               ret = nouveau_gem_set_domain(&nvbo->gem, b->read_domains,
                                             b->write_domains,
                                             b->valid_domains);
                if (unlikely(ret)) {
index 502e429..7caca05 100644 (file)
 static inline struct nouveau_bo *
 nouveau_gem_object(struct drm_gem_object *gem)
 {
-       return gem ? gem->driver_private : NULL;
+       return gem ? container_of(gem, struct nouveau_bo, gem) : NULL;
 }
 
 /* nouveau_gem.c */
 extern int nouveau_gem_new(struct drm_device *, int size, int align,
                           uint32_t domain, uint32_t tile_mode,
                           uint32_t tile_flags, struct nouveau_bo **);
-extern int nouveau_gem_object_new(struct drm_gem_object *);
 extern void nouveau_gem_object_del(struct drm_gem_object *);
 extern int nouveau_gem_object_open(struct drm_gem_object *, struct drm_file *);
 extern void nouveau_gem_object_close(struct drm_gem_object *,
similarity index 57%
rename from drivers/gpu/drm/nouveau/nouveau_pm.c
rename to drivers/gpu/drm/nouveau/nouveau_hwmon.c
index 936b442..38a4db5 100644 (file)
 #include <drm/drmP.h>
 
 #include "nouveau_drm.h"
-#include "nouveau_pm.h"
+#include "nouveau_hwmon.h"
 
 #include <subdev/gpio.h>
 #include <subdev/timer.h>
 #include <subdev/therm.h>
 
-MODULE_PARM_DESC(perflvl, "Performance level (default: boot)");
-static char *nouveau_perflvl;
-module_param_named(perflvl, nouveau_perflvl, charp, 0400);
-
-MODULE_PARM_DESC(perflvl_wr, "Allow perflvl changes (warning: dangerous!)");
-static int nouveau_perflvl_wr;
-module_param_named(perflvl_wr, nouveau_perflvl_wr, int, 0400);
-
-static int
-nouveau_pm_perflvl_aux(struct drm_device *dev, struct nouveau_pm_level *perflvl,
-                      struct nouveau_pm_level *a, struct nouveau_pm_level *b)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nouveau_therm *therm = nouveau_therm(drm->device);
-       int ret;
-
-       /*XXX: not on all boards, we should control based on temperature
-        *     on recent boards..  or maybe on some other factor we don't
-        *     know about?
-        */
-       if (therm && therm->fan_set &&
-               a->fanspeed && b->fanspeed && b->fanspeed > a->fanspeed) {
-               ret = therm->fan_set(therm, perflvl->fanspeed);
-               if (ret && ret != -ENODEV) {
-                       NV_ERROR(drm, "fanspeed set failed: %d\n", ret);
-               }
-       }
-
-       if (pm->voltage.supported && pm->voltage_set) {
-               if (perflvl->volt_min && b->volt_min > a->volt_min) {
-                       ret = pm->voltage_set(dev, perflvl->volt_min);
-                       if (ret) {
-                               NV_ERROR(drm, "voltage set failed: %d\n", ret);
-                               return ret;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static int
-nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       void *state;
-       int ret;
-
-       if (perflvl == pm->cur)
-               return 0;
-
-       ret = nouveau_pm_perflvl_aux(dev, perflvl, pm->cur, perflvl);
-       if (ret)
-               return ret;
-
-       state = pm->clocks_pre(dev, perflvl);
-       if (IS_ERR(state)) {
-               ret = PTR_ERR(state);
-               goto error;
-       }
-       ret = pm->clocks_set(dev, state);
-       if (ret)
-               goto error;
-
-       ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
-       if (ret)
-               return ret;
-
-       pm->cur = perflvl;
-       return 0;
-
-error:
-       /* restore the fan speed and voltage before leaving */
-       nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
-       return ret;
-}
-
-void
-nouveau_pm_trigger(struct drm_device *dev)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_timer *ptimer = nouveau_timer(drm->device);
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nouveau_pm_profile *profile = NULL;
-       struct nouveau_pm_level *perflvl = NULL;
-       int ret;
-
-       /* select power profile based on current power source */
-       if (power_supply_is_system_supplied())
-               profile = pm->profile_ac;
-       else
-               profile = pm->profile_dc;
-
-       if (profile != pm->profile) {
-               pm->profile->func->fini(pm->profile);
-               pm->profile = profile;
-               pm->profile->func->init(pm->profile);
-       }
-
-       /* select performance level based on profile */
-       perflvl = profile->func->select(profile);
-
-       /* change perflvl, if necessary */
-       if (perflvl != pm->cur) {
-               u64 time0 = ptimer->read(ptimer);
-
-               NV_INFO(drm, "setting performance level: %d", perflvl->id);
-               ret = nouveau_pm_perflvl_set(dev, perflvl);
-               if (ret)
-                       NV_INFO(drm, "> reclocking failed: %d\n\n", ret);
-
-               NV_INFO(drm, "> reclocking took %lluns\n\n",
-                            ptimer->read(ptimer) - time0);
-       }
-}
-
-static struct nouveau_pm_profile *
-profile_find(struct drm_device *dev, const char *string)
-{
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nouveau_pm_profile *profile;
-
-       list_for_each_entry(profile, &pm->profiles, head) {
-               if (!strncmp(profile->name, string, sizeof(profile->name)))
-                       return profile;
-       }
-
-       return NULL;
-}
-
-static int
-nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
-{
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nouveau_pm_profile *ac = NULL, *dc = NULL;
-       char string[16], *cur = string, *ptr;
-
-       /* safety precaution, for now */
-       if (nouveau_perflvl_wr != 7777)
-               return -EPERM;
-
-       strncpy(string, profile, sizeof(string));
-       string[sizeof(string) - 1] = 0;
-       if ((ptr = strchr(string, '\n')))
-               *ptr = '\0';
-
-       ptr = strsep(&cur, ",");
-       if (ptr)
-               ac = profile_find(dev, ptr);
-
-       ptr = strsep(&cur, ",");
-       if (ptr)
-               dc = profile_find(dev, ptr);
-       else
-               dc = ac;
-
-       if (ac == NULL || dc == NULL)
-               return -EINVAL;
-
-       pm->profile_ac = ac;
-       pm->profile_dc = dc;
-       nouveau_pm_trigger(dev);
-       return 0;
-}
-
-static void
-nouveau_pm_static_dummy(struct nouveau_pm_profile *profile)
-{
-}
-
-static struct nouveau_pm_level *
-nouveau_pm_static_select(struct nouveau_pm_profile *profile)
-{
-       return container_of(profile, struct nouveau_pm_level, profile);
-}
-
-const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = {
-       .destroy = nouveau_pm_static_dummy,
-       .init = nouveau_pm_static_dummy,
-       .fini = nouveau_pm_static_dummy,
-       .select = nouveau_pm_static_select,
-};
-
-static int
-nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nouveau_therm *therm = nouveau_therm(drm->device);
-       int ret;
-
-       memset(perflvl, 0, sizeof(*perflvl));
-
-       if (pm->clocks_get) {
-               ret = pm->clocks_get(dev, perflvl);
-               if (ret)
-                       return ret;
-       }
-
-       if (pm->voltage.supported && pm->voltage_get) {
-               ret = pm->voltage_get(dev);
-               if (ret > 0) {
-                       perflvl->volt_min = ret;
-                       perflvl->volt_max = ret;
-               }
-       }
-
-       if (therm && therm->fan_get) {
-               ret = therm->fan_get(therm);
-               if (ret >= 0)
-                       perflvl->fanspeed = ret;
-       }
-
-       nouveau_mem_timing_read(dev, &perflvl->timing);
-       return 0;
-}
-
-static void
-nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
-{
-       char c[16], s[16], v[32], f[16], m[16];
-
-       c[0] = '\0';
-       if (perflvl->core)
-               snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
-
-       s[0] = '\0';
-       if (perflvl->shader)
-               snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
-
-       m[0] = '\0';
-       if (perflvl->memory)
-               snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
-
-       v[0] = '\0';
-       if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
-               snprintf(v, sizeof(v), " voltage %dmV-%dmV",
-                        perflvl->volt_min / 1000, perflvl->volt_max / 1000);
-       } else
-       if (perflvl->volt_min) {
-               snprintf(v, sizeof(v), " voltage %dmV",
-                        perflvl->volt_min / 1000);
-       }
-
-       f[0] = '\0';
-       if (perflvl->fanspeed)
-               snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
-
-       snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f);
-}
-
-static ssize_t
-nouveau_pm_get_perflvl_info(struct device *d,
-                           struct device_attribute *a, char *buf)
-{
-       struct nouveau_pm_level *perflvl =
-               container_of(a, struct nouveau_pm_level, dev_attr);
-       char *ptr = buf;
-       int len = PAGE_SIZE;
-
-       snprintf(ptr, len, "%d:", perflvl->id);
-       ptr += strlen(buf);
-       len -= strlen(buf);
-
-       nouveau_pm_perflvl_info(perflvl, ptr, len);
-       return strlen(buf);
-}
-
-static ssize_t
-nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
-{
-       struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nouveau_pm_level cur;
-       int len = PAGE_SIZE, ret;
-       char *ptr = buf;
-
-       snprintf(ptr, len, "profile: %s, %s\nc:",
-                pm->profile_ac->name, pm->profile_dc->name);
-       ptr += strlen(buf);
-       len -= strlen(buf);
-
-       ret = nouveau_pm_perflvl_get(dev, &cur);
-       if (ret == 0)
-               nouveau_pm_perflvl_info(&cur, ptr, len);
-       return strlen(buf);
-}
-
-static ssize_t
-nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
-                      const char *buf, size_t count)
-{
-       struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
-       int ret;
-
-       ret = nouveau_pm_profile_set(dev, buf);
-       if (ret)
-               return ret;
-       return strlen(buf);
-}
-
-static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
-                  nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
-
-static int
-nouveau_sysfs_init(struct drm_device *dev)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct device *d = &dev->pdev->dev;
-       int ret, i;
-
-       ret = device_create_file(d, &dev_attr_performance_level);
-       if (ret)
-               return ret;
-
-       for (i = 0; i < pm->nr_perflvl; i++) {
-               struct nouveau_pm_level *perflvl = &pm->perflvl[i];
-
-               perflvl->dev_attr.attr.name = perflvl->name;
-               perflvl->dev_attr.attr.mode = S_IRUGO;
-               perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
-               perflvl->dev_attr.store = NULL;
-               sysfs_attr_init(&perflvl->dev_attr.attr);
-
-               ret = device_create_file(d, &perflvl->dev_attr);
-               if (ret) {
-                       NV_ERROR(drm, "failed pervlvl %d sysfs: %d\n",
-                                perflvl->id, i);
-                       perflvl->dev_attr.attr.name = NULL;
-                       nouveau_pm_fini(dev);
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-
-static void
-nouveau_sysfs_fini(struct drm_device *dev)
-{
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct device *d = &dev->pdev->dev;
-       int i;
-
-       device_remove_file(d, &dev_attr_performance_level);
-       for (i = 0; i < pm->nr_perflvl; i++) {
-               struct nouveau_pm_level *pl = &pm->perflvl[i];
-
-               if (!pl->dev_attr.attr.name)
-                       break;
-
-               device_remove_file(d, &pl->dev_attr);
-       }
-}
-
 #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
 static ssize_t
 nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
@@ -778,9 +421,6 @@ nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a,
        int ret = -ENODEV;
        long value;
 
-       if (nouveau_perflvl_wr != 7777)
-               return -EPERM;
-
        if (kstrtol(buf, 10, &value) == -EINVAL)
                return -EINVAL;
 
@@ -919,17 +559,21 @@ static const struct attribute_group hwmon_pwm_fan_attrgroup = {
 };
 #endif
 
-static int
+int
 nouveau_hwmon_init(struct drm_device *dev)
 {
-       struct nouveau_pm *pm = nouveau_pm(dev);
-
 #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_therm *therm = nouveau_therm(drm->device);
+       struct nouveau_hwmon *hwmon;
        struct device *hwmon_dev;
        int ret = 0;
 
+       hwmon = drm->hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL);
+       if (!hwmon)
+               return -ENOMEM;
+       hwmon->dev = dev;
+
        if (!therm || !therm->temp_get || !therm->attr_get || !therm->attr_set)
                return -ENODEV;
 
@@ -976,199 +620,37 @@ nouveau_hwmon_init(struct drm_device *dev)
                        goto error;
        }
 
-       pm->hwmon = hwmon_dev;
+       hwmon->hwmon = hwmon_dev;
 
        return 0;
 
 error:
        NV_ERROR(drm, "Unable to create some hwmon sysfs files: %d\n", ret);
        hwmon_device_unregister(hwmon_dev);
-       pm->hwmon = NULL;
+       hwmon->hwmon = NULL;
        return ret;
 #else
-       pm->hwmon = NULL;
+       hwmon->hwmon = NULL;
        return 0;
 #endif
 }
 
-static void
+void
 nouveau_hwmon_fini(struct drm_device *dev)
 {
 #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
-       struct nouveau_pm *pm = nouveau_pm(dev);
+       struct nouveau_hwmon *hwmon = nouveau_hwmon(dev);
 
-       if (pm->hwmon) {
-               sysfs_remove_group(&pm->hwmon->kobj, &hwmon_default_attrgroup);
-               sysfs_remove_group(&pm->hwmon->kobj, &hwmon_temp_attrgroup);
-               sysfs_remove_group(&pm->hwmon->kobj, &hwmon_pwm_fan_attrgroup);
-               sysfs_remove_group(&pm->hwmon->kobj, &hwmon_fan_rpm_attrgroup);
+       if (hwmon->hwmon) {
+               sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_default_attrgroup);
+               sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_temp_attrgroup);
+               sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_pwm_fan_attrgroup);
+               sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_fan_rpm_attrgroup);
 
-               hwmon_device_unregister(pm->hwmon);
+               hwmon_device_unregister(hwmon->hwmon);
        }
-#endif
-}
-
-#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
-static int
-nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
-{
-       struct nouveau_pm *pm = container_of(nb, struct nouveau_pm, acpi_nb);
-       struct nouveau_drm *drm = nouveau_drm(pm->dev);
-       struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
-
-       if (strcmp(entry->device_class, "ac_adapter") == 0) {
-               bool ac = power_supply_is_system_supplied();
 
-               NV_DEBUG(drm, "power supply changed: %s\n", ac ? "AC" : "DC");
-               nouveau_pm_trigger(pm->dev);
-       }
-
-       return NOTIFY_OK;
-}
+       nouveau_drm(dev)->hwmon = NULL;
+       kfree(hwmon);
 #endif
-
-int
-nouveau_pm_init(struct drm_device *dev)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_pm *pm;
-       char info[256];
-       int ret, i;
-
-       pm = drm->pm = kzalloc(sizeof(*pm), GFP_KERNEL);
-       if (!pm)
-               return -ENOMEM;
-
-       pm->dev = dev;
-
-       if (device->card_type < NV_40) {
-               pm->clocks_get = nv04_pm_clocks_get;
-               pm->clocks_pre = nv04_pm_clocks_pre;
-               pm->clocks_set = nv04_pm_clocks_set;
-               if (nouveau_gpio(drm->device)) {
-                       pm->voltage_get = nouveau_voltage_gpio_get;
-                       pm->voltage_set = nouveau_voltage_gpio_set;
-               }
-       } else
-       if (device->card_type < NV_50) {
-               pm->clocks_get = nv40_pm_clocks_get;
-               pm->clocks_pre = nv40_pm_clocks_pre;
-               pm->clocks_set = nv40_pm_clocks_set;
-               pm->voltage_get = nouveau_voltage_gpio_get;
-               pm->voltage_set = nouveau_voltage_gpio_set;
-       } else
-       if (device->card_type < NV_C0) {
-               if (device->chipset <  0xa3 ||
-                   device->chipset == 0xaa ||
-                   device->chipset == 0xac) {
-                       pm->clocks_get = nv50_pm_clocks_get;
-                       pm->clocks_pre = nv50_pm_clocks_pre;
-                       pm->clocks_set = nv50_pm_clocks_set;
-               } else {
-                       pm->clocks_get = nva3_pm_clocks_get;
-                       pm->clocks_pre = nva3_pm_clocks_pre;
-                       pm->clocks_set = nva3_pm_clocks_set;
-               }
-               pm->voltage_get = nouveau_voltage_gpio_get;
-               pm->voltage_set = nouveau_voltage_gpio_set;
-       } else
-       if (device->card_type < NV_E0) {
-               pm->clocks_get = nvc0_pm_clocks_get;
-               pm->clocks_pre = nvc0_pm_clocks_pre;
-               pm->clocks_set = nvc0_pm_clocks_set;
-               pm->voltage_get = nouveau_voltage_gpio_get;
-               pm->voltage_set = nouveau_voltage_gpio_set;
-       }
-
-
-       /* parse aux tables from vbios */
-       nouveau_volt_init(dev);
-
-       INIT_LIST_HEAD(&pm->profiles);
-
-       /* determine current ("boot") performance level */
-       ret = nouveau_pm_perflvl_get(dev, &pm->boot);
-       if (ret) {
-               NV_ERROR(drm, "failed to determine boot perflvl\n");
-               return ret;
-       }
-
-       strncpy(pm->boot.name, "boot", 4);
-       strncpy(pm->boot.profile.name, "boot", 4);
-       pm->boot.profile.func = &nouveau_pm_static_profile_func;
-
-       list_add(&pm->boot.profile.head, &pm->profiles);
-
-       pm->profile_ac = &pm->boot.profile;
-       pm->profile_dc = &pm->boot.profile;
-       pm->profile = &pm->boot.profile;
-       pm->cur = &pm->boot;
-
-       /* add performance levels from vbios */
-       nouveau_perf_init(dev);
-
-       /* display available performance levels */
-       NV_INFO(drm, "%d available performance level(s)\n", pm->nr_perflvl);
-       for (i = 0; i < pm->nr_perflvl; i++) {
-               nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
-               NV_INFO(drm, "%d:%s", pm->perflvl[i].id, info);
-       }
-
-       nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
-       NV_INFO(drm, "c:%s", info);
-
-       /* switch performance levels now if requested */
-       if (nouveau_perflvl != NULL)
-               nouveau_pm_profile_set(dev, nouveau_perflvl);
-
-       nouveau_sysfs_init(dev);
-       nouveau_hwmon_init(dev);
-#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
-       pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
-       register_acpi_notifier(&pm->acpi_nb);
-#endif
-
-       return 0;
-}
-
-void
-nouveau_pm_fini(struct drm_device *dev)
-{
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nouveau_pm_profile *profile, *tmp;
-
-       list_for_each_entry_safe(profile, tmp, &pm->profiles, head) {
-               list_del(&profile->head);
-               profile->func->destroy(profile);
-       }
-
-       if (pm->cur != &pm->boot)
-               nouveau_pm_perflvl_set(dev, &pm->boot);
-
-       nouveau_perf_fini(dev);
-       nouveau_volt_fini(dev);
-
-#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
-       unregister_acpi_notifier(&pm->acpi_nb);
-#endif
-       nouveau_hwmon_fini(dev);
-       nouveau_sysfs_fini(dev);
-
-       nouveau_drm(dev)->pm = NULL;
-       kfree(pm);
-}
-
-void
-nouveau_pm_resume(struct drm_device *dev)
-{
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nouveau_pm_level *perflvl;
-
-       if (!pm->cur || pm->cur == &pm->boot)
-               return;
-
-       perflvl = pm->cur;
-       pm->cur = &pm->boot;
-       nouveau_pm_perflvl_set(dev, perflvl);
 }
diff --git a/drivers/gpu/drm/nouveau/nouveau_hwmon.h b/drivers/gpu/drm/nouveau/nouveau_hwmon.h
new file mode 100644 (file)
index 0000000..62ccbb3
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifndef __NOUVEAU_PM_H__
+#define __NOUVEAU_PM_H__
+
+struct nouveau_hwmon {
+       struct drm_device *dev;
+       struct device *hwmon;
+};
+
+static inline struct nouveau_hwmon *
+nouveau_hwmon(struct drm_device *dev)
+{
+       return nouveau_drm(dev)->hwmon;
+}
+
+/* nouveau_hwmon.c */
+int  nouveau_hwmon_init(struct drm_device *dev);
+void nouveau_hwmon_fini(struct drm_device *dev);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_hwsq.h b/drivers/gpu/drm/nouveau/nouveau_hwsq.h
deleted file mode 100644 (file)
index 6976875..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2010 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-
-#ifndef __NOUVEAU_HWSQ_H__
-#define __NOUVEAU_HWSQ_H__
-
-struct hwsq_ucode {
-       u8 data[0x200];
-       union {
-               u8  *u08;
-               u16 *u16;
-               u32 *u32;
-       } ptr;
-       u16 len;
-
-       u32 reg;
-       u32 val;
-};
-
-static inline void
-hwsq_init(struct hwsq_ucode *hwsq)
-{
-       hwsq->ptr.u08 = hwsq->data;
-       hwsq->reg = 0xffffffff;
-       hwsq->val = 0xffffffff;
-}
-
-static inline void
-hwsq_fini(struct hwsq_ucode *hwsq)
-{
-       do {
-               *hwsq->ptr.u08++ = 0x7f;
-               hwsq->len = hwsq->ptr.u08 - hwsq->data;
-       } while (hwsq->len & 3);
-       hwsq->ptr.u08 = hwsq->data;
-}
-
-static inline void
-hwsq_usec(struct hwsq_ucode *hwsq, u8 usec)
-{
-       u32 shift = 0;
-       while (usec & ~3) {
-               usec >>= 2;
-               shift++;
-       }
-
-       *hwsq->ptr.u08++ = (shift << 2) | usec;
-}
-
-static inline void
-hwsq_setf(struct hwsq_ucode *hwsq, u8 flag, int val)
-{
-       flag += 0x80;
-       if (val >= 0)
-               flag += 0x20;
-       if (val >= 1)
-               flag += 0x20;
-       *hwsq->ptr.u08++ = flag;
-}
-
-static inline void
-hwsq_op5f(struct hwsq_ucode *hwsq, u8 v0, u8 v1)
-{
-       *hwsq->ptr.u08++ = 0x5f;
-       *hwsq->ptr.u08++ = v0;
-       *hwsq->ptr.u08++ = v1;
-}
-
-static inline void
-hwsq_wr32(struct hwsq_ucode *hwsq, u32 reg, u32 val)
-{
-       if (val != hwsq->val) {
-               if ((val & 0xffff0000) == (hwsq->val & 0xffff0000)) {
-                       *hwsq->ptr.u08++ = 0x42;
-                       *hwsq->ptr.u16++ = (val & 0x0000ffff);
-               } else {
-                       *hwsq->ptr.u08++ = 0xe2;
-                       *hwsq->ptr.u32++ = val;
-               }
-
-               hwsq->val = val;
-       }
-
-       if ((reg & 0xffff0000) == (hwsq->reg & 0xffff0000)) {
-               *hwsq->ptr.u08++ = 0x40;
-               *hwsq->ptr.u16++ = (reg & 0x0000ffff);
-       } else {
-               *hwsq->ptr.u08++ = 0xe0;
-               *hwsq->ptr.u32++ = reg;
-       }
-       hwsq->reg = reg;
-}
-
-#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
deleted file mode 100644 (file)
index 4f6a572..0000000
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Copyright (C) The Weather Channel, Inc.  2002.  All Rights Reserved.
- * Copyright 2005 Stephane Marchesin
- *
- * The Weather Channel (TM) funded Tungsten Graphics to develop the
- * initial release of the Radeon 8500 driver under the XFree86 license.
- * This notice must be preserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *    Ben Skeggs <bskeggs@redhat.com>
- *    Roy Spliet <r.spliet@student.tudelft.nl>
- */
-
-#include "nouveau_drm.h"
-#include "nouveau_pm.h"
-
-#include <subdev/fb.h>
-
-static int
-nv40_mem_timing_calc(struct drm_device *dev, u32 freq,
-                    struct nouveau_pm_tbl_entry *e, u8 len,
-                    struct nouveau_pm_memtiming *boot,
-                    struct nouveau_pm_memtiming *t)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC);
-
-       /* XXX: I don't trust the -1's and +1's... they must come
-        *      from somewhere! */
-       t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 |
-                   1 << 16 |
-                   (e->tWTR + 2 + (t->tCWL - 1)) << 8 |
-                   (e->tCL + 2 - (t->tCWL - 1));
-
-       t->reg[2] = 0x20200000 |
-                   ((t->tCWL - 1) << 24 |
-                    e->tRRD << 16 |
-                    e->tRCDWR << 8 |
-                    e->tRCDRD);
-
-       NV_DEBUG(drm, "Entry %d: 220: %08x %08x %08x\n", t->id,
-                t->reg[0], t->reg[1], t->reg[2]);
-       return 0;
-}
-
-static int
-nv50_mem_timing_calc(struct drm_device *dev, u32 freq,
-                    struct nouveau_pm_tbl_entry *e, u8 len,
-                    struct nouveau_pm_memtiming *boot,
-                    struct nouveau_pm_memtiming *t)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_fb *pfb = nouveau_fb(device);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct bit_entry P;
-       uint8_t unk18 = 1, unk20 = 0, unk21 = 0, tmp7_3;
-
-       if (bit_table(dev, 'P', &P))
-               return -EINVAL;
-
-       switch (min(len, (u8) 22)) {
-       case 22:
-               unk21 = e->tUNK_21;
-       case 21:
-               unk20 = e->tUNK_20;
-       case 20:
-               if (e->tCWL > 0)
-                       t->tCWL = e->tCWL;
-       case 19:
-               unk18 = e->tUNK_18;
-               break;
-       }
-
-       t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC);
-
-       t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 |
-                               max(unk18, (u8) 1) << 16 |
-                               (e->tWTR + 2 + (t->tCWL - 1)) << 8;
-
-       t->reg[2] = ((t->tCWL - 1) << 24 |
-                   e->tRRD << 16 |
-                   e->tRCDWR << 8 |
-                   e->tRCDRD);
-
-       t->reg[4] = e->tUNK_13 << 8  | e->tUNK_13;
-
-       t->reg[5] = (e->tRFC << 24 | max(e->tRCDRD, e->tRCDWR) << 16 | e->tRP);
-
-       t->reg[8] = boot->reg[8] & 0xffffff00;
-
-       if (P.version == 1) {
-               t->reg[1] |= (e->tCL + 2 - (t->tCWL - 1));
-
-               t->reg[3] = (0x14 + e->tCL) << 24 |
-                           0x16 << 16 |
-                           (e->tCL - 1) << 8 |
-                           (e->tCL - 1);
-
-               t->reg[4] |= boot->reg[4] & 0xffff0000;
-
-               t->reg[6] = (0x33 - t->tCWL) << 16 |
-                           t->tCWL << 8 |
-                           (0x2e + e->tCL - t->tCWL);
-
-               t->reg[7] = 0x4000202 | (e->tCL - 1) << 16;
-
-               /* XXX: P.version == 1 only has DDR2 and GDDR3? */
-               if (pfb->ram->type == NV_MEM_TYPE_DDR2) {
-                       t->reg[5] |= (e->tCL + 3) << 8;
-                       t->reg[6] |= (t->tCWL - 2) << 8;
-                       t->reg[8] |= (e->tCL - 4);
-               } else {
-                       t->reg[5] |= (e->tCL + 2) << 8;
-                       t->reg[6] |= t->tCWL << 8;
-                       t->reg[8] |= (e->tCL - 2);
-               }
-       } else {
-               t->reg[1] |= (5 + e->tCL - (t->tCWL));
-
-               /* XXX: 0xb? 0x30? */
-               t->reg[3] = (0x30 + e->tCL) << 24 |
-                           (boot->reg[3] & 0x00ff0000)|
-                           (0xb + e->tCL) << 8 |
-                           (e->tCL - 1);
-
-               t->reg[4] |= (unk20 << 24 | unk21 << 16);
-
-               /* XXX: +6? */
-               t->reg[5] |= (t->tCWL + 6) << 8;
-
-               t->reg[6] = (0x5a + e->tCL) << 16 |
-                           (6 - e->tCL + t->tCWL) << 8 |
-                           (0x50 + e->tCL - t->tCWL);
-
-               tmp7_3 = (boot->reg[7] & 0xff000000) >> 24;
-               t->reg[7] = (tmp7_3 << 24) |
-                           ((tmp7_3 - 6 + e->tCL) << 16) |
-                           0x202;
-       }
-
-       NV_DEBUG(drm, "Entry %d: 220: %08x %08x %08x %08x\n", t->id,
-                t->reg[0], t->reg[1], t->reg[2], t->reg[3]);
-       NV_DEBUG(drm, "         230: %08x %08x %08x %08x\n",
-                t->reg[4], t->reg[5], t->reg[6], t->reg[7]);
-       NV_DEBUG(drm, "         240: %08x\n", t->reg[8]);
-       return 0;
-}
-
-static int
-nvc0_mem_timing_calc(struct drm_device *dev, u32 freq,
-                    struct nouveau_pm_tbl_entry *e, u8 len,
-                    struct nouveau_pm_memtiming *boot,
-                    struct nouveau_pm_memtiming *t)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       if (e->tCWL > 0)
-               t->tCWL = e->tCWL;
-
-       t->reg[0] = (e->tRP << 24 | (e->tRAS & 0x7f) << 17 |
-                    e->tRFC << 8 | e->tRC);
-
-       t->reg[1] = (boot->reg[1] & 0xff000000) |
-                   (e->tRCDWR & 0x0f) << 20 |
-                   (e->tRCDRD & 0x0f) << 14 |
-                   (t->tCWL << 7) |
-                   (e->tCL & 0x0f);
-
-       t->reg[2] = (boot->reg[2] & 0xff0000ff) |
-                   e->tWR << 16 | e->tWTR << 8;
-
-       t->reg[3] = (e->tUNK_20 & 0x1f) << 9 |
-                   (e->tUNK_21 & 0xf) << 5 |
-                   (e->tUNK_13 & 0x1f);
-
-       t->reg[4] = (boot->reg[4] & 0xfff00fff) |
-                   (e->tRRD&0x1f) << 15;
-
-       NV_DEBUG(drm, "Entry %d: 290: %08x %08x %08x %08x\n", t->id,
-                t->reg[0], t->reg[1], t->reg[2], t->reg[3]);
-       NV_DEBUG(drm, "         2a0: %08x\n", t->reg[4]);
-       return 0;
-}
-
-/**
- * MR generation methods
- */
-
-static int
-nouveau_mem_ddr2_mr(struct drm_device *dev, u32 freq,
-                   struct nouveau_pm_tbl_entry *e, u8 len,
-                   struct nouveau_pm_memtiming *boot,
-                   struct nouveau_pm_memtiming *t)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       t->drive_strength = 0;
-       if (len < 15) {
-               t->odt = boot->odt;
-       } else {
-               t->odt = e->RAM_FT1 & 0x07;
-       }
-
-       if (e->tCL >= NV_MEM_CL_DDR2_MAX) {
-               NV_WARN(drm, "(%u) Invalid tCL: %u", t->id, e->tCL);
-               return -ERANGE;
-       }
-
-       if (e->tWR >= NV_MEM_WR_DDR2_MAX) {
-               NV_WARN(drm, "(%u) Invalid tWR: %u", t->id, e->tWR);
-               return -ERANGE;
-       }
-
-       if (t->odt > 3) {
-               NV_WARN(drm, "(%u) Invalid odt value, assuming disabled: %x",
-                       t->id, t->odt);
-               t->odt = 0;
-       }
-
-       t->mr[0] = (boot->mr[0] & 0x100f) |
-                  (e->tCL) << 4 |
-                  (e->tWR - 1) << 9;
-       t->mr[1] = (boot->mr[1] & 0x101fbb) |
-                  (t->odt & 0x1) << 2 |
-                  (t->odt & 0x2) << 5;
-
-       NV_DEBUG(drm, "(%u) MR: %08x", t->id, t->mr[0]);
-       return 0;
-}
-
-static const uint8_t nv_mem_wr_lut_ddr3[NV_MEM_WR_DDR3_MAX] = {
-       0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 0, 0};
-
-static int
-nouveau_mem_ddr3_mr(struct drm_device *dev, u32 freq,
-                   struct nouveau_pm_tbl_entry *e, u8 len,
-                   struct nouveau_pm_memtiming *boot,
-                   struct nouveau_pm_memtiming *t)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       u8 cl = e->tCL - 4;
-
-       t->drive_strength = 0;
-       if (len < 15) {
-               t->odt = boot->odt;
-       } else {
-               t->odt = e->RAM_FT1 & 0x07;
-       }
-
-       if (e->tCL >= NV_MEM_CL_DDR3_MAX || e->tCL < 4) {
-               NV_WARN(drm, "(%u) Invalid tCL: %u", t->id, e->tCL);
-               return -ERANGE;
-       }
-
-       if (e->tWR >= NV_MEM_WR_DDR3_MAX || e->tWR < 4) {
-               NV_WARN(drm, "(%u) Invalid tWR: %u", t->id, e->tWR);
-               return -ERANGE;
-       }
-
-       if (e->tCWL < 5) {
-               NV_WARN(drm, "(%u) Invalid tCWL: %u", t->id, e->tCWL);
-               return -ERANGE;
-       }
-
-       t->mr[0] = (boot->mr[0] & 0x180b) |
-                  /* CAS */
-                  (cl & 0x7) << 4 |
-                  (cl & 0x8) >> 1 |
-                  (nv_mem_wr_lut_ddr3[e->tWR]) << 9;
-       t->mr[1] = (boot->mr[1] & 0x101dbb) |
-                  (t->odt & 0x1) << 2 |
-                  (t->odt & 0x2) << 5 |
-                  (t->odt & 0x4) << 7;
-       t->mr[2] = (boot->mr[2] & 0x20ffb7) | (e->tCWL - 5) << 3;
-
-       NV_DEBUG(drm, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[2]);
-       return 0;
-}
-
-static const uint8_t nv_mem_cl_lut_gddr3[NV_MEM_CL_GDDR3_MAX] = {
-       0, 0, 0, 0, 4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11};
-static const uint8_t nv_mem_wr_lut_gddr3[NV_MEM_WR_GDDR3_MAX] = {
-       0, 0, 0, 0, 0, 2, 3, 8, 9, 10, 11, 0, 0, 1, 1, 0, 3};
-
-static int
-nouveau_mem_gddr3_mr(struct drm_device *dev, u32 freq,
-                    struct nouveau_pm_tbl_entry *e, u8 len,
-                    struct nouveau_pm_memtiming *boot,
-                    struct nouveau_pm_memtiming *t)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       if (len < 15) {
-               t->drive_strength = boot->drive_strength;
-               t->odt = boot->odt;
-       } else {
-               t->drive_strength = (e->RAM_FT1 & 0x30) >> 4;
-               t->odt = e->RAM_FT1 & 0x07;
-       }
-
-       if (e->tCL >= NV_MEM_CL_GDDR3_MAX) {
-               NV_WARN(drm, "(%u) Invalid tCL: %u", t->id, e->tCL);
-               return -ERANGE;
-       }
-
-       if (e->tWR >= NV_MEM_WR_GDDR3_MAX) {
-               NV_WARN(drm, "(%u) Invalid tWR: %u", t->id, e->tWR);
-               return -ERANGE;
-       }
-
-       if (t->odt > 3) {
-               NV_WARN(drm, "(%u) Invalid odt value, assuming autocal: %x",
-                       t->id, t->odt);
-               t->odt = 0;
-       }
-
-       t->mr[0] = (boot->mr[0] & 0xe0b) |
-                  /* CAS */
-                  ((nv_mem_cl_lut_gddr3[e->tCL] & 0x7) << 4) |
-                  ((nv_mem_cl_lut_gddr3[e->tCL] & 0x8) >> 2);
-       t->mr[1] = (boot->mr[1] & 0x100f40) | t->drive_strength |
-                  (t->odt << 2) |
-                  (nv_mem_wr_lut_gddr3[e->tWR] & 0xf) << 4;
-       t->mr[2] = boot->mr[2];
-
-       NV_DEBUG(drm, "(%u) MR: %08x %08x %08x", t->id,
-                     t->mr[0], t->mr[1], t->mr[2]);
-       return 0;
-}
-
-static int
-nouveau_mem_gddr5_mr(struct drm_device *dev, u32 freq,
-                    struct nouveau_pm_tbl_entry *e, u8 len,
-                    struct nouveau_pm_memtiming *boot,
-                    struct nouveau_pm_memtiming *t)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       if (len < 15) {
-               t->drive_strength = boot->drive_strength;
-               t->odt = boot->odt;
-       } else {
-               t->drive_strength = (e->RAM_FT1 & 0x30) >> 4;
-               t->odt = e->RAM_FT1 & 0x03;
-       }
-
-       if (e->tCL >= NV_MEM_CL_GDDR5_MAX) {
-               NV_WARN(drm, "(%u) Invalid tCL: %u", t->id, e->tCL);
-               return -ERANGE;
-       }
-
-       if (e->tWR >= NV_MEM_WR_GDDR5_MAX) {
-               NV_WARN(drm, "(%u) Invalid tWR: %u", t->id, e->tWR);
-               return -ERANGE;
-       }
-
-       if (t->odt > 3) {
-               NV_WARN(drm, "(%u) Invalid odt value, assuming autocal: %x",
-                       t->id, t->odt);
-               t->odt = 0;
-       }
-
-       t->mr[0] = (boot->mr[0] & 0x007) |
-                  ((e->tCL - 5) << 3) |
-                  ((e->tWR - 4) << 8);
-       t->mr[1] = (boot->mr[1] & 0x1007f0) |
-                  t->drive_strength |
-                  (t->odt << 2);
-
-       NV_DEBUG(drm, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[1]);
-       return 0;
-}
-
-int
-nouveau_mem_timing_calc(struct drm_device *dev, u32 freq,
-                       struct nouveau_pm_memtiming *t)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_fb *pfb = nouveau_fb(device);
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nouveau_pm_memtiming *boot = &pm->boot.timing;
-       struct nouveau_pm_tbl_entry *e;
-       u8 ver, len, *ptr, *ramcfg;
-       int ret;
-
-       ptr = nouveau_perf_timing(dev, freq, &ver, &len);
-       if (!ptr || ptr[0] == 0x00) {
-               *t = *boot;
-               return 0;
-       }
-       e = (struct nouveau_pm_tbl_entry *)ptr;
-
-       t->tCWL = boot->tCWL;
-
-       switch (device->card_type) {
-       case NV_40:
-               ret = nv40_mem_timing_calc(dev, freq, e, len, boot, t);
-               break;
-       case NV_50:
-               ret = nv50_mem_timing_calc(dev, freq, e, len, boot, t);
-               break;
-       case NV_C0:
-       case NV_D0:
-               ret = nvc0_mem_timing_calc(dev, freq, e, len, boot, t);
-               break;
-       default:
-               ret = -ENODEV;
-               break;
-       }
-
-       switch (pfb->ram->type * !ret) {
-       case NV_MEM_TYPE_GDDR3:
-               ret = nouveau_mem_gddr3_mr(dev, freq, e, len, boot, t);
-               break;
-       case NV_MEM_TYPE_GDDR5:
-               ret = nouveau_mem_gddr5_mr(dev, freq, e, len, boot, t);
-               break;
-       case NV_MEM_TYPE_DDR2:
-               ret = nouveau_mem_ddr2_mr(dev, freq, e, len, boot, t);
-               break;
-       case NV_MEM_TYPE_DDR3:
-               ret = nouveau_mem_ddr3_mr(dev, freq, e, len, boot, t);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       ramcfg = nouveau_perf_ramcfg(dev, freq, &ver, &len);
-       if (ramcfg) {
-               int dll_off;
-
-               if (ver == 0x00)
-                       dll_off = !!(ramcfg[3] & 0x04);
-               else
-                       dll_off = !!(ramcfg[2] & 0x40);
-
-               switch (pfb->ram->type) {
-               case NV_MEM_TYPE_GDDR3:
-                       t->mr[1] &= ~0x00000040;
-                       t->mr[1] |=  0x00000040 * dll_off;
-                       break;
-               default:
-                       t->mr[1] &= ~0x00000001;
-                       t->mr[1] |=  0x00000001 * dll_off;
-                       break;
-               }
-       }
-
-       return ret;
-}
-
-void
-nouveau_mem_timing_read(struct drm_device *dev, struct nouveau_pm_memtiming *t)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_fb *pfb = nouveau_fb(device);
-       u32 timing_base, timing_regs, mr_base;
-       int i;
-
-       if (device->card_type >= 0xC0) {
-               timing_base = 0x10f290;
-               mr_base = 0x10f300;
-       } else {
-               timing_base = 0x100220;
-               mr_base = 0x1002c0;
-       }
-
-       t->id = -1;
-
-       switch (device->card_type) {
-       case NV_50:
-               timing_regs = 9;
-               break;
-       case NV_C0:
-       case NV_D0:
-               timing_regs = 5;
-               break;
-       case NV_30:
-       case NV_40:
-               timing_regs = 3;
-               break;
-       default:
-               timing_regs = 0;
-               return;
-       }
-       for(i = 0; i < timing_regs; i++)
-               t->reg[i] = nv_rd32(device, timing_base + (0x04 * i));
-
-       t->tCWL = 0;
-       if (device->card_type < NV_C0) {
-               t->tCWL = ((nv_rd32(device, 0x100228) & 0x0f000000) >> 24) + 1;
-       } else if (device->card_type <= NV_D0) {
-               t->tCWL = ((nv_rd32(device, 0x10f294) & 0x00000f80) >> 7);
-       }
-
-       t->mr[0] = nv_rd32(device, mr_base);
-       t->mr[1] = nv_rd32(device, mr_base + 0x04);
-       t->mr[2] = nv_rd32(device, mr_base + 0x20);
-       t->mr[3] = nv_rd32(device, mr_base + 0x24);
-
-       t->odt = 0;
-       t->drive_strength = 0;
-
-       switch (pfb->ram->type) {
-       case NV_MEM_TYPE_DDR3:
-               t->odt |= (t->mr[1] & 0x200) >> 7;
-       case NV_MEM_TYPE_DDR2:
-               t->odt |= (t->mr[1] & 0x04) >> 2 |
-                         (t->mr[1] & 0x40) >> 5;
-               break;
-       case NV_MEM_TYPE_GDDR3:
-       case NV_MEM_TYPE_GDDR5:
-               t->drive_strength = t->mr[1] & 0x03;
-               t->odt = (t->mr[1] & 0x0c) >> 2;
-               break;
-       default:
-               break;
-       }
-}
-
-int
-nouveau_mem_exec(struct nouveau_mem_exec_func *exec,
-                struct nouveau_pm_level *perflvl)
-{
-       struct nouveau_drm *drm = nouveau_drm(exec->dev);
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nouveau_fb *pfb = nouveau_fb(device);
-       struct nouveau_pm_memtiming *info = &perflvl->timing;
-       u32 tMRD = 1000, tCKSRE = 0, tCKSRX = 0, tXS = 0, tDLLK = 0;
-       u32 mr[3] = { info->mr[0], info->mr[1], info->mr[2] };
-       u32 mr1_dlloff;
-
-       switch (pfb->ram->type) {
-       case NV_MEM_TYPE_DDR2:
-               tDLLK = 2000;
-               mr1_dlloff = 0x00000001;
-               break;
-       case NV_MEM_TYPE_DDR3:
-               tDLLK = 12000;
-               tCKSRE = 2000;
-               tXS = 1000;
-               mr1_dlloff = 0x00000001;
-               break;
-       case NV_MEM_TYPE_GDDR3:
-               tDLLK = 40000;
-               mr1_dlloff = 0x00000040;
-               break;
-       default:
-               NV_ERROR(drm, "cannot reclock unsupported memtype\n");
-               return -ENODEV;
-       }
-
-       /* fetch current MRs */
-       switch (pfb->ram->type) {
-       case NV_MEM_TYPE_GDDR3:
-       case NV_MEM_TYPE_DDR3:
-               mr[2] = exec->mrg(exec, 2);
-       default:
-               mr[1] = exec->mrg(exec, 1);
-               mr[0] = exec->mrg(exec, 0);
-               break;
-       }
-
-       /* DLL 'on' -> DLL 'off' mode, disable before entering self-refresh  */
-       if (!(mr[1] & mr1_dlloff) && (info->mr[1] & mr1_dlloff)) {
-               exec->precharge(exec);
-               exec->mrs (exec, 1, mr[1] | mr1_dlloff);
-               exec->wait(exec, tMRD);
-       }
-
-       /* enter self-refresh mode */
-       exec->precharge(exec);
-       exec->refresh(exec);
-       exec->refresh(exec);
-       exec->refresh_auto(exec, false);
-       exec->refresh_self(exec, true);
-       exec->wait(exec, tCKSRE);
-
-       /* modify input clock frequency */
-       exec->clock_set(exec);
-
-       /* exit self-refresh mode */
-       exec->wait(exec, tCKSRX);
-       exec->precharge(exec);
-       exec->refresh_self(exec, false);
-       exec->refresh_auto(exec, true);
-       exec->wait(exec, tXS);
-       exec->wait(exec, tXS);
-
-       /* update MRs */
-       if (mr[2] != info->mr[2]) {
-               exec->mrs (exec, 2, info->mr[2]);
-               exec->wait(exec, tMRD);
-       }
-
-       if (mr[1] != info->mr[1]) {
-               /* need to keep DLL off until later, at least on GDDR3 */
-               exec->mrs (exec, 1, info->mr[1] | (mr[1] & mr1_dlloff));
-               exec->wait(exec, tMRD);
-       }
-
-       if (mr[0] != info->mr[0]) {
-               exec->mrs (exec, 0, info->mr[0]);
-               exec->wait(exec, tMRD);
-       }
-
-       /* update PFB timing registers */
-       exec->timing_set(exec);
-
-       /* DLL (enable + ) reset */
-       if (!(info->mr[1] & mr1_dlloff)) {
-               if (mr[1] & mr1_dlloff) {
-                       exec->mrs (exec, 1, info->mr[1]);
-                       exec->wait(exec, tMRD);
-               }
-               exec->mrs (exec, 0, info->mr[0] | 0x00000100);
-               exec->wait(exec, tMRD);
-               exec->mrs (exec, 0, info->mr[0] | 0x00000000);
-               exec->wait(exec, tMRD);
-               exec->wait(exec, tDLLK);
-               if (pfb->ram->type == NV_MEM_TYPE_GDDR3)
-                       exec->precharge(exec);
-       }
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c
deleted file mode 100644 (file)
index 4fe883c..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Copyright 2010 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-
-#include <drm/drmP.h>
-
-#include "nouveau_drm.h"
-#include "nouveau_reg.h"
-#include "nouveau_pm.h"
-
-static u8 *
-nouveau_perf_table(struct drm_device *dev, u8 *ver)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nvbios *bios = &drm->vbios;
-       struct bit_entry P;
-
-       if (!bit_table(dev, 'P', &P) && P.version && P.version <= 2) {
-               u8 *perf = ROMPTR(dev, P.data[0]);
-               if (perf) {
-                       *ver = perf[0];
-                       return perf;
-               }
-       }
-
-       if (bios->type == NVBIOS_BMP) {
-               if (bios->data[bios->offset + 6] >= 0x25) {
-                       u8 *perf = ROMPTR(dev, bios->data[bios->offset + 0x94]);
-                       if (perf) {
-                               *ver = perf[1];
-                               return perf;
-                       }
-               }
-       }
-
-       return NULL;
-}
-
-static u8 *
-nouveau_perf_entry(struct drm_device *dev, int idx,
-                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
-{
-       u8 *perf = nouveau_perf_table(dev, ver);
-       if (perf) {
-               if (*ver >= 0x12 && *ver < 0x20 && idx < perf[2]) {
-                       *hdr = perf[3];
-                       *cnt = 0;
-                       *len = 0;
-                       return perf + perf[0] + idx * perf[3];
-               } else
-               if (*ver >= 0x20 && *ver < 0x40 && idx < perf[2]) {
-                       *hdr = perf[3];
-                       *cnt = perf[4];
-                       *len = perf[5];
-                       return perf + perf[1] + idx * (*hdr + (*cnt * *len));
-               } else
-               if (*ver >= 0x40 && *ver < 0x41 && idx < perf[5]) {
-                       *hdr = perf[2];
-                       *cnt = perf[4];
-                       *len = perf[3];
-                       return perf + perf[1] + idx * (*hdr + (*cnt * *len));
-               }
-       }
-       return NULL;
-}
-
-u8 *
-nouveau_perf_rammap(struct drm_device *dev, u32 freq,
-                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct bit_entry P;
-       u8 *perf, i = 0;
-
-       if (!bit_table(dev, 'P', &P) && P.version == 2) {
-               u8 *rammap = ROMPTR(dev, P.data[4]);
-               if (rammap) {
-                       u8 *ramcfg = rammap + rammap[1];
-
-                       *ver = rammap[0];
-                       *hdr = rammap[2];
-                       *cnt = rammap[4];
-                       *len = rammap[3];
-
-                       freq /= 1000;
-                       for (i = 0; i < rammap[5]; i++) {
-                               if (freq >= ROM16(ramcfg[0]) &&
-                                   freq <= ROM16(ramcfg[2]))
-                                       return ramcfg;
-
-                               ramcfg += *hdr + (*cnt * *len);
-                       }
-               }
-
-               return NULL;
-       }
-
-       if (nv_device(drm->device)->chipset == 0x49 ||
-           nv_device(drm->device)->chipset == 0x4b)
-               freq /= 2;
-
-       while ((perf = nouveau_perf_entry(dev, i++, ver, hdr, cnt, len))) {
-               if (*ver >= 0x20 && *ver < 0x25) {
-                       if (perf[0] != 0xff && freq <= ROM16(perf[11]) * 1000)
-                               break;
-               } else
-               if (*ver >= 0x25 && *ver < 0x40) {
-                       if (perf[0] != 0xff && freq <= ROM16(perf[12]) * 1000)
-                               break;
-               }
-       }
-
-       if (perf) {
-               u8 *ramcfg = perf + *hdr;
-               *ver = 0x00;
-               *hdr = 0;
-               return ramcfg;
-       }
-
-       return NULL;
-}
-
-u8 *
-nouveau_perf_ramcfg(struct drm_device *dev, u32 freq, u8 *ver, u8 *len)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nvbios *bios = &drm->vbios;
-       u8 strap, hdr, cnt;
-       u8 *rammap;
-
-       strap = (nv_rd32(device, 0x101000) & 0x0000003c) >> 2;
-       if (bios->ram_restrict_tbl_ptr)
-               strap = bios->data[bios->ram_restrict_tbl_ptr + strap];
-
-       rammap = nouveau_perf_rammap(dev, freq, ver, &hdr, &cnt, len);
-       if (rammap && strap < cnt)
-               return rammap + hdr + (strap * *len);
-
-       return NULL;
-}
-
-u8 *
-nouveau_perf_timing(struct drm_device *dev, u32 freq, u8 *ver, u8 *len)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nvbios *bios = &drm->vbios;
-       struct bit_entry P;
-       u8 *perf, *timing = NULL;
-       u8 i = 0, hdr, cnt;
-
-       if (bios->type == NVBIOS_BMP) {
-               while ((perf = nouveau_perf_entry(dev, i++, ver, &hdr, &cnt,
-                                                 len)) && *ver == 0x15) {
-                       if (freq <= ROM32(perf[5]) * 20) {
-                               *ver = 0x00;
-                               *len = 14;
-                               return perf + 41;
-                       }
-               }
-               return NULL;
-       }
-
-       if (!bit_table(dev, 'P', &P)) {
-               if (P.version == 1)
-                       timing = ROMPTR(dev, P.data[4]);
-               else
-               if (P.version == 2)
-                       timing = ROMPTR(dev, P.data[8]);
-       }
-
-       if (timing && timing[0] == 0x10) {
-               u8 *ramcfg = nouveau_perf_ramcfg(dev, freq, ver, len);
-               if (ramcfg && ramcfg[1] < timing[2]) {
-                       *ver = timing[0];
-                       *len = timing[3];
-                       return timing + timing[1] + (ramcfg[1] * timing[3]);
-               }
-       }
-
-       return NULL;
-}
-
-static void
-legacy_perf_init(struct drm_device *dev)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nvbios *bios = &drm->vbios;
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       char *perf, *entry, *bmp = &bios->data[bios->offset];
-       int headerlen, use_straps;
-
-       if (bmp[5] < 0x5 || bmp[6] < 0x14) {
-               NV_DEBUG(drm, "BMP version too old for perf\n");
-               return;
-       }
-
-       perf = ROMPTR(dev, bmp[0x73]);
-       if (!perf) {
-               NV_DEBUG(drm, "No memclock table pointer found.\n");
-               return;
-       }
-
-       switch (perf[0]) {
-       case 0x12:
-       case 0x14:
-       case 0x18:
-               use_straps = 0;
-               headerlen = 1;
-               break;
-       case 0x01:
-               use_straps = perf[1] & 1;
-               headerlen = (use_straps ? 8 : 2);
-               break;
-       default:
-               NV_WARN(drm, "Unknown memclock table version %x.\n", perf[0]);
-               return;
-       }
-
-       entry = perf + headerlen;
-       if (use_straps)
-               entry += (nv_rd32(device, NV_PEXTDEV_BOOT_0) & 0x3c) >> 1;
-
-       sprintf(pm->perflvl[0].name, "performance_level_0");
-       pm->perflvl[0].memory = ROM16(entry[0]) * 20;
-       pm->nr_perflvl = 1;
-}
-
-static void
-nouveau_perf_voltage(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct bit_entry P;
-       u8 *vmap;
-       int id;
-
-       id = perflvl->volt_min;
-       perflvl->volt_min = 0;
-
-       /* boards using voltage table version <0x40 store the voltage
-        * level directly in the perflvl entry as a multiple of 10mV
-        */
-       if (drm->pm->voltage.version < 0x40) {
-               perflvl->volt_min = id * 10000;
-               perflvl->volt_max = perflvl->volt_min;
-               return;
-       }
-
-       /* on newer ones, the perflvl stores an index into yet another
-        * vbios table containing a min/max voltage value for the perflvl
-        */
-       if (bit_table(dev, 'P', &P) || P.version != 2 || P.length < 34) {
-               NV_DEBUG(drm, "where's our volt map table ptr? %d %d\n",
-                        P.version, P.length);
-               return;
-       }
-
-       vmap = ROMPTR(dev, P.data[32]);
-       if (!vmap) {
-               NV_DEBUG(drm, "volt map table pointer invalid\n");
-               return;
-       }
-
-       if (id < vmap[3]) {
-               vmap += vmap[1] + (vmap[2] * id);
-               perflvl->volt_min = ROM32(vmap[0]);
-               perflvl->volt_max = ROM32(vmap[4]);
-       }
-}
-
-void
-nouveau_perf_init(struct drm_device *dev)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nvbios *bios = &drm->vbios;
-       u8 *perf, ver, hdr, cnt, len;
-       int ret, vid, i = -1;
-
-       if (bios->type == NVBIOS_BMP && bios->data[bios->offset + 6] < 0x25) {
-               legacy_perf_init(dev);
-               return;
-       }
-
-       perf = nouveau_perf_table(dev, &ver);
-
-       while ((perf = nouveau_perf_entry(dev, ++i, &ver, &hdr, &cnt, &len))) {
-               struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
-
-               if (perf[0] == 0xff)
-                       continue;
-
-               switch (ver) {
-               case 0x12:
-               case 0x13:
-               case 0x15:
-                       perflvl->fanspeed = perf[55];
-                       if (hdr > 56)
-                               perflvl->volt_min = perf[56];
-                       perflvl->core = ROM32(perf[1]) * 10;
-                       perflvl->memory = ROM32(perf[5]) * 20;
-                       break;
-               case 0x21:
-               case 0x23:
-               case 0x24:
-                       perflvl->fanspeed = perf[4];
-                       perflvl->volt_min = perf[5];
-                       perflvl->shader = ROM16(perf[6]) * 1000;
-                       perflvl->core = perflvl->shader;
-                       perflvl->core += (signed char)perf[8] * 1000;
-                       if (nv_device(drm->device)->chipset == 0x49 ||
-                           nv_device(drm->device)->chipset == 0x4b)
-                               perflvl->memory = ROM16(perf[11]) * 1000;
-                       else
-                               perflvl->memory = ROM16(perf[11]) * 2000;
-                       break;
-               case 0x25:
-                       perflvl->fanspeed = perf[4];
-                       perflvl->volt_min = perf[5];
-                       perflvl->core = ROM16(perf[6]) * 1000;
-                       perflvl->shader = ROM16(perf[10]) * 1000;
-                       perflvl->memory = ROM16(perf[12]) * 1000;
-                       break;
-               case 0x30:
-                       perflvl->memscript = ROM16(perf[2]);
-               case 0x35:
-                       perflvl->fanspeed = perf[6];
-                       perflvl->volt_min = perf[7];
-                       perflvl->core = ROM16(perf[8]) * 1000;
-                       perflvl->shader = ROM16(perf[10]) * 1000;
-                       perflvl->memory = ROM16(perf[12]) * 1000;
-                       perflvl->vdec = ROM16(perf[16]) * 1000;
-                       perflvl->dom6 = ROM16(perf[20]) * 1000;
-                       break;
-               case 0x40:
-#define subent(n) ((ROM16(perf[hdr + (n) * len]) & 0xfff) * 1000)
-                       perflvl->fanspeed = 0; /*XXX*/
-                       perflvl->volt_min = perf[2];
-                       if (nv_device(drm->device)->card_type == NV_50) {
-                               perflvl->core   = subent(0);
-                               perflvl->shader = subent(1);
-                               perflvl->memory = subent(2);
-                               perflvl->vdec   = subent(3);
-                               perflvl->unka0  = subent(4);
-                       } else {
-                               perflvl->hub06  = subent(0);
-                               perflvl->hub01  = subent(1);
-                               perflvl->copy   = subent(2);
-                               perflvl->shader = subent(3);
-                               perflvl->rop    = subent(4);
-                               perflvl->memory = subent(5);
-                               perflvl->vdec   = subent(6);
-                               perflvl->daemon = subent(10);
-                               perflvl->hub07  = subent(11);
-                               perflvl->core   = perflvl->shader / 2;
-                       }
-                       break;
-               }
-
-               /* make sure vid is valid */
-               nouveau_perf_voltage(dev, perflvl);
-               if (pm->voltage.supported && perflvl->volt_min) {
-                       vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min);
-                       if (vid < 0) {
-                               NV_DEBUG(drm, "perflvl %d, bad vid\n", i);
-                               continue;
-                       }
-               }
-
-               /* get the corresponding memory timings */
-               ret = nouveau_mem_timing_calc(dev, perflvl->memory,
-                                                 &perflvl->timing);
-               if (ret) {
-                       NV_DEBUG(drm, "perflvl %d, bad timing: %d\n", i, ret);
-                       continue;
-               }
-
-               snprintf(perflvl->name, sizeof(perflvl->name),
-                        "performance_level_%d", i);
-               perflvl->id = i;
-
-               snprintf(perflvl->profile.name, sizeof(perflvl->profile.name),
-                        "%d", perflvl->id);
-               perflvl->profile.func = &nouveau_pm_static_profile_func;
-               list_add_tail(&perflvl->profile.head, &pm->profiles);
-
-
-               pm->nr_perflvl++;
-       }
-}
-
-void
-nouveau_perf_fini(struct drm_device *dev)
-{
-}
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h
deleted file mode 100644 (file)
index 73b789c..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright 2010 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-
-#ifndef __NOUVEAU_PM_H__
-#define __NOUVEAU_PM_H__
-
-#include <subdev/bios/pll.h>
-#include <subdev/clock.h>
-
-struct nouveau_pm_voltage_level {
-       u32 voltage; /* microvolts */
-       u8  vid;
-};
-
-struct nouveau_pm_voltage {
-       bool supported;
-       u8 version;
-       u8 vid_mask;
-
-       struct nouveau_pm_voltage_level *level;
-       int nr_level;
-};
-
-/* Exclusive upper limits */
-#define NV_MEM_CL_DDR2_MAX 8
-#define NV_MEM_WR_DDR2_MAX 9
-#define NV_MEM_CL_DDR3_MAX 17
-#define NV_MEM_WR_DDR3_MAX 17
-#define NV_MEM_CL_GDDR3_MAX 16
-#define NV_MEM_WR_GDDR3_MAX 18
-#define NV_MEM_CL_GDDR5_MAX 21
-#define NV_MEM_WR_GDDR5_MAX 20
-
-struct nouveau_pm_memtiming {
-       int id;
-
-       u32 reg[9];
-       u32 mr[4];
-
-       u8 tCWL;
-
-       u8 odt;
-       u8 drive_strength;
-};
-
-struct nouveau_pm_tbl_header {
-       u8 version;
-       u8 header_len;
-       u8 entry_cnt;
-       u8 entry_len;
-};
-
-struct nouveau_pm_tbl_entry {
-       u8 tWR;
-       u8 tWTR;
-       u8 tCL;
-       u8 tRC;
-       u8 empty_4;
-       u8 tRFC;        /* Byte 5 */
-       u8 empty_6;
-       u8 tRAS;        /* Byte 7 */
-       u8 empty_8;
-       u8 tRP;         /* Byte 9 */
-       u8 tRCDRD;
-       u8 tRCDWR;
-       u8 tRRD;
-       u8 tUNK_13;
-       u8 RAM_FT1;             /* 14, a bitmask of random RAM features */
-       u8 empty_15;
-       u8 tUNK_16;
-       u8 empty_17;
-       u8 tUNK_18;
-       u8 tCWL;
-       u8 tUNK_20, tUNK_21;
-};
-
-struct nouveau_pm_profile;
-struct nouveau_pm_profile_func {
-       void (*destroy)(struct nouveau_pm_profile *);
-       void (*init)(struct nouveau_pm_profile *);
-       void (*fini)(struct nouveau_pm_profile *);
-       struct nouveau_pm_level *(*select)(struct nouveau_pm_profile *);
-};
-
-struct nouveau_pm_profile {
-       const struct nouveau_pm_profile_func *func;
-       struct list_head head;
-       char name[8];
-};
-
-#define NOUVEAU_PM_MAX_LEVEL 8
-struct nouveau_pm_level {
-       struct nouveau_pm_profile profile;
-       struct device_attribute dev_attr;
-       char name[32];
-       int id;
-
-       struct nouveau_pm_memtiming timing;
-       u32 memory;
-       u16 memscript;
-
-       u32 core;
-       u32 shader;
-       u32 rop;
-       u32 copy;
-       u32 daemon;
-       u32 vdec;
-       u32 dom6;
-       u32 unka0;      /* nva3:nvc0 */
-       u32 hub01;      /* nvc0- */
-       u32 hub06;      /* nvc0- */
-       u32 hub07;      /* nvc0- */
-
-       u32 volt_min; /* microvolts */
-       u32 volt_max;
-       u8  fanspeed;
-};
-
-struct nouveau_pm_temp_sensor_constants {
-       u16 offset_constant;
-       s16 offset_mult;
-       s16 offset_div;
-       s16 slope_mult;
-       s16 slope_div;
-};
-
-struct nouveau_pm_threshold_temp {
-       s16 critical;
-       s16 down_clock;
-};
-
-struct nouveau_pm {
-       struct drm_device *dev;
-
-       struct nouveau_pm_voltage voltage;
-       struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL];
-       int nr_perflvl;
-       struct nouveau_pm_temp_sensor_constants sensor_constants;
-       struct nouveau_pm_threshold_temp threshold_temp;
-
-       struct nouveau_pm_profile *profile_ac;
-       struct nouveau_pm_profile *profile_dc;
-       struct nouveau_pm_profile *profile;
-       struct list_head profiles;
-
-       struct nouveau_pm_level boot;
-       struct nouveau_pm_level *cur;
-
-       struct device *hwmon;
-       struct notifier_block acpi_nb;
-
-       int  (*clocks_get)(struct drm_device *, struct nouveau_pm_level *);
-       void *(*clocks_pre)(struct drm_device *, struct nouveau_pm_level *);
-       int (*clocks_set)(struct drm_device *, void *);
-
-       int (*voltage_get)(struct drm_device *);
-       int (*voltage_set)(struct drm_device *, int voltage);
-};
-
-static inline struct nouveau_pm *
-nouveau_pm(struct drm_device *dev)
-{
-       return nouveau_drm(dev)->pm;
-}
-
-struct nouveau_mem_exec_func {
-       struct drm_device *dev;
-       void (*precharge)(struct nouveau_mem_exec_func *);
-       void (*refresh)(struct nouveau_mem_exec_func *);
-       void (*refresh_auto)(struct nouveau_mem_exec_func *, bool);
-       void (*refresh_self)(struct nouveau_mem_exec_func *, bool);
-       void (*wait)(struct nouveau_mem_exec_func *, u32 nsec);
-       u32  (*mrg)(struct nouveau_mem_exec_func *, int mr);
-       void (*mrs)(struct nouveau_mem_exec_func *, int mr, u32 data);
-       void (*clock_set)(struct nouveau_mem_exec_func *);
-       void (*timing_set)(struct nouveau_mem_exec_func *);
-       void *priv;
-};
-
-/* nouveau_mem.c */
-int  nouveau_mem_exec(struct nouveau_mem_exec_func *,
-                     struct nouveau_pm_level *);
-
-/* nouveau_pm.c */
-int  nouveau_pm_init(struct drm_device *dev);
-void nouveau_pm_fini(struct drm_device *dev);
-void nouveau_pm_resume(struct drm_device *dev);
-extern const struct nouveau_pm_profile_func nouveau_pm_static_profile_func;
-void nouveau_pm_trigger(struct drm_device *dev);
-
-/* nouveau_volt.c */
-void nouveau_volt_init(struct drm_device *);
-void nouveau_volt_fini(struct drm_device *);
-int  nouveau_volt_vid_lookup(struct drm_device *, int voltage);
-int  nouveau_volt_lvl_lookup(struct drm_device *, int vid);
-int  nouveau_voltage_gpio_get(struct drm_device *);
-int  nouveau_voltage_gpio_set(struct drm_device *, int voltage);
-
-/* nouveau_perf.c */
-void nouveau_perf_init(struct drm_device *);
-void nouveau_perf_fini(struct drm_device *);
-u8 *nouveau_perf_rammap(struct drm_device *, u32 freq, u8 *ver,
-                       u8 *hdr, u8 *cnt, u8 *len);
-u8 *nouveau_perf_ramcfg(struct drm_device *, u32 freq, u8 *ver, u8 *len);
-u8 *nouveau_perf_timing(struct drm_device *, u32 freq, u8 *ver, u8 *len);
-
-/* nouveau_mem.c */
-void nouveau_mem_timing_init(struct drm_device *);
-void nouveau_mem_timing_fini(struct drm_device *);
-
-/* nv04_pm.c */
-int nv04_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
-void *nv04_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
-int nv04_pm_clocks_set(struct drm_device *, void *);
-
-/* nv40_pm.c */
-int nv40_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
-void *nv40_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
-int nv40_pm_clocks_set(struct drm_device *, void *);
-int nv40_pm_pwm_get(struct drm_device *, int, u32 *, u32 *);
-int nv40_pm_pwm_set(struct drm_device *, int, u32, u32);
-
-/* nv50_pm.c */
-int nv50_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
-void *nv50_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
-int nv50_pm_clocks_set(struct drm_device *, void *);
-int nv50_pm_pwm_get(struct drm_device *, int, u32 *, u32 *);
-int nv50_pm_pwm_set(struct drm_device *, int, u32, u32);
-
-/* nva3_pm.c */
-int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
-void *nva3_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
-int nva3_pm_clocks_set(struct drm_device *, void *);
-
-/* nvc0_pm.c */
-int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
-void *nvc0_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
-int nvc0_pm_clocks_set(struct drm_device *, void *);
-
-/* nouveau_mem.c */
-int  nouveau_mem_timing_calc(struct drm_device *, u32 freq,
-                            struct nouveau_pm_memtiming *);
-void nouveau_mem_timing_read(struct drm_device *,
-                            struct nouveau_pm_memtiming *);
-
-static inline int
-nva3_calc_pll(struct drm_device *dev, struct nvbios_pll *pll, u32 freq,
-             int *N, int *fN, int *M, int *P)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_clock *clk = nouveau_clock(device);
-       struct nouveau_pll_vals pv;
-       int ret;
-
-       ret = clk->pll_calc(clk, pll, freq, &pv);
-       *N = pv.N1;
-       *M = pv.M1;
-       *P = pv.log2P;
-       return ret;
-}
-
-#endif
index e90468d..51a2cb1 100644 (file)
@@ -71,14 +71,16 @@ struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev,
                return ERR_PTR(ret);
 
        nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_GART;
-       nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size);
-       if (!nvbo->gem) {
+
+       /* Initialize the embedded gem-object. We return a single gem-reference
+        * to the caller, instead of a normal nouveau_bo ttm reference. */
+       ret = drm_gem_object_init(dev, &nvbo->gem, nvbo->bo.mem.size);
+       if (ret) {
                nouveau_bo_ref(NULL, &nvbo);
                return ERR_PTR(-ENOMEM);
        }
 
-       nvbo->gem->driver_private = nvbo;
-       return nvbo->gem;
+       return &nvbo->gem;
 }
 
 int nouveau_gem_prime_pin(struct drm_gem_object *obj)
diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.c b/drivers/gpu/drm/nouveau/nouveau_sysfs.c
new file mode 100644 (file)
index 0000000..89201a1
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "nouveau_sysfs.h"
+
+#include <core/object.h>
+#include <core/class.h>
+
+static inline struct drm_device *
+drm_device(struct device *d)
+{
+       return pci_get_drvdata(to_pci_dev(d));
+}
+
+#define snappendf(p,r,f,a...) do {                                             \
+       snprintf(p, r, f, ##a);                                                \
+       r -= strlen(p);                                                        \
+       p += strlen(p);                                                        \
+} while(0)
+
+static ssize_t
+nouveau_sysfs_pstate_get(struct device *d, struct device_attribute *a, char *b)
+{
+       struct nouveau_sysfs *sysfs = nouveau_sysfs(drm_device(d));
+       struct nv_control_pstate_info info;
+       size_t cnt = PAGE_SIZE;
+       char *buf = b;
+       int ret, i;
+
+       ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_INFO, &info, sizeof(info));
+       if (ret)
+               return ret;
+
+       for (i = 0; i < info.count + 1; i++) {
+               const s32 state = i < info.count ? i :
+                       NV_CONTROL_PSTATE_ATTR_STATE_CURRENT;
+               struct nv_control_pstate_attr attr = {
+                       .state = state,
+                       .index = 0,
+               };
+
+               ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_ATTR,
+                            &attr, sizeof(attr));
+               if (ret)
+                       return ret;
+
+               if (i < info.count)
+                       snappendf(buf, cnt, "%02x:", attr.state);
+               else
+                       snappendf(buf, cnt, "--:");
+
+               attr.index = 0;
+               do {
+                       attr.state = state;
+                       ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_ATTR,
+                                    &attr, sizeof(attr));
+                       if (ret)
+                               return ret;
+
+                       snappendf(buf, cnt, " %s %d", attr.name, attr.min);
+                       if (attr.min != attr.max)
+                               snappendf(buf, cnt, "-%d", attr.max);
+                       snappendf(buf, cnt, " %s", attr.unit);
+               } while (attr.index);
+
+               if ((state >= 0 && info.pstate == state) ||
+                   (state <  0 && info.ustate < 0))
+                       snappendf(buf, cnt, " *");
+               snappendf(buf, cnt, "\n");
+       }
+
+       return strlen(b);
+}
+
+static ssize_t
+nouveau_sysfs_pstate_set(struct device *d, struct device_attribute *a,
+                        const char *buf, size_t count)
+{
+       struct nouveau_sysfs *sysfs = nouveau_sysfs(drm_device(d));
+       struct nv_control_pstate_user args;
+       long value, ret;
+       char *tmp;
+
+       if ((tmp = strchr(buf, '\n')))
+               *tmp = '\0';
+
+       if (!strcasecmp(buf, "none"))
+               args.state = NV_CONTROL_PSTATE_USER_STATE_UNKNOWN;
+       else
+       if (!strcasecmp(buf, "auto"))
+               args.state = NV_CONTROL_PSTATE_USER_STATE_PERFMON;
+       else {
+               ret = kstrtol(buf, 16, &value);
+               if (ret)
+                       return ret;
+               args.state = value;
+       }
+
+       ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_USER, &args, sizeof(args));
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(pstate, S_IRUGO | S_IWUSR,
+                  nouveau_sysfs_pstate_get, nouveau_sysfs_pstate_set);
+
+void
+nouveau_sysfs_fini(struct drm_device *dev)
+{
+       struct nouveau_sysfs *sysfs = nouveau_sysfs(dev);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+
+       if (sysfs->ctrl) {
+               device_remove_file(&dev->pdev->dev, &dev_attr_pstate);
+               nouveau_object_del(nv_object(drm), NVDRM_DEVICE, NVDRM_CONTROL);
+       }
+
+       drm->sysfs = NULL;
+       kfree(sysfs);
+}
+
+int
+nouveau_sysfs_init(struct drm_device *dev)
+{
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_sysfs *sysfs;
+       int ret;
+
+       sysfs = drm->sysfs = kzalloc(sizeof(*sysfs), GFP_KERNEL);
+       if (!sysfs)
+               return -ENOMEM;
+
+       ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, NVDRM_CONTROL,
+                                NV_CONTROL_CLASS, NULL, 0, &sysfs->ctrl);
+       if (ret == 0)
+               device_create_file(&dev->pdev->dev, &dev_attr_pstate);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.h b/drivers/gpu/drm/nouveau/nouveau_sysfs.h
new file mode 100644 (file)
index 0000000..74b47f1
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __NOUVEAU_SYSFS_H__
+#define __NOUVEAU_SYSFS_H__
+
+#include "nouveau_drm.h"
+
+struct nouveau_sysfs {
+       struct nouveau_object *ctrl;
+};
+
+static inline struct nouveau_sysfs *
+nouveau_sysfs(struct drm_device *dev)
+{
+       return nouveau_drm(dev)->sysfs;
+}
+
+int  nouveau_sysfs_init(struct drm_device *);
+void nouveau_sysfs_fini(struct drm_device *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c
deleted file mode 100644 (file)
index 9976414..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright 2010 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-
-#include <drm/drmP.h>
-
-#include "nouveau_drm.h"
-#include "nouveau_pm.h"
-
-#include <subdev/bios/gpio.h>
-#include <subdev/gpio.h>
-
-static const enum dcb_gpio_func_name vidtag[] = { 0x04, 0x05, 0x06, 0x1a, 0x73 };
-static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]);
-
-int
-nouveau_voltage_gpio_get(struct drm_device *dev)
-{
-       struct nouveau_pm_voltage *volt = &nouveau_pm(dev)->voltage;
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_gpio *gpio = nouveau_gpio(device);
-       u8 vid = 0;
-       int i;
-
-       for (i = 0; i < nr_vidtag; i++) {
-               if (!(volt->vid_mask & (1 << i)))
-                       continue;
-
-               vid |= gpio->get(gpio, 0, vidtag[i], 0xff) << i;
-       }
-
-       return nouveau_volt_lvl_lookup(dev, vid);
-}
-
-int
-nouveau_voltage_gpio_set(struct drm_device *dev, int voltage)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_gpio *gpio = nouveau_gpio(device);
-       struct nouveau_pm_voltage *volt = &nouveau_pm(dev)->voltage;
-       int vid, i;
-
-       vid = nouveau_volt_vid_lookup(dev, voltage);
-       if (vid < 0)
-               return vid;
-
-       for (i = 0; i < nr_vidtag; i++) {
-               if (!(volt->vid_mask & (1 << i)))
-                       continue;
-
-               gpio->set(gpio, 0, vidtag[i], 0xff, !!(vid & (1 << i)));
-       }
-
-       return 0;
-}
-
-int
-nouveau_volt_vid_lookup(struct drm_device *dev, int voltage)
-{
-       struct nouveau_pm_voltage *volt = &nouveau_pm(dev)->voltage;
-       int i;
-
-       for (i = 0; i < volt->nr_level; i++) {
-               if (volt->level[i].voltage == voltage)
-                       return volt->level[i].vid;
-       }
-
-       return -ENOENT;
-}
-
-int
-nouveau_volt_lvl_lookup(struct drm_device *dev, int vid)
-{
-       struct nouveau_pm_voltage *volt = &nouveau_pm(dev)->voltage;
-       int i;
-
-       for (i = 0; i < volt->nr_level; i++) {
-               if (volt->level[i].vid == vid)
-                       return volt->level[i].voltage;
-       }
-
-       return -ENOENT;
-}
-
-void
-nouveau_volt_init(struct drm_device *dev)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
-       struct nouveau_pm *pm = nouveau_pm(dev);
-       struct nouveau_pm_voltage *voltage = &pm->voltage;
-       struct nvbios *bios = &drm->vbios;
-       struct dcb_gpio_func func;
-       struct bit_entry P;
-       u8 *volt = NULL, *entry;
-       int i, headerlen, recordlen, entries, vidmask, vidshift;
-
-       if (bios->type == NVBIOS_BIT) {
-               if (bit_table(dev, 'P', &P))
-                       return;
-
-               if (P.version == 1)
-                       volt = ROMPTR(dev, P.data[16]);
-               else
-               if (P.version == 2)
-                       volt = ROMPTR(dev, P.data[12]);
-               else {
-                       NV_WARN(drm, "unknown volt for BIT P %d\n", P.version);
-               }
-       } else {
-               if (bios->data[bios->offset + 6] < 0x27) {
-                       NV_DEBUG(drm, "BMP version too old for voltage\n");
-                       return;
-               }
-
-               volt = ROMPTR(dev, bios->data[bios->offset + 0x98]);
-       }
-
-       if (!volt) {
-               NV_DEBUG(drm, "voltage table pointer invalid\n");
-               return;
-       }
-
-       switch (volt[0]) {
-       case 0x10:
-       case 0x11:
-       case 0x12:
-               headerlen = 5;
-               recordlen = volt[1];
-               entries   = volt[2];
-               vidshift  = 0;
-               vidmask   = volt[4];
-               break;
-       case 0x20:
-               headerlen = volt[1];
-               recordlen = volt[3];
-               entries   = volt[2];
-               vidshift  = 0; /* could be vidshift like 0x30? */
-               vidmask   = volt[5];
-               break;
-       case 0x30:
-               headerlen = volt[1];
-               recordlen = volt[2];
-               entries   = volt[3];
-               vidmask   = volt[4];
-               /* no longer certain what volt[5] is, if it's related to
-                * the vid shift then it's definitely not a function of
-                * how many bits are set.
-                *
-                * after looking at a number of nva3+ vbios images, they
-                * all seem likely to have a static shift of 2.. lets
-                * go with that for now until proven otherwise.
-                */
-               vidshift  = 2;
-               break;
-       case 0x40:
-               headerlen = volt[1];
-               recordlen = volt[2];
-               entries   = volt[3]; /* not a clue what the entries are for.. */
-               vidmask   = volt[11]; /* guess.. */
-               vidshift  = 0;
-               break;
-       default:
-               NV_WARN(drm, "voltage table 0x%02x unknown\n", volt[0]);
-               return;
-       }
-
-       /* validate vid mask */
-       voltage->vid_mask = vidmask;
-       if (!voltage->vid_mask)
-               return;
-
-       i = 0;
-       while (vidmask) {
-               if (i > nr_vidtag) {
-                       NV_DEBUG(drm, "vid bit %d unknown\n", i);
-                       return;
-               }
-
-               if (gpio && gpio->find(gpio, 0, vidtag[i], 0xff, &func)) {
-                       NV_DEBUG(drm, "vid bit %d has no gpio tag\n", i);
-                       return;
-               }
-
-               vidmask >>= 1;
-               i++;
-       }
-
-       /* parse vbios entries into common format */
-       voltage->version = volt[0];
-       if (voltage->version < 0x40) {
-               voltage->nr_level = entries;
-               voltage->level =
-                       kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL);
-               if (!voltage->level)
-                       return;
-
-               entry = volt + headerlen;
-               for (i = 0; i < entries; i++, entry += recordlen) {
-                       voltage->level[i].voltage = entry[0] * 10000;
-                       voltage->level[i].vid     = entry[1] >> vidshift;
-               }
-       } else {
-               u32 volt_uv = ROM32(volt[4]);
-               s16 step_uv = ROM16(volt[8]);
-               u8 vid;
-
-               voltage->nr_level = voltage->vid_mask + 1;
-               voltage->level = kcalloc(voltage->nr_level,
-                                        sizeof(*voltage->level), GFP_KERNEL);
-               if (!voltage->level)
-                       return;
-
-               for (vid = 0; vid <= voltage->vid_mask; vid++) {
-                       voltage->level[vid].voltage = volt_uv;
-                       voltage->level[vid].vid = vid;
-                       volt_uv += step_uv;
-               }
-       }
-
-       voltage->supported = true;
-}
-
-void
-nouveau_volt_fini(struct drm_device *dev)
-{
-       struct nouveau_pm_voltage *volt = &nouveau_pm(dev)->voltage;
-
-       kfree(volt->level);
-}
index 77dcc9c..8fe32bb 100644 (file)
@@ -255,6 +255,12 @@ nv04_fbcon_accel_init(struct fb_info *info)
        OUT_RING(chan, NvCtxSurf2D);
        BEGIN_NV04(chan, NvSubImageBlit, 0x02fc, 1);
        OUT_RING(chan, 3);
+       if (device->chipset >= 0x11 /*XXX: oclass == 0x009f*/) {
+               BEGIN_NV04(chan, NvSubImageBlit, 0x0120, 3);
+               OUT_RING(chan, 0);
+               OUT_RING(chan, 1);
+               OUT_RING(chan, 2);
+       }
 
        BEGIN_NV04(chan, NvSubGdiRect, 0x0000, 1);
        OUT_RING(chan, NvGdiRect);
diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c
deleted file mode 100644 (file)
index 27afc0e..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2010 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-
-#include <drm/drmP.h>
-#include "nouveau_drm.h"
-#include "nouveau_reg.h"
-#include "dispnv04/hw.h"
-#include "nouveau_pm.h"
-
-#include <subdev/bios/pll.h>
-#include <subdev/clock.h>
-#include <subdev/timer.h>
-
-int
-nv04_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       int ret;
-
-       ret = nouveau_hw_get_clock(dev, PLL_CORE);
-       if (ret < 0)
-               return ret;
-       perflvl->core = ret;
-
-       ret = nouveau_hw_get_clock(dev, PLL_MEMORY);
-       if (ret < 0)
-               return ret;
-       perflvl->memory = ret;
-
-       return 0;
-}
-
-struct nv04_pm_clock {
-       struct nvbios_pll pll;
-       struct nouveau_pll_vals calc;
-};
-
-struct nv04_pm_state {
-       struct nv04_pm_clock core;
-       struct nv04_pm_clock memory;
-};
-
-static int
-calc_pll(struct drm_device *dev, u32 id, int khz, struct nv04_pm_clock *clk)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_bios *bios = nouveau_bios(device);
-       struct nouveau_clock *pclk = nouveau_clock(device);
-       int ret;
-
-       ret = nvbios_pll_parse(bios, id, &clk->pll);
-       if (ret)
-               return ret;
-
-       ret = pclk->pll_calc(pclk, &clk->pll, khz, &clk->calc);
-       if (!ret)
-               return -EINVAL;
-
-       return 0;
-}
-
-void *
-nv04_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       struct nv04_pm_state *info;
-       int ret;
-
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return ERR_PTR(-ENOMEM);
-
-       ret = calc_pll(dev, PLL_CORE, perflvl->core, &info->core);
-       if (ret)
-               goto error;
-
-       if (perflvl->memory) {
-               ret = calc_pll(dev, PLL_MEMORY, perflvl->memory, &info->memory);
-               if (ret)
-                       goto error;
-       }
-
-       return info;
-error:
-       kfree(info);
-       return ERR_PTR(ret);
-}
-
-static void
-prog_pll(struct drm_device *dev, struct nv04_pm_clock *clk)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_clock *pclk = nouveau_clock(device);
-       u32 reg = clk->pll.reg;
-
-       /* thank the insane nouveau_hw_setpll() interface for this */
-       if (device->card_type >= NV_40)
-               reg += 4;
-
-       pclk->pll_prog(pclk, reg, &clk->calc);
-}
-
-int
-nv04_pm_clocks_set(struct drm_device *dev, void *pre_state)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_timer *ptimer = nouveau_timer(device);
-       struct nv04_pm_state *state = pre_state;
-
-       prog_pll(dev, &state->core);
-
-       if (state->memory.pll.reg) {
-               prog_pll(dev, &state->memory);
-               if (device->card_type < NV_30) {
-                       if (device->card_type == NV_20)
-                               nv_mask(device, 0x1002c4, 0, 1 << 20);
-
-                       /* Reset the DLLs */
-                       nv_mask(device, 0x1002c0, 0, 1 << 8);
-               }
-       }
-
-       nv_ofuncs(ptimer)->init(nv_object(ptimer));
-
-       kfree(state);
-       return 0;
-}
diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c
deleted file mode 100644 (file)
index 625f80d..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright 2011 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-
-#include <drm/drmP.h>
-#include "nouveau_drm.h"
-#include "nouveau_bios.h"
-#include "nouveau_pm.h"
-#include "dispnv04/hw.h"
-
-#include <subdev/bios/pll.h>
-#include <subdev/clock.h>
-#include <subdev/timer.h>
-
-#include <engine/fifo.h>
-
-#define min2(a,b) ((a) < (b) ? (a) : (b))
-
-static u32
-read_pll_1(struct drm_device *dev, u32 reg)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 ctrl = nv_rd32(device, reg + 0x00);
-       int P = (ctrl & 0x00070000) >> 16;
-       int N = (ctrl & 0x0000ff00) >> 8;
-       int M = (ctrl & 0x000000ff) >> 0;
-       u32 ref = 27000, clk = 0;
-
-       if (ctrl & 0x80000000)
-               clk = ref * N / M;
-
-       return clk >> P;
-}
-
-static u32
-read_pll_2(struct drm_device *dev, u32 reg)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 ctrl = nv_rd32(device, reg + 0x00);
-       u32 coef = nv_rd32(device, reg + 0x04);
-       int N2 = (coef & 0xff000000) >> 24;
-       int M2 = (coef & 0x00ff0000) >> 16;
-       int N1 = (coef & 0x0000ff00) >> 8;
-       int M1 = (coef & 0x000000ff) >> 0;
-       int P = (ctrl & 0x00070000) >> 16;
-       u32 ref = 27000, clk = 0;
-
-       if ((ctrl & 0x80000000) && M1) {
-               clk = ref * N1 / M1;
-               if ((ctrl & 0x40000100) == 0x40000000) {
-                       if (M2)
-                               clk = clk * N2 / M2;
-                       else
-                               clk = 0;
-               }
-       }
-
-       return clk >> P;
-}
-
-static u32
-read_clk(struct drm_device *dev, u32 src)
-{
-       switch (src) {
-       case 3:
-               return read_pll_2(dev, 0x004000);
-       case 2:
-               return read_pll_1(dev, 0x004008);
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-int
-nv40_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 ctrl = nv_rd32(device, 0x00c040);
-
-       perflvl->core   = read_clk(dev, (ctrl & 0x00000003) >> 0);
-       perflvl->shader = read_clk(dev, (ctrl & 0x00000030) >> 4);
-       perflvl->memory = read_pll_2(dev, 0x4020);
-       return 0;
-}
-
-struct nv40_pm_state {
-       u32 ctrl;
-       u32 npll_ctrl;
-       u32 npll_coef;
-       u32 spll;
-       u32 mpll_ctrl;
-       u32 mpll_coef;
-};
-
-static int
-nv40_calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll,
-             u32 clk, int *N1, int *M1, int *N2, int *M2, int *log2P)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_bios *bios = nouveau_bios(device);
-       struct nouveau_clock *pclk = nouveau_clock(device);
-       struct nouveau_pll_vals coef;
-       int ret;
-
-       ret = nvbios_pll_parse(bios, reg, pll);
-       if (ret)
-               return ret;
-
-       if (clk < pll->vco1.max_freq)
-               pll->vco2.max_freq = 0;
-
-       ret = pclk->pll_calc(pclk, pll, clk, &coef);
-       if (ret == 0)
-               return -ERANGE;
-
-       *N1 = coef.N1;
-       *M1 = coef.M1;
-       if (N2 && M2) {
-               if (pll->vco2.max_freq) {
-                       *N2 = coef.N2;
-                       *M2 = coef.M2;
-               } else {
-                       *N2 = 1;
-                       *M2 = 1;
-               }
-       }
-       *log2P = coef.log2P;
-       return 0;
-}
-
-void *
-nv40_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       struct nv40_pm_state *info;
-       struct nvbios_pll pll;
-       int N1, N2, M1, M2, log2P;
-       int ret;
-
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return ERR_PTR(-ENOMEM);
-
-       /* core/geometric clock */
-       ret = nv40_calc_pll(dev, 0x004000, &pll, perflvl->core,
-                           &N1, &M1, &N2, &M2, &log2P);
-       if (ret < 0)
-               goto out;
-
-       if (N2 == M2) {
-               info->npll_ctrl = 0x80000100 | (log2P << 16);
-               info->npll_coef = (N1 << 8) | M1;
-       } else {
-               info->npll_ctrl = 0xc0000000 | (log2P << 16);
-               info->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
-       }
-
-       /* use the second PLL for shader/rop clock, if it differs from core */
-       if (perflvl->shader && perflvl->shader != perflvl->core) {
-               ret = nv40_calc_pll(dev, 0x004008, &pll, perflvl->shader,
-                                   &N1, &M1, NULL, NULL, &log2P);
-               if (ret < 0)
-                       goto out;
-
-               info->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1;
-               info->ctrl = 0x00000223;
-       } else {
-               info->spll = 0x00000000;
-               info->ctrl = 0x00000333;
-       }
-
-       /* memory clock */
-       if (!perflvl->memory) {
-               info->mpll_ctrl = 0x00000000;
-               goto out;
-       }
-
-       ret = nv40_calc_pll(dev, 0x004020, &pll, perflvl->memory,
-                           &N1, &M1, &N2, &M2, &log2P);
-       if (ret < 0)
-               goto out;
-
-       info->mpll_ctrl  = 0x80000000 | (log2P << 16);
-       info->mpll_ctrl |= min2(pll.bias_p + log2P, pll.max_p) << 20;
-       if (N2 == M2) {
-               info->mpll_ctrl |= 0x00000100;
-               info->mpll_coef  = (N1 << 8) | M1;
-       } else {
-               info->mpll_ctrl |= 0x40000000;
-               info->mpll_coef  = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
-       }
-
-out:
-       if (ret < 0) {
-               kfree(info);
-               info = ERR_PTR(ret);
-       }
-       return info;
-}
-
-static bool
-nv40_pm_gr_idle(void *data)
-{
-       struct drm_device *dev = data;
-       struct nouveau_device *device = nouveau_dev(dev);
-
-       if ((nv_rd32(device, 0x400760) & 0x000000f0) >> 4 !=
-           (nv_rd32(device, 0x400760) & 0x0000000f))
-               return false;
-
-       if (nv_rd32(device, 0x400700))
-               return false;
-
-       return true;
-}
-
-int
-nv40_pm_clocks_set(struct drm_device *dev, void *pre_state)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_fifo *pfifo = nouveau_fifo(device);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nv40_pm_state *info = pre_state;
-       unsigned long flags;
-       struct bit_entry M;
-       u32 crtc_mask = 0;
-       u8 sr1[2];
-       int i, ret = -EAGAIN;
-
-       /* determine which CRTCs are active, fetch VGA_SR1 for each */
-       for (i = 0; i < 2; i++) {
-               u32 vbl = nv_rd32(device, 0x600808 + (i * 0x2000));
-               u32 cnt = 0;
-               do {
-                       if (vbl != nv_rd32(device, 0x600808 + (i * 0x2000))) {
-                               nv_wr08(device, 0x0c03c4 + (i * 0x2000), 0x01);
-                               sr1[i] = nv_rd08(device, 0x0c03c5 + (i * 0x2000));
-                               if (!(sr1[i] & 0x20))
-                                       crtc_mask |= (1 << i);
-                               break;
-                       }
-                       udelay(1);
-               } while (cnt++ < 32);
-       }
-
-       /* halt and idle engines */
-       pfifo->pause(pfifo, &flags);
-
-       if (!nv_wait_cb(device, nv40_pm_gr_idle, dev))
-               goto resume;
-
-       ret = 0;
-
-       /* set engine clocks */
-       nv_mask(device, 0x00c040, 0x00000333, 0x00000000);
-       nv_wr32(device, 0x004004, info->npll_coef);
-       nv_mask(device, 0x004000, 0xc0070100, info->npll_ctrl);
-       nv_mask(device, 0x004008, 0xc007ffff, info->spll);
-       mdelay(5);
-       nv_mask(device, 0x00c040, 0x00000333, info->ctrl);
-
-       if (!info->mpll_ctrl)
-               goto resume;
-
-       /* wait for vblank start on active crtcs, disable memory access */
-       for (i = 0; i < 2; i++) {
-               if (!(crtc_mask & (1 << i)))
-                       continue;
-               nv_wait(device, 0x600808 + (i * 0x2000), 0x00010000, 0x00000000);
-               nv_wait(device, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
-               nv_wr08(device, 0x0c03c4 + (i * 0x2000), 0x01);
-               nv_wr08(device, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20);
-       }
-
-       /* prepare ram for reclocking */
-       nv_wr32(device, 0x1002d4, 0x00000001); /* precharge */
-       nv_wr32(device, 0x1002d0, 0x00000001); /* refresh */
-       nv_wr32(device, 0x1002d0, 0x00000001); /* refresh */
-       nv_mask(device, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */
-       nv_wr32(device, 0x1002dc, 0x00000001); /* enable self-refresh */
-
-       /* change the PLL of each memory partition */
-       nv_mask(device, 0x00c040, 0x0000c000, 0x00000000);
-       switch (nv_device(drm->device)->chipset) {
-       case 0x40:
-       case 0x45:
-       case 0x41:
-       case 0x42:
-       case 0x47:
-               nv_mask(device, 0x004044, 0xc0771100, info->mpll_ctrl);
-               nv_mask(device, 0x00402c, 0xc0771100, info->mpll_ctrl);
-               nv_wr32(device, 0x004048, info->mpll_coef);
-               nv_wr32(device, 0x004030, info->mpll_coef);
-       case 0x43:
-       case 0x49:
-       case 0x4b:
-               nv_mask(device, 0x004038, 0xc0771100, info->mpll_ctrl);
-               nv_wr32(device, 0x00403c, info->mpll_coef);
-       default:
-               nv_mask(device, 0x004020, 0xc0771100, info->mpll_ctrl);
-               nv_wr32(device, 0x004024, info->mpll_coef);
-               break;
-       }
-       udelay(100);
-       nv_mask(device, 0x00c040, 0x0000c000, 0x0000c000);
-
-       /* re-enable normal operation of memory controller */
-       nv_wr32(device, 0x1002dc, 0x00000000);
-       nv_mask(device, 0x100210, 0x80000000, 0x80000000);
-       udelay(100);
-
-       /* execute memory reset script from vbios */
-       if (!bit_table(dev, 'M', &M))
-               nouveau_bios_run_init_table(dev, ROM16(M.data[0]), NULL, 0);
-
-       /* make sure we're in vblank (hopefully the same one as before), and
-        * then re-enable crtc memory access
-        */
-       for (i = 0; i < 2; i++) {
-               if (!(crtc_mask & (1 << i)))
-                       continue;
-               nv_wait(device, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
-               nv_wr08(device, 0x0c03c4 + (i * 0x2000), 0x01);
-               nv_wr08(device, 0x0c03c5 + (i * 0x2000), sr1[i]);
-       }
-
-       /* resume engines */
-resume:
-       pfifo->start(pfifo, &flags);
-       kfree(info);
-       return ret;
-}
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
deleted file mode 100644 (file)
index 4efc33f..0000000
+++ /dev/null
@@ -1,855 +0,0 @@
-/*
- * Copyright 2010 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-
-#include <drm/drmP.h>
-#include "nouveau_drm.h"
-#include "nouveau_bios.h"
-#include "dispnv04/hw.h"
-#include "nouveau_pm.h"
-#include "nouveau_hwsq.h"
-
-#include "nv50_display.h"
-
-#include <subdev/bios/pll.h>
-#include <subdev/clock.h>
-#include <subdev/timer.h>
-#include <subdev/fb.h>
-
-enum clk_src {
-       clk_src_crystal,
-       clk_src_href,
-       clk_src_hclk,
-       clk_src_hclkm3,
-       clk_src_hclkm3d2,
-       clk_src_host,
-       clk_src_nvclk,
-       clk_src_sclk,
-       clk_src_mclk,
-       clk_src_vdec,
-       clk_src_dom6
-};
-
-static u32 read_clk(struct drm_device *, enum clk_src);
-
-static u32
-read_div(struct drm_device *dev)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       switch (nv_device(drm->device)->chipset) {
-       case 0x50: /* it exists, but only has bit 31, not the dividers.. */
-       case 0x84:
-       case 0x86:
-       case 0x98:
-       case 0xa0:
-               return nv_rd32(device, 0x004700);
-       case 0x92:
-       case 0x94:
-       case 0x96:
-               return nv_rd32(device, 0x004800);
-       default:
-               return 0x00000000;
-       }
-}
-
-static u32
-read_pll_src(struct drm_device *dev, u32 base)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       u32 coef, ref = read_clk(dev, clk_src_crystal);
-       u32 rsel = nv_rd32(device, 0x00e18c);
-       int P, N, M, id;
-
-       switch (nv_device(drm->device)->chipset) {
-       case 0x50:
-       case 0xa0:
-               switch (base) {
-               case 0x4020:
-               case 0x4028: id = !!(rsel & 0x00000004); break;
-               case 0x4008: id = !!(rsel & 0x00000008); break;
-               case 0x4030: id = 0; break;
-               default:
-                       NV_ERROR(drm, "ref: bad pll 0x%06x\n", base);
-                       return 0;
-               }
-
-               coef = nv_rd32(device, 0x00e81c + (id * 0x0c));
-               ref *=  (coef & 0x01000000) ? 2 : 4;
-               P    =  (coef & 0x00070000) >> 16;
-               N    = ((coef & 0x0000ff00) >> 8) + 1;
-               M    = ((coef & 0x000000ff) >> 0) + 1;
-               break;
-       case 0x84:
-       case 0x86:
-       case 0x92:
-               coef = nv_rd32(device, 0x00e81c);
-               P    = (coef & 0x00070000) >> 16;
-               N    = (coef & 0x0000ff00) >> 8;
-               M    = (coef & 0x000000ff) >> 0;
-               break;
-       case 0x94:
-       case 0x96:
-       case 0x98:
-               rsel = nv_rd32(device, 0x00c050);
-               switch (base) {
-               case 0x4020: rsel = (rsel & 0x00000003) >> 0; break;
-               case 0x4008: rsel = (rsel & 0x0000000c) >> 2; break;
-               case 0x4028: rsel = (rsel & 0x00001800) >> 11; break;
-               case 0x4030: rsel = 3; break;
-               default:
-                       NV_ERROR(drm, "ref: bad pll 0x%06x\n", base);
-                       return 0;
-               }
-
-               switch (rsel) {
-               case 0: id = 1; break;
-               case 1: return read_clk(dev, clk_src_crystal);
-               case 2: return read_clk(dev, clk_src_href);
-               case 3: id = 0; break;
-               }
-
-               coef =  nv_rd32(device, 0x00e81c + (id * 0x28));
-               P    = (nv_rd32(device, 0x00e824 + (id * 0x28)) >> 16) & 7;
-               P   += (coef & 0x00070000) >> 16;
-               N    = (coef & 0x0000ff00) >> 8;
-               M    = (coef & 0x000000ff) >> 0;
-               break;
-       default:
-               BUG_ON(1);
-       }
-
-       if (M)
-               return (ref * N / M) >> P;
-       return 0;
-}
-
-static u32
-read_pll_ref(struct drm_device *dev, u32 base)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       u32 src, mast = nv_rd32(device, 0x00c040);
-
-       switch (base) {
-       case 0x004028:
-               src = !!(mast & 0x00200000);
-               break;
-       case 0x004020:
-               src = !!(mast & 0x00400000);
-               break;
-       case 0x004008:
-               src = !!(mast & 0x00010000);
-               break;
-       case 0x004030:
-               src = !!(mast & 0x02000000);
-               break;
-       case 0x00e810:
-               return read_clk(dev, clk_src_crystal);
-       default:
-               NV_ERROR(drm, "bad pll 0x%06x\n", base);
-               return 0;
-       }
-
-       if (src)
-               return read_clk(dev, clk_src_href);
-       return read_pll_src(dev, base);
-}
-
-static u32
-read_pll(struct drm_device *dev, u32 base)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       u32 mast = nv_rd32(device, 0x00c040);
-       u32 ctrl = nv_rd32(device, base + 0);
-       u32 coef = nv_rd32(device, base + 4);
-       u32 ref = read_pll_ref(dev, base);
-       u32 clk = 0;
-       int N1, N2, M1, M2;
-
-       if (base == 0x004028 && (mast & 0x00100000)) {
-               /* wtf, appears to only disable post-divider on nva0 */
-               if (nv_device(drm->device)->chipset != 0xa0)
-                       return read_clk(dev, clk_src_dom6);
-       }
-
-       N2 = (coef & 0xff000000) >> 24;
-       M2 = (coef & 0x00ff0000) >> 16;
-       N1 = (coef & 0x0000ff00) >> 8;
-       M1 = (coef & 0x000000ff);
-       if ((ctrl & 0x80000000) && M1) {
-               clk = ref * N1 / M1;
-               if ((ctrl & 0x40000100) == 0x40000000) {
-                       if (M2)
-                               clk = clk * N2 / M2;
-                       else
-                               clk = 0;
-               }
-       }
-
-       return clk;
-}
-
-static u32
-read_clk(struct drm_device *dev, enum clk_src src)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       u32 mast = nv_rd32(device, 0x00c040);
-       u32 P = 0;
-
-       switch (src) {
-       case clk_src_crystal:
-               return device->crystal;
-       case clk_src_href:
-               return 100000; /* PCIE reference clock */
-       case clk_src_hclk:
-               return read_clk(dev, clk_src_href) * 27778 / 10000;
-       case clk_src_hclkm3:
-               return read_clk(dev, clk_src_hclk) * 3;
-       case clk_src_hclkm3d2:
-               return read_clk(dev, clk_src_hclk) * 3 / 2;
-       case clk_src_host:
-               switch (mast & 0x30000000) {
-               case 0x00000000: return read_clk(dev, clk_src_href);
-               case 0x10000000: break;
-               case 0x20000000: /* !0x50 */
-               case 0x30000000: return read_clk(dev, clk_src_hclk);
-               }
-               break;
-       case clk_src_nvclk:
-               if (!(mast & 0x00100000))
-                       P = (nv_rd32(device, 0x004028) & 0x00070000) >> 16;
-               switch (mast & 0x00000003) {
-               case 0x00000000: return read_clk(dev, clk_src_crystal) >> P;
-               case 0x00000001: return read_clk(dev, clk_src_dom6);
-               case 0x00000002: return read_pll(dev, 0x004020) >> P;
-               case 0x00000003: return read_pll(dev, 0x004028) >> P;
-               }
-               break;
-       case clk_src_sclk:
-               P = (nv_rd32(device, 0x004020) & 0x00070000) >> 16;
-               switch (mast & 0x00000030) {
-               case 0x00000000:
-                       if (mast & 0x00000080)
-                               return read_clk(dev, clk_src_host) >> P;
-                       return read_clk(dev, clk_src_crystal) >> P;
-               case 0x00000010: break;
-               case 0x00000020: return read_pll(dev, 0x004028) >> P;
-               case 0x00000030: return read_pll(dev, 0x004020) >> P;
-               }
-               break;
-       case clk_src_mclk:
-               P = (nv_rd32(device, 0x004008) & 0x00070000) >> 16;
-               if (nv_rd32(device, 0x004008) & 0x00000200) {
-                       switch (mast & 0x0000c000) {
-                       case 0x00000000:
-                               return read_clk(dev, clk_src_crystal) >> P;
-                       case 0x00008000:
-                       case 0x0000c000:
-                               return read_clk(dev, clk_src_href) >> P;
-                       }
-               } else {
-                       return read_pll(dev, 0x004008) >> P;
-               }
-               break;
-       case clk_src_vdec:
-               P = (read_div(dev) & 0x00000700) >> 8;
-               switch (nv_device(drm->device)->chipset) {
-               case 0x84:
-               case 0x86:
-               case 0x92:
-               case 0x94:
-               case 0x96:
-               case 0xa0:
-                       switch (mast & 0x00000c00) {
-                       case 0x00000000:
-                               if (nv_device(drm->device)->chipset == 0xa0) /* wtf?? */
-                                       return read_clk(dev, clk_src_nvclk) >> P;
-                               return read_clk(dev, clk_src_crystal) >> P;
-                       case 0x00000400:
-                               return 0;
-                       case 0x00000800:
-                               if (mast & 0x01000000)
-                                       return read_pll(dev, 0x004028) >> P;
-                               return read_pll(dev, 0x004030) >> P;
-                       case 0x00000c00:
-                               return read_clk(dev, clk_src_nvclk) >> P;
-                       }
-                       break;
-               case 0x98:
-                       switch (mast & 0x00000c00) {
-                       case 0x00000000:
-                               return read_clk(dev, clk_src_nvclk) >> P;
-                       case 0x00000400:
-                               return 0;
-                       case 0x00000800:
-                               return read_clk(dev, clk_src_hclkm3d2) >> P;
-                       case 0x00000c00:
-                               return read_clk(dev, clk_src_mclk) >> P;
-                       }
-                       break;
-               }
-               break;
-       case clk_src_dom6:
-               switch (nv_device(drm->device)->chipset) {
-               case 0x50:
-               case 0xa0:
-                       return read_pll(dev, 0x00e810) >> 2;
-               case 0x84:
-               case 0x86:
-               case 0x92:
-               case 0x94:
-               case 0x96:
-               case 0x98:
-                       P = (read_div(dev) & 0x00000007) >> 0;
-                       switch (mast & 0x0c000000) {
-                       case 0x00000000: return read_clk(dev, clk_src_href);
-                       case 0x04000000: break;
-                       case 0x08000000: return read_clk(dev, clk_src_hclk);
-                       case 0x0c000000:
-                               return read_clk(dev, clk_src_hclkm3) >> P;
-                       }
-                       break;
-               default:
-                       break;
-               }
-       default:
-               break;
-       }
-
-       NV_DEBUG(drm, "unknown clock source %d 0x%08x\n", src, mast);
-       return 0;
-}
-
-int
-nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       if (nv_device(drm->device)->chipset == 0xaa ||
-           nv_device(drm->device)->chipset == 0xac)
-               return 0;
-
-       perflvl->core   = read_clk(dev, clk_src_nvclk);
-       perflvl->shader = read_clk(dev, clk_src_sclk);
-       perflvl->memory = read_clk(dev, clk_src_mclk);
-       if (nv_device(drm->device)->chipset != 0x50) {
-               perflvl->vdec = read_clk(dev, clk_src_vdec);
-               perflvl->dom6 = read_clk(dev, clk_src_dom6);
-       }
-
-       return 0;
-}
-
-struct nv50_pm_state {
-       struct nouveau_pm_level *perflvl;
-       struct hwsq_ucode eclk_hwsq;
-       struct hwsq_ucode mclk_hwsq;
-       u32 mscript;
-       u32 mmast;
-       u32 mctrl;
-       u32 mcoef;
-};
-
-static u32
-calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll,
-        u32 clk, int *N1, int *M1, int *log2P)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_bios *bios = nouveau_bios(device);
-       struct nouveau_clock *pclk = nouveau_clock(device);
-       struct nouveau_pll_vals coef;
-       int ret;
-
-       ret = nvbios_pll_parse(bios, reg, pll);
-       if (ret)
-               return 0;
-
-       pll->vco2.max_freq = 0;
-       pll->refclk = read_pll_ref(dev, reg);
-       if (!pll->refclk)
-               return 0;
-
-       ret = pclk->pll_calc(pclk, pll, clk, &coef);
-       if (ret == 0)
-               return 0;
-
-       *N1 = coef.N1;
-       *M1 = coef.M1;
-       *log2P = coef.log2P;
-       return ret;
-}
-
-static inline u32
-calc_div(u32 src, u32 target, int *div)
-{
-       u32 clk0 = src, clk1 = src;
-       for (*div = 0; *div <= 7; (*div)++) {
-               if (clk0 <= target) {
-                       clk1 = clk0 << (*div ? 1 : 0);
-                       break;
-               }
-               clk0 >>= 1;
-       }
-
-       if (target - clk0 <= clk1 - target)
-               return clk0;
-       (*div)--;
-       return clk1;
-}
-
-static inline u32
-clk_same(u32 a, u32 b)
-{
-       return ((a / 1000) == (b / 1000));
-}
-
-static void
-mclk_precharge(struct nouveau_mem_exec_func *exec)
-{
-       struct nv50_pm_state *info = exec->priv;
-       struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-
-       hwsq_wr32(hwsq, 0x1002d4, 0x00000001);
-}
-
-static void
-mclk_refresh(struct nouveau_mem_exec_func *exec)
-{
-       struct nv50_pm_state *info = exec->priv;
-       struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-
-       hwsq_wr32(hwsq, 0x1002d0, 0x00000001);
-}
-
-static void
-mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable)
-{
-       struct nv50_pm_state *info = exec->priv;
-       struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-
-       hwsq_wr32(hwsq, 0x100210, enable ? 0x80000000 : 0x00000000);
-}
-
-static void
-mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable)
-{
-       struct nv50_pm_state *info = exec->priv;
-       struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-
-       hwsq_wr32(hwsq, 0x1002dc, enable ? 0x00000001 : 0x00000000);
-}
-
-static void
-mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec)
-{
-       struct nv50_pm_state *info = exec->priv;
-       struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-
-       if (nsec > 1000)
-               hwsq_usec(hwsq, (nsec + 500) / 1000);
-}
-
-static u32
-mclk_mrg(struct nouveau_mem_exec_func *exec, int mr)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       if (mr <= 1)
-               return nv_rd32(device, 0x1002c0 + ((mr - 0) * 4));
-       if (mr <= 3)
-               return nv_rd32(device, 0x1002e0 + ((mr - 2) * 4));
-       return 0;
-}
-
-static void
-mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nouveau_fb *pfb = nouveau_fb(device);
-       struct nv50_pm_state *info = exec->priv;
-       struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-
-       if (mr <= 1) {
-               if (pfb->ram->ranks > 1)
-                       hwsq_wr32(hwsq, 0x1002c8 + ((mr - 0) * 4), data);
-               hwsq_wr32(hwsq, 0x1002c0 + ((mr - 0) * 4), data);
-       } else
-       if (mr <= 3) {
-               if (pfb->ram->ranks > 1)
-                       hwsq_wr32(hwsq, 0x1002e8 + ((mr - 2) * 4), data);
-               hwsq_wr32(hwsq, 0x1002e0 + ((mr - 2) * 4), data);
-       }
-}
-
-static void
-mclk_clock_set(struct nouveau_mem_exec_func *exec)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nv50_pm_state *info = exec->priv;
-       struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-       u32 ctrl = nv_rd32(device, 0x004008);
-
-       info->mmast = nv_rd32(device, 0x00c040);
-       info->mmast &= ~0xc0000000; /* get MCLK_2 from HREF */
-       info->mmast |=  0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */
-
-       hwsq_wr32(hwsq, 0xc040, info->mmast);
-       hwsq_wr32(hwsq, 0x4008, ctrl | 0x00000200); /* bypass MPLL */
-       if (info->mctrl & 0x80000000)
-               hwsq_wr32(hwsq, 0x400c, info->mcoef);
-       hwsq_wr32(hwsq, 0x4008, info->mctrl);
-}
-
-static void
-mclk_timing_set(struct nouveau_mem_exec_func *exec)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nv50_pm_state *info = exec->priv;
-       struct nouveau_pm_level *perflvl = info->perflvl;
-       struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-       int i;
-
-       for (i = 0; i < 9; i++) {
-               u32 reg = 0x100220 + (i * 4);
-               u32 val = nv_rd32(device, reg);
-               if (val != perflvl->timing.reg[i])
-                       hwsq_wr32(hwsq, reg, perflvl->timing.reg[i]);
-       }
-}
-
-static int
-calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl,
-         struct nv50_pm_state *info)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 crtc_mask = 0; /*XXX: nv50_display_active_crtcs(dev); */
-       struct nouveau_mem_exec_func exec = {
-               .dev = dev,
-               .precharge = mclk_precharge,
-               .refresh = mclk_refresh,
-               .refresh_auto = mclk_refresh_auto,
-               .refresh_self = mclk_refresh_self,
-               .wait = mclk_wait,
-               .mrg = mclk_mrg,
-               .mrs = mclk_mrs,
-               .clock_set = mclk_clock_set,
-               .timing_set = mclk_timing_set,
-               .priv = info
-       };
-       struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-       struct nvbios_pll pll;
-       int N, M, P;
-       int ret;
-
-       /* use pcie refclock if possible, otherwise use mpll */
-       info->mctrl  = nv_rd32(device, 0x004008);
-       info->mctrl &= ~0x81ff0200;
-       if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) {
-               info->mctrl |= 0x00000200 | (pll.bias_p << 19);
-       } else {
-               ret = calc_pll(dev, 0x4008, &pll, perflvl->memory, &N, &M, &P);
-               if (ret == 0)
-                       return -EINVAL;
-
-               info->mctrl |= 0x80000000 | (P << 22) | (P << 16);
-               info->mctrl |= pll.bias_p << 19;
-               info->mcoef  = (N << 8) | M;
-       }
-
-       /* build the ucode which will reclock the memory for us */
-       hwsq_init(hwsq);
-       if (crtc_mask) {
-               hwsq_op5f(hwsq, crtc_mask, 0x00); /* wait for scanout */
-               hwsq_op5f(hwsq, crtc_mask, 0x01); /* wait for vblank */
-       }
-       if (nv_device(drm->device)->chipset >= 0x92)
-               hwsq_wr32(hwsq, 0x611200, 0x00003300); /* disable scanout */
-       hwsq_setf(hwsq, 0x10, 0); /* disable bus access */
-       hwsq_op5f(hwsq, 0x00, 0x01); /* no idea :s */
-
-       ret = nouveau_mem_exec(&exec, perflvl);
-       if (ret)
-               return ret;
-
-       hwsq_setf(hwsq, 0x10, 1); /* enable bus access */
-       hwsq_op5f(hwsq, 0x00, 0x00); /* no idea, reverse of 0x00, 0x01? */
-       if (nv_device(drm->device)->chipset >= 0x92)
-               hwsq_wr32(hwsq, 0x611200, 0x00003330); /* enable scanout */
-       hwsq_fini(hwsq);
-       return 0;
-}
-
-void *
-nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nv50_pm_state *info;
-       struct hwsq_ucode *hwsq;
-       struct nvbios_pll pll;
-       u32 out, mast, divs, ctrl;
-       int clk, ret = -EINVAL;
-       int N, M, P1, P2;
-
-       if (nv_device(drm->device)->chipset == 0xaa ||
-           nv_device(drm->device)->chipset == 0xac)
-               return ERR_PTR(-ENODEV);
-
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return ERR_PTR(-ENOMEM);
-       info->perflvl = perflvl;
-
-       /* memory: build hwsq ucode which we'll use to reclock memory.
-        *         use pcie refclock if possible, otherwise use mpll */
-       info->mclk_hwsq.len = 0;
-       if (perflvl->memory) {
-               ret = calc_mclk(dev, perflvl, info);
-               if (ret)
-                       goto error;
-               info->mscript = perflvl->memscript;
-       }
-
-       divs = read_div(dev);
-       mast = info->mmast;
-
-       /* start building HWSQ script for engine reclocking */
-       hwsq = &info->eclk_hwsq;
-       hwsq_init(hwsq);
-       hwsq_setf(hwsq, 0x10, 0); /* disable bus access */
-       hwsq_op5f(hwsq, 0x00, 0x01); /* wait for access disabled? */
-
-       /* vdec/dom6: switch to "safe" clocks temporarily */
-       if (perflvl->vdec) {
-               mast &= ~0x00000c00;
-               divs &= ~0x00000700;
-       }
-
-       if (perflvl->dom6) {
-               mast &= ~0x0c000000;
-               divs &= ~0x00000007;
-       }
-
-       hwsq_wr32(hwsq, 0x00c040, mast);
-
-       /* vdec: avoid modifying xpll until we know exactly how the other
-        * clock domains work, i suspect at least some of them can also be
-        * tied to xpll...
-        */
-       if (perflvl->vdec) {
-               /* see how close we can get using nvclk as a source */
-               clk = calc_div(perflvl->core, perflvl->vdec, &P1);
-
-               /* see how close we can get using xpll/hclk as a source */
-               if (nv_device(drm->device)->chipset != 0x98)
-                       out = read_pll(dev, 0x004030);
-               else
-                       out = read_clk(dev, clk_src_hclkm3d2);
-               out = calc_div(out, perflvl->vdec, &P2);
-
-               /* select whichever gets us closest */
-               if (abs((int)perflvl->vdec - clk) <=
-                   abs((int)perflvl->vdec - out)) {
-                       if (nv_device(drm->device)->chipset != 0x98)
-                               mast |= 0x00000c00;
-                       divs |= P1 << 8;
-               } else {
-                       mast |= 0x00000800;
-                       divs |= P2 << 8;
-               }
-       }
-
-       /* dom6: nfi what this is, but we're limited to various combinations
-        * of the host clock frequency
-        */
-       if (perflvl->dom6) {
-               if (clk_same(perflvl->dom6, read_clk(dev, clk_src_href))) {
-                       mast |= 0x00000000;
-               } else
-               if (clk_same(perflvl->dom6, read_clk(dev, clk_src_hclk))) {
-                       mast |= 0x08000000;
-               } else {
-                       clk = read_clk(dev, clk_src_hclk) * 3;
-                       clk = calc_div(clk, perflvl->dom6, &P1);
-
-                       mast |= 0x0c000000;
-                       divs |= P1;
-               }
-       }
-
-       /* vdec/dom6: complete switch to new clocks */
-       switch (nv_device(drm->device)->chipset) {
-       case 0x92:
-       case 0x94:
-       case 0x96:
-               hwsq_wr32(hwsq, 0x004800, divs);
-               break;
-       default:
-               hwsq_wr32(hwsq, 0x004700, divs);
-               break;
-       }
-
-       hwsq_wr32(hwsq, 0x00c040, mast);
-
-       /* core/shader: make sure sclk/nvclk are disconnected from their
-        * PLLs (nvclk to dom6, sclk to hclk)
-        */
-       if (nv_device(drm->device)->chipset < 0x92)
-               mast = (mast & ~0x001000b0) | 0x00100080;
-       else
-               mast = (mast & ~0x000000b3) | 0x00000081;
-
-       hwsq_wr32(hwsq, 0x00c040, mast);
-
-       /* core: for the moment at least, always use nvpll */
-       clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1);
-       if (clk == 0)
-               goto error;
-
-       ctrl  = nv_rd32(device, 0x004028) & ~0xc03f0100;
-       mast &= ~0x00100000;
-       mast |= 3;
-
-       hwsq_wr32(hwsq, 0x004028, 0x80000000 | (P1 << 19) | (P1 << 16) | ctrl);
-       hwsq_wr32(hwsq, 0x00402c, (N << 8) | M);
-
-       /* shader: tie to nvclk if possible, otherwise use spll.  have to be
-        * very careful that the shader clock is at least twice the core, or
-        * some chipsets will be very unhappy.  i expect most or all of these
-        * cases will be handled by tying to nvclk, but it's possible there's
-        * corners
-        */
-       ctrl = nv_rd32(device, 0x004020) & ~0xc03f0100;
-
-       if (P1-- && perflvl->shader == (perflvl->core << 1)) {
-               hwsq_wr32(hwsq, 0x004020, (P1 << 19) | (P1 << 16) | ctrl);
-               hwsq_wr32(hwsq, 0x00c040, 0x00000020 | mast);
-       } else {
-               clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1);
-               if (clk == 0)
-                       goto error;
-               ctrl |= 0x80000000;
-
-               hwsq_wr32(hwsq, 0x004020, (P1 << 19) | (P1 << 16) | ctrl);
-               hwsq_wr32(hwsq, 0x004024, (N << 8) | M);
-               hwsq_wr32(hwsq, 0x00c040, 0x00000030 | mast);
-       }
-
-       hwsq_setf(hwsq, 0x10, 1); /* enable bus access */
-       hwsq_op5f(hwsq, 0x00, 0x00); /* wait for access enabled? */
-       hwsq_fini(hwsq);
-
-       return info;
-error:
-       kfree(info);
-       return ERR_PTR(ret);
-}
-
-static int
-prog_hwsq(struct drm_device *dev, struct hwsq_ucode *hwsq)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       u32 hwsq_data, hwsq_kick;
-       int i;
-
-       if (nv_device(drm->device)->chipset < 0x94) {
-               hwsq_data = 0x001400;
-               hwsq_kick = 0x00000003;
-       } else {
-               hwsq_data = 0x080000;
-               hwsq_kick = 0x00000001;
-       }
-       /* upload hwsq ucode */
-       nv_mask(device, 0x001098, 0x00000008, 0x00000000);
-       nv_wr32(device, 0x001304, 0x00000000);
-       if (nv_device(drm->device)->chipset >= 0x92)
-               nv_wr32(device, 0x001318, 0x00000000);
-       for (i = 0; i < hwsq->len / 4; i++)
-               nv_wr32(device, hwsq_data + (i * 4), hwsq->ptr.u32[i]);
-       nv_mask(device, 0x001098, 0x00000018, 0x00000018);
-
-       /* launch, and wait for completion */
-       nv_wr32(device, 0x00130c, hwsq_kick);
-       if (!nv_wait(device, 0x001308, 0x00000100, 0x00000000)) {
-               NV_ERROR(drm, "hwsq ucode exec timed out\n");
-               NV_ERROR(drm, "0x001308: 0x%08x\n", nv_rd32(device, 0x001308));
-               for (i = 0; i < hwsq->len / 4; i++) {
-                       NV_ERROR(drm, "0x%06x: 0x%08x\n", 0x1400 + (i * 4),
-                                nv_rd32(device, 0x001400 + (i * 4)));
-               }
-
-               return -EIO;
-       }
-
-       return 0;
-}
-
-int
-nv50_pm_clocks_set(struct drm_device *dev, void *data)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nv50_pm_state *info = data;
-       struct bit_entry M;
-       int ret = -EBUSY;
-
-       /* halt and idle execution engines */
-       nv_mask(device, 0x002504, 0x00000001, 0x00000001);
-       if (!nv_wait(device, 0x002504, 0x00000010, 0x00000010))
-               goto resume;
-       if (!nv_wait(device, 0x00251c, 0x0000003f, 0x0000003f))
-               goto resume;
-
-       /* program memory clock, if necessary - must come before engine clock
-        * reprogramming due to how we construct the hwsq scripts in pre()
-        */
-#define nouveau_bios_init_exec(a,b) nouveau_bios_run_init_table((a), (b), NULL, 0)
-       if (info->mclk_hwsq.len) {
-               /* execute some scripts that do ??? from the vbios.. */
-               if (!bit_table(dev, 'M', &M) && M.version == 1) {
-                       if (M.length >= 6)
-                               nouveau_bios_init_exec(dev, ROM16(M.data[5]));
-                       if (M.length >= 8)
-                               nouveau_bios_init_exec(dev, ROM16(M.data[7]));
-                       if (M.length >= 10)
-                               nouveau_bios_init_exec(dev, ROM16(M.data[9]));
-                       nouveau_bios_init_exec(dev, info->mscript);
-               }
-
-               ret = prog_hwsq(dev, &info->mclk_hwsq);
-               if (ret)
-                       goto resume;
-       }
-
-       /* program engine clocks */
-       ret = prog_hwsq(dev, &info->eclk_hwsq);
-
-resume:
-       nv_mask(device, 0x002504, 0x00000001, 0x00000000);
-       kfree(info);
-       return ret;
-}
diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c
deleted file mode 100644 (file)
index 0d0ed59..0000000
+++ /dev/null
@@ -1,624 +0,0 @@
-/*
- * Copyright 2010 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-
-#include <drm/drmP.h>
-#include "nouveau_drm.h"
-#include "nouveau_bios.h"
-#include "nouveau_pm.h"
-
-#include <subdev/bios/pll.h>
-#include <subdev/bios.h>
-#include <subdev/clock.h>
-#include <subdev/timer.h>
-#include <subdev/fb.h>
-
-static u32 read_clk(struct drm_device *, int, bool);
-static u32 read_pll(struct drm_device *, int, u32);
-
-static u32
-read_vco(struct drm_device *dev, int clk)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 sctl = nv_rd32(device, 0x4120 + (clk * 4));
-       if ((sctl & 0x00000030) != 0x00000030)
-               return read_pll(dev, 0x41, 0x00e820);
-       return read_pll(dev, 0x42, 0x00e8a0);
-}
-
-static u32
-read_clk(struct drm_device *dev, int clk, bool ignore_en)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       u32 sctl, sdiv, sclk;
-
-       /* refclk for the 0xe8xx plls is a fixed frequency */
-       if (clk >= 0x40) {
-               if (nv_device(drm->device)->chipset == 0xaf) {
-                       /* no joke.. seriously.. sigh.. */
-                       return nv_rd32(device, 0x00471c) * 1000;
-               }
-
-               return device->crystal;
-       }
-
-       sctl = nv_rd32(device, 0x4120 + (clk * 4));
-       if (!ignore_en && !(sctl & 0x00000100))
-               return 0;
-
-       switch (sctl & 0x00003000) {
-       case 0x00000000:
-               return device->crystal;
-       case 0x00002000:
-               if (sctl & 0x00000040)
-                       return 108000;
-               return 100000;
-       case 0x00003000:
-               sclk = read_vco(dev, clk);
-               sdiv = ((sctl & 0x003f0000) >> 16) + 2;
-               return (sclk * 2) / sdiv;
-       default:
-               return 0;
-       }
-}
-
-static u32
-read_pll(struct drm_device *dev, int clk, u32 pll)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 ctrl = nv_rd32(device, pll + 0);
-       u32 sclk = 0, P = 1, N = 1, M = 1;
-
-       if (!(ctrl & 0x00000008)) {
-               if (ctrl & 0x00000001) {
-                       u32 coef = nv_rd32(device, pll + 4);
-                       M = (coef & 0x000000ff) >> 0;
-                       N = (coef & 0x0000ff00) >> 8;
-                       P = (coef & 0x003f0000) >> 16;
-
-                       /* no post-divider on these.. */
-                       if ((pll & 0x00ff00) == 0x00e800)
-                               P = 1;
-
-                       sclk = read_clk(dev, 0x00 + clk, false);
-               }
-       } else {
-               sclk = read_clk(dev, 0x10 + clk, false);
-       }
-
-       if (M * P)
-               return sclk * N / (M * P);
-       return 0;
-}
-
-struct creg {
-       u32 clk;
-       u32 pll;
-};
-
-static int
-calc_clk(struct drm_device *dev, int clk, u32 pll, u32 khz, struct creg *reg)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_bios *bios = nouveau_bios(device);
-       struct nvbios_pll limits;
-       u32 oclk, sclk, sdiv;
-       int P, N, M, diff;
-       int ret;
-
-       reg->pll = 0;
-       reg->clk = 0;
-       if (!khz) {
-               NV_DEBUG(drm, "no clock for 0x%04x/0x%02x\n", pll, clk);
-               return 0;
-       }
-
-       switch (khz) {
-       case 27000:
-               reg->clk = 0x00000100;
-               return khz;
-       case 100000:
-               reg->clk = 0x00002100;
-               return khz;
-       case 108000:
-               reg->clk = 0x00002140;
-               return khz;
-       default:
-               sclk = read_vco(dev, clk);
-               sdiv = min((sclk * 2) / (khz - 2999), (u32)65);
-               /* if the clock has a PLL attached, and we can get a within
-                * [-2, 3) MHz of a divider, we'll disable the PLL and use
-                * the divider instead.
-                *
-                * divider can go as low as 2, limited here because NVIDIA
-                * and the VBIOS on my NVA8 seem to prefer using the PLL
-                * for 810MHz - is there a good reason?
-                */
-               if (sdiv > 4) {
-                       oclk = (sclk * 2) / sdiv;
-                       diff = khz - oclk;
-                       if (!pll || (diff >= -2000 && diff < 3000)) {
-                               reg->clk = (((sdiv - 2) << 16) | 0x00003100);
-                               return oclk;
-                       }
-               }
-
-               if (!pll) {
-                       NV_ERROR(drm, "bad freq %02x: %d %d\n", clk, khz, sclk);
-                       return -ERANGE;
-               }
-
-               break;
-       }
-
-       ret = nvbios_pll_parse(bios, pll, &limits);
-       if (ret)
-               return ret;
-
-       limits.refclk = read_clk(dev, clk - 0x10, true);
-       if (!limits.refclk)
-               return -EINVAL;
-
-       ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P);
-       if (ret >= 0) {
-               reg->clk = nv_rd32(device, 0x4120 + (clk * 4));
-               reg->pll = (P << 16) | (N << 8) | M;
-       }
-
-       return ret;
-}
-
-static void
-prog_pll(struct drm_device *dev, int clk, u32 pll, struct creg *reg)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       const u32 src0 = 0x004120 + (clk * 4);
-       const u32 src1 = 0x004160 + (clk * 4);
-       const u32 ctrl = pll + 0;
-       const u32 coef = pll + 4;
-
-       if (!reg->clk && !reg->pll) {
-               NV_DEBUG(drm, "no clock for %02x\n", clk);
-               return;
-       }
-
-       if (reg->pll) {
-               nv_mask(device, src0, 0x00000101, 0x00000101);
-               nv_wr32(device, coef, reg->pll);
-               nv_mask(device, ctrl, 0x00000015, 0x00000015);
-               nv_mask(device, ctrl, 0x00000010, 0x00000000);
-               nv_wait(device, ctrl, 0x00020000, 0x00020000);
-               nv_mask(device, ctrl, 0x00000010, 0x00000010);
-               nv_mask(device, ctrl, 0x00000008, 0x00000000);
-               nv_mask(device, src1, 0x00000100, 0x00000000);
-               nv_mask(device, src1, 0x00000001, 0x00000000);
-       } else {
-               nv_mask(device, src1, 0x003f3141, 0x00000101 | reg->clk);
-               nv_mask(device, ctrl, 0x00000018, 0x00000018);
-               udelay(20);
-               nv_mask(device, ctrl, 0x00000001, 0x00000000);
-               nv_mask(device, src0, 0x00000100, 0x00000000);
-               nv_mask(device, src0, 0x00000001, 0x00000000);
-       }
-}
-
-static void
-prog_clk(struct drm_device *dev, int clk, struct creg *reg)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       if (!reg->clk) {
-               NV_DEBUG(drm, "no clock for %02x\n", clk);
-               return;
-       }
-
-       nv_mask(device, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | reg->clk);
-}
-
-int
-nva3_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       perflvl->core   = read_pll(dev, 0x00, 0x4200);
-       perflvl->shader = read_pll(dev, 0x01, 0x4220);
-       perflvl->memory = read_pll(dev, 0x02, 0x4000);
-       perflvl->unka0  = read_clk(dev, 0x20, false);
-       perflvl->vdec   = read_clk(dev, 0x21, false);
-       perflvl->daemon = read_clk(dev, 0x25, false);
-       perflvl->copy   = perflvl->core;
-       return 0;
-}
-
-struct nva3_pm_state {
-       struct nouveau_pm_level *perflvl;
-
-       struct creg nclk;
-       struct creg sclk;
-       struct creg vdec;
-       struct creg unka0;
-
-       struct creg mclk;
-       u8 *rammap;
-       u8  rammap_ver;
-       u8  rammap_len;
-       u8 *ramcfg;
-       u8  ramcfg_len;
-       u32 r004018;
-       u32 r100760;
-};
-
-void *
-nva3_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       struct nva3_pm_state *info;
-       u8 ramcfg_cnt;
-       int ret;
-
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return ERR_PTR(-ENOMEM);
-
-       ret = calc_clk(dev, 0x10, 0x4200, perflvl->core, &info->nclk);
-       if (ret < 0)
-               goto out;
-
-       ret = calc_clk(dev, 0x11, 0x4220, perflvl->shader, &info->sclk);
-       if (ret < 0)
-               goto out;
-
-       ret = calc_clk(dev, 0x12, 0x4000, perflvl->memory, &info->mclk);
-       if (ret < 0)
-               goto out;
-
-       ret = calc_clk(dev, 0x20, 0x0000, perflvl->unka0, &info->unka0);
-       if (ret < 0)
-               goto out;
-
-       ret = calc_clk(dev, 0x21, 0x0000, perflvl->vdec, &info->vdec);
-       if (ret < 0)
-               goto out;
-
-       info->rammap = nouveau_perf_rammap(dev, perflvl->memory,
-                                          &info->rammap_ver,
-                                          &info->rammap_len,
-                                          &ramcfg_cnt, &info->ramcfg_len);
-       if (info->rammap_ver != 0x10 || info->rammap_len < 5)
-               info->rammap = NULL;
-
-       info->ramcfg = nouveau_perf_ramcfg(dev, perflvl->memory,
-                                          &info->rammap_ver,
-                                          &info->ramcfg_len);
-       if (info->rammap_ver != 0x10)
-               info->ramcfg = NULL;
-
-       info->perflvl = perflvl;
-out:
-       if (ret < 0) {
-               kfree(info);
-               info = ERR_PTR(ret);
-       }
-       return info;
-}
-
-static bool
-nva3_pm_grcp_idle(void *data)
-{
-       struct drm_device *dev = data;
-       struct nouveau_device *device = nouveau_dev(dev);
-
-       if (!(nv_rd32(device, 0x400304) & 0x00000001))
-               return true;
-       if (nv_rd32(device, 0x400308) == 0x0050001c)
-               return true;
-       return false;
-}
-
-static void
-mclk_precharge(struct nouveau_mem_exec_func *exec)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       nv_wr32(device, 0x1002d4, 0x00000001);
-}
-
-static void
-mclk_refresh(struct nouveau_mem_exec_func *exec)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       nv_wr32(device, 0x1002d0, 0x00000001);
-}
-
-static void
-mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       nv_wr32(device, 0x100210, enable ? 0x80000000 : 0x00000000);
-}
-
-static void
-mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       nv_wr32(device, 0x1002dc, enable ? 0x00000001 : 0x00000000);
-}
-
-static void
-mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       volatile u32 post = nv_rd32(device, 0); (void)post;
-       udelay((nsec + 500) / 1000);
-}
-
-static u32
-mclk_mrg(struct nouveau_mem_exec_func *exec, int mr)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       if (mr <= 1)
-               return nv_rd32(device, 0x1002c0 + ((mr - 0) * 4));
-       if (mr <= 3)
-               return nv_rd32(device, 0x1002e0 + ((mr - 2) * 4));
-       return 0;
-}
-
-static void
-mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nouveau_fb *pfb = nouveau_fb(device);
-       if (mr <= 1) {
-               if (pfb->ram->ranks > 1)
-                       nv_wr32(device, 0x1002c8 + ((mr - 0) * 4), data);
-               nv_wr32(device, 0x1002c0 + ((mr - 0) * 4), data);
-       } else
-       if (mr <= 3) {
-               if (pfb->ram->ranks > 1)
-                       nv_wr32(device, 0x1002e8 + ((mr - 2) * 4), data);
-               nv_wr32(device, 0x1002e0 + ((mr - 2) * 4), data);
-       }
-}
-
-static void
-mclk_clock_set(struct nouveau_mem_exec_func *exec)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nva3_pm_state *info = exec->priv;
-       u32 ctrl;
-
-       ctrl = nv_rd32(device, 0x004000);
-       if (!(ctrl & 0x00000008) && info->mclk.pll) {
-               nv_wr32(device, 0x004000, (ctrl |=  0x00000008));
-               nv_mask(device, 0x1110e0, 0x00088000, 0x00088000);
-               nv_wr32(device, 0x004018, 0x00001000);
-               nv_wr32(device, 0x004000, (ctrl &= ~0x00000001));
-               nv_wr32(device, 0x004004, info->mclk.pll);
-               nv_wr32(device, 0x004000, (ctrl |=  0x00000001));
-               udelay(64);
-               nv_wr32(device, 0x004018, 0x00005000 | info->r004018);
-               udelay(20);
-       } else
-       if (!info->mclk.pll) {
-               nv_mask(device, 0x004168, 0x003f3040, info->mclk.clk);
-               nv_wr32(device, 0x004000, (ctrl |= 0x00000008));
-               nv_mask(device, 0x1110e0, 0x00088000, 0x00088000);
-               nv_wr32(device, 0x004018, 0x0000d000 | info->r004018);
-       }
-
-       if (info->rammap) {
-               if (info->ramcfg && (info->rammap[4] & 0x08)) {
-                       u32 unk5a0 = (ROM16(info->ramcfg[5]) << 8) |
-                                     info->ramcfg[5];
-                       u32 unk5a4 = ROM16(info->ramcfg[7]);
-                       u32 unk804 = (info->ramcfg[9] & 0xf0) << 16 |
-                                    (info->ramcfg[3] & 0x0f) << 16 |
-                                    (info->ramcfg[9] & 0x0f) |
-                                    0x80000000;
-                       nv_wr32(device, 0x1005a0, unk5a0);
-                       nv_wr32(device, 0x1005a4, unk5a4);
-                       nv_wr32(device, 0x10f804, unk804);
-                       nv_mask(device, 0x10053c, 0x00001000, 0x00000000);
-               } else {
-                       nv_mask(device, 0x10053c, 0x00001000, 0x00001000);
-                       nv_mask(device, 0x10f804, 0x80000000, 0x00000000);
-                       nv_mask(device, 0x100760, 0x22222222, info->r100760);
-                       nv_mask(device, 0x1007a0, 0x22222222, info->r100760);
-                       nv_mask(device, 0x1007e0, 0x22222222, info->r100760);
-               }
-       }
-
-       if (info->mclk.pll) {
-               nv_mask(device, 0x1110e0, 0x00088000, 0x00011000);
-               nv_wr32(device, 0x004000, (ctrl &= ~0x00000008));
-       }
-}
-
-static void
-mclk_timing_set(struct nouveau_mem_exec_func *exec)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nva3_pm_state *info = exec->priv;
-       struct nouveau_pm_level *perflvl = info->perflvl;
-       int i;
-
-       for (i = 0; i < 9; i++)
-               nv_wr32(device, 0x100220 + (i * 4), perflvl->timing.reg[i]);
-
-       if (info->ramcfg) {
-               u32 data = (info->ramcfg[2] & 0x08) ? 0x00000000 : 0x00001000;
-               nv_mask(device, 0x100200, 0x00001000, data);
-       }
-
-       if (info->ramcfg) {
-               u32 unk714 = nv_rd32(device, 0x100714) & ~0xf0000010;
-               u32 unk718 = nv_rd32(device, 0x100718) & ~0x00000100;
-               u32 unk71c = nv_rd32(device, 0x10071c) & ~0x00000100;
-               if ( (info->ramcfg[2] & 0x20))
-                       unk714 |= 0xf0000000;
-               if (!(info->ramcfg[2] & 0x04))
-                       unk714 |= 0x00000010;
-               nv_wr32(device, 0x100714, unk714);
-
-               if (info->ramcfg[2] & 0x01)
-                       unk71c |= 0x00000100;
-               nv_wr32(device, 0x10071c, unk71c);
-
-               if (info->ramcfg[2] & 0x02)
-                       unk718 |= 0x00000100;
-               nv_wr32(device, 0x100718, unk718);
-
-               if (info->ramcfg[2] & 0x10)
-                       nv_wr32(device, 0x111100, 0x48000000); /*XXX*/
-       }
-}
-
-static void
-prog_mem(struct drm_device *dev, struct nva3_pm_state *info)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_mem_exec_func exec = {
-               .dev = dev,
-               .precharge = mclk_precharge,
-               .refresh = mclk_refresh,
-               .refresh_auto = mclk_refresh_auto,
-               .refresh_self = mclk_refresh_self,
-               .wait = mclk_wait,
-               .mrg = mclk_mrg,
-               .mrs = mclk_mrs,
-               .clock_set = mclk_clock_set,
-               .timing_set = mclk_timing_set,
-               .priv = info
-       };
-       u32 ctrl;
-
-       /* XXX: where the fuck does 750MHz come from? */
-       if (info->perflvl->memory <= 750000) {
-               info->r004018 = 0x10000000;
-               info->r100760 = 0x22222222;
-       }
-
-       ctrl = nv_rd32(device, 0x004000);
-       if (ctrl & 0x00000008) {
-               if (info->mclk.pll) {
-                       nv_mask(device, 0x004128, 0x00000101, 0x00000101);
-                       nv_wr32(device, 0x004004, info->mclk.pll);
-                       nv_wr32(device, 0x004000, (ctrl |= 0x00000001));
-                       nv_wr32(device, 0x004000, (ctrl &= 0xffffffef));
-                       nv_wait(device, 0x004000, 0x00020000, 0x00020000);
-                       nv_wr32(device, 0x004000, (ctrl |= 0x00000010));
-                       nv_wr32(device, 0x004018, 0x00005000 | info->r004018);
-                       nv_wr32(device, 0x004000, (ctrl |= 0x00000004));
-               }
-       } else {
-               u32 ssel = 0x00000101;
-               if (info->mclk.clk)
-                       ssel |= info->mclk.clk;
-               else
-                       ssel |= 0x00080000; /* 324MHz, shouldn't matter... */
-               nv_mask(device, 0x004168, 0x003f3141, ctrl);
-       }
-
-       if (info->ramcfg) {
-               if (info->ramcfg[2] & 0x10) {
-                       nv_mask(device, 0x111104, 0x00000600, 0x00000000);
-               } else {
-                       nv_mask(device, 0x111100, 0x40000000, 0x40000000);
-                       nv_mask(device, 0x111104, 0x00000180, 0x00000000);
-               }
-       }
-       if (info->rammap && !(info->rammap[4] & 0x02))
-               nv_mask(device, 0x100200, 0x00000800, 0x00000000);
-       nv_wr32(device, 0x611200, 0x00003300);
-       if (!(info->ramcfg[2] & 0x10))
-               nv_wr32(device, 0x111100, 0x4c020000); /*XXX*/
-
-       nouveau_mem_exec(&exec, info->perflvl);
-
-       nv_wr32(device, 0x611200, 0x00003330);
-       if (info->rammap && (info->rammap[4] & 0x02))
-               nv_mask(device, 0x100200, 0x00000800, 0x00000800);
-       if (info->ramcfg) {
-               if (info->ramcfg[2] & 0x10) {
-                       nv_mask(device, 0x111104, 0x00000180, 0x00000180);
-                       nv_mask(device, 0x111100, 0x40000000, 0x00000000);
-               } else {
-                       nv_mask(device, 0x111104, 0x00000600, 0x00000600);
-               }
-       }
-
-       if (info->mclk.pll) {
-               nv_mask(device, 0x004168, 0x00000001, 0x00000000);
-               nv_mask(device, 0x004168, 0x00000100, 0x00000000);
-       } else {
-               nv_mask(device, 0x004000, 0x00000001, 0x00000000);
-               nv_mask(device, 0x004128, 0x00000001, 0x00000000);
-               nv_mask(device, 0x004128, 0x00000100, 0x00000000);
-       }
-}
-
-int
-nva3_pm_clocks_set(struct drm_device *dev, void *pre_state)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nva3_pm_state *info = pre_state;
-       int ret = -EAGAIN;
-
-       /* prevent any new grctx switches from starting */
-       nv_wr32(device, 0x400324, 0x00000000);
-       nv_wr32(device, 0x400328, 0x0050001c); /* wait flag 0x1c */
-       /* wait for any pending grctx switches to complete */
-       if (!nv_wait_cb(device, nva3_pm_grcp_idle, dev)) {
-               NV_ERROR(drm, "pm: ctxprog didn't go idle\n");
-               goto cleanup;
-       }
-       /* freeze PFIFO */
-       nv_mask(device, 0x002504, 0x00000001, 0x00000001);
-       if (!nv_wait(device, 0x002504, 0x00000010, 0x00000010)) {
-               NV_ERROR(drm, "pm: fifo didn't go idle\n");
-               goto cleanup;
-       }
-
-       prog_pll(dev, 0x00, 0x004200, &info->nclk);
-       prog_pll(dev, 0x01, 0x004220, &info->sclk);
-       prog_clk(dev, 0x20, &info->unka0);
-       prog_clk(dev, 0x21, &info->vdec);
-
-       if (info->mclk.clk || info->mclk.pll)
-               prog_mem(dev, info);
-
-       ret = 0;
-
-cleanup:
-       /* unfreeze PFIFO */
-       nv_mask(device, 0x002504, 0x00000001, 0x00000000);
-       /* restore ctxprog to normal */
-       nv_wr32(device, 0x400324, 0x00000000);
-       nv_wr32(device, 0x400328, 0x0070009c); /* set flag 0x1c */
-       /* unblock it if necessary */
-       if (nv_rd32(device, 0x400308) == 0x0050001c)
-               nv_mask(device, 0x400824, 0x10000000, 0x10000000);
-       kfree(info);
-       return ret;
-}
diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c
deleted file mode 100644 (file)
index 3b7041c..0000000
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright 2011 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-
-#include "nouveau_drm.h"
-#include "nouveau_bios.h"
-#include "nouveau_pm.h"
-
-#include <subdev/bios/pll.h>
-#include <subdev/bios.h>
-#include <subdev/clock.h>
-#include <subdev/timer.h>
-#include <subdev/fb.h>
-
-static u32 read_div(struct drm_device *, int, u32, u32);
-static u32 read_pll(struct drm_device *, u32);
-
-static u32
-read_vco(struct drm_device *dev, u32 dsrc)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 ssrc = nv_rd32(device, dsrc);
-       if (!(ssrc & 0x00000100))
-               return read_pll(dev, 0x00e800);
-       return read_pll(dev, 0x00e820);
-}
-
-static u32
-read_pll(struct drm_device *dev, u32 pll)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 ctrl = nv_rd32(device, pll + 0);
-       u32 coef = nv_rd32(device, pll + 4);
-       u32 P = (coef & 0x003f0000) >> 16;
-       u32 N = (coef & 0x0000ff00) >> 8;
-       u32 M = (coef & 0x000000ff) >> 0;
-       u32 sclk, doff;
-
-       if (!(ctrl & 0x00000001))
-               return 0;
-
-       switch (pll & 0xfff000) {
-       case 0x00e000:
-               sclk = 27000;
-               P = 1;
-               break;
-       case 0x137000:
-               doff = (pll - 0x137000) / 0x20;
-               sclk = read_div(dev, doff, 0x137120, 0x137140);
-               break;
-       case 0x132000:
-               switch (pll) {
-               case 0x132000:
-                       sclk = read_pll(dev, 0x132020);
-                       break;
-               case 0x132020:
-                       sclk = read_div(dev, 0, 0x137320, 0x137330);
-                       break;
-               default:
-                       return 0;
-               }
-               break;
-       default:
-               return 0;
-       }
-
-       return sclk * N / M / P;
-}
-
-static u32
-read_div(struct drm_device *dev, int doff, u32 dsrc, u32 dctl)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 ssrc = nv_rd32(device, dsrc + (doff * 4));
-       u32 sctl = nv_rd32(device, dctl + (doff * 4));
-
-       switch (ssrc & 0x00000003) {
-       case 0:
-               if ((ssrc & 0x00030000) != 0x00030000)
-                       return 27000;
-               return 108000;
-       case 2:
-               return 100000;
-       case 3:
-               if (sctl & 0x80000000) {
-                       u32 sclk = read_vco(dev, dsrc + (doff * 4));
-                       u32 sdiv = (sctl & 0x0000003f) + 2;
-                       return (sclk * 2) / sdiv;
-               }
-
-               return read_vco(dev, dsrc + (doff * 4));
-       default:
-               return 0;
-       }
-}
-
-static u32
-read_mem(struct drm_device *dev)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 ssel = nv_rd32(device, 0x1373f0);
-       if (ssel & 0x00000001)
-               return read_div(dev, 0, 0x137300, 0x137310);
-       return read_pll(dev, 0x132000);
-}
-
-static u32
-read_clk(struct drm_device *dev, int clk)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 sctl = nv_rd32(device, 0x137250 + (clk * 4));
-       u32 ssel = nv_rd32(device, 0x137100);
-       u32 sclk, sdiv;
-
-       if (ssel & (1 << clk)) {
-               if (clk < 7)
-                       sclk = read_pll(dev, 0x137000 + (clk * 0x20));
-               else
-                       sclk = read_pll(dev, 0x1370e0);
-               sdiv = ((sctl & 0x00003f00) >> 8) + 2;
-       } else {
-               sclk = read_div(dev, clk, 0x137160, 0x1371d0);
-               sdiv = ((sctl & 0x0000003f) >> 0) + 2;
-       }
-
-       if (sctl & 0x80000000)
-               return (sclk * 2) / sdiv;
-       return sclk;
-}
-
-int
-nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       perflvl->shader = read_clk(dev, 0x00);
-       perflvl->core   = perflvl->shader / 2;
-       perflvl->memory = read_mem(dev);
-       perflvl->rop    = read_clk(dev, 0x01);
-       perflvl->hub07  = read_clk(dev, 0x02);
-       perflvl->hub06  = read_clk(dev, 0x07);
-       perflvl->hub01  = read_clk(dev, 0x08);
-       perflvl->copy   = read_clk(dev, 0x09);
-       perflvl->daemon = read_clk(dev, 0x0c);
-       perflvl->vdec   = read_clk(dev, 0x0e);
-       return 0;
-}
-
-struct nvc0_pm_clock {
-       u32 freq;
-       u32 ssel;
-       u32 mdiv;
-       u32 dsrc;
-       u32 ddiv;
-       u32 coef;
-};
-
-struct nvc0_pm_state {
-       struct nouveau_pm_level *perflvl;
-       struct nvc0_pm_clock eng[16];
-       struct nvc0_pm_clock mem;
-};
-
-static u32
-calc_div(struct drm_device *dev, int clk, u32 ref, u32 freq, u32 *ddiv)
-{
-       u32 div = min((ref * 2) / freq, (u32)65);
-       if (div < 2)
-               div = 2;
-
-       *ddiv = div - 2;
-       return (ref * 2) / div;
-}
-
-static u32
-calc_src(struct drm_device *dev, int clk, u32 freq, u32 *dsrc, u32 *ddiv)
-{
-       u32 sclk;
-
-       /* use one of the fixed frequencies if possible */
-       *ddiv = 0x00000000;
-       switch (freq) {
-       case  27000:
-       case 108000:
-               *dsrc = 0x00000000;
-               if (freq == 108000)
-                       *dsrc |= 0x00030000;
-               return freq;
-       case 100000:
-               *dsrc = 0x00000002;
-               return freq;
-       default:
-               *dsrc = 0x00000003;
-               break;
-       }
-
-       /* otherwise, calculate the closest divider */
-       sclk = read_vco(dev, clk);
-       if (clk < 7)
-               sclk = calc_div(dev, clk, sclk, freq, ddiv);
-       return sclk;
-}
-
-static u32
-calc_pll(struct drm_device *dev, int clk, u32 freq, u32 *coef)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_bios *bios = nouveau_bios(device);
-       struct nvbios_pll limits;
-       int N, M, P, ret;
-
-       ret = nvbios_pll_parse(bios, 0x137000 + (clk * 0x20), &limits);
-       if (ret)
-               return 0;
-
-       limits.refclk = read_div(dev, clk, 0x137120, 0x137140);
-       if (!limits.refclk)
-               return 0;
-
-       ret = nva3_calc_pll(dev, &limits, freq, &N, NULL, &M, &P);
-       if (ret <= 0)
-               return 0;
-
-       *coef = (P << 16) | (N << 8) | M;
-       return ret;
-}
-
-/* A (likely rather simplified and incomplete) view of the clock tree
- *
- * Key:
- *
- * S: source select
- * D: divider
- * P: pll
- * F: switch
- *
- * Engine clocks:
- *
- * 137250(D) ---- 137100(F0) ---- 137160(S)/1371d0(D) ------------------- ref
- *                      (F1) ---- 1370X0(P) ---- 137120(S)/137140(D) ---- ref
- *
- * Not all registers exist for all clocks.  For example: clocks >= 8 don't
- * have their own PLL (all tied to clock 7's PLL when in PLL mode), nor do
- * they have the divider at 1371d0, though the source selection at 137160
- * still exists.  You must use the divider at 137250 for these instead.
- *
- * Memory clock:
- *
- * TBD, read_mem() above is likely very wrong...
- *
- */
-
-static int
-calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq)
-{
-       u32 src0, div0, div1D, div1P = 0;
-       u32 clk0, clk1 = 0;
-
-       /* invalid clock domain */
-       if (!freq)
-               return 0;
-
-       /* first possible path, using only dividers */
-       clk0 = calc_src(dev, clk, freq, &src0, &div0);
-       clk0 = calc_div(dev, clk, clk0, freq, &div1D);
-
-       /* see if we can get any closer using PLLs */
-       if (clk0 != freq && (0x00004387 & (1 << clk))) {
-               if (clk < 7)
-                       clk1 = calc_pll(dev, clk, freq, &info->coef);
-               else
-                       clk1 = read_pll(dev, 0x1370e0);
-               clk1 = calc_div(dev, clk, clk1, freq, &div1P);
-       }
-
-       /* select the method which gets closest to target freq */
-       if (abs((int)freq - clk0) <= abs((int)freq - clk1)) {
-               info->dsrc = src0;
-               if (div0) {
-                       info->ddiv |= 0x80000000;
-                       info->ddiv |= div0 << 8;
-                       info->ddiv |= div0;
-               }
-               if (div1D) {
-                       info->mdiv |= 0x80000000;
-                       info->mdiv |= div1D;
-               }
-               info->ssel = 0;
-               info->freq = clk0;
-       } else {
-               if (div1P) {
-                       info->mdiv |= 0x80000000;
-                       info->mdiv |= div1P << 8;
-               }
-               info->ssel = (1 << clk);
-               info->freq = clk1;
-       }
-
-       return 0;
-}
-
-static int
-calc_mem(struct drm_device *dev, struct nvc0_pm_clock *info, u32 freq)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_bios *bios = nouveau_bios(device);
-       struct nvbios_pll pll;
-       int N, M, P, ret;
-       u32 ctrl;
-
-       /* mclk pll input freq comes from another pll, make sure it's on */
-       ctrl = nv_rd32(device, 0x132020);
-       if (!(ctrl & 0x00000001)) {
-               /* if not, program it to 567MHz.  nfi where this value comes
-                * from - it looks like it's in the pll limits table for
-                * 132000 but the binary driver ignores all my attempts to
-                * change this value.
-                */
-               nv_wr32(device, 0x137320, 0x00000103);
-               nv_wr32(device, 0x137330, 0x81200606);
-               nv_wait(device, 0x132020, 0x00010000, 0x00010000);
-               nv_wr32(device, 0x132024, 0x0001150f);
-               nv_mask(device, 0x132020, 0x00000001, 0x00000001);
-               nv_wait(device, 0x137390, 0x00020000, 0x00020000);
-               nv_mask(device, 0x132020, 0x00000004, 0x00000004);
-       }
-
-       /* for the moment, until the clock tree is better understood, use
-        * pll mode for all clock frequencies
-        */
-       ret = nvbios_pll_parse(bios, 0x132000, &pll);
-       if (ret == 0) {
-               pll.refclk = read_pll(dev, 0x132020);
-               if (pll.refclk) {
-                       ret = nva3_calc_pll(dev, &pll, freq, &N, NULL, &M, &P);
-                       if (ret > 0) {
-                               info->coef = (P << 16) | (N << 8) | M;
-                               return 0;
-                       }
-               }
-       }
-
-       return -EINVAL;
-}
-
-void *
-nvc0_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nvc0_pm_state *info;
-       int ret;
-
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return ERR_PTR(-ENOMEM);
-
-       /* NFI why this is still in the performance table, the ROPCs appear
-        * to get their clock from clock 2 ("hub07", actually hub05 on this
-        * chip, but, anyway...) as well.  nvatiming confirms hub05 and ROP
-        * are always the same freq with the binary driver even when the
-        * performance table says they should differ.
-        */
-       if (device->chipset == 0xd9)
-               perflvl->rop = 0;
-
-       if ((ret = calc_clk(dev, 0x00, &info->eng[0x00], perflvl->shader)) ||
-           (ret = calc_clk(dev, 0x01, &info->eng[0x01], perflvl->rop)) ||
-           (ret = calc_clk(dev, 0x02, &info->eng[0x02], perflvl->hub07)) ||
-           (ret = calc_clk(dev, 0x07, &info->eng[0x07], perflvl->hub06)) ||
-           (ret = calc_clk(dev, 0x08, &info->eng[0x08], perflvl->hub01)) ||
-           (ret = calc_clk(dev, 0x09, &info->eng[0x09], perflvl->copy)) ||
-           (ret = calc_clk(dev, 0x0c, &info->eng[0x0c], perflvl->daemon)) ||
-           (ret = calc_clk(dev, 0x0e, &info->eng[0x0e], perflvl->vdec))) {
-               kfree(info);
-               return ERR_PTR(ret);
-       }
-
-       if (perflvl->memory) {
-               ret = calc_mem(dev, &info->mem, perflvl->memory);
-               if (ret) {
-                       kfree(info);
-                       return ERR_PTR(ret);
-               }
-       }
-
-       info->perflvl = perflvl;
-       return info;
-}
-
-static void
-prog_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-
-       /* program dividers at 137160/1371d0 first */
-       if (clk < 7 && !info->ssel) {
-               nv_mask(device, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv);
-               nv_wr32(device, 0x137160 + (clk * 0x04), info->dsrc);
-       }
-
-       /* switch clock to non-pll mode */
-       nv_mask(device, 0x137100, (1 << clk), 0x00000000);
-       nv_wait(device, 0x137100, (1 << clk), 0x00000000);
-
-       /* reprogram pll */
-       if (clk < 7) {
-               /* make sure it's disabled first... */
-               u32 base = 0x137000 + (clk * 0x20);
-               u32 ctrl = nv_rd32(device, base + 0x00);
-               if (ctrl & 0x00000001) {
-                       nv_mask(device, base + 0x00, 0x00000004, 0x00000000);
-                       nv_mask(device, base + 0x00, 0x00000001, 0x00000000);
-               }
-               /* program it to new values, if necessary */
-               if (info->ssel) {
-                       nv_wr32(device, base + 0x04, info->coef);
-                       nv_mask(device, base + 0x00, 0x00000001, 0x00000001);
-                       nv_wait(device, base + 0x00, 0x00020000, 0x00020000);
-                       nv_mask(device, base + 0x00, 0x00020004, 0x00000004);
-               }
-       }
-
-       /* select pll/non-pll mode, and program final clock divider */
-       nv_mask(device, 0x137100, (1 << clk), info->ssel);
-       nv_wait(device, 0x137100, (1 << clk), info->ssel);
-       nv_mask(device, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv);
-}
-
-static void
-mclk_precharge(struct nouveau_mem_exec_func *exec)
-{
-}
-
-static void
-mclk_refresh(struct nouveau_mem_exec_func *exec)
-{
-}
-
-static void
-mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       nv_wr32(device, 0x10f210, enable ? 0x80000000 : 0x00000000);
-}
-
-static void
-mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable)
-{
-}
-
-static void
-mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec)
-{
-       udelay((nsec + 500) / 1000);
-}
-
-static u32
-mclk_mrg(struct nouveau_mem_exec_func *exec, int mr)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nouveau_fb *pfb = nouveau_fb(device);
-       if (pfb->ram->type != NV_MEM_TYPE_GDDR5) {
-               if (mr <= 1)
-                       return nv_rd32(device, 0x10f300 + ((mr - 0) * 4));
-               return nv_rd32(device, 0x10f320 + ((mr - 2) * 4));
-       } else {
-               if (mr == 0)
-                       return nv_rd32(device, 0x10f300 + (mr * 4));
-               else
-               if (mr <= 7)
-                       return nv_rd32(device, 0x10f32c + (mr * 4));
-               return nv_rd32(device, 0x10f34c);
-       }
-}
-
-static void
-mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nouveau_fb *pfb = nouveau_fb(device);
-       if (pfb->ram->type != NV_MEM_TYPE_GDDR5) {
-               if (mr <= 1) {
-                       nv_wr32(device, 0x10f300 + ((mr - 0) * 4), data);
-                       if (pfb->ram->ranks > 1)
-                               nv_wr32(device, 0x10f308 + ((mr - 0) * 4), data);
-               } else
-               if (mr <= 3) {
-                       nv_wr32(device, 0x10f320 + ((mr - 2) * 4), data);
-                       if (pfb->ram->ranks > 1)
-                               nv_wr32(device, 0x10f328 + ((mr - 2) * 4), data);
-               }
-       } else {
-               if      (mr ==  0) nv_wr32(device, 0x10f300 + (mr * 4), data);
-               else if (mr <=  7) nv_wr32(device, 0x10f32c + (mr * 4), data);
-               else if (mr == 15) nv_wr32(device, 0x10f34c, data);
-       }
-}
-
-static void
-mclk_clock_set(struct nouveau_mem_exec_func *exec)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nvc0_pm_state *info = exec->priv;
-       u32 ctrl = nv_rd32(device, 0x132000);
-
-       nv_wr32(device, 0x137360, 0x00000001);
-       nv_wr32(device, 0x137370, 0x00000000);
-       nv_wr32(device, 0x137380, 0x00000000);
-       if (ctrl & 0x00000001)
-               nv_wr32(device, 0x132000, (ctrl &= ~0x00000001));
-
-       nv_wr32(device, 0x132004, info->mem.coef);
-       nv_wr32(device, 0x132000, (ctrl |= 0x00000001));
-       nv_wait(device, 0x137390, 0x00000002, 0x00000002);
-       nv_wr32(device, 0x132018, 0x00005000);
-
-       nv_wr32(device, 0x137370, 0x00000001);
-       nv_wr32(device, 0x137380, 0x00000001);
-       nv_wr32(device, 0x137360, 0x00000000);
-}
-
-static void
-mclk_timing_set(struct nouveau_mem_exec_func *exec)
-{
-       struct nouveau_device *device = nouveau_dev(exec->dev);
-       struct nvc0_pm_state *info = exec->priv;
-       struct nouveau_pm_level *perflvl = info->perflvl;
-       int i;
-
-       for (i = 0; i < 5; i++)
-               nv_wr32(device, 0x10f290 + (i * 4), perflvl->timing.reg[i]);
-}
-
-static void
-prog_mem(struct drm_device *dev, struct nvc0_pm_state *info)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_mem_exec_func exec = {
-               .dev = dev,
-               .precharge = mclk_precharge,
-               .refresh = mclk_refresh,
-               .refresh_auto = mclk_refresh_auto,
-               .refresh_self = mclk_refresh_self,
-               .wait = mclk_wait,
-               .mrg = mclk_mrg,
-               .mrs = mclk_mrs,
-               .clock_set = mclk_clock_set,
-               .timing_set = mclk_timing_set,
-               .priv = info
-       };
-
-       if (device->chipset < 0xd0)
-               nv_wr32(device, 0x611200, 0x00003300);
-       else
-               nv_wr32(device, 0x62c000, 0x03030000);
-
-       nouveau_mem_exec(&exec, info->perflvl);
-
-       if (device->chipset < 0xd0)
-               nv_wr32(device, 0x611200, 0x00003330);
-       else
-               nv_wr32(device, 0x62c000, 0x03030300);
-}
-int
-nvc0_pm_clocks_set(struct drm_device *dev, void *data)
-{
-       struct nvc0_pm_state *info = data;
-       int i;
-
-       if (info->mem.coef)
-               prog_mem(dev, info);
-
-       for (i = 0; i < 16; i++) {
-               if (!info->eng[i].freq)
-                       continue;
-               prog_clk(dev, i, &info->eng[i]);
-       }
-
-       kfree(info);
-       return 0;
-}
index 20c41e7..6c220cd 100644 (file)
@@ -5,6 +5,7 @@ config DRM_OMAP
        depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM
        depends on OMAP2_DSS
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select FB_SYS_FILLRECT
        select FB_SYS_COPYAREA
        select FB_SYS_IMAGEBLIT
index 2603d90..e7fa3cd 100644 (file)
@@ -620,7 +620,6 @@ static struct drm_driver omap_drm_driver = {
                .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
                .gem_prime_export = omap_gem_prime_export,
                .gem_prime_import = omap_gem_prime_import,
-               .gem_init_object = omap_gem_init_object,
                .gem_free_object = omap_gem_free_object,
                .gem_vm_ops = &omap_gem_vm_ops,
                .dumb_create = omap_gem_dumb_create,
index 30b95b7..0784769 100644 (file)
@@ -220,7 +220,6 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
 int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
                union omap_gem_size gsize, uint32_t flags, uint32_t *handle);
 void omap_gem_free_object(struct drm_gem_object *obj);
-int omap_gem_init_object(struct drm_gem_object *obj);
 void *omap_gem_vaddr(struct drm_gem_object *obj);
 int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
                uint32_t handle, uint64_t *offset);
index 533f6eb..5aec3e8 100644 (file)
@@ -1274,11 +1274,6 @@ unlock:
        return ret;
 }
 
-int omap_gem_init_object(struct drm_gem_object *obj)
-{
-       return -EINVAL;          /* unused */
-}
-
 /* don't call directly.. called from GEM core when it is time to actually
  * free the object..
  */
index 9263db1..cb85860 100644 (file)
@@ -261,7 +261,7 @@ int omap_drm_irq_install(struct drm_device *dev)
                mutex_unlock(&dev->struct_mutex);
                return -EBUSY;
        }
-       dev->irq_enabled = 1;
+       dev->irq_enabled = true;
        mutex_unlock(&dev->struct_mutex);
 
        /* Before installing handler */
@@ -272,7 +272,7 @@ int omap_drm_irq_install(struct drm_device *dev)
 
        if (ret < 0) {
                mutex_lock(&dev->struct_mutex);
-               dev->irq_enabled = 0;
+               dev->irq_enabled = false;
                mutex_unlock(&dev->struct_mutex);
                return ret;
        }
@@ -283,7 +283,7 @@ int omap_drm_irq_install(struct drm_device *dev)
 
        if (ret < 0) {
                mutex_lock(&dev->struct_mutex);
-               dev->irq_enabled = 0;
+               dev->irq_enabled = false;
                mutex_unlock(&dev->struct_mutex);
                dispc_free_irq(dev);
        }
@@ -294,11 +294,12 @@ int omap_drm_irq_install(struct drm_device *dev)
 int omap_drm_irq_uninstall(struct drm_device *dev)
 {
        unsigned long irqflags;
-       int irq_enabled, i;
+       bool irq_enabled;
+       int i;
 
        mutex_lock(&dev->struct_mutex);
        irq_enabled = dev->irq_enabled;
-       dev->irq_enabled = 0;
+       dev->irq_enabled = false;
        mutex_unlock(&dev->struct_mutex);
 
        /*
@@ -307,9 +308,9 @@ int omap_drm_irq_uninstall(struct drm_device *dev)
        if (dev->num_crtcs) {
                spin_lock_irqsave(&dev->vbl_lock, irqflags);
                for (i = 0; i < dev->num_crtcs; i++) {
-                       DRM_WAKEUP(&dev->vbl_queue[i]);
-                       dev->vblank_enabled[i] = 0;
-                       dev->last_vblank[i] =
+                       DRM_WAKEUP(&dev->vblank[i].queue);
+                       dev->vblank[i].enabled = false;
+                       dev->vblank[i].last =
                                dev->driver->get_vblank_counter(dev, i);
                }
                spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
index d6c1279..037d324 100644 (file)
@@ -6,6 +6,7 @@ config DRM_QXL
        select FB_SYS_IMAGEBLIT
        select FB_DEFERRED_IO
         select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
         select DRM_TTM
        help
                QXL virtual GPU for Spice virtualization desktop integration. Do not enable this driver unless your distro ships a corresponding X.org QXL driver that can handle kernel modesetting.
index 835caba..5e827c2 100644 (file)
@@ -107,10 +107,17 @@ void qxl_display_read_client_monitors_config(struct qxl_device *qdev)
                qxl_io_log(qdev, "failed crc check for client_monitors_config,"
                                 " retrying\n");
        }
-       drm_helper_hpd_irq_event(qdev->ddev);
+
+       if (!drm_helper_hpd_irq_event(qdev->ddev)) {
+               /* notify that the monitor configuration changed, to
+                  adjust at the arbitrary resolution */
+               drm_kms_helper_hotplug_event(qdev->ddev);
+       }
 }
 
-static int qxl_add_monitors_config_modes(struct drm_connector *connector)
+static int qxl_add_monitors_config_modes(struct drm_connector *connector,
+                                         unsigned *pwidth,
+                                         unsigned *pheight)
 {
        struct drm_device *dev = connector->dev;
        struct qxl_device *qdev = dev->dev_private;
@@ -126,11 +133,15 @@ static int qxl_add_monitors_config_modes(struct drm_connector *connector)
        mode = drm_cvt_mode(dev, head->width, head->height, 60, false, false,
                            false);
        mode->type |= DRM_MODE_TYPE_PREFERRED;
+       *pwidth = head->width;
+       *pheight = head->height;
        drm_mode_probed_add(connector, mode);
        return 1;
 }
 
-static int qxl_add_common_modes(struct drm_connector *connector)
+static int qxl_add_common_modes(struct drm_connector *connector,
+                                unsigned pwidth,
+                                unsigned pheight)
 {
        struct drm_device *dev = connector->dev;
        struct drm_display_mode *mode = NULL;
@@ -159,12 +170,9 @@ static int qxl_add_common_modes(struct drm_connector *connector)
        };
 
        for (i = 0; i < ARRAY_SIZE(common_modes); i++) {
-               if (common_modes[i].w < 320 || common_modes[i].h < 200)
-                       continue;
-
                mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h,
                                    60, false, false, false);
-               if (common_modes[i].w == 1024 && common_modes[i].h == 768)
+               if (common_modes[i].w == pwidth && common_modes[i].h == pheight)
                        mode->type |= DRM_MODE_TYPE_PREFERRED;
                drm_mode_probed_add(connector, mode);
        }
@@ -720,16 +728,18 @@ static int qxl_conn_get_modes(struct drm_connector *connector)
 {
        int ret = 0;
        struct qxl_device *qdev = connector->dev->dev_private;
+       unsigned pwidth = 1024;
+       unsigned pheight = 768;
 
        DRM_DEBUG_KMS("monitors_config=%p\n", qdev->monitors_config);
        /* TODO: what should we do here? only show the configured modes for the
         * device, or allow the full list, or both? */
        if (qdev->monitors_config && qdev->monitors_config->count) {
-               ret = qxl_add_monitors_config_modes(connector);
+               ret = qxl_add_monitors_config_modes(connector, &pwidth, &pheight);
                if (ret < 0)
                        return ret;
        }
-       ret += qxl_add_common_modes(connector);
+       ret += qxl_add_common_modes(connector, pwidth, pheight);
        return ret;
 }
 
@@ -793,7 +803,10 @@ static enum drm_connector_status qxl_conn_detect(
                     qdev->client_monitors_config->count > output->index &&
                     qxl_head_enabled(&qdev->client_monitors_config->heads[output->index]));
 
-       DRM_DEBUG("\n");
+       DRM_DEBUG("#%d connected: %d\n", output->index, connected);
+       if (!connected)
+               qxl_monitors_config_set(qdev, output->index, 0, 0, 0, 0, 0);
+
        return connected ? connector_status_connected
                         : connector_status_disconnected;
 }
@@ -835,8 +848,21 @@ static const struct drm_encoder_funcs qxl_enc_funcs = {
        .destroy = qxl_enc_destroy,
 };
 
+static int qxl_mode_create_hotplug_mode_update_property(struct qxl_device *qdev)
+{
+       if (qdev->hotplug_mode_update_property)
+               return 0;
+
+       qdev->hotplug_mode_update_property =
+               drm_property_create_range(qdev->ddev, DRM_MODE_PROP_IMMUTABLE,
+                                         "hotplug_mode_update", 0, 1);
+
+       return 0;
+}
+
 static int qdev_output_init(struct drm_device *dev, int num_output)
 {
+       struct qxl_device *qdev = dev->dev_private;
        struct qxl_output *qxl_output;
        struct drm_connector *connector;
        struct drm_encoder *encoder;
@@ -863,6 +889,8 @@ static int qdev_output_init(struct drm_device *dev, int num_output)
        drm_encoder_helper_add(encoder, &qxl_enc_helper_funcs);
        drm_connector_helper_add(connector, &qxl_connector_helper_funcs);
 
+       drm_object_attach_property(&connector->base,
+                                  qdev->hotplug_mode_update_property, 0);
        drm_sysfs_connector_add(connector);
        return 0;
 }
@@ -975,6 +1003,9 @@ int qxl_modeset_init(struct qxl_device *qdev)
        qdev->ddev->mode_config.max_height = 8192;
 
        qdev->ddev->mode_config.fb_base = qdev->vram_base;
+
+       qxl_mode_create_hotplug_mode_update_property(qdev);
+
        for (i = 0 ; i < qxl_num_crtc; ++i) {
                qdev_crtc_init(qdev->ddev, i);
                qdev_output_init(qdev->ddev, i);
index 514118a..fee8748 100644 (file)
@@ -225,7 +225,6 @@ static struct drm_driver qxl_driver = {
        .debugfs_init = qxl_debugfs_init,
        .debugfs_cleanup = qxl_debugfs_takedown,
 #endif
-       .gem_init_object = qxl_gem_object_init,
        .gem_free_object = qxl_gem_object_free,
        .gem_open_object = qxl_gem_object_open,
        .gem_close_object = qxl_gem_object_close,
index f7c9add..7bda32f 100644 (file)
@@ -323,6 +323,8 @@ struct qxl_device {
        struct work_struct gc_work;
 
        struct work_struct fb_work;
+
+       struct drm_property *hotplug_mode_update_property;
 };
 
 /* forward declaration for QXL_INFO_IO */
@@ -412,7 +414,6 @@ int qxl_gem_object_create_with_handle(struct qxl_device *qdev,
                                      struct qxl_surface *surf,
                                      struct qxl_bo **qobj,
                                      uint32_t *handle);
-int qxl_gem_object_init(struct drm_gem_object *obj);
 void qxl_gem_object_free(struct drm_gem_object *gobj);
 int qxl_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_priv);
 void qxl_gem_object_close(struct drm_gem_object *obj,
index 88722f2..f437b30 100644 (file)
@@ -108,7 +108,7 @@ static void qxl_fb_dirty_flush(struct fb_info *info)
        u32 x1, x2, y1, y2;
 
        /* TODO: hard coding 32 bpp */
-       int stride = qfbdev->qfb.base.pitches[0] * 4;
+       int stride = qfbdev->qfb.base.pitches[0];
 
        x1 = qfbdev->dirty.x1;
        x2 = qfbdev->dirty.x2;
index 1648e41..b96f0c9 100644 (file)
 #include "qxl_drv.h"
 #include "qxl_object.h"
 
-int qxl_gem_object_init(struct drm_gem_object *obj)
-{
-       /* we do nothings here */
-       return 0;
-}
-
 void qxl_gem_object_free(struct drm_gem_object *gobj)
 {
        struct qxl_bo *qobj = gem_to_qxl_bo(gobj);
index 9e8da9e..e5ca498 100644 (file)
@@ -120,7 +120,7 @@ int qxl_device_init(struct qxl_device *qdev,
                    struct pci_dev *pdev,
                    unsigned long flags)
 {
-       int r;
+       int r, sb;
 
        qdev->dev = &pdev->dev;
        qdev->ddev = ddev;
@@ -136,21 +136,39 @@ int qxl_device_init(struct qxl_device *qdev,
        qdev->rom_base = pci_resource_start(pdev, 2);
        qdev->rom_size = pci_resource_len(pdev, 2);
        qdev->vram_base = pci_resource_start(pdev, 0);
-       qdev->surfaceram_base = pci_resource_start(pdev, 1);
-       qdev->surfaceram_size = pci_resource_len(pdev, 1);
        qdev->io_base = pci_resource_start(pdev, 3);
 
        qdev->vram_mapping = io_mapping_create_wc(qdev->vram_base, pci_resource_len(pdev, 0));
-       qdev->surface_mapping = io_mapping_create_wc(qdev->surfaceram_base, qdev->surfaceram_size);
-       DRM_DEBUG_KMS("qxl: vram %llx-%llx(%dM %dk), surface %llx-%llx(%dM %dk)\n",
+
+       if (pci_resource_len(pdev, 4) > 0) {
+               /* 64bit surface bar present */
+               sb = 4;
+               qdev->surfaceram_base = pci_resource_start(pdev, sb);
+               qdev->surfaceram_size = pci_resource_len(pdev, sb);
+               qdev->surface_mapping =
+                       io_mapping_create_wc(qdev->surfaceram_base,
+                                            qdev->surfaceram_size);
+       }
+       if (qdev->surface_mapping == NULL) {
+               /* 64bit surface bar not present (or mapping failed) */
+               sb = 1;
+               qdev->surfaceram_base = pci_resource_start(pdev, sb);
+               qdev->surfaceram_size = pci_resource_len(pdev, sb);
+               qdev->surface_mapping =
+                       io_mapping_create_wc(qdev->surfaceram_base,
+                                            qdev->surfaceram_size);
+       }
+
+       DRM_DEBUG_KMS("qxl: vram %llx-%llx(%dM %dk), surface %llx-%llx(%dM %dk, %s)\n",
                 (unsigned long long)qdev->vram_base,
                 (unsigned long long)pci_resource_end(pdev, 0),
                 (int)pci_resource_len(pdev, 0) / 1024 / 1024,
                 (int)pci_resource_len(pdev, 0) / 1024,
                 (unsigned long long)qdev->surfaceram_base,
-                (unsigned long long)pci_resource_end(pdev, 1),
+                (unsigned long long)pci_resource_end(pdev, sb),
                 (int)qdev->surfaceram_size / 1024 / 1024,
-                (int)qdev->surfaceram_size / 1024);
+                (int)qdev->surfaceram_size / 1024,
+                (sb == 4) ? "64bit" : "32bit");
 
        qdev->rom = ioremap(qdev->rom_base, qdev->rom_size);
        if (!qdev->rom) {
@@ -230,9 +248,13 @@ int qxl_device_init(struct qxl_device *qdev,
        qdev->surfaces_mem_slot = setup_slot(qdev, 1,
                (unsigned long)qdev->surfaceram_base,
                (unsigned long)qdev->surfaceram_base + qdev->surfaceram_size);
-       DRM_INFO("main mem slot %d [%lx,%x)\n",
-               qdev->main_mem_slot,
-               (unsigned long)qdev->vram_base, qdev->rom->ram_header_offset);
+       DRM_INFO("main mem slot %d [%lx,%x]\n",
+                qdev->main_mem_slot,
+                (unsigned long)qdev->vram_base, qdev->rom->ram_header_offset);
+       DRM_INFO("surface mem slot %d [%lx,%lx]\n",
+                qdev->surfaces_mem_slot,
+                (unsigned long)qdev->surfaceram_base,
+                (unsigned long)qdev->surfaceram_size);
 
 
        qdev->gc_queue = create_singlethread_workqueue("qxl_gc");
index 037786d..c7e7e65 100644 (file)
@@ -516,6 +516,8 @@ int qxl_ttm_init(struct qxl_device *qdev)
                 (unsigned)qdev->vram_size / (1024 * 1024));
        DRM_INFO("qxl: %luM of IO pages memory ready (VRAM domain)\n",
                 ((unsigned)num_io_pages * PAGE_SIZE) / (1024 * 1024));
+       DRM_INFO("qxl: %uM of Surface memory size\n",
+                (unsigned)qdev->surfaceram_size / (1024 * 1024));
        if (unlikely(qdev->mman.bdev.dev_mapping == NULL))
                qdev->mman.bdev.dev_mapping = qdev->ddev->dev_mapping;
        r = qxl_ttm_debugfs_init(qdev);
index af10f85..92be50c 100644 (file)
@@ -1711,7 +1711,9 @@ typedef struct _PIXEL_CLOCK_PARAMETERS_V6
 #define PIXEL_CLOCK_V6_MISC_HDMI_BPP_MASK           0x0c
 #define PIXEL_CLOCK_V6_MISC_HDMI_24BPP              0x00
 #define PIXEL_CLOCK_V6_MISC_HDMI_36BPP              0x04
+#define PIXEL_CLOCK_V6_MISC_HDMI_36BPP_V6           0x08    //for V6, the correct defintion for 36bpp should be 2 for 36bpp(2:1)
 #define PIXEL_CLOCK_V6_MISC_HDMI_30BPP              0x08
+#define PIXEL_CLOCK_V6_MISC_HDMI_30BPP_V6           0x04    //for V6, the correct defintion for 30bpp should be 1 for 36bpp(5:4)
 #define PIXEL_CLOCK_V6_MISC_HDMI_48BPP              0x0c
 #define PIXEL_CLOCK_V6_MISC_REF_DIV_SRC             0x10
 #define PIXEL_CLOCK_V6_MISC_GEN_DPREFCLK            0x40
@@ -2223,7 +2225,7 @@ typedef struct    _SET_VOLTAGE_PARAMETERS_V2
   USHORT   usVoltageLevel;              // real voltage level
 }SET_VOLTAGE_PARAMETERS_V2;
 
-
+// used by both SetVoltageTable v1.3 and v1.4
 typedef struct _SET_VOLTAGE_PARAMETERS_V1_3
 {
   UCHAR    ucVoltageType;               // To tell which voltage to set up, VDDC/MVDDC/MVDDQ/VDDCI
@@ -2290,15 +2292,36 @@ typedef struct  _GET_LEAKAGE_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1
 #define        ATOM_GET_VOLTAGE_VID                0x00
 #define ATOM_GET_VOTLAGE_INIT_SEQ           0x03
 #define ATOM_GET_VOLTTAGE_PHASE_PHASE_VID   0x04
-// for SI, this state map to 0xff02 voltage state in Power Play table, which is power boost state
-#define        ATOM_GET_VOLTAGE_STATE0_LEAKAGE_VID 0x10
+#define ATOM_GET_VOLTAGE_SVID2              0x07        //Get SVI2 Regulator Info
 
+// for SI, this state map to 0xff02 voltage state in Power Play table, which is power boost state
+#define ATOM_GET_VOLTAGE_STATE0_LEAKAGE_VID 0x10
 // for SI, this state map to 0xff01 voltage state in Power Play table, which is performance state
 #define        ATOM_GET_VOLTAGE_STATE1_LEAKAGE_VID 0x11
-// undefined power state
+
 #define        ATOM_GET_VOLTAGE_STATE2_LEAKAGE_VID 0x12
 #define        ATOM_GET_VOLTAGE_STATE3_LEAKAGE_VID 0x13
 
+// New Added from CI Hawaii for GetVoltageInfoTable, input parameter structure
+typedef struct  _GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_2
+{
+  UCHAR    ucVoltageType;               // Input: To tell which voltage to set up, VDDC/MVDDC/MVDDQ/VDDCI
+  UCHAR    ucVoltageMode;               // Input: Indicate action: Get voltage info
+  USHORT   usVoltageLevel;              // Input: real voltage level in unit of mv or Voltage Phase (0, 1, 2, .. ) or Leakage Id 
+  ULONG    ulSCLKFreq;                  // Input: when ucVoltageMode= ATOM_GET_VOLTAGE_EVV_VOLTAGE, DPM state SCLK frequency, Define in PPTable SCLK/Voltage dependence table
+}GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_2;
+
+// New in GetVoltageInfo v1.2 ucVoltageMode
+#define ATOM_GET_VOLTAGE_EVV_VOLTAGE        0x09        
+
+// New Added from CI Hawaii for EVV feature 
+typedef struct  _GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_2
+{
+  USHORT   usVoltageLevel;                               // real voltage level in unit of mv
+  USHORT   usVoltageId;                                  // Voltage Id programmed in Voltage Regulator
+  ULONG    ulReseved;
+}GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_2;
+
 /****************************************************************************/ 
 // Structures used by TVEncoderControlTable
 /****************************************************************************/ 
@@ -3864,6 +3887,8 @@ typedef struct _ATOM_GPIO_PIN_ASSIGNMENT
 #define PP_AC_DC_SWITCH_GPIO_PINID          60
 //from SMU7.x, if ucGPIO_ID=VDDC_REGULATOR_VRHOT_GPIO_PINID in GPIO_LUTable, VRHot feature is enable
 #define VDDC_VRHOT_GPIO_PINID               61
+//if ucGPIO_ID=VDDC_PCC_GPIO_PINID in GPIO_LUTable, Peak Current Control feature is enabled
+#define VDDC_PCC_GPIO_PINID                 62
 
 typedef struct _ATOM_GPIO_PIN_LUT
 {
@@ -4169,10 +4194,10 @@ typedef struct _ATOM_COMMON_RECORD_HEADER
 #define ATOM_OBJECT_LINK_RECORD_TYPE                   18 //Once this record is present under one object, it indicats the oobject is linked to another obj described by the record
 #define ATOM_CONNECTOR_REMOTE_CAP_RECORD_TYPE          19
 #define ATOM_ENCODER_CAP_RECORD_TYPE                   20
-
+#define ATOM_BRACKET_LAYOUT_RECORD_TYPE                21
 
 //Must be updated when new record type is added,equal to that record definition!
-#define ATOM_MAX_OBJECT_RECORD_NUMBER             ATOM_ENCODER_CAP_RECORD_TYPE
+#define ATOM_MAX_OBJECT_RECORD_NUMBER             ATOM_BRACKET_LAYOUT_RECORD_TYPE
 
 typedef struct  _ATOM_I2C_RECORD
 {
@@ -4397,6 +4422,31 @@ typedef struct _ATOM_CONNECTOR_REMOTE_CAP_RECORD
   USHORT                      usReserved;
 }ATOM_CONNECTOR_REMOTE_CAP_RECORD;
 
+typedef struct  _ATOM_CONNECTOR_LAYOUT_INFO
+{
+   USHORT usConnectorObjectId;
+   UCHAR  ucConnectorType;
+   UCHAR  ucPosition;
+}ATOM_CONNECTOR_LAYOUT_INFO;
+
+// define ATOM_CONNECTOR_LAYOUT_INFO.ucConnectorType to describe the display connector size
+#define CONNECTOR_TYPE_DVI_D                 1
+#define CONNECTOR_TYPE_DVI_I                 2
+#define CONNECTOR_TYPE_VGA                   3
+#define CONNECTOR_TYPE_HDMI                  4
+#define CONNECTOR_TYPE_DISPLAY_PORT          5
+#define CONNECTOR_TYPE_MINI_DISPLAY_PORT     6
+
+typedef struct  _ATOM_BRACKET_LAYOUT_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  UCHAR                       ucLength;
+  UCHAR                       ucWidth;
+  UCHAR                       ucConnNum;
+  UCHAR                       ucReserved;
+  ATOM_CONNECTOR_LAYOUT_INFO  asConnInfo[1];
+}ATOM_BRACKET_LAYOUT_RECORD;
+
 /****************************************************************************/ 
 // ASIC voltage data table
 /****************************************************************************/ 
@@ -4524,8 +4574,9 @@ typedef struct _ATOM_VOLTAGE_OBJECT_HEADER_V3{
 #define VOLTAGE_OBJ_VR_I2C_INIT_SEQ          3        //VOLTAGE REGULATOR INIT sequece through I2C -> ATOM_I2C_VOLTAGE_OBJECT_V3
 #define VOLTAGE_OBJ_PHASE_LUT                4        //Set Vregulator Phase lookup table ->ATOM_GPIO_VOLTAGE_OBJECT_V3
 #define VOLTAGE_OBJ_SVID2                    7        //Indicate voltage control by SVID2 ->ATOM_SVID2_VOLTAGE_OBJECT_V3
-#define        VOLTAGE_OBJ_PWRBOOST_LEAKAGE_LUT     0x10     //Powerboost Voltage and LeakageId lookup table->ATOM_LEAKAGE_VOLTAGE_OBJECT_V3
-#define        VOLTAGE_OBJ_HIGH_STATE_LEAKAGE_LUT   0x11     //High voltage state Voltage and LeakageId lookup table->ATOM_LEAKAGE_VOLTAGE_OBJECT_V3
+#define VOLTAGE_OBJ_EVV                      8 
+#define VOLTAGE_OBJ_PWRBOOST_LEAKAGE_LUT     0x10     //Powerboost Voltage and LeakageId lookup table->ATOM_LEAKAGE_VOLTAGE_OBJECT_V3
+#define VOLTAGE_OBJ_HIGH_STATE_LEAKAGE_LUT   0x11     //High voltage state Voltage and LeakageId lookup table->ATOM_LEAKAGE_VOLTAGE_OBJECT_V3
 #define VOLTAGE_OBJ_HIGH1_STATE_LEAKAGE_LUT  0x12     //High1 voltage state Voltage and LeakageId lookup table->ATOM_LEAKAGE_VOLTAGE_OBJECT_V3
 
 typedef struct  _VOLTAGE_LUT_ENTRY_V2
@@ -4552,6 +4603,10 @@ typedef struct  _ATOM_I2C_VOLTAGE_OBJECT_V3
    VOLTAGE_LUT_ENTRY asVolI2cLut[1];        // end with 0xff
 }ATOM_I2C_VOLTAGE_OBJECT_V3;
 
+// ATOM_I2C_VOLTAGE_OBJECT_V3.ucVoltageControlFlag
+#define VOLTAGE_DATA_ONE_BYTE                0
+#define VOLTAGE_DATA_TWO_BYTE                1
+
 typedef struct  _ATOM_GPIO_VOLTAGE_OBJECT_V3
 {
    ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;   // voltage mode = VOLTAGE_OBJ_GPIO_LUT or VOLTAGE_OBJ_PHASE_LUT
@@ -4584,7 +4639,8 @@ typedef struct  _ATOM_SVID2_VOLTAGE_OBJECT_V3
 // 1:0 \96 offset trim, 
    USHORT   usLoadLine_PSI;    
 // GPU GPIO pin Id to SVID2 regulator VRHot pin. possible value 0~31. 0 means GPIO0, 31 means GPIO31
-   UCHAR    ucReserved[2];
+   UCHAR    ucSVDGpioId;     //0~31 indicate GPIO0~31
+   UCHAR    ucSVCGpioId;     //0~31 indicate GPIO0~31
    ULONG    ulReserved;
 }ATOM_SVID2_VOLTAGE_OBJECT_V3;
 
@@ -4637,6 +4693,49 @@ typedef struct  _ATOM_ASIC_PROFILING_INFO_V2_1
   USHORT usElbVDDCI_LevelArrayOffset;    // offset of 2 dimension voltage level USHORT array
 }ATOM_ASIC_PROFILING_INFO_V2_1;
 
+typedef struct  _ATOM_ASIC_PROFILING_INFO_V3_1
+{
+  ATOM_COMMON_TABLE_HEADER         asHeader; 
+  ULONG  ulEvvDerateTdp;
+  ULONG  ulEvvDerateTdc;
+  ULONG  ulBoardCoreTemp;
+  ULONG  ulMaxVddc;
+  ULONG  ulMinVddc;
+  ULONG  ulLoadLineSlop;
+  ULONG  ulLeakageTemp;
+  ULONG  ulLeakageVoltage;
+  ULONG  ulCACmEncodeRange;
+  ULONG  ulCACmEncodeAverage;
+  ULONG  ulCACbEncodeRange;
+  ULONG  ulCACbEncodeAverage;
+  ULONG  ulKt_bEncodeRange;
+  ULONG  ulKt_bEncodeAverage;
+  ULONG  ulKv_mEncodeRange;
+  ULONG  ulKv_mEncodeAverage;
+  ULONG  ulKv_bEncodeRange;
+  ULONG  ulKv_bEncodeAverage;
+  ULONG  ulLkgEncodeLn_MaxDivMin;
+  ULONG  ulLkgEncodeMin;
+  ULONG  ulEfuseLogisticAlpha;
+  USHORT usPowerDpm0;
+  USHORT usCurrentDpm0;
+  USHORT usPowerDpm1;
+  USHORT usCurrentDpm1;
+  USHORT usPowerDpm2;
+  USHORT usCurrentDpm2;
+  USHORT usPowerDpm3;
+  USHORT usCurrentDpm3;
+  USHORT usPowerDpm4;
+  USHORT usCurrentDpm4;
+  USHORT usPowerDpm5;
+  USHORT usCurrentDpm5;
+  USHORT usPowerDpm6;
+  USHORT usCurrentDpm6;
+  USHORT usPowerDpm7;
+  USHORT usCurrentDpm7;
+}ATOM_ASIC_PROFILING_INFO_V3_1;
+
+
 typedef struct _ATOM_POWER_SOURCE_OBJECT
 {
        UCHAR   ucPwrSrcId;                                                                                                     // Power source
@@ -5808,6 +5907,8 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V3
 #define ATOM_S7_DOS_MODE_PIXEL_DEPTHb0      0x0C
 #define ATOM_S7_DOS_MODE_PIXEL_FORMATb0     0xF0
 #define ATOM_S7_DOS_8BIT_DAC_ENb1           0x01
+#define ATOM_S7_ASIC_INIT_COMPLETEb1        0x02
+#define ATOM_S7_ASIC_INIT_COMPLETE_MASK     0x00000200
 #define ATOM_S7_DOS_MODE_NUMBERw1           0x0FFFF
 
 #define ATOM_S7_DOS_8BIT_DAC_EN_SHIFT       8
@@ -6242,6 +6343,7 @@ typedef struct _ATOM_MC_INIT_PARAM_TABLE
 #define _128Mx32            0x53
 #define _256Mx8             0x61
 #define _256Mx16            0x62
+#define _512Mx8             0x71
 
 #define SAMSUNG             0x1
 #define INFINEON            0x2
@@ -6987,9 +7089,10 @@ typedef struct _ATOM_DISP_OUT_INFO_V3
   UCHAR  ucMaxDispEngineNum;
   UCHAR  ucMaxActiveDispEngineNum;
   UCHAR  ucMaxPPLLNum;
-  UCHAR  ucCoreRefClkSource;                          // value of CORE_REF_CLK_SOURCE
-  UCHAR  ucReserved[3];
-       ASIC_TRANSMITTER_INFO_V2  asTransmitterInfo[1];     // for alligment only
+  UCHAR  ucCoreRefClkSource;                    // value of CORE_REF_CLK_SOURCE
+  UCHAR  ucDispCaps;
+  UCHAR  ucReserved[2];
+  ASIC_TRANSMITTER_INFO_V2  asTransmitterInfo[1];     // for alligment only
 }ATOM_DISP_OUT_INFO_V3;
 
 //ucDispCaps
index bf87f6d..80a2012 100644 (file)
@@ -1753,7 +1753,7 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
                                if (pll != ATOM_PPLL_INVALID)
                                        return pll;
                        }
-               } else {
+               } else if (!ASIC_IS_DCE41(rdev)) { /* Don't share PLLs on DCE4.1 chips */
                        /* use the same PPLL for all monitors with the same clock */
                        pll = radeon_get_shared_nondp_ppll(crtc);
                        if (pll != ATOM_PPLL_INVALID)
@@ -1910,6 +1910,21 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
        int i;
 
        atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       if (crtc->fb) {
+               int r;
+               struct radeon_framebuffer *radeon_fb;
+               struct radeon_bo *rbo;
+
+               radeon_fb = to_radeon_framebuffer(crtc->fb);
+               rbo = gem_to_radeon_bo(radeon_fb->obj);
+               r = radeon_bo_reserve(rbo, false);
+               if (unlikely(r))
+                       DRM_ERROR("failed to reserve rbo before unpin\n");
+               else {
+                       radeon_bo_unpin(rbo);
+                       radeon_bo_unreserve(rbo);
+               }
+       }
        /* disable the GRPH */
        if (ASIC_IS_DCE4(rdev))
                WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 0);
@@ -1940,7 +1955,9 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
                break;
        case ATOM_PPLL0:
                /* disable the ppll */
-               if ((rdev->family == CHIP_ARUBA) || (rdev->family == CHIP_BONAIRE))
+               if ((rdev->family == CHIP_ARUBA) ||
+                   (rdev->family == CHIP_BONAIRE) ||
+                   (rdev->family == CHIP_HAWAII))
                        atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
                                                  0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss);
                break;
index 0088541..fb3ae07 100644 (file)
@@ -690,8 +690,7 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
 
        /* set the lane count on the sink */
        tmp = dp_info->dp_lane_count;
-       if (dp_info->dpcd[DP_DPCD_REV] >= 0x11 &&
-           dp_info->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)
+       if (drm_dp_enhanced_frame_cap(dp_info->dpcd))
                tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
        radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp);
 
index 5e891b2..a42d615 100644 (file)
@@ -213,7 +213,7 @@ void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder,
        props.type = BACKLIGHT_RAW;
        snprintf(bl_name, sizeof(bl_name),
                 "radeon_bl%d", dev->primary->index);
-       bd = backlight_device_register(bl_name, &drm_connector->kdev,
+       bd = backlight_device_register(bl_name, drm_connector->kdev,
                                       pdata, &radeon_atom_backlight_ops, &props);
        if (IS_ERR(bd)) {
                DRM_ERROR("Backlight registration failed\n");
@@ -1662,19 +1662,11 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
                        atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0);
                        /* enable the transmitter */
                        atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
-                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
                } else {
                        /* setup and enable the encoder and transmitter */
                        atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0);
                        atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
                        atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
-                       /* some dce3.x boards have a bug in their transmitter control table.
-                        * ACTION_ENABLE_OUTPUT can probably be dropped since ACTION_ENABLE
-                        * does the same thing and more.
-                        */
-                       if ((rdev->family != CHIP_RV710) && (rdev->family != CHIP_RV730) &&
-                           (rdev->family != CHIP_RS780) && (rdev->family != CHIP_RS880))
-                               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
                }
                if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
                        if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
@@ -1692,16 +1684,11 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
        case DRM_MODE_DPMS_OFF:
-               if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) {
-                       /* disable the transmitter */
-                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
-               } else if (ASIC_IS_DCE4(rdev)) {
+               if (ASIC_IS_DCE4(rdev)) {
                        /* disable the transmitter */
-                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0);
                        atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
                } else {
                        /* disable the encoder and transmitter */
-                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0);
                        atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
                        atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0);
                }
@@ -2410,6 +2397,15 @@ static void radeon_atom_encoder_prepare(struct drm_encoder *encoder)
 
        /* this is needed for the pll/ss setup to work correctly in some cases */
        atombios_set_encoder_crtc_source(encoder);
+       /* set up the FMT blocks */
+       if (ASIC_IS_DCE8(rdev))
+               dce8_program_fmt(encoder);
+       else if (ASIC_IS_DCE4(rdev))
+               dce4_program_fmt(encoder);
+       else if (ASIC_IS_DCE3(rdev))
+               dce3_program_fmt(encoder);
+       else if (ASIC_IS_AVIVO(rdev))
+               avivo_program_fmt(encoder);
 }
 
 static void radeon_atom_encoder_commit(struct drm_encoder *encoder)
index 51e947a..1ed4799 100644 (file)
 #define VOLTAGE_VID_OFFSET_SCALE1    625
 #define VOLTAGE_VID_OFFSET_SCALE2    100
 
+static const struct ci_pt_defaults defaults_hawaii_xt =
+{
+       1, 0xF, 0xFD, 0x19, 5, 0x14, 0, 0xB0000,
+       { 0x84,  0x0,   0x0,   0x7F,  0x0,   0x0,   0x5A,  0x60,  0x51,  0x8E,  0x79,  0x6B,  0x5F,  0x90,  0x79  },
+       { 0x1EA, 0x1EA, 0x1EA, 0x224, 0x224, 0x224, 0x24F, 0x24F, 0x24F, 0x28E, 0x28E, 0x28E, 0x2BC, 0x2BC, 0x2BC }
+};
+
+static const struct ci_pt_defaults defaults_hawaii_pro =
+{
+       1, 0xF, 0xFD, 0x19, 5, 0x14, 0, 0x65062,
+       { 0x93,  0x0,   0x0,   0x97,  0x0,   0x0,   0x6B,  0x60,  0x51,  0x95,  0x79,  0x6B,  0x5F,  0x90,  0x79  },
+       { 0x1EA, 0x1EA, 0x1EA, 0x224, 0x224, 0x224, 0x24F, 0x24F, 0x24F, 0x28E, 0x28E, 0x28E, 0x2BC, 0x2BC, 0x2BC }
+};
+
 static const struct ci_pt_defaults defaults_bonaire_xt =
 {
        1, 0xF, 0xFD, 0x19, 5, 45, 0, 0xB0000,
@@ -187,22 +201,38 @@ static void ci_initialize_powertune_defaults(struct radeon_device *rdev)
        struct ci_power_info *pi = ci_get_pi(rdev);
 
        switch (rdev->pdev->device) {
-        case 0x6650:
-        case 0x6658:
-        case 0x665C:
-        default:
+       case 0x6650:
+       case 0x6658:
+       case 0x665C:
+       default:
                pi->powertune_defaults = &defaults_bonaire_xt;
                break;
-        case 0x6651:
-        case 0x665D:
+       case 0x6651:
+       case 0x665D:
                pi->powertune_defaults = &defaults_bonaire_pro;
                break;
-        case 0x6640:
+       case 0x6640:
                pi->powertune_defaults = &defaults_saturn_xt;
                break;
-        case 0x6641:
+       case 0x6641:
                pi->powertune_defaults = &defaults_saturn_pro;
                break;
+       case 0x67B8:
+       case 0x67B0:
+       case 0x67A0:
+       case 0x67A1:
+       case 0x67A2:
+       case 0x67A8:
+       case 0x67A9:
+       case 0x67AA:
+       case 0x67B9:
+       case 0x67BE:
+               pi->powertune_defaults = &defaults_hawaii_xt;
+               break;
+       case 0x67BA:
+       case 0x67B1:
+               pi->powertune_defaults = &defaults_hawaii_pro;
+               break;
        }
 
        pi->dte_tj_offset = 0;
@@ -5142,9 +5172,15 @@ int ci_dpm_init(struct radeon_device *rdev)
        rdev->pm.dpm.dyn_state.valid_mclk_values.count = 0;
        rdev->pm.dpm.dyn_state.valid_mclk_values.values = NULL;
 
-       pi->thermal_temp_setting.temperature_low = 99500;
-       pi->thermal_temp_setting.temperature_high = 100000;
-       pi->thermal_temp_setting.temperature_shutdown = 104000;
+       if (rdev->family == CHIP_HAWAII) {
+               pi->thermal_temp_setting.temperature_low = 94500;
+               pi->thermal_temp_setting.temperature_high = 95000;
+               pi->thermal_temp_setting.temperature_shutdown = 104000;
+       } else {
+               pi->thermal_temp_setting.temperature_low = 99500;
+               pi->thermal_temp_setting.temperature_high = 100000;
+               pi->thermal_temp_setting.temperature_shutdown = 104000;
+       }
 
        pi->uvd_enabled = false;
 
index 252e10a..9c745dd 100644 (file)
@@ -217,6 +217,10 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
                ucode_start_address = BONAIRE_SMC_UCODE_START;
                ucode_size = BONAIRE_SMC_UCODE_SIZE;
                break;
+       case CHIP_HAWAII:
+               ucode_start_address = HAWAII_SMC_UCODE_START;
+               ucode_size = HAWAII_SMC_UCODE_SIZE;
+               break;
        default:
                DRM_ERROR("unknown asic in smc ucode loader\n");
                BUG();
index 9cd2bc9..ae92aa0 100644 (file)
@@ -41,6 +41,14 @@ MODULE_FIRMWARE("radeon/BONAIRE_mc.bin");
 MODULE_FIRMWARE("radeon/BONAIRE_rlc.bin");
 MODULE_FIRMWARE("radeon/BONAIRE_sdma.bin");
 MODULE_FIRMWARE("radeon/BONAIRE_smc.bin");
+MODULE_FIRMWARE("radeon/HAWAII_pfp.bin");
+MODULE_FIRMWARE("radeon/HAWAII_me.bin");
+MODULE_FIRMWARE("radeon/HAWAII_ce.bin");
+MODULE_FIRMWARE("radeon/HAWAII_mec.bin");
+MODULE_FIRMWARE("radeon/HAWAII_mc.bin");
+MODULE_FIRMWARE("radeon/HAWAII_rlc.bin");
+MODULE_FIRMWARE("radeon/HAWAII_sdma.bin");
+MODULE_FIRMWARE("radeon/HAWAII_smc.bin");
 MODULE_FIRMWARE("radeon/KAVERI_pfp.bin");
 MODULE_FIRMWARE("radeon/KAVERI_me.bin");
 MODULE_FIRMWARE("radeon/KAVERI_ce.bin");
@@ -67,11 +75,6 @@ extern void si_init_uvd_internal_cg(struct radeon_device *rdev);
 extern int cik_sdma_resume(struct radeon_device *rdev);
 extern void cik_sdma_enable(struct radeon_device *rdev, bool enable);
 extern void cik_sdma_fini(struct radeon_device *rdev);
-extern void cik_sdma_vm_set_page(struct radeon_device *rdev,
-                                struct radeon_ib *ib,
-                                uint64_t pe,
-                                uint64_t addr, unsigned count,
-                                uint32_t incr, uint32_t flags);
 static void cik_rlc_stop(struct radeon_device *rdev);
 static void cik_pcie_gen3_enable(struct radeon_device *rdev);
 static void cik_program_aspm(struct radeon_device *rdev);
@@ -1302,6 +1305,171 @@ static const u32 kalindi_mgcg_cgcg_init[] =
        0xd80c, 0xff000ff0, 0x00000100
 };
 
+static const u32 hawaii_golden_spm_registers[] =
+{
+       0x30800, 0xe0ffffff, 0xe0000000
+};
+
+static const u32 hawaii_golden_common_registers[] =
+{
+       0x30800, 0xffffffff, 0xe0000000,
+       0x28350, 0xffffffff, 0x3a00161a,
+       0x28354, 0xffffffff, 0x0000002e,
+       0x9a10, 0xffffffff, 0x00018208,
+       0x98f8, 0xffffffff, 0x12011003
+};
+
+static const u32 hawaii_golden_registers[] =
+{
+       0x3354, 0x00000333, 0x00000333,
+       0x9a10, 0x00010000, 0x00058208,
+       0x9830, 0xffffffff, 0x00000000,
+       0x9834, 0xf00fffff, 0x00000400,
+       0x9838, 0x0002021c, 0x00020200,
+       0xc78, 0x00000080, 0x00000000,
+       0x5bb0, 0x000000f0, 0x00000070,
+       0x5bc0, 0xf0311fff, 0x80300000,
+       0x350c, 0x00810000, 0x408af000,
+       0x7030, 0x31000111, 0x00000011,
+       0x2f48, 0x73773777, 0x12010001,
+       0x2120, 0x0000007f, 0x0000001b,
+       0x21dc, 0x00007fb6, 0x00002191,
+       0x3628, 0x0000003f, 0x0000000a,
+       0x362c, 0x0000003f, 0x0000000a,
+       0x2ae4, 0x00073ffe, 0x000022a2,
+       0x240c, 0x000007ff, 0x00000000,
+       0x8bf0, 0x00002001, 0x00000001,
+       0x8b24, 0xffffffff, 0x00ffffff,
+       0x30a04, 0x0000ff0f, 0x00000000,
+       0x28a4c, 0x07ffffff, 0x06000000,
+       0x3e78, 0x00000001, 0x00000002,
+       0xc768, 0x00000008, 0x00000008,
+       0xc770, 0x00000f00, 0x00000800,
+       0xc774, 0x00000f00, 0x00000800,
+       0xc798, 0x00ffffff, 0x00ff7fbf,
+       0xc79c, 0x00ffffff, 0x00ff7faf,
+       0x8c00, 0x000000ff, 0x00000800,
+       0xe40, 0x00001fff, 0x00001fff,
+       0x9060, 0x0000007f, 0x00000020,
+       0x9508, 0x00010000, 0x00010000,
+       0xae00, 0x00100000, 0x000ff07c,
+       0xac14, 0x000003ff, 0x0000000f,
+       0xac10, 0xffffffff, 0x7564fdec,
+       0xac0c, 0xffffffff, 0x3120b9a8,
+       0xac08, 0x20000000, 0x0f9c0000
+};
+
+static const u32 hawaii_mgcg_cgcg_init[] =
+{
+       0xc420, 0xffffffff, 0xfffffffd,
+       0x30800, 0xffffffff, 0xe0000000,
+       0x3c2a0, 0xffffffff, 0x00000100,
+       0x3c208, 0xffffffff, 0x00000100,
+       0x3c2c0, 0xffffffff, 0x00000100,
+       0x3c2c8, 0xffffffff, 0x00000100,
+       0x3c2c4, 0xffffffff, 0x00000100,
+       0x55e4, 0xffffffff, 0x00200100,
+       0x3c280, 0xffffffff, 0x00000100,
+       0x3c214, 0xffffffff, 0x06000100,
+       0x3c220, 0xffffffff, 0x00000100,
+       0x3c218, 0xffffffff, 0x06000100,
+       0x3c204, 0xffffffff, 0x00000100,
+       0x3c2e0, 0xffffffff, 0x00000100,
+       0x3c224, 0xffffffff, 0x00000100,
+       0x3c200, 0xffffffff, 0x00000100,
+       0x3c230, 0xffffffff, 0x00000100,
+       0x3c234, 0xffffffff, 0x00000100,
+       0x3c250, 0xffffffff, 0x00000100,
+       0x3c254, 0xffffffff, 0x00000100,
+       0x3c258, 0xffffffff, 0x00000100,
+       0x3c25c, 0xffffffff, 0x00000100,
+       0x3c260, 0xffffffff, 0x00000100,
+       0x3c27c, 0xffffffff, 0x00000100,
+       0x3c278, 0xffffffff, 0x00000100,
+       0x3c210, 0xffffffff, 0x06000100,
+       0x3c290, 0xffffffff, 0x00000100,
+       0x3c274, 0xffffffff, 0x00000100,
+       0x3c2b4, 0xffffffff, 0x00000100,
+       0x3c2b0, 0xffffffff, 0x00000100,
+       0x3c270, 0xffffffff, 0x00000100,
+       0x30800, 0xffffffff, 0xe0000000,
+       0x3c020, 0xffffffff, 0x00010000,
+       0x3c024, 0xffffffff, 0x00030002,
+       0x3c028, 0xffffffff, 0x00040007,
+       0x3c02c, 0xffffffff, 0x00060005,
+       0x3c030, 0xffffffff, 0x00090008,
+       0x3c034, 0xffffffff, 0x00010000,
+       0x3c038, 0xffffffff, 0x00030002,
+       0x3c03c, 0xffffffff, 0x00040007,
+       0x3c040, 0xffffffff, 0x00060005,
+       0x3c044, 0xffffffff, 0x00090008,
+       0x3c048, 0xffffffff, 0x00010000,
+       0x3c04c, 0xffffffff, 0x00030002,
+       0x3c050, 0xffffffff, 0x00040007,
+       0x3c054, 0xffffffff, 0x00060005,
+       0x3c058, 0xffffffff, 0x00090008,
+       0x3c05c, 0xffffffff, 0x00010000,
+       0x3c060, 0xffffffff, 0x00030002,
+       0x3c064, 0xffffffff, 0x00040007,
+       0x3c068, 0xffffffff, 0x00060005,
+       0x3c06c, 0xffffffff, 0x00090008,
+       0x3c070, 0xffffffff, 0x00010000,
+       0x3c074, 0xffffffff, 0x00030002,
+       0x3c078, 0xffffffff, 0x00040007,
+       0x3c07c, 0xffffffff, 0x00060005,
+       0x3c080, 0xffffffff, 0x00090008,
+       0x3c084, 0xffffffff, 0x00010000,
+       0x3c088, 0xffffffff, 0x00030002,
+       0x3c08c, 0xffffffff, 0x00040007,
+       0x3c090, 0xffffffff, 0x00060005,
+       0x3c094, 0xffffffff, 0x00090008,
+       0x3c098, 0xffffffff, 0x00010000,
+       0x3c09c, 0xffffffff, 0x00030002,
+       0x3c0a0, 0xffffffff, 0x00040007,
+       0x3c0a4, 0xffffffff, 0x00060005,
+       0x3c0a8, 0xffffffff, 0x00090008,
+       0x3c0ac, 0xffffffff, 0x00010000,
+       0x3c0b0, 0xffffffff, 0x00030002,
+       0x3c0b4, 0xffffffff, 0x00040007,
+       0x3c0b8, 0xffffffff, 0x00060005,
+       0x3c0bc, 0xffffffff, 0x00090008,
+       0x3c0c0, 0xffffffff, 0x00010000,
+       0x3c0c4, 0xffffffff, 0x00030002,
+       0x3c0c8, 0xffffffff, 0x00040007,
+       0x3c0cc, 0xffffffff, 0x00060005,
+       0x3c0d0, 0xffffffff, 0x00090008,
+       0x3c0d4, 0xffffffff, 0x00010000,
+       0x3c0d8, 0xffffffff, 0x00030002,
+       0x3c0dc, 0xffffffff, 0x00040007,
+       0x3c0e0, 0xffffffff, 0x00060005,
+       0x3c0e4, 0xffffffff, 0x00090008,
+       0x3c0e8, 0xffffffff, 0x00010000,
+       0x3c0ec, 0xffffffff, 0x00030002,
+       0x3c0f0, 0xffffffff, 0x00040007,
+       0x3c0f4, 0xffffffff, 0x00060005,
+       0x3c0f8, 0xffffffff, 0x00090008,
+       0xc318, 0xffffffff, 0x00020200,
+       0x3350, 0xffffffff, 0x00000200,
+       0x15c0, 0xffffffff, 0x00000400,
+       0x55e8, 0xffffffff, 0x00000000,
+       0x2f50, 0xffffffff, 0x00000902,
+       0x3c000, 0xffffffff, 0x96940200,
+       0x8708, 0xffffffff, 0x00900100,
+       0xc424, 0xffffffff, 0x0020003f,
+       0x38, 0xffffffff, 0x0140001c,
+       0x3c, 0x000f0000, 0x000f0000,
+       0x220, 0xffffffff, 0xc060000c,
+       0x224, 0xc0000fff, 0x00000100,
+       0xf90, 0xffffffff, 0x00000100,
+       0xf98, 0x00000101, 0x00000000,
+       0x20a8, 0xffffffff, 0x00000104,
+       0x55e4, 0xff000fff, 0x00000100,
+       0x30cc, 0xc0000fff, 0x00000104,
+       0xc1e4, 0x00000001, 0x00000001,
+       0xd00c, 0xff000ff0, 0x00000100,
+       0xd80c, 0xff000ff0, 0x00000100
+};
+
 static void cik_init_golden_registers(struct radeon_device *rdev)
 {
        switch (rdev->family) {
@@ -1347,6 +1515,20 @@ static void cik_init_golden_registers(struct radeon_device *rdev)
                                                 spectre_golden_spm_registers,
                                                 (const u32)ARRAY_SIZE(spectre_golden_spm_registers));
                break;
+       case CHIP_HAWAII:
+               radeon_program_register_sequence(rdev,
+                                                hawaii_mgcg_cgcg_init,
+                                                (const u32)ARRAY_SIZE(hawaii_mgcg_cgcg_init));
+               radeon_program_register_sequence(rdev,
+                                                hawaii_golden_registers,
+                                                (const u32)ARRAY_SIZE(hawaii_golden_registers));
+               radeon_program_register_sequence(rdev,
+                                                hawaii_golden_common_registers,
+                                                (const u32)ARRAY_SIZE(hawaii_golden_common_registers));
+               radeon_program_register_sequence(rdev,
+                                                hawaii_golden_spm_registers,
+                                                (const u32)ARRAY_SIZE(hawaii_golden_spm_registers));
+               break;
        default:
                break;
        }
@@ -1454,6 +1636,35 @@ static const u32 bonaire_io_mc_regs[BONAIRE_IO_MC_REGS_SIZE][2] =
        {0x0000009f, 0x00b48000}
 };
 
+#define HAWAII_IO_MC_REGS_SIZE 22
+
+static const u32 hawaii_io_mc_regs[HAWAII_IO_MC_REGS_SIZE][2] =
+{
+       {0x0000007d, 0x40000000},
+       {0x0000007e, 0x40180304},
+       {0x0000007f, 0x0000ff00},
+       {0x00000081, 0x00000000},
+       {0x00000083, 0x00000800},
+       {0x00000086, 0x00000000},
+       {0x00000087, 0x00000100},
+       {0x00000088, 0x00020100},
+       {0x00000089, 0x00000000},
+       {0x0000008b, 0x00040000},
+       {0x0000008c, 0x00000100},
+       {0x0000008e, 0xff010000},
+       {0x00000090, 0xffffefff},
+       {0x00000091, 0xfff3efff},
+       {0x00000092, 0xfff3efbf},
+       {0x00000093, 0xf7ffffff},
+       {0x00000094, 0xffffff7f},
+       {0x00000095, 0x00000fff},
+       {0x00000096, 0x00116fff},
+       {0x00000097, 0x60010000},
+       {0x00000098, 0x10010000},
+       {0x0000009f, 0x00c79000}
+};
+
+
 /**
  * cik_srbm_select - select specific register instances
  *
@@ -1498,11 +1709,17 @@ static int ci_mc_load_microcode(struct radeon_device *rdev)
 
        switch (rdev->family) {
        case CHIP_BONAIRE:
-       default:
                io_mc_regs = (u32 *)&bonaire_io_mc_regs;
                ucode_size = CIK_MC_UCODE_SIZE;
                regs_size = BONAIRE_IO_MC_REGS_SIZE;
                break;
+       case CHIP_HAWAII:
+               io_mc_regs = (u32 *)&hawaii_io_mc_regs;
+               ucode_size = HAWAII_MC_UCODE_SIZE;
+               regs_size = HAWAII_IO_MC_REGS_SIZE;
+               break;
+       default:
+               return -EINVAL;
        }
 
        running = RREG32(MC_SEQ_SUP_CNTL) & RUN_MASK;
@@ -1564,8 +1781,8 @@ static int cik_init_microcode(struct radeon_device *rdev)
 {
        const char *chip_name;
        size_t pfp_req_size, me_req_size, ce_req_size,
-               mec_req_size, rlc_req_size, mc_req_size,
-               sdma_req_size, smc_req_size;
+               mec_req_size, rlc_req_size, mc_req_size = 0,
+               sdma_req_size, smc_req_size = 0;
        char fw_name[30];
        int err;
 
@@ -1583,6 +1800,17 @@ static int cik_init_microcode(struct radeon_device *rdev)
                sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
                smc_req_size = ALIGN(BONAIRE_SMC_UCODE_SIZE, 4);
                break;
+       case CHIP_HAWAII:
+               chip_name = "HAWAII";
+               pfp_req_size = CIK_PFP_UCODE_SIZE * 4;
+               me_req_size = CIK_ME_UCODE_SIZE * 4;
+               ce_req_size = CIK_CE_UCODE_SIZE * 4;
+               mec_req_size = CIK_MEC_UCODE_SIZE * 4;
+               rlc_req_size = BONAIRE_RLC_UCODE_SIZE * 4;
+               mc_req_size = HAWAII_MC_UCODE_SIZE * 4;
+               sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(HAWAII_SMC_UCODE_SIZE, 4);
+               break;
        case CHIP_KAVERI:
                chip_name = "KAVERI";
                pfp_req_size = CIK_PFP_UCODE_SIZE * 4;
@@ -1763,9 +1991,227 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
 
        num_pipe_configs = rdev->config.cik.max_tile_pipes;
        if (num_pipe_configs > 8)
-               num_pipe_configs = 8; /* ??? */
+               num_pipe_configs = 16;
 
-       if (num_pipe_configs == 8) {
+       if (num_pipe_configs == 16) {
+               for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) {
+                       switch (reg_offset) {
+                       case 0:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B));
+                               break;
+                       case 1:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B));
+                               break;
+                       case 2:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B));
+                               break;
+                       case 3:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B));
+                               break;
+                       case 4:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                TILE_SPLIT(split_equal_to_row_size));
+                               break;
+                       case 5:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING));
+                               break;
+                       case 6:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B));
+                               break;
+                       case 7:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                TILE_SPLIT(split_equal_to_row_size));
+                               break;
+                       case 8:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16));
+                               break;
+                       case 9:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING));
+                               break;
+                       case 10:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2));
+                               break;
+                       case 11:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_8x16) |
+                                                SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2));
+                               break;
+                       case 12:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2));
+                               break;
+                       case 13:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING));
+                               break;
+                       case 14:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2));
+                               break;
+                       case 16:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_8x16) |
+                                                SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2));
+                               break;
+                       case 17:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2));
+                               break;
+                       case 27:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING));
+                               break;
+                       case 28:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2));
+                               break;
+                       case 29:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_8x16) |
+                                                SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2));
+                               break;
+                       case 30:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
+                                                SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2));
+                               break;
+                       default:
+                               gb_tile_moden = 0;
+                               break;
+                       }
+                       rdev->config.cik.tile_mode_array[reg_offset] = gb_tile_moden;
+                       WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden);
+               }
+               for (reg_offset = 0; reg_offset < num_secondary_tile_mode_states; reg_offset++) {
+                       switch (reg_offset) {
+                       case 0:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 1:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 2:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 3:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 4:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) |
+                                                NUM_BANKS(ADDR_SURF_8_BANK));
+                               break;
+                       case 5:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) |
+                                                NUM_BANKS(ADDR_SURF_4_BANK));
+                               break;
+                       case 6:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) |
+                                                NUM_BANKS(ADDR_SURF_2_BANK));
+                               break;
+                       case 8:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 9:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 10:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 11:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) |
+                                                NUM_BANKS(ADDR_SURF_8_BANK));
+                               break;
+                       case 12:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) |
+                                                NUM_BANKS(ADDR_SURF_4_BANK));
+                               break;
+                       case 13:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) |
+                                                NUM_BANKS(ADDR_SURF_2_BANK));
+                               break;
+                       case 14:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) |
+                                                NUM_BANKS(ADDR_SURF_2_BANK));
+                               break;
+                       default:
+                               gb_tile_moden = 0;
+                               break;
+                       }
+                       WREG32(GB_MACROTILE_MODE0 + (reg_offset * 4), gb_tile_moden);
+               }
+       } else if (num_pipe_configs == 8) {
                for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) {
                        switch (reg_offset) {
                        case 0:
@@ -2650,7 +3096,10 @@ static void cik_setup_rb(struct radeon_device *rdev,
                for (j = 0; j < sh_per_se; j++) {
                        cik_select_se_sh(rdev, i, j);
                        data = cik_get_rb_disabled(rdev, max_rb_num, se_num, sh_per_se);
-                       disabled_rbs |= data << ((i * sh_per_se + j) * CIK_RB_BITMAP_WIDTH_PER_SH);
+                       if (rdev->family == CHIP_HAWAII)
+                               disabled_rbs |= data << ((i * sh_per_se + j) * HAWAII_RB_BITMAP_WIDTH_PER_SH);
+                       else
+                               disabled_rbs |= data << ((i * sh_per_se + j) * CIK_RB_BITMAP_WIDTH_PER_SH);
                }
        }
        cik_select_se_sh(rdev, 0xffffffff, 0xffffffff);
@@ -2667,6 +3116,12 @@ static void cik_setup_rb(struct radeon_device *rdev,
                data = 0;
                for (j = 0; j < sh_per_se; j++) {
                        switch (enabled_rbs & 3) {
+                       case 0:
+                               if (j == 0)
+                                       data |= PKR_MAP(RASTER_CONFIG_RB_MAP_3);
+                               else
+                                       data |= PKR_MAP(RASTER_CONFIG_RB_MAP_0);
+                               break;
                        case 1:
                                data |= (RASTER_CONFIG_RB_MAP_0 << (i * sh_per_se + j) * 2);
                                break;
@@ -2719,6 +3174,23 @@ static void cik_gpu_init(struct radeon_device *rdev)
                rdev->config.cik.sc_earlyz_tile_fifo_size = 0x130;
                gb_addr_config = BONAIRE_GB_ADDR_CONFIG_GOLDEN;
                break;
+       case CHIP_HAWAII:
+               rdev->config.cik.max_shader_engines = 4;
+               rdev->config.cik.max_tile_pipes = 16;
+               rdev->config.cik.max_cu_per_sh = 11;
+               rdev->config.cik.max_sh_per_se = 1;
+               rdev->config.cik.max_backends_per_se = 4;
+               rdev->config.cik.max_texture_channel_caches = 16;
+               rdev->config.cik.max_gprs = 256;
+               rdev->config.cik.max_gs_threads = 32;
+               rdev->config.cik.max_hw_contexts = 8;
+
+               rdev->config.cik.sc_prim_fifo_size_frontend = 0x20;
+               rdev->config.cik.sc_prim_fifo_size_backend = 0x100;
+               rdev->config.cik.sc_hiz_tile_fifo_size = 0x30;
+               rdev->config.cik.sc_earlyz_tile_fifo_size = 0x130;
+               gb_addr_config = HAWAII_GB_ADDR_CONFIG_GOLDEN;
+               break;
        case CHIP_KAVERI:
                rdev->config.cik.max_shader_engines = 1;
                rdev->config.cik.max_tile_pipes = 4;
@@ -3097,6 +3569,85 @@ void cik_semaphore_ring_emit(struct radeon_device *rdev,
        radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | sel);
 }
 
+/**
+ * cik_copy_cpdma - copy pages using the CP DMA engine
+ *
+ * @rdev: radeon_device pointer
+ * @src_offset: src GPU address
+ * @dst_offset: dst GPU address
+ * @num_gpu_pages: number of GPU pages to xfer
+ * @fence: radeon fence object
+ *
+ * Copy GPU paging using the CP DMA engine (CIK+).
+ * Used by the radeon ttm implementation to move pages if
+ * registered as the asic copy callback.
+ */
+int cik_copy_cpdma(struct radeon_device *rdev,
+                  uint64_t src_offset, uint64_t dst_offset,
+                  unsigned num_gpu_pages,
+                  struct radeon_fence **fence)
+{
+       struct radeon_semaphore *sem = NULL;
+       int ring_index = rdev->asic->copy.blit_ring_index;
+       struct radeon_ring *ring = &rdev->ring[ring_index];
+       u32 size_in_bytes, cur_size_in_bytes, control;
+       int i, num_loops;
+       int r = 0;
+
+       r = radeon_semaphore_create(rdev, &sem);
+       if (r) {
+               DRM_ERROR("radeon: moving bo (%d).\n", r);
+               return r;
+       }
+
+       size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT);
+       num_loops = DIV_ROUND_UP(size_in_bytes, 0x1fffff);
+       r = radeon_ring_lock(rdev, ring, num_loops * 7 + 18);
+       if (r) {
+               DRM_ERROR("radeon: moving bo (%d).\n", r);
+               radeon_semaphore_free(rdev, &sem, NULL);
+               return r;
+       }
+
+       if (radeon_fence_need_sync(*fence, ring->idx)) {
+               radeon_semaphore_sync_rings(rdev, sem, (*fence)->ring,
+                                           ring->idx);
+               radeon_fence_note_sync(*fence, ring->idx);
+       } else {
+               radeon_semaphore_free(rdev, &sem, NULL);
+       }
+
+       for (i = 0; i < num_loops; i++) {
+               cur_size_in_bytes = size_in_bytes;
+               if (cur_size_in_bytes > 0x1fffff)
+                       cur_size_in_bytes = 0x1fffff;
+               size_in_bytes -= cur_size_in_bytes;
+               control = 0;
+               if (size_in_bytes == 0)
+                       control |= PACKET3_DMA_DATA_CP_SYNC;
+               radeon_ring_write(ring, PACKET3(PACKET3_DMA_DATA, 5));
+               radeon_ring_write(ring, control);
+               radeon_ring_write(ring, lower_32_bits(src_offset));
+               radeon_ring_write(ring, upper_32_bits(src_offset));
+               radeon_ring_write(ring, lower_32_bits(dst_offset));
+               radeon_ring_write(ring, upper_32_bits(dst_offset));
+               radeon_ring_write(ring, cur_size_in_bytes);
+               src_offset += cur_size_in_bytes;
+               dst_offset += cur_size_in_bytes;
+       }
+
+       r = radeon_fence_emit(rdev, fence, ring->idx);
+       if (r) {
+               radeon_ring_unlock_undo(rdev, ring);
+               return r;
+       }
+
+       radeon_ring_unlock_commit(rdev, ring);
+       radeon_semaphore_free(rdev, &sem, *fence);
+
+       return r;
+}
+
 /*
  * IB stuff
  */
@@ -3403,7 +3954,8 @@ static int cik_cp_gfx_resume(struct radeon_device *rdev)
        int r;
 
        WREG32(CP_SEM_WAIT_TIMER, 0x0);
-       WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0);
+       if (rdev->family != CHIP_HAWAII)
+               WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0);
 
        /* Set the write pointer delay */
        WREG32(CP_RB_WPTR_DELAY, 0);
@@ -4740,12 +5292,17 @@ void cik_vm_fini(struct radeon_device *rdev)
 static void cik_vm_decode_fault(struct radeon_device *rdev,
                                u32 status, u32 addr, u32 mc_client)
 {
-       u32 mc_id = (status & MEMORY_CLIENT_ID_MASK) >> MEMORY_CLIENT_ID_SHIFT;
+       u32 mc_id;
        u32 vmid = (status & FAULT_VMID_MASK) >> FAULT_VMID_SHIFT;
        u32 protections = (status & PROTECTIONS_MASK) >> PROTECTIONS_SHIFT;
        char block[5] = { mc_client >> 24, (mc_client >> 16) & 0xff,
                (mc_client >> 8) & 0xff, mc_client & 0xff, 0 };
 
+       if (rdev->family == CHIP_HAWAII)
+               mc_id = (status & HAWAII_MEMORY_CLIENT_ID_MASK) >> MEMORY_CLIENT_ID_SHIFT;
+       else
+               mc_id = (status & MEMORY_CLIENT_ID_MASK) >> MEMORY_CLIENT_ID_SHIFT;
+
        printk("VM fault (0x%02x, vmid %d) at page %u, %s from '%s' (0x%08x) (%d)\n",
               protections, vmid, addr,
               (status & MEMORY_CLIENT_RW_MASK) ? "write" : "read",
@@ -4834,62 +5391,6 @@ void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
        }
 }
 
-/**
- * cik_vm_set_page - update the page tables using sDMA
- *
- * @rdev: radeon_device pointer
- * @ib: indirect buffer to fill with commands
- * @pe: addr of the page entry
- * @addr: dst addr to write into pe
- * @count: number of page entries to update
- * @incr: increase next addr by incr bytes
- * @flags: access flags
- *
- * Update the page tables using CP or sDMA (CIK).
- */
-void cik_vm_set_page(struct radeon_device *rdev,
-                    struct radeon_ib *ib,
-                    uint64_t pe,
-                    uint64_t addr, unsigned count,
-                    uint32_t incr, uint32_t flags)
-{
-       uint32_t r600_flags = cayman_vm_page_flags(rdev, flags);
-       uint64_t value;
-       unsigned ndw;
-
-       if (rdev->asic->vm.pt_ring_index == RADEON_RING_TYPE_GFX_INDEX) {
-               /* CP */
-               while (count) {
-                       ndw = 2 + count * 2;
-                       if (ndw > 0x3FFE)
-                               ndw = 0x3FFE;
-
-                       ib->ptr[ib->length_dw++] = PACKET3(PACKET3_WRITE_DATA, ndw);
-                       ib->ptr[ib->length_dw++] = (WRITE_DATA_ENGINE_SEL(0) |
-                                                   WRITE_DATA_DST_SEL(1));
-                       ib->ptr[ib->length_dw++] = pe;
-                       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-                       for (; ndw > 2; ndw -= 2, --count, pe += 8) {
-                               if (flags & RADEON_VM_PAGE_SYSTEM) {
-                                       value = radeon_vm_map_gart(rdev, addr);
-                                       value &= 0xFFFFFFFFFFFFF000ULL;
-                               } else if (flags & RADEON_VM_PAGE_VALID) {
-                                       value = addr;
-                               } else {
-                                       value = 0;
-                               }
-                               addr += incr;
-                               value |= r600_flags;
-                               ib->ptr[ib->length_dw++] = value;
-                               ib->ptr[ib->length_dw++] = upper_32_bits(value);
-                       }
-               }
-       } else {
-               /* DMA */
-               cik_sdma_vm_set_page(rdev, ib, pe, addr, count, incr, flags);
-       }
-}
-
 /*
  * RLC
  * The RLC is a multi-purpose microengine that handles a
@@ -5058,6 +5559,7 @@ static int cik_rlc_resume(struct radeon_device *rdev)
 
        switch (rdev->family) {
        case CHIP_BONAIRE:
+       case CHIP_HAWAII:
        default:
                size = BONAIRE_RLC_UCODE_SIZE;
                break;
@@ -5556,7 +6058,7 @@ void cik_init_cp_pg_table(struct radeon_device *rdev)
                }
 
                for (i = 0; i < CP_ME_TABLE_SIZE; i ++) {
-                       dst_ptr[bo_offset + i] = be32_to_cpu(fw_data[table_offset + i]);
+                       dst_ptr[bo_offset + i] = cpu_to_le32(be32_to_cpu(fw_data[table_offset + i]));
                }
                bo_offset += CP_ME_TABLE_SIZE;
        }
@@ -5778,52 +6280,57 @@ void cik_get_csb_buffer(struct radeon_device *rdev, volatile u32 *buffer)
        if (buffer == NULL)
                return;
 
-       buffer[count++] = PACKET3(PACKET3_PREAMBLE_CNTL, 0);
-       buffer[count++] = PACKET3_PREAMBLE_BEGIN_CLEAR_STATE;
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+       buffer[count++] = cpu_to_le32(PACKET3_PREAMBLE_BEGIN_CLEAR_STATE);
 
-       buffer[count++] = PACKET3(PACKET3_CONTEXT_CONTROL, 1);
-       buffer[count++] = 0x80000000;
-       buffer[count++] = 0x80000000;
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_CONTEXT_CONTROL, 1));
+       buffer[count++] = cpu_to_le32(0x80000000);
+       buffer[count++] = cpu_to_le32(0x80000000);
 
        for (sect = rdev->rlc.cs_data; sect->section != NULL; ++sect) {
                for (ext = sect->section; ext->extent != NULL; ++ext) {
                        if (sect->id == SECT_CONTEXT) {
-                               buffer[count++] = PACKET3(PACKET3_SET_CONTEXT_REG, ext->reg_count);
-                               buffer[count++] = ext->reg_index - 0xa000;
+                               buffer[count++] =
+                                       cpu_to_le32(PACKET3(PACKET3_SET_CONTEXT_REG, ext->reg_count));
+                               buffer[count++] = cpu_to_le32(ext->reg_index - 0xa000);
                                for (i = 0; i < ext->reg_count; i++)
-                                       buffer[count++] = ext->extent[i];
+                                       buffer[count++] = cpu_to_le32(ext->extent[i]);
                        } else {
                                return;
                        }
                }
        }
 
-       buffer[count++] = PACKET3(PACKET3_SET_CONTEXT_REG, 2);
-       buffer[count++] = PA_SC_RASTER_CONFIG - PACKET3_SET_CONTEXT_REG_START;
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_SET_CONTEXT_REG, 2));
+       buffer[count++] = cpu_to_le32(PA_SC_RASTER_CONFIG - PACKET3_SET_CONTEXT_REG_START);
        switch (rdev->family) {
        case CHIP_BONAIRE:
-               buffer[count++] = 0x16000012;
-               buffer[count++] = 0x00000000;
+               buffer[count++] = cpu_to_le32(0x16000012);
+               buffer[count++] = cpu_to_le32(0x00000000);
                break;
        case CHIP_KAVERI:
-               buffer[count++] = 0x00000000; /* XXX */
-               buffer[count++] = 0x00000000;
+               buffer[count++] = cpu_to_le32(0x00000000); /* XXX */
+               buffer[count++] = cpu_to_le32(0x00000000);
                break;
        case CHIP_KABINI:
-               buffer[count++] = 0x00000000; /* XXX */
-               buffer[count++] = 0x00000000;
+               buffer[count++] = cpu_to_le32(0x00000000); /* XXX */
+               buffer[count++] = cpu_to_le32(0x00000000);
+               break;
+       case CHIP_HAWAII:
+               buffer[count++] = 0x3a00161a;
+               buffer[count++] = 0x0000002e;
                break;
        default:
-               buffer[count++] = 0x00000000;
-               buffer[count++] = 0x00000000;
+               buffer[count++] = cpu_to_le32(0x00000000);
+               buffer[count++] = cpu_to_le32(0x00000000);
                break;
        }
 
-       buffer[count++] = PACKET3(PACKET3_PREAMBLE_CNTL, 0);
-       buffer[count++] = PACKET3_PREAMBLE_END_CLEAR_STATE;
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+       buffer[count++] = cpu_to_le32(PACKET3_PREAMBLE_END_CLEAR_STATE);
 
-       buffer[count++] = PACKET3(PACKET3_CLEAR_STATE, 0);
-       buffer[count++] = 0;
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_CLEAR_STATE, 0));
+       buffer[count++] = cpu_to_le32(0);
 }
 
 static void cik_init_pg(struct radeon_device *rdev)
@@ -7118,7 +7625,7 @@ static int cik_startup(struct radeon_device *rdev)
        ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
        r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET,
                             CP_RB0_RPTR, CP_RB0_WPTR,
-                            RADEON_CP_PACKET2);
+                            PACKET3(PACKET3_NOP, 0x3FFF));
        if (r)
                return r;
 
@@ -7428,6 +7935,70 @@ void cik_fini(struct radeon_device *rdev)
        rdev->bios = NULL;
 }
 
+void dce8_program_fmt(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+       struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+       int bpc = 0;
+       u32 tmp = 0;
+       enum radeon_connector_dither dither = RADEON_FMT_DITHER_DISABLE;
+
+       if (connector) {
+               struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+               bpc = radeon_get_monitor_bpc(connector);
+               dither = radeon_connector->dither;
+       }
+
+       /* LVDS/eDP FMT is set up by atom */
+       if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
+               return;
+
+       /* not needed for analog */
+       if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1) ||
+           (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2))
+               return;
+
+       if (bpc == 0)
+               return;
+
+       switch (bpc) {
+       case 6:
+               if (dither == RADEON_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
+                               FMT_SPATIAL_DITHER_EN | FMT_SPATIAL_DITHER_DEPTH(0));
+               else
+                       tmp |= (FMT_TRUNCATE_EN | FMT_TRUNCATE_DEPTH(0));
+               break;
+       case 8:
+               if (dither == RADEON_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
+                               FMT_RGB_RANDOM_ENABLE |
+                               FMT_SPATIAL_DITHER_EN | FMT_SPATIAL_DITHER_DEPTH(1));
+               else
+                       tmp |= (FMT_TRUNCATE_EN | FMT_TRUNCATE_DEPTH(1));
+               break;
+       case 10:
+               if (dither == RADEON_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
+                               FMT_RGB_RANDOM_ENABLE |
+                               FMT_SPATIAL_DITHER_EN | FMT_SPATIAL_DITHER_DEPTH(2));
+               else
+                       tmp |= (FMT_TRUNCATE_EN | FMT_TRUNCATE_DEPTH(2));
+               break;
+       default:
+               /* not needed */
+               break;
+       }
+
+       WREG32(FMT_BIT_DEPTH_CONTROL + radeon_crtc->crtc_offset, tmp);
+}
+
 /* display watermark setup */
 /**
  * dce8_line_buffer_adjust - Set up the line buffer
index b628606..9c9529d 100644 (file)
@@ -25,6 +25,7 @@
 #include <drm/drmP.h>
 #include "radeon.h"
 #include "radeon_asic.h"
+#include "radeon_trace.h"
 #include "cikd.h"
 
 /* sdma */
@@ -101,14 +102,6 @@ void cik_sdma_fence_ring_emit(struct radeon_device *rdev,
 {
        struct radeon_ring *ring = &rdev->ring[fence->ring];
        u64 addr = rdev->fence_drv[fence->ring].gpu_addr;
-       u32 extra_bits = (SDMA_POLL_REG_MEM_EXTRA_OP(1) |
-                         SDMA_POLL_REG_MEM_EXTRA_FUNC(3)); /* == */
-       u32 ref_and_mask;
-
-       if (fence->ring == R600_RING_TYPE_DMA_INDEX)
-               ref_and_mask = SDMA0;
-       else
-               ref_and_mask = SDMA1;
 
        /* write the fence */
        radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_FENCE, 0, 0));
@@ -118,12 +111,12 @@ void cik_sdma_fence_ring_emit(struct radeon_device *rdev,
        /* generate an interrupt */
        radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_TRAP, 0, 0));
        /* flush HDP */
-       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_POLL_REG_MEM, 0, extra_bits));
-       radeon_ring_write(ring, GPU_HDP_FLUSH_DONE);
-       radeon_ring_write(ring, GPU_HDP_FLUSH_REQ);
-       radeon_ring_write(ring, ref_and_mask); /* REFERENCE */
-       radeon_ring_write(ring, ref_and_mask); /* MASK */
-       radeon_ring_write(ring, (4 << 16) | 10); /* RETRY_COUNT, POLL_INTERVAL */
+       /* We should be using the new POLL_REG_MEM special op packet here
+        * but it causes sDMA to hang sometimes
+        */
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
+       radeon_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL >> 2);
+       radeon_ring_write(ring, 0);
 }
 
 /**
@@ -653,11 +646,12 @@ void cik_sdma_vm_set_page(struct radeon_device *rdev,
                          uint64_t addr, unsigned count,
                          uint32_t incr, uint32_t flags)
 {
-       uint32_t r600_flags = cayman_vm_page_flags(rdev, flags);
        uint64_t value;
        unsigned ndw;
 
-       if (flags & RADEON_VM_PAGE_SYSTEM) {
+       trace_radeon_vm_set_page(pe, addr, count, incr, flags);
+
+       if (flags & R600_PTE_SYSTEM) {
                while (count) {
                        ndw = count * 2;
                        if (ndw > 0xFFFFE)
@@ -669,16 +663,10 @@ void cik_sdma_vm_set_page(struct radeon_device *rdev,
                        ib->ptr[ib->length_dw++] = upper_32_bits(pe);
                        ib->ptr[ib->length_dw++] = ndw;
                        for (; ndw > 0; ndw -= 2, --count, pe += 8) {
-                               if (flags & RADEON_VM_PAGE_SYSTEM) {
-                                       value = radeon_vm_map_gart(rdev, addr);
-                                       value &= 0xFFFFFFFFFFFFF000ULL;
-                               } else if (flags & RADEON_VM_PAGE_VALID) {
-                                       value = addr;
-                               } else {
-                                       value = 0;
-                               }
+                               value = radeon_vm_map_gart(rdev, addr);
+                               value &= 0xFFFFFFFFFFFFF000ULL;
                                addr += incr;
-                               value |= r600_flags;
+                               value |= flags;
                                ib->ptr[ib->length_dw++] = value;
                                ib->ptr[ib->length_dw++] = upper_32_bits(value);
                        }
@@ -689,7 +677,7 @@ void cik_sdma_vm_set_page(struct radeon_device *rdev,
                        if (ndw > 0x7FFFF)
                                ndw = 0x7FFFF;
 
-                       if (flags & RADEON_VM_PAGE_VALID)
+                       if (flags & R600_PTE_VALID)
                                value = addr;
                        else
                                value = 0;
@@ -697,7 +685,7 @@ void cik_sdma_vm_set_page(struct radeon_device *rdev,
                        ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_GENERATE_PTE_PDE, 0, 0);
                        ib->ptr[ib->length_dw++] = pe; /* dst addr */
                        ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-                       ib->ptr[ib->length_dw++] = r600_flags; /* mask */
+                       ib->ptr[ib->length_dw++] = flags; /* mask */
                        ib->ptr[ib->length_dw++] = 0;
                        ib->ptr[ib->length_dw++] = value; /* value */
                        ib->ptr[ib->length_dw++] = upper_32_bits(value);
@@ -724,18 +712,10 @@ void cik_sdma_vm_set_page(struct radeon_device *rdev,
 void cik_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
 {
        struct radeon_ring *ring = &rdev->ring[ridx];
-       u32 extra_bits = (SDMA_POLL_REG_MEM_EXTRA_OP(1) |
-                         SDMA_POLL_REG_MEM_EXTRA_FUNC(3)); /* == */
-       u32 ref_and_mask;
 
        if (vm == NULL)
                return;
 
-       if (ridx == R600_RING_TYPE_DMA_INDEX)
-               ref_and_mask = SDMA0;
-       else
-               ref_and_mask = SDMA1;
-
        radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
        if (vm->id < 8) {
                radeon_ring_write(ring, (VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm->id << 2)) >> 2);
@@ -770,12 +750,12 @@ void cik_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm
        radeon_ring_write(ring, VMID(0));
 
        /* flush HDP */
-       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_POLL_REG_MEM, 0, extra_bits));
-       radeon_ring_write(ring, GPU_HDP_FLUSH_DONE);
-       radeon_ring_write(ring, GPU_HDP_FLUSH_REQ);
-       radeon_ring_write(ring, ref_and_mask); /* REFERENCE */
-       radeon_ring_write(ring, ref_and_mask); /* MASK */
-       radeon_ring_write(ring, (4 << 16) | 10); /* RETRY_COUNT, POLL_INTERVAL */
+       /* We should be using the new POLL_REG_MEM special op packet here
+        * but it causes sDMA to hang sometimes
+        */
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
+       radeon_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL >> 2);
+       radeon_ring_write(ring, 0);
 
        /* flush TLB */
        radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
index 203d2a0..5964af5 100644 (file)
 #define CIK_H
 
 #define BONAIRE_GB_ADDR_CONFIG_GOLDEN        0x12010001
+#define HAWAII_GB_ADDR_CONFIG_GOLDEN         0x12011003
 
-#define CIK_RB_BITMAP_WIDTH_PER_SH  2
+#define CIK_RB_BITMAP_WIDTH_PER_SH     2
+#define HAWAII_RB_BITMAP_WIDTH_PER_SH  4
 
 /* DIDT IND registers */
 #define DIDT_SQ_CTRL0                                     0x0
                 * bit 4: write
                 */
 #define                MEMORY_CLIENT_ID_MASK                   (0xff << 12)
+#define                HAWAII_MEMORY_CLIENT_ID_MASK            (0x1ff << 12)
 #define                MEMORY_CLIENT_ID_SHIFT                  12
 #define                MEMORY_CLIENT_RW_MASK                   (1 << 24)
 #define                MEMORY_CLIENT_RW_SHIFT                  24
 #define DPG_PIPE_STUTTER_CONTROL                          0x6cd4
 #       define STUTTER_ENABLE                             (1 << 0)
 
+/* DCE8 FMT blocks */
+#define FMT_DYNAMIC_EXP_CNTL                 0x6fb4
+#       define FMT_DYNAMIC_EXP_EN            (1 << 0)
+#       define FMT_DYNAMIC_EXP_MODE          (1 << 4)
+        /* 0 = 10bit -> 12bit, 1 = 8bit -> 12bit */
+#define FMT_CONTROL                          0x6fb8
+#       define FMT_PIXEL_ENCODING            (1 << 16)
+        /* 0 = RGB 4:4:4 or YCbCr 4:4:4, 1 = YCbCr 4:2:2 */
+#define FMT_BIT_DEPTH_CONTROL                0x6fc8
+#       define FMT_TRUNCATE_EN               (1 << 0)
+#       define FMT_TRUNCATE_MODE             (1 << 1)
+#       define FMT_TRUNCATE_DEPTH(x)         ((x) << 4) /* 0 - 18bpp, 1 - 24bpp, 2 - 30bpp */
+#       define FMT_SPATIAL_DITHER_EN         (1 << 8)
+#       define FMT_SPATIAL_DITHER_MODE(x)    ((x) << 9)
+#       define FMT_SPATIAL_DITHER_DEPTH(x)   ((x) << 11) /* 0 - 18bpp, 1 - 24bpp, 2 - 30bpp */
+#       define FMT_FRAME_RANDOM_ENABLE       (1 << 13)
+#       define FMT_RGB_RANDOM_ENABLE         (1 << 14)
+#       define FMT_HIGHPASS_RANDOM_ENABLE    (1 << 15)
+#       define FMT_TEMPORAL_DITHER_EN        (1 << 16)
+#       define FMT_TEMPORAL_DITHER_DEPTH(x)  ((x) << 17) /* 0 - 18bpp, 1 - 24bpp, 2 - 30bpp */
+#       define FMT_TEMPORAL_DITHER_OFFSET(x) ((x) << 21)
+#       define FMT_TEMPORAL_LEVEL            (1 << 24)
+#       define FMT_TEMPORAL_DITHER_RESET     (1 << 25)
+#       define FMT_25FRC_SEL(x)              ((x) << 26)
+#       define FMT_50FRC_SEL(x)              ((x) << 28)
+#       define FMT_75FRC_SEL(x)              ((x) << 30)
+#define FMT_CLAMP_CONTROL                    0x6fe4
+#       define FMT_CLAMP_DATA_EN             (1 << 0)
+#       define FMT_CLAMP_COLOR_FORMAT(x)     ((x) << 16)
+#       define FMT_CLAMP_6BPC                0
+#       define FMT_CLAMP_8BPC                1
+#       define FMT_CLAMP_10BPC               2
+
 #define        GRBM_CNTL                                       0x8000
 #define                GRBM_READ_TIMEOUT(x)                            ((x) << 0)
 
 #              define  ADDR_SURF_P8_32x32_16x16                12
 #              define  ADDR_SURF_P8_32x32_16x32                13
 #              define  ADDR_SURF_P8_32x64_32x32                14
+#              define  ADDR_SURF_P16_32x32_8x16                16
+#              define  ADDR_SURF_P16_32x32_16x16               17
 #       define TILE_SPLIT(x)                                   ((x) << 11)
 #              define  ADDR_SURF_TILE_SPLIT_64B                0
 #              define  ADDR_SURF_TILE_SPLIT_128B               1
 #       define RASTER_CONFIG_RB_MAP_1                   1
 #       define RASTER_CONFIG_RB_MAP_2                   2
 #       define RASTER_CONFIG_RB_MAP_3                   3
+#define                PKR_MAP(x)                              ((x) << 8)
 
 #define VGT_EVENT_INITIATOR                             0x28a90
 #       define SAMPLE_STREAMOUTSTATS1                   (1 << 0)
 #              define PACKET3_PREAMBLE_BEGIN_CLEAR_STATE     (2 << 28)
 #              define PACKET3_PREAMBLE_END_CLEAR_STATE       (3 << 28)
 #define        PACKET3_DMA_DATA                                0x50
+/* 1. header
+ * 2. CONTROL
+ * 3. SRC_ADDR_LO or DATA [31:0]
+ * 4. SRC_ADDR_HI [31:0]
+ * 5. DST_ADDR_LO [31:0]
+ * 6. DST_ADDR_HI [7:0]
+ * 7. COMMAND [30:21] | BYTE_COUNT [20:0]
+ */
+/* CONTROL */
+#              define PACKET3_DMA_DATA_ENGINE(x)     ((x) << 0)
+                /* 0 - ME
+                * 1 - PFP
+                */
+#              define PACKET3_DMA_DATA_SRC_CACHE_POLICY(x) ((x) << 13)
+                /* 0 - LRU
+                * 1 - Stream
+                * 2 - Bypass
+                */
+#              define PACKET3_DMA_DATA_SRC_VOLATILE (1 << 15)
+#              define PACKET3_DMA_DATA_DST_SEL(x)  ((x) << 20)
+                /* 0 - DST_ADDR using DAS
+                * 1 - GDS
+                * 3 - DST_ADDR using L2
+                */
+#              define PACKET3_DMA_DATA_DST_CACHE_POLICY(x) ((x) << 25)
+                /* 0 - LRU
+                * 1 - Stream
+                * 2 - Bypass
+                */
+#              define PACKET3_DMA_DATA_DST_VOLATILE (1 << 27)
+#              define PACKET3_DMA_DATA_SRC_SEL(x)  ((x) << 29)
+                /* 0 - SRC_ADDR using SAS
+                * 1 - GDS
+                * 2 - DATA
+                * 3 - SRC_ADDR using L2
+                */
+#              define PACKET3_DMA_DATA_CP_SYNC     (1 << 31)
+/* COMMAND */
+#              define PACKET3_DMA_DATA_DIS_WC      (1 << 21)
+#              define PACKET3_DMA_DATA_CMD_SRC_SWAP(x) ((x) << 22)
+                /* 0 - none
+                * 1 - 8 in 16
+                * 2 - 8 in 32
+                * 3 - 8 in 64
+                */
+#              define PACKET3_DMA_DATA_CMD_DST_SWAP(x) ((x) << 24)
+                /* 0 - none
+                * 1 - 8 in 16
+                * 2 - 8 in 32
+                * 3 - 8 in 64
+                */
+#              define PACKET3_DMA_DATA_CMD_SAS     (1 << 26)
+                /* 0 - memory
+                * 1 - register
+                */
+#              define PACKET3_DMA_DATA_CMD_DAS     (1 << 27)
+                /* 0 - memory
+                * 1 - register
+                */
+#              define PACKET3_DMA_DATA_CMD_SAIC    (1 << 28)
+#              define PACKET3_DMA_DATA_CMD_DAIC    (1 << 29)
+#              define PACKET3_DMA_DATA_CMD_RAW_WAIT  (1 << 30)
 #define        PACKET3_AQUIRE_MEM                              0x58
 #define        PACKET3_REWIND                                  0x59
 #define        PACKET3_LOAD_UCONFIG_REG                        0x5E
index 9fcd338..009f46e 100644 (file)
@@ -102,6 +102,49 @@ void dce6_afmt_select_pin(struct drm_encoder *encoder)
               AFMT_AUDIO_SRC_SELECT(dig->afmt->pin->id));
 }
 
+void dce6_afmt_write_latency_fields(struct drm_encoder *encoder,
+                                   struct drm_display_mode *mode)
+{
+       struct radeon_device *rdev = encoder->dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+       struct drm_connector *connector;
+       struct radeon_connector *radeon_connector = NULL;
+       u32 tmp = 0, offset;
+
+       if (!dig->afmt->pin)
+               return;
+
+       offset = dig->afmt->pin->offset;
+
+       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+               if (connector->encoder == encoder) {
+                       radeon_connector = to_radeon_connector(connector);
+                       break;
+               }
+       }
+
+       if (!radeon_connector) {
+               DRM_ERROR("Couldn't find encoder's connector\n");
+               return;
+       }
+
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+               if (connector->latency_present[1])
+                       tmp = VIDEO_LIPSYNC(connector->video_latency[1]) |
+                               AUDIO_LIPSYNC(connector->audio_latency[1]);
+               else
+                       tmp = VIDEO_LIPSYNC(255) | AUDIO_LIPSYNC(255);
+       } else {
+               if (connector->latency_present[0])
+                       tmp = VIDEO_LIPSYNC(connector->video_latency[0]) |
+                               AUDIO_LIPSYNC(connector->audio_latency[0]);
+               else
+                       tmp = VIDEO_LIPSYNC(255) | AUDIO_LIPSYNC(255);
+       }
+       WREG32_ENDPOINT(offset, AZ_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, tmp);
+}
+
 void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder)
 {
        struct radeon_device *rdev = encoder->dev->dev_private;
@@ -113,9 +156,6 @@ void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder)
        u8 *sadb;
        int sad_count;
 
-       /* XXX: setting this register causes hangs on some asics */
-       return;
-
        if (!dig->afmt->pin)
                return;
 
@@ -201,20 +241,30 @@ void dce6_afmt_write_sad_regs(struct drm_encoder *encoder)
 
        for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
                u32 value = 0;
+               u8 stereo_freqs = 0;
+               int max_channels = -1;
                int j;
 
                for (j = 0; j < sad_count; j++) {
                        struct cea_sad *sad = &sads[j];
 
                        if (sad->format == eld_reg_to_type[i][1]) {
-                               value = MAX_CHANNELS(sad->channels) |
-                                       DESCRIPTOR_BYTE_2(sad->byte2) |
-                                       SUPPORTED_FREQUENCIES(sad->freq);
+                               if (sad->channels > max_channels) {
+                                       value = MAX_CHANNELS(sad->channels) |
+                                               DESCRIPTOR_BYTE_2(sad->byte2) |
+                                               SUPPORTED_FREQUENCIES(sad->freq);
+                                       max_channels = sad->channels;
+                               }
+
                                if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
-                                       value |= SUPPORTED_FREQUENCIES_STEREO(sad->freq);
-                               break;
+                                       stereo_freqs |= sad->freq;
+                               else
+                                       break;
                        }
                }
+
+               value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs);
+
                WREG32_ENDPOINT(offset, eld_reg_to_type[i][0], value);
        }
 
index 56f6bec..9702e55 100644 (file)
@@ -1186,6 +1186,62 @@ void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev)
                pcie_set_readrq(rdev->pdev, 512);
 }
 
+void dce4_program_fmt(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+       struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+       int bpc = 0;
+       u32 tmp = 0;
+       enum radeon_connector_dither dither = RADEON_FMT_DITHER_DISABLE;
+
+       if (connector) {
+               struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+               bpc = radeon_get_monitor_bpc(connector);
+               dither = radeon_connector->dither;
+       }
+
+       /* LVDS/eDP FMT is set up by atom */
+       if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
+               return;
+
+       /* not needed for analog */
+       if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1) ||
+           (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2))
+               return;
+
+       if (bpc == 0)
+               return;
+
+       switch (bpc) {
+       case 6:
+               if (dither == RADEON_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
+                               FMT_SPATIAL_DITHER_EN);
+               else
+                       tmp |= FMT_TRUNCATE_EN;
+               break;
+       case 8:
+               if (dither == RADEON_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
+                               FMT_RGB_RANDOM_ENABLE |
+                               FMT_SPATIAL_DITHER_EN | FMT_SPATIAL_DITHER_DEPTH);
+               else
+                       tmp |= (FMT_TRUNCATE_EN | FMT_TRUNCATE_DEPTH);
+               break;
+       case 10:
+       default:
+               /* not needed */
+               break;
+       }
+
+       WREG32(FMT_BIT_DEPTH_CONTROL + radeon_crtc->crtc_offset, tmp);
+}
+
 static bool dce4_is_in_vblank(struct radeon_device *rdev, int crtc)
 {
        if (RREG32(EVERGREEN_CRTC_STATUS + crtc_offsets[crtc]) & EVERGREEN_CRTC_V_BLANK)
@@ -3956,7 +4012,7 @@ int sumo_rlc_init(struct radeon_device *rdev)
                if (rdev->family >= CHIP_TAHITI) {
                        /* SI */
                        for (i = 0; i < rdev->rlc.reg_list_size; i++)
-                               dst_ptr[i] = src_ptr[i];
+                               dst_ptr[i] = cpu_to_le32(src_ptr[i]);
                } else {
                        /* ON/LN/TN */
                        /* format:
@@ -3970,10 +4026,10 @@ int sumo_rlc_init(struct radeon_device *rdev)
                                if (i < dws)
                                        data |= (src_ptr[i] >> 2) << 16;
                                j = (((i - 1) * 3) / 2);
-                               dst_ptr[j] = data;
+                               dst_ptr[j] = cpu_to_le32(data);
                        }
                        j = ((i * 3) / 2);
-                       dst_ptr[j] = RLC_SAVE_RESTORE_LIST_END_MARKER;
+                       dst_ptr[j] = cpu_to_le32(RLC_SAVE_RESTORE_LIST_END_MARKER);
                }
                radeon_bo_kunmap(rdev->rlc.save_restore_obj);
                radeon_bo_unreserve(rdev->rlc.save_restore_obj);
@@ -4035,40 +4091,40 @@ int sumo_rlc_init(struct radeon_device *rdev)
                        cik_get_csb_buffer(rdev, dst_ptr);
                } else if (rdev->family >= CHIP_TAHITI) {
                        reg_list_mc_addr = rdev->rlc.clear_state_gpu_addr + 256;
-                       dst_ptr[0] = upper_32_bits(reg_list_mc_addr);
-                       dst_ptr[1] = lower_32_bits(reg_list_mc_addr);
-                       dst_ptr[2] = rdev->rlc.clear_state_size;
+                       dst_ptr[0] = cpu_to_le32(upper_32_bits(reg_list_mc_addr));
+                       dst_ptr[1] = cpu_to_le32(lower_32_bits(reg_list_mc_addr));
+                       dst_ptr[2] = cpu_to_le32(rdev->rlc.clear_state_size);
                        si_get_csb_buffer(rdev, &dst_ptr[(256/4)]);
                } else {
                        reg_list_hdr_blk_index = 0;
                        reg_list_mc_addr = rdev->rlc.clear_state_gpu_addr + (reg_list_blk_index * 4);
                        data = upper_32_bits(reg_list_mc_addr);
-                       dst_ptr[reg_list_hdr_blk_index] = data;
+                       dst_ptr[reg_list_hdr_blk_index] = cpu_to_le32(data);
                        reg_list_hdr_blk_index++;
                        for (i = 0; cs_data[i].section != NULL; i++) {
                                for (j = 0; cs_data[i].section[j].extent != NULL; j++) {
                                        reg_num = cs_data[i].section[j].reg_count;
                                        data = reg_list_mc_addr & 0xffffffff;
-                                       dst_ptr[reg_list_hdr_blk_index] = data;
+                                       dst_ptr[reg_list_hdr_blk_index] = cpu_to_le32(data);
                                        reg_list_hdr_blk_index++;
 
                                        data = (cs_data[i].section[j].reg_index * 4) & 0xffffffff;
-                                       dst_ptr[reg_list_hdr_blk_index] = data;
+                                       dst_ptr[reg_list_hdr_blk_index] = cpu_to_le32(data);
                                        reg_list_hdr_blk_index++;
 
                                        data = 0x08000000 | (reg_num * 4);
-                                       dst_ptr[reg_list_hdr_blk_index] = data;
+                                       dst_ptr[reg_list_hdr_blk_index] = cpu_to_le32(data);
                                        reg_list_hdr_blk_index++;
 
                                        for (k = 0; k < reg_num; k++) {
                                                data = cs_data[i].section[j].extent[k];
-                                               dst_ptr[reg_list_blk_index + k] = data;
+                                               dst_ptr[reg_list_blk_index + k] = cpu_to_le32(data);
                                        }
                                        reg_list_mc_addr += reg_num * 4;
                                        reg_list_blk_index += reg_num;
                                }
                        }
-                       dst_ptr[reg_list_hdr_blk_index] = RLC_CLEAR_STATE_END_MARKER;
+                       dst_ptr[reg_list_hdr_blk_index] = cpu_to_le32(RLC_CLEAR_STATE_END_MARKER);
                }
                radeon_bo_kunmap(rdev->rlc.clear_state_obj);
                radeon_bo_unreserve(rdev->rlc.clear_state_obj);
index 57fcc4b..aa695c4 100644 (file)
@@ -35,6 +35,8 @@
 extern void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder);
 extern void dce6_afmt_write_sad_regs(struct drm_encoder *encoder);
 extern void dce6_afmt_select_pin(struct drm_encoder *encoder);
+extern void dce6_afmt_write_latency_fields(struct drm_encoder *encoder,
+                                          struct drm_display_mode *mode);
 
 /*
  * update the N and CTS parameters for a given pixel clock rate
@@ -58,6 +60,42 @@ static void evergreen_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t cloc
        WREG32(HDMI_ACR_48_1 + offset, acr.n_48khz);
 }
 
+static void dce4_afmt_write_latency_fields(struct drm_encoder *encoder,
+                                          struct drm_display_mode *mode)
+{
+       struct radeon_device *rdev = encoder->dev->dev_private;
+       struct drm_connector *connector;
+       struct radeon_connector *radeon_connector = NULL;
+       u32 tmp = 0;
+
+       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+               if (connector->encoder == encoder) {
+                       radeon_connector = to_radeon_connector(connector);
+                       break;
+               }
+       }
+
+       if (!radeon_connector) {
+               DRM_ERROR("Couldn't find encoder's connector\n");
+               return;
+       }
+
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+               if (connector->latency_present[1])
+                       tmp = VIDEO_LIPSYNC(connector->video_latency[1]) |
+                               AUDIO_LIPSYNC(connector->audio_latency[1]);
+               else
+                       tmp = VIDEO_LIPSYNC(255) | AUDIO_LIPSYNC(255);
+       } else {
+               if (connector->latency_present[0])
+                       tmp = VIDEO_LIPSYNC(connector->video_latency[0]) |
+                               AUDIO_LIPSYNC(connector->audio_latency[0]);
+               else
+                       tmp = VIDEO_LIPSYNC(255) | AUDIO_LIPSYNC(255);
+       }
+       WREG32(AZ_F0_CODEC_PIN0_CONTROL_RESPONSE_LIPSYNC, tmp);
+}
+
 static void dce4_afmt_write_speaker_allocation(struct drm_encoder *encoder)
 {
        struct radeon_device *rdev = encoder->dev->dev_private;
@@ -67,12 +105,11 @@ static void dce4_afmt_write_speaker_allocation(struct drm_encoder *encoder)
        u8 *sadb;
        int sad_count;
 
-       /* XXX: setting this register causes hangs on some asics */
-       return;
-
        list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
-               if (connector->encoder == encoder)
+               if (connector->encoder == encoder) {
                        radeon_connector = to_radeon_connector(connector);
+                       break;
+               }
        }
 
        if (!radeon_connector) {
@@ -124,8 +161,10 @@ static void evergreen_hdmi_write_sad_regs(struct drm_encoder *encoder)
        };
 
        list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
-               if (connector->encoder == encoder)
+               if (connector->encoder == encoder) {
                        radeon_connector = to_radeon_connector(connector);
+                       break;
+               }
        }
 
        if (!radeon_connector) {
@@ -142,20 +181,30 @@ static void evergreen_hdmi_write_sad_regs(struct drm_encoder *encoder)
 
        for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
                u32 value = 0;
+               u8 stereo_freqs = 0;
+               int max_channels = -1;
                int j;
 
                for (j = 0; j < sad_count; j++) {
                        struct cea_sad *sad = &sads[j];
 
                        if (sad->format == eld_reg_to_type[i][1]) {
-                               value = MAX_CHANNELS(sad->channels) |
-                                       DESCRIPTOR_BYTE_2(sad->byte2) |
-                                       SUPPORTED_FREQUENCIES(sad->freq);
+                               if (sad->channels > max_channels) {
+                                       value = MAX_CHANNELS(sad->channels) |
+                                               DESCRIPTOR_BYTE_2(sad->byte2) |
+                                               SUPPORTED_FREQUENCIES(sad->freq);
+                                       max_channels = sad->channels;
+                               }
+
                                if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
-                                       value |= SUPPORTED_FREQUENCIES_STEREO(sad->freq);
-                               break;
+                                       stereo_freqs |= sad->freq;
+                               else
+                                       break;
                        }
                }
+
+               value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs);
+
                WREG32(eld_reg_to_type[i][0], value);
        }
 
@@ -324,8 +373,10 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
        if (ASIC_IS_DCE6(rdev)) {
                dce6_afmt_select_pin(encoder);
                dce6_afmt_write_sad_regs(encoder);
+               dce6_afmt_write_latency_fields(encoder, mode);
        } else {
                evergreen_hdmi_write_sad_regs(encoder);
+               dce4_afmt_write_latency_fields(encoder, mode);
        }
 
        err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
index 4f6d296..17f9907 100644 (file)
  * bit6 = 192 kHz
  */
 
+#define AZ_CHANNEL_COUNT_CONTROL                          0x5fe4
+#       define HBR_CHANNEL_COUNT(x)                       (((x) & 0x7) << 0)
+#       define COMPRESSED_CHANNEL_COUNT(x)                (((x) & 0x7) << 4)
+/* HBR_CHANNEL_COUNT, COMPRESSED_CHANNEL_COUNT
+ * 0   = use stream header
+ * 1-7 = channel count - 1
+ */
+#define AZ_F0_CODEC_PIN0_CONTROL_RESPONSE_LIPSYNC         0x5fe8
+#       define VIDEO_LIPSYNC(x)                           (((x) & 0xff) << 0)
+#       define AUDIO_LIPSYNC(x)                           (((x) & 0xff) << 8)
+/* VIDEO_LIPSYNC, AUDIO_LIPSYNC
+ * 0   = invalid
+ * x   = legal delay value
+ * 255 = sync not supported
+ */
+#define AZ_F0_CODEC_PIN0_CONTROL_RESPONSE_HBR             0x5fec
+#       define HBR_CAPABLE                                (1 << 0) /* enabled by default */
+
+#define AZ_F0_CODEC_PIN0_CONTROL_RESPONSE_AV_ASSOCIATION0 0x5ff4
+#       define DISPLAY0_TYPE(x)                           (((x) & 0x3) << 0)
+#       define DISPLAY_TYPE_NONE                   0
+#       define DISPLAY_TYPE_HDMI                   1
+#       define DISPLAY_TYPE_DP                     2
+#       define DISPLAY0_ID(x)                             (((x) & 0x3f) << 2)
+#       define DISPLAY1_TYPE(x)                           (((x) & 0x3) << 8)
+#       define DISPLAY1_ID(x)                             (((x) & 0x3f) << 10)
+#       define DISPLAY2_TYPE(x)                           (((x) & 0x3) << 16)
+#       define DISPLAY2_ID(x)                             (((x) & 0x3f) << 18)
+#       define DISPLAY3_TYPE(x)                           (((x) & 0x3) << 24)
+#       define DISPLAY3_ID(x)                             (((x) & 0x3f) << 26)
+#define AZ_F0_CODEC_PIN0_CONTROL_RESPONSE_AV_ASSOCIATION1 0x5ff8
+#       define DISPLAY4_TYPE(x)                           (((x) & 0x3) << 0)
+#       define DISPLAY4_ID(x)                             (((x) & 0x3f) << 2)
+#       define DISPLAY5_TYPE(x)                           (((x) & 0x3) << 8)
+#       define DISPLAY5_ID(x)                             (((x) & 0x3f) << 10)
+#define AZ_F0_CODEC_PIN0_CONTROL_RESPONSE_AV_NUMBER       0x5ffc
+#       define NUMBER_OF_DISPLAY_ID(x)                    (((x) & 0x7) << 0)
+
 #define AZ_HOT_PLUG_CONTROL                               0x5e78
 #       define AZ_FORCE_CODEC_WAKE                        (1 << 0)
 #       define PIN0_JACK_DETECTION_ENABLE                 (1 << 4)
 #       define DC_HPDx_RX_INT_TIMER(x)                    ((x) << 16)
 #       define DC_HPDx_EN                                 (1 << 28)
 
+/* DCE4/5/6 FMT blocks */
+#define FMT_DYNAMIC_EXP_CNTL                 0x6fb4
+#       define FMT_DYNAMIC_EXP_EN            (1 << 0)
+#       define FMT_DYNAMIC_EXP_MODE          (1 << 4)
+        /* 0 = 10bit -> 12bit, 1 = 8bit -> 12bit */
+#define FMT_CONTROL                          0x6fb8
+#       define FMT_PIXEL_ENCODING            (1 << 16)
+        /* 0 = RGB 4:4:4 or YCbCr 4:4:4, 1 = YCbCr 4:2:2 */
+#define FMT_BIT_DEPTH_CONTROL                0x6fc8
+#       define FMT_TRUNCATE_EN               (1 << 0)
+#       define FMT_TRUNCATE_DEPTH            (1 << 4)
+#       define FMT_SPATIAL_DITHER_EN         (1 << 8)
+#       define FMT_SPATIAL_DITHER_MODE(x)    ((x) << 9)
+#       define FMT_SPATIAL_DITHER_DEPTH      (1 << 12)
+#       define FMT_FRAME_RANDOM_ENABLE       (1 << 13)
+#       define FMT_RGB_RANDOM_ENABLE         (1 << 14)
+#       define FMT_HIGHPASS_RANDOM_ENABLE    (1 << 15)
+#       define FMT_TEMPORAL_DITHER_EN        (1 << 16)
+#       define FMT_TEMPORAL_DITHER_DEPTH     (1 << 20)
+#       define FMT_TEMPORAL_DITHER_OFFSET(x) ((x) << 21)
+#       define FMT_TEMPORAL_LEVEL            (1 << 24)
+#       define FMT_TEMPORAL_DITHER_RESET     (1 << 25)
+#       define FMT_25FRC_SEL(x)              ((x) << 26)
+#       define FMT_50FRC_SEL(x)              ((x) << 28)
+#       define FMT_75FRC_SEL(x)              ((x) << 30)
+#define FMT_CLAMP_CONTROL                    0x6fe4
+#       define FMT_CLAMP_DATA_EN             (1 << 0)
+#       define FMT_CLAMP_COLOR_FORMAT(x)     ((x) << 16)
+#       define FMT_CLAMP_6BPC                0
+#       define FMT_CLAMP_8BPC                1
+#       define FMT_CLAMP_10BPC               2
+
 /* ASYNC DMA */
 #define DMA_RB_RPTR                                       0xd008
 #define DMA_RB_WPTR                                       0xd00c
index cac2866..11aab2a 100644 (file)
@@ -174,11 +174,6 @@ extern void evergreen_pcie_gen2_enable(struct radeon_device *rdev);
 extern void evergreen_program_aspm(struct radeon_device *rdev);
 extern void sumo_rlc_fini(struct radeon_device *rdev);
 extern int sumo_rlc_init(struct radeon_device *rdev);
-extern void cayman_dma_vm_set_page(struct radeon_device *rdev,
-                                  struct radeon_ib *ib,
-                                  uint64_t pe,
-                                  uint64_t addr, unsigned count,
-                                  uint32_t incr, uint32_t flags);
 
 /* Firmware Names */
 MODULE_FIRMWARE("radeon/BARTS_pfp.bin");
@@ -2400,77 +2395,6 @@ void cayman_vm_decode_fault(struct radeon_device *rdev,
               block, mc_id);
 }
 
-#define R600_ENTRY_VALID   (1 << 0)
-#define R600_PTE_SYSTEM    (1 << 1)
-#define R600_PTE_SNOOPED   (1 << 2)
-#define R600_PTE_READABLE  (1 << 5)
-#define R600_PTE_WRITEABLE (1 << 6)
-
-uint32_t cayman_vm_page_flags(struct radeon_device *rdev, uint32_t flags)
-{
-       uint32_t r600_flags = 0;
-       r600_flags |= (flags & RADEON_VM_PAGE_VALID) ? R600_ENTRY_VALID : 0;
-       r600_flags |= (flags & RADEON_VM_PAGE_READABLE) ? R600_PTE_READABLE : 0;
-       r600_flags |= (flags & RADEON_VM_PAGE_WRITEABLE) ? R600_PTE_WRITEABLE : 0;
-       if (flags & RADEON_VM_PAGE_SYSTEM) {
-               r600_flags |= R600_PTE_SYSTEM;
-               r600_flags |= (flags & RADEON_VM_PAGE_SNOOPED) ? R600_PTE_SNOOPED : 0;
-       }
-       return r600_flags;
-}
-
-/**
- * cayman_vm_set_page - update the page tables using the CP
- *
- * @rdev: radeon_device pointer
- * @ib: indirect buffer to fill with commands
- * @pe: addr of the page entry
- * @addr: dst addr to write into pe
- * @count: number of page entries to update
- * @incr: increase next addr by incr bytes
- * @flags: access flags
- *
- * Update the page tables using the CP (cayman/TN).
- */
-void cayman_vm_set_page(struct radeon_device *rdev,
-                       struct radeon_ib *ib,
-                       uint64_t pe,
-                       uint64_t addr, unsigned count,
-                       uint32_t incr, uint32_t flags)
-{
-       uint32_t r600_flags = cayman_vm_page_flags(rdev, flags);
-       uint64_t value;
-       unsigned ndw;
-
-       if (rdev->asic->vm.pt_ring_index == RADEON_RING_TYPE_GFX_INDEX) {
-               while (count) {
-                       ndw = 1 + count * 2;
-                       if (ndw > 0x3FFF)
-                               ndw = 0x3FFF;
-
-                       ib->ptr[ib->length_dw++] = PACKET3(PACKET3_ME_WRITE, ndw);
-                       ib->ptr[ib->length_dw++] = pe;
-                       ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
-                       for (; ndw > 1; ndw -= 2, --count, pe += 8) {
-                               if (flags & RADEON_VM_PAGE_SYSTEM) {
-                                       value = radeon_vm_map_gart(rdev, addr);
-                                       value &= 0xFFFFFFFFFFFFF000ULL;
-                               } else if (flags & RADEON_VM_PAGE_VALID) {
-                                       value = addr;
-                               } else {
-                                       value = 0;
-                               }
-                               addr += incr;
-                               value |= r600_flags;
-                               ib->ptr[ib->length_dw++] = value;
-                               ib->ptr[ib->length_dw++] = upper_32_bits(value);
-                       }
-               }
-       } else {
-               cayman_dma_vm_set_page(rdev, ib, pe, addr, count, incr, flags);
-       }
-}
-
 /**
  * cayman_vm_flush - vm flush using the CP
  *
index dd6e968..bdeb65e 100644 (file)
@@ -24,6 +24,7 @@
 #include <drm/drmP.h>
 #include "radeon.h"
 #include "radeon_asic.h"
+#include "radeon_trace.h"
 #include "nid.h"
 
 u32 cayman_gpu_check_soft_reset(struct radeon_device *rdev);
@@ -245,8 +246,7 @@ bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
  * @addr: dst addr to write into pe
  * @count: number of page entries to update
  * @incr: increase next addr by incr bytes
- * @flags: access flags
- * @r600_flags: hw access flags 
+ * @flags: hw access flags 
  *
  * Update the page tables using the DMA (cayman/TN).
  */
@@ -256,11 +256,12 @@ void cayman_dma_vm_set_page(struct radeon_device *rdev,
                            uint64_t addr, unsigned count,
                            uint32_t incr, uint32_t flags)
 {
-       uint32_t r600_flags = cayman_vm_page_flags(rdev, flags);
        uint64_t value;
        unsigned ndw;
 
-       if ((flags & RADEON_VM_PAGE_SYSTEM) || (count == 1)) {
+       trace_radeon_vm_set_page(pe, addr, count, incr, flags);
+
+       if ((flags & R600_PTE_SYSTEM) || (count == 1)) {
                while (count) {
                        ndw = count * 2;
                        if (ndw > 0xFFFFE)
@@ -271,16 +272,16 @@ void cayman_dma_vm_set_page(struct radeon_device *rdev,
                        ib->ptr[ib->length_dw++] = pe;
                        ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
                        for (; ndw > 0; ndw -= 2, --count, pe += 8) {
-                               if (flags & RADEON_VM_PAGE_SYSTEM) {
+                               if (flags & R600_PTE_SYSTEM) {
                                        value = radeon_vm_map_gart(rdev, addr);
                                        value &= 0xFFFFFFFFFFFFF000ULL;
-                               } else if (flags & RADEON_VM_PAGE_VALID) {
+                               } else if (flags & R600_PTE_VALID) {
                                        value = addr;
                                } else {
                                        value = 0;
                                }
                                addr += incr;
-                               value |= r600_flags;
+                               value |= flags;
                                ib->ptr[ib->length_dw++] = value;
                                ib->ptr[ib->length_dw++] = upper_32_bits(value);
                        }
@@ -291,7 +292,7 @@ void cayman_dma_vm_set_page(struct radeon_device *rdev,
                        if (ndw > 0xFFFFE)
                                ndw = 0xFFFFE;
 
-                       if (flags & RADEON_VM_PAGE_VALID)
+                       if (flags & R600_PTE_VALID)
                                value = addr;
                        else
                                value = 0;
@@ -299,7 +300,7 @@ void cayman_dma_vm_set_page(struct radeon_device *rdev,
                        ib->ptr[ib->length_dw++] = DMA_PTE_PDE_PACKET(ndw);
                        ib->ptr[ib->length_dw++] = pe; /* dst addr */
                        ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
-                       ib->ptr[ib->length_dw++] = r600_flags; /* mask */
+                       ib->ptr[ib->length_dw++] = flags; /* mask */
                        ib->ptr[ib->length_dw++] = 0;
                        ib->ptr[ib->length_dw++] = value; /* value */
                        ib->ptr[ib->length_dw++] = upper_32_bits(value);
index d713330..784983d 100644 (file)
@@ -1434,7 +1434,7 @@ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p)
        obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj) {
                DRM_ERROR("cannot find crtc %d\n", crtc_id);
-               return -EINVAL;
+               return -ENOENT;
        }
        crtc = obj_to_crtc(obj);
        radeon_crtc = to_radeon_crtc(crtc);
index f9be220..4e609e8 100644 (file)
@@ -124,6 +124,59 @@ int r600_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
        return 0;
 }
 
+void dce3_program_fmt(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+       struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+       int bpc = 0;
+       u32 tmp = 0;
+       enum radeon_connector_dither dither = RADEON_FMT_DITHER_DISABLE;
+
+       if (connector) {
+               struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+               bpc = radeon_get_monitor_bpc(connector);
+               dither = radeon_connector->dither;
+       }
+
+       /* LVDS FMT is set up by atom */
+       if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
+               return;
+
+       /* not needed for analog */
+       if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1) ||
+           (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2))
+               return;
+
+       if (bpc == 0)
+               return;
+
+       switch (bpc) {
+       case 6:
+               if (dither == RADEON_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= FMT_SPATIAL_DITHER_EN;
+               else
+                       tmp |= FMT_TRUNCATE_EN;
+               break;
+       case 8:
+               if (dither == RADEON_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= (FMT_SPATIAL_DITHER_EN | FMT_SPATIAL_DITHER_DEPTH);
+               else
+                       tmp |= (FMT_TRUNCATE_EN | FMT_TRUNCATE_DEPTH);
+               break;
+       case 10:
+       default:
+               /* not needed */
+               break;
+       }
+
+       WREG32(FMT_BIT_DEPTH_CONTROL + radeon_crtc->crtc_offset, tmp);
+}
+
 /* get temperature in millidegrees */
 int rv6xx_get_temp(struct radeon_device *rdev)
 {
index 01a3ec8..5dceea6 100644 (file)
@@ -887,7 +887,7 @@ int r600_cs_common_vline_parse(struct radeon_cs_parser *p,
        obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj) {
                DRM_ERROR("cannot find crtc %d\n", crtc_id);
-               return -EINVAL;
+               return -ENOENT;
        }
        crtc = obj_to_crtc(obj);
        radeon_crtc = to_radeon_crtc(crtc);
@@ -2328,13 +2328,8 @@ static void r600_cs_parser_fini(struct radeon_cs_parser *parser, int error)
        unsigned i;
 
        kfree(parser->relocs);
-       for (i = 0; i < parser->nchunks; i++) {
-               kfree(parser->chunks[i].kdata);
-               if (parser->rdev && (parser->rdev->flags & RADEON_IS_AGP)) {
-                       kfree(parser->chunks[i].kpage[0]);
-                       kfree(parser->chunks[i].kpage[1]);
-               }
-       }
+       for (i = 0; i < parser->nchunks; i++)
+               drm_free_large(parser->chunks[i].kdata);
        kfree(parser->chunks);
        kfree(parser->chunks_array);
 }
@@ -2391,13 +2386,12 @@ int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp,
        ib_chunk = &parser.chunks[parser.chunk_ib_idx];
        parser.ib.length_dw = ib_chunk->length_dw;
        *l = parser.ib.length_dw;
-       r = r600_cs_parse(&parser);
-       if (r) {
-               DRM_ERROR("Invalid command stream !\n");
+       if (DRM_COPY_FROM_USER(ib, ib_chunk->user_ptr, ib_chunk->length_dw * 4)) {
+               r = -EFAULT;
                r600_cs_parser_fini(&parser, r);
                return r;
        }
-       r = radeon_cs_finish_pages(&parser);
+       r = r600_cs_parse(&parser);
        if (r) {
                DRM_ERROR("Invalid command stream !\n");
                r600_cs_parser_fini(&parser, r);
index 06022e3..4b89262 100644 (file)
@@ -24,6 +24,7 @@
  * Authors: Christian König
  */
 #include <linux/hdmi.h>
+#include <linux/gcd.h>
 #include <drm/drmP.h>
 #include <drm/radeon_drm.h>
 #include "radeon.h"
@@ -57,35 +58,57 @@ enum r600_hdmi_iec_status_bits {
 static const struct radeon_hdmi_acr r600_hdmi_predefined_acr[] = {
     /*      32kHz        44.1kHz       48kHz    */
     /* Clock      N     CTS      N     CTS      N     CTS */
-    {  25175,  4576,  28125,  7007,  31250,  6864,  28125 }, /*  25,20/1.001 MHz */
+    {  25175,  4096,  25175, 28224, 125875,  6144,  25175 }, /*  25,20/1.001 MHz */
     {  25200,  4096,  25200,  6272,  28000,  6144,  25200 }, /*  25.20       MHz */
     {  27000,  4096,  27000,  6272,  30000,  6144,  27000 }, /*  27.00       MHz */
     {  27027,  4096,  27027,  6272,  30030,  6144,  27027 }, /*  27.00*1.001 MHz */
     {  54000,  4096,  54000,  6272,  60000,  6144,  54000 }, /*  54.00       MHz */
     {  54054,  4096,  54054,  6272,  60060,  6144,  54054 }, /*  54.00*1.001 MHz */
-    {  74176, 11648, 210937, 17836, 234375, 11648, 140625 }, /*  74.25/1.001 MHz */
+    {  74176,  4096,  74176,  5733,  75335,  6144,  74176 }, /*  74.25/1.001 MHz */
     {  74250,  4096,  74250,  6272,  82500,  6144,  74250 }, /*  74.25       MHz */
-    { 148352, 11648, 421875,  8918, 234375,  5824, 140625 }, /* 148.50/1.001 MHz */
+    { 148352,  4096, 148352,  5733, 150670,  6144, 148352 }, /* 148.50/1.001 MHz */
     { 148500,  4096, 148500,  6272, 165000,  6144, 148500 }, /* 148.50       MHz */
-    {      0,  4096,      0,  6272,      0,  6144,      0 }  /* Other */
 };
 
+
 /*
- * calculate CTS value if it's not found in the table
+ * calculate CTS and N values if they are not found in the table
  */
-static void r600_hdmi_calc_cts(uint32_t clock, int *CTS, int N, int freq)
+static void r600_hdmi_calc_cts(uint32_t clock, int *CTS, int *N, int freq)
 {
-       u64 n;
-       u32 d;
-
-       if (*CTS == 0) {
-               n = (u64)clock * (u64)N * 1000ULL;
-               d = 128 * freq;
-               do_div(n, d);
-               *CTS = n;
-       }
-       DRM_DEBUG("Using ACR timing N=%d CTS=%d for frequency %d\n",
-                 N, *CTS, freq);
+       int n, cts;
+       unsigned long div, mul;
+
+       /* Safe, but overly large values */
+       n = 128 * freq;
+       cts = clock * 1000;
+
+       /* Smallest valid fraction */
+       div = gcd(n, cts);
+
+       n /= div;
+       cts /= div;
+
+       /*
+        * The optimal N is 128*freq/1000. Calculate the closest larger
+        * value that doesn't truncate any bits.
+        */
+       mul = ((128*freq/1000) + (n-1))/n;
+
+       n *= mul;
+       cts *= mul;
+
+       /* Check that we are in spec (not always possible) */
+       if (n < (128*freq/1500))
+               printk(KERN_WARNING "Calculated ACR N value is too small. You may experience audio problems.\n");
+       if (n > (128*freq/300))
+               printk(KERN_WARNING "Calculated ACR N value is too large. You may experience audio problems.\n");
+
+       *N = n;
+       *CTS = cts;
+
+       DRM_DEBUG("Calculated ACR timing N=%d CTS=%d for frequency %d\n",
+                 *N, *CTS, freq);
 }
 
 struct radeon_hdmi_acr r600_hdmi_acr(uint32_t clock)
@@ -93,15 +116,16 @@ struct radeon_hdmi_acr r600_hdmi_acr(uint32_t clock)
        struct radeon_hdmi_acr res;
        u8 i;
 
-       for (i = 0; r600_hdmi_predefined_acr[i].clock != clock &&
-            r600_hdmi_predefined_acr[i].clock != 0; i++)
-               ;
-       res = r600_hdmi_predefined_acr[i];
+       /* Precalculated values for common clocks */
+       for (i = 0; i < ARRAY_SIZE(r600_hdmi_predefined_acr); i++) {
+               if (r600_hdmi_predefined_acr[i].clock == clock)
+                       return r600_hdmi_predefined_acr[i];
+       }
 
-       /* In case some CTS are missing */
-       r600_hdmi_calc_cts(clock, &res.cts_32khz, res.n_32khz, 32000);
-       r600_hdmi_calc_cts(clock, &res.cts_44_1khz, res.n_44_1khz, 44100);
-       r600_hdmi_calc_cts(clock, &res.cts_48khz, res.n_48khz, 48000);
+       /* And odd clocks get manually calculated */
+       r600_hdmi_calc_cts(clock, &res.cts_32khz, &res.n_32khz, 32000);
+       r600_hdmi_calc_cts(clock, &res.cts_44_1khz, &res.n_44_1khz, 44100);
+       r600_hdmi_calc_cts(clock, &res.cts_48khz, &res.n_48khz, 48000);
 
        return res;
 }
@@ -313,8 +337,10 @@ static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder)
        return;
 
        list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
-               if (connector->encoder == encoder)
+               if (connector->encoder == encoder) {
                        radeon_connector = to_radeon_connector(connector);
+                       break;
+               }
        }
 
        if (!radeon_connector) {
@@ -366,8 +392,10 @@ static void dce3_2_afmt_write_sad_regs(struct drm_encoder *encoder)
        };
 
        list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
-               if (connector->encoder == encoder)
+               if (connector->encoder == encoder) {
                        radeon_connector = to_radeon_connector(connector);
+                       break;
+               }
        }
 
        if (!radeon_connector) {
@@ -384,20 +412,30 @@ static void dce3_2_afmt_write_sad_regs(struct drm_encoder *encoder)
 
        for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
                u32 value = 0;
+               u8 stereo_freqs = 0;
+               int max_channels = -1;
                int j;
 
                for (j = 0; j < sad_count; j++) {
                        struct cea_sad *sad = &sads[j];
 
                        if (sad->format == eld_reg_to_type[i][1]) {
-                               value = MAX_CHANNELS(sad->channels) |
-                                       DESCRIPTOR_BYTE_2(sad->byte2) |
-                                       SUPPORTED_FREQUENCIES(sad->freq);
+                               if (sad->channels > max_channels) {
+                                       value = MAX_CHANNELS(sad->channels) |
+                                               DESCRIPTOR_BYTE_2(sad->byte2) |
+                                               SUPPORTED_FREQUENCIES(sad->freq);
+                                       max_channels = sad->channels;
+                               }
+
                                if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
-                                       value |= SUPPORTED_FREQUENCIES_STEREO(sad->freq);
-                               break;
+                                       stereo_freqs |= sad->freq;
+                               else
+                                       break;
                        }
                }
+
+               value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs);
+
                WREG32(eld_reg_to_type[i][0], value);
        }
 
index 7b3c7b5..ebe3872 100644 (file)
 #       define AFMT_AZ_FORMAT_WTRIG_ACK      (1 << 29)
 #       define AFMT_AZ_AUDIO_ENABLE_CHG_ACK  (1 << 30)
 
+/* DCE3 FMT blocks */
+#define FMT_CONTROL                          0x6700
+#       define FMT_PIXEL_ENCODING            (1 << 16)
+        /* 0 = RGB 4:4:4 or YCbCr 4:4:4, 1 = YCbCr 4:2:2 */
+#define FMT_BIT_DEPTH_CONTROL                0x6710
+#       define FMT_TRUNCATE_EN               (1 << 0)
+#       define FMT_TRUNCATE_DEPTH            (1 << 4)
+#       define FMT_SPATIAL_DITHER_EN         (1 << 8)
+#       define FMT_SPATIAL_DITHER_MODE(x)    ((x) << 9)
+#       define FMT_SPATIAL_DITHER_DEPTH      (1 << 12)
+#       define FMT_FRAME_RANDOM_ENABLE       (1 << 13)
+#       define FMT_RGB_RANDOM_ENABLE         (1 << 14)
+#       define FMT_HIGHPASS_RANDOM_ENABLE    (1 << 15)
+#       define FMT_TEMPORAL_DITHER_EN        (1 << 16)
+#       define FMT_TEMPORAL_DITHER_DEPTH     (1 << 20)
+#       define FMT_TEMPORAL_DITHER_OFFSET(x) ((x) << 21)
+#       define FMT_TEMPORAL_LEVEL            (1 << 24)
+#       define FMT_TEMPORAL_DITHER_RESET     (1 << 25)
+#       define FMT_25FRC_SEL(x)              ((x) << 26)
+#       define FMT_50FRC_SEL(x)              ((x) << 28)
+#       define FMT_75FRC_SEL(x)              ((x) << 30)
+#define FMT_CLAMP_CONTROL                    0x672c
+#       define FMT_CLAMP_DATA_EN             (1 << 0)
+#       define FMT_CLAMP_COLOR_FORMAT(x)     ((x) << 16)
+#       define FMT_CLAMP_6BPC                0
+#       define FMT_CLAMP_8BPC                1
+#       define FMT_CLAMP_10BPC               2
+
 /* Power management */
 #define CG_SPLL_FUNC_CNTL                                 0x600
 #       define SPLL_RESET                                (1 << 0)
index 24f4960..b9ee992 100644 (file)
@@ -98,6 +98,7 @@ extern int radeon_lockup_timeout;
 extern int radeon_fastfb;
 extern int radeon_dpm;
 extern int radeon_aspm;
+extern int radeon_runtime_pm;
 
 /*
  * Copy from radeon_drv.h so we don't have to include both and have conflicting
@@ -327,7 +328,6 @@ struct radeon_fence_driver {
        /* sync_seq is protected by ring emission lock */
        uint64_t                        sync_seq[RADEON_NUM_RINGS];
        atomic64_t                      last_seq;
-       unsigned long                   last_activity;
        bool                            initialized;
 };
 
@@ -832,6 +832,12 @@ struct radeon_mec {
 #define RADEON_VM_PTB_ALIGN_MASK (RADEON_VM_PTB_ALIGN_SIZE - 1)
 #define RADEON_VM_PTB_ALIGN(a) (((a) + RADEON_VM_PTB_ALIGN_MASK) & ~RADEON_VM_PTB_ALIGN_MASK)
 
+#define R600_PTE_VALID         (1 << 0)
+#define R600_PTE_SYSTEM                (1 << 1)
+#define R600_PTE_SNOOPED       (1 << 2)
+#define R600_PTE_READABLE      (1 << 5)
+#define R600_PTE_WRITEABLE     (1 << 6)
+
 struct radeon_vm {
        struct list_head                list;
        struct list_head                va;
@@ -967,12 +973,8 @@ struct radeon_cs_reloc {
 struct radeon_cs_chunk {
        uint32_t                chunk_id;
        uint32_t                length_dw;
-       int                     kpage_idx[2];
-       uint32_t                *kpage[2];
        uint32_t                *kdata;
        void __user             *user_ptr;
-       int                     last_copied_page;
-       int                     last_page_index;
 };
 
 struct radeon_cs_parser {
@@ -1007,8 +1009,15 @@ struct radeon_cs_parser {
        struct ww_acquire_ctx   ticket;
 };
 
-extern int radeon_cs_finish_pages(struct radeon_cs_parser *p);
-extern u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx);
+static inline u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx)
+{
+       struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
+
+       if (ibc->kdata)
+               return ibc->kdata[idx];
+       return p->ib.ptr[idx];
+}
+
 
 struct radeon_cs_packet {
        unsigned        idx;
@@ -1675,8 +1684,6 @@ struct radeon_asic {
        struct {
                int (*init)(struct radeon_device *rdev);
                void (*fini)(struct radeon_device *rdev);
-
-               u32 pt_ring_index;
                void (*set_page)(struct radeon_device *rdev,
                                 struct radeon_ib *ib,
                                 uint64_t pe,
@@ -2170,6 +2177,7 @@ struct radeon_device {
        bool                            need_dma32;
        bool                            accel_working;
        bool                            fastfb_working; /* IGP feature*/
+       bool                            needs_reset;
        struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES];
        const struct firmware *me_fw;   /* all family ME firmware */
        const struct firmware *pfp_fw;  /* r6/700 PFP firmware */
@@ -2212,6 +2220,9 @@ struct radeon_device {
        /* clock, powergating flags */
        u32 cg_flags;
        u32 pg_flags;
+
+       struct dev_pm_domain vga_pm_domain;
+       bool have_disp_power_ref;
 };
 
 int radeon_device_init(struct radeon_device *rdev,
@@ -2673,8 +2684,8 @@ extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
 extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
 extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base);
 extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
-extern int radeon_resume_kms(struct drm_device *dev);
-extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state);
+extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
+extern int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon);
 extern void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size);
 extern void radeon_program_register_sequence(struct radeon_device *rdev,
                                             const u32 *registers,
index 10f98c7..98a9074 100644 (file)
@@ -369,7 +369,7 @@ int radeon_atif_handler(struct radeon_device *rdev,
                return NOTIFY_DONE;
 
        /* Check pending SBIOS requests */
-       handle = DEVICE_ACPI_HANDLE(&rdev->pdev->dev);
+       handle = ACPI_HANDLE(&rdev->pdev->dev);
        count = radeon_atif_get_sbios_requests(handle, &req);
 
        if (count <= 0)
@@ -556,7 +556,7 @@ int radeon_acpi_pcie_notify_device_ready(struct radeon_device *rdev)
        struct radeon_atcs *atcs = &rdev->atcs;
 
        /* Get the device handle */
-       handle = DEVICE_ACPI_HANDLE(&rdev->pdev->dev);
+       handle = ACPI_HANDLE(&rdev->pdev->dev);
        if (!handle)
                return -EINVAL;
 
@@ -596,7 +596,7 @@ int radeon_acpi_pcie_performance_request(struct radeon_device *rdev,
        u32 retry = 3;
 
        /* Get the device handle */
-       handle = DEVICE_ACPI_HANDLE(&rdev->pdev->dev);
+       handle = ACPI_HANDLE(&rdev->pdev->dev);
        if (!handle)
                return -EINVAL;
 
@@ -699,7 +699,7 @@ int radeon_acpi_init(struct radeon_device *rdev)
        int ret;
 
        /* Get the device handle */
-       handle = DEVICE_ACPI_HANDLE(&rdev->pdev->dev);
+       handle = ACPI_HANDLE(&rdev->pdev->dev);
 
        /* No need to proceed if we're sure that ATIF is not supported */
        if (!ASIC_IS_AVIVO(rdev) || !rdev->bios || !handle)
index 8f7e045..50853c0 100644 (file)
@@ -1622,8 +1622,7 @@ static struct radeon_asic cayman_asic = {
        .vm = {
                .init = &cayman_vm_init,
                .fini = &cayman_vm_fini,
-               .pt_ring_index = R600_RING_TYPE_DMA_INDEX,
-               .set_page = &cayman_vm_set_page,
+               .set_page = &cayman_dma_vm_set_page,
        },
        .ring = {
                [RADEON_RING_TYPE_GFX_INDEX] = &cayman_gfx_ring,
@@ -1723,8 +1722,7 @@ static struct radeon_asic trinity_asic = {
        .vm = {
                .init = &cayman_vm_init,
                .fini = &cayman_vm_fini,
-               .pt_ring_index = R600_RING_TYPE_DMA_INDEX,
-               .set_page = &cayman_vm_set_page,
+               .set_page = &cayman_dma_vm_set_page,
        },
        .ring = {
                [RADEON_RING_TYPE_GFX_INDEX] = &cayman_gfx_ring,
@@ -1854,8 +1852,7 @@ static struct radeon_asic si_asic = {
        .vm = {
                .init = &si_vm_init,
                .fini = &si_vm_fini,
-               .pt_ring_index = R600_RING_TYPE_DMA_INDEX,
-               .set_page = &si_vm_set_page,
+               .set_page = &si_dma_vm_set_page,
        },
        .ring = {
                [RADEON_RING_TYPE_GFX_INDEX] = &si_gfx_ring,
@@ -1879,7 +1876,7 @@ static struct radeon_asic si_asic = {
                .hdmi_setmode = &evergreen_hdmi_setmode,
        },
        .copy = {
-               .blit = NULL,
+               .blit = &r600_copy_cpdma,
                .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
                .dma = &si_copy_dma,
                .dma_ring_index = R600_RING_TYPE_DMA_INDEX,
@@ -2000,8 +1997,7 @@ static struct radeon_asic ci_asic = {
        .vm = {
                .init = &cik_vm_init,
                .fini = &cik_vm_fini,
-               .pt_ring_index = R600_RING_TYPE_DMA_INDEX,
-               .set_page = &cik_vm_set_page,
+               .set_page = &cik_sdma_vm_set_page,
        },
        .ring = {
                [RADEON_RING_TYPE_GFX_INDEX] = &ci_gfx_ring,
@@ -2100,8 +2096,7 @@ static struct radeon_asic kv_asic = {
        .vm = {
                .init = &cik_vm_init,
                .fini = &cik_vm_fini,
-               .pt_ring_index = R600_RING_TYPE_DMA_INDEX,
-               .set_page = &cik_vm_set_page,
+               .set_page = &cik_sdma_vm_set_page,
        },
        .ring = {
                [RADEON_RING_TYPE_GFX_INDEX] = &ci_gfx_ring,
@@ -2442,27 +2437,48 @@ int radeon_asic_init(struct radeon_device *rdev)
                }
                break;
        case CHIP_BONAIRE:
+       case CHIP_HAWAII:
                rdev->asic = &ci_asic;
                rdev->num_crtc = 6;
                rdev->has_uvd = true;
-               rdev->cg_flags =
-                       RADEON_CG_SUPPORT_GFX_MGCG |
-                       RADEON_CG_SUPPORT_GFX_MGLS |
-                       /*RADEON_CG_SUPPORT_GFX_CGCG |*/
-                       RADEON_CG_SUPPORT_GFX_CGLS |
-                       RADEON_CG_SUPPORT_GFX_CGTS |
-                       RADEON_CG_SUPPORT_GFX_CGTS_LS |
-                       RADEON_CG_SUPPORT_GFX_CP_LS |
-                       RADEON_CG_SUPPORT_MC_LS |
-                       RADEON_CG_SUPPORT_MC_MGCG |
-                       RADEON_CG_SUPPORT_SDMA_MGCG |
-                       RADEON_CG_SUPPORT_SDMA_LS |
-                       RADEON_CG_SUPPORT_BIF_LS |
-                       RADEON_CG_SUPPORT_VCE_MGCG |
-                       RADEON_CG_SUPPORT_UVD_MGCG |
-                       RADEON_CG_SUPPORT_HDP_LS |
-                       RADEON_CG_SUPPORT_HDP_MGCG;
-               rdev->pg_flags = 0;
+               if (rdev->family == CHIP_BONAIRE) {
+                       rdev->cg_flags =
+                               RADEON_CG_SUPPORT_GFX_MGCG |
+                               RADEON_CG_SUPPORT_GFX_MGLS |
+                               /*RADEON_CG_SUPPORT_GFX_CGCG |*/
+                               RADEON_CG_SUPPORT_GFX_CGLS |
+                               RADEON_CG_SUPPORT_GFX_CGTS |
+                               RADEON_CG_SUPPORT_GFX_CGTS_LS |
+                               RADEON_CG_SUPPORT_GFX_CP_LS |
+                               RADEON_CG_SUPPORT_MC_LS |
+                               RADEON_CG_SUPPORT_MC_MGCG |
+                               RADEON_CG_SUPPORT_SDMA_MGCG |
+                               RADEON_CG_SUPPORT_SDMA_LS |
+                               RADEON_CG_SUPPORT_BIF_LS |
+                               RADEON_CG_SUPPORT_VCE_MGCG |
+                               RADEON_CG_SUPPORT_UVD_MGCG |
+                               RADEON_CG_SUPPORT_HDP_LS |
+                               RADEON_CG_SUPPORT_HDP_MGCG;
+                       rdev->pg_flags = 0;
+               } else {
+                       rdev->cg_flags =
+                               RADEON_CG_SUPPORT_GFX_MGCG |
+                               RADEON_CG_SUPPORT_GFX_MGLS |
+                               /*RADEON_CG_SUPPORT_GFX_CGCG |*/
+                               RADEON_CG_SUPPORT_GFX_CGLS |
+                               RADEON_CG_SUPPORT_GFX_CGTS |
+                               RADEON_CG_SUPPORT_GFX_CP_LS |
+                               RADEON_CG_SUPPORT_MC_LS |
+                               RADEON_CG_SUPPORT_MC_MGCG |
+                               RADEON_CG_SUPPORT_SDMA_MGCG |
+                               RADEON_CG_SUPPORT_SDMA_LS |
+                               RADEON_CG_SUPPORT_BIF_LS |
+                               RADEON_CG_SUPPORT_VCE_MGCG |
+                               RADEON_CG_SUPPORT_UVD_MGCG |
+                               RADEON_CG_SUPPORT_HDP_LS |
+                               RADEON_CG_SUPPORT_HDP_MGCG;
+                       rdev->pg_flags = 0;
+               }
                break;
        case CHIP_KAVERI:
        case CHIP_KABINI:
index 70c29d5..f2833ee 100644 (file)
@@ -581,17 +581,18 @@ int cayman_vm_init(struct radeon_device *rdev);
 void cayman_vm_fini(struct radeon_device *rdev);
 void cayman_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
 uint32_t cayman_vm_page_flags(struct radeon_device *rdev, uint32_t flags);
-void cayman_vm_set_page(struct radeon_device *rdev,
-                       struct radeon_ib *ib,
-                       uint64_t pe,
-                       uint64_t addr, unsigned count,
-                       uint32_t incr, uint32_t flags);
 int evergreen_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
 int evergreen_dma_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
 void cayman_dma_ring_ib_execute(struct radeon_device *rdev,
                                struct radeon_ib *ib);
 bool cayman_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring);
 bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring);
+void cayman_dma_vm_set_page(struct radeon_device *rdev,
+                           struct radeon_ib *ib,
+                           uint64_t pe,
+                           uint64_t addr, unsigned count,
+                           uint32_t incr, uint32_t flags);
+
 void cayman_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
 
 int ni_dpm_init(struct radeon_device *rdev);
@@ -653,17 +654,17 @@ int si_irq_set(struct radeon_device *rdev);
 int si_irq_process(struct radeon_device *rdev);
 int si_vm_init(struct radeon_device *rdev);
 void si_vm_fini(struct radeon_device *rdev);
-void si_vm_set_page(struct radeon_device *rdev,
-                   struct radeon_ib *ib,
-                   uint64_t pe,
-                   uint64_t addr, unsigned count,
-                   uint32_t incr, uint32_t flags);
 void si_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
 int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
 int si_copy_dma(struct radeon_device *rdev,
                uint64_t src_offset, uint64_t dst_offset,
                unsigned num_gpu_pages,
                struct radeon_fence **fence);
+void si_dma_vm_set_page(struct radeon_device *rdev,
+                       struct radeon_ib *ib,
+                       uint64_t pe,
+                       uint64_t addr, unsigned count,
+                       uint32_t incr, uint32_t flags);
 void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
 u32 si_get_xclk(struct radeon_device *rdev);
 uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev);
@@ -705,6 +706,10 @@ int cik_copy_dma(struct radeon_device *rdev,
                 uint64_t src_offset, uint64_t dst_offset,
                 unsigned num_gpu_pages,
                 struct radeon_fence **fence);
+int cik_copy_cpdma(struct radeon_device *rdev,
+                  uint64_t src_offset, uint64_t dst_offset,
+                  unsigned num_gpu_pages,
+                  struct radeon_fence **fence);
 int cik_sdma_ring_test(struct radeon_device *rdev, struct radeon_ring *ring);
 int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring);
 bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring);
@@ -731,11 +736,11 @@ int cik_irq_process(struct radeon_device *rdev);
 int cik_vm_init(struct radeon_device *rdev);
 void cik_vm_fini(struct radeon_device *rdev);
 void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
-void cik_vm_set_page(struct radeon_device *rdev,
-                    struct radeon_ib *ib,
-                    uint64_t pe,
-                    uint64_t addr, unsigned count,
-                    uint32_t incr, uint32_t flags);
+void cik_sdma_vm_set_page(struct radeon_device *rdev,
+                         struct radeon_ib *ib,
+                         uint64_t pe,
+                         uint64_t addr, unsigned count,
+                         uint32_t incr, uint32_t flags);
 void cik_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
 int cik_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
 u32 cik_compute_ring_get_rptr(struct radeon_device *rdev,
index d96070b..9d302ea 100644 (file)
@@ -8,8 +8,7 @@
  */
 #include <linux/vga_switcheroo.h>
 #include <linux/slab.h>
-#include <acpi/acpi.h>
-#include <acpi/acpi_bus.h>
+#include <linux/acpi.h>
 #include <linux/pci.h>
 
 #include "radeon_acpi.h"
@@ -59,6 +58,10 @@ struct atpx_mux {
        u16 mux;
 } __packed;
 
+bool radeon_is_px(void) {
+       return radeon_atpx_priv.atpx_detected;
+}
+
 /**
  * radeon_atpx_call - call an ATPX method
  *
@@ -443,7 +446,7 @@ static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev)
        acpi_handle dhandle, atpx_handle;
        acpi_status status;
 
-       dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
+       dhandle = ACPI_HANDLE(&pdev->dev);
        if (!dhandle)
                return false;
 
@@ -489,7 +492,7 @@ static int radeon_atpx_init(void)
  */
 static int radeon_atpx_get_client_id(struct pci_dev *pdev)
 {
-       if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
+       if (radeon_atpx_priv.dhandle == ACPI_HANDLE(&pdev->dev))
                return VGA_SWITCHEROO_IGD;
        else
                return VGA_SWITCHEROO_DIS;
index 061b227..b3633d9 100644 (file)
@@ -185,7 +185,7 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev)
                return false;
 
        while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
-               dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
+               dhandle = ACPI_HANDLE(&pdev->dev);
                if (!dhandle)
                        continue;
 
@@ -499,7 +499,7 @@ static bool legacy_read_disabled_bios(struct radeon_device *rdev)
        crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL);
        fp2_gen_cntl = 0;
 
-       if (rdev->ddev->pci_device == PCI_DEVICE_ID_ATI_RADEON_QY) {
+       if (rdev->ddev->pdev->device == PCI_DEVICE_ID_ATI_RADEON_QY) {
                fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL);
        }
 
@@ -536,7 +536,7 @@ static bool legacy_read_disabled_bios(struct radeon_device *rdev)
                (RADEON_CRTC_SYNC_TRISTAT |
                 RADEON_CRTC_DISPLAY_DIS)));
 
-       if (rdev->ddev->pci_device == PCI_DEVICE_ID_ATI_RADEON_QY) {
+       if (rdev->ddev->pdev->device == PCI_DEVICE_ID_ATI_RADEON_QY) {
                WREG32(RADEON_FP2_GEN_CNTL, (fp2_gen_cntl & ~RADEON_FP2_ON));
        }
 
@@ -554,7 +554,7 @@ static bool legacy_read_disabled_bios(struct radeon_device *rdev)
                WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl);
        }
        WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl);
-       if (rdev->ddev->pci_device == PCI_DEVICE_ID_ATI_RADEON_QY) {
+       if (rdev->ddev->pdev->device == PCI_DEVICE_ID_ATI_RADEON_QY) {
                WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl);
        }
        return r;
index 6456573..20a768a 100644 (file)
@@ -31,6 +31,8 @@
 #include "radeon.h"
 #include "atom.h"
 
+#include <linux/pm_runtime.h>
+
 extern void
 radeon_combios_connected_scratch_regs(struct drm_connector *connector,
                                      struct drm_encoder *encoder,
@@ -411,6 +413,21 @@ static int radeon_connector_set_property(struct drm_connector *connector, struct
                }
        }
 
+       if (property == rdev->mode_info.dither_property) {
+               struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+               /* need to find digital encoder on connector */
+               encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS);
+               if (!encoder)
+                       return 0;
+
+               radeon_encoder = to_radeon_encoder(encoder);
+
+               if (radeon_connector->dither != val) {
+                       radeon_connector->dither = val;
+                       radeon_property_change_mode(&radeon_encoder->base);
+               }
+       }
+
        if (property == rdev->mode_info.underscan_property) {
                /* need to find digital encoder on connector */
                encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS);
@@ -626,6 +643,11 @@ radeon_lvds_detect(struct drm_connector *connector, bool force)
        struct radeon_connector *radeon_connector = to_radeon_connector(connector);
        struct drm_encoder *encoder = radeon_best_single_encoder(connector);
        enum drm_connector_status ret = connector_status_disconnected;
+       int r;
+
+       r = pm_runtime_get_sync(connector->dev->dev);
+       if (r < 0)
+               return connector_status_disconnected;
 
        if (encoder) {
                struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
@@ -651,6 +673,8 @@ radeon_lvds_detect(struct drm_connector *connector, bool force)
        /* check acpi lid status ??? */
 
        radeon_connector_update_scratch_regs(connector, ret);
+       pm_runtime_mark_last_busy(connector->dev->dev);
+       pm_runtime_put_autosuspend(connector->dev->dev);
        return ret;
 }
 
@@ -750,6 +774,11 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
        struct drm_encoder_helper_funcs *encoder_funcs;
        bool dret = false;
        enum drm_connector_status ret = connector_status_disconnected;
+       int r;
+
+       r = pm_runtime_get_sync(connector->dev->dev);
+       if (r < 0)
+               return connector_status_disconnected;
 
        encoder = radeon_best_single_encoder(connector);
        if (!encoder)
@@ -790,9 +819,8 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
                         * detected a monitor via load.
                         */
                        if (radeon_connector->detected_by_load)
-                               return connector->status;
-                       else
-                               return ret;
+                               ret = connector->status;
+                       goto out;
                }
 
                if (radeon_connector->dac_load_detect && encoder) {
@@ -817,6 +845,11 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
        }
 
        radeon_connector_update_scratch_regs(connector, ret);
+
+out:
+       pm_runtime_mark_last_busy(connector->dev->dev);
+       pm_runtime_put_autosuspend(connector->dev->dev);
+
        return ret;
 }
 
@@ -873,10 +906,15 @@ radeon_tv_detect(struct drm_connector *connector, bool force)
        struct drm_encoder_helper_funcs *encoder_funcs;
        struct radeon_connector *radeon_connector = to_radeon_connector(connector);
        enum drm_connector_status ret = connector_status_disconnected;
+       int r;
 
        if (!radeon_connector->dac_load_detect)
                return ret;
 
+       r = pm_runtime_get_sync(connector->dev->dev);
+       if (r < 0)
+               return connector_status_disconnected;
+
        encoder = radeon_best_single_encoder(connector);
        if (!encoder)
                ret = connector_status_disconnected;
@@ -887,6 +925,8 @@ radeon_tv_detect(struct drm_connector *connector, bool force)
        if (ret == connector_status_connected)
                ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false);
        radeon_connector_update_scratch_regs(connector, ret);
+       pm_runtime_mark_last_busy(connector->dev->dev);
+       pm_runtime_put_autosuspend(connector->dev->dev);
        return ret;
 }
 
@@ -954,12 +994,18 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
        struct drm_encoder *encoder = NULL;
        struct drm_encoder_helper_funcs *encoder_funcs;
        struct drm_mode_object *obj;
-       int i;
+       int i, r;
        enum drm_connector_status ret = connector_status_disconnected;
        bool dret = false, broken_edid = false;
 
-       if (!force && radeon_check_hpd_status_unchanged(connector))
-               return connector->status;
+       r = pm_runtime_get_sync(connector->dev->dev);
+       if (r < 0)
+               return connector_status_disconnected;
+
+       if (!force && radeon_check_hpd_status_unchanged(connector)) {
+               ret = connector->status;
+               goto exit;
+       }
 
        if (radeon_connector->ddc_bus)
                dret = radeon_ddc_probe(radeon_connector, false);
@@ -1110,6 +1156,11 @@ out:
 
        /* updated in get modes as well since we need to know if it's analog or digital */
        radeon_connector_update_scratch_regs(connector, ret);
+
+exit:
+       pm_runtime_mark_last_busy(connector->dev->dev);
+       pm_runtime_put_autosuspend(connector->dev->dev);
+
        return ret;
 }
 
@@ -1377,9 +1428,16 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
        enum drm_connector_status ret = connector_status_disconnected;
        struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
        struct drm_encoder *encoder = radeon_best_single_encoder(connector);
+       int r;
 
-       if (!force && radeon_check_hpd_status_unchanged(connector))
-               return connector->status;
+       r = pm_runtime_get_sync(connector->dev->dev);
+       if (r < 0)
+               return connector_status_disconnected;
+
+       if (!force && radeon_check_hpd_status_unchanged(connector)) {
+               ret = connector->status;
+               goto out;
+       }
 
        if (radeon_connector->edid) {
                kfree(radeon_connector->edid);
@@ -1443,6 +1501,10 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
        }
 
        radeon_connector_update_scratch_regs(connector, ret);
+out:
+       pm_runtime_mark_last_busy(connector->dev->dev);
+       pm_runtime_put_autosuspend(connector->dev->dev);
+
        return ret;
 }
 
@@ -1658,12 +1720,16 @@ radeon_add_atom_connector(struct drm_device *dev,
                        drm_object_attach_property(&radeon_connector->base.base,
                                                      rdev->mode_info.underscan_vborder_property,
                                                      0);
+
+                       drm_object_attach_property(&radeon_connector->base.base,
+                                                  rdev->mode_info.dither_property,
+                                                  RADEON_FMT_DITHER_DISABLE);
+
                        if (radeon_audio != 0)
                                drm_object_attach_property(&radeon_connector->base.base,
                                                           rdev->mode_info.audio_property,
-                                                          (radeon_audio == 1) ?
-                                                          RADEON_AUDIO_AUTO :
-                                                          RADEON_AUDIO_DISABLE);
+                                                          RADEON_AUDIO_AUTO);
+
                        subpixel_order = SubPixelHorizontalRGB;
                        connector->interlace_allowed = true;
                        if (connector_type == DRM_MODE_CONNECTOR_HDMIB)
@@ -1760,9 +1826,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                        if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
                                drm_object_attach_property(&radeon_connector->base.base,
                                                           rdev->mode_info.audio_property,
-                                                          (radeon_audio == 1) ?
-                                                          RADEON_AUDIO_AUTO :
-                                                          RADEON_AUDIO_DISABLE);
+                                                          RADEON_AUDIO_AUTO);
+                       }
+                       if (ASIC_IS_AVIVO(rdev)) {
+                               drm_object_attach_property(&radeon_connector->base.base,
+                                                          rdev->mode_info.dither_property,
+                                                          RADEON_FMT_DITHER_DISABLE);
                        }
                        if (connector_type == DRM_MODE_CONNECTOR_DVII) {
                                radeon_connector->dac_load_detect = true;
@@ -1807,9 +1876,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                        if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
                                drm_object_attach_property(&radeon_connector->base.base,
                                                           rdev->mode_info.audio_property,
-                                                          (radeon_audio == 1) ?
-                                                          RADEON_AUDIO_AUTO :
-                                                          RADEON_AUDIO_DISABLE);
+                                                          RADEON_AUDIO_AUTO);
+                       }
+                       if (ASIC_IS_AVIVO(rdev)) {
+                               drm_object_attach_property(&radeon_connector->base.base,
+                                                          rdev->mode_info.dither_property,
+                                                          RADEON_FMT_DITHER_DISABLE);
                        }
                        subpixel_order = SubPixelHorizontalRGB;
                        connector->interlace_allowed = true;
@@ -1853,9 +1925,13 @@ radeon_add_atom_connector(struct drm_device *dev,
                        if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
                                drm_object_attach_property(&radeon_connector->base.base,
                                                           rdev->mode_info.audio_property,
-                                                          (radeon_audio == 1) ?
-                                                          RADEON_AUDIO_AUTO :
-                                                          RADEON_AUDIO_DISABLE);
+                                                          RADEON_AUDIO_AUTO);
+                       }
+                       if (ASIC_IS_AVIVO(rdev)) {
+                               drm_object_attach_property(&radeon_connector->base.base,
+                                                          rdev->mode_info.dither_property,
+                                                          RADEON_FMT_DITHER_DISABLE);
+
                        }
                        connector->interlace_allowed = true;
                        /* in theory with a DP to VGA converter... */
index 80285e3..26ca223 100644 (file)
@@ -212,9 +212,7 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
                        return -EFAULT;
                }
                p->chunks[i].length_dw = user_chunk.length_dw;
-               p->chunks[i].kdata = NULL;
                p->chunks[i].chunk_id = user_chunk.chunk_id;
-               p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data;
                if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) {
                        p->chunk_relocs_idx = i;
                }
@@ -237,25 +235,31 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
                                return -EINVAL;
                }
 
-               cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data;
-               if ((p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) ||
-                   (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS)) {
-                       size = p->chunks[i].length_dw * sizeof(uint32_t);
-                       p->chunks[i].kdata = kmalloc(size, GFP_KERNEL);
-                       if (p->chunks[i].kdata == NULL) {
-                               return -ENOMEM;
-                       }
-                       if (DRM_COPY_FROM_USER(p->chunks[i].kdata,
-                                              p->chunks[i].user_ptr, size)) {
-                               return -EFAULT;
-                       }
-                       if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) {
-                               p->cs_flags = p->chunks[i].kdata[0];
-                               if (p->chunks[i].length_dw > 1)
-                                       ring = p->chunks[i].kdata[1];
-                               if (p->chunks[i].length_dw > 2)
-                                       priority = (s32)p->chunks[i].kdata[2];
-                       }
+               size = p->chunks[i].length_dw;
+               cdata = (void __user *)(unsigned long)user_chunk.chunk_data;
+               p->chunks[i].user_ptr = cdata;
+               if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_CONST_IB)
+                       continue;
+
+               if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_IB) {
+                       if (!p->rdev || !(p->rdev->flags & RADEON_IS_AGP))
+                               continue;
+               }
+
+               p->chunks[i].kdata = drm_malloc_ab(size, sizeof(uint32_t));
+               size *= sizeof(uint32_t);
+               if (p->chunks[i].kdata == NULL) {
+                       return -ENOMEM;
+               }
+               if (DRM_COPY_FROM_USER(p->chunks[i].kdata, cdata, size)) {
+                       return -EFAULT;
+               }
+               if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) {
+                       p->cs_flags = p->chunks[i].kdata[0];
+                       if (p->chunks[i].length_dw > 1)
+                               ring = p->chunks[i].kdata[1];
+                       if (p->chunks[i].length_dw > 2)
+                               priority = (s32)p->chunks[i].kdata[2];
                }
        }
 
@@ -278,34 +282,6 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
                }
        }
 
-       /* deal with non-vm */
-       if ((p->chunk_ib_idx != -1) &&
-           ((p->cs_flags & RADEON_CS_USE_VM) == 0) &&
-           (p->chunks[p->chunk_ib_idx].chunk_id == RADEON_CHUNK_ID_IB)) {
-               if (p->chunks[p->chunk_ib_idx].length_dw > (16 * 1024)) {
-                       DRM_ERROR("cs IB too big: %d\n",
-                                 p->chunks[p->chunk_ib_idx].length_dw);
-                       return -EINVAL;
-               }
-               if (p->rdev && (p->rdev->flags & RADEON_IS_AGP)) {
-                       p->chunks[p->chunk_ib_idx].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL);
-                       p->chunks[p->chunk_ib_idx].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL);
-                       if (p->chunks[p->chunk_ib_idx].kpage[0] == NULL ||
-                           p->chunks[p->chunk_ib_idx].kpage[1] == NULL) {
-                               kfree(p->chunks[p->chunk_ib_idx].kpage[0]);
-                               kfree(p->chunks[p->chunk_ib_idx].kpage[1]);
-                               p->chunks[p->chunk_ib_idx].kpage[0] = NULL;
-                               p->chunks[p->chunk_ib_idx].kpage[1] = NULL;
-                               return -ENOMEM;
-                       }
-               }
-               p->chunks[p->chunk_ib_idx].kpage_idx[0] = -1;
-               p->chunks[p->chunk_ib_idx].kpage_idx[1] = -1;
-               p->chunks[p->chunk_ib_idx].last_copied_page = -1;
-               p->chunks[p->chunk_ib_idx].last_page_index =
-                       ((p->chunks[p->chunk_ib_idx].length_dw * 4) - 1) / PAGE_SIZE;
-       }
-
        return 0;
 }
 
@@ -339,13 +315,8 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bo
        kfree(parser->track);
        kfree(parser->relocs);
        kfree(parser->relocs_ptr);
-       for (i = 0; i < parser->nchunks; i++) {
-               kfree(parser->chunks[i].kdata);
-               if ((parser->rdev->flags & RADEON_IS_AGP)) {
-                       kfree(parser->chunks[i].kpage[0]);
-                       kfree(parser->chunks[i].kpage[1]);
-               }
-       }
+       for (i = 0; i < parser->nchunks; i++)
+               drm_free_large(parser->chunks[i].kdata);
        kfree(parser->chunks);
        kfree(parser->chunks_array);
        radeon_ib_free(parser->rdev, &parser->ib);
@@ -355,7 +326,6 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bo
 static int radeon_cs_ib_chunk(struct radeon_device *rdev,
                              struct radeon_cs_parser *parser)
 {
-       struct radeon_cs_chunk *ib_chunk;
        int r;
 
        if (parser->chunk_ib_idx == -1)
@@ -364,28 +334,11 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev,
        if (parser->cs_flags & RADEON_CS_USE_VM)
                return 0;
 
-       ib_chunk = &parser->chunks[parser->chunk_ib_idx];
-       /* Copy the packet into the IB, the parser will read from the
-        * input memory (cached) and write to the IB (which can be
-        * uncached).
-        */
-       r =  radeon_ib_get(rdev, parser->ring, &parser->ib,
-                          NULL, ib_chunk->length_dw * 4);
-       if (r) {
-               DRM_ERROR("Failed to get ib !\n");
-               return r;
-       }
-       parser->ib.length_dw = ib_chunk->length_dw;
        r = radeon_cs_parse(rdev, parser->ring, parser);
        if (r || parser->parser_error) {
                DRM_ERROR("Invalid command stream !\n");
                return r;
        }
-       r = radeon_cs_finish_pages(parser);
-       if (r) {
-               DRM_ERROR("Invalid command stream !\n");
-               return r;
-       }
 
        if (parser->ring == R600_RING_TYPE_UVD_INDEX)
                radeon_uvd_note_usage(rdev);
@@ -423,7 +376,6 @@ static int radeon_bo_vm_update_pte(struct radeon_cs_parser *parser,
 static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
                                 struct radeon_cs_parser *parser)
 {
-       struct radeon_cs_chunk *ib_chunk;
        struct radeon_fpriv *fpriv = parser->filp->driver_priv;
        struct radeon_vm *vm = &fpriv->vm;
        int r;
@@ -433,49 +385,13 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
        if ((parser->cs_flags & RADEON_CS_USE_VM) == 0)
                return 0;
 
-       if ((rdev->family >= CHIP_TAHITI) &&
-           (parser->chunk_const_ib_idx != -1)) {
-               ib_chunk = &parser->chunks[parser->chunk_const_ib_idx];
-               if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) {
-                       DRM_ERROR("cs IB CONST too big: %d\n", ib_chunk->length_dw);
-                       return -EINVAL;
-               }
-               r =  radeon_ib_get(rdev, parser->ring, &parser->const_ib,
-                                  vm, ib_chunk->length_dw * 4);
-               if (r) {
-                       DRM_ERROR("Failed to get const ib !\n");
-                       return r;
-               }
-               parser->const_ib.is_const_ib = true;
-               parser->const_ib.length_dw = ib_chunk->length_dw;
-               /* Copy the packet into the IB */
-               if (DRM_COPY_FROM_USER(parser->const_ib.ptr, ib_chunk->user_ptr,
-                                      ib_chunk->length_dw * 4)) {
-                       return -EFAULT;
-               }
+       if (parser->const_ib.length_dw) {
                r = radeon_ring_ib_parse(rdev, parser->ring, &parser->const_ib);
                if (r) {
                        return r;
                }
        }
 
-       ib_chunk = &parser->chunks[parser->chunk_ib_idx];
-       if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) {
-               DRM_ERROR("cs IB too big: %d\n", ib_chunk->length_dw);
-               return -EINVAL;
-       }
-       r =  radeon_ib_get(rdev, parser->ring, &parser->ib,
-                          vm, ib_chunk->length_dw * 4);
-       if (r) {
-               DRM_ERROR("Failed to get ib !\n");
-               return r;
-       }
-       parser->ib.length_dw = ib_chunk->length_dw;
-       /* Copy the packet into the IB */
-       if (DRM_COPY_FROM_USER(parser->ib.ptr, ib_chunk->user_ptr,
-                              ib_chunk->length_dw * 4)) {
-               return -EFAULT;
-       }
        r = radeon_ring_ib_parse(rdev, parser->ring, &parser->ib);
        if (r) {
                return r;
@@ -527,6 +443,62 @@ static int radeon_cs_handle_lockup(struct radeon_device *rdev, int r)
        return r;
 }
 
+static int radeon_cs_ib_fill(struct radeon_device *rdev, struct radeon_cs_parser *parser)
+{
+       struct radeon_cs_chunk *ib_chunk;
+       struct radeon_vm *vm = NULL;
+       int r;
+
+       if (parser->chunk_ib_idx == -1)
+               return 0;
+
+       if (parser->cs_flags & RADEON_CS_USE_VM) {
+               struct radeon_fpriv *fpriv = parser->filp->driver_priv;
+               vm = &fpriv->vm;
+
+               if ((rdev->family >= CHIP_TAHITI) &&
+                   (parser->chunk_const_ib_idx != -1)) {
+                       ib_chunk = &parser->chunks[parser->chunk_const_ib_idx];
+                       if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) {
+                               DRM_ERROR("cs IB CONST too big: %d\n", ib_chunk->length_dw);
+                               return -EINVAL;
+                       }
+                       r =  radeon_ib_get(rdev, parser->ring, &parser->const_ib,
+                                          vm, ib_chunk->length_dw * 4);
+                       if (r) {
+                               DRM_ERROR("Failed to get const ib !\n");
+                               return r;
+                       }
+                       parser->const_ib.is_const_ib = true;
+                       parser->const_ib.length_dw = ib_chunk->length_dw;
+                       if (DRM_COPY_FROM_USER(parser->const_ib.ptr,
+                                              ib_chunk->user_ptr,
+                                              ib_chunk->length_dw * 4))
+                               return -EFAULT;
+               }
+
+               ib_chunk = &parser->chunks[parser->chunk_ib_idx];
+               if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) {
+                       DRM_ERROR("cs IB too big: %d\n", ib_chunk->length_dw);
+                       return -EINVAL;
+               }
+       }
+       ib_chunk = &parser->chunks[parser->chunk_ib_idx];
+
+       r =  radeon_ib_get(rdev, parser->ring, &parser->ib,
+                          vm, ib_chunk->length_dw * 4);
+       if (r) {
+               DRM_ERROR("Failed to get ib !\n");
+               return r;
+       }
+       parser->ib.length_dw = ib_chunk->length_dw;
+       if (ib_chunk->kdata)
+               memcpy(parser->ib.ptr, ib_chunk->kdata, ib_chunk->length_dw * 4);
+       else if (DRM_COPY_FROM_USER(parser->ib.ptr, ib_chunk->user_ptr, ib_chunk->length_dw * 4))
+               return -EFAULT;
+       return 0;
+}
+
 int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
 {
        struct radeon_device *rdev = dev->dev_private;
@@ -552,10 +524,15 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                r = radeon_cs_handle_lockup(rdev, r);
                return r;
        }
-       r = radeon_cs_parser_relocs(&parser);
-       if (r) {
-               if (r != -ERESTARTSYS)
+
+       r = radeon_cs_ib_fill(rdev, &parser);
+       if (!r) {
+               r = radeon_cs_parser_relocs(&parser);
+               if (r && r != -ERESTARTSYS)
                        DRM_ERROR("Failed to parse relocation %d!\n", r);
+       }
+
+       if (r) {
                radeon_cs_parser_fini(&parser, r, false);
                up_read(&rdev->exclusive_lock);
                r = radeon_cs_handle_lockup(rdev, r);
@@ -579,97 +556,6 @@ out:
        return r;
 }
 
-int radeon_cs_finish_pages(struct radeon_cs_parser *p)
-{
-       struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
-       int i;
-       int size = PAGE_SIZE;
-
-       for (i = ibc->last_copied_page + 1; i <= ibc->last_page_index; i++) {
-               if (i == ibc->last_page_index) {
-                       size = (ibc->length_dw * 4) % PAGE_SIZE;
-                       if (size == 0)
-                               size = PAGE_SIZE;
-               }
-               
-               if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)),
-                                      ibc->user_ptr + (i * PAGE_SIZE),
-                                      size))
-                       return -EFAULT;
-       }
-       return 0;
-}
-
-static int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx)
-{
-       int new_page;
-       struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
-       int i;
-       int size = PAGE_SIZE;
-       bool copy1 = (p->rdev && (p->rdev->flags & RADEON_IS_AGP)) ?
-               false : true;
-
-       for (i = ibc->last_copied_page + 1; i < pg_idx; i++) {
-               if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)),
-                                      ibc->user_ptr + (i * PAGE_SIZE),
-                                      PAGE_SIZE)) {
-                       p->parser_error = -EFAULT;
-                       return 0;
-               }
-       }
-
-       if (pg_idx == ibc->last_page_index) {
-               size = (ibc->length_dw * 4) % PAGE_SIZE;
-               if (size == 0)
-                       size = PAGE_SIZE;
-       }
-
-       new_page = ibc->kpage_idx[0] < ibc->kpage_idx[1] ? 0 : 1;
-       if (copy1)
-               ibc->kpage[new_page] = p->ib.ptr + (pg_idx * (PAGE_SIZE / 4));
-
-       if (DRM_COPY_FROM_USER(ibc->kpage[new_page],
-                              ibc->user_ptr + (pg_idx * PAGE_SIZE),
-                              size)) {
-               p->parser_error = -EFAULT;
-               return 0;
-       }
-
-       /* copy to IB for non single case */
-       if (!copy1)
-               memcpy((void *)(p->ib.ptr+(pg_idx*(PAGE_SIZE/4))), ibc->kpage[new_page], size);
-
-       ibc->last_copied_page = pg_idx;
-       ibc->kpage_idx[new_page] = pg_idx;
-
-       return new_page;
-}
-
-u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx)
-{
-       struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
-       u32 pg_idx, pg_offset;
-       u32 idx_value = 0;
-       int new_page;
-
-       pg_idx = (idx * 4) / PAGE_SIZE;
-       pg_offset = (idx * 4) % PAGE_SIZE;
-
-       if (ibc->kpage_idx[0] == pg_idx)
-               return ibc->kpage[0][pg_offset/4];
-       if (ibc->kpage_idx[1] == pg_idx)
-               return ibc->kpage[1][pg_offset/4];
-
-       new_page = radeon_cs_update_pages(p, pg_idx);
-       if (new_page < 0) {
-               p->parser_error = new_page;
-               return 0;
-       }
-
-       idx_value = ibc->kpage[new_page][pg_offset/4];
-       return idx_value;
-}
-
 /**
  * radeon_cs_packet_parse() - parse cp packet and point ib index to next packet
  * @parser:    parser structure holding parsing context.
index 841d0e0..b9234c4 100644 (file)
@@ -98,9 +98,16 @@ static const char radeon_family_name[][16] = {
        "BONAIRE",
        "KAVERI",
        "KABINI",
+       "HAWAII",
        "LAST",
 };
 
+#if defined(CONFIG_VGA_SWITCHEROO)
+bool radeon_is_px(void);
+#else
+static inline bool radeon_is_px(void) { return false; }
+#endif
+
 /**
  * radeon_program_register_sequence - program an array of registers.
  *
@@ -1076,7 +1083,10 @@ static bool radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
 static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
 {
        struct drm_device *dev = pci_get_drvdata(pdev);
-       pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
+
+       if (radeon_is_px() && state == VGA_SWITCHEROO_OFF)
+               return;
+
        if (state == VGA_SWITCHEROO_ON) {
                unsigned d3_delay = dev->pdev->d3_delay;
 
@@ -1087,7 +1097,7 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
                if (d3_delay < 20 && radeon_switcheroo_quirk_long_wakeup(pdev))
                        dev->pdev->d3_delay = 20;
 
-               radeon_resume_kms(dev);
+               radeon_resume_kms(dev, true, true);
 
                dev->pdev->d3_delay = d3_delay;
 
@@ -1097,7 +1107,7 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
                printk(KERN_INFO "radeon: switched off\n");
                drm_kms_helper_poll_disable(dev);
                dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
-               radeon_suspend_kms(dev, pmm);
+               radeon_suspend_kms(dev, true, true);
                dev->switch_power_state = DRM_SWITCH_POWER_OFF;
        }
 }
@@ -1147,6 +1157,7 @@ int radeon_device_init(struct radeon_device *rdev,
 {
        int r, i;
        int dma_bits;
+       bool runtime = false;
 
        rdev->shutdown = false;
        rdev->dev = &pdev->dev;
@@ -1293,7 +1304,14 @@ int radeon_device_init(struct radeon_device *rdev,
        /* this will fail for cards that aren't VGA class devices, just
         * ignore it */
        vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
-       vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, false);
+
+       if (radeon_runtime_pm == 1)
+               runtime = true;
+       if ((radeon_runtime_pm == -1) && radeon_is_px())
+               runtime = true;
+       vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, runtime);
+       if (runtime)
+               vga_switcheroo_init_domain_pm_ops(rdev->dev, &rdev->vga_pm_domain);
 
        r = radeon_init(rdev);
        if (r)
@@ -1383,7 +1401,7 @@ void radeon_device_fini(struct radeon_device *rdev)
  * Returns 0 for success or an error on failure.
  * Called at driver suspend.
  */
-int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
+int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
 {
        struct radeon_device *rdev;
        struct drm_crtc *crtc;
@@ -1394,9 +1412,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
        if (dev == NULL || dev->dev_private == NULL) {
                return -ENODEV;
        }
-       if (state.event == PM_EVENT_PRETHAW) {
-               return 0;
-       }
+
        rdev = dev->dev_private;
 
        if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
@@ -1455,14 +1471,17 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
        radeon_agp_suspend(rdev);
 
        pci_save_state(dev->pdev);
-       if (state.event == PM_EVENT_SUSPEND) {
+       if (suspend) {
                /* Shut down the device */
                pci_disable_device(dev->pdev);
                pci_set_power_state(dev->pdev, PCI_D3hot);
        }
-       console_lock();
-       radeon_fbdev_set_suspend(rdev, 1);
-       console_unlock();
+
+       if (fbcon) {
+               console_lock();
+               radeon_fbdev_set_suspend(rdev, 1);
+               console_unlock();
+       }
        return 0;
 }
 
@@ -1475,7 +1494,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
  * Returns 0 for success or an error on failure.
  * Called at driver resume.
  */
-int radeon_resume_kms(struct drm_device *dev)
+int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
 {
        struct drm_connector *connector;
        struct radeon_device *rdev = dev->dev_private;
@@ -1484,12 +1503,17 @@ int radeon_resume_kms(struct drm_device *dev)
        if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
-       console_lock();
-       pci_set_power_state(dev->pdev, PCI_D0);
-       pci_restore_state(dev->pdev);
-       if (pci_enable_device(dev->pdev)) {
-               console_unlock();
-               return -1;
+       if (fbcon) {
+               console_lock();
+       }
+       if (resume) {
+               pci_set_power_state(dev->pdev, PCI_D0);
+               pci_restore_state(dev->pdev);
+               if (pci_enable_device(dev->pdev)) {
+                       if (fbcon)
+                               console_unlock();
+                       return -1;
+               }
        }
        /* resume AGP if in use */
        radeon_agp_resume(rdev);
@@ -1502,9 +1526,11 @@ int radeon_resume_kms(struct drm_device *dev)
        radeon_pm_resume(rdev);
        radeon_restore_bios_scratch_regs(rdev);
 
-       radeon_fbdev_set_suspend(rdev, 0);
-       console_unlock();
-
+       if (fbcon) {
+               radeon_fbdev_set_suspend(rdev, 0);
+               console_unlock();
+       }
+       
        /* init dig PHYs, disp eng pll */
        if (rdev->is_atom_bios) {
                radeon_atom_encoder_init(rdev);
@@ -1549,6 +1575,14 @@ int radeon_gpu_reset(struct radeon_device *rdev)
        int resched;
 
        down_write(&rdev->exclusive_lock);
+
+       if (!rdev->needs_reset) {
+               up_write(&rdev->exclusive_lock);
+               return 0;
+       }
+
+       rdev->needs_reset = false;
+
        radeon_save_bios_scratch_regs(rdev);
        /* block TTM */
        resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev);
index 0d1aa05..7b25381 100644 (file)
@@ -30,6 +30,7 @@
 #include "atom.h"
 #include <asm/div64.h>
 
+#include <linux/pm_runtime.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 
@@ -306,7 +307,7 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
         */
        if (update_pending &&
            (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id,
-                                                              &vpos, &hpos)) &&
+                                                              &vpos, &hpos, NULL, NULL)) &&
            ((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) ||
             (vpos < 0 && !ASIC_IS_AVIVO(rdev)))) {
                /* crtc didn't flip in this target vblank interval,
@@ -494,11 +495,55 @@ unlock_free:
        return r;
 }
 
+static int
+radeon_crtc_set_config(struct drm_mode_set *set)
+{
+       struct drm_device *dev;
+       struct radeon_device *rdev;
+       struct drm_crtc *crtc;
+       bool active = false;
+       int ret;
+
+       if (!set || !set->crtc)
+               return -EINVAL;
+
+       dev = set->crtc->dev;
+
+       ret = pm_runtime_get_sync(dev->dev);
+       if (ret < 0)
+               return ret;
+
+       ret = drm_crtc_helper_set_config(set);
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+               if (crtc->enabled)
+                       active = true;
+
+       pm_runtime_mark_last_busy(dev->dev);
+
+       rdev = dev->dev_private;
+       /* if we have active crtcs and we don't have a power ref,
+          take the current one */
+       if (active && !rdev->have_disp_power_ref) {
+               rdev->have_disp_power_ref = true;
+               return ret;
+       }
+       /* if we have no active crtcs, then drop the power ref
+          we got before */
+       if (!active && rdev->have_disp_power_ref) {
+               pm_runtime_put_autosuspend(dev->dev);
+               rdev->have_disp_power_ref = false;
+       }
+
+       /* drop the power reference we got coming in here */
+       pm_runtime_put_autosuspend(dev->dev);
+       return ret;
+}
 static const struct drm_crtc_funcs radeon_crtc_funcs = {
        .cursor_set = radeon_crtc_cursor_set,
        .cursor_move = radeon_crtc_cursor_move,
        .gamma_set = radeon_crtc_gamma_set,
-       .set_config = drm_crtc_helper_set_config,
+       .set_config = radeon_crtc_set_config,
        .destroy = radeon_crtc_destroy,
        .page_flip = radeon_crtc_page_flip,
 };
@@ -1178,6 +1223,12 @@ static struct drm_prop_enum_list radeon_audio_enum_list[] =
        { RADEON_AUDIO_AUTO, "auto" },
 };
 
+/* XXX support different dither options? spatial, temporal, both, etc. */
+static struct drm_prop_enum_list radeon_dither_enum_list[] =
+{      { RADEON_FMT_DITHER_DISABLE, "off" },
+       { RADEON_FMT_DITHER_ENABLE, "on" },
+};
+
 static int radeon_modeset_create_props(struct radeon_device *rdev)
 {
        int sz;
@@ -1234,6 +1285,12 @@ static int radeon_modeset_create_props(struct radeon_device *rdev)
                                         "audio",
                                         radeon_audio_enum_list, sz);
 
+       sz = ARRAY_SIZE(radeon_dither_enum_list);
+       rdev->mode_info.dither_property =
+               drm_property_create_enum(rdev->ddev, 0,
+                                        "dither",
+                                        radeon_dither_enum_list, sz);
+
        return 0;
 }
 
@@ -1539,12 +1596,17 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
 }
 
 /*
- * Retrieve current video scanout position of crtc on a given gpu.
+ * Retrieve current video scanout position of crtc on a given gpu, and
+ * an optional accurate timestamp of when query happened.
  *
  * \param dev Device to query.
  * \param crtc Crtc to query.
  * \param *vpos Location where vertical scanout position should be stored.
  * \param *hpos Location where horizontal scanout position should go.
+ * \param *stime Target location for timestamp taken immediately before
+ *               scanout position query. Can be NULL to skip timestamp.
+ * \param *etime Target location for timestamp taken immediately after
+ *               scanout position query. Can be NULL to skip timestamp.
  *
  * Returns vpos as a positive number while in active scanout area.
  * Returns vpos as a negative number inside vblank, counting the number
@@ -1560,7 +1622,8 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
  * unknown small number of scanlines wrt. real scanout position.
  *
  */
-int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int *hpos)
+int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int *hpos,
+                              ktime_t *stime, ktime_t *etime)
 {
        u32 stat_crtc = 0, vbl = 0, position = 0;
        int vbl_start, vbl_end, vtotal, ret = 0;
@@ -1568,6 +1631,12 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int
 
        struct radeon_device *rdev = dev->dev_private;
 
+       /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
+
+       /* Get optional system timestamp before query. */
+       if (stime)
+               *stime = ktime_get();
+
        if (ASIC_IS_DCE4(rdev)) {
                if (crtc == 0) {
                        vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
@@ -1650,6 +1719,12 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int
                }
        }
 
+       /* Get optional system timestamp after query. */
+       if (etime)
+               *etime = ktime_get();
+
+       /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
+
        /* Decode into vertical and horizontal scanout position. */
        *vpos = position & 0x1fff;
        *hpos = (position >> 16) & 0x1fff;
index 9c14a1b..1aee322 100644 (file)
@@ -36,8 +36,9 @@
 #include <drm/drm_pciids.h>
 #include <linux/console.h>
 #include <linux/module.h>
-
-
+#include <linux/pm_runtime.h>
+#include <linux/vga_switcheroo.h>
+#include "drm_crtc_helper.h"
 /*
  * KMS wrapper.
  * - 2.0.0 - initial interface
@@ -87,8 +88,8 @@ void radeon_driver_postclose_kms(struct drm_device *dev,
                                 struct drm_file *file_priv);
 void radeon_driver_preclose_kms(struct drm_device *dev,
                                struct drm_file *file_priv);
-int radeon_suspend_kms(struct drm_device *dev, pm_message_t state);
-int radeon_resume_kms(struct drm_device *dev);
+int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon);
+int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
 u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc);
 int radeon_enable_vblank_kms(struct drm_device *dev, int crtc);
 void radeon_disable_vblank_kms(struct drm_device *dev, int crtc);
@@ -100,14 +101,14 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev);
 int radeon_driver_irq_postinstall_kms(struct drm_device *dev);
 void radeon_driver_irq_uninstall_kms(struct drm_device *dev);
 irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS);
-int radeon_gem_object_init(struct drm_gem_object *obj);
 void radeon_gem_object_free(struct drm_gem_object *obj);
 int radeon_gem_object_open(struct drm_gem_object *obj,
                                struct drm_file *file_priv);
 void radeon_gem_object_close(struct drm_gem_object *obj,
                                struct drm_file *file_priv);
 extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc,
-                                     int *vpos, int *hpos);
+                                     int *vpos, int *hpos, ktime_t *stime,
+                                     ktime_t *etime);
 extern const struct drm_ioctl_desc radeon_ioctls_kms[];
 extern int radeon_max_kms_ioctl;
 int radeon_mmap(struct file *filp, struct vm_area_struct *vma);
@@ -137,9 +138,11 @@ void radeon_debugfs_cleanup(struct drm_minor *minor);
 #if defined(CONFIG_VGA_SWITCHEROO)
 void radeon_register_atpx_handler(void);
 void radeon_unregister_atpx_handler(void);
+bool radeon_is_px(void);
 #else
 static inline void radeon_register_atpx_handler(void) {}
 static inline void radeon_unregister_atpx_handler(void) {}
+static inline bool radeon_is_px(void) { return false; }
 #endif
 
 int radeon_no_wb;
@@ -162,6 +165,7 @@ int radeon_lockup_timeout = 10000;
 int radeon_fastfb = 0;
 int radeon_dpm = -1;
 int radeon_aspm = -1;
+int radeon_runtime_pm = -1;
 
 MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
 module_param_named(no_wb, radeon_no_wb, int, 0444);
@@ -223,6 +227,9 @@ module_param_named(dpm, radeon_dpm, int, 0444);
 MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 = auto)");
 module_param_named(aspm, radeon_aspm, int, 0444);
 
+MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 = disable, -1 = PX only default)");
+module_param_named(runpm, radeon_runtime_pm, int, 0444);
+
 static struct pci_device_id pciidlist[] = {
        radeon_PCI_IDS
 };
@@ -259,6 +266,7 @@ static int radeon_resume(struct drm_device *dev)
        return 0;
 }
 
+
 static const struct file_operations radeon_driver_old_fops = {
        .owner = THIS_MODULE,
        .open = drm_open,
@@ -353,25 +361,144 @@ radeon_pci_remove(struct pci_dev *pdev)
        drm_put_dev(dev);
 }
 
-static int
-radeon_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+static int radeon_pmops_suspend(struct device *dev)
 {
-       struct drm_device *dev = pci_get_drvdata(pdev);
-       return radeon_suspend_kms(dev, state);
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       return radeon_suspend_kms(drm_dev, true, true);
 }
 
-static int
-radeon_pci_resume(struct pci_dev *pdev)
+static int radeon_pmops_resume(struct device *dev)
 {
-       struct drm_device *dev = pci_get_drvdata(pdev);
-       return radeon_resume_kms(dev);
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       return radeon_resume_kms(drm_dev, true, true);
+}
+
+static int radeon_pmops_freeze(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       return radeon_suspend_kms(drm_dev, false, true);
+}
+
+static int radeon_pmops_thaw(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       return radeon_resume_kms(drm_dev, false, true);
+}
+
+static int radeon_pmops_runtime_suspend(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       int ret;
+
+       if (radeon_runtime_pm == 0)
+               return -EINVAL;
+
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
+       drm_kms_helper_poll_disable(drm_dev);
+       vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
+
+       ret = radeon_suspend_kms(drm_dev, false, false);
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, PCI_D3cold);
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
+
+       return 0;
+}
+
+static int radeon_pmops_runtime_resume(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       int ret;
+
+       if (radeon_runtime_pm == 0)
+               return -EINVAL;
+
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       ret = pci_enable_device(pdev);
+       if (ret)
+               return ret;
+       pci_set_master(pdev);
+
+       ret = radeon_resume_kms(drm_dev, false, false);
+       drm_kms_helper_poll_enable(drm_dev);
+       vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
+       return 0;
 }
 
+static int radeon_pmops_runtime_idle(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       struct drm_crtc *crtc;
+
+       if (radeon_runtime_pm == 0)
+               return -EBUSY;
+
+       /* are we PX enabled? */
+       if (radeon_runtime_pm == -1 && !radeon_is_px()) {
+               DRM_DEBUG_DRIVER("failing to power off - not px\n");
+               return -EBUSY;
+       }
+
+       list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
+               if (crtc->enabled) {
+                       DRM_DEBUG_DRIVER("failing to power off - crtc active\n");
+                       return -EBUSY;
+               }
+       }
+
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_autosuspend(dev);
+       /* we don't want the main rpm_idle to call suspend - we want to autosuspend */
+       return 1;
+}
+
+long radeon_drm_ioctl(struct file *filp,
+                     unsigned int cmd, unsigned long arg)
+{
+       struct drm_file *file_priv = filp->private_data;
+       struct drm_device *dev;
+       long ret;
+       dev = file_priv->minor->dev;
+       ret = pm_runtime_get_sync(dev->dev);
+       if (ret < 0)
+               return ret;
+
+       ret = drm_ioctl(filp, cmd, arg);
+       
+       pm_runtime_mark_last_busy(dev->dev);
+       pm_runtime_put_autosuspend(dev->dev);
+       return ret;
+}
+
+static const struct dev_pm_ops radeon_pm_ops = {
+       .suspend = radeon_pmops_suspend,
+       .resume = radeon_pmops_resume,
+       .freeze = radeon_pmops_freeze,
+       .thaw = radeon_pmops_thaw,
+       .poweroff = radeon_pmops_freeze,
+       .restore = radeon_pmops_resume,
+       .runtime_suspend = radeon_pmops_runtime_suspend,
+       .runtime_resume = radeon_pmops_runtime_resume,
+       .runtime_idle = radeon_pmops_runtime_idle,
+};
+
 static const struct file_operations radeon_driver_kms_fops = {
        .owner = THIS_MODULE,
        .open = drm_open,
        .release = drm_release,
-       .unlocked_ioctl = drm_ioctl,
+       .unlocked_ioctl = radeon_drm_ioctl,
        .mmap = radeon_mmap,
        .poll = drm_poll,
        .read = drm_read,
@@ -380,6 +507,15 @@ static const struct file_operations radeon_driver_kms_fops = {
 #endif
 };
 
+
+static void
+radeon_pci_shutdown(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+
+       radeon_driver_unload_kms(dev);
+}
+
 static struct drm_driver kms_driver = {
        .driver_features =
            DRIVER_USE_AGP |
@@ -392,8 +528,6 @@ static struct drm_driver kms_driver = {
        .postclose = radeon_driver_postclose_kms,
        .lastclose = radeon_driver_lastclose_kms,
        .unload = radeon_driver_unload_kms,
-       .suspend = radeon_suspend_kms,
-       .resume = radeon_resume_kms,
        .get_vblank_counter = radeon_get_vblank_counter_kms,
        .enable_vblank = radeon_enable_vblank_kms,
        .disable_vblank = radeon_disable_vblank_kms,
@@ -408,7 +542,6 @@ static struct drm_driver kms_driver = {
        .irq_uninstall = radeon_driver_irq_uninstall_kms,
        .irq_handler = radeon_driver_irq_handler_kms,
        .ioctls = radeon_ioctls_kms,
-       .gem_init_object = radeon_gem_object_init,
        .gem_free_object = radeon_gem_object_free,
        .gem_open_object = radeon_gem_object_open,
        .gem_close_object = radeon_gem_object_close,
@@ -451,8 +584,8 @@ static struct pci_driver radeon_kms_pci_driver = {
        .id_table = pciidlist,
        .probe = radeon_pci_probe,
        .remove = radeon_pci_remove,
-       .suspend = radeon_pci_suspend,
-       .resume = radeon_pci_resume,
+       .driver.pm = &radeon_pm_ops,
+       .shutdown = radeon_pci_shutdown,
 };
 
 static int __init radeon_init(void)
index b369d42..543dcfa 100644 (file)
 #define DRIVER_MINOR           33
 #define DRIVER_PATCHLEVEL      0
 
+long radeon_drm_ioctl(struct file *filp,
+                     unsigned int cmd, unsigned long arg);
+
 /* The rest of the file is DEPRECATED! */
 #ifdef CONFIG_DRM_RADEON_UMS
 
index 3c82890..614ad54 100644 (file)
@@ -96,6 +96,7 @@ enum radeon_family {
        CHIP_BONAIRE,
        CHIP_KAVERI,
        CHIP_KABINI,
+       CHIP_HAWAII,
        CHIP_LAST,
 };
 
index ddb8f8e..281d14c 100644 (file)
@@ -190,10 +190,8 @@ void radeon_fence_process(struct radeon_device *rdev, int ring)
                }
        } while (atomic64_xchg(&rdev->fence_drv[ring].last_seq, seq) > seq);
 
-       if (wake) {
-               rdev->fence_drv[ring].last_activity = jiffies;
+       if (wake)
                wake_up_all(&rdev->fence_queue);
-       }
 }
 
 /**
@@ -212,13 +210,13 @@ static void radeon_fence_destroy(struct kref *kref)
 }
 
 /**
- * radeon_fence_seq_signaled - check if a fence sequeuce number has signaled
+ * radeon_fence_seq_signaled - check if a fence sequence number has signaled
  *
  * @rdev: radeon device pointer
  * @seq: sequence number
  * @ring: ring index the fence is associated with
  *
- * Check if the last singled fence sequnce number is >= the requested
+ * Check if the last signaled fence sequnce number is >= the requested
  * sequence number (all asics).
  * Returns true if the fence has signaled (current fence value
  * is >= requested value) or false if it has not (current fence
@@ -263,113 +261,131 @@ bool radeon_fence_signaled(struct radeon_fence *fence)
 }
 
 /**
- * radeon_fence_wait_seq - wait for a specific sequence number
+ * radeon_fence_any_seq_signaled - check if any sequence number is signaled
  *
  * @rdev: radeon device pointer
- * @target_seq: sequence number we want to wait for
- * @ring: ring index the fence is associated with
+ * @seq: sequence numbers
+ *
+ * Check if the last signaled fence sequnce number is >= the requested
+ * sequence number (all asics).
+ * Returns true if any has signaled (current value is >= requested value)
+ * or false if it has not. Helper function for radeon_fence_wait_seq.
+ */
+static bool radeon_fence_any_seq_signaled(struct radeon_device *rdev, u64 *seq)
+{
+       unsigned i;
+
+       for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+               if (seq[i] && radeon_fence_seq_signaled(rdev, seq[i], i))
+                       return true;
+       }
+       return false;
+}
+
+/**
+ * radeon_fence_wait_seq - wait for a specific sequence numbers
+ *
+ * @rdev: radeon device pointer
+ * @target_seq: sequence number(s) we want to wait for
  * @intr: use interruptable sleep
  * @lock_ring: whether the ring should be locked or not
  *
- * Wait for the requested sequence number to be written (all asics).
+ * Wait for the requested sequence number(s) to be written by any ring
+ * (all asics).  Sequnce number array is indexed by ring id.
  * @intr selects whether to use interruptable (true) or non-interruptable
  * (false) sleep when waiting for the sequence number.  Helper function
- * for radeon_fence_wait(), et al.
+ * for radeon_fence_wait_*().
  * Returns 0 if the sequence number has passed, error for all other cases.
- * -EDEADLK is returned when a GPU lockup has been detected and the ring is
- * marked as not ready so no further jobs get scheduled until a successful
- * reset.
+ * -EDEADLK is returned when a GPU lockup has been detected.
  */
-static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 target_seq,
-                                unsigned ring, bool intr, bool lock_ring)
+static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 *target_seq,
+                                bool intr, bool lock_ring)
 {
-       unsigned long timeout, last_activity;
-       uint64_t seq;
-       unsigned i;
+       uint64_t last_seq[RADEON_NUM_RINGS];
        bool signaled;
-       int r;
+       int i, r;
+
+       while (!radeon_fence_any_seq_signaled(rdev, target_seq)) {
+
+               /* Save current sequence values, used to check for GPU lockups */
+               for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+                       if (!target_seq[i])
+                               continue;
 
-       while (target_seq > atomic64_read(&rdev->fence_drv[ring].last_seq)) {
-               if (!rdev->ring[ring].ready) {
-                       return -EBUSY;
+                       last_seq[i] = atomic64_read(&rdev->fence_drv[i].last_seq);
+                       trace_radeon_fence_wait_begin(rdev->ddev, target_seq[i]);
+                       radeon_irq_kms_sw_irq_get(rdev, i);
                }
 
-               timeout = jiffies - RADEON_FENCE_JIFFIES_TIMEOUT;
-               if (time_after(rdev->fence_drv[ring].last_activity, timeout)) {
-                       /* the normal case, timeout is somewhere before last_activity */
-                       timeout = rdev->fence_drv[ring].last_activity - timeout;
+               if (intr) {
+                       r = wait_event_interruptible_timeout(rdev->fence_queue, (
+                               (signaled = radeon_fence_any_seq_signaled(rdev, target_seq))
+                                || rdev->needs_reset), RADEON_FENCE_JIFFIES_TIMEOUT);
                } else {
-                       /* either jiffies wrapped around, or no fence was signaled in the last 500ms
-                        * anyway we will just wait for the minimum amount and then check for a lockup
-                        */
-                       timeout = 1;
+                       r = wait_event_timeout(rdev->fence_queue, (
+                               (signaled = radeon_fence_any_seq_signaled(rdev, target_seq))
+                                || rdev->needs_reset), RADEON_FENCE_JIFFIES_TIMEOUT);
                }
-               seq = atomic64_read(&rdev->fence_drv[ring].last_seq);
-               /* Save current last activity valuee, used to check for GPU lockups */
-               last_activity = rdev->fence_drv[ring].last_activity;
 
-               trace_radeon_fence_wait_begin(rdev->ddev, seq);
-               radeon_irq_kms_sw_irq_get(rdev, ring);
-               if (intr) {
-                       r = wait_event_interruptible_timeout(rdev->fence_queue,
-                               (signaled = radeon_fence_seq_signaled(rdev, target_seq, ring)),
-                               timeout);
-                } else {
-                       r = wait_event_timeout(rdev->fence_queue,
-                               (signaled = radeon_fence_seq_signaled(rdev, target_seq, ring)),
-                               timeout);
+               for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+                       if (!target_seq[i])
+                               continue;
+
+                       radeon_irq_kms_sw_irq_put(rdev, i);
+                       trace_radeon_fence_wait_end(rdev->ddev, target_seq[i]);
                }
-               radeon_irq_kms_sw_irq_put(rdev, ring);
-               if (unlikely(r < 0)) {
+
+               if (unlikely(r < 0))
                        return r;
-               }
-               trace_radeon_fence_wait_end(rdev->ddev, seq);
 
                if (unlikely(!signaled)) {
+                       if (rdev->needs_reset)
+                               return -EDEADLK;
+
                        /* we were interrupted for some reason and fence
                         * isn't signaled yet, resume waiting */
-                       if (r) {
+                       if (r)
                                continue;
+
+                       for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+                               if (!target_seq[i])
+                                       continue;
+
+                               if (last_seq[i] != atomic64_read(&rdev->fence_drv[i].last_seq))
+                                       break;
                        }
 
-                       /* check if sequence value has changed since last_activity */
-                       if (seq != atomic64_read(&rdev->fence_drv[ring].last_seq)) {
+                       if (i != RADEON_NUM_RINGS)
                                continue;
-                       }
 
-                       if (lock_ring) {
+                       if (lock_ring)
                                mutex_lock(&rdev->ring_lock);
-                       }
 
-                       /* test if somebody else has already decided that this is a lockup */
-                       if (last_activity != rdev->fence_drv[ring].last_activity) {
-                               if (lock_ring) {
-                                       mutex_unlock(&rdev->ring_lock);
-                               }
-                               continue;
+                       for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+                               if (!target_seq[i])
+                                       continue;
+
+                               if (radeon_ring_is_lockup(rdev, i, &rdev->ring[i]))
+                                       break;
                        }
 
-                       if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) {
+                       if (i < RADEON_NUM_RINGS) {
                                /* good news we believe it's a lockup */
-                               dev_warn(rdev->dev, "GPU lockup (waiting for 0x%016llx last fence id 0x%016llx)\n",
-                                        target_seq, seq);
-
-                               /* change last activity so nobody else think there is a lockup */
-                               for (i = 0; i < RADEON_NUM_RINGS; ++i) {
-                                       rdev->fence_drv[i].last_activity = jiffies;
-                               }
-
-                               /* mark the ring as not ready any more */
-                               rdev->ring[ring].ready = false;
-                               if (lock_ring) {
+                               dev_warn(rdev->dev, "GPU lockup (waiting for "
+                                        "0x%016llx last fence id 0x%016llx on"
+                                        " ring %d)\n",
+                                        target_seq[i], last_seq[i], i);
+
+                               /* remember that we need an reset */
+                               rdev->needs_reset = true;
+                               if (lock_ring)
                                        mutex_unlock(&rdev->ring_lock);
-                               }
+                               wake_up_all(&rdev->fence_queue);
                                return -EDEADLK;
                        }
 
-                       if (lock_ring) {
+                       if (lock_ring)
                                mutex_unlock(&rdev->ring_lock);
-                       }
                }
        }
        return 0;
@@ -388,6 +404,7 @@ static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 target_seq,
  */
 int radeon_fence_wait(struct radeon_fence *fence, bool intr)
 {
+       uint64_t seq[RADEON_NUM_RINGS] = {};
        int r;
 
        if (fence == NULL) {
@@ -395,147 +412,15 @@ int radeon_fence_wait(struct radeon_fence *fence, bool intr)
                return -EINVAL;
        }
 
-       r = radeon_fence_wait_seq(fence->rdev, fence->seq,
-                                 fence->ring, intr, true);
-       if (r) {
-               return r;
-       }
-       fence->seq = RADEON_FENCE_SIGNALED_SEQ;
-       return 0;
-}
-
-static bool radeon_fence_any_seq_signaled(struct radeon_device *rdev, u64 *seq)
-{
-       unsigned i;
-
-       for (i = 0; i < RADEON_NUM_RINGS; ++i) {
-               if (seq[i] && radeon_fence_seq_signaled(rdev, seq[i], i)) {
-                       return true;
-               }
-       }
-       return false;
-}
+       seq[fence->ring] = fence->seq;
+       if (seq[fence->ring] == RADEON_FENCE_SIGNALED_SEQ)
+               return 0;
 
-/**
- * radeon_fence_wait_any_seq - wait for a sequence number on any ring
- *
- * @rdev: radeon device pointer
- * @target_seq: sequence number(s) we want to wait for
- * @intr: use interruptable sleep
- *
- * Wait for the requested sequence number(s) to be written by any ring
- * (all asics).  Sequnce number array is indexed by ring id.
- * @intr selects whether to use interruptable (true) or non-interruptable
- * (false) sleep when waiting for the sequence number.  Helper function
- * for radeon_fence_wait_any(), et al.
- * Returns 0 if the sequence number has passed, error for all other cases.
- */
-static int radeon_fence_wait_any_seq(struct radeon_device *rdev,
-                                    u64 *target_seq, bool intr)
-{
-       unsigned long timeout, last_activity, tmp;
-       unsigned i, ring = RADEON_NUM_RINGS;
-       bool signaled;
-       int r;
-
-       for (i = 0, last_activity = 0; i < RADEON_NUM_RINGS; ++i) {
-               if (!target_seq[i]) {
-                       continue;
-               }
-
-               /* use the most recent one as indicator */
-               if (time_after(rdev->fence_drv[i].last_activity, last_activity)) {
-                       last_activity = rdev->fence_drv[i].last_activity;
-               }
-
-               /* For lockup detection just pick the lowest ring we are
-                * actively waiting for
-                */
-               if (i < ring) {
-                       ring = i;
-               }
-       }
-
-       /* nothing to wait for ? */
-       if (ring == RADEON_NUM_RINGS) {
-               return -ENOENT;
-       }
-
-       while (!radeon_fence_any_seq_signaled(rdev, target_seq)) {
-               timeout = jiffies - RADEON_FENCE_JIFFIES_TIMEOUT;
-               if (time_after(last_activity, timeout)) {
-                       /* the normal case, timeout is somewhere before last_activity */
-                       timeout = last_activity - timeout;
-               } else {
-                       /* either jiffies wrapped around, or no fence was signaled in the last 500ms
-                        * anyway we will just wait for the minimum amount and then check for a lockup
-                        */
-                       timeout = 1;
-               }
-
-               trace_radeon_fence_wait_begin(rdev->ddev, target_seq[ring]);
-               for (i = 0; i < RADEON_NUM_RINGS; ++i) {
-                       if (target_seq[i]) {
-                               radeon_irq_kms_sw_irq_get(rdev, i);
-                       }
-               }
-               if (intr) {
-                       r = wait_event_interruptible_timeout(rdev->fence_queue,
-                               (signaled = radeon_fence_any_seq_signaled(rdev, target_seq)),
-                               timeout);
-               } else {
-                       r = wait_event_timeout(rdev->fence_queue,
-                               (signaled = radeon_fence_any_seq_signaled(rdev, target_seq)),
-                               timeout);
-               }
-               for (i = 0; i < RADEON_NUM_RINGS; ++i) {
-                       if (target_seq[i]) {
-                               radeon_irq_kms_sw_irq_put(rdev, i);
-                       }
-               }
-               if (unlikely(r < 0)) {
-                       return r;
-               }
-               trace_radeon_fence_wait_end(rdev->ddev, target_seq[ring]);
-
-               if (unlikely(!signaled)) {
-                       /* we were interrupted for some reason and fence
-                        * isn't signaled yet, resume waiting */
-                       if (r) {
-                               continue;
-                       }
-
-                       mutex_lock(&rdev->ring_lock);
-                       for (i = 0, tmp = 0; i < RADEON_NUM_RINGS; ++i) {
-                               if (time_after(rdev->fence_drv[i].last_activity, tmp)) {
-                                       tmp = rdev->fence_drv[i].last_activity;
-                               }
-                       }
-                       /* test if somebody else has already decided that this is a lockup */
-                       if (last_activity != tmp) {
-                               last_activity = tmp;
-                               mutex_unlock(&rdev->ring_lock);
-                               continue;
-                       }
-
-                       if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) {
-                               /* good news we believe it's a lockup */
-                               dev_warn(rdev->dev, "GPU lockup (waiting for 0x%016llx)\n",
-                                        target_seq[ring]);
-
-                               /* change last activity so nobody else think there is a lockup */
-                               for (i = 0; i < RADEON_NUM_RINGS; ++i) {
-                                       rdev->fence_drv[i].last_activity = jiffies;
-                               }
+       r = radeon_fence_wait_seq(fence->rdev, seq, intr, true);
+       if (r)
+               return r;
 
-                               /* mark the ring as not ready any more */
-                               rdev->ring[ring].ready = false;
-                               mutex_unlock(&rdev->ring_lock);
-                               return -EDEADLK;
-                       }
-                       mutex_unlock(&rdev->ring_lock);
-               }
-       }
+       fence->seq = RADEON_FENCE_SIGNALED_SEQ;
        return 0;
 }
 
@@ -557,7 +442,7 @@ int radeon_fence_wait_any(struct radeon_device *rdev,
                          bool intr)
 {
        uint64_t seq[RADEON_NUM_RINGS];
-       unsigned i;
+       unsigned i, num_rings = 0;
        int r;
 
        for (i = 0; i < RADEON_NUM_RINGS; ++i) {
@@ -567,15 +452,19 @@ int radeon_fence_wait_any(struct radeon_device *rdev,
                        continue;
                }
 
-               if (fences[i]->seq == RADEON_FENCE_SIGNALED_SEQ) {
-                       /* something was allready signaled */
-                       return 0;
-               }
-
                seq[i] = fences[i]->seq;
+               ++num_rings;
+
+               /* test if something was allready signaled */
+               if (seq[i] == RADEON_FENCE_SIGNALED_SEQ)
+                       return 0;
        }
 
-       r = radeon_fence_wait_any_seq(rdev, seq, intr);
+       /* nothing to wait for ? */
+       if (num_rings == 0)
+               return -ENOENT;
+
+       r = radeon_fence_wait_seq(rdev, seq, intr, true);
        if (r) {
                return r;
        }
@@ -594,15 +483,15 @@ int radeon_fence_wait_any(struct radeon_device *rdev,
  */
 int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring)
 {
-       uint64_t seq;
+       uint64_t seq[RADEON_NUM_RINGS] = {};
 
-       seq = atomic64_read(&rdev->fence_drv[ring].last_seq) + 1ULL;
-       if (seq >= rdev->fence_drv[ring].sync_seq[ring]) {
+       seq[ring] = atomic64_read(&rdev->fence_drv[ring].last_seq) + 1ULL;
+       if (seq[ring] >= rdev->fence_drv[ring].sync_seq[ring]) {
                /* nothing to wait for, last_seq is
                   already the last emited fence */
                return -ENOENT;
        }
-       return radeon_fence_wait_seq(rdev, seq, ring, false, false);
+       return radeon_fence_wait_seq(rdev, seq, false, false);
 }
 
 /**
@@ -617,14 +506,18 @@ int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring)
  */
 int radeon_fence_wait_empty_locked(struct radeon_device *rdev, int ring)
 {
-       uint64_t seq = rdev->fence_drv[ring].sync_seq[ring];
+       uint64_t seq[RADEON_NUM_RINGS] = {};
        int r;
 
-       r = radeon_fence_wait_seq(rdev, seq, ring, false, false);
+       seq[ring] = rdev->fence_drv[ring].sync_seq[ring];
+       if (!seq[ring])
+               return 0;
+
+       r = radeon_fence_wait_seq(rdev, seq, false, false);
        if (r) {
-               if (r == -EDEADLK) {
+               if (r == -EDEADLK)
                        return -EDEADLK;
-               }
+
                dev_err(rdev->dev, "error waiting for ring[%d] to become idle (%d)\n",
                        ring, r);
        }
@@ -826,7 +719,6 @@ static void radeon_fence_driver_init_ring(struct radeon_device *rdev, int ring)
        for (i = 0; i < RADEON_NUM_RINGS; ++i)
                rdev->fence_drv[ring].sync_seq[i] = 0;
        atomic64_set(&rdev->fence_drv[ring].last_seq, 0);
-       rdev->fence_drv[ring].last_activity = jiffies;
        rdev->fence_drv[ring].initialized = false;
 }
 
index b990b1a..8a83b89 100644 (file)
@@ -607,8 +607,8 @@ static int radeon_vm_evict(struct radeon_device *rdev, struct radeon_vm *vm)
  */
 int radeon_vm_alloc_pt(struct radeon_device *rdev, struct radeon_vm *vm)
 {
-       unsigned pd_size, pts_size;
-       u64 *pd_addr;
+       unsigned pd_size, pd_entries, pts_size;
+       struct radeon_ib ib;
        int r;
 
        if (vm == NULL) {
@@ -619,8 +619,10 @@ int radeon_vm_alloc_pt(struct radeon_device *rdev, struct radeon_vm *vm)
                return 0;
        }
 
-retry:
        pd_size = radeon_vm_directory_size(rdev);
+       pd_entries = radeon_vm_num_pdes(rdev);
+
+retry:
        r = radeon_sa_bo_new(rdev, &rdev->vm_manager.sa_manager,
                             &vm->page_directory, pd_size,
                             RADEON_VM_PTB_ALIGN_SIZE, false);
@@ -637,9 +639,31 @@ retry:
        vm->pd_gpu_addr = radeon_sa_bo_gpu_addr(vm->page_directory);
 
        /* Initially clear the page directory */
-       pd_addr = radeon_sa_bo_cpu_addr(vm->page_directory);
-       memset(pd_addr, 0, pd_size);
+       r = radeon_ib_get(rdev, R600_RING_TYPE_DMA_INDEX, &ib,
+                         NULL, pd_entries * 2 + 64);
+       if (r) {
+               radeon_sa_bo_free(rdev, &vm->page_directory, vm->fence);
+               return r;
+       }
+
+       ib.length_dw = 0;
+
+       radeon_asic_vm_set_page(rdev, &ib, vm->pd_gpu_addr,
+                               0, pd_entries, 0, 0);
+
+       radeon_ib_sync_to(&ib, vm->fence);
+       r = radeon_ib_schedule(rdev, &ib, NULL);
+       if (r) {
+               radeon_ib_free(rdev, &ib);
+               radeon_sa_bo_free(rdev, &vm->page_directory, vm->fence);
+               return r;
+       }
+       radeon_fence_unref(&vm->fence);
+       vm->fence = radeon_fence_ref(ib.fence);
+       radeon_ib_free(rdev, &ib);
+       radeon_fence_unref(&vm->last_flush);
 
+       /* allocate page table array */
        pts_size = radeon_vm_num_pdes(rdev) * sizeof(struct radeon_sa_bo *);
        vm->page_tables = kzalloc(pts_size, GFP_KERNEL);
 
@@ -914,6 +938,26 @@ uint64_t radeon_vm_map_gart(struct radeon_device *rdev, uint64_t addr)
 }
 
 /**
+ * radeon_vm_page_flags - translate page flags to what the hw uses
+ *
+ * @flags: flags comming from userspace
+ *
+ * Translate the flags the userspace ABI uses to hw flags.
+ */
+static uint32_t radeon_vm_page_flags(uint32_t flags)
+{
+        uint32_t hw_flags = 0;
+        hw_flags |= (flags & RADEON_VM_PAGE_VALID) ? R600_PTE_VALID : 0;
+        hw_flags |= (flags & RADEON_VM_PAGE_READABLE) ? R600_PTE_READABLE : 0;
+        hw_flags |= (flags & RADEON_VM_PAGE_WRITEABLE) ? R600_PTE_WRITEABLE : 0;
+        if (flags & RADEON_VM_PAGE_SYSTEM) {
+                hw_flags |= R600_PTE_SYSTEM;
+                hw_flags |= (flags & RADEON_VM_PAGE_SNOOPED) ? R600_PTE_SNOOPED : 0;
+        }
+        return hw_flags;
+}
+
+/**
  * radeon_vm_update_pdes - make sure that page directory is valid
  *
  * @rdev: radeon_device pointer
@@ -974,7 +1018,11 @@ retry:
                        if (count) {
                                radeon_asic_vm_set_page(rdev, ib, last_pde,
                                                        last_pt, count, incr,
-                                                       RADEON_VM_PAGE_VALID);
+                                                       R600_PTE_VALID);
+
+                               count *= RADEON_VM_PTE_COUNT;
+                               radeon_asic_vm_set_page(rdev, ib, last_pt, 0,
+                                                       count, 0, 0);
                        }
 
                        count = 1;
@@ -987,8 +1035,11 @@ retry:
 
        if (count) {
                radeon_asic_vm_set_page(rdev, ib, last_pde, last_pt, count,
-                                       incr, RADEON_VM_PAGE_VALID);
+                                       incr, R600_PTE_VALID);
 
+               count *= RADEON_VM_PTE_COUNT;
+               radeon_asic_vm_set_page(rdev, ib, last_pt, 0,
+                                       count, 0, 0);
        }
 
        return 0;
@@ -1082,7 +1133,6 @@ int radeon_vm_bo_update_pte(struct radeon_device *rdev,
                            struct radeon_bo *bo,
                            struct ttm_mem_reg *mem)
 {
-       unsigned ridx = rdev->asic->vm.pt_ring_index;
        struct radeon_ib ib;
        struct radeon_bo_va *bo_va;
        unsigned nptes, npdes, ndw;
@@ -1151,11 +1201,14 @@ int radeon_vm_bo_update_pte(struct radeon_device *rdev,
        /* reserve space for pde addresses */
        ndw += npdes * 2;
 
+       /* reserve space for clearing new page tables */
+       ndw += npdes * 2 * RADEON_VM_PTE_COUNT;
+
        /* update too big for an IB */
        if (ndw > 0xfffff)
                return -ENOMEM;
 
-       r = radeon_ib_get(rdev, ridx, &ib, NULL, ndw * 4);
+       r = radeon_ib_get(rdev, R600_RING_TYPE_DMA_INDEX, &ib, NULL, ndw * 4);
        ib.length_dw = 0;
 
        r = radeon_vm_update_pdes(rdev, vm, &ib, bo_va->soffset, bo_va->eoffset);
@@ -1165,7 +1218,7 @@ int radeon_vm_bo_update_pte(struct radeon_device *rdev,
        }
 
        radeon_vm_update_ptes(rdev, vm, &ib, bo_va->soffset, bo_va->eoffset,
-                             addr, bo_va->flags);
+                             addr, radeon_vm_page_flags(bo_va->flags));
 
        radeon_ib_sync_to(&ib, vm->fence);
        r = radeon_ib_schedule(rdev, &ib, NULL);
index dce99c8..805c5e5 100644 (file)
 #include <drm/radeon_drm.h>
 #include "radeon.h"
 
-int radeon_gem_object_init(struct drm_gem_object *obj)
-{
-       BUG();
-
-       return 0;
-}
-
 void radeon_gem_object_free(struct drm_gem_object *gobj)
 {
        struct radeon_bo *robj = gem_to_radeon_bo(gobj);
index c180df8..bdb0f93 100644 (file)
@@ -418,7 +418,7 @@ long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long
        if (nr < DRM_COMMAND_BASE)
                return drm_compat_ioctl(filp, cmd, arg);
 
-       ret = drm_ioctl(filp, cmd, arg);
+       ret = radeon_drm_ioctl(filp, cmd, arg);
 
        return ret;
 }
index cc9e848..ec6240b 100644 (file)
@@ -32,6 +32,8 @@
 #include "radeon.h"
 #include "atom.h"
 
+#include <linux/pm_runtime.h>
+
 #define RADEON_WAIT_IDLE_TIMEOUT 200
 
 /**
@@ -47,8 +49,12 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
 {
        struct drm_device *dev = (struct drm_device *) arg;
        struct radeon_device *rdev = dev->dev_private;
+       irqreturn_t ret;
 
-       return radeon_irq_process(rdev);
+       ret = radeon_irq_process(rdev);
+       if (ret == IRQ_HANDLED)
+               pm_runtime_mark_last_busy(dev->dev);
+       return ret;
 }
 
 /*
index 61580dd..bb87105 100644 (file)
@@ -32,7 +32,7 @@
 
 #include <linux/vga_switcheroo.h>
 #include <linux/slab.h>
-
+#include <linux/pm_runtime.h>
 /**
  * radeon_driver_unload_kms - Main unload function for KMS.
  *
@@ -50,9 +50,14 @@ int radeon_driver_unload_kms(struct drm_device *dev)
 
        if (rdev == NULL)
                return 0;
+
        if (rdev->rmmio == NULL)
                goto done_free;
+
+       pm_runtime_get_sync(dev->dev);
+
        radeon_acpi_fini(rdev);
+       
        radeon_modeset_fini(rdev);
        radeon_device_fini(rdev);
 
@@ -125,9 +130,20 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
                                "Error during ACPI methods call\n");
        }
 
+       if (radeon_runtime_pm != 0) {
+               pm_runtime_use_autosuspend(dev->dev);
+               pm_runtime_set_autosuspend_delay(dev->dev, 5000);
+               pm_runtime_set_active(dev->dev);
+               pm_runtime_allow(dev->dev);
+               pm_runtime_mark_last_busy(dev->dev);
+               pm_runtime_put_autosuspend(dev->dev);
+       }
+
 out:
        if (r)
                radeon_driver_unload_kms(dev);
+
+
        return r;
 }
 
@@ -191,7 +207,7 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
 
        switch (info->request) {
        case RADEON_INFO_DEVICE_ID:
-               *value = dev->pci_device;
+               *value = dev->pdev->device;
                break;
        case RADEON_INFO_NUM_GB_PIPES:
                *value = rdev->num_gb_pipes;
@@ -475,9 +491,14 @@ void radeon_driver_lastclose_kms(struct drm_device *dev)
 int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
 {
        struct radeon_device *rdev = dev->dev_private;
+       int r;
 
        file_priv->driver_priv = NULL;
 
+       r = pm_runtime_get_sync(dev->dev);
+       if (r < 0)
+               return r;
+
        /* new gpu have virtual address space support */
        if (rdev->family >= CHIP_CAYMAN) {
                struct radeon_fpriv *fpriv;
@@ -506,6 +527,9 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
 
                file_priv->driver_priv = fpriv;
        }
+
+       pm_runtime_mark_last_busy(dev->dev);
+       pm_runtime_put_autosuspend(dev->dev);
        return 0;
 }
 
index 7cb178a..0c7b8c6 100644 (file)
@@ -1056,6 +1056,26 @@ static void radeon_crtc_commit(struct drm_crtc *crtc)
        }
 }
 
+static void radeon_crtc_disable(struct drm_crtc *crtc)
+{
+       radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       if (crtc->fb) {
+               int r;
+               struct radeon_framebuffer *radeon_fb;
+               struct radeon_bo *rbo;
+
+               radeon_fb = to_radeon_framebuffer(crtc->fb);
+               rbo = gem_to_radeon_bo(radeon_fb->obj);
+               r = radeon_bo_reserve(rbo, false);
+               if (unlikely(r))
+                       DRM_ERROR("failed to reserve rbo before unpin\n");
+               else {
+                       radeon_bo_unpin(rbo);
+                       radeon_bo_unreserve(rbo);
+               }
+       }
+}
+
 static const struct drm_crtc_helper_funcs legacy_helper_funcs = {
        .dpms = radeon_crtc_dpms,
        .mode_fixup = radeon_crtc_mode_fixup,
@@ -1065,6 +1085,7 @@ static const struct drm_crtc_helper_funcs legacy_helper_funcs = {
        .prepare = radeon_crtc_prepare,
        .commit = radeon_crtc_commit,
        .load_lut = radeon_crtc_load_lut,
+       .disable = radeon_crtc_disable
 };
 
 
index 62cd512..c89971d 100644 (file)
@@ -392,7 +392,7 @@ void radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder,
        props.type = BACKLIGHT_RAW;
        snprintf(bl_name, sizeof(bl_name),
                 "radeon_bl%d", dev->primary->index);
-       bd = backlight_device_register(bl_name, &drm_connector->kdev,
+       bd = backlight_device_register(bl_name, drm_connector->kdev,
                                       pdata, &radeon_backlight_ops, &props);
        if (IS_ERR(bd)) {
                DRM_ERROR("Backlight registration failed\n");
index ef63d3f..3f0dd66 100644 (file)
@@ -249,6 +249,8 @@ struct radeon_mode_info {
        struct drm_property *underscan_vborder_property;
        /* audio */
        struct drm_property *audio_property;
+       /* FMT dithering */
+       struct drm_property *dither_property;
        /* hardcoded DFP edid from BIOS */
        struct edid *bios_hardcoded_edid;
        int bios_hardcoded_edid_size;
@@ -479,6 +481,11 @@ enum radeon_connector_audio {
        RADEON_AUDIO_AUTO = 2
 };
 
+enum radeon_connector_dither {
+       RADEON_FMT_DITHER_DISABLE = 0,
+       RADEON_FMT_DITHER_ENABLE = 1,
+};
+
 struct radeon_connector {
        struct drm_connector base;
        uint32_t connector_id;
@@ -498,6 +505,7 @@ struct radeon_connector {
        struct radeon_router router;
        struct radeon_i2c_chan *router_bus;
        enum radeon_connector_audio audio;
+       enum radeon_connector_dither dither;
 };
 
 struct radeon_framebuffer {
@@ -758,7 +766,8 @@ extern int radeon_crtc_cursor_move(struct drm_crtc *crtc,
                                   int x, int y);
 
 extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc,
-                                     int *vpos, int *hpos);
+                                     int *vpos, int *hpos, ktime_t *stime,
+                                     ktime_t *etime);
 
 extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev);
 extern struct edid *
@@ -850,6 +859,12 @@ void radeon_legacy_tv_mode_set(struct drm_encoder *encoder,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode);
 
+/* fmt blocks */
+void avivo_program_fmt(struct drm_encoder *encoder);
+void dce3_program_fmt(struct drm_encoder *encoder);
+void dce4_program_fmt(struct drm_encoder *encoder);
+void dce8_program_fmt(struct drm_encoder *encoder);
+
 /* fbdev layer */
 int radeon_fbdev_init(struct radeon_device *rdev);
 void radeon_fbdev_fini(struct radeon_device *rdev);
index 4f6b7fc..866ace0 100644 (file)
@@ -508,17 +508,21 @@ static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev,
        } else if (strncmp("auto", buf, strlen("auto")) == 0) {
                level = RADEON_DPM_FORCED_LEVEL_AUTO;
        } else {
-               mutex_unlock(&rdev->pm.mutex);
                count = -EINVAL;
                goto fail;
        }
        if (rdev->asic->dpm.force_performance_level) {
+               if (rdev->pm.dpm.thermal_active) {
+                       count = -EINVAL;
+                       goto fail;
+               }
                ret = radeon_dpm_force_performance_level(rdev, level);
                if (ret)
                        count = -EINVAL;
        }
-       mutex_unlock(&rdev->pm.mutex);
 fail:
+       mutex_unlock(&rdev->pm.mutex);
+
        return count;
 }
 
@@ -881,11 +885,12 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
                }
        }
 
-       printk("switching from power state:\n");
-       radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps);
-       printk("switching to power state:\n");
-       radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps);
-
+       if (radeon_dpm == 1) {
+               printk("switching from power state:\n");
+               radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps);
+               printk("switching to power state:\n");
+               radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps);
+       }
        mutex_lock(&rdev->ddev->struct_mutex);
        down_write(&rdev->pm.mclk_lock);
        mutex_lock(&rdev->ring_lock);
@@ -918,12 +923,16 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
        radeon_dpm_post_set_power_state(rdev);
 
        if (rdev->asic->dpm.force_performance_level) {
-               if (rdev->pm.dpm.thermal_active)
+               if (rdev->pm.dpm.thermal_active) {
+                       enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;
                        /* force low perf level for thermal */
                        radeon_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_LOW);
-               else
-                       /* otherwise, enable auto */
-                       radeon_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
+                       /* save the user's level */
+                       rdev->pm.dpm.forced_level = level;
+               } else {
+                       /* otherwise, user selected level */
+                       radeon_dpm_force_performance_level(rdev, rdev->pm.dpm.forced_level);
+               }
        }
 
 done:
@@ -1179,7 +1188,8 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev)
        mutex_lock(&rdev->pm.mutex);
        radeon_dpm_init(rdev);
        rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
-       radeon_dpm_print_power_states(rdev);
+       if (radeon_dpm == 1)
+               radeon_dpm_print_power_states(rdev);
        radeon_dpm_setup_asic(rdev);
        ret = radeon_dpm_enable(rdev);
        mutex_unlock(&rdev->pm.mutex);
@@ -1241,6 +1251,24 @@ int radeon_pm_init(struct radeon_device *rdev)
        case CHIP_RV670:
        case CHIP_RS780:
        case CHIP_RS880:
+       case CHIP_CAYMAN:
+       case CHIP_ARUBA:
+       case CHIP_BONAIRE:
+       case CHIP_KABINI:
+       case CHIP_KAVERI:
+       case CHIP_HAWAII:
+               /* DPM requires the RLC, RV770+ dGPU requires SMC */
+               if (!rdev->rlc_fw)
+                       rdev->pm.pm_method = PM_METHOD_PROFILE;
+               else if ((rdev->family >= CHIP_RV770) &&
+                        (!(rdev->flags & RADEON_IS_IGP)) &&
+                        (!rdev->smc_fw))
+                       rdev->pm.pm_method = PM_METHOD_PROFILE;
+               else if (radeon_dpm == 1)
+                       rdev->pm.pm_method = PM_METHOD_DPM;
+               else
+                       rdev->pm.pm_method = PM_METHOD_PROFILE;
+               break;
        case CHIP_RV770:
        case CHIP_RV730:
        case CHIP_RV710:
@@ -1256,16 +1284,11 @@ int radeon_pm_init(struct radeon_device *rdev)
        case CHIP_BARTS:
        case CHIP_TURKS:
        case CHIP_CAICOS:
-       case CHIP_CAYMAN:
-       case CHIP_ARUBA:
        case CHIP_TAHITI:
        case CHIP_PITCAIRN:
        case CHIP_VERDE:
        case CHIP_OLAND:
        case CHIP_HAINAN:
-       case CHIP_BONAIRE:
-       case CHIP_KABINI:
-       case CHIP_KAVERI:
                /* DPM requires the RLC, RV770+ dGPU requires SMC */
                if (!rdev->rlc_fw)
                        rdev->pm.pm_method = PM_METHOD_PROFILE;
@@ -1273,10 +1296,10 @@ int radeon_pm_init(struct radeon_device *rdev)
                         (!(rdev->flags & RADEON_IS_IGP)) &&
                         (!rdev->smc_fw))
                        rdev->pm.pm_method = PM_METHOD_PROFILE;
-               else if (radeon_dpm == 1)
-                       rdev->pm.pm_method = PM_METHOD_DPM;
-               else
+               else if (radeon_dpm == 0)
                        rdev->pm.pm_method = PM_METHOD_PROFILE;
+               else
+                       rdev->pm.pm_method = PM_METHOD_DPM;
                break;
        default:
                /* default to profile method */
@@ -1468,7 +1491,7 @@ static bool radeon_pm_in_vbl(struct radeon_device *rdev)
         */
        for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) {
                if (rdev->pm.active_crtcs & (1 << crtc)) {
-                       vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, &vpos, &hpos);
+                       vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, &vpos, &hpos, NULL, NULL);
                        if ((vbl_status & DRM_SCANOUTPOS_VALID) &&
                            !(vbl_status & DRM_SCANOUTPOS_INVBL))
                                in_vbl = false;
index f7e3678..811bca6 100644 (file)
@@ -47,6 +47,30 @@ TRACE_EVENT(radeon_cs,
                      __entry->fences)
 );
 
+TRACE_EVENT(radeon_vm_set_page,
+           TP_PROTO(uint64_t pe, uint64_t addr, unsigned count,
+                    uint32_t incr, uint32_t flags),
+           TP_ARGS(pe, addr, count, incr, flags),
+           TP_STRUCT__entry(
+                            __field(u64, pe)
+                            __field(u64, addr)
+                            __field(u32, count)
+                            __field(u32, incr)
+                            __field(u32, flags)
+                            ),
+
+           TP_fast_assign(
+                          __entry->pe = pe;
+                          __entry->addr = addr;
+                          __entry->count = count;
+                          __entry->incr = incr;
+                          __entry->flags = flags;
+                          ),
+           TP_printk("pe=%010Lx, addr=%010Lx, incr=%u, flags=%08x, count=%u",
+                     __entry->pe, __entry->addr, __entry->incr,
+                     __entry->flags, __entry->count)
+);
+
 DECLARE_EVENT_CLASS(radeon_fence_request,
 
            TP_PROTO(struct drm_device *dev, u32 seqno),
index 3385836..a77cd27 100644 (file)
@@ -59,6 +59,7 @@
 #define SI_MC_UCODE_SIZE             7769
 #define OLAND_MC_UCODE_SIZE          7863
 #define CIK_MC_UCODE_SIZE            7866
+#define HAWAII_MC_UCODE_SIZE         7933
 
 /* SDMA */
 #define CIK_SDMA_UCODE_SIZE          1050
 #define BONAIRE_SMC_UCODE_START      0x20000
 #define BONAIRE_SMC_UCODE_SIZE       0x1FDEC
 
+#define HAWAII_SMC_UCODE_START       0x20000
+#define HAWAII_SMC_UCODE_SIZE        0x1FDEC
+
 #endif
index 308eff5..373d088 100644 (file)
@@ -97,6 +97,7 @@ int radeon_uvd_init(struct radeon_device *rdev)
        case CHIP_BONAIRE:
        case CHIP_KABINI:
        case CHIP_KAVERI:
+       case CHIP_HAWAII:
                fw_name = FIRMWARE_BONAIRE;
                break;
 
@@ -240,6 +241,8 @@ void radeon_uvd_free_handles(struct radeon_device *rdev, struct drm_file *filp)
                if (handle != 0 && rdev->uvd.filp[i] == filp) {
                        struct radeon_fence *fence;
 
+                       radeon_uvd_note_usage(rdev);
+
                        r = radeon_uvd_get_destroy_msg(rdev,
                                R600_RING_TYPE_UVD_INDEX, handle, &fence);
                        if (r) {
@@ -620,7 +623,7 @@ static int radeon_uvd_send_msg(struct radeon_device *rdev,
        if (r) 
                goto err;
 
-       r = radeon_ib_get(rdev, ring, &ib, NULL, 16);
+       r = radeon_ib_get(rdev, ring, &ib, NULL, 64);
        if (r)
                goto err;
 
index 6acba80..76cc8d3 100644 (file)
@@ -153,6 +153,70 @@ u32 rs600_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
        return RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) & AVIVO_D1GRPH_SURFACE_UPDATE_PENDING;
 }
 
+void avivo_program_fmt(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+       int bpc = 0;
+       u32 tmp = 0;
+       enum radeon_connector_dither dither = RADEON_FMT_DITHER_DISABLE;
+
+       if (connector) {
+               struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+               bpc = radeon_get_monitor_bpc(connector);
+               dither = radeon_connector->dither;
+       }
+
+       /* LVDS FMT is set up by atom */
+       if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
+               return;
+
+       if (bpc == 0)
+               return;
+
+       switch (bpc) {
+       case 6:
+               if (dither == RADEON_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN;
+               else
+                       tmp |= AVIVO_TMDS_BIT_DEPTH_CONTROL_TRUNCATE_EN;
+               break;
+       case 8:
+               if (dither == RADEON_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= (AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN |
+                               AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_DEPTH);
+               else
+                       tmp |= (AVIVO_TMDS_BIT_DEPTH_CONTROL_TRUNCATE_EN |
+                               AVIVO_TMDS_BIT_DEPTH_CONTROL_TRUNCATE_DEPTH);
+               break;
+       case 10:
+       default:
+               /* not needed */
+               break;
+       }
+
+       switch (radeon_encoder->encoder_id) {
+       case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+               WREG32(AVIVO_TMDSA_BIT_DEPTH_CONTROL, tmp);
+               break;
+       case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+               WREG32(AVIVO_LVTMA_BIT_DEPTH_CONTROL, tmp);
+               break;
+       case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+               WREG32(AVIVO_DVOA_BIT_DEPTH_CONTROL, tmp);
+               break;
+       case ENCODER_OBJECT_ID_INTERNAL_DDI:
+               WREG32(AVIVO_DDIA_BIT_DEPTH_CONTROL, tmp);
+               break;
+       default:
+               break;
+       }
+}
+
 void rs600_pm_misc(struct radeon_device *rdev)
 {
        int requested_index = rdev->pm.requested_power_state_index;
index 1447d79..1c56062 100644 (file)
@@ -345,9 +345,11 @@ static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
                if (max_bandwidth.full > rdev->pm.sideport_bandwidth.full &&
                        rdev->pm.sideport_bandwidth.full)
                        max_bandwidth = rdev->pm.sideport_bandwidth;
-               read_delay_latency.full = dfixed_const(370 * 800 * 1000);
-               read_delay_latency.full = dfixed_div(read_delay_latency,
-                       rdev->pm.igp_sideport_mclk);
+               read_delay_latency.full = dfixed_const(370 * 800);
+               a.full = dfixed_const(1000);
+               b.full = dfixed_div(rdev->pm.igp_sideport_mclk, a);
+               read_delay_latency.full = dfixed_div(read_delay_latency, b);
+               read_delay_latency.full = dfixed_mul(read_delay_latency, a);
        } else {
                if (max_bandwidth.full > rdev->pm.k8_bandwidth.full &&
                        rdev->pm.k8_bandwidth.full)
@@ -488,14 +490,10 @@ static void rs690_compute_mode_priority(struct radeon_device *rdev,
                }
                if (wm0->priority_mark.full > priority_mark02.full)
                        priority_mark02.full = wm0->priority_mark.full;
-               if (dfixed_trunc(priority_mark02) < 0)
-                       priority_mark02.full = 0;
                if (wm0->priority_mark_max.full > priority_mark02.full)
                        priority_mark02.full = wm0->priority_mark_max.full;
                if (wm1->priority_mark.full > priority_mark12.full)
                        priority_mark12.full = wm1->priority_mark.full;
-               if (dfixed_trunc(priority_mark12) < 0)
-                       priority_mark12.full = 0;
                if (wm1->priority_mark_max.full > priority_mark12.full)
                        priority_mark12.full = wm1->priority_mark_max.full;
                *d1mode_priority_a_cnt = dfixed_trunc(priority_mark02);
@@ -526,8 +524,6 @@ static void rs690_compute_mode_priority(struct radeon_device *rdev,
                }
                if (wm0->priority_mark.full > priority_mark02.full)
                        priority_mark02.full = wm0->priority_mark.full;
-               if (dfixed_trunc(priority_mark02) < 0)
-                       priority_mark02.full = 0;
                if (wm0->priority_mark_max.full > priority_mark02.full)
                        priority_mark02.full = wm0->priority_mark_max.full;
                *d1mode_priority_a_cnt = dfixed_trunc(priority_mark02);
@@ -555,8 +551,6 @@ static void rs690_compute_mode_priority(struct radeon_device *rdev,
                }
                if (wm1->priority_mark.full > priority_mark12.full)
                        priority_mark12.full = wm1->priority_mark.full;
-               if (dfixed_trunc(priority_mark12) < 0)
-                       priority_mark12.full = 0;
                if (wm1->priority_mark_max.full > priority_mark12.full)
                        priority_mark12.full = wm1->priority_mark_max.full;
                *d2mode_priority_a_cnt = dfixed_trunc(priority_mark12);
index 873eb4b..5d1c316 100644 (file)
@@ -1155,14 +1155,10 @@ static void rv515_compute_mode_priority(struct radeon_device *rdev,
                }
                if (wm0->priority_mark.full > priority_mark02.full)
                        priority_mark02.full = wm0->priority_mark.full;
-               if (dfixed_trunc(priority_mark02) < 0)
-                       priority_mark02.full = 0;
                if (wm0->priority_mark_max.full > priority_mark02.full)
                        priority_mark02.full = wm0->priority_mark_max.full;
                if (wm1->priority_mark.full > priority_mark12.full)
                        priority_mark12.full = wm1->priority_mark.full;
-               if (dfixed_trunc(priority_mark12) < 0)
-                       priority_mark12.full = 0;
                if (wm1->priority_mark_max.full > priority_mark12.full)
                        priority_mark12.full = wm1->priority_mark_max.full;
                *d1mode_priority_a_cnt = dfixed_trunc(priority_mark02);
@@ -1193,8 +1189,6 @@ static void rv515_compute_mode_priority(struct radeon_device *rdev,
                }
                if (wm0->priority_mark.full > priority_mark02.full)
                        priority_mark02.full = wm0->priority_mark.full;
-               if (dfixed_trunc(priority_mark02) < 0)
-                       priority_mark02.full = 0;
                if (wm0->priority_mark_max.full > priority_mark02.full)
                        priority_mark02.full = wm0->priority_mark_max.full;
                *d1mode_priority_a_cnt = dfixed_trunc(priority_mark02);
@@ -1222,8 +1216,6 @@ static void rv515_compute_mode_priority(struct radeon_device *rdev,
                }
                if (wm1->priority_mark.full > priority_mark12.full)
                        priority_mark12.full = wm1->priority_mark.full;
-               if (dfixed_trunc(priority_mark12) < 0)
-                       priority_mark12.full = 0;
                if (wm1->priority_mark_max.full > priority_mark12.full)
                        priority_mark12.full = wm1->priority_mark_max.full;
                *d2mode_priority_a_cnt = dfixed_trunc(priority_mark12);
index 5811d27..26633a0 100644 (file)
@@ -407,9 +407,9 @@ static void rv6xx_enable_engine_feedback_and_reference_sync(struct radeon_device
        WREG32_P(SPLL_CNTL_MODE, SPLL_DIV_SYNC, ~SPLL_DIV_SYNC);
 }
 
-static u64 rv6xx_clocks_per_unit(u32 unit)
+static u32 rv6xx_clocks_per_unit(u32 unit)
 {
-       u64 tmp = 1 << (2 * unit);
+       u32 tmp = 1 << (2 * unit);
 
        return tmp;
 }
@@ -417,7 +417,7 @@ static u64 rv6xx_clocks_per_unit(u32 unit)
 static u32 rv6xx_scale_count_given_unit(struct radeon_device *rdev,
                                        u32 unscaled_count, u32 unit)
 {
-       u32 count_per_unit = (u32)rv6xx_clocks_per_unit(unit);
+       u32 count_per_unit = rv6xx_clocks_per_unit(unit);
 
        return (unscaled_count + count_per_unit - 1) / count_per_unit;
 }
index d96f7cb..6a64cca 100644 (file)
@@ -78,11 +78,6 @@ extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_
 extern u32 evergreen_get_number_of_dram_channels(struct radeon_device *rdev);
 extern void evergreen_print_gpu_status_regs(struct radeon_device *rdev);
 extern bool evergreen_is_display_hung(struct radeon_device *rdev);
-extern void si_dma_vm_set_page(struct radeon_device *rdev,
-                              struct radeon_ib *ib,
-                              uint64_t pe,
-                              uint64_t addr, unsigned count,
-                              uint32_t incr, uint32_t flags);
 static void si_enable_gui_idle_interrupt(struct radeon_device *rdev,
                                         bool enable);
 static void si_fini_pg(struct radeon_device *rdev);
@@ -4673,61 +4668,6 @@ static void si_vm_decode_fault(struct radeon_device *rdev,
               block, mc_id);
 }
 
-/**
- * si_vm_set_page - update the page tables using the CP
- *
- * @rdev: radeon_device pointer
- * @ib: indirect buffer to fill with commands
- * @pe: addr of the page entry
- * @addr: dst addr to write into pe
- * @count: number of page entries to update
- * @incr: increase next addr by incr bytes
- * @flags: access flags
- *
- * Update the page tables using the CP (SI).
- */
-void si_vm_set_page(struct radeon_device *rdev,
-                   struct radeon_ib *ib,
-                   uint64_t pe,
-                   uint64_t addr, unsigned count,
-                   uint32_t incr, uint32_t flags)
-{
-       uint32_t r600_flags = cayman_vm_page_flags(rdev, flags);
-       uint64_t value;
-       unsigned ndw;
-
-       if (rdev->asic->vm.pt_ring_index == RADEON_RING_TYPE_GFX_INDEX) {
-               while (count) {
-                       ndw = 2 + count * 2;
-                       if (ndw > 0x3FFE)
-                               ndw = 0x3FFE;
-
-                       ib->ptr[ib->length_dw++] = PACKET3(PACKET3_WRITE_DATA, ndw);
-                       ib->ptr[ib->length_dw++] = (WRITE_DATA_ENGINE_SEL(0) |
-                                       WRITE_DATA_DST_SEL(1));
-                       ib->ptr[ib->length_dw++] = pe;
-                       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-                       for (; ndw > 2; ndw -= 2, --count, pe += 8) {
-                               if (flags & RADEON_VM_PAGE_SYSTEM) {
-                                       value = radeon_vm_map_gart(rdev, addr);
-                                       value &= 0xFFFFFFFFFFFFF000ULL;
-                               } else if (flags & RADEON_VM_PAGE_VALID) {
-                                       value = addr;
-                               } else {
-                                       value = 0;
-                               }
-                               addr += incr;
-                               value |= r600_flags;
-                               ib->ptr[ib->length_dw++] = value;
-                               ib->ptr[ib->length_dw++] = upper_32_bits(value);
-                       }
-               }
-       } else {
-               /* DMA */
-               si_dma_vm_set_page(rdev, ib, pe, addr, count, incr, flags);
-       }
-}
-
 void si_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
 {
        struct radeon_ring *ring = &rdev->ring[ridx];
@@ -5372,52 +5312,53 @@ void si_get_csb_buffer(struct radeon_device *rdev, volatile u32 *buffer)
        if (buffer == NULL)
                return;
 
-       buffer[count++] = PACKET3(PACKET3_PREAMBLE_CNTL, 0);
-       buffer[count++] = PACKET3_PREAMBLE_BEGIN_CLEAR_STATE;
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+       buffer[count++] = cpu_to_le32(PACKET3_PREAMBLE_BEGIN_CLEAR_STATE);
 
-       buffer[count++] = PACKET3(PACKET3_CONTEXT_CONTROL, 1);
-       buffer[count++] = 0x80000000;
-       buffer[count++] = 0x80000000;
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_CONTEXT_CONTROL, 1));
+       buffer[count++] = cpu_to_le32(0x80000000);
+       buffer[count++] = cpu_to_le32(0x80000000);
 
        for (sect = rdev->rlc.cs_data; sect->section != NULL; ++sect) {
                for (ext = sect->section; ext->extent != NULL; ++ext) {
                        if (sect->id == SECT_CONTEXT) {
-                               buffer[count++] = PACKET3(PACKET3_SET_CONTEXT_REG, ext->reg_count);
-                               buffer[count++] = ext->reg_index - 0xa000;
+                               buffer[count++] =
+                                       cpu_to_le32(PACKET3(PACKET3_SET_CONTEXT_REG, ext->reg_count));
+                               buffer[count++] = cpu_to_le32(ext->reg_index - 0xa000);
                                for (i = 0; i < ext->reg_count; i++)
-                                       buffer[count++] = ext->extent[i];
+                                       buffer[count++] = cpu_to_le32(ext->extent[i]);
                        } else {
                                return;
                        }
                }
        }
 
-       buffer[count++] = PACKET3(PACKET3_SET_CONTEXT_REG, 1);
-       buffer[count++] = PA_SC_RASTER_CONFIG - PACKET3_SET_CONTEXT_REG_START;
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_SET_CONTEXT_REG, 1));
+       buffer[count++] = cpu_to_le32(PA_SC_RASTER_CONFIG - PACKET3_SET_CONTEXT_REG_START);
        switch (rdev->family) {
        case CHIP_TAHITI:
        case CHIP_PITCAIRN:
-               buffer[count++] = 0x2a00126a;
+               buffer[count++] = cpu_to_le32(0x2a00126a);
                break;
        case CHIP_VERDE:
-               buffer[count++] = 0x0000124a;
+               buffer[count++] = cpu_to_le32(0x0000124a);
                break;
        case CHIP_OLAND:
-               buffer[count++] = 0x00000082;
+               buffer[count++] = cpu_to_le32(0x00000082);
                break;
        case CHIP_HAINAN:
-               buffer[count++] = 0x00000000;
+               buffer[count++] = cpu_to_le32(0x00000000);
                break;
        default:
-               buffer[count++] = 0x00000000;
+               buffer[count++] = cpu_to_le32(0x00000000);
                break;
        }
 
-       buffer[count++] = PACKET3(PACKET3_PREAMBLE_CNTL, 0);
-       buffer[count++] = PACKET3_PREAMBLE_END_CLEAR_STATE;
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+       buffer[count++] = cpu_to_le32(PACKET3_PREAMBLE_END_CLEAR_STATE);
 
-       buffer[count++] = PACKET3(PACKET3_CLEAR_STATE, 0);
-       buffer[count++] = 0;
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_CLEAR_STATE, 0));
+       buffer[count++] = cpu_to_le32(0);
 }
 
 static void si_init_pg(struct radeon_device *rdev)
index 49909d2..8e8f461 100644 (file)
@@ -24,6 +24,7 @@
 #include <drm/drmP.h>
 #include "radeon.h"
 #include "radeon_asic.h"
+#include "radeon_trace.h"
 #include "sid.h"
 
 u32 si_gpu_check_soft_reset(struct radeon_device *rdev);
@@ -75,11 +76,12 @@ void si_dma_vm_set_page(struct radeon_device *rdev,
                        uint64_t addr, unsigned count,
                        uint32_t incr, uint32_t flags)
 {
-       uint32_t r600_flags = cayman_vm_page_flags(rdev, flags);
        uint64_t value;
        unsigned ndw;
 
-       if (flags & RADEON_VM_PAGE_SYSTEM) {
+       trace_radeon_vm_set_page(pe, addr, count, incr, flags);
+
+       if (flags & R600_PTE_SYSTEM) {
                while (count) {
                        ndw = count * 2;
                        if (ndw > 0xFFFFE)
@@ -90,16 +92,10 @@ void si_dma_vm_set_page(struct radeon_device *rdev,
                        ib->ptr[ib->length_dw++] = pe;
                        ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
                        for (; ndw > 0; ndw -= 2, --count, pe += 8) {
-                               if (flags & RADEON_VM_PAGE_SYSTEM) {
-                                       value = radeon_vm_map_gart(rdev, addr);
-                                       value &= 0xFFFFFFFFFFFFF000ULL;
-                               } else if (flags & RADEON_VM_PAGE_VALID) {
-                                       value = addr;
-                               } else {
-                                       value = 0;
-                               }
+                               value = radeon_vm_map_gart(rdev, addr);
+                               value &= 0xFFFFFFFFFFFFF000ULL;
                                addr += incr;
-                               value |= r600_flags;
+                               value |= flags;
                                ib->ptr[ib->length_dw++] = value;
                                ib->ptr[ib->length_dw++] = upper_32_bits(value);
                        }
@@ -110,7 +106,7 @@ void si_dma_vm_set_page(struct radeon_device *rdev,
                        if (ndw > 0xFFFFE)
                                ndw = 0xFFFFE;
 
-                       if (flags & RADEON_VM_PAGE_VALID)
+                       if (flags & R600_PTE_VALID)
                                value = addr;
                        else
                                value = 0;
@@ -118,7 +114,7 @@ void si_dma_vm_set_page(struct radeon_device *rdev,
                        ib->ptr[ib->length_dw++] = DMA_PTE_PDE_PACKET(ndw);
                        ib->ptr[ib->length_dw++] = pe; /* dst addr */
                        ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
-                       ib->ptr[ib->length_dw++] = r600_flags; /* mask */
+                       ib->ptr[ib->length_dw++] = flags; /* mask */
                        ib->ptr[ib->length_dw++] = 0;
                        ib->ptr[ib->length_dw++] = value; /* value */
                        ib->ptr[ib->length_dw++] = upper_32_bits(value);
index 2332aa1..0b00c79 100644 (file)
@@ -3589,7 +3589,12 @@ static void si_program_display_gap(struct radeon_device *rdev)
                WREG32(DCCG_DISP_SLOW_SELECT_REG, tmp);
        }
 
-       si_notify_smc_display_change(rdev, rdev->pm.dpm.new_active_crtc_count > 0);
+       /* Setting this to false forces the performance state to low if the crtcs are disabled.
+        * This can be a problem on PowerXpress systems or if you want to use the card
+        * for offscreen rendering or compute if there are no crtcs enabled.  Set it to
+        * true for now so that performance scales even if the displays are off.
+        */
+       si_notify_smc_display_change(rdev, true /*rdev->pm.dpm.new_active_crtc_count > 0*/);
 }
 
 static void si_enable_spread_spectrum(struct radeon_device *rdev, bool enable)
@@ -4553,7 +4558,7 @@ static int si_init_smc_table(struct radeon_device *rdev)
                table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
 
        if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REVERT_GPIO5_POLARITY)
-               table->systemFlags |= PPSMC_EXTRAFLAGS_AC2DC_GPIO5_POLARITY_HIGH;
+               table->extraFlags |= PPSMC_EXTRAFLAGS_AC2DC_GPIO5_POLARITY_HIGH;
 
        if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_VRHOT_GPIO_CONFIGURABLE) {
                table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT_PROG_GPIO;
index 7e2e0ea..b322acc 100644 (file)
 #define                STATE3_MASK                             (0x1f << 15)
 #define                STATE3_SHIFT                            15
 
-#define        MC_SEQ_TRAIN_WAKEUP_CNTL                        0x2808
+#define        MC_SEQ_TRAIN_WAKEUP_CNTL                        0x28e8
 #define                TRAIN_DONE_D0                           (1 << 30)
 #define                TRAIN_DONE_D1                           (1 << 31)
 
  * bit5 = 176.4 kHz
  * bit6 = 192 kHz
  */
+
+#define AZ_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC         0x37
+#       define VIDEO_LIPSYNC(x)                           (((x) & 0xff) << 0)
+#       define AUDIO_LIPSYNC(x)                           (((x) & 0xff) << 8)
+/* VIDEO_LIPSYNC, AUDIO_LIPSYNC
+ * 0   = invalid
+ * x   = legal delay value
+ * 255 = sync not supported
+ */
+#define AZ_F0_CODEC_PIN_CONTROL_RESPONSE_HBR             0x38
+#       define HBR_CAPABLE                                (1 << 0) /* enabled by default */
+
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO0               0x3a
+#       define MANUFACTURER_ID(x)                        (((x) & 0xffff) << 0)
+#       define PRODUCT_ID(x)                             (((x) & 0xffff) << 16)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO1               0x3b
+#       define SINK_DESCRIPTION_LEN(x)                   (((x) & 0xff) << 0)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO2               0x3c
+#       define PORT_ID0(x)                               (((x) & 0xffffffff) << 0)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO3               0x3d
+#       define PORT_ID1(x)                               (((x) & 0xffffffff) << 0)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO4               0x3e
+#       define DESCRIPTION0(x)                           (((x) & 0xff) << 0)
+#       define DESCRIPTION1(x)                           (((x) & 0xff) << 8)
+#       define DESCRIPTION2(x)                           (((x) & 0xff) << 16)
+#       define DESCRIPTION3(x)                           (((x) & 0xff) << 24)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO5               0x3f
+#       define DESCRIPTION4(x)                           (((x) & 0xff) << 0)
+#       define DESCRIPTION5(x)                           (((x) & 0xff) << 8)
+#       define DESCRIPTION6(x)                           (((x) & 0xff) << 16)
+#       define DESCRIPTION7(x)                           (((x) & 0xff) << 24)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO6               0x40
+#       define DESCRIPTION8(x)                           (((x) & 0xff) << 0)
+#       define DESCRIPTION9(x)                           (((x) & 0xff) << 8)
+#       define DESCRIPTION10(x)                          (((x) & 0xff) << 16)
+#       define DESCRIPTION11(x)                          (((x) & 0xff) << 24)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO7               0x41
+#       define DESCRIPTION12(x)                          (((x) & 0xff) << 0)
+#       define DESCRIPTION13(x)                          (((x) & 0xff) << 8)
+#       define DESCRIPTION14(x)                          (((x) & 0xff) << 16)
+#       define DESCRIPTION15(x)                          (((x) & 0xff) << 24)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO8               0x42
+#       define DESCRIPTION16(x)                          (((x) & 0xff) << 0)
+#       define DESCRIPTION17(x)                          (((x) & 0xff) << 8)
+
 #define AZ_F0_CODEC_PIN_CONTROL_HOTPLUG_CONTROL          0x54
 #       define AUDIO_ENABLED                             (1 << 31)
 
index c590cd9..d8e835a 100644 (file)
@@ -4,6 +4,7 @@ config DRM_RCAR_DU
        select DRM_KMS_HELPER
        select DRM_KMS_CMA_HELPER
        select DRM_GEM_CMA_HELPER
+       select DRM_KMS_FB_HELPER
        help
          Choose this option if you have an R-Car chipset.
          If M is selected the module will be called rcar-du-drm.
index ca498d1..2ee44ca 100644 (file)
@@ -1,7 +1,9 @@
 config DRM_SHMOBILE
        tristate "DRM Support for SH Mobile"
        depends on DRM && (ARM || SUPERH)
+       select BACKLIGHT_CLASS_DEVICE
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select DRM_KMS_CMA_HELPER
        select DRM_GEM_CMA_HELPER
        help
index 54bad98..562f9a4 100644 (file)
@@ -40,7 +40,7 @@
 static void shmob_drm_clk_on(struct shmob_drm_device *sdev)
 {
        if (sdev->clock)
-               clk_enable(sdev->clock);
+               clk_prepare_enable(sdev->clock);
 #if 0
        if (sdev->meram_dev && sdev->meram_dev->pdev)
                pm_runtime_get_sync(&sdev->meram_dev->pdev->dev);
@@ -54,7 +54,7 @@ static void shmob_drm_clk_off(struct shmob_drm_device *sdev)
                pm_runtime_put_sync(&sdev->meram_dev->pdev->dev);
 #endif
        if (sdev->clock)
-               clk_disable(sdev->clock);
+               clk_disable_unprepare(sdev->clock);
 }
 
 /* -----------------------------------------------------------------------------
similarity index 87%
rename from drivers/gpu/host1x/drm/Kconfig
rename to drivers/gpu/drm/tegra/Kconfig
index 69853a4..8961ba6 100644 (file)
@@ -1,7 +1,10 @@
 config DRM_TEGRA
        bool "NVIDIA Tegra DRM"
+       depends on ARCH_TEGRA || ARCH_MULTIPLATFORM
        depends on DRM
+       select TEGRA_HOST1X
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select FB_SYS_FILLRECT
        select FB_SYS_COPYAREA
        select FB_SYS_IMAGEBLIT
@@ -13,6 +16,11 @@ config DRM_TEGRA
 
 if DRM_TEGRA
 
+config DRM_TEGRA_DEBUG
+       bool "NVIDIA Tegra DRM debug support"
+       help
+         Say yes here to enable debugging support.
+
 config DRM_TEGRA_STAGING
        bool "Enable HOST1X interface"
        depends on STAGING
@@ -21,9 +29,4 @@ config DRM_TEGRA_STAGING
 
          If unsure, choose N.
 
-config DRM_TEGRA_DEBUG
-       bool "NVIDIA Tegra DRM debug support"
-       help
-         Say yes here to enable debugging support.
-
 endif
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
new file mode 100644 (file)
index 0000000..edc76ab
--- /dev/null
@@ -0,0 +1,15 @@
+ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
+
+tegra-drm-y := \
+       bus.o \
+       drm.o \
+       gem.o \
+       fb.o \
+       dc.o \
+       output.o \
+       rgb.o \
+       hdmi.o \
+       gr2d.o \
+       gr3d.o
+
+obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o
diff --git a/drivers/gpu/drm/tegra/bus.c b/drivers/gpu/drm/tegra/bus.c
new file mode 100644 (file)
index 0000000..565f8f7
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "drm.h"
+
+static int drm_host1x_set_busid(struct drm_device *dev,
+                               struct drm_master *master)
+{
+       const char *device = dev_name(dev->dev);
+       const char *driver = dev->driver->name;
+       const char *bus = dev->dev->bus->name;
+       int length;
+
+       master->unique_len = strlen(bus) + 1 + strlen(device);
+       master->unique_size = master->unique_len;
+
+       master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
+       if (!master->unique)
+               return -ENOMEM;
+
+       snprintf(master->unique, master->unique_len + 1, "%s:%s", bus, device);
+
+       length = strlen(driver) + 1 + master->unique_len;
+
+       dev->devname = kmalloc(length + 1, GFP_KERNEL);
+       if (!dev->devname)
+               return -ENOMEM;
+
+       snprintf(dev->devname, length + 1, "%s@%s", driver, master->unique);
+
+       return 0;
+}
+
+static struct drm_bus drm_host1x_bus = {
+       .bus_type = DRIVER_BUS_HOST1X,
+       .set_busid = drm_host1x_set_busid,
+};
+
+int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device)
+{
+       struct drm_device *drm;
+       int ret;
+
+       INIT_LIST_HEAD(&driver->device_list);
+       driver->bus = &drm_host1x_bus;
+
+       drm = drm_dev_alloc(driver, &device->dev);
+       if (!drm)
+               return -ENOMEM;
+
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               goto err_free;
+
+       DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
+                driver->major, driver->minor, driver->patchlevel,
+                driver->date, drm->primary->index);
+
+       return 0;
+
+err_free:
+       drm_dev_free(drm);
+       return ret;
+}
+
+void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device)
+{
+       struct tegra_drm *tegra = dev_get_drvdata(&device->dev);
+
+       drm_put_dev(tegra->drm);
+}
similarity index 93%
rename from drivers/gpu/host1x/drm/dc.c
rename to drivers/gpu/drm/tegra/dc.c
index b1a05ad..ae1cb31 100644 (file)
@@ -8,13 +8,9 @@
  */
 
 #include <linux/clk.h>
-#include <linux/debugfs.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
 #include <linux/clk/tegra.h>
+#include <linux/debugfs.h>
 
-#include "host1x_client.h"
 #include "dc.h"
 #include "drm.h"
 #include "gem.h"
@@ -51,6 +47,8 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
        window.dst.h = crtc_h;
        window.format = tegra_dc_format(fb->pixel_format);
        window.bits_per_pixel = fb->bits_per_pixel;
+       window.bottom_up = tegra_fb_is_bottom_up(fb);
+       window.tiled = tegra_fb_is_tiled(fb);
 
        for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
                struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
@@ -97,8 +95,11 @@ static int tegra_plane_disable(struct drm_plane *plane)
 
 static void tegra_plane_destroy(struct drm_plane *plane)
 {
+       struct tegra_plane *p = to_tegra_plane(plane);
+
        tegra_plane_disable(plane);
        drm_plane_cleanup(plane);
+       kfree(p);
 }
 
 static const struct drm_plane_funcs tegra_plane_funcs = {
@@ -124,7 +125,7 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
        for (i = 0; i < 2; i++) {
                struct tegra_plane *plane;
 
-               plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
+               plane = kzalloc(sizeof(*plane), GFP_KERNEL);
                if (!plane)
                        return -ENOMEM;
 
@@ -133,8 +134,10 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
                err = drm_plane_init(drm, &plane->base, 1 << dc->pipe,
                                     &tegra_plane_funcs, plane_formats,
                                     ARRAY_SIZE(plane_formats), false);
-               if (err < 0)
+               if (err < 0) {
+                       kfree(plane);
                        return err;
+               }
        }
 
        return 0;
@@ -145,6 +148,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
 {
        unsigned int format = tegra_dc_format(fb->pixel_format);
        struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
+       unsigned int h_offset = 0, v_offset = 0;
        unsigned long value;
 
        tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
@@ -156,6 +160,32 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
        tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
        tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH);
 
+       if (tegra_fb_is_tiled(fb)) {
+               value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
+                       DC_WIN_BUFFER_ADDR_MODE_TILE;
+       } else {
+               value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
+                       DC_WIN_BUFFER_ADDR_MODE_LINEAR;
+       }
+
+       tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+
+       /* make sure bottom-up buffers are properly displayed */
+       if (tegra_fb_is_bottom_up(fb)) {
+               value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
+               value |= INVERT_V;
+               tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
+
+               v_offset += fb->height - 1;
+       } else {
+               value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
+               value &= ~INVERT_V;
+               tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
+       }
+
+       tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
+       tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
+
        value = GENERAL_UPDATE | WIN_A_UPDATE;
        tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
 
@@ -255,14 +285,26 @@ static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        return 0;
 }
 
+static void drm_crtc_clear(struct drm_crtc *crtc)
+{
+       memset(crtc, 0, sizeof(*crtc));
+}
+
+static void tegra_dc_destroy(struct drm_crtc *crtc)
+{
+       drm_crtc_cleanup(crtc);
+       drm_crtc_clear(crtc);
+}
+
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
        .page_flip = tegra_dc_page_flip,
        .set_config = drm_crtc_helper_set_config,
-       .destroy = drm_crtc_cleanup,
+       .destroy = tegra_dc_destroy,
 };
 
 static void tegra_crtc_disable(struct drm_crtc *crtc)
 {
+       struct tegra_dc *dc = to_tegra_dc(crtc);
        struct drm_device *drm = crtc->dev;
        struct drm_plane *plane;
 
@@ -277,6 +319,8 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
                        }
                }
        }
+
+       drm_vblank_off(drm, dc->pipe);
 }
 
 static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -491,9 +535,22 @@ int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
                tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
        }
 
+       if (window->bottom_up)
+               v_offset += window->src.h - 1;
+
        tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
        tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
 
+       if (window->tiled) {
+               value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
+                       DC_WIN_BUFFER_ADDR_MODE_TILE;
+       } else {
+               value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
+                       DC_WIN_BUFFER_ADDR_MODE_LINEAR;
+       }
+
+       tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+
        value = WIN_ENABLE;
 
        if (yuv) {
@@ -512,6 +569,9 @@ int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
                value |= COLOR_EXPAND;
        }
 
+       if (window->bottom_up)
+               value |= INVERT_V;
+
        tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
 
        /*
@@ -1041,30 +1101,30 @@ static int tegra_dc_debugfs_exit(struct tegra_dc *dc)
        return 0;
 }
 
-static int tegra_dc_drm_init(struct host1x_client *client,
-                            struct drm_device *drm)
+static int tegra_dc_init(struct host1x_client *client)
 {
+       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
        struct tegra_dc *dc = host1x_client_to_dc(client);
        int err;
 
-       dc->pipe = drm->mode_config.num_crtc;
+       dc->pipe = tegra->drm->mode_config.num_crtc;
 
-       drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs);
+       drm_crtc_init(tegra->drm, &dc->base, &tegra_crtc_funcs);
        drm_mode_crtc_set_gamma_size(&dc->base, 256);
        drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
 
-       err = tegra_dc_rgb_init(drm, dc);
+       err = tegra_dc_rgb_init(tegra->drm, dc);
        if (err < 0 && err != -ENODEV) {
                dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
                return err;
        }
 
-       err = tegra_dc_add_planes(drm, dc);
+       err = tegra_dc_add_planes(tegra->drm, dc);
        if (err < 0)
                return err;
 
        if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-               err = tegra_dc_debugfs_init(dc, drm->primary);
+               err = tegra_dc_debugfs_init(dc, tegra->drm->primary);
                if (err < 0)
                        dev_err(dc->dev, "debugfs setup failed: %d\n", err);
        }
@@ -1080,7 +1140,7 @@ static int tegra_dc_drm_init(struct host1x_client *client,
        return 0;
 }
 
-static int tegra_dc_drm_exit(struct host1x_client *client)
+static int tegra_dc_exit(struct host1x_client *client)
 {
        struct tegra_dc *dc = host1x_client_to_dc(client);
        int err;
@@ -1103,13 +1163,12 @@ static int tegra_dc_drm_exit(struct host1x_client *client)
 }
 
 static const struct host1x_client_ops dc_client_ops = {
-       .drm_init = tegra_dc_drm_init,
-       .drm_exit = tegra_dc_drm_exit,
+       .init = tegra_dc_init,
+       .exit = tegra_dc_exit,
 };
 
 static int tegra_dc_probe(struct platform_device *pdev)
 {
-       struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
        struct resource *regs;
        struct tegra_dc *dc;
        int err;
@@ -1153,7 +1212,7 @@ static int tegra_dc_probe(struct platform_device *pdev)
                return err;
        }
 
-       err = host1x_register_client(host1x, &dc->client);
+       err = host1x_client_register(&dc->client);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to register host1x client: %d\n",
                        err);
@@ -1167,17 +1226,22 @@ static int tegra_dc_probe(struct platform_device *pdev)
 
 static int tegra_dc_remove(struct platform_device *pdev)
 {
-       struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
        struct tegra_dc *dc = platform_get_drvdata(pdev);
        int err;
 
-       err = host1x_unregister_client(host1x, &dc->client);
+       err = host1x_client_unregister(&dc->client);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
                        err);
                return err;
        }
 
+       err = tegra_dc_rgb_remove(dc);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err);
+               return err;
+       }
+
        clk_disable_unprepare(dc->clk);
 
        return 0;
similarity index 98%
rename from drivers/gpu/host1x/drm/dc.h
rename to drivers/gpu/drm/tegra/dc.h
index 79eaec9..91bbda2 100644 (file)
 #define DC_WIN_CSC_KVB                         0x618
 
 #define DC_WIN_WIN_OPTIONS                     0x700
+#define INVERT_V     (1 <<  2)
 #define COLOR_EXPAND (1 <<  6)
 #define CSC_ENABLE   (1 << 18)
 #define WIN_ENABLE   (1 << 30)
 #define DC_WIN_BUF_STRIDE                      0x70b
 #define DC_WIN_UV_BUF_STRIDE                   0x70c
 #define DC_WIN_BUFFER_ADDR_MODE                        0x70d
+#define DC_WIN_BUFFER_ADDR_MODE_LINEAR         (0 <<  0)
+#define DC_WIN_BUFFER_ADDR_MODE_TILE           (1 <<  0)
+#define DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV      (0 << 16)
+#define DC_WIN_BUFFER_ADDR_MODE_TILE_UV                (1 << 16)
 #define DC_WIN_DV_CONTROL                      0x70e
 
 #define DC_WIN_BLEND_NOKEY                     0x70f
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
new file mode 100644 (file)
index 0000000..28e1781
--- /dev/null
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ * Copyright (C) 2012-2013 NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/host1x.h>
+
+#include "drm.h"
+#include "gem.h"
+
+#define DRIVER_NAME "tegra"
+#define DRIVER_DESC "NVIDIA Tegra graphics"
+#define DRIVER_DATE "20120330"
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+
+struct tegra_drm_file {
+       struct list_head contexts;
+};
+
+static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
+{
+       struct host1x_device *device = to_host1x_device(drm->dev);
+       struct tegra_drm *tegra;
+       int err;
+
+       tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
+       if (!tegra)
+               return -ENOMEM;
+
+       dev_set_drvdata(drm->dev, tegra);
+       mutex_init(&tegra->clients_lock);
+       INIT_LIST_HEAD(&tegra->clients);
+       drm->dev_private = tegra;
+       tegra->drm = drm;
+
+       drm_mode_config_init(drm);
+
+       err = host1x_device_init(device);
+       if (err < 0)
+               return err;
+
+       /*
+        * We don't use the drm_irq_install() helpers provided by the DRM
+        * core, so we need to set this manually in order to allow the
+        * DRM_IOCTL_WAIT_VBLANK to operate correctly.
+        */
+       drm->irq_enabled = true;
+
+       err = drm_vblank_init(drm, drm->mode_config.num_crtc);
+       if (err < 0)
+               return err;
+
+       err = tegra_drm_fb_init(drm);
+       if (err < 0)
+               return err;
+
+       drm_kms_helper_poll_init(drm);
+
+       return 0;
+}
+
+static int tegra_drm_unload(struct drm_device *drm)
+{
+       struct host1x_device *device = to_host1x_device(drm->dev);
+       int err;
+
+       drm_kms_helper_poll_fini(drm);
+       tegra_drm_fb_exit(drm);
+       drm_vblank_cleanup(drm);
+       drm_mode_config_cleanup(drm);
+
+       err = host1x_device_exit(device);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
+{
+       struct tegra_drm_file *fpriv;
+
+       fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
+       if (!fpriv)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&fpriv->contexts);
+       filp->driver_priv = fpriv;
+
+       return 0;
+}
+
+static void tegra_drm_context_free(struct tegra_drm_context *context)
+{
+       context->client->ops->close_channel(context);
+       kfree(context);
+}
+
+static void tegra_drm_lastclose(struct drm_device *drm)
+{
+       struct tegra_drm *tegra = drm->dev_private;
+
+       tegra_fbdev_restore_mode(tegra->fbdev);
+}
+
+static struct host1x_bo *
+host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle)
+{
+       struct drm_gem_object *gem;
+       struct tegra_bo *bo;
+
+       gem = drm_gem_object_lookup(drm, file, handle);
+       if (!gem)
+               return NULL;
+
+       mutex_lock(&drm->struct_mutex);
+       drm_gem_object_unreference(gem);
+       mutex_unlock(&drm->struct_mutex);
+
+       bo = to_tegra_bo(gem);
+       return &bo->base;
+}
+
+int tegra_drm_submit(struct tegra_drm_context *context,
+                    struct drm_tegra_submit *args, struct drm_device *drm,
+                    struct drm_file *file)
+{
+       unsigned int num_cmdbufs = args->num_cmdbufs;
+       unsigned int num_relocs = args->num_relocs;
+       unsigned int num_waitchks = args->num_waitchks;
+       struct drm_tegra_cmdbuf __user *cmdbufs =
+               (void * __user)(uintptr_t)args->cmdbufs;
+       struct drm_tegra_reloc __user *relocs =
+               (void * __user)(uintptr_t)args->relocs;
+       struct drm_tegra_waitchk __user *waitchks =
+               (void * __user)(uintptr_t)args->waitchks;
+       struct drm_tegra_syncpt syncpt;
+       struct host1x_job *job;
+       int err;
+
+       /* We don't yet support other than one syncpt_incr struct per submit */
+       if (args->num_syncpts != 1)
+               return -EINVAL;
+
+       job = host1x_job_alloc(context->channel, args->num_cmdbufs,
+                              args->num_relocs, args->num_waitchks);
+       if (!job)
+               return -ENOMEM;
+
+       job->num_relocs = args->num_relocs;
+       job->num_waitchk = args->num_waitchks;
+       job->client = (u32)args->context;
+       job->class = context->client->base.class;
+       job->serialize = true;
+
+       while (num_cmdbufs) {
+               struct drm_tegra_cmdbuf cmdbuf;
+               struct host1x_bo *bo;
+
+               err = copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf));
+               if (err)
+                       goto fail;
+
+               bo = host1x_bo_lookup(drm, file, cmdbuf.handle);
+               if (!bo) {
+                       err = -ENOENT;
+                       goto fail;
+               }
+
+               host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
+               num_cmdbufs--;
+               cmdbufs++;
+       }
+
+       err = copy_from_user(job->relocarray, relocs,
+                            sizeof(*relocs) * num_relocs);
+       if (err)
+               goto fail;
+
+       while (num_relocs--) {
+               struct host1x_reloc *reloc = &job->relocarray[num_relocs];
+               struct host1x_bo *cmdbuf, *target;
+
+               cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf);
+               target = host1x_bo_lookup(drm, file, (u32)reloc->target);
+
+               reloc->cmdbuf = cmdbuf;
+               reloc->target = target;
+
+               if (!reloc->target || !reloc->cmdbuf) {
+                       err = -ENOENT;
+                       goto fail;
+               }
+       }
+
+       err = copy_from_user(job->waitchk, waitchks,
+                            sizeof(*waitchks) * num_waitchks);
+       if (err)
+               goto fail;
+
+       err = copy_from_user(&syncpt, (void * __user)(uintptr_t)args->syncpts,
+                            sizeof(syncpt));
+       if (err)
+               goto fail;
+
+       job->is_addr_reg = context->client->ops->is_addr_reg;
+       job->syncpt_incrs = syncpt.incrs;
+       job->syncpt_id = syncpt.id;
+       job->timeout = 10000;
+
+       if (args->timeout && args->timeout < 10000)
+               job->timeout = args->timeout;
+
+       err = host1x_job_pin(job, context->client->base.dev);
+       if (err)
+               goto fail;
+
+       err = host1x_job_submit(job);
+       if (err)
+               goto fail_submit;
+
+       args->fence = job->syncpt_end;
+
+       host1x_job_put(job);
+       return 0;
+
+fail_submit:
+       host1x_job_unpin(job);
+fail:
+       host1x_job_put(job);
+       return err;
+}
+
+
+#ifdef CONFIG_DRM_TEGRA_STAGING
+static struct tegra_drm_context *tegra_drm_get_context(__u64 context)
+{
+       return (struct tegra_drm_context *)(uintptr_t)context;
+}
+
+static bool tegra_drm_file_owns_context(struct tegra_drm_file *file,
+                                       struct tegra_drm_context *context)
+{
+       struct tegra_drm_context *ctx;
+
+       list_for_each_entry(ctx, &file->contexts, list)
+               if (ctx == context)
+                       return true;
+
+       return false;
+}
+
+static int tegra_gem_create(struct drm_device *drm, void *data,
+                           struct drm_file *file)
+{
+       struct drm_tegra_gem_create *args = data;
+       struct tegra_bo *bo;
+
+       bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags,
+                                        &args->handle);
+       if (IS_ERR(bo))
+               return PTR_ERR(bo);
+
+       return 0;
+}
+
+static int tegra_gem_mmap(struct drm_device *drm, void *data,
+                         struct drm_file *file)
+{
+       struct drm_tegra_gem_mmap *args = data;
+       struct drm_gem_object *gem;
+       struct tegra_bo *bo;
+
+       gem = drm_gem_object_lookup(drm, file, args->handle);
+       if (!gem)
+               return -EINVAL;
+
+       bo = to_tegra_bo(gem);
+
+       args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node);
+
+       drm_gem_object_unreference(gem);
+
+       return 0;
+}
+
+static int tegra_syncpt_read(struct drm_device *drm, void *data,
+                            struct drm_file *file)
+{
+       struct host1x *host = dev_get_drvdata(drm->dev->parent);
+       struct drm_tegra_syncpt_read *args = data;
+       struct host1x_syncpt *sp;
+
+       sp = host1x_syncpt_get(host, args->id);
+       if (!sp)
+               return -EINVAL;
+
+       args->value = host1x_syncpt_read_min(sp);
+       return 0;
+}
+
+static int tegra_syncpt_incr(struct drm_device *drm, void *data,
+                            struct drm_file *file)
+{
+       struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
+       struct drm_tegra_syncpt_incr *args = data;
+       struct host1x_syncpt *sp;
+
+       sp = host1x_syncpt_get(host1x, args->id);
+       if (!sp)
+               return -EINVAL;
+
+       return host1x_syncpt_incr(sp);
+}
+
+static int tegra_syncpt_wait(struct drm_device *drm, void *data,
+                            struct drm_file *file)
+{
+       struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
+       struct drm_tegra_syncpt_wait *args = data;
+       struct host1x_syncpt *sp;
+
+       sp = host1x_syncpt_get(host1x, args->id);
+       if (!sp)
+               return -EINVAL;
+
+       return host1x_syncpt_wait(sp, args->thresh, args->timeout,
+                                 &args->value);
+}
+
+static int tegra_open_channel(struct drm_device *drm, void *data,
+                             struct drm_file *file)
+{
+       struct tegra_drm_file *fpriv = file->driver_priv;
+       struct tegra_drm *tegra = drm->dev_private;
+       struct drm_tegra_open_channel *args = data;
+       struct tegra_drm_context *context;
+       struct tegra_drm_client *client;
+       int err = -ENODEV;
+
+       context = kzalloc(sizeof(*context), GFP_KERNEL);
+       if (!context)
+               return -ENOMEM;
+
+       list_for_each_entry(client, &tegra->clients, list)
+               if (client->base.class == args->client) {
+                       err = client->ops->open_channel(client, context);
+                       if (err)
+                               break;
+
+                       list_add(&context->list, &fpriv->contexts);
+                       args->context = (uintptr_t)context;
+                       context->client = client;
+                       return 0;
+               }
+
+       kfree(context);
+       return err;
+}
+
+static int tegra_close_channel(struct drm_device *drm, void *data,
+                              struct drm_file *file)
+{
+       struct tegra_drm_file *fpriv = file->driver_priv;
+       struct drm_tegra_close_channel *args = data;
+       struct tegra_drm_context *context;
+
+       context = tegra_drm_get_context(args->context);
+
+       if (!tegra_drm_file_owns_context(fpriv, context))
+               return -EINVAL;
+
+       list_del(&context->list);
+       tegra_drm_context_free(context);
+
+       return 0;
+}
+
+static int tegra_get_syncpt(struct drm_device *drm, void *data,
+                           struct drm_file *file)
+{
+       struct tegra_drm_file *fpriv = file->driver_priv;
+       struct drm_tegra_get_syncpt *args = data;
+       struct tegra_drm_context *context;
+       struct host1x_syncpt *syncpt;
+
+       context = tegra_drm_get_context(args->context);
+
+       if (!tegra_drm_file_owns_context(fpriv, context))
+               return -ENODEV;
+
+       if (args->index >= context->client->base.num_syncpts)
+               return -EINVAL;
+
+       syncpt = context->client->base.syncpts[args->index];
+       args->id = host1x_syncpt_id(syncpt);
+
+       return 0;
+}
+
+static int tegra_submit(struct drm_device *drm, void *data,
+                       struct drm_file *file)
+{
+       struct tegra_drm_file *fpriv = file->driver_priv;
+       struct drm_tegra_submit *args = data;
+       struct tegra_drm_context *context;
+
+       context = tegra_drm_get_context(args->context);
+
+       if (!tegra_drm_file_owns_context(fpriv, context))
+               return -ENODEV;
+
+       return context->client->ops->submit(context, args, drm, file);
+}
+
+static int tegra_get_syncpt_base(struct drm_device *drm, void *data,
+                                struct drm_file *file)
+{
+       struct tegra_drm_file *fpriv = file->driver_priv;
+       struct drm_tegra_get_syncpt_base *args = data;
+       struct tegra_drm_context *context;
+       struct host1x_syncpt_base *base;
+       struct host1x_syncpt *syncpt;
+
+       context = tegra_drm_get_context(args->context);
+
+       if (!tegra_drm_file_owns_context(fpriv, context))
+               return -ENODEV;
+
+       if (args->syncpt >= context->client->base.num_syncpts)
+               return -EINVAL;
+
+       syncpt = context->client->base.syncpts[args->syncpt];
+
+       base = host1x_syncpt_get_base(syncpt);
+       if (!base)
+               return -ENXIO;
+
+       args->id = host1x_syncpt_base_id(base);
+
+       return 0;
+}
+#endif
+
+static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
+#ifdef CONFIG_DRM_TEGRA_STAGING
+       DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_UNLOCKED | DRM_AUTH),
+       DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, DRM_UNLOCKED),
+#endif
+};
+
+static const struct file_operations tegra_drm_fops = {
+       .owner = THIS_MODULE,
+       .open = drm_open,
+       .release = drm_release,
+       .unlocked_ioctl = drm_ioctl,
+       .mmap = tegra_drm_mmap,
+       .poll = drm_poll,
+       .read = drm_read,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = drm_compat_ioctl,
+#endif
+       .llseek = noop_llseek,
+};
+
+static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe)
+{
+       struct drm_crtc *crtc;
+
+       list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
+               struct tegra_dc *dc = to_tegra_dc(crtc);
+
+               if (dc->pipe == pipe)
+                       return crtc;
+       }
+
+       return NULL;
+}
+
+static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc)
+{
+       /* TODO: implement real hardware counter using syncpoints */
+       return drm_vblank_count(dev, crtc);
+}
+
+static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
+{
+       struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
+       struct tegra_dc *dc = to_tegra_dc(crtc);
+
+       if (!crtc)
+               return -ENODEV;
+
+       tegra_dc_enable_vblank(dc);
+
+       return 0;
+}
+
+static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe)
+{
+       struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
+       struct tegra_dc *dc = to_tegra_dc(crtc);
+
+       if (crtc)
+               tegra_dc_disable_vblank(dc);
+}
+
+static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
+{
+       struct tegra_drm_file *fpriv = file->driver_priv;
+       struct tegra_drm_context *context, *tmp;
+       struct drm_crtc *crtc;
+
+       list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
+               tegra_dc_cancel_page_flip(crtc, file);
+
+       list_for_each_entry_safe(context, tmp, &fpriv->contexts, list)
+               tegra_drm_context_free(context);
+
+       kfree(fpriv);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
+{
+       struct drm_info_node *node = (struct drm_info_node *)s->private;
+       struct drm_device *drm = node->minor->dev;
+       struct drm_framebuffer *fb;
+
+       mutex_lock(&drm->mode_config.fb_lock);
+
+       list_for_each_entry(fb, &drm->mode_config.fb_list, head) {
+               seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n",
+                          fb->base.id, fb->width, fb->height, fb->depth,
+                          fb->bits_per_pixel,
+                          atomic_read(&fb->refcount.refcount));
+       }
+
+       mutex_unlock(&drm->mode_config.fb_lock);
+
+       return 0;
+}
+
+static struct drm_info_list tegra_debugfs_list[] = {
+       { "framebuffers", tegra_debugfs_framebuffers, 0 },
+};
+
+static int tegra_debugfs_init(struct drm_minor *minor)
+{
+       return drm_debugfs_create_files(tegra_debugfs_list,
+                                       ARRAY_SIZE(tegra_debugfs_list),
+                                       minor->debugfs_root, minor);
+}
+
+static void tegra_debugfs_cleanup(struct drm_minor *minor)
+{
+       drm_debugfs_remove_files(tegra_debugfs_list,
+                                ARRAY_SIZE(tegra_debugfs_list), minor);
+}
+#endif
+
+struct drm_driver tegra_drm_driver = {
+       .driver_features = DRIVER_MODESET | DRIVER_GEM,
+       .load = tegra_drm_load,
+       .unload = tegra_drm_unload,
+       .open = tegra_drm_open,
+       .preclose = tegra_drm_preclose,
+       .lastclose = tegra_drm_lastclose,
+
+       .get_vblank_counter = tegra_drm_get_vblank_counter,
+       .enable_vblank = tegra_drm_enable_vblank,
+       .disable_vblank = tegra_drm_disable_vblank,
+
+#if defined(CONFIG_DEBUG_FS)
+       .debugfs_init = tegra_debugfs_init,
+       .debugfs_cleanup = tegra_debugfs_cleanup,
+#endif
+
+       .gem_free_object = tegra_bo_free_object,
+       .gem_vm_ops = &tegra_bo_vm_ops,
+       .dumb_create = tegra_bo_dumb_create,
+       .dumb_map_offset = tegra_bo_dumb_map_offset,
+       .dumb_destroy = drm_gem_dumb_destroy,
+
+       .ioctls = tegra_drm_ioctls,
+       .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
+       .fops = &tegra_drm_fops,
+
+       .name = DRIVER_NAME,
+       .desc = DRIVER_DESC,
+       .date = DRIVER_DATE,
+       .major = DRIVER_MAJOR,
+       .minor = DRIVER_MINOR,
+       .patchlevel = DRIVER_PATCHLEVEL,
+};
+
+int tegra_drm_register_client(struct tegra_drm *tegra,
+                             struct tegra_drm_client *client)
+{
+       mutex_lock(&tegra->clients_lock);
+       list_add_tail(&client->list, &tegra->clients);
+       mutex_unlock(&tegra->clients_lock);
+
+       return 0;
+}
+
+int tegra_drm_unregister_client(struct tegra_drm *tegra,
+                               struct tegra_drm_client *client)
+{
+       mutex_lock(&tegra->clients_lock);
+       list_del_init(&client->list);
+       mutex_unlock(&tegra->clients_lock);
+
+       return 0;
+}
+
+static int host1x_drm_probe(struct host1x_device *device)
+{
+       return drm_host1x_init(&tegra_drm_driver, device);
+}
+
+static int host1x_drm_remove(struct host1x_device *device)
+{
+       drm_host1x_exit(&tegra_drm_driver, device);
+
+       return 0;
+}
+
+static const struct of_device_id host1x_drm_subdevs[] = {
+       { .compatible = "nvidia,tegra20-dc", },
+       { .compatible = "nvidia,tegra20-hdmi", },
+       { .compatible = "nvidia,tegra20-gr2d", },
+       { .compatible = "nvidia,tegra20-gr3d", },
+       { .compatible = "nvidia,tegra30-dc", },
+       { .compatible = "nvidia,tegra30-hdmi", },
+       { .compatible = "nvidia,tegra30-gr2d", },
+       { .compatible = "nvidia,tegra30-gr3d", },
+       { .compatible = "nvidia,tegra114-hdmi", },
+       { .compatible = "nvidia,tegra114-gr3d", },
+       { /* sentinel */ }
+};
+
+static struct host1x_driver host1x_drm_driver = {
+       .name = "drm",
+       .probe = host1x_drm_probe,
+       .remove = host1x_drm_remove,
+       .subdevs = host1x_drm_subdevs,
+};
+
+static int __init host1x_drm_init(void)
+{
+       int err;
+
+       err = host1x_driver_register(&host1x_drm_driver);
+       if (err < 0)
+               return err;
+
+       err = platform_driver_register(&tegra_dc_driver);
+       if (err < 0)
+               goto unregister_host1x;
+
+       err = platform_driver_register(&tegra_hdmi_driver);
+       if (err < 0)
+               goto unregister_dc;
+
+       err = platform_driver_register(&tegra_gr2d_driver);
+       if (err < 0)
+               goto unregister_hdmi;
+
+       err = platform_driver_register(&tegra_gr3d_driver);
+       if (err < 0)
+               goto unregister_gr2d;
+
+       return 0;
+
+unregister_gr2d:
+       platform_driver_unregister(&tegra_gr2d_driver);
+unregister_hdmi:
+       platform_driver_unregister(&tegra_hdmi_driver);
+unregister_dc:
+       platform_driver_unregister(&tegra_dc_driver);
+unregister_host1x:
+       host1x_driver_unregister(&host1x_drm_driver);
+       return err;
+}
+module_init(host1x_drm_init);
+
+static void __exit host1x_drm_exit(void)
+{
+       platform_driver_unregister(&tegra_gr3d_driver);
+       platform_driver_unregister(&tegra_gr2d_driver);
+       platform_driver_unregister(&tegra_hdmi_driver);
+       platform_driver_unregister(&tegra_dc_driver);
+       host1x_driver_unregister(&host1x_drm_driver);
+}
+module_exit(host1x_drm_exit);
+
+MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
+MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
+MODULE_LICENSE("GPL v2");
similarity index 72%
rename from drivers/gpu/host1x/drm/drm.h
rename to drivers/gpu/drm/tegra/drm.h
index 02ce020..fdfe259 100644 (file)
 #ifndef HOST1X_DRM_H
 #define HOST1X_DRM_H 1
 
+#include <uapi/drm/tegra_drm.h>
+#include <linux/host1x.h>
+
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fixed.h>
-#include <uapi/drm/tegra_drm.h>
-
-#include "host1x.h"
 
 struct tegra_fb {
        struct drm_framebuffer base;
@@ -30,17 +30,8 @@ struct tegra_fbdev {
        struct tegra_fb *fb;
 };
 
-struct host1x_drm {
+struct tegra_drm {
        struct drm_device *drm;
-       struct device *dev;
-       void __iomem *regs;
-       struct clk *clk;
-       int syncpt;
-       int irq;
-
-       struct mutex drm_clients_lock;
-       struct list_head drm_clients;
-       struct list_head drm_active;
 
        struct mutex clients_lock;
        struct list_head clients;
@@ -48,66 +39,60 @@ struct host1x_drm {
        struct tegra_fbdev *fbdev;
 };
 
-struct host1x_client;
+struct tegra_drm_client;
 
-struct host1x_drm_context {
-       struct host1x_client *client;
+struct tegra_drm_context {
+       struct tegra_drm_client *client;
        struct host1x_channel *channel;
        struct list_head list;
 };
 
-struct host1x_client_ops {
-       int (*drm_init)(struct host1x_client *client, struct drm_device *drm);
-       int (*drm_exit)(struct host1x_client *client);
-       int (*open_channel)(struct host1x_client *client,
-                           struct host1x_drm_context *context);
-       void (*close_channel)(struct host1x_drm_context *context);
-       int (*submit)(struct host1x_drm_context *context,
+struct tegra_drm_client_ops {
+       int (*open_channel)(struct tegra_drm_client *client,
+                           struct tegra_drm_context *context);
+       void (*close_channel)(struct tegra_drm_context *context);
+       int (*is_addr_reg)(struct device *dev, u32 class, u32 offset);
+       int (*submit)(struct tegra_drm_context *context,
                      struct drm_tegra_submit *args, struct drm_device *drm,
                      struct drm_file *file);
 };
 
-struct host1x_drm_file {
-       struct list_head contexts;
-};
-
-struct host1x_client {
-       struct host1x_drm *host1x;
-       struct device *dev;
-
-       const struct host1x_client_ops *ops;
-
-       enum host1x_class class;
-       struct host1x_channel *channel;
-
-       struct host1x_syncpt **syncpts;
-       unsigned int num_syncpts;
+int tegra_drm_submit(struct tegra_drm_context *context,
+                    struct drm_tegra_submit *args, struct drm_device *drm,
+                    struct drm_file *file);
 
+struct tegra_drm_client {
+       struct host1x_client base;
        struct list_head list;
+
+       const struct tegra_drm_client_ops *ops;
 };
 
-extern int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm);
-extern int host1x_drm_exit(struct host1x_drm *host1x);
+static inline struct tegra_drm_client *
+host1x_to_drm_client(struct host1x_client *client)
+{
+       return container_of(client, struct tegra_drm_client, base);
+}
+
+extern int tegra_drm_register_client(struct tegra_drm *tegra,
+                                    struct tegra_drm_client *client);
+extern int tegra_drm_unregister_client(struct tegra_drm *tegra,
+                                      struct tegra_drm_client *client);
 
-extern int host1x_register_client(struct host1x_drm *host1x,
-                                 struct host1x_client *client);
-extern int host1x_unregister_client(struct host1x_drm *host1x,
-                                   struct host1x_client *client);
+extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
+extern int tegra_drm_exit(struct tegra_drm *tegra);
 
 struct tegra_output;
 
 struct tegra_dc {
        struct host1x_client client;
-       spinlock_t lock;
-
-       struct host1x_drm *host1x;
        struct device *dev;
+       spinlock_t lock;
 
        struct drm_crtc base;
        int pipe;
 
        struct clk *clk;
-
        void __iomem *regs;
        int irq;
 
@@ -123,7 +108,8 @@ struct tegra_dc {
        struct drm_pending_vblank_event *event;
 };
 
-static inline struct tegra_dc *host1x_client_to_dc(struct host1x_client *client)
+static inline struct tegra_dc *
+host1x_client_to_dc(struct host1x_client *client)
 {
        return container_of(client, struct tegra_dc, client);
 }
@@ -162,6 +148,8 @@ struct tegra_dc_window {
        unsigned int format;
        unsigned int stride[2];
        unsigned long base[3];
+       bool bottom_up;
+       bool tiled;
 };
 
 /* from dc.c */
@@ -249,23 +237,34 @@ static inline int tegra_output_check_mode(struct tegra_output *output,
        return output ? -ENOSYS : -EINVAL;
 }
 
+/* from bus.c */
+int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device);
+void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device);
+
 /* from rgb.c */
 extern int tegra_dc_rgb_probe(struct tegra_dc *dc);
+extern int tegra_dc_rgb_remove(struct tegra_dc *dc);
 extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
 extern int tegra_dc_rgb_exit(struct tegra_dc *dc);
 
 /* from output.c */
-extern int tegra_output_parse_dt(struct tegra_output *output);
+extern int tegra_output_probe(struct tegra_output *output);
+extern int tegra_output_remove(struct tegra_output *output);
 extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
 extern int tegra_output_exit(struct tegra_output *output);
 
 /* from fb.c */
 struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
                                    unsigned int index);
+bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
+bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer);
 extern int tegra_drm_fb_init(struct drm_device *drm);
 extern void tegra_drm_fb_exit(struct drm_device *drm);
 extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
 
-extern struct drm_driver tegra_drm_driver;
+extern struct platform_driver tegra_dc_driver;
+extern struct platform_driver tegra_hdmi_driver;
+extern struct platform_driver tegra_gr2d_driver;
+extern struct platform_driver tegra_gr3d_driver;
 
 #endif /* HOST1X_DRM_H */
similarity index 92%
rename from drivers/gpu/host1x/drm/fb.c
rename to drivers/gpu/drm/tegra/fb.c
index 979a3e3..490f771 100644 (file)
@@ -10,8 +10,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
-
 #include "drm.h"
 #include "gem.h"
 
@@ -36,6 +34,26 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
        return fb->planes[index];
 }
 
+bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
+{
+       struct tegra_fb *fb = to_tegra_fb(framebuffer);
+
+       if (fb->planes[0]->flags & TEGRA_BO_BOTTOM_UP)
+               return true;
+
+       return false;
+}
+
+bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer)
+{
+       struct tegra_fb *fb = to_tegra_fb(framebuffer);
+
+       if (fb->planes[0]->flags & TEGRA_BO_TILED)
+               return true;
+
+       return false;
+}
+
 static void tegra_fb_destroy(struct drm_framebuffer *framebuffer)
 {
        struct tegra_fb *fb = to_tegra_fb(framebuffer);
@@ -190,7 +208,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
 
        size = cmd.pitches[0] * cmd.height;
 
-       bo = tegra_bo_create(drm, size);
+       bo = tegra_bo_create(drm, size, 0);
        if (IS_ERR(bo))
                return PTR_ERR(bo);
 
@@ -323,10 +341,10 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
 
 static void tegra_fb_output_poll_changed(struct drm_device *drm)
 {
-       struct host1x_drm *host1x = drm->dev_private;
+       struct tegra_drm *tegra = drm->dev_private;
 
-       if (host1x->fbdev)
-               drm_fb_helper_hotplug_event(&host1x->fbdev->base);
+       if (tegra->fbdev)
+               drm_fb_helper_hotplug_event(&tegra->fbdev->base);
 }
 
 static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
@@ -336,7 +354,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
 
 int tegra_drm_fb_init(struct drm_device *drm)
 {
-       struct host1x_drm *host1x = drm->dev_private;
+       struct tegra_drm *tegra = drm->dev_private;
        struct tegra_fbdev *fbdev;
 
        drm->mode_config.min_width = 0;
@@ -352,16 +370,16 @@ int tegra_drm_fb_init(struct drm_device *drm)
        if (IS_ERR(fbdev))
                return PTR_ERR(fbdev);
 
-       host1x->fbdev = fbdev;
+       tegra->fbdev = fbdev;
 
        return 0;
 }
 
 void tegra_drm_fb_exit(struct drm_device *drm)
 {
-       struct host1x_drm *host1x = drm->dev_private;
+       struct tegra_drm *tegra = drm->dev_private;
 
-       tegra_fbdev_free(host1x->fbdev);
+       tegra_fbdev_free(tegra->fbdev);
 }
 
 void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
similarity index 86%
rename from drivers/gpu/host1x/drm/gem.c
rename to drivers/gpu/drm/tegra/gem.c
index 59623de..28a9cbc 100644 (file)
  * GNU General Public License for more details.
  */
 
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/export.h>
-#include <linux/dma-mapping.h>
-
-#include <drm/drmP.h>
-#include <drm/drm.h>
+#include <drm/tegra_drm.h>
 
 #include "gem.h"
 
-static inline struct tegra_bo *host1x_to_drm_bo(struct host1x_bo *bo)
+static inline struct tegra_bo *host1x_to_tegra_bo(struct host1x_bo *bo)
 {
        return container_of(bo, struct tegra_bo, base);
 }
 
 static void tegra_bo_put(struct host1x_bo *bo)
 {
-       struct tegra_bo *obj = host1x_to_drm_bo(bo);
+       struct tegra_bo *obj = host1x_to_tegra_bo(bo);
        struct drm_device *drm = obj->gem.dev;
 
        mutex_lock(&drm->struct_mutex);
@@ -46,7 +39,7 @@ static void tegra_bo_put(struct host1x_bo *bo)
 
 static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt)
 {
-       struct tegra_bo *obj = host1x_to_drm_bo(bo);
+       struct tegra_bo *obj = host1x_to_tegra_bo(bo);
 
        return obj->paddr;
 }
@@ -57,7 +50,7 @@ static void tegra_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
 
 static void *tegra_bo_mmap(struct host1x_bo *bo)
 {
-       struct tegra_bo *obj = host1x_to_drm_bo(bo);
+       struct tegra_bo *obj = host1x_to_tegra_bo(bo);
 
        return obj->vaddr;
 }
@@ -68,7 +61,7 @@ static void tegra_bo_munmap(struct host1x_bo *bo, void *addr)
 
 static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page)
 {
-       struct tegra_bo *obj = host1x_to_drm_bo(bo);
+       struct tegra_bo *obj = host1x_to_tegra_bo(bo);
 
        return obj->vaddr + page * PAGE_SIZE;
 }
@@ -80,7 +73,7 @@ static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page,
 
 static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo)
 {
-       struct tegra_bo *obj = host1x_to_drm_bo(bo);
+       struct tegra_bo *obj = host1x_to_tegra_bo(bo);
        struct drm_device *drm = obj->gem.dev;
 
        mutex_lock(&drm->struct_mutex);
@@ -106,7 +99,8 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo)
        dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr, bo->paddr);
 }
 
-struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
+struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size,
+                                unsigned long flags)
 {
        struct tegra_bo *bo;
        int err;
@@ -135,6 +129,12 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
        if (err)
                goto err_mmap;
 
+       if (flags & DRM_TEGRA_GEM_CREATE_TILED)
+               bo->flags |= TEGRA_BO_TILED;
+
+       if (flags & DRM_TEGRA_GEM_CREATE_BOTTOM_UP)
+               bo->flags |= TEGRA_BO_BOTTOM_UP;
+
        return bo;
 
 err_mmap:
@@ -149,14 +149,15 @@ err_dma:
 }
 
 struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
-                                           struct drm_device *drm,
-                                           unsigned int size,
-                                           unsigned int *handle)
+                                            struct drm_device *drm,
+                                            unsigned int size,
+                                            unsigned long flags,
+                                            unsigned int *handle)
 {
        struct tegra_bo *bo;
        int ret;
 
-       bo = tegra_bo_create(drm, size);
+       bo = tegra_bo_create(drm, size, flags);
        if (IS_ERR(bo))
                return bo;
 
@@ -178,7 +179,6 @@ void tegra_bo_free_object(struct drm_gem_object *gem)
        struct tegra_bo *bo = to_tegra_bo(gem);
 
        drm_gem_free_mmap_offset(gem);
-
        drm_gem_object_release(gem);
        tegra_bo_destroy(gem->dev, bo);
 
@@ -197,8 +197,8 @@ int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
        if (args->size < args->pitch * args->height)
                args->size = args->pitch * args->height;
 
-       bo = tegra_bo_create_with_handle(file, drm, args->size,
-                                           &args->handle);
+       bo = tegra_bo_create_with_handle(file, drm, args->size, 0,
+                                        &args->handle);
        if (IS_ERR(bo))
                return PTR_ERR(bo);
 
similarity index 84%
rename from drivers/gpu/host1x/drm/gem.h
rename to drivers/gpu/drm/tegra/gem.h
index 492533a..7674000 100644 (file)
 #ifndef __HOST1X_GEM_H
 #define __HOST1X_GEM_H
 
+#include <linux/host1x.h>
+
 #include <drm/drm.h>
 #include <drm/drmP.h>
 
-#include "host1x_bo.h"
+#define TEGRA_BO_TILED     (1 << 0)
+#define TEGRA_BO_BOTTOM_UP (1 << 1)
 
 struct tegra_bo {
        struct drm_gem_object gem;
        struct host1x_bo base;
+       unsigned long flags;
        dma_addr_t paddr;
        void *vaddr;
 };
@@ -38,11 +42,13 @@ static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem)
 
 extern const struct host1x_bo_ops tegra_bo_ops;
 
-struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size);
+struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size,
+                                unsigned long flags);
 struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
-                                           struct drm_device *drm,
-                                           unsigned int size,
-                                           unsigned int *handle);
+                                            struct drm_device *drm,
+                                            unsigned int size,
+                                            unsigned long flags,
+                                            unsigned int *handle);
 void tegra_bo_free_object(struct drm_gem_object *gem);
 int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
                         struct drm_mode_create_dumb *args);
diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c
new file mode 100644 (file)
index 0000000..7ec4259
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2012-2013, NVIDIA Corporation.
+ *
+ * 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/clk.h>
+
+#include "drm.h"
+#include "gem.h"
+#include "gr2d.h"
+
+struct gr2d {
+       struct tegra_drm_client client;
+       struct host1x_channel *channel;
+       struct clk *clk;
+
+       DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS);
+};
+
+static inline struct gr2d *to_gr2d(struct tegra_drm_client *client)
+{
+       return container_of(client, struct gr2d, client);
+}
+
+static int gr2d_init(struct host1x_client *client)
+{
+       struct tegra_drm_client *drm = host1x_to_drm_client(client);
+       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
+       struct gr2d *gr2d = to_gr2d(drm);
+
+       gr2d->channel = host1x_channel_request(client->dev);
+       if (!gr2d->channel)
+               return -ENOMEM;
+
+       client->syncpts[0] = host1x_syncpt_request(client->dev, flags);
+       if (!client->syncpts[0]) {
+               host1x_channel_free(gr2d->channel);
+               return -ENOMEM;
+       }
+
+       return tegra_drm_register_client(tegra, drm);
+}
+
+static int gr2d_exit(struct host1x_client *client)
+{
+       struct tegra_drm_client *drm = host1x_to_drm_client(client);
+       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct gr2d *gr2d = to_gr2d(drm);
+       int err;
+
+       err = tegra_drm_unregister_client(tegra, drm);
+       if (err < 0)
+               return err;
+
+       host1x_syncpt_free(client->syncpts[0]);
+       host1x_channel_free(gr2d->channel);
+
+       return 0;
+}
+
+static const struct host1x_client_ops gr2d_client_ops = {
+       .init = gr2d_init,
+       .exit = gr2d_exit,
+};
+
+static int gr2d_open_channel(struct tegra_drm_client *client,
+                            struct tegra_drm_context *context)
+{
+       struct gr2d *gr2d = to_gr2d(client);
+
+       context->channel = host1x_channel_get(gr2d->channel);
+       if (!context->channel)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void gr2d_close_channel(struct tegra_drm_context *context)
+{
+       host1x_channel_put(context->channel);
+}
+
+static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 offset)
+{
+       struct gr2d *gr2d = dev_get_drvdata(dev);
+
+       switch (class) {
+       case HOST1X_CLASS_HOST1X:
+               if (offset == 0x2b)
+                       return 1;
+
+               break;
+
+       case HOST1X_CLASS_GR2D:
+       case HOST1X_CLASS_GR2D_SB:
+               if (offset >= GR2D_NUM_REGS)
+                       break;
+
+               if (test_bit(offset, gr2d->addr_regs))
+                       return 1;
+
+               break;
+       }
+
+       return 0;
+}
+
+static const struct tegra_drm_client_ops gr2d_ops = {
+       .open_channel = gr2d_open_channel,
+       .close_channel = gr2d_close_channel,
+       .is_addr_reg = gr2d_is_addr_reg,
+       .submit = tegra_drm_submit,
+};
+
+static const struct of_device_id gr2d_match[] = {
+       { .compatible = "nvidia,tegra30-gr2d" },
+       { .compatible = "nvidia,tegra20-gr2d" },
+       { },
+};
+
+static const u32 gr2d_addr_regs[] = {
+       GR2D_UA_BASE_ADDR,
+       GR2D_VA_BASE_ADDR,
+       GR2D_PAT_BASE_ADDR,
+       GR2D_DSTA_BASE_ADDR,
+       GR2D_DSTB_BASE_ADDR,
+       GR2D_DSTC_BASE_ADDR,
+       GR2D_SRCA_BASE_ADDR,
+       GR2D_SRCB_BASE_ADDR,
+       GR2D_SRC_BASE_ADDR_SB,
+       GR2D_DSTA_BASE_ADDR_SB,
+       GR2D_DSTB_BASE_ADDR_SB,
+       GR2D_UA_BASE_ADDR_SB,
+       GR2D_VA_BASE_ADDR_SB,
+};
+
+static int gr2d_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct host1x_syncpt **syncpts;
+       struct gr2d *gr2d;
+       unsigned int i;
+       int err;
+
+       gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL);
+       if (!gr2d)
+               return -ENOMEM;
+
+       syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
+       if (!syncpts)
+               return -ENOMEM;
+
+       gr2d->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(gr2d->clk)) {
+               dev_err(dev, "cannot get clock\n");
+               return PTR_ERR(gr2d->clk);
+       }
+
+       err = clk_prepare_enable(gr2d->clk);
+       if (err) {
+               dev_err(dev, "cannot turn on clock\n");
+               return err;
+       }
+
+       INIT_LIST_HEAD(&gr2d->client.base.list);
+       gr2d->client.base.ops = &gr2d_client_ops;
+       gr2d->client.base.dev = dev;
+       gr2d->client.base.class = HOST1X_CLASS_GR2D;
+       gr2d->client.base.syncpts = syncpts;
+       gr2d->client.base.num_syncpts = 1;
+
+       INIT_LIST_HEAD(&gr2d->client.list);
+       gr2d->client.ops = &gr2d_ops;
+
+       err = host1x_client_register(&gr2d->client.base);
+       if (err < 0) {
+               dev_err(dev, "failed to register host1x client: %d\n", err);
+               clk_disable_unprepare(gr2d->clk);
+               return err;
+       }
+
+       /* initialize address register map */
+       for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
+               set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
+
+       platform_set_drvdata(pdev, gr2d);
+
+       return 0;
+}
+
+static int gr2d_remove(struct platform_device *pdev)
+{
+       struct gr2d *gr2d = platform_get_drvdata(pdev);
+       int err;
+
+       err = host1x_client_unregister(&gr2d->client.base);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
+                       err);
+               return err;
+       }
+
+       clk_disable_unprepare(gr2d->clk);
+
+       return 0;
+}
+
+struct platform_driver tegra_gr2d_driver = {
+       .driver = {
+               .name = "tegra-gr2d",
+               .of_match_table = gr2d_match,
+       },
+       .probe = gr2d_probe,
+       .remove = gr2d_remove,
+};
diff --git a/drivers/gpu/drm/tegra/gr2d.h b/drivers/gpu/drm/tegra/gr2d.h
new file mode 100644 (file)
index 0000000..4d7304f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef TEGRA_GR2D_H
+#define TEGRA_GR2D_H
+
+#define GR2D_UA_BASE_ADDR              0x1a
+#define GR2D_VA_BASE_ADDR              0x1b
+#define GR2D_PAT_BASE_ADDR             0x26
+#define GR2D_DSTA_BASE_ADDR            0x2b
+#define GR2D_DSTB_BASE_ADDR            0x2c
+#define GR2D_DSTC_BASE_ADDR            0x2d
+#define GR2D_SRCA_BASE_ADDR            0x31
+#define GR2D_SRCB_BASE_ADDR            0x32
+#define GR2D_SRC_BASE_ADDR_SB          0x48
+#define GR2D_DSTA_BASE_ADDR_SB         0x49
+#define GR2D_DSTB_BASE_ADDR_SB         0x4a
+#define GR2D_UA_BASE_ADDR_SB           0x4b
+#define GR2D_VA_BASE_ADDR_SB           0x4c
+
+#define GR2D_NUM_REGS                  0x4d
+
+#endif
diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c
new file mode 100644 (file)
index 0000000..4cec8f5
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2013 Avionic Design GmbH
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/host1x.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/tegra-powergate.h>
+
+#include "drm.h"
+#include "gem.h"
+#include "gr3d.h"
+
+struct gr3d {
+       struct tegra_drm_client client;
+       struct host1x_channel *channel;
+       struct clk *clk_secondary;
+       struct clk *clk;
+
+       DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS);
+};
+
+static inline struct gr3d *to_gr3d(struct tegra_drm_client *client)
+{
+       return container_of(client, struct gr3d, client);
+}
+
+static int gr3d_init(struct host1x_client *client)
+{
+       struct tegra_drm_client *drm = host1x_to_drm_client(client);
+       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
+       struct gr3d *gr3d = to_gr3d(drm);
+
+       gr3d->channel = host1x_channel_request(client->dev);
+       if (!gr3d->channel)
+               return -ENOMEM;
+
+       client->syncpts[0] = host1x_syncpt_request(client->dev, flags);
+       if (!client->syncpts[0]) {
+               host1x_channel_free(gr3d->channel);
+               return -ENOMEM;
+       }
+
+       return tegra_drm_register_client(tegra, drm);
+}
+
+static int gr3d_exit(struct host1x_client *client)
+{
+       struct tegra_drm_client *drm = host1x_to_drm_client(client);
+       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct gr3d *gr3d = to_gr3d(drm);
+       int err;
+
+       err = tegra_drm_unregister_client(tegra, drm);
+       if (err < 0)
+               return err;
+
+       host1x_syncpt_free(client->syncpts[0]);
+       host1x_channel_free(gr3d->channel);
+
+       return 0;
+}
+
+static const struct host1x_client_ops gr3d_client_ops = {
+       .init = gr3d_init,
+       .exit = gr3d_exit,
+};
+
+static int gr3d_open_channel(struct tegra_drm_client *client,
+                            struct tegra_drm_context *context)
+{
+       struct gr3d *gr3d = to_gr3d(client);
+
+       context->channel = host1x_channel_get(gr3d->channel);
+       if (!context->channel)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void gr3d_close_channel(struct tegra_drm_context *context)
+{
+       host1x_channel_put(context->channel);
+}
+
+static int gr3d_is_addr_reg(struct device *dev, u32 class, u32 offset)
+{
+       struct gr3d *gr3d = dev_get_drvdata(dev);
+
+       switch (class) {
+       case HOST1X_CLASS_HOST1X:
+               if (offset == 0x2b)
+                       return 1;
+
+               break;
+
+       case HOST1X_CLASS_GR3D:
+               if (offset >= GR3D_NUM_REGS)
+                       break;
+
+               if (test_bit(offset, gr3d->addr_regs))
+                       return 1;
+
+               break;
+       }
+
+       return 0;
+}
+
+static const struct tegra_drm_client_ops gr3d_ops = {
+       .open_channel = gr3d_open_channel,
+       .close_channel = gr3d_close_channel,
+       .is_addr_reg = gr3d_is_addr_reg,
+       .submit = tegra_drm_submit,
+};
+
+static const struct of_device_id tegra_gr3d_match[] = {
+       { .compatible = "nvidia,tegra114-gr3d" },
+       { .compatible = "nvidia,tegra30-gr3d" },
+       { .compatible = "nvidia,tegra20-gr3d" },
+       { }
+};
+
+static const u32 gr3d_addr_regs[] = {
+       GR3D_IDX_ATTRIBUTE( 0),
+       GR3D_IDX_ATTRIBUTE( 1),
+       GR3D_IDX_ATTRIBUTE( 2),
+       GR3D_IDX_ATTRIBUTE( 3),
+       GR3D_IDX_ATTRIBUTE( 4),
+       GR3D_IDX_ATTRIBUTE( 5),
+       GR3D_IDX_ATTRIBUTE( 6),
+       GR3D_IDX_ATTRIBUTE( 7),
+       GR3D_IDX_ATTRIBUTE( 8),
+       GR3D_IDX_ATTRIBUTE( 9),
+       GR3D_IDX_ATTRIBUTE(10),
+       GR3D_IDX_ATTRIBUTE(11),
+       GR3D_IDX_ATTRIBUTE(12),
+       GR3D_IDX_ATTRIBUTE(13),
+       GR3D_IDX_ATTRIBUTE(14),
+       GR3D_IDX_ATTRIBUTE(15),
+       GR3D_IDX_INDEX_BASE,
+       GR3D_QR_ZTAG_ADDR,
+       GR3D_QR_CTAG_ADDR,
+       GR3D_QR_CZ_ADDR,
+       GR3D_TEX_TEX_ADDR( 0),
+       GR3D_TEX_TEX_ADDR( 1),
+       GR3D_TEX_TEX_ADDR( 2),
+       GR3D_TEX_TEX_ADDR( 3),
+       GR3D_TEX_TEX_ADDR( 4),
+       GR3D_TEX_TEX_ADDR( 5),
+       GR3D_TEX_TEX_ADDR( 6),
+       GR3D_TEX_TEX_ADDR( 7),
+       GR3D_TEX_TEX_ADDR( 8),
+       GR3D_TEX_TEX_ADDR( 9),
+       GR3D_TEX_TEX_ADDR(10),
+       GR3D_TEX_TEX_ADDR(11),
+       GR3D_TEX_TEX_ADDR(12),
+       GR3D_TEX_TEX_ADDR(13),
+       GR3D_TEX_TEX_ADDR(14),
+       GR3D_TEX_TEX_ADDR(15),
+       GR3D_DW_MEMORY_OUTPUT_ADDRESS,
+       GR3D_GLOBAL_SURFADDR( 0),
+       GR3D_GLOBAL_SURFADDR( 1),
+       GR3D_GLOBAL_SURFADDR( 2),
+       GR3D_GLOBAL_SURFADDR( 3),
+       GR3D_GLOBAL_SURFADDR( 4),
+       GR3D_GLOBAL_SURFADDR( 5),
+       GR3D_GLOBAL_SURFADDR( 6),
+       GR3D_GLOBAL_SURFADDR( 7),
+       GR3D_GLOBAL_SURFADDR( 8),
+       GR3D_GLOBAL_SURFADDR( 9),
+       GR3D_GLOBAL_SURFADDR(10),
+       GR3D_GLOBAL_SURFADDR(11),
+       GR3D_GLOBAL_SURFADDR(12),
+       GR3D_GLOBAL_SURFADDR(13),
+       GR3D_GLOBAL_SURFADDR(14),
+       GR3D_GLOBAL_SURFADDR(15),
+       GR3D_GLOBAL_SPILLSURFADDR,
+       GR3D_GLOBAL_SURFOVERADDR( 0),
+       GR3D_GLOBAL_SURFOVERADDR( 1),
+       GR3D_GLOBAL_SURFOVERADDR( 2),
+       GR3D_GLOBAL_SURFOVERADDR( 3),
+       GR3D_GLOBAL_SURFOVERADDR( 4),
+       GR3D_GLOBAL_SURFOVERADDR( 5),
+       GR3D_GLOBAL_SURFOVERADDR( 6),
+       GR3D_GLOBAL_SURFOVERADDR( 7),
+       GR3D_GLOBAL_SURFOVERADDR( 8),
+       GR3D_GLOBAL_SURFOVERADDR( 9),
+       GR3D_GLOBAL_SURFOVERADDR(10),
+       GR3D_GLOBAL_SURFOVERADDR(11),
+       GR3D_GLOBAL_SURFOVERADDR(12),
+       GR3D_GLOBAL_SURFOVERADDR(13),
+       GR3D_GLOBAL_SURFOVERADDR(14),
+       GR3D_GLOBAL_SURFOVERADDR(15),
+       GR3D_GLOBAL_SAMP01SURFADDR( 0),
+       GR3D_GLOBAL_SAMP01SURFADDR( 1),
+       GR3D_GLOBAL_SAMP01SURFADDR( 2),
+       GR3D_GLOBAL_SAMP01SURFADDR( 3),
+       GR3D_GLOBAL_SAMP01SURFADDR( 4),
+       GR3D_GLOBAL_SAMP01SURFADDR( 5),
+       GR3D_GLOBAL_SAMP01SURFADDR( 6),
+       GR3D_GLOBAL_SAMP01SURFADDR( 7),
+       GR3D_GLOBAL_SAMP01SURFADDR( 8),
+       GR3D_GLOBAL_SAMP01SURFADDR( 9),
+       GR3D_GLOBAL_SAMP01SURFADDR(10),
+       GR3D_GLOBAL_SAMP01SURFADDR(11),
+       GR3D_GLOBAL_SAMP01SURFADDR(12),
+       GR3D_GLOBAL_SAMP01SURFADDR(13),
+       GR3D_GLOBAL_SAMP01SURFADDR(14),
+       GR3D_GLOBAL_SAMP01SURFADDR(15),
+       GR3D_GLOBAL_SAMP23SURFADDR( 0),
+       GR3D_GLOBAL_SAMP23SURFADDR( 1),
+       GR3D_GLOBAL_SAMP23SURFADDR( 2),
+       GR3D_GLOBAL_SAMP23SURFADDR( 3),
+       GR3D_GLOBAL_SAMP23SURFADDR( 4),
+       GR3D_GLOBAL_SAMP23SURFADDR( 5),
+       GR3D_GLOBAL_SAMP23SURFADDR( 6),
+       GR3D_GLOBAL_SAMP23SURFADDR( 7),
+       GR3D_GLOBAL_SAMP23SURFADDR( 8),
+       GR3D_GLOBAL_SAMP23SURFADDR( 9),
+       GR3D_GLOBAL_SAMP23SURFADDR(10),
+       GR3D_GLOBAL_SAMP23SURFADDR(11),
+       GR3D_GLOBAL_SAMP23SURFADDR(12),
+       GR3D_GLOBAL_SAMP23SURFADDR(13),
+       GR3D_GLOBAL_SAMP23SURFADDR(14),
+       GR3D_GLOBAL_SAMP23SURFADDR(15),
+};
+
+static int gr3d_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct host1x_syncpt **syncpts;
+       struct gr3d *gr3d;
+       unsigned int i;
+       int err;
+
+       gr3d = devm_kzalloc(&pdev->dev, sizeof(*gr3d), GFP_KERNEL);
+       if (!gr3d)
+               return -ENOMEM;
+
+       syncpts = devm_kzalloc(&pdev->dev, sizeof(*syncpts), GFP_KERNEL);
+       if (!syncpts)
+               return -ENOMEM;
+
+       gr3d->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(gr3d->clk)) {
+               dev_err(&pdev->dev, "cannot get clock\n");
+               return PTR_ERR(gr3d->clk);
+       }
+
+       if (of_device_is_compatible(np, "nvidia,tegra30-gr3d")) {
+               gr3d->clk_secondary = devm_clk_get(&pdev->dev, "3d2");
+               if (IS_ERR(gr3d->clk)) {
+                       dev_err(&pdev->dev, "cannot get secondary clock\n");
+                       return PTR_ERR(gr3d->clk);
+               }
+       }
+
+       err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D, gr3d->clk);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to power up 3D unit\n");
+               return err;
+       }
+
+       if (gr3d->clk_secondary) {
+               err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D1,
+                                                       gr3d->clk_secondary);
+               if (err < 0) {
+                       dev_err(&pdev->dev,
+                               "failed to power up secondary 3D unit\n");
+                       return err;
+               }
+       }
+
+       INIT_LIST_HEAD(&gr3d->client.base.list);
+       gr3d->client.base.ops = &gr3d_client_ops;
+       gr3d->client.base.dev = &pdev->dev;
+       gr3d->client.base.class = HOST1X_CLASS_GR3D;
+       gr3d->client.base.syncpts = syncpts;
+       gr3d->client.base.num_syncpts = 1;
+
+       INIT_LIST_HEAD(&gr3d->client.list);
+       gr3d->client.ops = &gr3d_ops;
+
+       err = host1x_client_register(&gr3d->client.base);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to register host1x client: %d\n",
+                       err);
+               return err;
+       }
+
+       /* initialize address register map */
+       for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++)
+               set_bit(gr3d_addr_regs[i], gr3d->addr_regs);
+
+       platform_set_drvdata(pdev, gr3d);
+
+       return 0;
+}
+
+static int gr3d_remove(struct platform_device *pdev)
+{
+       struct gr3d *gr3d = platform_get_drvdata(pdev);
+       int err;
+
+       err = host1x_client_unregister(&gr3d->client.base);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
+                       err);
+               return err;
+       }
+
+       if (gr3d->clk_secondary) {
+               tegra_powergate_power_off(TEGRA_POWERGATE_3D1);
+               clk_disable_unprepare(gr3d->clk_secondary);
+       }
+
+       tegra_powergate_power_off(TEGRA_POWERGATE_3D);
+       clk_disable_unprepare(gr3d->clk);
+
+       return 0;
+}
+
+struct platform_driver tegra_gr3d_driver = {
+       .driver = {
+               .name = "tegra-gr3d",
+               .of_match_table = tegra_gr3d_match,
+       },
+       .probe = gr3d_probe,
+       .remove = gr3d_remove,
+};
diff --git a/drivers/gpu/drm/tegra/gr3d.h b/drivers/gpu/drm/tegra/gr3d.h
new file mode 100644 (file)
index 0000000..0c30a13
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef TEGRA_GR3D_H
+#define TEGRA_GR3D_H
+
+#define GR3D_IDX_ATTRIBUTE(x)          (0x100 + (x) * 2)
+#define GR3D_IDX_INDEX_BASE            0x121
+#define GR3D_QR_ZTAG_ADDR              0x415
+#define GR3D_QR_CTAG_ADDR              0x417
+#define GR3D_QR_CZ_ADDR                        0x419
+#define GR3D_TEX_TEX_ADDR(x)           (0x710 + (x))
+#define GR3D_DW_MEMORY_OUTPUT_ADDRESS  0x904
+#define GR3D_GLOBAL_SURFADDR(x)                (0xe00 + (x))
+#define GR3D_GLOBAL_SPILLSURFADDR      0xe2a
+#define GR3D_GLOBAL_SURFOVERADDR(x)    (0xe30 + (x))
+#define GR3D_GLOBAL_SAMP01SURFADDR(x)  (0xe50 + (x))
+#define GR3D_GLOBAL_SAMP23SURFADDR(x)  (0xe60 + (x))
+
+#define GR3D_NUM_REGS                  0xe88
+
+#endif
similarity index 83%
rename from drivers/gpu/host1x/drm/hdmi.c
rename to drivers/gpu/drm/tegra/hdmi.c
index 644d95c..0cd9bc2 100644 (file)
@@ -8,21 +8,33 @@
  */
 
 #include <linux/clk.h>
+#include <linux/clk/tegra.h>
 #include <linux/debugfs.h>
-#include <linux/gpio.h>
 #include <linux/hdmi.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
-#include <linux/clk/tegra.h>
-
-#include <drm/drm_edid.h>
 
 #include "hdmi.h"
 #include "drm.h"
 #include "dc.h"
-#include "host1x_client.h"
+
+struct tmds_config {
+       unsigned int pclk;
+       u32 pll0;
+       u32 pll1;
+       u32 pe_current;
+       u32 drive_current;
+       u32 peak_current;
+};
+
+struct tegra_hdmi_config {
+       const struct tmds_config *tmds;
+       unsigned int num_tmds;
+
+       unsigned long fuse_override_offset;
+       unsigned long fuse_override_value;
+
+       bool has_sor_io_peak_current;
+};
 
 struct tegra_hdmi {
        struct host1x_client client;
@@ -38,6 +50,8 @@ struct tegra_hdmi {
        struct clk *clk_parent;
        struct clk *clk;
 
+       const struct tegra_hdmi_config *config;
+
        unsigned int audio_source;
        unsigned int audio_freq;
        bool stereo;
@@ -143,15 +157,7 @@ static const struct tegra_hdmi_audio_config tegra_hdmi_audio_192k[] = {
        {         0,     0,      0,     0 },
 };
 
-struct tmds_config {
-       unsigned int pclk;
-       u32 pll0;
-       u32 pll1;
-       u32 pe_current;
-       u32 drive_current;
-};
-
-static const struct tmds_config tegra2_tmds_config[] = {
+static const struct tmds_config tegra20_tmds_config[] = {
        { /* slow pixel clock modes */
                .pclk = 27000000,
                .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
@@ -184,7 +190,7 @@ static const struct tmds_config tegra2_tmds_config[] = {
        },
 };
 
-static const struct tmds_config tegra3_tmds_config[] = {
+static const struct tmds_config tegra30_tmds_config[] = {
        { /* 480p modes */
                .pclk = 27000000,
                .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
@@ -230,6 +236,85 @@ static const struct tmds_config tegra3_tmds_config[] = {
        },
 };
 
+static const struct tmds_config tegra114_tmds_config[] = {
+       { /* 480p/576p / 25.2MHz/27MHz modes */
+               .pclk = 27000000,
+               .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+                       SOR_PLL_VCOCAP(0) | SOR_PLL_RESISTORSEL,
+               .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(0),
+               .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT1(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT2(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT3(PE_CURRENT_0_mA_T114),
+               .drive_current =
+                       DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114),
+               .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+       }, { /* 720p / 74.25MHz modes */
+               .pclk = 74250000,
+               .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+                       SOR_PLL_VCOCAP(1) | SOR_PLL_RESISTORSEL,
+               .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) |
+                       SOR_PLL_TMDS_TERMADJ(0),
+               .pe_current = PE_CURRENT0(PE_CURRENT_15_mA_T114) |
+                       PE_CURRENT1(PE_CURRENT_15_mA_T114) |
+                       PE_CURRENT2(PE_CURRENT_15_mA_T114) |
+                       PE_CURRENT3(PE_CURRENT_15_mA_T114),
+               .drive_current =
+                       DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114),
+               .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+       }, { /* 1080p / 148.5MHz modes */
+               .pclk = 148500000,
+               .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+                       SOR_PLL_VCOCAP(3) | SOR_PLL_RESISTORSEL,
+               .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) |
+                       SOR_PLL_TMDS_TERMADJ(0),
+               .pe_current = PE_CURRENT0(PE_CURRENT_10_mA_T114) |
+                       PE_CURRENT1(PE_CURRENT_10_mA_T114) |
+                       PE_CURRENT2(PE_CURRENT_10_mA_T114) |
+                       PE_CURRENT3(PE_CURRENT_10_mA_T114),
+               .drive_current =
+                       DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_12_400_mA_T114) |
+                       DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_12_400_mA_T114) |
+                       DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_12_400_mA_T114) |
+                       DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_12_400_mA_T114),
+               .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+       }, { /* 225/297MHz modes */
+               .pclk = UINT_MAX,
+               .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+                       SOR_PLL_VCOCAP(0xf) | SOR_PLL_RESISTORSEL,
+               .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(7)
+                       | SOR_PLL_TMDS_TERM_ENABLE,
+               .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT1(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT2(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT3(PE_CURRENT_0_mA_T114),
+               .drive_current =
+                       DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_25_200_mA_T114) |
+                       DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_25_200_mA_T114) |
+                       DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_25_200_mA_T114) |
+                       DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_19_200_mA_T114),
+               .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_3_000_mA) |
+                       PEAK_CURRENT_LANE1(PEAK_CURRENT_3_000_mA) |
+                       PEAK_CURRENT_LANE2(PEAK_CURRENT_3_000_mA) |
+                       PEAK_CURRENT_LANE3(PEAK_CURRENT_0_800_mA),
+       },
+};
+
 static const struct tegra_hdmi_audio_config *
 tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pclk)
 {
@@ -511,7 +596,7 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
 
        err = hdmi_audio_infoframe_init(&frame);
        if (err < 0) {
-               dev_err(hdmi->dev, "failed to initialize audio infoframe: %d\n",
+               dev_err(hdmi->dev, "failed to setup audio infoframe: %zd\n",
                        err);
                return;
        }
@@ -531,7 +616,7 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
         * contain 7 bytes. Including the 3 byte header only the first 10
         * bytes can be programmed.
         */
-       tegra_hdmi_write_infopack(hdmi, buffer, min(10, err));
+       tegra_hdmi_write_infopack(hdmi, buffer, min_t(size_t, 10, err));
 
        tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE,
                          HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
@@ -577,8 +662,28 @@ static void tegra_hdmi_setup_tmds(struct tegra_hdmi *hdmi,
        tegra_hdmi_writel(hdmi, tmds->pll1, HDMI_NV_PDISP_SOR_PLL1);
        tegra_hdmi_writel(hdmi, tmds->pe_current, HDMI_NV_PDISP_PE_CURRENT);
 
-       value = tmds->drive_current | DRIVE_CURRENT_FUSE_OVERRIDE;
-       tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT);
+       tegra_hdmi_writel(hdmi, tmds->drive_current,
+                         HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT);
+
+       value = tegra_hdmi_readl(hdmi, hdmi->config->fuse_override_offset);
+       value |= hdmi->config->fuse_override_value;
+       tegra_hdmi_writel(hdmi, value, hdmi->config->fuse_override_offset);
+
+       if (hdmi->config->has_sor_io_peak_current)
+               tegra_hdmi_writel(hdmi, tmds->peak_current,
+                                 HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT);
+}
+
+static bool tegra_output_is_hdmi(struct tegra_output *output)
+{
+       struct edid *edid;
+
+       if (!output->connector.edid_blob_ptr)
+               return false;
+
+       edid = (struct edid *)output->connector.edid_blob_ptr->data;
+
+       return drm_detect_hdmi_monitor(edid);
 }
 
 static int tegra_output_hdmi_enable(struct tegra_output *output)
@@ -589,23 +694,17 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
        struct tegra_hdmi *hdmi = to_hdmi(output);
        struct device_node *node = hdmi->dev->of_node;
        unsigned int pulse_start, div82, pclk;
-       const struct tmds_config *tmds;
-       unsigned int num_tmds;
        unsigned long value;
        int retries = 1000;
        int err;
 
+       hdmi->dvi = !tegra_output_is_hdmi(output);
+
        pclk = mode->clock * 1000;
        h_sync_width = mode->hsync_end - mode->hsync_start;
        h_back_porch = mode->htotal - mode->hsync_end;
        h_front_porch = mode->hsync_start - mode->hdisplay;
 
-       err = regulator_enable(hdmi->vdd);
-       if (err < 0) {
-               dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err);
-               return err;
-       }
-
        err = regulator_enable(hdmi->pll);
        if (err < 0) {
                dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err);
@@ -710,17 +809,9 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
        tegra_hdmi_setup_stereo_infoframe(hdmi);
 
        /* TMDS CONFIG */
-       if (of_device_is_compatible(node, "nvidia,tegra30-hdmi")) {
-               num_tmds = ARRAY_SIZE(tegra3_tmds_config);
-               tmds = tegra3_tmds_config;
-       } else {
-               num_tmds = ARRAY_SIZE(tegra2_tmds_config);
-               tmds = tegra2_tmds_config;
-       }
-
-       for (i = 0; i < num_tmds; i++) {
-               if (pclk <= tmds[i].pclk) {
-                       tegra_hdmi_setup_tmds(hdmi, &tmds[i]);
+       for (i = 0; i < hdmi->config->num_tmds; i++) {
+               if (pclk <= hdmi->config->tmds[i].pclk) {
+                       tegra_hdmi_setup_tmds(hdmi, &hdmi->config->tmds[i]);
                        break;
                }
        }
@@ -824,7 +915,6 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
        tegra_periph_reset_assert(hdmi->clk);
        clk_disable(hdmi->clk);
        regulator_disable(hdmi->pll);
-       regulator_disable(hdmi->vdd);
 
        return 0;
 }
@@ -1055,6 +1145,7 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
        DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0);
        DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR);
        DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE);
+       DUMP_REG(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT);
 
 #undef DUMP_REG
 
@@ -1122,24 +1213,31 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
        return 0;
 }
 
-static int tegra_hdmi_drm_init(struct host1x_client *client,
-                              struct drm_device *drm)
+static int tegra_hdmi_init(struct host1x_client *client)
 {
+       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
        struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
        int err;
 
+       err = regulator_enable(hdmi->vdd);
+       if (err < 0) {
+               dev_err(client->dev, "failed to enable VDD regulator: %d\n",
+                       err);
+               return err;
+       }
+
        hdmi->output.type = TEGRA_OUTPUT_HDMI;
        hdmi->output.dev = client->dev;
        hdmi->output.ops = &hdmi_ops;
 
-       err = tegra_output_init(drm, &hdmi->output);
+       err = tegra_output_init(tegra->drm, &hdmi->output);
        if (err < 0) {
                dev_err(client->dev, "output setup failed: %d\n", err);
                return err;
        }
 
        if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-               err = tegra_hdmi_debugfs_init(hdmi, drm->primary);
+               err = tegra_hdmi_debugfs_init(hdmi, tegra->drm->primary);
                if (err < 0)
                        dev_err(client->dev, "debugfs setup failed: %d\n", err);
        }
@@ -1147,7 +1245,7 @@ static int tegra_hdmi_drm_init(struct host1x_client *client,
        return 0;
 }
 
-static int tegra_hdmi_drm_exit(struct host1x_client *client)
+static int tegra_hdmi_exit(struct host1x_client *client)
 {
        struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
        int err;
@@ -1171,25 +1269,63 @@ static int tegra_hdmi_drm_exit(struct host1x_client *client)
                return err;
        }
 
+       regulator_disable(hdmi->vdd);
+
        return 0;
 }
 
 static const struct host1x_client_ops hdmi_client_ops = {
-       .drm_init = tegra_hdmi_drm_init,
-       .drm_exit = tegra_hdmi_drm_exit,
+       .init = tegra_hdmi_init,
+       .exit = tegra_hdmi_exit,
+};
+
+static const struct tegra_hdmi_config tegra20_hdmi_config = {
+       .tmds = tegra20_tmds_config,
+       .num_tmds = ARRAY_SIZE(tegra20_tmds_config),
+       .fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT,
+       .fuse_override_value = 1 << 31,
+       .has_sor_io_peak_current = false,
+};
+
+static const struct tegra_hdmi_config tegra30_hdmi_config = {
+       .tmds = tegra30_tmds_config,
+       .num_tmds = ARRAY_SIZE(tegra30_tmds_config),
+       .fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT,
+       .fuse_override_value = 1 << 31,
+       .has_sor_io_peak_current = false,
+};
+
+static const struct tegra_hdmi_config tegra114_hdmi_config = {
+       .tmds = tegra114_tmds_config,
+       .num_tmds = ARRAY_SIZE(tegra114_tmds_config),
+       .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0,
+       .fuse_override_value = 1 << 31,
+       .has_sor_io_peak_current = true,
+};
+
+static const struct of_device_id tegra_hdmi_of_match[] = {
+       { .compatible = "nvidia,tegra114-hdmi", .data = &tegra114_hdmi_config },
+       { .compatible = "nvidia,tegra30-hdmi", .data = &tegra30_hdmi_config },
+       { .compatible = "nvidia,tegra20-hdmi", .data = &tegra20_hdmi_config },
+       { },
 };
 
 static int tegra_hdmi_probe(struct platform_device *pdev)
 {
-       struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
+       const struct of_device_id *match;
        struct tegra_hdmi *hdmi;
        struct resource *regs;
        int err;
 
+       match = of_match_node(tegra_hdmi_of_match, pdev->dev.of_node);
+       if (!match)
+               return -ENODEV;
+
        hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
        if (!hdmi)
                return -ENOMEM;
 
+       hdmi->config = match->data;
        hdmi->dev = &pdev->dev;
        hdmi->audio_source = AUTO;
        hdmi->audio_freq = 44100;
@@ -1234,7 +1370,7 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
 
        hdmi->output.dev = &pdev->dev;
 
-       err = tegra_output_parse_dt(&hdmi->output);
+       err = tegra_output_probe(&hdmi->output);
        if (err < 0)
                return err;
 
@@ -1252,11 +1388,11 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
 
        hdmi->irq = err;
 
-       hdmi->client.ops = &hdmi_client_ops;
        INIT_LIST_HEAD(&hdmi->client.list);
+       hdmi->client.ops = &hdmi_client_ops;
        hdmi->client.dev = &pdev->dev;
 
-       err = host1x_register_client(host1x, &hdmi->client);
+       err = host1x_client_register(&hdmi->client);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to register host1x client: %d\n",
                        err);
@@ -1270,29 +1406,28 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
 
 static int tegra_hdmi_remove(struct platform_device *pdev)
 {
-       struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
        struct tegra_hdmi *hdmi = platform_get_drvdata(pdev);
        int err;
 
-       err = host1x_unregister_client(host1x, &hdmi->client);
+       err = host1x_client_unregister(&hdmi->client);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
                        err);
                return err;
        }
 
+       err = tegra_output_remove(&hdmi->output);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to remove output: %d\n", err);
+               return err;
+       }
+
        clk_unprepare(hdmi->clk_parent);
        clk_unprepare(hdmi->clk);
 
        return 0;
 }
 
-static struct of_device_id tegra_hdmi_of_match[] = {
-       { .compatible = "nvidia,tegra30-hdmi", },
-       { .compatible = "nvidia,tegra20-hdmi", },
-       { },
-};
-
 struct platform_driver tegra_hdmi_driver = {
        .driver = {
                .name = "tegra-hdmi",
similarity index 72%
rename from drivers/gpu/host1x/drm/hdmi.h
rename to drivers/gpu/drm/tegra/hdmi.h
index 52ac36e..0aebc48 100644 (file)
 #define DRIVE_CURRENT_LANE1(x)      (((x) & 0x3f) <<  8)
 #define DRIVE_CURRENT_LANE2(x)      (((x) & 0x3f) << 16)
 #define DRIVE_CURRENT_LANE3(x)      (((x) & 0x3f) << 24)
-#define DRIVE_CURRENT_FUSE_OVERRIDE (1 << 31)
+#define DRIVE_CURRENT_LANE0_T114(x) (((x) & 0x7f) <<  0)
+#define DRIVE_CURRENT_LANE1_T114(x) (((x) & 0x7f) <<  8)
+#define DRIVE_CURRENT_LANE2_T114(x) (((x) & 0x7f) << 16)
+#define DRIVE_CURRENT_LANE3_T114(x) (((x) & 0x7f) << 24)
 
 #define DRIVE_CURRENT_1_500_mA  0x00
 #define DRIVE_CURRENT_1_875_mA  0x01
 #define DRIVE_CURRENT_24_375_mA 0x3d
 #define DRIVE_CURRENT_24_750_mA 0x3e
 
+#define DRIVE_CURRENT_0_000_mA_T114 0x00
+#define DRIVE_CURRENT_0_400_mA_T114 0x01
+#define DRIVE_CURRENT_0_800_mA_T114 0x02
+#define DRIVE_CURRENT_1_200_mA_T114 0x03
+#define DRIVE_CURRENT_1_600_mA_T114 0x04
+#define DRIVE_CURRENT_2_000_mA_T114 0x05
+#define DRIVE_CURRENT_2_400_mA_T114 0x06
+#define DRIVE_CURRENT_2_800_mA_T114 0x07
+#define DRIVE_CURRENT_3_200_mA_T114 0x08
+#define DRIVE_CURRENT_3_600_mA_T114 0x09
+#define DRIVE_CURRENT_4_000_mA_T114 0x0a
+#define DRIVE_CURRENT_4_400_mA_T114 0x0b
+#define DRIVE_CURRENT_4_800_mA_T114 0x0c
+#define DRIVE_CURRENT_5_200_mA_T114 0x0d
+#define DRIVE_CURRENT_5_600_mA_T114 0x0e
+#define DRIVE_CURRENT_6_000_mA_T114 0x0f
+#define DRIVE_CURRENT_6_400_mA_T114 0x10
+#define DRIVE_CURRENT_6_800_mA_T114 0x11
+#define DRIVE_CURRENT_7_200_mA_T114 0x12
+#define DRIVE_CURRENT_7_600_mA_T114 0x13
+#define DRIVE_CURRENT_8_000_mA_T114 0x14
+#define DRIVE_CURRENT_8_400_mA_T114 0x15
+#define DRIVE_CURRENT_8_800_mA_T114 0x16
+#define DRIVE_CURRENT_9_200_mA_T114 0x17
+#define DRIVE_CURRENT_9_600_mA_T114 0x18
+#define DRIVE_CURRENT_10_000_mA_T114 0x19
+#define DRIVE_CURRENT_10_400_mA_T114 0x1a
+#define DRIVE_CURRENT_10_800_mA_T114 0x1b
+#define DRIVE_CURRENT_11_200_mA_T114 0x1c
+#define DRIVE_CURRENT_11_600_mA_T114 0x1d
+#define DRIVE_CURRENT_12_000_mA_T114 0x1e
+#define DRIVE_CURRENT_12_400_mA_T114 0x1f
+#define DRIVE_CURRENT_12_800_mA_T114 0x20
+#define DRIVE_CURRENT_13_200_mA_T114 0x21
+#define DRIVE_CURRENT_13_600_mA_T114 0x22
+#define DRIVE_CURRENT_14_000_mA_T114 0x23
+#define DRIVE_CURRENT_14_400_mA_T114 0x24
+#define DRIVE_CURRENT_14_800_mA_T114 0x25
+#define DRIVE_CURRENT_15_200_mA_T114 0x26
+#define DRIVE_CURRENT_15_600_mA_T114 0x27
+#define DRIVE_CURRENT_16_000_mA_T114 0x28
+#define DRIVE_CURRENT_16_400_mA_T114 0x29
+#define DRIVE_CURRENT_16_800_mA_T114 0x2a
+#define DRIVE_CURRENT_17_200_mA_T114 0x2b
+#define DRIVE_CURRENT_17_600_mA_T114 0x2c
+#define DRIVE_CURRENT_18_000_mA_T114 0x2d
+#define DRIVE_CURRENT_18_400_mA_T114 0x2e
+#define DRIVE_CURRENT_18_800_mA_T114 0x2f
+#define DRIVE_CURRENT_19_200_mA_T114 0x30
+#define DRIVE_CURRENT_19_600_mA_T114 0x31
+#define DRIVE_CURRENT_20_000_mA_T114 0x32
+#define DRIVE_CURRENT_20_400_mA_T114 0x33
+#define DRIVE_CURRENT_20_800_mA_T114 0x34
+#define DRIVE_CURRENT_21_200_mA_T114 0x35
+#define DRIVE_CURRENT_21_600_mA_T114 0x36
+#define DRIVE_CURRENT_22_000_mA_T114 0x37
+#define DRIVE_CURRENT_22_400_mA_T114 0x38
+#define DRIVE_CURRENT_22_800_mA_T114 0x39
+#define DRIVE_CURRENT_23_200_mA_T114 0x3a
+#define DRIVE_CURRENT_23_600_mA_T114 0x3b
+#define DRIVE_CURRENT_24_000_mA_T114 0x3c
+#define DRIVE_CURRENT_24_400_mA_T114 0x3d
+#define DRIVE_CURRENT_24_800_mA_T114 0x3e
+#define DRIVE_CURRENT_25_200_mA_T114 0x3f
+#define DRIVE_CURRENT_25_400_mA_T114 0x40
+#define DRIVE_CURRENT_25_800_mA_T114 0x41
+#define DRIVE_CURRENT_26_200_mA_T114 0x42
+#define DRIVE_CURRENT_26_600_mA_T114 0x43
+#define DRIVE_CURRENT_27_000_mA_T114 0x44
+#define DRIVE_CURRENT_27_400_mA_T114 0x45
+#define DRIVE_CURRENT_27_800_mA_T114 0x46
+#define DRIVE_CURRENT_28_200_mA_T114 0x47
+
 #define HDMI_NV_PDISP_AUDIO_DEBUG0                             0x7f
 #define HDMI_NV_PDISP_AUDIO_DEBUG1                             0x80
 #define HDMI_NV_PDISP_AUDIO_DEBUG2                             0x81
 #define PE_CURRENT_7_0_mA 0xe
 #define PE_CURRENT_7_5_mA 0xf
 
+#define PE_CURRENT_0_mA_T114 0x0
+#define PE_CURRENT_1_mA_T114 0x1
+#define PE_CURRENT_2_mA_T114 0x2
+#define PE_CURRENT_3_mA_T114 0x3
+#define PE_CURRENT_4_mA_T114 0x4
+#define PE_CURRENT_5_mA_T114 0x5
+#define PE_CURRENT_6_mA_T114 0x6
+#define PE_CURRENT_7_mA_T114 0x7
+#define PE_CURRENT_8_mA_T114 0x8
+#define PE_CURRENT_9_mA_T114 0x9
+#define PE_CURRENT_10_mA_T114 0xa
+#define PE_CURRENT_11_mA_T114 0xb
+#define PE_CURRENT_12_mA_T114 0xc
+#define PE_CURRENT_13_mA_T114 0xd
+#define PE_CURRENT_14_mA_T114 0xe
+#define PE_CURRENT_15_mA_T114 0xf
+
 #define HDMI_NV_PDISP_KEY_CTRL                                 0x9a
 #define HDMI_NV_PDISP_KEY_DEBUG0                               0x9b
 #define HDMI_NV_PDISP_KEY_DEBUG1                               0x9c
 #define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920    0xc5
 #define HDMI_NV_PDISP_SOR_AUDIO_AVAL_DEFAULT 0xc5
 
+#define HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT              0xd1
+#define PEAK_CURRENT_LANE0(x) (((x) & 0x7f) <<  0)
+#define PEAK_CURRENT_LANE1(x) (((x) & 0x7f) <<  8)
+#define PEAK_CURRENT_LANE2(x) (((x) & 0x7f) << 16)
+#define PEAK_CURRENT_LANE3(x) (((x) & 0x7f) << 24)
+
+#define PEAK_CURRENT_0_000_mA 0x00
+#define PEAK_CURRENT_0_200_mA 0x01
+#define PEAK_CURRENT_0_400_mA 0x02
+#define PEAK_CURRENT_0_600_mA 0x03
+#define PEAK_CURRENT_0_800_mA 0x04
+#define PEAK_CURRENT_1_000_mA 0x05
+#define PEAK_CURRENT_1_200_mA 0x06
+#define PEAK_CURRENT_1_400_mA 0x07
+#define PEAK_CURRENT_1_600_mA 0x08
+#define PEAK_CURRENT_1_800_mA 0x09
+#define PEAK_CURRENT_2_000_mA 0x0a
+#define PEAK_CURRENT_2_200_mA 0x0b
+#define PEAK_CURRENT_2_400_mA 0x0c
+#define PEAK_CURRENT_2_600_mA 0x0d
+#define PEAK_CURRENT_2_800_mA 0x0e
+#define PEAK_CURRENT_3_000_mA 0x0f
+#define PEAK_CURRENT_3_200_mA 0x10
+#define PEAK_CURRENT_3_400_mA 0x11
+#define PEAK_CURRENT_3_600_mA 0x12
+#define PEAK_CURRENT_3_800_mA 0x13
+#define PEAK_CURRENT_4_000_mA 0x14
+#define PEAK_CURRENT_4_200_mA 0x15
+#define PEAK_CURRENT_4_400_mA 0x16
+#define PEAK_CURRENT_4_600_mA 0x17
+#define PEAK_CURRENT_4_800_mA 0x18
+#define PEAK_CURRENT_5_000_mA 0x19
+#define PEAK_CURRENT_5_200_mA 0x1a
+#define PEAK_CURRENT_5_400_mA 0x1b
+#define PEAK_CURRENT_5_600_mA 0x1c
+#define PEAK_CURRENT_5_800_mA 0x1d
+#define PEAK_CURRENT_6_000_mA 0x1e
+#define PEAK_CURRENT_6_200_mA 0x1f
+#define PEAK_CURRENT_6_400_mA 0x20
+#define PEAK_CURRENT_6_600_mA 0x21
+#define PEAK_CURRENT_6_800_mA 0x22
+#define PEAK_CURRENT_7_000_mA 0x23
+#define PEAK_CURRENT_7_200_mA 0x24
+#define PEAK_CURRENT_7_400_mA 0x25
+#define PEAK_CURRENT_7_600_mA 0x26
+#define PEAK_CURRENT_7_800_mA 0x27
+#define PEAK_CURRENT_8_000_mA 0x28
+#define PEAK_CURRENT_8_200_mA 0x29
+#define PEAK_CURRENT_8_400_mA 0x2a
+#define PEAK_CURRENT_8_600_mA 0x2b
+#define PEAK_CURRENT_8_800_mA 0x2c
+#define PEAK_CURRENT_9_000_mA 0x2d
+#define PEAK_CURRENT_9_200_mA 0x2e
+#define PEAK_CURRENT_9_400_mA 0x2f
+
+#define HDMI_NV_PDISP_SOR_PAD_CTLS0            0xd2
+
 #endif /* TEGRA_HDMI_H */
similarity index 91%
rename from drivers/gpu/host1x/drm/output.c
rename to drivers/gpu/drm/tegra/output.c
index 137ae81..2cb0065 100644 (file)
@@ -7,9 +7,7 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
 #include <linux/of_gpio.h>
-#include <linux/i2c.h>
 
 #include "drm.h"
 
@@ -81,10 +79,16 @@ tegra_connector_detect(struct drm_connector *connector, bool force)
        return status;
 }
 
+static void drm_connector_clear(struct drm_connector *connector)
+{
+       memset(connector, 0, sizeof(*connector));
+}
+
 static void tegra_connector_destroy(struct drm_connector *connector)
 {
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
+       drm_connector_clear(connector);
 }
 
 static const struct drm_connector_funcs connector_funcs = {
@@ -94,9 +98,15 @@ static const struct drm_connector_funcs connector_funcs = {
        .destroy = tegra_connector_destroy,
 };
 
+static void drm_encoder_clear(struct drm_encoder *encoder)
+{
+       memset(encoder, 0, sizeof(*encoder));
+}
+
 static void tegra_encoder_destroy(struct drm_encoder *encoder)
 {
        drm_encoder_cleanup(encoder);
+       drm_encoder_clear(encoder);
 }
 
 static const struct drm_encoder_funcs encoder_funcs = {
@@ -151,7 +161,7 @@ static irqreturn_t hpd_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-int tegra_output_parse_dt(struct tegra_output *output)
+int tegra_output_probe(struct tegra_output *output)
 {
        enum of_gpio_flags flags;
        struct device_node *ddc;
@@ -181,14 +191,6 @@ int tegra_output_parse_dt(struct tegra_output *output)
        output->hpd_gpio = of_get_named_gpio_flags(output->of_node,
                                                   "nvidia,hpd-gpio", 0,
                                                   &flags);
-
-       return 0;
-}
-
-int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
-{
-       int connector, encoder, err;
-
        if (gpio_is_valid(output->hpd_gpio)) {
                unsigned long flags;
 
@@ -202,7 +204,8 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
                err = gpio_to_irq(output->hpd_gpio);
                if (err < 0) {
                        dev_err(output->dev, "gpio_to_irq(): %d\n", err);
-                       goto free_hpd;
+                       gpio_free(output->hpd_gpio);
+                       return err;
                }
 
                output->hpd_irq = err;
@@ -215,12 +218,33 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
                if (err < 0) {
                        dev_err(output->dev, "failed to request IRQ#%u: %d\n",
                                output->hpd_irq, err);
-                       goto free_hpd;
+                       gpio_free(output->hpd_gpio);
+                       return err;
                }
 
                output->connector.polled = DRM_CONNECTOR_POLL_HPD;
        }
 
+       return 0;
+}
+
+int tegra_output_remove(struct tegra_output *output)
+{
+       if (gpio_is_valid(output->hpd_gpio)) {
+               free_irq(output->hpd_irq, output);
+               gpio_free(output->hpd_gpio);
+       }
+
+       if (output->ddc)
+               put_device(&output->ddc->dev);
+
+       return 0;
+}
+
+int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
+{
+       int connector, encoder;
+
        switch (output->type) {
        case TEGRA_OUTPUT_RGB:
                connector = DRM_MODE_CONNECTOR_LVDS;
@@ -241,6 +265,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
        drm_connector_init(drm, &output->connector, &connector_funcs,
                           connector);
        drm_connector_helper_add(&output->connector, &connector_helper_funcs);
+       output->connector.dpms = DRM_MODE_DPMS_OFF;
 
        drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder);
        drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs);
@@ -251,22 +276,9 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
        output->encoder.possible_crtcs = 0x3;
 
        return 0;
-
-free_hpd:
-       gpio_free(output->hpd_gpio);
-
-       return err;
 }
 
 int tegra_output_exit(struct tegra_output *output)
 {
-       if (gpio_is_valid(output->hpd_gpio)) {
-               free_irq(output->hpd_irq, output);
-               gpio_free(output->hpd_gpio);
-       }
-
-       if (output->ddc)
-               put_device(&output->ddc->dev);
-
        return 0;
 }
similarity index 96%
rename from drivers/gpu/host1x/drm/rgb.c
rename to drivers/gpu/drm/tegra/rgb.c
index 5aa66ef..ba47ca4 100644 (file)
@@ -8,9 +8,6 @@
  */
 
 #include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
 
 #include "drm.h"
 #include "dc.h"
@@ -150,7 +147,7 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
        rgb->output.dev = dc->dev;
        rgb->output.of_node = np;
 
-       err = tegra_output_parse_dt(&rgb->output);
+       err = tegra_output_probe(&rgb->output);
        if (err < 0)
                return err;
 
@@ -177,6 +174,20 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
        return 0;
 }
 
+int tegra_dc_rgb_remove(struct tegra_dc *dc)
+{
+       int err;
+
+       if (!dc->rgb)
+               return 0;
+
+       err = tegra_output_remove(dc->rgb);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
 int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 {
        struct tegra_rgb *rgb = to_rgb(dc->rgb);
index 7a4d101..7c3ef79 100644 (file)
@@ -2,6 +2,7 @@ config DRM_TILCDC
        tristate "DRM Support for TI LCDC Display Controller"
        depends on DRM && OF && ARM
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select DRM_KMS_CMA_HELPER
        select DRM_GEM_CMA_HELPER
        select VIDEOMODE_HELPERS
index b2b33dd..b433b9f 100644 (file)
@@ -5,10 +5,6 @@ ccflags-y := -Iinclude/drm
 ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \
        ttm_bo_util.o ttm_bo_vm.o ttm_module.o \
        ttm_object.o ttm_lock.o ttm_execbuf_util.o ttm_page_alloc.o \
-       ttm_bo_manager.o
-
-ifeq ($(CONFIG_SWIOTLB),y)
-ttm-y += ttm_page_alloc_dma.o
-endif
+       ttm_bo_manager.o ttm_page_alloc_dma.o
 
 obj-$(CONFIG_DRM_TTM) += ttm.o
index f1a857e..8d5a646 100644 (file)
@@ -429,8 +429,20 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)
                sync_obj = driver->sync_obj_ref(bo->sync_obj);
        spin_unlock(&bdev->fence_lock);
 
-       if (!ret)
+       if (!ret) {
+
+               /*
+                * Make NO_EVICT bos immediately available to
+                * shrinkers, now that they are queued for
+                * destruction.
+                */
+               if (bo->mem.placement & TTM_PL_FLAG_NO_EVICT) {
+                       bo->mem.placement &= ~TTM_PL_FLAG_NO_EVICT;
+                       ttm_bo_add_to_lru(bo);
+               }
+
                ww_mutex_unlock(&bo->resv->lock);
+       }
 
        kref_get(&bo->list_kref);
        list_add_tail(&bo->ddestroy, &bdev->ddestroy);
@@ -986,24 +998,32 @@ out_unlock:
        return ret;
 }
 
-static int ttm_bo_mem_compat(struct ttm_placement *placement,
-                            struct ttm_mem_reg *mem)
+static bool ttm_bo_mem_compat(struct ttm_placement *placement,
+                             struct ttm_mem_reg *mem,
+                             uint32_t *new_flags)
 {
        int i;
 
        if (mem->mm_node && placement->lpfn != 0 &&
            (mem->start < placement->fpfn ||
             mem->start + mem->num_pages > placement->lpfn))
-               return -1;
+               return false;
 
        for (i = 0; i < placement->num_placement; i++) {
-               if ((placement->placement[i] & mem->placement &
-                       TTM_PL_MASK_CACHING) &&
-                       (placement->placement[i] & mem->placement &
-                       TTM_PL_MASK_MEM))
-                       return i;
+               *new_flags = placement->placement[i];
+               if ((*new_flags & mem->placement & TTM_PL_MASK_CACHING) &&
+                   (*new_flags & mem->placement & TTM_PL_MASK_MEM))
+                       return true;
        }
-       return -1;
+
+       for (i = 0; i < placement->num_busy_placement; i++) {
+               *new_flags = placement->busy_placement[i];
+               if ((*new_flags & mem->placement & TTM_PL_MASK_CACHING) &&
+                   (*new_flags & mem->placement & TTM_PL_MASK_MEM))
+                       return true;
+       }
+
+       return false;
 }
 
 int ttm_bo_validate(struct ttm_buffer_object *bo,
@@ -1012,6 +1032,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,
                        bool no_wait_gpu)
 {
        int ret;
+       uint32_t new_flags;
 
        lockdep_assert_held(&bo->resv->lock.base);
        /* Check that range is valid */
@@ -1022,8 +1043,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,
        /*
         * Check whether we need to move buffer.
         */
-       ret = ttm_bo_mem_compat(placement, &bo->mem);
-       if (ret < 0) {
+       if (!ttm_bo_mem_compat(placement, &bo->mem, &new_flags)) {
                ret = ttm_bo_move_buffer(bo, placement, interruptible,
                                         no_wait_gpu);
                if (ret)
@@ -1033,7 +1053,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,
                 * Use the access and other non-mapping-related flag bits from
                 * the compatible memory placement flags to the active flags
                 */
-               ttm_flag_masked(&bo->mem.placement, placement->placement[ret],
+               ttm_flag_masked(&bo->mem.placement, new_flags,
                                ~TTM_PL_MASK_MEMTYPE);
        }
        /*
index 7cc904d..4834c46 100644 (file)
@@ -343,19 +343,25 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
        if (ret)
                goto out;
 
+       /*
+        * Single TTM move. NOP.
+        */
        if (old_iomap == NULL && new_iomap == NULL)
                goto out2;
+
+       /*
+        * Move nonexistent data. NOP.
+        */
        if (old_iomap == NULL && ttm == NULL)
                goto out2;
 
-       if (ttm->state == tt_unpopulated) {
+       /*
+        * TTM might be null for moves within the same region.
+        */
+       if (ttm && ttm->state == tt_unpopulated) {
                ret = ttm->bdev->driver->ttm_tt_populate(ttm);
-               if (ret) {
-                       /* if we fail here don't nuke the mm node
-                        * as the bo still owns it */
-                       old_copy.mm_node = NULL;
+               if (ret)
                        goto out1;
-               }
        }
 
        add = 0;
@@ -381,11 +387,8 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
                                                   prot);
                } else
                        ret = ttm_copy_io_page(new_iomap, old_iomap, page);
-               if (ret) {
-                       /* failing here, means keep old copy as-is */
-                       old_copy.mm_node = NULL;
+               if (ret)
                        goto out1;
-               }
        }
        mb();
 out2:
@@ -403,7 +406,12 @@ out1:
        ttm_mem_reg_iounmap(bdev, old_mem, new_iomap);
 out:
        ttm_mem_reg_iounmap(bdev, &old_copy, old_iomap);
-       ttm_bo_mem_put(bo, &old_copy);
+
+       /*
+        * On error, keep the mm node!
+        */
+       if (!ret)
+               ttm_bo_mem_put(bo, &old_copy);
        return ret;
 }
 EXPORT_SYMBOL(ttm_bo_move_memcpy);
index 1006c15..ac617f3 100644 (file)
 
 #define TTM_BO_VM_NUM_PREFAULT 16
 
+static int ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo,
+                               struct vm_area_struct *vma,
+                               struct vm_fault *vmf)
+{
+       struct ttm_bo_device *bdev = bo->bdev;
+       int ret = 0;
+
+       spin_lock(&bdev->fence_lock);
+       if (likely(!test_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags)))
+               goto out_unlock;
+
+       /*
+        * Quick non-stalling check for idle.
+        */
+       ret = ttm_bo_wait(bo, false, false, true);
+       if (likely(ret == 0))
+               goto out_unlock;
+
+       /*
+        * If possible, avoid waiting for GPU with mmap_sem
+        * held.
+        */
+       if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) {
+               ret = VM_FAULT_RETRY;
+               if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT)
+                       goto out_unlock;
+
+               up_read(&vma->vm_mm->mmap_sem);
+               (void) ttm_bo_wait(bo, false, true, false);
+               goto out_unlock;
+       }
+
+       /*
+        * Ordinary wait.
+        */
+       ret = ttm_bo_wait(bo, false, true, false);
+       if (unlikely(ret != 0))
+               ret = (ret != -ERESTARTSYS) ? VM_FAULT_SIGBUS :
+                       VM_FAULT_NOPAGE;
+
+out_unlock:
+       spin_unlock(&bdev->fence_lock);
+       return ret;
+}
+
 static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
        struct ttm_buffer_object *bo = (struct ttm_buffer_object *)
@@ -57,6 +102,7 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        int retval = VM_FAULT_NOPAGE;
        struct ttm_mem_type_manager *man =
                &bdev->man[bo->mem.mem_type];
+       struct vm_area_struct cvma;
 
        /*
         * Work around locking order reversal in fault / nopfn
@@ -91,18 +137,11 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
         * Wait for buffer data in transit, due to a pipelined
         * move.
         */
-
-       spin_lock(&bdev->fence_lock);
-       if (test_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags)) {
-               ret = ttm_bo_wait(bo, false, true, false);
-               spin_unlock(&bdev->fence_lock);
-               if (unlikely(ret != 0)) {
-                       retval = (ret != -ERESTARTSYS) ?
-                           VM_FAULT_SIGBUS : VM_FAULT_NOPAGE;
-                       goto out_unlock;
-               }
-       } else
-               spin_unlock(&bdev->fence_lock);
+       ret = ttm_bo_vm_fault_idle(bo, vma, vmf);
+       if (unlikely(ret != 0)) {
+               retval = ret;
+               goto out_unlock;
+       }
 
        ret = ttm_mem_io_lock(man, true);
        if (unlikely(ret != 0)) {
@@ -126,26 +165,21 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        }
 
        /*
-        * Strictly, we're not allowed to modify vma->vm_page_prot here,
-        * since the mmap_sem is only held in read mode. However, we
-        * modify only the caching bits of vma->vm_page_prot and
-        * consider those bits protected by
-        * the bo->mutex, as we should be the only writers.
-        * There shouldn't really be any readers of these bits except
-        * within vm_insert_mixed()? fork?
-        *
-        * TODO: Add a list of vmas to the bo, and change the
-        * vma->vm_page_prot when the object changes caching policy, with
-        * the correct locks held.
+        * Make a local vma copy to modify the page_prot member
+        * and vm_flags if necessary. The vma parameter is protected
+        * by mmap_sem in write mode.
         */
+       cvma = *vma;
+       cvma.vm_page_prot = vm_get_page_prot(cvma.vm_flags);
+
        if (bo->mem.bus.is_iomem) {
-               vma->vm_page_prot = ttm_io_prot(bo->mem.placement,
-                                               vma->vm_page_prot);
+               cvma.vm_page_prot = ttm_io_prot(bo->mem.placement,
+                                               cvma.vm_page_prot);
        } else {
                ttm = bo->ttm;
-               vma->vm_page_prot = (bo->mem.placement & TTM_PL_FLAG_CACHED) ?
-                   vm_get_page_prot(vma->vm_flags) :
-                   ttm_io_prot(bo->mem.placement, vma->vm_page_prot);
+               if (!(bo->mem.placement & TTM_PL_FLAG_CACHED))
+                       cvma.vm_page_prot = ttm_io_prot(bo->mem.placement,
+                                                       cvma.vm_page_prot);
 
                /* Allocate all page at once, most common usage */
                if (ttm->bdev->driver->ttm_tt_populate(ttm)) {
@@ -172,7 +206,7 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
                        pfn = page_to_pfn(page);
                }
 
-               ret = vm_insert_mixed(vma, address, pfn);
+               ret = vm_insert_mixed(&cvma, address, pfn);
                /*
                 * Somebody beat us to this PTE or prefaulting to
                 * an already populated PTE, or prefaulting error.
index 7957bee..fb8259f 100644 (file)
@@ -33,6 +33,7 @@
  *   when freed).
  */
 
+#if defined(CONFIG_SWIOTLB) || defined(CONFIG_INTEL_IOMMU)
 #define pr_fmt(fmt) "[TTM] " fmt
 
 #include <linux/dma-mapping.h>
@@ -1142,3 +1143,5 @@ int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data)
        return 0;
 }
 EXPORT_SYMBOL_GPL(ttm_dma_page_alloc_debugfs);
+
+#endif
index 6222af1..f025286 100644 (file)
@@ -8,6 +8,7 @@ config DRM_UDL
        select FB_SYS_IMAGEBLIT
        select FB_DEFERRED_IO
        select DRM_KMS_HELPER
+        select DRM_KMS_FB_HELPER
        help
          This is a KMS driver for the USB displaylink video adapters.
           Say M/Y to add support for these devices via drm/kms interfaces.
index 7650dc0..3ddd6cd 100644 (file)
@@ -77,7 +77,6 @@ static struct drm_driver driver = {
        .unload = udl_driver_unload,
 
        /* gem hooks */
-       .gem_init_object = udl_gem_init_object,
        .gem_free_object = udl_gem_free_object,
        .gem_vm_ops = &udl_gem_vm_ops,
 
index 56aec94..1fbf7b3 100644 (file)
@@ -115,7 +115,6 @@ int udl_dumb_create(struct drm_file *file_priv,
 int udl_gem_mmap(struct drm_file *file_priv, struct drm_device *dev,
                 uint32_t handle, uint64_t *offset);
 
-int udl_gem_init_object(struct drm_gem_object *obj);
 void udl_gem_free_object(struct drm_gem_object *gem_obj);
 struct udl_gem_object *udl_gem_alloc_object(struct drm_device *dev,
                                            size_t size);
index 8bf6461..24ffbe9 100644 (file)
@@ -107,13 +107,6 @@ int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        }
 }
 
-int udl_gem_init_object(struct drm_gem_object *obj)
-{
-       BUG();
-
-       return 0;
-}
-
 static int udl_gem_get_pages(struct udl_gem_object *obj, gfp_t gfpmask)
 {
        struct page **pages;
index 7e3ad87..9278891 100644 (file)
@@ -79,7 +79,7 @@ int via_final_context(struct drm_device *dev, int context)
 
        /* Linux specific until context tracking code gets ported to BSD */
        /* Last context, perform cleanup */
-       if (dev->ctx_count == 1 && dev->dev_private) {
+       if (list_is_singular(&dev->ctxlist) && dev->dev_private) {
                DRM_DEBUG("Last Context\n");
                drm_irq_uninstall(dev);
                via_cleanup_futex(dev_priv);
index 96dc84d..7776e6f 100644 (file)
@@ -141,37 +141,374 @@ struct ttm_placement vmw_srf_placement = {
 };
 
 struct vmw_ttm_tt {
-       struct ttm_tt ttm;
+       struct ttm_dma_tt dma_ttm;
        struct vmw_private *dev_priv;
        int gmr_id;
+       struct sg_table sgt;
+       struct vmw_sg_table vsgt;
+       uint64_t sg_alloc_size;
+       bool mapped;
 };
 
+/**
+ * Helper functions to advance a struct vmw_piter iterator.
+ *
+ * @viter: Pointer to the iterator.
+ *
+ * These functions return false if past the end of the list,
+ * true otherwise. Functions are selected depending on the current
+ * DMA mapping mode.
+ */
+static bool __vmw_piter_non_sg_next(struct vmw_piter *viter)
+{
+       return ++(viter->i) < viter->num_pages;
+}
+
+static bool __vmw_piter_sg_next(struct vmw_piter *viter)
+{
+       return __sg_page_iter_next(&viter->iter);
+}
+
+
+/**
+ * Helper functions to return a pointer to the current page.
+ *
+ * @viter: Pointer to the iterator
+ *
+ * These functions return a pointer to the page currently
+ * pointed to by @viter. Functions are selected depending on the
+ * current mapping mode.
+ */
+static struct page *__vmw_piter_non_sg_page(struct vmw_piter *viter)
+{
+       return viter->pages[viter->i];
+}
+
+static struct page *__vmw_piter_sg_page(struct vmw_piter *viter)
+{
+       return sg_page_iter_page(&viter->iter);
+}
+
+
+/**
+ * Helper functions to return the DMA address of the current page.
+ *
+ * @viter: Pointer to the iterator
+ *
+ * These functions return the DMA address of the page currently
+ * pointed to by @viter. Functions are selected depending on the
+ * current mapping mode.
+ */
+static dma_addr_t __vmw_piter_phys_addr(struct vmw_piter *viter)
+{
+       return page_to_phys(viter->pages[viter->i]);
+}
+
+static dma_addr_t __vmw_piter_dma_addr(struct vmw_piter *viter)
+{
+       return viter->addrs[viter->i];
+}
+
+static dma_addr_t __vmw_piter_sg_addr(struct vmw_piter *viter)
+{
+       return sg_page_iter_dma_address(&viter->iter);
+}
+
+
+/**
+ * vmw_piter_start - Initialize a struct vmw_piter.
+ *
+ * @viter: Pointer to the iterator to initialize
+ * @vsgt: Pointer to a struct vmw_sg_table to initialize from
+ *
+ * Note that we're following the convention of __sg_page_iter_start, so that
+ * the iterator doesn't point to a valid page after initialization; it has
+ * to be advanced one step first.
+ */
+void vmw_piter_start(struct vmw_piter *viter, const struct vmw_sg_table *vsgt,
+                    unsigned long p_offset)
+{
+       viter->i = p_offset - 1;
+       viter->num_pages = vsgt->num_pages;
+       switch (vsgt->mode) {
+       case vmw_dma_phys:
+               viter->next = &__vmw_piter_non_sg_next;
+               viter->dma_address = &__vmw_piter_phys_addr;
+               viter->page = &__vmw_piter_non_sg_page;
+               viter->pages = vsgt->pages;
+               break;
+       case vmw_dma_alloc_coherent:
+               viter->next = &__vmw_piter_non_sg_next;
+               viter->dma_address = &__vmw_piter_dma_addr;
+               viter->page = &__vmw_piter_non_sg_page;
+               viter->addrs = vsgt->addrs;
+               break;
+       case vmw_dma_map_populate:
+       case vmw_dma_map_bind:
+               viter->next = &__vmw_piter_sg_next;
+               viter->dma_address = &__vmw_piter_sg_addr;
+               viter->page = &__vmw_piter_sg_page;
+               __sg_page_iter_start(&viter->iter, vsgt->sgt->sgl,
+                                    vsgt->sgt->orig_nents, p_offset);
+               break;
+       default:
+               BUG();
+       }
+}
+
+/**
+ * vmw_ttm_unmap_from_dma - unmap  device addresses previsouly mapped for
+ * TTM pages
+ *
+ * @vmw_tt: Pointer to a struct vmw_ttm_backend
+ *
+ * Used to free dma mappings previously mapped by vmw_ttm_map_for_dma.
+ */
+static void vmw_ttm_unmap_from_dma(struct vmw_ttm_tt *vmw_tt)
+{
+       struct device *dev = vmw_tt->dev_priv->dev->dev;
+
+       dma_unmap_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.nents,
+               DMA_BIDIRECTIONAL);
+       vmw_tt->sgt.nents = vmw_tt->sgt.orig_nents;
+}
+
+/**
+ * vmw_ttm_map_for_dma - map TTM pages to get device addresses
+ *
+ * @vmw_tt: Pointer to a struct vmw_ttm_backend
+ *
+ * This function is used to get device addresses from the kernel DMA layer.
+ * However, it's violating the DMA API in that when this operation has been
+ * performed, it's illegal for the CPU to write to the pages without first
+ * unmapping the DMA mappings, or calling dma_sync_sg_for_cpu(). It is
+ * therefore only legal to call this function if we know that the function
+ * dma_sync_sg_for_cpu() is a NOP, and dma_sync_sg_for_device() is at most
+ * a CPU write buffer flush.
+ */
+static int vmw_ttm_map_for_dma(struct vmw_ttm_tt *vmw_tt)
+{
+       struct device *dev = vmw_tt->dev_priv->dev->dev;
+       int ret;
+
+       ret = dma_map_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.orig_nents,
+                        DMA_BIDIRECTIONAL);
+       if (unlikely(ret == 0))
+               return -ENOMEM;
+
+       vmw_tt->sgt.nents = ret;
+
+       return 0;
+}
+
+/**
+ * vmw_ttm_map_dma - Make sure TTM pages are visible to the device
+ *
+ * @vmw_tt: Pointer to a struct vmw_ttm_tt
+ *
+ * Select the correct function for and make sure the TTM pages are
+ * visible to the device. Allocate storage for the device mappings.
+ * If a mapping has already been performed, indicated by the storage
+ * pointer being non NULL, the function returns success.
+ */
+static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt)
+{
+       struct vmw_private *dev_priv = vmw_tt->dev_priv;
+       struct ttm_mem_global *glob = vmw_mem_glob(dev_priv);
+       struct vmw_sg_table *vsgt = &vmw_tt->vsgt;
+       struct vmw_piter iter;
+       dma_addr_t old;
+       int ret = 0;
+       static size_t sgl_size;
+       static size_t sgt_size;
+
+       if (vmw_tt->mapped)
+               return 0;
+
+       vsgt->mode = dev_priv->map_mode;
+       vsgt->pages = vmw_tt->dma_ttm.ttm.pages;
+       vsgt->num_pages = vmw_tt->dma_ttm.ttm.num_pages;
+       vsgt->addrs = vmw_tt->dma_ttm.dma_address;
+       vsgt->sgt = &vmw_tt->sgt;
+
+       switch (dev_priv->map_mode) {
+       case vmw_dma_map_bind:
+       case vmw_dma_map_populate:
+               if (unlikely(!sgl_size)) {
+                       sgl_size = ttm_round_pot(sizeof(struct scatterlist));
+                       sgt_size = ttm_round_pot(sizeof(struct sg_table));
+               }
+               vmw_tt->sg_alloc_size = sgt_size + sgl_size * vsgt->num_pages;
+               ret = ttm_mem_global_alloc(glob, vmw_tt->sg_alloc_size, false,
+                                          true);
+               if (unlikely(ret != 0))
+                       return ret;
+
+               ret = sg_alloc_table_from_pages(&vmw_tt->sgt, vsgt->pages,
+                                               vsgt->num_pages, 0,
+                                               (unsigned long)
+                                               vsgt->num_pages << PAGE_SHIFT,
+                                               GFP_KERNEL);
+               if (unlikely(ret != 0))
+                       goto out_sg_alloc_fail;
+
+               if (vsgt->num_pages > vmw_tt->sgt.nents) {
+                       uint64_t over_alloc =
+                               sgl_size * (vsgt->num_pages -
+                                           vmw_tt->sgt.nents);
+
+                       ttm_mem_global_free(glob, over_alloc);
+                       vmw_tt->sg_alloc_size -= over_alloc;
+               }
+
+               ret = vmw_ttm_map_for_dma(vmw_tt);
+               if (unlikely(ret != 0))
+                       goto out_map_fail;
+
+               break;
+       default:
+               break;
+       }
+
+       old = ~((dma_addr_t) 0);
+       vmw_tt->vsgt.num_regions = 0;
+       for (vmw_piter_start(&iter, vsgt, 0); vmw_piter_next(&iter);) {
+               dma_addr_t cur = vmw_piter_dma_addr(&iter);
+
+               if (cur != old + PAGE_SIZE)
+                       vmw_tt->vsgt.num_regions++;
+               old = cur;
+       }
+
+       vmw_tt->mapped = true;
+       return 0;
+
+out_map_fail:
+       sg_free_table(vmw_tt->vsgt.sgt);
+       vmw_tt->vsgt.sgt = NULL;
+out_sg_alloc_fail:
+       ttm_mem_global_free(glob, vmw_tt->sg_alloc_size);
+       return ret;
+}
+
+/**
+ * vmw_ttm_unmap_dma - Tear down any TTM page device mappings
+ *
+ * @vmw_tt: Pointer to a struct vmw_ttm_tt
+ *
+ * Tear down any previously set up device DMA mappings and free
+ * any storage space allocated for them. If there are no mappings set up,
+ * this function is a NOP.
+ */
+static void vmw_ttm_unmap_dma(struct vmw_ttm_tt *vmw_tt)
+{
+       struct vmw_private *dev_priv = vmw_tt->dev_priv;
+
+       if (!vmw_tt->vsgt.sgt)
+               return;
+
+       switch (dev_priv->map_mode) {
+       case vmw_dma_map_bind:
+       case vmw_dma_map_populate:
+               vmw_ttm_unmap_from_dma(vmw_tt);
+               sg_free_table(vmw_tt->vsgt.sgt);
+               vmw_tt->vsgt.sgt = NULL;
+               ttm_mem_global_free(vmw_mem_glob(dev_priv),
+                                   vmw_tt->sg_alloc_size);
+               break;
+       default:
+               break;
+       }
+       vmw_tt->mapped = false;
+}
+
 static int vmw_ttm_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem)
 {
-       struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm);
+       struct vmw_ttm_tt *vmw_be =
+               container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm);
+       int ret;
+
+       ret = vmw_ttm_map_dma(vmw_be);
+       if (unlikely(ret != 0))
+               return ret;
 
        vmw_be->gmr_id = bo_mem->start;
 
-       return vmw_gmr_bind(vmw_be->dev_priv, ttm->pages,
+       return vmw_gmr_bind(vmw_be->dev_priv, &vmw_be->vsgt,
                            ttm->num_pages, vmw_be->gmr_id);
 }
 
 static int vmw_ttm_unbind(struct ttm_tt *ttm)
 {
-       struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm);
+       struct vmw_ttm_tt *vmw_be =
+               container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm);
 
        vmw_gmr_unbind(vmw_be->dev_priv, vmw_be->gmr_id);
+
+       if (vmw_be->dev_priv->map_mode == vmw_dma_map_bind)
+               vmw_ttm_unmap_dma(vmw_be);
+
        return 0;
 }
 
 static void vmw_ttm_destroy(struct ttm_tt *ttm)
 {
-       struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm);
-
-       ttm_tt_fini(ttm);
+       struct vmw_ttm_tt *vmw_be =
+               container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm);
+
+       vmw_ttm_unmap_dma(vmw_be);
+       if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent)
+               ttm_dma_tt_fini(&vmw_be->dma_ttm);
+       else
+               ttm_tt_fini(ttm);
        kfree(vmw_be);
 }
 
+static int vmw_ttm_populate(struct ttm_tt *ttm)
+{
+       struct vmw_ttm_tt *vmw_tt =
+               container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm);
+       struct vmw_private *dev_priv = vmw_tt->dev_priv;
+       struct ttm_mem_global *glob = vmw_mem_glob(dev_priv);
+       int ret;
+
+       if (ttm->state != tt_unpopulated)
+               return 0;
+
+       if (dev_priv->map_mode == vmw_dma_alloc_coherent) {
+               size_t size =
+                       ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t));
+               ret = ttm_mem_global_alloc(glob, size, false, true);
+               if (unlikely(ret != 0))
+                       return ret;
+
+               ret = ttm_dma_populate(&vmw_tt->dma_ttm, dev_priv->dev->dev);
+               if (unlikely(ret != 0))
+                       ttm_mem_global_free(glob, size);
+       } else
+               ret = ttm_pool_populate(ttm);
+
+       return ret;
+}
+
+static void vmw_ttm_unpopulate(struct ttm_tt *ttm)
+{
+       struct vmw_ttm_tt *vmw_tt = container_of(ttm, struct vmw_ttm_tt,
+                                                dma_ttm.ttm);
+       struct vmw_private *dev_priv = vmw_tt->dev_priv;
+       struct ttm_mem_global *glob = vmw_mem_glob(dev_priv);
+
+       vmw_ttm_unmap_dma(vmw_tt);
+       if (dev_priv->map_mode == vmw_dma_alloc_coherent) {
+               size_t size =
+                       ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t));
+
+               ttm_dma_unpopulate(&vmw_tt->dma_ttm, dev_priv->dev->dev);
+               ttm_mem_global_free(glob, size);
+       } else
+               ttm_pool_unpopulate(ttm);
+}
+
 static struct ttm_backend_func vmw_ttm_func = {
        .bind = vmw_ttm_bind,
        .unbind = vmw_ttm_unbind,
@@ -183,20 +520,28 @@ struct ttm_tt *vmw_ttm_tt_create(struct ttm_bo_device *bdev,
                                 struct page *dummy_read_page)
 {
        struct vmw_ttm_tt *vmw_be;
+       int ret;
 
-       vmw_be = kmalloc(sizeof(*vmw_be), GFP_KERNEL);
+       vmw_be = kzalloc(sizeof(*vmw_be), GFP_KERNEL);
        if (!vmw_be)
                return NULL;
 
-       vmw_be->ttm.func = &vmw_ttm_func;
+       vmw_be->dma_ttm.ttm.func = &vmw_ttm_func;
        vmw_be->dev_priv = container_of(bdev, struct vmw_private, bdev);
 
-       if (ttm_tt_init(&vmw_be->ttm, bdev, size, page_flags, dummy_read_page)) {
-               kfree(vmw_be);
-               return NULL;
-       }
-
-       return &vmw_be->ttm;
+       if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent)
+               ret = ttm_dma_tt_init(&vmw_be->dma_ttm, bdev, size, page_flags,
+                                     dummy_read_page);
+       else
+               ret = ttm_tt_init(&vmw_be->dma_ttm.ttm, bdev, size, page_flags,
+                                 dummy_read_page);
+       if (unlikely(ret != 0))
+               goto out_no_init;
+
+       return &vmw_be->dma_ttm.ttm;
+out_no_init:
+       kfree(vmw_be);
+       return NULL;
 }
 
 int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
@@ -332,8 +677,8 @@ static int vmw_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible)
 
 struct ttm_bo_driver vmw_bo_driver = {
        .ttm_tt_create = &vmw_ttm_tt_create,
-       .ttm_tt_populate = &ttm_pool_populate,
-       .ttm_tt_unpopulate = &ttm_pool_unpopulate,
+       .ttm_tt_populate = &vmw_ttm_populate,
+       .ttm_tt_unpopulate = &vmw_ttm_unpopulate,
        .invalidate_caches = vmw_invalidate_caches,
        .init_mem_type = vmw_init_mem_type,
        .evict_flags = vmw_evict_flags,
index 0508f93..20d5485 100644 (file)
@@ -32,6 +32,7 @@
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/ttm/ttm_object.h>
 #include <drm/ttm/ttm_module.h>
+#include <linux/dma_remapping.h>
 
 #define VMWGFX_DRIVER_NAME "vmwgfx"
 #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices"
@@ -185,6 +186,9 @@ static struct pci_device_id vmw_pci_id_list[] = {
 MODULE_DEVICE_TABLE(pci, vmw_pci_id_list);
 
 static int enable_fbdev = IS_ENABLED(CONFIG_DRM_VMWGFX_FBCON);
+static int vmw_force_iommu;
+static int vmw_restrict_iommu;
+static int vmw_force_coherent;
 
 static int vmw_probe(struct pci_dev *, const struct pci_device_id *);
 static void vmw_master_init(struct vmw_master *);
@@ -193,6 +197,13 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
 
 MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev");
 module_param_named(enable_fbdev, enable_fbdev, int, 0600);
+MODULE_PARM_DESC(force_dma_api, "Force using the DMA API for TTM pages");
+module_param_named(force_dma_api, vmw_force_iommu, int, 0600);
+MODULE_PARM_DESC(restrict_iommu, "Try to limit IOMMU usage for TTM pages");
+module_param_named(restrict_iommu, vmw_restrict_iommu, int, 0600);
+MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages");
+module_param_named(force_coherent, vmw_force_coherent, int, 0600);
+
 
 static void vmw_print_capabilities(uint32_t capabilities)
 {
@@ -427,12 +438,85 @@ static void vmw_get_initial_size(struct vmw_private *dev_priv)
        dev_priv->initial_height = height;
 }
 
+/**
+ * vmw_dma_select_mode - Determine how DMA mappings should be set up for this
+ * system.
+ *
+ * @dev_priv: Pointer to a struct vmw_private
+ *
+ * This functions tries to determine the IOMMU setup and what actions
+ * need to be taken by the driver to make system pages visible to the
+ * device.
+ * If this function decides that DMA is not possible, it returns -EINVAL.
+ * The driver may then try to disable features of the device that require
+ * DMA.
+ */
+static int vmw_dma_select_mode(struct vmw_private *dev_priv)
+{
+       static const char *names[vmw_dma_map_max] = {
+               [vmw_dma_phys] = "Using physical TTM page addresses.",
+               [vmw_dma_alloc_coherent] = "Using coherent TTM pages.",
+               [vmw_dma_map_populate] = "Keeping DMA mappings.",
+               [vmw_dma_map_bind] = "Giving up DMA mappings early."};
+#ifdef CONFIG_X86
+       const struct dma_map_ops *dma_ops = get_dma_ops(dev_priv->dev->dev);
+
+#ifdef CONFIG_INTEL_IOMMU
+       if (intel_iommu_enabled) {
+               dev_priv->map_mode = vmw_dma_map_populate;
+               goto out_fixup;
+       }
+#endif
+
+       if (!(vmw_force_iommu || vmw_force_coherent)) {
+               dev_priv->map_mode = vmw_dma_phys;
+               DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]);
+               return 0;
+       }
+
+       dev_priv->map_mode = vmw_dma_map_populate;
+
+       if (dma_ops->sync_single_for_cpu)
+               dev_priv->map_mode = vmw_dma_alloc_coherent;
+#ifdef CONFIG_SWIOTLB
+       if (swiotlb_nr_tbl() == 0)
+               dev_priv->map_mode = vmw_dma_map_populate;
+#endif
+
+#ifdef CONFIG_INTEL_IOMMU
+out_fixup:
+#endif
+       if (dev_priv->map_mode == vmw_dma_map_populate &&
+           vmw_restrict_iommu)
+               dev_priv->map_mode = vmw_dma_map_bind;
+
+       if (vmw_force_coherent)
+               dev_priv->map_mode = vmw_dma_alloc_coherent;
+
+#if !defined(CONFIG_SWIOTLB) && !defined(CONFIG_INTEL_IOMMU)
+       /*
+        * No coherent page pool
+        */
+       if (dev_priv->map_mode == vmw_dma_alloc_coherent)
+               return -EINVAL;
+#endif
+
+#else /* CONFIG_X86 */
+       dev_priv->map_mode = vmw_dma_map_populate;
+#endif /* CONFIG_X86 */
+
+       DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]);
+
+       return 0;
+}
+
 static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
 {
        struct vmw_private *dev_priv;
        int ret;
        uint32_t svga_id;
        enum vmw_res_type i;
+       bool refuse_dma = false;
 
        dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
        if (unlikely(dev_priv == NULL)) {
@@ -481,6 +565,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
        }
 
        dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES);
+       ret = vmw_dma_select_mode(dev_priv);
+       if (unlikely(ret != 0)) {
+               DRM_INFO("Restricting capabilities due to IOMMU setup.\n");
+               refuse_dma = true;
+       }
 
        dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE);
        dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE);
@@ -558,8 +647,9 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
        }
 
        dev_priv->has_gmr = true;
-       if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR,
-                          dev_priv->max_gmr_ids) != 0) {
+       if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) ||
+           refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR,
+                                        dev_priv->max_gmr_ids) != 0) {
                DRM_INFO("No GMR memory available. "
                         "Graphics memory resources are very limited.\n");
                dev_priv->has_gmr = false;
index 150ec64..e401d5d 100644 (file)
@@ -177,6 +177,58 @@ struct vmw_res_cache_entry {
        struct vmw_resource_val_node *node;
 };
 
+/**
+ * enum vmw_dma_map_mode - indicate how to perform TTM page dma mappings.
+ */
+enum vmw_dma_map_mode {
+       vmw_dma_phys,           /* Use physical page addresses */
+       vmw_dma_alloc_coherent, /* Use TTM coherent pages */
+       vmw_dma_map_populate,   /* Unmap from DMA just after unpopulate */
+       vmw_dma_map_bind,       /* Unmap from DMA just before unbind */
+       vmw_dma_map_max
+};
+
+/**
+ * struct vmw_sg_table - Scatter/gather table for binding, with additional
+ * device-specific information.
+ *
+ * @sgt: Pointer to a struct sg_table with binding information
+ * @num_regions: Number of regions with device-address contigous pages
+ */
+struct vmw_sg_table {
+       enum vmw_dma_map_mode mode;
+       struct page **pages;
+       const dma_addr_t *addrs;
+       struct sg_table *sgt;
+       unsigned long num_regions;
+       unsigned long num_pages;
+};
+
+/**
+ * struct vmw_piter - Page iterator that iterates over a list of pages
+ * and DMA addresses that could be either a scatter-gather list or
+ * arrays
+ *
+ * @pages: Array of page pointers to the pages.
+ * @addrs: DMA addresses to the pages if coherent pages are used.
+ * @iter: Scatter-gather page iterator. Current position in SG list.
+ * @i: Current position in arrays.
+ * @num_pages: Number of pages total.
+ * @next: Function to advance the iterator. Returns false if past the list
+ * of pages, true otherwise.
+ * @dma_address: Function to return the DMA address of the current page.
+ */
+struct vmw_piter {
+       struct page **pages;
+       const dma_addr_t *addrs;
+       struct sg_page_iter iter;
+       unsigned long i;
+       unsigned long num_pages;
+       bool (*next)(struct vmw_piter *);
+       dma_addr_t (*dma_address)(struct vmw_piter *);
+       struct page *(*page)(struct vmw_piter *);
+};
+
 struct vmw_sw_context{
        struct drm_open_hash res_ht;
        bool res_ht_initialized;
@@ -358,6 +410,11 @@ struct vmw_private {
 
        struct list_head res_lru[vmw_res_max];
        uint32_t used_memory_size;
+
+       /*
+        * DMA mapping stuff.
+        */
+       enum vmw_dma_map_mode map_mode;
 };
 
 static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res)
@@ -405,7 +462,7 @@ void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga);
  */
 
 extern int vmw_gmr_bind(struct vmw_private *dev_priv,
-                       struct page *pages[],
+                       const struct vmw_sg_table *vsgt,
                        unsigned long num_pages,
                        int gmr_id);
 extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id);
@@ -568,6 +625,45 @@ extern struct ttm_placement vmw_evictable_placement;
 extern struct ttm_placement vmw_srf_placement;
 extern struct ttm_bo_driver vmw_bo_driver;
 extern int vmw_dma_quiescent(struct drm_device *dev);
+extern void vmw_piter_start(struct vmw_piter *viter,
+                           const struct vmw_sg_table *vsgt,
+                           unsigned long p_offs);
+
+/**
+ * vmw_piter_next - Advance the iterator one page.
+ *
+ * @viter: Pointer to the iterator to advance.
+ *
+ * Returns false if past the list of pages, true otherwise.
+ */
+static inline bool vmw_piter_next(struct vmw_piter *viter)
+{
+       return viter->next(viter);
+}
+
+/**
+ * vmw_piter_dma_addr - Return the DMA address of the current page.
+ *
+ * @viter: Pointer to the iterator
+ *
+ * Returns the DMA address of the page pointed to by @viter.
+ */
+static inline dma_addr_t vmw_piter_dma_addr(struct vmw_piter *viter)
+{
+       return viter->dma_address(viter);
+}
+
+/**
+ * vmw_piter_page - Return a pointer to the current page.
+ *
+ * @viter: Pointer to the iterator
+ *
+ * Returns the DMA address of the page pointed to by @viter.
+ */
+static inline struct page *vmw_piter_page(struct vmw_piter *viter)
+{
+       return viter->page(viter);
+}
 
 /**
  * Command submission - vmwgfx_execbuf.c
index 1a0bf07..6ef0b03 100644 (file)
 #define VMW_PPN_SIZE (sizeof(unsigned long))
 /* A future safe maximum remap size. */
 #define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE)
+#define DMA_ADDR_INVALID ((dma_addr_t) 0)
+#define DMA_PAGE_INVALID 0UL
 
 static int vmw_gmr2_bind(struct vmw_private *dev_priv,
-                        struct page *pages[],
+                        struct vmw_piter *iter,
                         unsigned long num_pages,
                         int gmr_id)
 {
@@ -81,11 +83,13 @@ static int vmw_gmr2_bind(struct vmw_private *dev_priv,
 
                for (i = 0; i < nr; ++i) {
                        if (VMW_PPN_SIZE <= 4)
-                               *cmd = page_to_pfn(*pages++);
+                               *cmd = vmw_piter_dma_addr(iter) >> PAGE_SHIFT;
                        else
-                               *((uint64_t *)cmd) = page_to_pfn(*pages++);
+                               *((uint64_t *)cmd) = vmw_piter_dma_addr(iter) >>
+                                       PAGE_SHIFT;
 
                        cmd += VMW_PPN_SIZE / sizeof(*cmd);
+                       vmw_piter_next(iter);
                }
 
                num_pages -= nr;
@@ -120,22 +124,56 @@ static void vmw_gmr2_unbind(struct vmw_private *dev_priv,
        vmw_fifo_commit(dev_priv, define_size);
 }
 
+
+static void vmw_gmr_free_descriptors(struct device *dev, dma_addr_t desc_dma,
+                                    struct list_head *desc_pages)
+{
+       struct page *page, *next;
+       struct svga_guest_mem_descriptor *page_virtual;
+       unsigned int desc_per_page = PAGE_SIZE /
+               sizeof(struct svga_guest_mem_descriptor) - 1;
+
+       if (list_empty(desc_pages))
+               return;
+
+       list_for_each_entry_safe(page, next, desc_pages, lru) {
+               list_del_init(&page->lru);
+
+               if (likely(desc_dma != DMA_ADDR_INVALID)) {
+                       dma_unmap_page(dev, desc_dma, PAGE_SIZE,
+                                      DMA_TO_DEVICE);
+               }
+
+               page_virtual = kmap_atomic(page);
+               desc_dma = (dma_addr_t)
+                       le32_to_cpu(page_virtual[desc_per_page].ppn) <<
+                       PAGE_SHIFT;
+               kunmap_atomic(page_virtual);
+
+               __free_page(page);
+       }
+}
+
 /**
  * FIXME: Adjust to the ttm lowmem / highmem storage to minimize
  * the number of used descriptors.
+ *
  */
 
-static int vmw_gmr_build_descriptors(struct list_head *desc_pages,
-                                    struct page *pages[],
-                                    unsigned long num_pages)
+static int vmw_gmr_build_descriptors(struct device *dev,
+                                    struct list_head *desc_pages,
+                                    struct vmw_piter *iter,
+                                    unsigned long num_pages,
+                                    dma_addr_t *first_dma)
 {
-       struct page *page, *next;
+       struct page *page;
        struct svga_guest_mem_descriptor *page_virtual = NULL;
        struct svga_guest_mem_descriptor *desc_virtual = NULL;
        unsigned int desc_per_page;
        unsigned long prev_pfn;
        unsigned long pfn;
        int ret;
+       dma_addr_t desc_dma;
 
        desc_per_page = PAGE_SIZE /
            sizeof(struct svga_guest_mem_descriptor) - 1;
@@ -148,23 +186,12 @@ static int vmw_gmr_build_descriptors(struct list_head *desc_pages,
                }
 
                list_add_tail(&page->lru, desc_pages);
-
-               /*
-                * Point previous page terminating descriptor to this
-                * page before unmapping it.
-                */
-
-               if (likely(page_virtual != NULL)) {
-                       desc_virtual->ppn = page_to_pfn(page);
-                       kunmap_atomic(page_virtual);
-               }
-
                page_virtual = kmap_atomic(page);
                desc_virtual = page_virtual - 1;
                prev_pfn = ~(0UL);
 
                while (likely(num_pages != 0)) {
-                       pfn = page_to_pfn(*pages);
+                       pfn = vmw_piter_dma_addr(iter) >> PAGE_SHIFT;
 
                        if (pfn != prev_pfn + 1) {
 
@@ -181,104 +208,82 @@ static int vmw_gmr_build_descriptors(struct list_head *desc_pages,
                        }
                        prev_pfn = pfn;
                        --num_pages;
-                       ++pages;
+                       vmw_piter_next(iter);
                }
 
-               (++desc_virtual)->ppn = cpu_to_le32(0);
+               (++desc_virtual)->ppn = DMA_PAGE_INVALID;
                desc_virtual->num_pages = cpu_to_le32(0);
+               kunmap_atomic(page_virtual);
        }
 
-       if (likely(page_virtual != NULL))
+       desc_dma = 0;
+       list_for_each_entry_reverse(page, desc_pages, lru) {
+               page_virtual = kmap_atomic(page);
+               page_virtual[desc_per_page].ppn = cpu_to_le32
+                       (desc_dma >> PAGE_SHIFT);
                kunmap_atomic(page_virtual);
+               desc_dma = dma_map_page(dev, page, 0, PAGE_SIZE,
+                                       DMA_TO_DEVICE);
+
+               if (unlikely(dma_mapping_error(dev, desc_dma)))
+                       goto out_err;
+       }
+       *first_dma = desc_dma;
 
        return 0;
 out_err:
-       list_for_each_entry_safe(page, next, desc_pages, lru) {
-               list_del_init(&page->lru);
-               __free_page(page);
-       }
+       vmw_gmr_free_descriptors(dev, DMA_ADDR_INVALID, desc_pages);
        return ret;
 }
 
-static inline void vmw_gmr_free_descriptors(struct list_head *desc_pages)
-{
-       struct page *page, *next;
-
-       list_for_each_entry_safe(page, next, desc_pages, lru) {
-               list_del_init(&page->lru);
-               __free_page(page);
-       }
-}
-
 static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv,
-                                    int gmr_id, struct list_head *desc_pages)
+                                    int gmr_id, dma_addr_t desc_dma)
 {
-       struct page *page;
-
-       if (unlikely(list_empty(desc_pages)))
-               return;
-
-       page = list_entry(desc_pages->next, struct page, lru);
-
        mutex_lock(&dev_priv->hw_mutex);
 
        vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id);
        wmb();
-       vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, page_to_pfn(page));
+       vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, desc_dma >> PAGE_SHIFT);
        mb();
 
        mutex_unlock(&dev_priv->hw_mutex);
 
 }
 
-/**
- * FIXME: Adjust to the ttm lowmem / highmem storage to minimize
- * the number of used descriptors.
- */
-
-static unsigned long vmw_gmr_count_descriptors(struct page *pages[],
-                                       unsigned long num_pages)
-{
-       unsigned long prev_pfn = ~(0UL);
-       unsigned long pfn;
-       unsigned long descriptors = 0;
-
-       while (num_pages--) {
-               pfn = page_to_pfn(*pages++);
-               if (prev_pfn + 1 != pfn)
-                       ++descriptors;
-               prev_pfn = pfn;
-       }
-
-       return descriptors;
-}
-
 int vmw_gmr_bind(struct vmw_private *dev_priv,
-                struct page *pages[],
+                const struct vmw_sg_table *vsgt,
                 unsigned long num_pages,
                 int gmr_id)
 {
        struct list_head desc_pages;
+       dma_addr_t desc_dma = 0;
+       struct device *dev = dev_priv->dev->dev;
+       struct vmw_piter data_iter;
        int ret;
 
+       vmw_piter_start(&data_iter, vsgt, 0);
+
+       if (unlikely(!vmw_piter_next(&data_iter)))
+               return 0;
+
        if (likely(dev_priv->capabilities & SVGA_CAP_GMR2))
-               return vmw_gmr2_bind(dev_priv, pages, num_pages, gmr_id);
+               return vmw_gmr2_bind(dev_priv, &data_iter, num_pages, gmr_id);
 
        if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR)))
                return -EINVAL;
 
-       if (vmw_gmr_count_descriptors(pages, num_pages) >
-           dev_priv->max_gmr_descriptors)
+       if (vsgt->num_regions > dev_priv->max_gmr_descriptors)
                return -EINVAL;
 
        INIT_LIST_HEAD(&desc_pages);
 
-       ret = vmw_gmr_build_descriptors(&desc_pages, pages, num_pages);
+       ret = vmw_gmr_build_descriptors(dev, &desc_pages, &data_iter,
+                                       num_pages, &desc_dma);
        if (unlikely(ret != 0))
                return ret;
 
-       vmw_gmr_fire_descriptors(dev_priv, gmr_id, &desc_pages);
-       vmw_gmr_free_descriptors(&desc_pages);
+       vmw_gmr_fire_descriptors(dev_priv, gmr_id, desc_dma);
+       vmw_gmr_free_descriptors(dev, desc_dma, &desc_pages);
 
        return 0;
 }
index c509d40..a51f48e 100644 (file)
@@ -168,7 +168,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data,
        fb = drm_framebuffer_lookup(dev, arg->fb_id);
        if (!fb) {
                DRM_ERROR("Invalid framebuffer id.\n");
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out_no_fb;
        }
        vfb = vmw_framebuffer_to_vfb(fb);
@@ -252,7 +252,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
        fb = drm_framebuffer_lookup(dev, arg->fb_id);
        if (!fb) {
                DRM_ERROR("Invalid framebuffer id.\n");
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out_no_fb;
        }
 
index fc43c06..ecb3d86 100644 (file)
@@ -1508,7 +1508,7 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
 
        obj = drm_mode_object_find(dev, arg->crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj) {
-               ret = -EINVAL;
+               ret = -ENOENT;
                goto out;
        }
 
index 37fb4be..252501a 100644 (file)
@@ -32,6 +32,8 @@
 #include <drm/drmP.h>
 #include "vmwgfx_resource_priv.h"
 
+#define VMW_RES_EVICT_ERR_COUNT 10
+
 struct vmw_user_dma_buffer {
        struct ttm_base_object base;
        struct vmw_dma_buffer dma;
@@ -1091,8 +1093,9 @@ vmw_resource_backoff_reservation(struct ww_acquire_ctx *ticket,
  *                         to a backup buffer.
  *
  * @res:            The resource to evict.
+ * @interruptible:  Whether to wait interruptible.
  */
-int vmw_resource_do_evict(struct vmw_resource *res)
+int vmw_resource_do_evict(struct vmw_resource *res, bool interruptible)
 {
        struct ttm_validate_buffer val_buf;
        const struct vmw_res_func *func = res->func;
@@ -1102,7 +1105,8 @@ int vmw_resource_do_evict(struct vmw_resource *res)
        BUG_ON(!func->may_evict);
 
        val_buf.bo = NULL;
-       ret = vmw_resource_check_buffer(res, &ticket, true, &val_buf);
+       ret = vmw_resource_check_buffer(res, &ticket, interruptible,
+                                       &val_buf);
        if (unlikely(ret != 0))
                return ret;
 
@@ -1141,6 +1145,7 @@ int vmw_resource_validate(struct vmw_resource *res)
        struct vmw_private *dev_priv = res->dev_priv;
        struct list_head *lru_list = &dev_priv->res_lru[res->func->res_type];
        struct ttm_validate_buffer val_buf;
+       unsigned err_count = 0;
 
        if (likely(!res->func->may_evict))
                return 0;
@@ -1155,7 +1160,7 @@ int vmw_resource_validate(struct vmw_resource *res)
 
                write_lock(&dev_priv->resource_lock);
                if (list_empty(lru_list) || !res->func->may_evict) {
-                       DRM_ERROR("Out of device device id entries "
+                       DRM_ERROR("Out of device device resources "
                                  "for %s.\n", res->func->type_name);
                        ret = -EBUSY;
                        write_unlock(&dev_priv->resource_lock);
@@ -1168,7 +1173,19 @@ int vmw_resource_validate(struct vmw_resource *res)
                list_del_init(&evict_res->lru_head);
 
                write_unlock(&dev_priv->resource_lock);
-               vmw_resource_do_evict(evict_res);
+
+               ret = vmw_resource_do_evict(evict_res, true);
+               if (unlikely(ret != 0)) {
+                       write_lock(&dev_priv->resource_lock);
+                       list_add_tail(&evict_res->lru_head, lru_list);
+                       write_unlock(&dev_priv->resource_lock);
+                       if (ret == -ERESTARTSYS ||
+                           ++err_count > VMW_RES_EVICT_ERR_COUNT) {
+                               vmw_resource_unreference(&evict_res);
+                               goto out_no_validate;
+                       }
+               }
+
                vmw_resource_unreference(&evict_res);
        } while (1);
 
@@ -1253,13 +1270,15 @@ bool vmw_resource_needs_backup(const struct vmw_resource *res)
  * @type:           The resource type to evict
  *
  * To avoid thrashing starvation or as part of the hibernation sequence,
- * evict all evictable resources of a specific type.
+ * try to evict all evictable resources of a specific type.
  */
 static void vmw_resource_evict_type(struct vmw_private *dev_priv,
                                    enum vmw_res_type type)
 {
        struct list_head *lru_list = &dev_priv->res_lru[type];
        struct vmw_resource *evict_res;
+       unsigned err_count = 0;
+       int ret;
 
        do {
                write_lock(&dev_priv->resource_lock);
@@ -1272,7 +1291,18 @@ static void vmw_resource_evict_type(struct vmw_private *dev_priv,
                                         lru_head));
                list_del_init(&evict_res->lru_head);
                write_unlock(&dev_priv->resource_lock);
-               vmw_resource_do_evict(evict_res);
+
+               ret = vmw_resource_do_evict(evict_res, false);
+               if (unlikely(ret != 0)) {
+                       write_lock(&dev_priv->resource_lock);
+                       list_add_tail(&evict_res->lru_head, lru_list);
+                       write_unlock(&dev_priv->resource_lock);
+                       if (++err_count > VMW_RES_EVICT_ERR_COUNT) {
+                               vmw_resource_unreference(&evict_res);
+                               return;
+                       }
+               }
+
                vmw_resource_unreference(&evict_res);
        } while (1);
 
index ccfd42b..7d6bed2 100644 (file)
@@ -19,6 +19,4 @@ config TEGRA_HOST1X_FIREWALL
 
          If unsure, choose Y.
 
-source "drivers/gpu/host1x/drm/Kconfig"
-
 endif
index 3b037b6..afa1e9e 100644 (file)
@@ -1,6 +1,5 @@
-ccflags-y = -Idrivers/gpu/host1x
-
 host1x-y = \
+       bus.o \
        syncpt.o \
        dev.o \
        intr.o \
@@ -8,13 +7,7 @@ host1x-y = \
        channel.o \
        job.o \
        debug.o \
-       hw/host1x01.o
-
-ccflags-y += -Iinclude/drm
-ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
+       hw/host1x01.o \
+       hw/host1x02.o
 
-host1x-$(CONFIG_DRM_TEGRA) += drm/drm.o drm/fb.o drm/dc.o
-host1x-$(CONFIG_DRM_TEGRA) += drm/output.o drm/rgb.o drm/hdmi.o
-host1x-$(CONFIG_DRM_TEGRA) += drm/gem.o
-host1x-$(CONFIG_DRM_TEGRA) += drm/gr2d.o
 obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
new file mode 100644 (file)
index 0000000..509383f
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ * Copyright (C) 2012-2013, NVIDIA Corporation
+ *
+ * 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/host1x.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include "dev.h"
+
+static DEFINE_MUTEX(clients_lock);
+static LIST_HEAD(clients);
+
+static DEFINE_MUTEX(drivers_lock);
+static LIST_HEAD(drivers);
+
+static DEFINE_MUTEX(devices_lock);
+static LIST_HEAD(devices);
+
+struct host1x_subdev {
+       struct host1x_client *client;
+       struct device_node *np;
+       struct list_head list;
+};
+
+/**
+ * host1x_subdev_add() - add a new subdevice with an associated device node
+ */
+static int host1x_subdev_add(struct host1x_device *device,
+                            struct device_node *np)
+{
+       struct host1x_subdev *subdev;
+
+       subdev = kzalloc(sizeof(*subdev), GFP_KERNEL);
+       if (!subdev)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&subdev->list);
+       subdev->np = of_node_get(np);
+
+       mutex_lock(&device->subdevs_lock);
+       list_add_tail(&subdev->list, &device->subdevs);
+       mutex_unlock(&device->subdevs_lock);
+
+       return 0;
+}
+
+/**
+ * host1x_subdev_del() - remove subdevice
+ */
+static void host1x_subdev_del(struct host1x_subdev *subdev)
+{
+       list_del(&subdev->list);
+       of_node_put(subdev->np);
+       kfree(subdev);
+}
+
+/**
+ * host1x_device_parse_dt() - scan device tree and add matching subdevices
+ */
+static int host1x_device_parse_dt(struct host1x_device *device)
+{
+       struct device_node *np;
+       int err;
+
+       for_each_child_of_node(device->dev.parent->of_node, np) {
+               if (of_match_node(device->driver->subdevs, np) &&
+                   of_device_is_available(np)) {
+                       err = host1x_subdev_add(device, np);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
+       return 0;
+}
+
+static void host1x_subdev_register(struct host1x_device *device,
+                                  struct host1x_subdev *subdev,
+                                  struct host1x_client *client)
+{
+       int err;
+
+       /*
+        * Move the subdevice to the list of active (registered) subdevices
+        * and associate it with a client. At the same time, associate the
+        * client with its parent device.
+        */
+       mutex_lock(&device->subdevs_lock);
+       mutex_lock(&device->clients_lock);
+       list_move_tail(&client->list, &device->clients);
+       list_move_tail(&subdev->list, &device->active);
+       client->parent = &device->dev;
+       subdev->client = client;
+       mutex_unlock(&device->clients_lock);
+       mutex_unlock(&device->subdevs_lock);
+
+       /*
+        * When all subdevices have been registered, the composite device is
+        * ready to be probed.
+        */
+       if (list_empty(&device->subdevs)) {
+               err = device->driver->probe(device);
+               if (err < 0)
+                       dev_err(&device->dev, "probe failed: %d\n", err);
+       }
+}
+
+static void __host1x_subdev_unregister(struct host1x_device *device,
+                                      struct host1x_subdev *subdev)
+{
+       struct host1x_client *client = subdev->client;
+       int err;
+
+       /*
+        * If all subdevices have been activated, we're about to remove the
+        * first active subdevice, so unload the driver first.
+        */
+       if (list_empty(&device->subdevs)) {
+               err = device->driver->remove(device);
+               if (err < 0)
+                       dev_err(&device->dev, "remove failed: %d\n", err);
+       }
+
+       /*
+        * Move the subdevice back to the list of idle subdevices and remove
+        * it from list of clients.
+        */
+       mutex_lock(&device->clients_lock);
+       subdev->client = NULL;
+       client->parent = NULL;
+       list_move_tail(&subdev->list, &device->subdevs);
+       /*
+        * XXX: Perhaps don't do this here, but rather explicitly remove it
+        * when the device is about to be deleted.
+        *
+        * This is somewhat complicated by the fact that this function is
+        * used to remove the subdevice when a client is unregistered but
+        * also when the composite device is about to be removed.
+        */
+       list_del_init(&client->list);
+       mutex_unlock(&device->clients_lock);
+}
+
+static void host1x_subdev_unregister(struct host1x_device *device,
+                                    struct host1x_subdev *subdev)
+{
+       mutex_lock(&device->subdevs_lock);
+       __host1x_subdev_unregister(device, subdev);
+       mutex_unlock(&device->subdevs_lock);
+}
+
+int host1x_device_init(struct host1x_device *device)
+{
+       struct host1x_client *client;
+       int err;
+
+       mutex_lock(&device->clients_lock);
+
+       list_for_each_entry(client, &device->clients, list) {
+               if (client->ops && client->ops->init) {
+                       err = client->ops->init(client);
+                       if (err < 0) {
+                               dev_err(&device->dev,
+                                       "failed to initialize %s: %d\n",
+                                       dev_name(client->dev), err);
+                               mutex_unlock(&device->clients_lock);
+                               return err;
+                       }
+               }
+       }
+
+       mutex_unlock(&device->clients_lock);
+
+       return 0;
+}
+
+int host1x_device_exit(struct host1x_device *device)
+{
+       struct host1x_client *client;
+       int err;
+
+       mutex_lock(&device->clients_lock);
+
+       list_for_each_entry_reverse(client, &device->clients, list) {
+               if (client->ops && client->ops->exit) {
+                       err = client->ops->exit(client);
+                       if (err < 0) {
+                               dev_err(&device->dev,
+                                       "failed to cleanup %s: %d\n",
+                                       dev_name(client->dev), err);
+                               mutex_unlock(&device->clients_lock);
+                               return err;
+                       }
+               }
+       }
+
+       mutex_unlock(&device->clients_lock);
+
+       return 0;
+}
+
+static int host1x_register_client(struct host1x *host1x,
+                                 struct host1x_client *client)
+{
+       struct host1x_device *device;
+       struct host1x_subdev *subdev;
+
+       mutex_lock(&host1x->devices_lock);
+
+       list_for_each_entry(device, &host1x->devices, list) {
+               list_for_each_entry(subdev, &device->subdevs, list) {
+                       if (subdev->np == client->dev->of_node) {
+                               host1x_subdev_register(device, subdev, client);
+                               mutex_unlock(&host1x->devices_lock);
+                               return 0;
+                       }
+               }
+       }
+
+       mutex_unlock(&host1x->devices_lock);
+       return -ENODEV;
+}
+
+static int host1x_unregister_client(struct host1x *host1x,
+                                   struct host1x_client *client)
+{
+       struct host1x_device *device, *dt;
+       struct host1x_subdev *subdev;
+
+       mutex_lock(&host1x->devices_lock);
+
+       list_for_each_entry_safe(device, dt, &host1x->devices, list) {
+               list_for_each_entry(subdev, &device->active, list) {
+                       if (subdev->client == client) {
+                               host1x_subdev_unregister(device, subdev);
+                               mutex_unlock(&host1x->devices_lock);
+                               return 0;
+                       }
+               }
+       }
+
+       mutex_unlock(&host1x->devices_lock);
+       return -ENODEV;
+}
+
+struct bus_type host1x_bus_type = {
+       .name = "host1x",
+};
+
+int host1x_bus_init(void)
+{
+       return bus_register(&host1x_bus_type);
+}
+
+void host1x_bus_exit(void)
+{
+       bus_unregister(&host1x_bus_type);
+}
+
+static void host1x_device_release(struct device *dev)
+{
+       struct host1x_device *device = to_host1x_device(dev);
+
+       kfree(device);
+}
+
+static int host1x_device_add(struct host1x *host1x,
+                            struct host1x_driver *driver)
+{
+       struct host1x_client *client, *tmp;
+       struct host1x_subdev *subdev;
+       struct host1x_device *device;
+       int err;
+
+       device = kzalloc(sizeof(*device), GFP_KERNEL);
+       if (!device)
+               return -ENOMEM;
+
+       mutex_init(&device->subdevs_lock);
+       INIT_LIST_HEAD(&device->subdevs);
+       INIT_LIST_HEAD(&device->active);
+       mutex_init(&device->clients_lock);
+       INIT_LIST_HEAD(&device->clients);
+       INIT_LIST_HEAD(&device->list);
+       device->driver = driver;
+
+       device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask;
+       device->dev.dma_mask = &device->dev.coherent_dma_mask;
+       device->dev.release = host1x_device_release;
+       dev_set_name(&device->dev, driver->name);
+       device->dev.bus = &host1x_bus_type;
+       device->dev.parent = host1x->dev;
+
+       err = device_register(&device->dev);
+       if (err < 0)
+               return err;
+
+       err = host1x_device_parse_dt(device);
+       if (err < 0) {
+               device_unregister(&device->dev);
+               return err;
+       }
+
+       mutex_lock(&host1x->devices_lock);
+       list_add_tail(&device->list, &host1x->devices);
+       mutex_unlock(&host1x->devices_lock);
+
+       mutex_lock(&clients_lock);
+
+       list_for_each_entry_safe(client, tmp, &clients, list) {
+               list_for_each_entry(subdev, &device->subdevs, list) {
+                       if (subdev->np == client->dev->of_node) {
+                               host1x_subdev_register(device, subdev, client);
+                               break;
+                       }
+               }
+       }
+
+       mutex_unlock(&clients_lock);
+
+       return 0;
+}
+
+/*
+ * Removes a device by first unregistering any subdevices and then removing
+ * itself from the list of devices.
+ *
+ * This function must be called with the host1x->devices_lock held.
+ */
+static void host1x_device_del(struct host1x *host1x,
+                             struct host1x_device *device)
+{
+       struct host1x_subdev *subdev, *sd;
+       struct host1x_client *client, *cl;
+
+       mutex_lock(&device->subdevs_lock);
+
+       /* unregister subdevices */
+       list_for_each_entry_safe(subdev, sd, &device->active, list) {
+               /*
+                * host1x_subdev_unregister() will remove the client from
+                * any lists, so we'll need to manually add it back to the
+                * list of idle clients.
+                *
+                * XXX: Alternatively, perhaps don't remove the client from
+                * any lists in host1x_subdev_unregister() and instead do
+                * that explicitly from host1x_unregister_client()?
+                */
+               client = subdev->client;
+
+               __host1x_subdev_unregister(device, subdev);
+
+               /* add the client to the list of idle clients */
+               mutex_lock(&clients_lock);
+               list_add_tail(&client->list, &clients);
+               mutex_unlock(&clients_lock);
+       }
+
+       /* remove subdevices */
+       list_for_each_entry_safe(subdev, sd, &device->subdevs, list)
+               host1x_subdev_del(subdev);
+
+       mutex_unlock(&device->subdevs_lock);
+
+       /* move clients to idle list */
+       mutex_lock(&clients_lock);
+       mutex_lock(&device->clients_lock);
+
+       list_for_each_entry_safe(client, cl, &device->clients, list)
+               list_move_tail(&client->list, &clients);
+
+       mutex_unlock(&device->clients_lock);
+       mutex_unlock(&clients_lock);
+
+       /* finally remove the device */
+       list_del_init(&device->list);
+       device_unregister(&device->dev);
+}
+
+static void host1x_attach_driver(struct host1x *host1x,
+                                struct host1x_driver *driver)
+{
+       struct host1x_device *device;
+       int err;
+
+       mutex_lock(&host1x->devices_lock);
+
+       list_for_each_entry(device, &host1x->devices, list) {
+               if (device->driver == driver) {
+                       mutex_unlock(&host1x->devices_lock);
+                       return;
+               }
+       }
+
+       mutex_unlock(&host1x->devices_lock);
+
+       err = host1x_device_add(host1x, driver);
+       if (err < 0)
+               dev_err(host1x->dev, "failed to allocate device: %d\n", err);
+}
+
+static void host1x_detach_driver(struct host1x *host1x,
+                                struct host1x_driver *driver)
+{
+       struct host1x_device *device, *tmp;
+
+       mutex_lock(&host1x->devices_lock);
+
+       list_for_each_entry_safe(device, tmp, &host1x->devices, list)
+               if (device->driver == driver)
+                       host1x_device_del(host1x, device);
+
+       mutex_unlock(&host1x->devices_lock);
+}
+
+int host1x_register(struct host1x *host1x)
+{
+       struct host1x_driver *driver;
+
+       mutex_lock(&devices_lock);
+       list_add_tail(&host1x->list, &devices);
+       mutex_unlock(&devices_lock);
+
+       mutex_lock(&drivers_lock);
+
+       list_for_each_entry(driver, &drivers, list)
+               host1x_attach_driver(host1x, driver);
+
+       mutex_unlock(&drivers_lock);
+
+       return 0;
+}
+
+int host1x_unregister(struct host1x *host1x)
+{
+       struct host1x_driver *driver;
+
+       mutex_lock(&drivers_lock);
+
+       list_for_each_entry(driver, &drivers, list)
+               host1x_detach_driver(host1x, driver);
+
+       mutex_unlock(&drivers_lock);
+
+       mutex_lock(&devices_lock);
+       list_del_init(&host1x->list);
+       mutex_unlock(&devices_lock);
+
+       return 0;
+}
+
+int host1x_driver_register(struct host1x_driver *driver)
+{
+       struct host1x *host1x;
+
+       INIT_LIST_HEAD(&driver->list);
+
+       mutex_lock(&drivers_lock);
+       list_add_tail(&driver->list, &drivers);
+       mutex_unlock(&drivers_lock);
+
+       mutex_lock(&devices_lock);
+
+       list_for_each_entry(host1x, &devices, list)
+               host1x_attach_driver(host1x, driver);
+
+       mutex_unlock(&devices_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(host1x_driver_register);
+
+void host1x_driver_unregister(struct host1x_driver *driver)
+{
+       mutex_lock(&drivers_lock);
+       list_del_init(&driver->list);
+       mutex_unlock(&drivers_lock);
+}
+EXPORT_SYMBOL(host1x_driver_unregister);
+
+int host1x_client_register(struct host1x_client *client)
+{
+       struct host1x *host1x;
+       int err;
+
+       mutex_lock(&devices_lock);
+
+       list_for_each_entry(host1x, &devices, list) {
+               err = host1x_register_client(host1x, client);
+               if (!err) {
+                       mutex_unlock(&devices_lock);
+                       return 0;
+               }
+       }
+
+       mutex_unlock(&devices_lock);
+
+       mutex_lock(&clients_lock);
+       list_add_tail(&client->list, &clients);
+       mutex_unlock(&clients_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(host1x_client_register);
+
+int host1x_client_unregister(struct host1x_client *client)
+{
+       struct host1x_client *c;
+       struct host1x *host1x;
+       int err;
+
+       mutex_lock(&devices_lock);
+
+       list_for_each_entry(host1x, &devices, list) {
+               err = host1x_unregister_client(host1x, client);
+               if (!err) {
+                       mutex_unlock(&devices_lock);
+                       return 0;
+               }
+       }
+
+       mutex_unlock(&devices_lock);
+       mutex_lock(&clients_lock);
+
+       list_for_each_entry(c, &clients, list) {
+               if (c == client) {
+                       list_del_init(&c->list);
+                       break;
+               }
+       }
+
+       mutex_unlock(&clients_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(host1x_client_unregister);
similarity index 60%
rename from drivers/gpu/host1x/host1x_client.h
rename to drivers/gpu/host1x/bus.h
index 9b85f10..4099e99 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2013, NVIDIA Corporation.
+ * Copyright (C) 2012 Avionic Design GmbH
+ * Copyright (C) 2012-2013, NVIDIA Corporation
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef HOST1X_CLIENT_H
-#define HOST1X_CLIENT_H
+#ifndef HOST1X_BUS_H
+#define HOST1X_BUS_H
 
-struct device;
-struct platform_device;
+struct host1x;
 
-#ifdef CONFIG_DRM_TEGRA
-int host1x_drm_alloc(struct platform_device *pdev);
-#else
-static inline int host1x_drm_alloc(struct platform_device *pdev)
-{
-       return 0;
-}
-#endif
+int host1x_bus_init(void);
+void host1x_bus_exit(void);
 
-void host1x_set_drm_data(struct device *dev, void *data);
-void *host1x_get_drm_data(struct device *dev);
+int host1x_register(struct host1x *host1x);
+int host1x_unregister(struct host1x *host1x);
 
 #endif
index de72172..3995255 100644 (file)
@@ -20,6 +20,7 @@
 #include <asm/cacheflush.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
+#include <linux/host1x.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/kfifo.h>
@@ -30,7 +31,6 @@
 #include "channel.h"
 #include "dev.h"
 #include "debug.h"
-#include "host1x_bo.h"
 #include "job.h"
 
 /*
index 48723b8..df767cf 100644 (file)
@@ -40,12 +40,6 @@ struct host1x_channel {
 /* channel list operations */
 int host1x_channel_list_init(struct host1x *host);
 
-struct host1x_channel *host1x_channel_request(struct device *dev);
-void host1x_channel_free(struct host1x_channel *channel);
-struct host1x_channel *host1x_channel_get(struct host1x_channel *channel);
-void host1x_channel_put(struct host1x_channel *channel);
-int host1x_job_submit(struct host1x_job *job);
-
 #define host1x_for_each_channel(host, channel)                         \
        list_for_each_entry(channel, &host->chlist.list, list)
 
index 4716302..80da003 100644 (file)
 #define CREATE_TRACE_POINTS
 #include <trace/events/host1x.h>
 
+#include "bus.h"
 #include "dev.h"
 #include "intr.h"
 #include "channel.h"
 #include "debug.h"
 #include "hw/host1x01.h"
-#include "host1x_client.h"
-
-void host1x_set_drm_data(struct device *dev, void *data)
-{
-       struct host1x *host1x = dev_get_drvdata(dev);
-       host1x->drm_data = data;
-}
-
-void *host1x_get_drm_data(struct device *dev)
-{
-       struct host1x *host1x = dev_get_drvdata(dev);
-       return host1x ? host1x->drm_data : NULL;
-}
+#include "hw/host1x02.h"
 
 void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r)
 {
@@ -79,7 +68,17 @@ static const struct host1x_info host1x01_info = {
        .sync_offset    = 0x3000,
 };
 
+static const struct host1x_info host1x02_info = {
+       .nb_channels = 9,
+       .nb_pts = 32,
+       .nb_mlocks = 16,
+       .nb_bases = 12,
+       .init = host1x02_init,
+       .sync_offset = 0x3000,
+};
+
 static struct of_device_id host1x_of_match[] = {
+       { .compatible = "nvidia,tegra114-host1x", .data = &host1x02_info, },
        { .compatible = "nvidia,tegra30-host1x", .data = &host1x01_info, },
        { .compatible = "nvidia,tegra20-host1x", .data = &host1x01_info, },
        { },
@@ -114,6 +113,9 @@ static int host1x_probe(struct platform_device *pdev)
        if (!host)
                return -ENOMEM;
 
+       mutex_init(&host->devices_lock);
+       INIT_LIST_HEAD(&host->devices);
+       INIT_LIST_HEAD(&host->list);
        host->dev = &pdev->dev;
        host->info = id->data;
 
@@ -152,7 +154,7 @@ static int host1x_probe(struct platform_device *pdev)
        err = host1x_syncpt_init(host);
        if (err) {
                dev_err(&pdev->dev, "failed to initialize syncpts\n");
-               return err;
+               goto fail_unprepare_disable;
        }
 
        err = host1x_intr_init(host, syncpt_irq);
@@ -163,19 +165,26 @@ static int host1x_probe(struct platform_device *pdev)
 
        host1x_debug_init(host);
 
-       host1x_drm_alloc(pdev);
+       err = host1x_register(host);
+       if (err < 0)
+               goto fail_deinit_intr;
 
        return 0;
 
+fail_deinit_intr:
+       host1x_intr_deinit(host);
 fail_deinit_syncpt:
        host1x_syncpt_deinit(host);
+fail_unprepare_disable:
+       clk_disable_unprepare(host->clk);
        return err;
 }
 
-static int __exit host1x_remove(struct platform_device *pdev)
+static int host1x_remove(struct platform_device *pdev)
 {
        struct host1x *host = platform_get_drvdata(pdev);
 
+       host1x_unregister(host);
        host1x_intr_deinit(host);
        host1x_syncpt_deinit(host);
        clk_disable_unprepare(host->clk);
@@ -184,59 +193,36 @@ static int __exit host1x_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver tegra_host1x_driver = {
-       .probe = host1x_probe,
-       .remove = __exit_p(host1x_remove),
        .driver = {
-               .owner = THIS_MODULE,
                .name = "tegra-host1x",
                .of_match_table = host1x_of_match,
        },
+       .probe = host1x_probe,
+       .remove = host1x_remove,
 };
 
 static int __init tegra_host1x_init(void)
 {
        int err;
 
-       err = platform_driver_register(&tegra_host1x_driver);
+       err = host1x_bus_init();
        if (err < 0)
                return err;
 
-#ifdef CONFIG_DRM_TEGRA
-       err = platform_driver_register(&tegra_dc_driver);
-       if (err < 0)
-               goto unregister_host1x;
-
-       err = platform_driver_register(&tegra_hdmi_driver);
-       if (err < 0)
-               goto unregister_dc;
-
-       err = platform_driver_register(&tegra_gr2d_driver);
-       if (err < 0)
-               goto unregister_hdmi;
-#endif
+       err = platform_driver_register(&tegra_host1x_driver);
+       if (err < 0) {
+               host1x_bus_exit();
+               return err;
+       }
 
        return 0;
-
-#ifdef CONFIG_DRM_TEGRA
-unregister_hdmi:
-       platform_driver_unregister(&tegra_hdmi_driver);
-unregister_dc:
-       platform_driver_unregister(&tegra_dc_driver);
-unregister_host1x:
-       platform_driver_unregister(&tegra_host1x_driver);
-       return err;
-#endif
 }
 module_init(tegra_host1x_init);
 
 static void __exit tegra_host1x_exit(void)
 {
-#ifdef CONFIG_DRM_TEGRA
-       platform_driver_unregister(&tegra_gr2d_driver);
-       platform_driver_unregister(&tegra_hdmi_driver);
-       platform_driver_unregister(&tegra_dc_driver);
-#endif
        platform_driver_unregister(&tegra_host1x_driver);
+       host1x_bus_exit();
 }
 module_exit(tegra_host1x_exit);
 
index bed90a8..a61a976 100644 (file)
@@ -27,6 +27,7 @@
 #include "job.h"
 
 struct host1x_syncpt;
+struct host1x_syncpt_base;
 struct host1x_channel;
 struct host1x_cdma;
 struct host1x_job;
@@ -102,6 +103,7 @@ struct host1x {
 
        void __iomem *regs;
        struct host1x_syncpt *syncpt;
+       struct host1x_syncpt_base *bases;
        struct device *dev;
        struct clk *clk;
 
@@ -125,7 +127,10 @@ struct host1x {
 
        struct dentry *debugfs;
 
-       void *drm_data;
+       struct mutex devices_lock;
+       struct list_head devices;
+
+       struct list_head list;
 };
 
 void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v);
@@ -301,8 +306,4 @@ static inline void host1x_hw_show_mlocks(struct host1x *host, struct output *o)
        host->debug_op->show_mlocks(host, o);
 }
 
-extern struct platform_driver tegra_dc_driver;
-extern struct platform_driver tegra_hdmi_driver;
-extern struct platform_driver tegra_gr2d_driver;
-
 #endif
diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c
deleted file mode 100644 (file)
index 8c61cee..0000000
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Copyright (C) 2012 Avionic Design GmbH
- * Copyright (C) 2012-2013 NVIDIA CORPORATION.  All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-
-#include <linux/dma-mapping.h>
-#include <asm/dma-iommu.h>
-
-#include <drm/drm.h>
-#include <drm/drmP.h>
-
-#include "host1x_client.h"
-#include "dev.h"
-#include "drm.h"
-#include "gem.h"
-#include "syncpt.h"
-
-#define DRIVER_NAME "tegra"
-#define DRIVER_DESC "NVIDIA Tegra graphics"
-#define DRIVER_DATE "20120330"
-#define DRIVER_MAJOR 0
-#define DRIVER_MINOR 0
-#define DRIVER_PATCHLEVEL 0
-
-struct host1x_drm_client {
-       struct host1x_client *client;
-       struct device_node *np;
-       struct list_head list;
-};
-
-static int host1x_add_drm_client(struct host1x_drm *host1x,
-                                struct device_node *np)
-{
-       struct host1x_drm_client *client;
-
-       client = kzalloc(sizeof(*client), GFP_KERNEL);
-       if (!client)
-               return -ENOMEM;
-
-       INIT_LIST_HEAD(&client->list);
-       client->np = of_node_get(np);
-
-       list_add_tail(&client->list, &host1x->drm_clients);
-
-       return 0;
-}
-
-static int host1x_activate_drm_client(struct host1x_drm *host1x,
-                                     struct host1x_drm_client *drm,
-                                     struct host1x_client *client)
-{
-       mutex_lock(&host1x->drm_clients_lock);
-       list_del_init(&drm->list);
-       list_add_tail(&drm->list, &host1x->drm_active);
-       drm->client = client;
-       mutex_unlock(&host1x->drm_clients_lock);
-
-       return 0;
-}
-
-static int host1x_remove_drm_client(struct host1x_drm *host1x,
-                                   struct host1x_drm_client *client)
-{
-       mutex_lock(&host1x->drm_clients_lock);
-       list_del_init(&client->list);
-       mutex_unlock(&host1x->drm_clients_lock);
-
-       of_node_put(client->np);
-       kfree(client);
-
-       return 0;
-}
-
-static int host1x_parse_dt(struct host1x_drm *host1x)
-{
-       static const char * const compat[] = {
-               "nvidia,tegra20-dc",
-               "nvidia,tegra20-hdmi",
-               "nvidia,tegra20-gr2d",
-               "nvidia,tegra30-dc",
-               "nvidia,tegra30-hdmi",
-               "nvidia,tegra30-gr2d",
-       };
-       unsigned int i;
-       int err;
-
-       for (i = 0; i < ARRAY_SIZE(compat); i++) {
-               struct device_node *np;
-
-               for_each_child_of_node(host1x->dev->of_node, np) {
-                       if (of_device_is_compatible(np, compat[i]) &&
-                           of_device_is_available(np)) {
-                               err = host1x_add_drm_client(host1x, np);
-                               if (err < 0)
-                                       return err;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-int host1x_drm_alloc(struct platform_device *pdev)
-{
-       struct host1x_drm *host1x;
-       int err;
-
-       host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL);
-       if (!host1x)
-               return -ENOMEM;
-
-       mutex_init(&host1x->drm_clients_lock);
-       INIT_LIST_HEAD(&host1x->drm_clients);
-       INIT_LIST_HEAD(&host1x->drm_active);
-       mutex_init(&host1x->clients_lock);
-       INIT_LIST_HEAD(&host1x->clients);
-       host1x->dev = &pdev->dev;
-
-       err = host1x_parse_dt(host1x);
-       if (err < 0) {
-               dev_err(&pdev->dev, "failed to parse DT: %d\n", err);
-               return err;
-       }
-
-       host1x_set_drm_data(&pdev->dev, host1x);
-
-       return 0;
-}
-
-int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm)
-{
-       struct host1x_client *client;
-
-       mutex_lock(&host1x->clients_lock);
-
-       list_for_each_entry(client, &host1x->clients, list) {
-               if (client->ops && client->ops->drm_init) {
-                       int err = client->ops->drm_init(client, drm);
-                       if (err < 0) {
-                               dev_err(host1x->dev,
-                                       "DRM setup failed for %s: %d\n",
-                                       dev_name(client->dev), err);
-                               mutex_unlock(&host1x->clients_lock);
-                               return err;
-                       }
-               }
-       }
-
-       mutex_unlock(&host1x->clients_lock);
-
-       return 0;
-}
-
-int host1x_drm_exit(struct host1x_drm *host1x)
-{
-       struct platform_device *pdev = to_platform_device(host1x->dev);
-       struct host1x_client *client;
-
-       if (!host1x->drm)
-               return 0;
-
-       mutex_lock(&host1x->clients_lock);
-
-       list_for_each_entry_reverse(client, &host1x->clients, list) {
-               if (client->ops && client->ops->drm_exit) {
-                       int err = client->ops->drm_exit(client);
-                       if (err < 0) {
-                               dev_err(host1x->dev,
-                                       "DRM cleanup failed for %s: %d\n",
-                                       dev_name(client->dev), err);
-                               mutex_unlock(&host1x->clients_lock);
-                               return err;
-                       }
-               }
-       }
-
-       mutex_unlock(&host1x->clients_lock);
-
-       drm_platform_exit(&tegra_drm_driver, pdev);
-       host1x->drm = NULL;
-
-       return 0;
-}
-
-int host1x_register_client(struct host1x_drm *host1x,
-                          struct host1x_client *client)
-{
-       struct host1x_drm_client *drm, *tmp;
-       int err;
-
-       mutex_lock(&host1x->clients_lock);
-       list_add_tail(&client->list, &host1x->clients);
-       mutex_unlock(&host1x->clients_lock);
-
-       list_for_each_entry_safe(drm, tmp, &host1x->drm_clients, list)
-               if (drm->np == client->dev->of_node)
-                       host1x_activate_drm_client(host1x, drm, client);
-
-       if (list_empty(&host1x->drm_clients)) {
-               struct platform_device *pdev = to_platform_device(host1x->dev);
-
-               err = drm_platform_init(&tegra_drm_driver, pdev);
-               if (err < 0) {
-                       dev_err(host1x->dev, "drm_platform_init(): %d\n", err);
-                       return err;
-               }
-       }
-
-       return 0;
-}
-
-int host1x_unregister_client(struct host1x_drm *host1x,
-                            struct host1x_client *client)
-{
-       struct host1x_drm_client *drm, *tmp;
-       int err;
-
-       list_for_each_entry_safe(drm, tmp, &host1x->drm_active, list) {
-               if (drm->client == client) {
-                       err = host1x_drm_exit(host1x);
-                       if (err < 0) {
-                               dev_err(host1x->dev, "host1x_drm_exit(): %d\n",
-                                       err);
-                               return err;
-                       }
-
-                       host1x_remove_drm_client(host1x, drm);
-                       break;
-               }
-       }
-
-       mutex_lock(&host1x->clients_lock);
-       list_del_init(&client->list);
-       mutex_unlock(&host1x->clients_lock);
-
-       return 0;
-}
-
-static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
-{
-       struct host1x_drm *host1x;
-       int err;
-
-       host1x = host1x_get_drm_data(drm->dev);
-       drm->dev_private = host1x;
-       host1x->drm = drm;
-
-       drm_mode_config_init(drm);
-
-       err = host1x_drm_init(host1x, drm);
-       if (err < 0)
-               return err;
-
-       /*
-        * We don't use the drm_irq_install() helpers provided by the DRM
-        * core, so we need to set this manually in order to allow the
-        * DRM_IOCTL_WAIT_VBLANK to operate correctly.
-        */
-       drm->irq_enabled = 1;
-
-       err = drm_vblank_init(drm, drm->mode_config.num_crtc);
-       if (err < 0)
-               return err;
-
-       err = tegra_drm_fb_init(drm);
-       if (err < 0)
-               return err;
-
-       drm_kms_helper_poll_init(drm);
-
-       return 0;
-}
-
-static int tegra_drm_unload(struct drm_device *drm)
-{
-       drm_kms_helper_poll_fini(drm);
-       tegra_drm_fb_exit(drm);
-
-       drm_mode_config_cleanup(drm);
-
-       return 0;
-}
-
-static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
-{
-       struct host1x_drm_file *fpriv;
-
-       fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
-       if (!fpriv)
-               return -ENOMEM;
-
-       INIT_LIST_HEAD(&fpriv->contexts);
-       filp->driver_priv = fpriv;
-
-       return 0;
-}
-
-static void host1x_drm_context_free(struct host1x_drm_context *context)
-{
-       context->client->ops->close_channel(context);
-       kfree(context);
-}
-
-static void tegra_drm_lastclose(struct drm_device *drm)
-{
-       struct host1x_drm *host1x = drm->dev_private;
-
-       tegra_fbdev_restore_mode(host1x->fbdev);
-}
-
-#ifdef CONFIG_DRM_TEGRA_STAGING
-static bool host1x_drm_file_owns_context(struct host1x_drm_file *file,
-                                        struct host1x_drm_context *context)
-{
-       struct host1x_drm_context *ctx;
-
-       list_for_each_entry(ctx, &file->contexts, list)
-               if (ctx == context)
-                       return true;
-
-       return false;
-}
-
-static int tegra_gem_create(struct drm_device *drm, void *data,
-                           struct drm_file *file)
-{
-       struct drm_tegra_gem_create *args = data;
-       struct tegra_bo *bo;
-
-       bo = tegra_bo_create_with_handle(file, drm, args->size,
-                                        &args->handle);
-       if (IS_ERR(bo))
-               return PTR_ERR(bo);
-
-       return 0;
-}
-
-static int tegra_gem_mmap(struct drm_device *drm, void *data,
-                         struct drm_file *file)
-{
-       struct drm_tegra_gem_mmap *args = data;
-       struct drm_gem_object *gem;
-       struct tegra_bo *bo;
-
-       gem = drm_gem_object_lookup(drm, file, args->handle);
-       if (!gem)
-               return -EINVAL;
-
-       bo = to_tegra_bo(gem);
-
-       args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node);
-
-       drm_gem_object_unreference(gem);
-
-       return 0;
-}
-
-static int tegra_syncpt_read(struct drm_device *drm, void *data,
-                            struct drm_file *file)
-{
-       struct drm_tegra_syncpt_read *args = data;
-       struct host1x *host = dev_get_drvdata(drm->dev);
-       struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id);
-
-       if (!sp)
-               return -EINVAL;
-
-       args->value = host1x_syncpt_read_min(sp);
-       return 0;
-}
-
-static int tegra_syncpt_incr(struct drm_device *drm, void *data,
-                            struct drm_file *file)
-{
-       struct drm_tegra_syncpt_incr *args = data;
-       struct host1x *host = dev_get_drvdata(drm->dev);
-       struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id);
-
-       if (!sp)
-               return -EINVAL;
-
-       return host1x_syncpt_incr(sp);
-}
-
-static int tegra_syncpt_wait(struct drm_device *drm, void *data,
-                            struct drm_file *file)
-{
-       struct drm_tegra_syncpt_wait *args = data;
-       struct host1x *host = dev_get_drvdata(drm->dev);
-       struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id);
-
-       if (!sp)
-               return -EINVAL;
-
-       return host1x_syncpt_wait(sp, args->thresh, args->timeout,
-                                 &args->value);
-}
-
-static int tegra_open_channel(struct drm_device *drm, void *data,
-                             struct drm_file *file)
-{
-       struct drm_tegra_open_channel *args = data;
-       struct host1x_client *client;
-       struct host1x_drm_context *context;
-       struct host1x_drm_file *fpriv = file->driver_priv;
-       struct host1x_drm *host1x = drm->dev_private;
-       int err = -ENODEV;
-
-       context = kzalloc(sizeof(*context), GFP_KERNEL);
-       if (!context)
-               return -ENOMEM;
-
-       list_for_each_entry(client, &host1x->clients, list)
-               if (client->class == args->client) {
-                       err = client->ops->open_channel(client, context);
-                       if (err)
-                               break;
-
-                       context->client = client;
-                       list_add(&context->list, &fpriv->contexts);
-                       args->context = (uintptr_t)context;
-                       return 0;
-               }
-
-       kfree(context);
-       return err;
-}
-
-static int tegra_close_channel(struct drm_device *drm, void *data,
-                              struct drm_file *file)
-{
-       struct drm_tegra_close_channel *args = data;
-       struct host1x_drm_file *fpriv = file->driver_priv;
-       struct host1x_drm_context *context =
-               (struct host1x_drm_context *)(uintptr_t)args->context;
-
-       if (!host1x_drm_file_owns_context(fpriv, context))
-               return -EINVAL;
-
-       list_del(&context->list);
-       host1x_drm_context_free(context);
-
-       return 0;
-}
-
-static int tegra_get_syncpt(struct drm_device *drm, void *data,
-                           struct drm_file *file)
-{
-       struct drm_tegra_get_syncpt *args = data;
-       struct host1x_drm_file *fpriv = file->driver_priv;
-       struct host1x_drm_context *context =
-               (struct host1x_drm_context *)(uintptr_t)args->context;
-       struct host1x_syncpt *syncpt;
-
-       if (!host1x_drm_file_owns_context(fpriv, context))
-               return -ENODEV;
-
-       if (args->index >= context->client->num_syncpts)
-               return -EINVAL;
-
-       syncpt = context->client->syncpts[args->index];
-       args->id = host1x_syncpt_id(syncpt);
-
-       return 0;
-}
-
-static int tegra_submit(struct drm_device *drm, void *data,
-                       struct drm_file *file)
-{
-       struct drm_tegra_submit *args = data;
-       struct host1x_drm_file *fpriv = file->driver_priv;
-       struct host1x_drm_context *context =
-               (struct host1x_drm_context *)(uintptr_t)args->context;
-
-       if (!host1x_drm_file_owns_context(fpriv, context))
-               return -ENODEV;
-
-       return context->client->ops->submit(context, args, drm, file);
-}
-#endif
-
-static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
-#ifdef CONFIG_DRM_TEGRA_STAGING
-       DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_UNLOCKED | DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_UNLOCKED),
-       DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, DRM_UNLOCKED),
-       DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, DRM_UNLOCKED),
-       DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait, DRM_UNLOCKED),
-       DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel, DRM_UNLOCKED),
-       DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, DRM_UNLOCKED),
-       DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, DRM_UNLOCKED),
-       DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, DRM_UNLOCKED),
-#endif
-};
-
-static const struct file_operations tegra_drm_fops = {
-       .owner = THIS_MODULE,
-       .open = drm_open,
-       .release = drm_release,
-       .unlocked_ioctl = drm_ioctl,
-       .mmap = tegra_drm_mmap,
-       .poll = drm_poll,
-       .read = drm_read,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl = drm_compat_ioctl,
-#endif
-       .llseek = noop_llseek,
-};
-
-static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe)
-{
-       struct drm_crtc *crtc;
-
-       list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
-               struct tegra_dc *dc = to_tegra_dc(crtc);
-
-               if (dc->pipe == pipe)
-                       return crtc;
-       }
-
-       return NULL;
-}
-
-static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc)
-{
-       /* TODO: implement real hardware counter using syncpoints */
-       return drm_vblank_count(dev, crtc);
-}
-
-static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
-{
-       struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
-       struct tegra_dc *dc = to_tegra_dc(crtc);
-
-       if (!crtc)
-               return -ENODEV;
-
-       tegra_dc_enable_vblank(dc);
-
-       return 0;
-}
-
-static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe)
-{
-       struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
-       struct tegra_dc *dc = to_tegra_dc(crtc);
-
-       if (crtc)
-               tegra_dc_disable_vblank(dc);
-}
-
-static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
-{
-       struct host1x_drm_file *fpriv = file->driver_priv;
-       struct host1x_drm_context *context, *tmp;
-       struct drm_crtc *crtc;
-
-       list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
-               tegra_dc_cancel_page_flip(crtc, file);
-
-       list_for_each_entry_safe(context, tmp, &fpriv->contexts, list)
-               host1x_drm_context_free(context);
-
-       kfree(fpriv);
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
-{
-       struct drm_info_node *node = (struct drm_info_node *)s->private;
-       struct drm_device *drm = node->minor->dev;
-       struct drm_framebuffer *fb;
-
-       mutex_lock(&drm->mode_config.fb_lock);
-
-       list_for_each_entry(fb, &drm->mode_config.fb_list, head) {
-               seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n",
-                          fb->base.id, fb->width, fb->height, fb->depth,
-                          fb->bits_per_pixel,
-                          atomic_read(&fb->refcount.refcount));
-       }
-
-       mutex_unlock(&drm->mode_config.fb_lock);
-
-       return 0;
-}
-
-static struct drm_info_list tegra_debugfs_list[] = {
-       { "framebuffers", tegra_debugfs_framebuffers, 0 },
-};
-
-static int tegra_debugfs_init(struct drm_minor *minor)
-{
-       return drm_debugfs_create_files(tegra_debugfs_list,
-                                       ARRAY_SIZE(tegra_debugfs_list),
-                                       minor->debugfs_root, minor);
-}
-
-static void tegra_debugfs_cleanup(struct drm_minor *minor)
-{
-       drm_debugfs_remove_files(tegra_debugfs_list,
-                                ARRAY_SIZE(tegra_debugfs_list), minor);
-}
-#endif
-
-struct drm_driver tegra_drm_driver = {
-       .driver_features = DRIVER_MODESET | DRIVER_GEM,
-       .load = tegra_drm_load,
-       .unload = tegra_drm_unload,
-       .open = tegra_drm_open,
-       .preclose = tegra_drm_preclose,
-       .lastclose = tegra_drm_lastclose,
-
-       .get_vblank_counter = tegra_drm_get_vblank_counter,
-       .enable_vblank = tegra_drm_enable_vblank,
-       .disable_vblank = tegra_drm_disable_vblank,
-
-#if defined(CONFIG_DEBUG_FS)
-       .debugfs_init = tegra_debugfs_init,
-       .debugfs_cleanup = tegra_debugfs_cleanup,
-#endif
-
-       .gem_free_object = tegra_bo_free_object,
-       .gem_vm_ops = &tegra_bo_vm_ops,
-       .dumb_create = tegra_bo_dumb_create,
-       .dumb_map_offset = tegra_bo_dumb_map_offset,
-       .dumb_destroy = drm_gem_dumb_destroy,
-
-       .ioctls = tegra_drm_ioctls,
-       .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
-       .fops = &tegra_drm_fops,
-
-       .name = DRIVER_NAME,
-       .desc = DRIVER_DESC,
-       .date = DRIVER_DATE,
-       .major = DRIVER_MAJOR,
-       .minor = DRIVER_MINOR,
-       .patchlevel = DRIVER_PATCHLEVEL,
-};
diff --git a/drivers/gpu/host1x/drm/gr2d.c b/drivers/gpu/host1x/drm/gr2d.c
deleted file mode 100644 (file)
index 27ffcf1..0000000
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * drivers/video/tegra/host/gr2d/gr2d.c
- *
- * Tegra Graphics 2D
- *
- * Copyright (c) 2012-2013, NVIDIA Corporation.
- *
- * 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/export.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/clk.h>
-
-#include "channel.h"
-#include "drm.h"
-#include "gem.h"
-#include "job.h"
-#include "host1x.h"
-#include "host1x_bo.h"
-#include "host1x_client.h"
-#include "syncpt.h"
-
-struct gr2d {
-       struct host1x_client client;
-       struct clk *clk;
-       struct host1x_channel *channel;
-       unsigned long *addr_regs;
-};
-
-static inline struct gr2d *to_gr2d(struct host1x_client *client)
-{
-       return container_of(client, struct gr2d, client);
-}
-
-static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 reg);
-
-static int gr2d_client_init(struct host1x_client *client,
-                           struct drm_device *drm)
-{
-       return 0;
-}
-
-static int gr2d_client_exit(struct host1x_client *client)
-{
-       return 0;
-}
-
-static int gr2d_open_channel(struct host1x_client *client,
-                            struct host1x_drm_context *context)
-{
-       struct gr2d *gr2d = to_gr2d(client);
-
-       context->channel = host1x_channel_get(gr2d->channel);
-
-       if (!context->channel)
-               return -ENOMEM;
-
-       return 0;
-}
-
-static void gr2d_close_channel(struct host1x_drm_context *context)
-{
-       host1x_channel_put(context->channel);
-}
-
-static struct host1x_bo *host1x_bo_lookup(struct drm_device *drm,
-                                         struct drm_file *file,
-                                         u32 handle)
-{
-       struct drm_gem_object *gem;
-       struct tegra_bo *bo;
-
-       gem = drm_gem_object_lookup(drm, file, handle);
-       if (!gem)
-               return NULL;
-
-       mutex_lock(&drm->struct_mutex);
-       drm_gem_object_unreference(gem);
-       mutex_unlock(&drm->struct_mutex);
-
-       bo = to_tegra_bo(gem);
-       return &bo->base;
-}
-
-static int gr2d_submit(struct host1x_drm_context *context,
-                      struct drm_tegra_submit *args, struct drm_device *drm,
-                      struct drm_file *file)
-{
-       struct host1x_job *job;
-       unsigned int num_cmdbufs = args->num_cmdbufs;
-       unsigned int num_relocs = args->num_relocs;
-       unsigned int num_waitchks = args->num_waitchks;
-       struct drm_tegra_cmdbuf __user *cmdbufs =
-               (void * __user)(uintptr_t)args->cmdbufs;
-       struct drm_tegra_reloc __user *relocs =
-               (void * __user)(uintptr_t)args->relocs;
-       struct drm_tegra_waitchk __user *waitchks =
-               (void * __user)(uintptr_t)args->waitchks;
-       struct drm_tegra_syncpt syncpt;
-       int err;
-
-       /* We don't yet support other than one syncpt_incr struct per submit */
-       if (args->num_syncpts != 1)
-               return -EINVAL;
-
-       job = host1x_job_alloc(context->channel, args->num_cmdbufs,
-                              args->num_relocs, args->num_waitchks);
-       if (!job)
-               return -ENOMEM;
-
-       job->num_relocs = args->num_relocs;
-       job->num_waitchk = args->num_waitchks;
-       job->client = (u32)args->context;
-       job->class = context->client->class;
-       job->serialize = true;
-
-       while (num_cmdbufs) {
-               struct drm_tegra_cmdbuf cmdbuf;
-               struct host1x_bo *bo;
-
-               err = copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf));
-               if (err)
-                       goto fail;
-
-               bo = host1x_bo_lookup(drm, file, cmdbuf.handle);
-               if (!bo) {
-                       err = -ENOENT;
-                       goto fail;
-               }
-
-               host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
-               num_cmdbufs--;
-               cmdbufs++;
-       }
-
-       err = copy_from_user(job->relocarray, relocs,
-                            sizeof(*relocs) * num_relocs);
-       if (err)
-               goto fail;
-
-       while (num_relocs--) {
-               struct host1x_reloc *reloc = &job->relocarray[num_relocs];
-               struct host1x_bo *cmdbuf, *target;
-
-               cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf);
-               target = host1x_bo_lookup(drm, file, (u32)reloc->target);
-
-               reloc->cmdbuf = cmdbuf;
-               reloc->target = target;
-
-               if (!reloc->target || !reloc->cmdbuf) {
-                       err = -ENOENT;
-                       goto fail;
-               }
-       }
-
-       err = copy_from_user(job->waitchk, waitchks,
-                            sizeof(*waitchks) * num_waitchks);
-       if (err)
-               goto fail;
-
-       err = copy_from_user(&syncpt, (void * __user)(uintptr_t)args->syncpts,
-                            sizeof(syncpt));
-       if (err)
-               goto fail;
-
-       job->syncpt_id = syncpt.id;
-       job->syncpt_incrs = syncpt.incrs;
-       job->timeout = 10000;
-       job->is_addr_reg = gr2d_is_addr_reg;
-
-       if (args->timeout && args->timeout < 10000)
-               job->timeout = args->timeout;
-
-       err = host1x_job_pin(job, context->client->dev);
-       if (err)
-               goto fail;
-
-       err = host1x_job_submit(job);
-       if (err)
-               goto fail_submit;
-
-       args->fence = job->syncpt_end;
-
-       host1x_job_put(job);
-       return 0;
-
-fail_submit:
-       host1x_job_unpin(job);
-fail:
-       host1x_job_put(job);
-       return err;
-}
-
-static struct host1x_client_ops gr2d_client_ops = {
-       .drm_init = gr2d_client_init,
-       .drm_exit = gr2d_client_exit,
-       .open_channel = gr2d_open_channel,
-       .close_channel = gr2d_close_channel,
-       .submit = gr2d_submit,
-};
-
-static void gr2d_init_addr_reg_map(struct device *dev, struct gr2d *gr2d)
-{
-       const u32 gr2d_addr_regs[] = {0x1a, 0x1b, 0x26, 0x2b, 0x2c, 0x2d, 0x31,
-                                     0x32, 0x48, 0x49, 0x4a, 0x4b, 0x4c};
-       unsigned long *bitmap;
-       int i;
-
-       bitmap = devm_kzalloc(dev, DIV_ROUND_UP(256, BITS_PER_BYTE),
-                             GFP_KERNEL);
-
-       for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); ++i) {
-               u32 reg = gr2d_addr_regs[i];
-               bitmap[BIT_WORD(reg)] |= BIT_MASK(reg);
-       }
-
-       gr2d->addr_regs = bitmap;
-}
-
-static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 reg)
-{
-       struct gr2d *gr2d = dev_get_drvdata(dev);
-
-       switch (class) {
-       case HOST1X_CLASS_HOST1X:
-               return reg == 0x2b;
-       case HOST1X_CLASS_GR2D:
-       case HOST1X_CLASS_GR2D_SB:
-               reg &= 0xff;
-               if (gr2d->addr_regs[BIT_WORD(reg)] & BIT_MASK(reg))
-                       return 1;
-       default:
-               return 0;
-       }
-}
-
-static const struct of_device_id gr2d_match[] = {
-       { .compatible = "nvidia,tegra30-gr2d" },
-       { .compatible = "nvidia,tegra20-gr2d" },
-       { },
-};
-
-static int gr2d_probe(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct host1x_drm *host1x = host1x_get_drm_data(dev->parent);
-       int err;
-       struct gr2d *gr2d = NULL;
-       struct host1x_syncpt **syncpts;
-
-       gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL);
-       if (!gr2d)
-               return -ENOMEM;
-
-       syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
-       if (!syncpts)
-               return -ENOMEM;
-
-       gr2d->clk = devm_clk_get(dev, NULL);
-       if (IS_ERR(gr2d->clk)) {
-               dev_err(dev, "cannot get clock\n");
-               return PTR_ERR(gr2d->clk);
-       }
-
-       err = clk_prepare_enable(gr2d->clk);
-       if (err) {
-               dev_err(dev, "cannot turn on clock\n");
-               return err;
-       }
-
-       gr2d->channel = host1x_channel_request(dev);
-       if (!gr2d->channel)
-               return -ENOMEM;
-
-       *syncpts = host1x_syncpt_request(dev, false);
-       if (!(*syncpts)) {
-               host1x_channel_free(gr2d->channel);
-               return -ENOMEM;
-       }
-
-       gr2d->client.ops = &gr2d_client_ops;
-       gr2d->client.dev = dev;
-       gr2d->client.class = HOST1X_CLASS_GR2D;
-       gr2d->client.syncpts = syncpts;
-       gr2d->client.num_syncpts = 1;
-
-       err = host1x_register_client(host1x, &gr2d->client);
-       if (err < 0) {
-               dev_err(dev, "failed to register host1x client: %d\n", err);
-               return err;
-       }
-
-       gr2d_init_addr_reg_map(dev, gr2d);
-
-       platform_set_drvdata(pdev, gr2d);
-
-       return 0;
-}
-
-static int __exit gr2d_remove(struct platform_device *pdev)
-{
-       struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
-       struct gr2d *gr2d = platform_get_drvdata(pdev);
-       unsigned int i;
-       int err;
-
-       err = host1x_unregister_client(host1x, &gr2d->client);
-       if (err < 0) {
-               dev_err(&pdev->dev, "failed to unregister client: %d\n", err);
-               return err;
-       }
-
-       for (i = 0; i < gr2d->client.num_syncpts; i++)
-               host1x_syncpt_free(gr2d->client.syncpts[i]);
-
-       host1x_channel_free(gr2d->channel);
-       clk_disable_unprepare(gr2d->clk);
-
-       return 0;
-}
-
-struct platform_driver tegra_gr2d_driver = {
-       .probe = gr2d_probe,
-       .remove = __exit_p(gr2d_remove),
-       .driver = {
-               .owner = THIS_MODULE,
-               .name = "gr2d",
-               .of_match_table = gr2d_match,
-       }
-};
diff --git a/drivers/gpu/host1x/host1x.h b/drivers/gpu/host1x/host1x.h
deleted file mode 100644 (file)
index a2bc1e6..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Tegra host1x driver
- *
- * Copyright (c) 2009-2013, NVIDIA Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-#ifndef __LINUX_HOST1X_H
-#define __LINUX_HOST1X_H
-
-enum host1x_class {
-       HOST1X_CLASS_HOST1X     = 0x1,
-       HOST1X_CLASS_GR2D       = 0x51,
-       HOST1X_CLASS_GR2D_SB    = 0x52
-};
-
-#endif
diff --git a/drivers/gpu/host1x/host1x_bo.h b/drivers/gpu/host1x/host1x_bo.h
deleted file mode 100644 (file)
index 4c1f10b..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Tegra host1x Memory Management Abstraction header
- *
- * Copyright (c) 2012-2013, NVIDIA Corporation.
- *
- * 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/>.
- */
-
-#ifndef _HOST1X_BO_H
-#define _HOST1X_BO_H
-
-struct host1x_bo;
-
-struct host1x_bo_ops {
-       struct host1x_bo *(*get)(struct host1x_bo *bo);
-       void (*put)(struct host1x_bo *bo);
-       dma_addr_t (*pin)(struct host1x_bo *bo, struct sg_table **sgt);
-       void (*unpin)(struct host1x_bo *bo, struct sg_table *sgt);
-       void *(*mmap)(struct host1x_bo *bo);
-       void (*munmap)(struct host1x_bo *bo, void *addr);
-       void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum);
-       void (*kunmap)(struct host1x_bo *bo, unsigned int pagenum, void *addr);
-};
-
-struct host1x_bo {
-       const struct host1x_bo_ops *ops;
-};
-
-static inline void host1x_bo_init(struct host1x_bo *bo,
-                                 const struct host1x_bo_ops *ops)
-{
-       bo->ops = ops;
-}
-
-static inline struct host1x_bo *host1x_bo_get(struct host1x_bo *bo)
-{
-       return bo->ops->get(bo);
-}
-
-static inline void host1x_bo_put(struct host1x_bo *bo)
-{
-       bo->ops->put(bo);
-}
-
-static inline dma_addr_t host1x_bo_pin(struct host1x_bo *bo,
-                                      struct sg_table **sgt)
-{
-       return bo->ops->pin(bo, sgt);
-}
-
-static inline void host1x_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
-{
-       bo->ops->unpin(bo, sgt);
-}
-
-static inline void *host1x_bo_mmap(struct host1x_bo *bo)
-{
-       return bo->ops->mmap(bo);
-}
-
-static inline void host1x_bo_munmap(struct host1x_bo *bo, void *addr)
-{
-       bo->ops->munmap(bo, addr);
-}
-
-static inline void *host1x_bo_kmap(struct host1x_bo *bo, unsigned int pagenum)
-{
-       return bo->ops->kmap(bo, pagenum);
-}
-
-static inline void host1x_bo_kunmap(struct host1x_bo *bo,
-                                   unsigned int pagenum, void *addr)
-{
-       bo->ops->kunmap(bo, pagenum, addr);
-}
-
-#endif
diff --git a/drivers/gpu/host1x/hw/Makefile b/drivers/gpu/host1x/hw/Makefile
deleted file mode 100644 (file)
index 9b50863..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-ccflags-y = -Idrivers/gpu/host1x
-
-host1x-hw-objs  = \
-       host1x01.o
-
-obj-$(CONFIG_TEGRA_HOST1X) += host1x-hw.o
index 2ee4ad5..37e2a63 100644 (file)
 #include <linux/scatterlist.h>
 #include <linux/dma-mapping.h>
 
-#include "cdma.h"
-#include "channel.h"
-#include "dev.h"
-#include "debug.h"
+#include "../cdma.h"
+#include "../channel.h"
+#include "../dev.h"
+#include "../debug.h"
 
 /*
  * Put the restart at the end of pushbuffer memor
index ee19962..4608257 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/host1x.h>
 #include <linux/slab.h>
+
 #include <trace/events/host1x.h>
 
-#include "host1x.h"
-#include "host1x_bo.h"
-#include "channel.h"
-#include "dev.h"
-#include "intr.h"
-#include "job.h"
+#include "../channel.h"
+#include "../dev.h"
+#include "../intr.h"
+#include "../job.h"
 
 #define HOST1X_CHANNEL_SIZE 16384
 #define TRACE_MAX_LENGTH 128U
@@ -67,6 +67,22 @@ static void submit_gathers(struct host1x_job *job)
        }
 }
 
+static inline void synchronize_syncpt_base(struct host1x_job *job)
+{
+       struct host1x *host = dev_get_drvdata(job->channel->dev->parent);
+       struct host1x_syncpt *sp = host->syncpt + job->syncpt_id;
+       u32 id, value;
+
+       value = host1x_syncpt_read_max(sp);
+       id = sp->base->id;
+
+       host1x_cdma_push(&job->channel->cdma,
+                        host1x_opcode_setclass(HOST1X_CLASS_HOST1X,
+                               HOST1X_UCLASS_LOAD_SYNCPT_BASE, 1),
+                        HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(id) |
+                        HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(value));
+}
+
 static int channel_submit(struct host1x_job *job)
 {
        struct host1x_channel *ch = job->channel;
@@ -118,6 +134,10 @@ static int channel_submit(struct host1x_job *job)
                                        host1x_syncpt_read_max(sp)));
        }
 
+       /* Synchronize base register to allow using it for relative waiting */
+       if (sp->base)
+               synchronize_syncpt_base(job);
+
        syncval = host1x_syncpt_incr_max(sp, user_syncpt_incrs);
 
        job->syncpt_end = syncval;
index 334c038..640c75c 100644 (file)
  *
  */
 
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/mm.h>
-#include <linux/scatterlist.h>
-
-#include <linux/io.h>
-
-#include "dev.h"
-#include "debug.h"
-#include "cdma.h"
-#include "channel.h"
-#include "host1x_bo.h"
+#include "../dev.h"
+#include "../debug.h"
+#include "../cdma.h"
+#include "../channel.h"
 
 #define HOST1X_DEBUG_MAX_PAGE_OFFSET 102400
 
index a14e91c..859b73b 100644 (file)
  */
 
 /* include hw specification */
-#include "hw/host1x01.h"
-#include "hw/host1x01_hardware.h"
+#include "host1x01.h"
+#include "host1x01_hardware.h"
 
 /* include code */
-#include "hw/cdma_hw.c"
-#include "hw/channel_hw.c"
-#include "hw/debug_hw.c"
-#include "hw/intr_hw.c"
-#include "hw/syncpt_hw.c"
+#include "cdma_hw.c"
+#include "channel_hw.c"
+#include "debug_hw.c"
+#include "intr_hw.c"
+#include "syncpt_hw.c"
 
-#include "dev.h"
+#include "../dev.h"
 
 int host1x01_init(struct host1x *host)
 {
diff --git a/drivers/gpu/host1x/hw/host1x02.c b/drivers/gpu/host1x/hw/host1x02.c
new file mode 100644 (file)
index 0000000..e98caca
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Host1x init for Tegra114 SoCs
+ *
+ * Copyright (c) 2013 NVIDIA Corporation.
+ *
+ * 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 hw specification */
+#include "host1x01.h"
+#include "host1x01_hardware.h"
+
+/* include code */
+#include "cdma_hw.c"
+#include "channel_hw.c"
+#include "debug_hw.c"
+#include "intr_hw.c"
+#include "syncpt_hw.c"
+
+#include "../dev.h"
+
+int host1x02_init(struct host1x *host)
+{
+       host->channel_op = &host1x_channel_ops;
+       host->cdma_op = &host1x_cdma_ops;
+       host->cdma_pb_op = &host1x_pushbuffer_ops;
+       host->syncpt_op = &host1x_syncpt_ops;
+       host->intr_op = &host1x_intr_ops;
+       host->debug_op = &host1x_debug_ops;
+
+       return 0;
+}
diff --git a/drivers/gpu/host1x/hw/host1x02.h b/drivers/gpu/host1x/hw/host1x02.h
new file mode 100644 (file)
index 0000000..f748660
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Host1x init for Tegra114 SoCs
+ *
+ * Copyright (c) 2013 NVIDIA Corporation.
+ *
+ * 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/>.
+ */
+
+#ifndef HOST1X_HOST1X02_H
+#define HOST1X_HOST1X02_H
+
+struct host1x;
+
+int host1x02_init(struct host1x *host);
+
+#endif
index 42f3ce1..f755359 100644 (file)
@@ -111,6 +111,12 @@ static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v)
 }
 #define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \
        host1x_uclass_wait_syncpt_base_offset_f(v)
+static inline u32 host1x_uclass_load_syncpt_base_r(void)
+{
+       return 0xb;
+}
+#define HOST1X_UCLASS_LOAD_SYNCPT_BASE \
+       host1x_uclass_load_syncpt_base_r()
 static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v)
 {
        return (v & 0xff) << 24;
diff --git a/drivers/gpu/host1x/hw/hw_host1x02_channel.h b/drivers/gpu/host1x/hw/hw_host1x02_channel.h
new file mode 100644 (file)
index 0000000..e490bcd
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2013 NVIDIA Corporation.
+ *
+ * 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/>.
+ *
+ */
+
+ /*
+  * Function naming determines intended use:
+  *
+  *     <x>_r(void) : Returns the offset for register <x>.
+  *
+  *     <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
+  *
+  *     <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
+  *
+  *     <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
+  *         and masked to place it at field <y> of register <x>.  This value
+  *         can be |'d with others to produce a full register value for
+  *         register <x>.
+  *
+  *     <x>_<y>_m(void) : Returns a mask for field <y> of register <x>.  This
+  *         value can be ~'d and then &'d to clear the value of field <y> for
+  *         register <x>.
+  *
+  *     <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
+  *         to place it at field <y> of register <x>.  This value can be |'d
+  *         with others to produce a full register value for <x>.
+  *
+  *     <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
+  *         <x> value 'r' after being shifted to place its LSB at bit 0.
+  *         This value is suitable for direct comparison with other unshifted
+  *         values appropriate for use in field <y> of register <x>.
+  *
+  *     <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
+  *         field <y> of register <x>.  This value is suitable for direct
+  *         comparison with unshifted values appropriate for use in field <y>
+  *         of register <x>.
+  */
+
+#ifndef HOST1X_HW_HOST1X02_CHANNEL_H
+#define HOST1X_HW_HOST1X02_CHANNEL_H
+
+static inline u32 host1x_channel_fifostat_r(void)
+{
+       return 0x0;
+}
+#define HOST1X_CHANNEL_FIFOSTAT \
+       host1x_channel_fifostat_r()
+static inline u32 host1x_channel_fifostat_cfempty_v(u32 r)
+{
+       return (r >> 11) & 0x1;
+}
+#define HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_V(r) \
+       host1x_channel_fifostat_cfempty_v(r)
+static inline u32 host1x_channel_dmastart_r(void)
+{
+       return 0x14;
+}
+#define HOST1X_CHANNEL_DMASTART \
+       host1x_channel_dmastart_r()
+static inline u32 host1x_channel_dmaput_r(void)
+{
+       return 0x18;
+}
+#define HOST1X_CHANNEL_DMAPUT \
+       host1x_channel_dmaput_r()
+static inline u32 host1x_channel_dmaget_r(void)
+{
+       return 0x1c;
+}
+#define HOST1X_CHANNEL_DMAGET \
+       host1x_channel_dmaget_r()
+static inline u32 host1x_channel_dmaend_r(void)
+{
+       return 0x20;
+}
+#define HOST1X_CHANNEL_DMAEND \
+       host1x_channel_dmaend_r()
+static inline u32 host1x_channel_dmactrl_r(void)
+{
+       return 0x24;
+}
+#define HOST1X_CHANNEL_DMACTRL \
+       host1x_channel_dmactrl_r()
+static inline u32 host1x_channel_dmactrl_dmastop(void)
+{
+       return 1 << 0;
+}
+#define HOST1X_CHANNEL_DMACTRL_DMASTOP \
+       host1x_channel_dmactrl_dmastop()
+static inline u32 host1x_channel_dmactrl_dmastop_v(u32 r)
+{
+       return (r >> 0) & 0x1;
+}
+#define HOST1X_CHANNEL_DMACTRL_DMASTOP_V(r) \
+       host1x_channel_dmactrl_dmastop_v(r)
+static inline u32 host1x_channel_dmactrl_dmagetrst(void)
+{
+       return 1 << 1;
+}
+#define HOST1X_CHANNEL_DMACTRL_DMAGETRST \
+       host1x_channel_dmactrl_dmagetrst()
+static inline u32 host1x_channel_dmactrl_dmainitget(void)
+{
+       return 1 << 2;
+}
+#define HOST1X_CHANNEL_DMACTRL_DMAINITGET \
+       host1x_channel_dmactrl_dmainitget()
+
+#endif
diff --git a/drivers/gpu/host1x/hw/hw_host1x02_sync.h b/drivers/gpu/host1x/hw/hw_host1x02_sync.h
new file mode 100644 (file)
index 0000000..4495401
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2013 NVIDIA Corporation.
+ *
+ * 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/>.
+ *
+ */
+
+ /*
+  * Function naming determines intended use:
+  *
+  *     <x>_r(void) : Returns the offset for register <x>.
+  *
+  *     <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
+  *
+  *     <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
+  *
+  *     <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
+  *         and masked to place it at field <y> of register <x>.  This value
+  *         can be |'d with others to produce a full register value for
+  *         register <x>.
+  *
+  *     <x>_<y>_m(void) : Returns a mask for field <y> of register <x>.  This
+  *         value can be ~'d and then &'d to clear the value of field <y> for
+  *         register <x>.
+  *
+  *     <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
+  *         to place it at field <y> of register <x>.  This value can be |'d
+  *         with others to produce a full register value for <x>.
+  *
+  *     <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
+  *         <x> value 'r' after being shifted to place its LSB at bit 0.
+  *         This value is suitable for direct comparison with other unshifted
+  *         values appropriate for use in field <y> of register <x>.
+  *
+  *     <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
+  *         field <y> of register <x>.  This value is suitable for direct
+  *         comparison with unshifted values appropriate for use in field <y>
+  *         of register <x>.
+  */
+
+#ifndef HOST1X_HW_HOST1X02_SYNC_H
+#define HOST1X_HW_HOST1X02_SYNC_H
+
+#define REGISTER_STRIDE        4
+
+static inline u32 host1x_sync_syncpt_r(unsigned int id)
+{
+       return 0x400 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT(id) \
+       host1x_sync_syncpt_r(id)
+static inline u32 host1x_sync_syncpt_thresh_cpu0_int_status_r(unsigned int id)
+{
+       return 0x40 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id) \
+       host1x_sync_syncpt_thresh_cpu0_int_status_r(id)
+static inline u32 host1x_sync_syncpt_thresh_int_disable_r(unsigned int id)
+{
+       return 0x60 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id) \
+       host1x_sync_syncpt_thresh_int_disable_r(id)
+static inline u32 host1x_sync_syncpt_thresh_int_enable_cpu0_r(unsigned int id)
+{
+       return 0x68 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id) \
+       host1x_sync_syncpt_thresh_int_enable_cpu0_r(id)
+static inline u32 host1x_sync_cf_setup_r(unsigned int channel)
+{
+       return 0x80 + channel * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_CF_SETUP(channel) \
+       host1x_sync_cf_setup_r(channel)
+static inline u32 host1x_sync_cf_setup_base_v(u32 r)
+{
+       return (r >> 0) & 0x3ff;
+}
+#define HOST1X_SYNC_CF_SETUP_BASE_V(r) \
+       host1x_sync_cf_setup_base_v(r)
+static inline u32 host1x_sync_cf_setup_limit_v(u32 r)
+{
+       return (r >> 16) & 0x3ff;
+}
+#define HOST1X_SYNC_CF_SETUP_LIMIT_V(r) \
+       host1x_sync_cf_setup_limit_v(r)
+static inline u32 host1x_sync_cmdproc_stop_r(void)
+{
+       return 0xac;
+}
+#define HOST1X_SYNC_CMDPROC_STOP \
+       host1x_sync_cmdproc_stop_r()
+static inline u32 host1x_sync_ch_teardown_r(void)
+{
+       return 0xb0;
+}
+#define HOST1X_SYNC_CH_TEARDOWN \
+       host1x_sync_ch_teardown_r()
+static inline u32 host1x_sync_usec_clk_r(void)
+{
+       return 0x1a4;
+}
+#define HOST1X_SYNC_USEC_CLK \
+       host1x_sync_usec_clk_r()
+static inline u32 host1x_sync_ctxsw_timeout_cfg_r(void)
+{
+       return 0x1a8;
+}
+#define HOST1X_SYNC_CTXSW_TIMEOUT_CFG \
+       host1x_sync_ctxsw_timeout_cfg_r()
+static inline u32 host1x_sync_ip_busy_timeout_r(void)
+{
+       return 0x1bc;
+}
+#define HOST1X_SYNC_IP_BUSY_TIMEOUT \
+       host1x_sync_ip_busy_timeout_r()
+static inline u32 host1x_sync_mlock_owner_r(unsigned int id)
+{
+       return 0x340 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_MLOCK_OWNER(id) \
+       host1x_sync_mlock_owner_r(id)
+static inline u32 host1x_sync_mlock_owner_chid_f(u32 v)
+{
+       return (v & 0xf) << 8;
+}
+#define HOST1X_SYNC_MLOCK_OWNER_CHID_F(v) \
+       host1x_sync_mlock_owner_chid_f(v)
+static inline u32 host1x_sync_mlock_owner_cpu_owns_v(u32 r)
+{
+       return (r >> 1) & 0x1;
+}
+#define HOST1X_SYNC_MLOCK_OWNER_CPU_OWNS_V(r) \
+       host1x_sync_mlock_owner_cpu_owns_v(r)
+static inline u32 host1x_sync_mlock_owner_ch_owns_v(u32 r)
+{
+       return (r >> 0) & 0x1;
+}
+#define HOST1X_SYNC_MLOCK_OWNER_CH_OWNS_V(r) \
+       host1x_sync_mlock_owner_ch_owns_v(r)
+static inline u32 host1x_sync_syncpt_int_thresh_r(unsigned int id)
+{
+       return 0x500 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_INT_THRESH(id) \
+       host1x_sync_syncpt_int_thresh_r(id)
+static inline u32 host1x_sync_syncpt_base_r(unsigned int id)
+{
+       return 0x600 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_BASE(id) \
+       host1x_sync_syncpt_base_r(id)
+static inline u32 host1x_sync_syncpt_cpu_incr_r(unsigned int id)
+{
+       return 0x700 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_CPU_INCR(id) \
+       host1x_sync_syncpt_cpu_incr_r(id)
+static inline u32 host1x_sync_cbread_r(unsigned int channel)
+{
+       return 0x720 + channel * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_CBREAD(channel) \
+       host1x_sync_cbread_r(channel)
+static inline u32 host1x_sync_cfpeek_ctrl_r(void)
+{
+       return 0x74c;
+}
+#define HOST1X_SYNC_CFPEEK_CTRL \
+       host1x_sync_cfpeek_ctrl_r()
+static inline u32 host1x_sync_cfpeek_ctrl_addr_f(u32 v)
+{
+       return (v & 0x3ff) << 0;
+}
+#define HOST1X_SYNC_CFPEEK_CTRL_ADDR_F(v) \
+       host1x_sync_cfpeek_ctrl_addr_f(v)
+static inline u32 host1x_sync_cfpeek_ctrl_channr_f(u32 v)
+{
+       return (v & 0xf) << 16;
+}
+#define HOST1X_SYNC_CFPEEK_CTRL_CHANNR_F(v) \
+       host1x_sync_cfpeek_ctrl_channr_f(v)
+static inline u32 host1x_sync_cfpeek_ctrl_ena_f(u32 v)
+{
+       return (v & 0x1) << 31;
+}
+#define HOST1X_SYNC_CFPEEK_CTRL_ENA_F(v) \
+       host1x_sync_cfpeek_ctrl_ena_f(v)
+static inline u32 host1x_sync_cfpeek_read_r(void)
+{
+       return 0x750;
+}
+#define HOST1X_SYNC_CFPEEK_READ \
+       host1x_sync_cfpeek_read_r()
+static inline u32 host1x_sync_cfpeek_ptrs_r(void)
+{
+       return 0x754;
+}
+#define HOST1X_SYNC_CFPEEK_PTRS \
+       host1x_sync_cfpeek_ptrs_r()
+static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(u32 r)
+{
+       return (r >> 0) & 0x3ff;
+}
+#define HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_V(r) \
+       host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(r)
+static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(u32 r)
+{
+       return (r >> 16) & 0x3ff;
+}
+#define HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_V(r) \
+       host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(r)
+static inline u32 host1x_sync_cbstat_r(unsigned int channel)
+{
+       return 0x758 + channel * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_CBSTAT(channel) \
+       host1x_sync_cbstat_r(channel)
+static inline u32 host1x_sync_cbstat_cboffset_v(u32 r)
+{
+       return (r >> 0) & 0xffff;
+}
+#define HOST1X_SYNC_CBSTAT_CBOFFSET_V(r) \
+       host1x_sync_cbstat_cboffset_v(r)
+static inline u32 host1x_sync_cbstat_cbclass_v(u32 r)
+{
+       return (r >> 16) & 0x3ff;
+}
+#define HOST1X_SYNC_CBSTAT_CBCLASS_V(r) \
+       host1x_sync_cbstat_cbclass_v(r)
+
+#endif
diff --git a/drivers/gpu/host1x/hw/hw_host1x02_uclass.h b/drivers/gpu/host1x/hw/hw_host1x02_uclass.h
new file mode 100644 (file)
index 0000000..a3b3c98
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2013 NVIDIA Corporation.
+ *
+ * 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/>.
+ *
+ */
+
+ /*
+  * Function naming determines intended use:
+  *
+  *     <x>_r(void) : Returns the offset for register <x>.
+  *
+  *     <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
+  *
+  *     <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
+  *
+  *     <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
+  *         and masked to place it at field <y> of register <x>.  This value
+  *         can be |'d with others to produce a full register value for
+  *         register <x>.
+  *
+  *     <x>_<y>_m(void) : Returns a mask for field <y> of register <x>.  This
+  *         value can be ~'d and then &'d to clear the value of field <y> for
+  *         register <x>.
+  *
+  *     <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
+  *         to place it at field <y> of register <x>.  This value can be |'d
+  *         with others to produce a full register value for <x>.
+  *
+  *     <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
+  *         <x> value 'r' after being shifted to place its LSB at bit 0.
+  *         This value is suitable for direct comparison with other unshifted
+  *         values appropriate for use in field <y> of register <x>.
+  *
+  *     <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
+  *         field <y> of register <x>.  This value is suitable for direct
+  *         comparison with unshifted values appropriate for use in field <y>
+  *         of register <x>.
+  */
+
+#ifndef HOST1X_HW_HOST1X02_UCLASS_H
+#define HOST1X_HW_HOST1X02_UCLASS_H
+
+static inline u32 host1x_uclass_incr_syncpt_r(void)
+{
+       return 0x0;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT \
+       host1x_uclass_incr_syncpt_r()
+static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
+{
+       return (v & 0xff) << 8;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_COND_F(v) \
+       host1x_uclass_incr_syncpt_cond_f(v)
+static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
+{
+       return (v & 0xff) << 0;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
+       host1x_uclass_incr_syncpt_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_r(void)
+{
+       return 0x8;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT \
+       host1x_uclass_wait_syncpt_r()
+static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v)
+{
+       return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_INDX_F(v) \
+       host1x_uclass_wait_syncpt_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v)
+{
+       return (v & 0xffffff) << 0;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_THRESH_F(v) \
+       host1x_uclass_wait_syncpt_thresh_f(v)
+static inline u32 host1x_uclass_wait_syncpt_base_r(void)
+{
+       return 0x9;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE \
+       host1x_uclass_wait_syncpt_base_r()
+static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v)
+{
+       return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_INDX_F(v) \
+       host1x_uclass_wait_syncpt_base_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v)
+{
+       return (v & 0xff) << 16;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_BASE_INDX_F(v) \
+       host1x_uclass_wait_syncpt_base_base_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v)
+{
+       return (v & 0xffff) << 0;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \
+       host1x_uclass_wait_syncpt_base_offset_f(v)
+static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v)
+{
+       return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(v) \
+       host1x_uclass_load_syncpt_base_base_indx_f(v)
+static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v)
+{
+       return (v & 0xffffff) << 0;
+}
+#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(v) \
+       host1x_uclass_load_syncpt_base_value_f(v)
+static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v)
+{
+       return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_BASE_BASE_INDX_F(v) \
+       host1x_uclass_incr_syncpt_base_base_indx_f(v)
+static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v)
+{
+       return (v & 0xffffff) << 0;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_BASE_OFFSET_F(v) \
+       host1x_uclass_incr_syncpt_base_offset_f(v)
+static inline u32 host1x_uclass_indoff_r(void)
+{
+       return 0x2d;
+}
+#define HOST1X_UCLASS_INDOFF \
+       host1x_uclass_indoff_r()
+static inline u32 host1x_uclass_indoff_indbe_f(u32 v)
+{
+       return (v & 0xf) << 28;
+}
+#define HOST1X_UCLASS_INDOFF_INDBE_F(v) \
+       host1x_uclass_indoff_indbe_f(v)
+static inline u32 host1x_uclass_indoff_autoinc_f(u32 v)
+{
+       return (v & 0x1) << 27;
+}
+#define HOST1X_UCLASS_INDOFF_AUTOINC_F(v) \
+       host1x_uclass_indoff_autoinc_f(v)
+static inline u32 host1x_uclass_indoff_indmodid_f(u32 v)
+{
+       return (v & 0xff) << 18;
+}
+#define HOST1X_UCLASS_INDOFF_INDMODID_F(v) \
+       host1x_uclass_indoff_indmodid_f(v)
+static inline u32 host1x_uclass_indoff_indroffset_f(u32 v)
+{
+       return (v & 0xffff) << 2;
+}
+#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
+       host1x_uclass_indoff_indroffset_f(v)
+static inline u32 host1x_uclass_indoff_rwn_read_v(void)
+{
+       return 1;
+}
+#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
+       host1x_uclass_indoff_indroffset_f(v)
+
+#endif
index b592eef..b26dcc8 100644 (file)
@@ -22,8 +22,8 @@
 #include <linux/io.h>
 #include <asm/mach/irq.h>
 
-#include "intr.h"
-#include "dev.h"
+#include "../intr.h"
+#include "../dev.h"
 
 /*
  * Sync point threshold interrupt service function
index 0cf6095..56e8539 100644 (file)
@@ -18,8 +18,8 @@
 
 #include <linux/io.h>
 
-#include "dev.h"
-#include "syncpt.h"
+#include "../dev.h"
+#include "../syncpt.h"
 
 /*
  * Write the current syncpoint value back to hw.
index c4e1050..de5ec33 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <linux/dma-mapping.h>
 #include <linux/err.h>
+#include <linux/host1x.h>
 #include <linux/kref.h>
 #include <linux/module.h>
 #include <linux/scatterlist.h>
@@ -27,7 +28,6 @@
 
 #include "channel.h"
 #include "dev.h"
-#include "host1x_bo.h"
 #include "job.h"
 #include "syncpt.h"
 
@@ -264,7 +264,7 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
 }
 
 static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
-                      unsigned int offset)
+                       unsigned int offset)
 {
        offset *= sizeof(u32);
 
@@ -281,7 +281,7 @@ struct host1x_firewall {
        unsigned int num_relocs;
        struct host1x_reloc *reloc;
 
-       struct host1x_bo *cmdbuf_id;
+       struct host1x_bo *cmdbuf;
        unsigned int offset;
 
        u32 words;
@@ -291,25 +291,37 @@ struct host1x_firewall {
        u32 count;
 };
 
+static int check_register(struct host1x_firewall *fw, unsigned long offset)
+{
+       if (fw->job->is_addr_reg(fw->dev, fw->class, offset)) {
+               if (!fw->num_relocs)
+                       return -EINVAL;
+
+               if (!check_reloc(fw->reloc, fw->cmdbuf, fw->offset))
+                       return -EINVAL;
+
+               fw->num_relocs--;
+               fw->reloc++;
+       }
+
+       return 0;
+}
+
 static int check_mask(struct host1x_firewall *fw)
 {
        u32 mask = fw->mask;
        u32 reg = fw->reg;
+       int ret;
 
        while (mask) {
                if (fw->words == 0)
                        return -EINVAL;
 
                if (mask & 1) {
-                       if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) {
-                               if (!fw->num_relocs)
-                                       return -EINVAL;
-                               if (!check_reloc(fw->reloc, fw->cmdbuf_id,
-                                                fw->offset))
-                                       return -EINVAL;
-                               fw->reloc++;
-                               fw->num_relocs--;
-                       }
+                       ret = check_register(fw, reg);
+                       if (ret < 0)
+                               return ret;
+
                        fw->words--;
                        fw->offset++;
                }
@@ -324,19 +336,16 @@ static int check_incr(struct host1x_firewall *fw)
 {
        u32 count = fw->count;
        u32 reg = fw->reg;
+       int ret;
 
        while (count) {
                if (fw->words == 0)
                        return -EINVAL;
 
-               if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) {
-                       if (!fw->num_relocs)
-                               return -EINVAL;
-                       if (!check_reloc(fw->reloc, fw->cmdbuf_id, fw->offset))
-                               return -EINVAL;
-                       fw->reloc++;
-                       fw->num_relocs--;
-               }
+               ret = check_register(fw, reg);
+               if (ret < 0)
+                       return ret;
+
                reg++;
                fw->words--;
                fw->offset++;
@@ -348,21 +357,17 @@ static int check_incr(struct host1x_firewall *fw)
 
 static int check_nonincr(struct host1x_firewall *fw)
 {
-       int is_addr_reg = fw->job->is_addr_reg(fw->dev, fw->class, fw->reg);
        u32 count = fw->count;
+       int ret;
 
        while (count) {
                if (fw->words == 0)
                        return -EINVAL;
 
-               if (is_addr_reg) {
-                       if (!fw->num_relocs)
-                               return -EINVAL;
-                       if (!check_reloc(fw->reloc, fw->cmdbuf_id, fw->offset))
-                               return -EINVAL;
-                       fw->reloc++;
-                       fw->num_relocs--;
-               }
+               ret = check_register(fw, fw->reg);
+               if (ret < 0)
+                       return ret;
+
                fw->words--;
                fw->offset++;
                count--;
@@ -381,7 +386,7 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
                return 0;
 
        fw->words = g->words;
-       fw->cmdbuf_id = g->bo;
+       fw->cmdbuf = g->bo;
        fw->offset = 0;
 
        while (fw->words && !err) {
@@ -436,10 +441,6 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
                }
        }
 
-       /* No relocs should remain at this point */
-       if (fw->num_relocs)
-               err = -EINVAL;
-
 out:
        return err;
 }
@@ -493,6 +494,10 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev)
                offset += g->words * sizeof(u32);
        }
 
+       /* No relocs should remain at this point */
+       if (fw.num_relocs)
+               return -EINVAL;
+
        return 0;
 }
 
index fba45f2..33a697d 100644 (file)
@@ -34,15 +34,6 @@ struct host1x_cmdbuf {
        u32 pad;
 };
 
-struct host1x_reloc {
-       struct host1x_bo *cmdbuf;
-       u32 cmdbuf_offset;
-       struct host1x_bo *target;
-       u32 target_offset;
-       u32 shift;
-       u32 pad;
-};
-
 struct host1x_waitchk {
        struct host1x_bo *bo;
        u32 offset;
@@ -56,105 +47,6 @@ struct host1x_job_unpin_data {
 };
 
 /*
- * Each submit is tracked as a host1x_job.
- */
-struct host1x_job {
-       /* When refcount goes to zero, job can be freed */
-       struct kref ref;
-
-       /* List entry */
-       struct list_head list;
-
-       /* Channel where job is submitted to */
-       struct host1x_channel *channel;
-
-       u32 client;
-
-       /* Gathers and their memory */
-       struct host1x_job_gather *gathers;
-       unsigned int num_gathers;
-
-       /* Wait checks to be processed at submit time */
-       struct host1x_waitchk *waitchk;
-       unsigned int num_waitchk;
-       u32 waitchk_mask;
-
-       /* Array of handles to be pinned & unpinned */
-       struct host1x_reloc *relocarray;
-       unsigned int num_relocs;
-       struct host1x_job_unpin_data *unpins;
-       unsigned int num_unpins;
-
-       dma_addr_t *addr_phys;
-       dma_addr_t *gather_addr_phys;
-       dma_addr_t *reloc_addr_phys;
-
-       /* Sync point id, number of increments and end related to the submit */
-       u32 syncpt_id;
-       u32 syncpt_incrs;
-       u32 syncpt_end;
-
-       /* Maximum time to wait for this job */
-       unsigned int timeout;
-
-       /* Index and number of slots used in the push buffer */
-       unsigned int first_get;
-       unsigned int num_slots;
-
-       /* Copy of gathers */
-       size_t gather_copy_size;
-       dma_addr_t gather_copy;
-       u8 *gather_copy_mapped;
-
-       /* Check if register is marked as an address reg */
-       int (*is_addr_reg)(struct device *dev, u32 reg, u32 class);
-
-       /* Request a SETCLASS to this class */
-       u32 class;
-
-       /* Add a channel wait for previous ops to complete */
-       bool serialize;
-};
-/*
- * Allocate memory for a job. Just enough memory will be allocated to
- * accomodate the submit.
- */
-struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
-                                   u32 num_cmdbufs, u32 num_relocs,
-                                   u32 num_waitchks);
-
-/*
- * Add a gather to a job.
- */
-void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *mem_id,
-                          u32 words, u32 offset);
-
-/*
- * Increment reference going to host1x_job.
- */
-struct host1x_job *host1x_job_get(struct host1x_job *job);
-
-/*
- * Decrement reference job, free if goes to zero.
- */
-void host1x_job_put(struct host1x_job *job);
-
-/*
- * Pin memory related to job. This handles relocation of addresses to the
- * host1x address space. Handles both the gather memory and any other memory
- * referred to from the gather buffers.
- *
- * Handles also patching out host waits that would wait for an expired sync
- * point value.
- */
-int host1x_job_pin(struct host1x_job *job, struct device *dev);
-
-/*
- * Unpin memory related to job.
- */
-void host1x_job_unpin(struct host1x_job *job);
-
-/*
  * Dump contents of job to debug output.
  */
 void host1x_job_dump(struct device *dev, struct host1x_job *job);
index 409745b..159c479 100644 (file)
 #define SYNCPT_CHECK_PERIOD (2 * HZ)
 #define MAX_STUCK_CHECK_COUNT 15
 
-static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
-                                                 struct device *dev,
-                                                 bool client_managed)
+static struct host1x_syncpt_base *
+host1x_syncpt_base_request(struct host1x *host)
+{
+       struct host1x_syncpt_base *bases = host->bases;
+       unsigned int i;
+
+       for (i = 0; i < host->info->nb_bases; i++)
+               if (!bases[i].requested)
+                       break;
+
+       if (i >= host->info->nb_bases)
+               return NULL;
+
+       bases[i].requested = true;
+       return &bases[i];
+}
+
+static void host1x_syncpt_base_free(struct host1x_syncpt_base *base)
+{
+       if (base)
+               base->requested = false;
+}
+
+static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
+                                                struct device *dev,
+                                                unsigned long flags)
 {
        int i;
        struct host1x_syncpt *sp = host->syncpt;
@@ -44,6 +67,12 @@ static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
        if (i >= host->info->nb_pts)
                return NULL;
 
+       if (flags & HOST1X_SYNCPT_HAS_BASE) {
+               sp->base = host1x_syncpt_base_request(host);
+               if (!sp->base)
+                       return NULL;
+       }
+
        name = kasprintf(GFP_KERNEL, "%02d-%s", sp->id,
                        dev ? dev_name(dev) : NULL);
        if (!name)
@@ -51,7 +80,11 @@ static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
 
        sp->dev = dev;
        sp->name = name;
-       sp->client_managed = client_managed;
+
+       if (flags & HOST1X_SYNCPT_CLIENT_MANAGED)
+               sp->client_managed = true;
+       else
+               sp->client_managed = false;
 
        return sp;
 }
@@ -303,25 +336,35 @@ int host1x_syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr)
 
 int host1x_syncpt_init(struct host1x *host)
 {
+       struct host1x_syncpt_base *bases;
        struct host1x_syncpt *syncpt;
        int i;
 
        syncpt = devm_kzalloc(host->dev, sizeof(*syncpt) * host->info->nb_pts,
-               GFP_KERNEL);
+                             GFP_KERNEL);
        if (!syncpt)
                return -ENOMEM;
 
-       for (i = 0; i < host->info->nb_pts; ++i) {
+       bases = devm_kzalloc(host->dev, sizeof(*bases) * host->info->nb_bases,
+                            GFP_KERNEL);
+       if (!bases)
+               return -ENOMEM;
+
+       for (i = 0; i < host->info->nb_pts; i++) {
                syncpt[i].id = i;
                syncpt[i].host = host;
        }
 
+       for (i = 0; i < host->info->nb_bases; i++)
+               bases[i].id = i;
+
        host->syncpt = syncpt;
+       host->bases = bases;
 
        host1x_syncpt_restore(host);
 
        /* Allocate sync point to use for clearing waits for expired fences */
-       host->nop_sp = _host1x_syncpt_alloc(host, NULL, false);
+       host->nop_sp = host1x_syncpt_alloc(host, NULL, 0);
        if (!host->nop_sp)
                return -ENOMEM;
 
@@ -329,10 +372,10 @@ int host1x_syncpt_init(struct host1x *host)
 }
 
 struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
-                                           bool client_managed)
+                                           unsigned long flags)
 {
        struct host1x *host = dev_get_drvdata(dev->parent);
-       return _host1x_syncpt_alloc(host, dev, client_managed);
+       return host1x_syncpt_alloc(host, dev, flags);
 }
 
 void host1x_syncpt_free(struct host1x_syncpt *sp)
@@ -340,7 +383,9 @@ void host1x_syncpt_free(struct host1x_syncpt *sp)
        if (!sp)
                return;
 
+       host1x_syncpt_base_free(sp->base);
        kfree(sp->name);
+       sp->base = NULL;
        sp->dev = NULL;
        sp->name = NULL;
        sp->client_managed = false;
@@ -354,6 +399,25 @@ void host1x_syncpt_deinit(struct host1x *host)
                kfree(sp->name);
 }
 
+/*
+ * Read max. It indicates how many operations there are in queue, either in
+ * channel or in a software thread.
+ * */
+u32 host1x_syncpt_read_max(struct host1x_syncpt *sp)
+{
+       smp_rmb();
+       return (u32)atomic_read(&sp->max_val);
+}
+
+/*
+ * Read min, which is a shadow of the current sync point value in hardware.
+ */
+u32 host1x_syncpt_read_min(struct host1x_syncpt *sp)
+{
+       smp_rmb();
+       return (u32)atomic_read(&sp->min_val);
+}
+
 int host1x_syncpt_nb_pts(struct host1x *host)
 {
        return host->info->nb_pts;
@@ -375,3 +439,13 @@ struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id)
                return NULL;
        return host->syncpt + id;
 }
+
+struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp)
+{
+       return sp ? sp->base : NULL;
+}
+
+u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base)
+{
+       return base->id;
+}
index 267c0b9..9056465 100644 (file)
@@ -20,6 +20,7 @@
 #define __HOST1X_SYNCPT_H
 
 #include <linux/atomic.h>
+#include <linux/host1x.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 
@@ -30,6 +31,11 @@ struct host1x;
 /* Reserved for replacing an expired wait with a NOP */
 #define HOST1X_SYNCPT_RESERVED                 0
 
+struct host1x_syncpt_base {
+       unsigned int id;
+       bool requested;
+};
+
 struct host1x_syncpt {
        int id;
        atomic_t min_val;
@@ -39,6 +45,7 @@ struct host1x_syncpt {
        bool client_managed;
        struct host1x *host;
        struct device *dev;
+       struct host1x_syncpt_base *base;
 
        /* interrupt data */
        struct host1x_syncpt_intr intr;
@@ -50,25 +57,6 @@ int host1x_syncpt_init(struct host1x *host);
 /*  Free sync point array */
 void host1x_syncpt_deinit(struct host1x *host);
 
-/*
- * Read max. It indicates how many operations there are in queue, either in
- * channel or in a software thread.
- * */
-static inline u32 host1x_syncpt_read_max(struct host1x_syncpt *sp)
-{
-       smp_rmb();
-       return (u32)atomic_read(&sp->max_val);
-}
-
-/*
- * Read min, which is a shadow of the current sync point value in hardware.
- */
-static inline u32 host1x_syncpt_read_min(struct host1x_syncpt *sp)
-{
-       smp_rmb();
-       return (u32)atomic_read(&sp->min_val);
-}
-
 /* Return number of sync point supported. */
 int host1x_syncpt_nb_pts(struct host1x *host);
 
@@ -112,9 +100,6 @@ static inline bool host1x_syncpt_idle(struct host1x_syncpt *sp)
        return (min == max);
 }
 
-/* Return pointer to struct denoting sync point id. */
-struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id);
-
 /* Load current value from hardware to the shadow register. */
 u32 host1x_syncpt_load(struct host1x_syncpt *sp);
 
@@ -130,16 +115,9 @@ void host1x_syncpt_restore(struct host1x *host);
 /* Read current wait base value into shadow register and return it. */
 u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp);
 
-/* Request incrementing a sync point. */
-int host1x_syncpt_incr(struct host1x_syncpt *sp);
-
 /* Indicate future operations by incrementing the sync point max. */
 u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs);
 
-/* Wait until sync point reaches a threshold value, or a timeout. */
-int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh,
-                       long timeout, u32 *value);
-
 /* Check if sync point id is valid. */
 static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp)
 {
@@ -149,14 +127,4 @@ static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp)
 /* Patch a wait by replacing it with a wait for syncpt 0 value 0 */
 int host1x_syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr);
 
-/* Return id of the sync point */
-u32 host1x_syncpt_id(struct host1x_syncpt *sp);
-
-/* Allocate a sync point for a device. */
-struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
-                                           bool client_managed);
-
-/* Free a sync point. */
-void host1x_syncpt_free(struct host1x_syncpt *sp);
-
 #endif
index c91d547..329fbb9 100644 (file)
@@ -242,6 +242,7 @@ config HID_HOLTEK
          - Tracer Sniper TRM-503 / NOVA Gaming Slider X200 /
            Zalman ZM-GM1
          - SHARKOON DarkGlider Gaming mouse
+         - LEETGION Hellion Gaming Mouse
 
 config HOLTEK_FF
        bool "Holtek On Line Grip force feedback support"
@@ -323,7 +324,7 @@ config HID_LCPOWER
 
 config HID_LENOVO_TPKBD
        tristate "Lenovo ThinkPad USB Keyboard with TrackPoint"
-       depends on USB_HID
+       depends on HID
        select NEW_LEDS
        select LEDS_CLASS
        ---help---
@@ -362,19 +363,20 @@ config LOGITECH_FF
          - Logitech WingMan Force 3D
          - Logitech Formula Force EX
          - Logitech WingMan Formula Force GP
-         - Logitech MOMO Force wheel
 
          and if you want to enable force feedback for them.
          Note: if you say N here, this device will still be supported, but without
          force feedback.
 
 config LOGIRUMBLEPAD2_FF
-       bool "Logitech RumblePad/Rumblepad 2 force feedback support"
+       bool "Logitech force feedback support (variant 2)"
        depends on HID_LOGITECH
        select INPUT_FF_MEMLESS
        help
-         Say Y here if you want to enable force feedback support for Logitech
-         RumblePad and Rumblepad 2 devices.
+         Say Y here if you want to enable force feedback support for:
+         - Logitech RumblePad
+         - Logitech Rumblepad 2
+         - Logitech Formula Vibration Feedback Wheel
 
 config LOGIG940_FF
        bool "Logitech Flight System G940 force feedback support"
@@ -437,6 +439,7 @@ config HID_MULTITOUCH
          - Chunghwa panels
          - CVTouch panels
          - Cypress TrueTouch panels
+         - Elan Microelectronics touch panels
          - Elo TouchSystems IntelliTouch Plus panels
          - GeneralTouch 'Sensing Win7-TwoFinger' panels
          - GoodTouch panels
@@ -453,6 +456,7 @@ config HID_MULTITOUCH
          - Pixcir dual touch panels
          - Quanta panels
          - eGalax dual-touch panels, including the Joojoo and Wetab tablets
+         - SiS multitouch panels
          - Stantum multitouch panels
          - Touch International Panels
          - Unitec Panels
@@ -614,6 +618,14 @@ config HID_SONY
          * Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
          * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
 
+config SONY_FF
+       bool "Sony PS2/3 accessories force feedback support"
+       depends on HID_SONY
+       select INPUT_FF_MEMLESS
+       ---help---
+       Say Y here if you have a Sony PS2/3 accessory and want to enable force
+       feedback support for it.
+
 config HID_SPEEDLINK
        tristate "Speedlink VAD Cezanne mouse support"
        depends on HID
index a959f4a..30e4431 100644 (file)
@@ -95,7 +95,7 @@ obj-$(CONFIG_HID_PRIMAX)      += hid-primax.o
 obj-$(CONFIG_HID_ROCCAT)       += hid-roccat.o hid-roccat-common.o \
        hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
        hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \
-       hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-savu.o
+       hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-ryos.o hid-roccat-savu.o
 obj-$(CONFIG_HID_SAITEK)       += hid-saitek.o
 obj-$(CONFIG_HID_SAMSUNG)      += hid-samsung.o
 obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
index 881cf7b..4975581 100644 (file)
@@ -46,6 +46,12 @@ module_param(iso_layout, uint, 0644);
 MODULE_PARM_DESC(iso_layout, "Enable/Disable hardcoded ISO-layout of the keyboard. "
                "(0 = disabled, [1] = enabled)");
 
+static unsigned int swap_opt_cmd;
+module_param(swap_opt_cmd, uint, 0644);
+MODULE_PARM_DESC(swap_opt_cmd, "Swap the Option (\"Alt\") and Command (\"Flag\") keys. "
+               "(For people who want to keep Windows PC keyboard muscle memory. "
+               "[0] = as-is, Mac layout. 1 = swapped, Windows layout.)");
+
 struct apple_sc {
        unsigned long quirks;
        unsigned int fn_on;
@@ -150,6 +156,14 @@ static const struct apple_key_translation apple_iso_keyboard[] = {
        { }
 };
 
+static const struct apple_key_translation swapped_option_cmd_keys[] = {
+       { KEY_LEFTALT,  KEY_LEFTMETA },
+       { KEY_LEFTMETA, KEY_LEFTALT },
+       { KEY_RIGHTALT, KEY_RIGHTMETA },
+       { KEY_RIGHTMETA,KEY_RIGHTALT },
+       { }
+};
+
 static const struct apple_key_translation *apple_find_translation(
                const struct apple_key_translation *table, u16 from)
 {
@@ -242,6 +256,14 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
                }
        }
 
+       if (swap_opt_cmd) {
+               trans = apple_find_translation(swapped_option_cmd_keys, usage->code);
+               if (trans) {
+                       input_event(input, usage->type, trans->to, value);
+                       return 1;
+               }
+       }
+
        return 0;
 }
 
index 64ab94a..a594e47 100644 (file)
@@ -95,7 +95,7 @@ static int axff_init(struct hid_device *hid)
                }
        }
 
-       if (field_count < 4) {
+       if (field_count < 4 && hid->product != 0xf705) {
                hid_err(hid, "not enough fields in the report: %d\n",
                        field_count);
                return -ENODEV;
@@ -180,6 +180,7 @@ static void ax_remove(struct hid_device *hdev)
 
 static const struct hid_device_id ax_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705), },
        { }
 };
 MODULE_DEVICE_TABLE(hid, ax_devices);
index e80da62..8c10f27 100644 (file)
@@ -1418,10 +1418,8 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
 
        if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
                ret = hdrv->raw_event(hid, report, data, size);
-               if (ret < 0) {
-                       ret = ret < 0 ? ret : 0;
+               if (ret < 0)
                        goto unlock;
-               }
        }
 
        ret = hid_report_raw_event(hid, type, data, size, interrupt);
@@ -1605,6 +1603,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
        { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) },
@@ -1716,6 +1715,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) },
        { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) },
@@ -1754,6 +1754,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
@@ -1801,21 +1802,28 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
 #if IS_ENABLED(CONFIG_HID_ROCCAT)
-       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKUFX) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEXTD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
 #endif
        { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH, USB_DEVICE_ID_SIS9200_TOUCH) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH, USB_DEVICE_ID_SIS817_TOUCH) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
@@ -2376,15 +2384,6 @@ bool hid_ignore(struct hid_device *hdev)
                                hdev->type == HID_TYPE_USBNONE)
                        return true;
                break;
-       case USB_VENDOR_ID_DWAV:
-               /* These are handled by usbtouchscreen. hdev->type is probably
-                * HID_TYPE_USBNONE, but we say !HID_TYPE_USBMOUSE to match
-                * usbtouchscreen. */
-               if ((hdev->product == USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER ||
-                    hdev->product == USB_DEVICE_ID_DWAV_TOUCHCONTROLLER) &&
-                   hdev->type != HID_TYPE_USBMOUSE)
-                       return true;
-               break;
        case USB_VENDOR_ID_VELLEMAN:
                /* These are not HID devices.  They are handled by comedi. */
                if ((hdev->product >= USB_DEVICE_ID_VELLEMAN_K8055_FIRST &&
index f042a6c..4e49462 100644 (file)
@@ -181,7 +181,40 @@ fail:
  */
 static bool elo_broken_firmware(struct usb_device *dev)
 {
-       return use_fw_quirk && le16_to_cpu(dev->descriptor.bcdDevice) == 0x10d;
+       struct usb_device *hub = dev->parent;
+       struct usb_device *child = NULL;
+       u16 fw_lvl = le16_to_cpu(dev->descriptor.bcdDevice);
+       u16 child_vid, child_pid;
+       int i;
+    
+       if (!use_fw_quirk)
+               return false;
+       if (fw_lvl != 0x10d)
+               return false;
+
+       /* iterate sibling devices of the touch controller */
+       usb_hub_for_each_child(hub, i, child) {
+               child_vid = le16_to_cpu(child->descriptor.idVendor);
+               child_pid = le16_to_cpu(child->descriptor.idProduct);
+
+               /*
+                * If one of the devices below is present attached as a sibling of 
+                * the touch controller then  this is a newer IBM 4820 monitor that 
+                * does not need the IBM-requested workaround if fw level is
+                * 0x010d - aka 'M'.
+                * No other HW can have this combination.
+                */
+               if (child_vid==0x04b3) {
+                       switch (child_pid) {
+                       case 0x4676: /* 4820 21x Video */
+                       case 0x4677: /* 4820 51x Video */
+                       case 0x4678: /* 4820 2Lx Video */
+                       case 0x4679: /* 4820 5Lx Video */
+                               return false;
+                       }
+               }
+       }
+       return true;
 }
 
 static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id)
index e696566..0caa676 100644 (file)
@@ -28,6 +28,7 @@
  * - USB ID 04d9:a04a, sold as Tracer Sniper TRM-503, NOVA Gaming Slider X200
  *   and Zalman ZM-GM1
  * - USB ID 04d9:a081, sold as SHARKOON DarkGlider Gaming mouse
+ * - USB ID 04d9:a072, sold as LEETGION Hellion Gaming Mouse
  */
 
 static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
@@ -40,6 +41,7 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                 * 0x2fff, so they don't exceed HID_MAX_USAGES */
                switch (hdev->product) {
                case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067:
+               case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072:
                        if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f
                                        && rdesc[120] == 0xff && rdesc[121] == 0x7f) {
                                hid_info(hdev, "Fixing up report descriptor\n");
@@ -66,6 +68,8 @@ static const struct hid_device_id holtek_mouse_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
                        USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
+                       USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
                        USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
        { }
 };
index f0296a5..7655962 100644 (file)
 #define USB_VENDOR_ID_GENERAL_TOUCH    0x0dfc
 #define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003
 #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0101 0x0101
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0102 0x0102
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0106 0x0106
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100
 
 #define USB_VENDOR_ID_GLAB             0x06c2
 #define USB_DEVICE_ID_4_PHIDGETSERVO_30        0x0038
 
 #define USB_VENDOR_ID_HOLTEK_ALT               0x04d9
 #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD      0xa055
-#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067    0xa067
 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A    0xa04a
+#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067    0xa067
+#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072    0xa072
 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081    0xa081
 
 #define USB_VENDOR_ID_IMATION          0x0718
 #define USB_DEVICE_ID_DINOVO_EDGE      0xc714
 #define USB_DEVICE_ID_DINOVO_MINI      0xc71f
 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2     0xca03
+#define USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL 0xca04
 
 #define USB_VENDOR_ID_LUMIO            0x202e
 #define USB_DEVICE_ID_CRYSTALTOUCH     0x0006
 #define USB_DEVICE_ID_ROCCAT_LUA       0x2c2e
 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED        0x2c24
 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS     0x2cf6
+#define USB_DEVICE_ID_ROCCAT_RYOS_MK   0x3138
+#define USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW      0x31ce
+#define USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO       0x3232
 #define USB_DEVICE_ID_ROCCAT_SAVU      0x2d5a
 
 #define USB_VENDOR_ID_SAITEK           0x06a3
 #define USB_VENDOR_ID_SIGMATEL         0x066F
 #define USB_DEVICE_ID_SIGMATEL_STMP3780        0x3780
 
+#define USB_VENDOR_ID_SIS2_TOUCH       0x0457
+#define USB_DEVICE_ID_SIS9200_TOUCH    0x9200
+#define USB_DEVICE_ID_SIS817_TOUCH     0x0817
+
 #define USB_VENDOR_ID_SKYCABLE                 0x1223
 #define        USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER       0x3F07
 
index 31cf29a..2d25b6c 100644 (file)
 #include <linux/module.h>
 #include <linux/sysfs.h>
 #include <linux/device.h>
-#include <linux/usb.h>
 #include <linux/hid.h>
 #include <linux/input.h>
 #include <linux/leds.h>
-#include "usbhid/usbhid.h"
 
 #include "hid-ids.h"
 
@@ -41,10 +39,9 @@ static int tpkbd_input_mapping(struct hid_device *hdev,
                struct hid_input *hi, struct hid_field *field,
                struct hid_usage *usage, unsigned long **bit, int *max)
 {
-       struct usbhid_device *uhdev;
-
-       uhdev = (struct usbhid_device *) hdev->driver_data;
-       if (uhdev->ifnum == 1 && usage->hid == (HID_UP_BUTTON | 0x0010)) {
+       if (usage->hid == (HID_UP_BUTTON | 0x0010)) {
+               /* mark the device as pointer */
+               hid_set_drvdata(hdev, (void *)1);
                map_key_clear(KEY_MICMUTE);
                return 1;
        }
@@ -339,7 +336,7 @@ static int tpkbd_probe_tp(struct hid_device *hdev)
        struct tpkbd_data_pointer *data_pointer;
        size_t name_sz = strlen(dev_name(dev)) + 16;
        char *name_mute, *name_micmute;
-       int i, ret;
+       int i;
 
        /* Validate required reports. */
        for (i = 0; i < 4; i++) {
@@ -354,7 +351,9 @@ static int tpkbd_probe_tp(struct hid_device *hdev)
                hid_warn(hdev, "Could not create sysfs group\n");
        }
 
-       data_pointer = kzalloc(sizeof(struct tpkbd_data_pointer), GFP_KERNEL);
+       data_pointer = devm_kzalloc(&hdev->dev,
+                                   sizeof(struct tpkbd_data_pointer),
+                                   GFP_KERNEL);
        if (data_pointer == NULL) {
                hid_err(hdev, "Could not allocate memory for driver data\n");
                return -ENOMEM;
@@ -364,20 +363,13 @@ static int tpkbd_probe_tp(struct hid_device *hdev)
        data_pointer->sensitivity = 0xa0;
        data_pointer->press_speed = 0x38;
 
-       name_mute = kzalloc(name_sz, GFP_KERNEL);
-       if (name_mute == NULL) {
+       name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
+       name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
+       if (name_mute == NULL || name_micmute == NULL) {
                hid_err(hdev, "Could not allocate memory for led data\n");
-               ret = -ENOMEM;
-               goto err;
+               return -ENOMEM;
        }
        snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
-
-       name_micmute = kzalloc(name_sz, GFP_KERNEL);
-       if (name_micmute == NULL) {
-               hid_err(hdev, "Could not allocate memory for led data\n");
-               ret = -ENOMEM;
-               goto err2;
-       }
        snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
 
        hid_set_drvdata(hdev, data_pointer);
@@ -397,19 +389,12 @@ static int tpkbd_probe_tp(struct hid_device *hdev)
        tpkbd_features_set(hdev);
 
        return 0;
-
-err2:
-       kfree(name_mute);
-err:
-       kfree(data_pointer);
-       return ret;
 }
 
 static int tpkbd_probe(struct hid_device *hdev,
                const struct hid_device_id *id)
 {
        int ret;
-       struct usbhid_device *uhdev;
 
        ret = hid_parse(hdev);
        if (ret) {
@@ -423,9 +408,8 @@ static int tpkbd_probe(struct hid_device *hdev,
                goto err;
        }
 
-       uhdev = (struct usbhid_device *) hdev->driver_data;
-
-       if (uhdev->ifnum == 1) {
+       if (hid_get_drvdata(hdev)) {
+               hid_set_drvdata(hdev, NULL);
                ret = tpkbd_probe_tp(hdev);
                if (ret)
                        goto err_hid;
@@ -449,17 +433,11 @@ static void tpkbd_remove_tp(struct hid_device *hdev)
        led_classdev_unregister(&data_pointer->led_mute);
 
        hid_set_drvdata(hdev, NULL);
-       kfree(data_pointer->led_micmute.name);
-       kfree(data_pointer->led_mute.name);
-       kfree(data_pointer);
 }
 
 static void tpkbd_remove(struct hid_device *hdev)
 {
-       struct usbhid_device *uhdev;
-
-       uhdev = (struct usbhid_device *) hdev->driver_data;
-       if (uhdev->ifnum == 1)
+       if (hid_get_drvdata(hdev))
                tpkbd_remove_tp(hdev);
 
        hid_hw_stop(hdev);
index 6f12ecd..06eb45f 100644 (file)
@@ -45,7 +45,9 @@
 /* Size of the original descriptors of the Driving Force (and Pro) wheels */
 #define DF_RDESC_ORIG_SIZE     130
 #define DFP_RDESC_ORIG_SIZE    97
+#define FV_RDESC_ORIG_SIZE     130
 #define MOMO_RDESC_ORIG_SIZE   87
+#define MOMO2_RDESC_ORIG_SIZE  87
 
 /* Fixed report descriptors for Logitech Driving Force (and Pro)
  * wheel controllers
@@ -170,6 +172,73 @@ static __u8 dfp_rdesc_fixed[] = {
 0xC0                /*  End Collection                          */
 };
 
+static __u8 fv_rdesc_fixed[] = {
+0x05, 0x01,         /*  Usage Page (Desktop),                   */
+0x09, 0x04,         /*  Usage (Joystik),                        */
+0xA1, 0x01,         /*  Collection (Application),               */
+0xA1, 0x02,         /*      Collection (Logical),               */
+0x95, 0x01,         /*          Report Count (1),               */
+0x75, 0x0A,         /*          Report Size (10),               */
+0x15, 0x00,         /*          Logical Minimum (0),            */
+0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */
+0x35, 0x00,         /*          Physical Minimum (0),           */
+0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */
+0x09, 0x30,         /*          Usage (X),                      */
+0x81, 0x02,         /*          Input (Variable),               */
+0x95, 0x0C,         /*          Report Count (12),              */
+0x75, 0x01,         /*          Report Size (1),                */
+0x25, 0x01,         /*          Logical Maximum (1),            */
+0x45, 0x01,         /*          Physical Maximum (1),           */
+0x05, 0x09,         /*          Usage Page (Button),            */
+0x19, 0x01,         /*          Usage Minimum (01h),            */
+0x29, 0x0C,         /*          Usage Maximum (0Ch),            */
+0x81, 0x02,         /*          Input (Variable),               */
+0x95, 0x02,         /*          Report Count (2),               */
+0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */
+0x09, 0x01,         /*          Usage (01h),                    */
+0x81, 0x02,         /*          Input (Variable),               */
+0x09, 0x02,         /*          Usage (02h),                    */
+0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
+0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
+0x95, 0x01,         /*          Report Count (1),               */
+0x75, 0x08,         /*          Report Size (8),                */
+0x81, 0x02,         /*          Input (Variable),               */
+0x05, 0x01,         /*          Usage Page (Desktop),           */
+0x25, 0x07,         /*          Logical Maximum (7),            */
+0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
+0x75, 0x04,         /*          Report Size (4),                */
+0x65, 0x14,         /*          Unit (Degrees),                 */
+0x09, 0x39,         /*          Usage (Hat Switch),             */
+0x81, 0x42,         /*          Input (Variable, Null State),   */
+0x75, 0x01,         /*          Report Size (1),                */
+0x95, 0x04,         /*          Report Count (4),               */
+0x65, 0x00,         /*          Unit,                           */
+0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */
+0x09, 0x01,         /*          Usage (01h),                    */
+0x25, 0x01,         /*          Logical Maximum (1),            */
+0x45, 0x01,         /*          Physical Maximum (1),           */
+0x81, 0x02,         /*          Input (Variable),               */
+0x05, 0x01,         /*          Usage Page (Desktop),           */
+0x95, 0x01,         /*          Report Count (1),               */
+0x75, 0x08,         /*          Report Size (8),                */
+0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
+0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
+0x09, 0x31,         /*          Usage (Y),                      */
+0x81, 0x02,         /*          Input (Variable),               */
+0x09, 0x32,         /*          Usage (Z),                      */
+0x81, 0x02,         /*          Input (Variable),               */
+0xC0,               /*      End Collection,                     */
+0xA1, 0x02,         /*      Collection (Logical),               */
+0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
+0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
+0x95, 0x07,         /*          Report Count (7),               */
+0x75, 0x08,         /*          Report Size (8),                */
+0x09, 0x03,         /*          Usage (03h),                    */
+0x91, 0x02,         /*          Output (Variable),              */
+0xC0,               /*      End Collection,                     */
+0xC0                /*  End Collection                          */
+};
+
 static __u8 momo_rdesc_fixed[] = {
 0x05, 0x01,         /*  Usage Page (Desktop),               */
 0x09, 0x04,         /*  Usage (Joystik),                    */
@@ -216,6 +285,54 @@ static __u8 momo_rdesc_fixed[] = {
 0xC0                /*  End Collection                      */
 };
 
+static __u8 momo2_rdesc_fixed[] = {
+0x05, 0x01,         /*  Usage Page (Desktop),               */
+0x09, 0x04,         /*  Usage (Joystik),                    */
+0xA1, 0x01,         /*  Collection (Application),           */
+0xA1, 0x02,         /*      Collection (Logical),           */
+0x95, 0x01,         /*          Report Count (1),           */
+0x75, 0x0A,         /*          Report Size (10),           */
+0x15, 0x00,         /*          Logical Minimum (0),        */
+0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
+0x35, 0x00,         /*          Physical Minimum (0),       */
+0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
+0x09, 0x30,         /*          Usage (X),                  */
+0x81, 0x02,         /*          Input (Variable),           */
+0x95, 0x0A,         /*          Report Count (10),          */
+0x75, 0x01,         /*          Report Size (1),            */
+0x25, 0x01,         /*          Logical Maximum (1),        */
+0x45, 0x01,         /*          Physical Maximum (1),       */
+0x05, 0x09,         /*          Usage Page (Button),        */
+0x19, 0x01,         /*          Usage Minimum (01h),        */
+0x29, 0x0A,         /*          Usage Maximum (0Ah),        */
+0x81, 0x02,         /*          Input (Variable),           */
+0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
+0x09, 0x00,         /*          Usage (00h),                */
+0x95, 0x04,         /*          Report Count (4),           */
+0x81, 0x02,         /*          Input (Variable),           */
+0x95, 0x01,         /*          Report Count (1),           */
+0x75, 0x08,         /*          Report Size (8),            */
+0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
+0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
+0x09, 0x01,         /*          Usage (01h),                */
+0x81, 0x02,         /*          Input (Variable),           */
+0x05, 0x01,         /*          Usage Page (Desktop),       */
+0x09, 0x31,         /*          Usage (Y),                  */
+0x81, 0x02,         /*          Input (Variable),           */
+0x09, 0x32,         /*          Usage (Z),                  */
+0x81, 0x02,         /*          Input (Variable),           */
+0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
+0x09, 0x00,         /*          Usage (00h),                */
+0x81, 0x02,         /*          Input (Variable),           */
+0xC0,               /*      End Collection,                 */
+0xA1, 0x02,         /*      Collection (Logical),           */
+0x09, 0x02,         /*          Usage (02h),                */
+0x95, 0x07,         /*          Report Count (7),           */
+0x91, 0x02,         /*          Output (Variable),          */
+0xC0,               /*      End Collection,                 */
+0xC0                /*  End Collection                      */
+};
+
 /*
  * Certain Logitech keyboards send in report #3 keys which are far
  * above the logical maximum described in descriptor. This extends
@@ -275,6 +392,24 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                }
                break;
 
+       case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
+               if (*rsize == MOMO2_RDESC_ORIG_SIZE) {
+                       hid_info(hdev,
+                               "fixing up Logitech Momo Racing Force (Black) report descriptor\n");
+                       rdesc = momo2_rdesc_fixed;
+                       *rsize = sizeof(momo2_rdesc_fixed);
+               }
+               break;
+
+       case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
+               if (*rsize == FV_RDESC_ORIG_SIZE) {
+                       hid_info(hdev,
+                               "fixing up Logitech Formula Vibration report descriptor\n");
+                       rdesc = fv_rdesc_fixed;
+                       *rsize = sizeof(fv_rdesc_fixed);
+               }
+               break;
+
        case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
                if (*rsize == DFP_RDESC_ORIG_SIZE) {
                        hid_info(hdev,
@@ -492,6 +627,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
                case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
                case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
                case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
+               case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
                        field->application = HID_GD_MULTIAXIS;
                        break;
                default:
@@ -639,6 +775,8 @@ static const struct hid_device_id lg_devices[] = {
                .driver_data = LG_NOGET | LG_FF4 },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
                .driver_data = LG_FF4 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
+               .driver_data = LG_FF2 },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
                .driver_data = LG_FF4 },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
index 1a42eaa..0e3fb1a 100644 (file)
@@ -95,7 +95,7 @@ int lg2ff_init(struct hid_device *hid)
 
        hid_hw_request(hid, report, HID_REQ_SET_REPORT);
 
-       hid_info(hid, "Force feedback for Logitech RumblePad/Rumblepad 2 by Anssi Hannula <anssi.hannula@gmail.com>\n");
+       hid_info(hid, "Force feedback for Logitech variant 2 rumble devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
 
        return 0;
 }
index 8782fe1..befe0e3 100644 (file)
@@ -196,6 +196,21 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
        case FF_CONSTANT:
                x = effect->u.ramp.start_level + 0x80;  /* 0x80 is no force */
                CLAMP(x);
+
+               if (x == 0x80) {
+                       /* De-activate force in slot-1*/
+                       value[0] = 0x13;
+                       value[1] = 0x00;
+                       value[2] = 0x00;
+                       value[3] = 0x00;
+                       value[4] = 0x00;
+                       value[5] = 0x00;
+                       value[6] = 0x00;
+
+                       hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+                       return 0;
+               }
+
                value[0] = 0x11;        /* Slot 1 */
                value[1] = 0x08;
                value[2] = x;
@@ -218,12 +233,70 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
        __s32 *value = report->field[0]->value;
+       __u32 expand_a, expand_b;
+       struct lg4ff_device_entry *entry;
+       struct lg_drv_data *drv_data;
+
+       drv_data = hid_get_drvdata(hid);
+       if (!drv_data) {
+               hid_err(hid, "Private driver data not found!\n");
+               return;
+       }
+
+       entry = drv_data->device_props;
+       if (!entry) {
+               hid_err(hid, "Device properties not found!\n");
+               return;
+       }
+
+       /* De-activate Auto-Center */
+       if (magnitude == 0) {
+               value[0] = 0xf5;
+               value[1] = 0x00;
+               value[2] = 0x00;
+               value[3] = 0x00;
+               value[4] = 0x00;
+               value[5] = 0x00;
+               value[6] = 0x00;
+
+               hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+               return;
+       }
+
+       if (magnitude <= 0xaaaa) {
+               expand_a = 0x0c * magnitude;
+               expand_b = 0x80 * magnitude;
+       } else {
+               expand_a = (0x0c * 0xaaaa) + 0x06 * (magnitude - 0xaaaa);
+               expand_b = (0x80 * 0xaaaa) + 0xff * (magnitude - 0xaaaa);
+       }
+
+       /* Adjust for non-MOMO wheels */
+       switch (entry->product_id) {
+       case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
+       case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
+               break;
+       default:
+               expand_a = expand_a >> 1;
+               break;
+       }
 
        value[0] = 0xfe;
        value[1] = 0x0d;
-       value[2] = magnitude >> 13;
-       value[3] = magnitude >> 13;
-       value[4] = magnitude >> 8;
+       value[2] = expand_a / 0xaaaa;
+       value[3] = expand_a / 0xaaaa;
+       value[4] = expand_b / 0xaaaa;
+       value[5] = 0x00;
+       value[6] = 0x00;
+
+       hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+
+       /* Activate Auto-Center */
+       value[0] = 0x14;
+       value[1] = 0x00;
+       value[2] = 0x00;
+       value[3] = 0x00;
+       value[4] = 0x00;
        value[5] = 0x00;
        value[6] = 0x00;
 
@@ -540,17 +613,6 @@ int lg4ff_init(struct hid_device *hid)
        if (error)
                return error;
 
-       /* Check if autocentering is available and
-        * set the centering force to zero by default */
-       if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
-               if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
-                       dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
-               else
-                       dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
-
-               dev->ff->set_autocenter(dev, 0);
-       }
-
        /* Get private driver data */
        drv_data = hid_get_drvdata(hid);
        if (!drv_data) {
@@ -571,6 +633,17 @@ int lg4ff_init(struct hid_device *hid)
        entry->max_range = lg4ff_devices[i].max_range;
        entry->set_range = lg4ff_devices[i].set_range;
 
+       /* Check if autocentering is available and
+        * set the centering force to zero by default */
+       if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
+               if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
+                       dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
+               else
+                       dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
+
+               dev->ff->set_autocenter(dev, 0);
+       }
+
        /* Create sysfs interface */
        error = device_create_file(&hid->dev, &dev_attr_range);
        if (error)
index 2e53024..a7947d8 100644 (file)
@@ -542,9 +542,9 @@ static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
        return 0;
 }
 
-static void rdcat(char **rdesc, unsigned int *rsize, const char *data, unsigned int size)
+static void rdcat(char *rdesc, unsigned int *rsize, const char *data, unsigned int size)
 {
-       memcpy(*rdesc + *rsize, data, size);
+       memcpy(rdesc + *rsize, data, size);
        *rsize += size;
 }
 
@@ -567,31 +567,31 @@ static int logi_dj_ll_parse(struct hid_device *hid)
        if (djdev->reports_supported & STD_KEYBOARD) {
                dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n",
                        __func__, djdev->reports_supported);
-               rdcat(&rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor));
+               rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor));
        }
 
        if (djdev->reports_supported & STD_MOUSE) {
                dbg_hid("%s: sending a mouse descriptor, reports_supported: "
                        "%x\n", __func__, djdev->reports_supported);
-               rdcat(&rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor));
+               rdcat(rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor));
        }
 
        if (djdev->reports_supported & MULTIMEDIA) {
                dbg_hid("%s: sending a multimedia report descriptor: %x\n",
                        __func__, djdev->reports_supported);
-               rdcat(&rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor));
+               rdcat(rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor));
        }
 
        if (djdev->reports_supported & POWER_KEYS) {
                dbg_hid("%s: sending a power keys report descriptor: %x\n",
                        __func__, djdev->reports_supported);
-               rdcat(&rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor));
+               rdcat(rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor));
        }
 
        if (djdev->reports_supported & MEDIA_CENTER) {
                dbg_hid("%s: sending a media center report descriptor: %x\n",
                        __func__, djdev->reports_supported);
-               rdcat(&rdesc, &rsize, media_descriptor, sizeof(media_descriptor));
+               rdcat(rdesc, &rsize, media_descriptor, sizeof(media_descriptor));
        }
 
        if (djdev->reports_supported & KBD_LEDS) {
index 5e5fe1b..a2cedb8 100644 (file)
@@ -250,12 +250,12 @@ static struct mt_class mt_classes[] = {
        { .name = MT_CLS_GENERALTOUCH_TWOFINGERS,
                .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
                        MT_QUIRK_VALID_IS_INRANGE |
-                       MT_QUIRK_SLOT_IS_CONTACTNUMBER,
+                       MT_QUIRK_SLOT_IS_CONTACTID,
                .maxcontacts = 2
        },
        { .name = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
                .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
-                       MT_QUIRK_SLOT_IS_CONTACTNUMBER
+                       MT_QUIRK_SLOT_IS_CONTACTID
        },
 
        { .name = MT_CLS_FLATFROG,
@@ -1173,6 +1173,21 @@ static const struct hid_device_id mt_devices[] = {
        { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
                MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
                        USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS) },
+       { .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS,
+               MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
+                       USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0101) },
+       { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
+               MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
+                       USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0102) },
+       { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
+               MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
+                       USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0106) },
+       { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
+               MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
+                       USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A) },
+       { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
+               MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
+                       USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100) },
 
        /* Gametel game controller */
        { .driver_data = MT_CLS_NSMU,
@@ -1284,6 +1299,14 @@ static const struct hid_device_id mt_devices[] = {
                MT_USB_DEVICE(USB_VENDOR_ID_QUANTA,
                        USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) },
 
+       /* SiS panels */
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH,
+               USB_DEVICE_ID_SIS9200_TOUCH) },
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH,
+               USB_DEVICE_ID_SIS817_TOUCH) },
+
        /* Stantum panels */
        { .driver_data = MT_CLS_CONFIDENCE,
                MT_USB_DEVICE(USB_VENDOR_ID_STANTUM,
index 74f7040..02e28e9 100644 (file)
@@ -65,10 +65,11 @@ int roccat_common2_send(struct usb_device *usb_dev, uint report_id,
 EXPORT_SYMBOL_GPL(roccat_common2_send);
 
 enum roccat_common2_control_states {
-       ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD = 0,
+       ROCCAT_COMMON_CONTROL_STATUS_CRITICAL = 0,
        ROCCAT_COMMON_CONTROL_STATUS_OK = 1,
        ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2,
-       ROCCAT_COMMON_CONTROL_STATUS_WAIT = 3,
+       ROCCAT_COMMON_CONTROL_STATUS_BUSY = 3,
+       ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW = 4,
 };
 
 static int roccat_common2_receive_control_status(struct usb_device *usb_dev)
@@ -88,13 +89,12 @@ static int roccat_common2_receive_control_status(struct usb_device *usb_dev)
                switch (control.value) {
                case ROCCAT_COMMON_CONTROL_STATUS_OK:
                        return 0;
-               case ROCCAT_COMMON_CONTROL_STATUS_WAIT:
+               case ROCCAT_COMMON_CONTROL_STATUS_BUSY:
                        msleep(500);
                        continue;
                case ROCCAT_COMMON_CONTROL_STATUS_INVALID:
-
-               case ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD:
-                       /* seems to be critical - replug necessary */
+               case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL:
+               case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW:
                        return -EINVAL;
                default:
                        dev_err(&usb_dev->dev,
@@ -122,6 +122,59 @@ int roccat_common2_send_with_status(struct usb_device *usb_dev,
 }
 EXPORT_SYMBOL_GPL(roccat_common2_send_with_status);
 
+int roccat_common2_device_init_struct(struct usb_device *usb_dev,
+               struct roccat_common2_device *dev)
+{
+       mutex_init(&dev->lock);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(roccat_common2_device_init_struct);
+
+ssize_t roccat_common2_sysfs_read(struct file *fp, struct kobject *kobj,
+               char *buf, loff_t off, size_t count,
+               size_t real_size, uint command)
+{
+       struct device *dev =
+                       container_of(kobj, struct device, kobj)->parent->parent;
+       struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev));
+       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+       int retval;
+
+       if (off >= real_size)
+               return 0;
+
+       if (off != 0 || count != real_size)
+               return -EINVAL;
+
+       mutex_lock(&roccat_dev->lock);
+       retval = roccat_common2_receive(usb_dev, command, buf, real_size);
+       mutex_unlock(&roccat_dev->lock);
+
+       return retval ? retval : real_size;
+}
+EXPORT_SYMBOL_GPL(roccat_common2_sysfs_read);
+
+ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj,
+               void const *buf, loff_t off, size_t count,
+               size_t real_size, uint command)
+{
+       struct device *dev =
+                       container_of(kobj, struct device, kobj)->parent->parent;
+       struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev));
+       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+       int retval;
+
+       if (off != 0 || count != real_size)
+               return -EINVAL;
+
+       mutex_lock(&roccat_dev->lock);
+       retval = roccat_common2_send_with_status(usb_dev, command, buf, real_size);
+       mutex_unlock(&roccat_dev->lock);
+
+       return retval ? retval : real_size;
+}
+EXPORT_SYMBOL_GPL(roccat_common2_sysfs_write);
+
 MODULE_AUTHOR("Stefan Achatz");
 MODULE_DESCRIPTION("USB Roccat common driver");
 MODULE_LICENSE("GPL v2");
index a97746a..eaa56eb 100644 (file)
@@ -32,4 +32,66 @@ int roccat_common2_send(struct usb_device *usb_dev, uint report_id,
 int roccat_common2_send_with_status(struct usb_device *usb_dev,
                uint command, void const *buf, uint size);
 
+struct roccat_common2_device {
+       int roccat_claimed;
+       int chrdev_minor;
+       struct mutex lock;
+};
+
+int roccat_common2_device_init_struct(struct usb_device *usb_dev,
+               struct roccat_common2_device *dev);
+ssize_t roccat_common2_sysfs_read(struct file *fp, struct kobject *kobj,
+               char *buf, loff_t off, size_t count,
+               size_t real_size, uint command);
+ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj,
+               void const *buf, loff_t off, size_t count,
+               size_t real_size, uint command);
+
+#define ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE) \
+static ssize_t roccat_common2_sysfs_write_ ## thingy(struct file *fp, \
+               struct kobject *kobj, struct bin_attribute *attr, char *buf, \
+               loff_t off, size_t count) \
+{ \
+       return roccat_common2_sysfs_write(fp, kobj, buf, off, count, \
+                       SIZE, COMMAND); \
+}
+
+#define ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE) \
+static ssize_t roccat_common2_sysfs_read_ ## thingy(struct file *fp, \
+               struct kobject *kobj, struct bin_attribute *attr, char *buf, \
+               loff_t off, size_t count) \
+{ \
+       return roccat_common2_sysfs_read(fp, kobj, buf, off, count, \
+                       SIZE, COMMAND); \
+}
+
+#define ROCCAT_COMMON2_SYSFS_RW(thingy, COMMAND, SIZE) \
+ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE) \
+ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE)
+
+#define ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(thingy, COMMAND, SIZE) \
+ROCCAT_COMMON2_SYSFS_RW(thingy, COMMAND, SIZE); \
+static struct bin_attribute bin_attr_ ## thingy = { \
+       .attr = { .name = #thingy, .mode = 0660 }, \
+       .size = SIZE, \
+       .read = roccat_common2_sysfs_read_ ## thingy, \
+       .write = roccat_common2_sysfs_write_ ## thingy \
+}
+
+#define ROCCAT_COMMON2_BIN_ATTRIBUTE_R(thingy, COMMAND, SIZE) \
+ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE); \
+static struct bin_attribute bin_attr_ ## thingy = { \
+       .attr = { .name = #thingy, .mode = 0440 }, \
+       .size = SIZE, \
+       .read = roccat_common2_sysfs_read_ ## thingy, \
+}
+
+#define ROCCAT_COMMON2_BIN_ATTRIBUTE_W(thingy, COMMAND, SIZE) \
+ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE); \
+static struct bin_attribute bin_attr_ ## thingy = { \
+       .attr = { .name = #thingy, .mode = 0220 }, \
+       .size = SIZE, \
+       .write = roccat_common2_sysfs_write_ ## thingy \
+}
+
 #endif
index 99a605e..07de2f9 100644 (file)
@@ -15,6 +15,7 @@
  * Roccat KonePure is a smaller version of KoneXTD with less buttons and lights.
  */
 
+#include <linux/types.h>
 #include <linux/device.h>
 #include <linux/input.h>
 #include <linux/hid.h>
 #include <linux/hid-roccat.h>
 #include "hid-ids.h"
 #include "hid-roccat-common.h"
-#include "hid-roccat-konepure.h"
 
-static struct class *konepure_class;
-
-static ssize_t konepure_sysfs_read(struct file *fp, struct kobject *kobj,
-               char *buf, loff_t off, size_t count,
-               size_t real_size, uint command)
-{
-       struct device *dev =
-                       container_of(kobj, struct device, kobj)->parent->parent;
-       struct konepure_device *konepure = hid_get_drvdata(dev_get_drvdata(dev));
-       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
-       int retval;
-
-       if (off >= real_size)
-               return 0;
-
-       if (off != 0 || count != real_size)
-               return -EINVAL;
-
-       mutex_lock(&konepure->konepure_lock);
-       retval = roccat_common2_receive(usb_dev, command, buf, real_size);
-       mutex_unlock(&konepure->konepure_lock);
-
-       return retval ? retval : real_size;
-}
-
-static ssize_t konepure_sysfs_write(struct file *fp, struct kobject *kobj,
-               void const *buf, loff_t off, size_t count,
-               size_t real_size, uint command)
-{
-       struct device *dev =
-                       container_of(kobj, struct device, kobj)->parent->parent;
-       struct konepure_device *konepure = hid_get_drvdata(dev_get_drvdata(dev));
-       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
-       int retval;
-
-       if (off != 0 || count != real_size)
-               return -EINVAL;
-
-       mutex_lock(&konepure->konepure_lock);
-       retval = roccat_common2_send_with_status(usb_dev, command,
-                       (void *)buf, real_size);
-       mutex_unlock(&konepure->konepure_lock);
-
-       return retval ? retval : real_size;
-}
-
-#define KONEPURE_SYSFS_W(thingy, THINGY) \
-static ssize_t konepure_sysfs_write_ ## thingy(struct file *fp, \
-               struct kobject *kobj, struct bin_attribute *attr, char *buf, \
-               loff_t off, size_t count) \
-{ \
-       return konepure_sysfs_write(fp, kobj, buf, off, count, \
-                       KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \
-}
-
-#define KONEPURE_SYSFS_R(thingy, THINGY) \
-static ssize_t konepure_sysfs_read_ ## thingy(struct file *fp, \
-               struct kobject *kobj, struct bin_attribute *attr, char *buf, \
-               loff_t off, size_t count) \
-{ \
-       return konepure_sysfs_read(fp, kobj, buf, off, count, \
-                       KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \
-}
+enum {
+       KONEPURE_MOUSE_REPORT_NUMBER_BUTTON = 3,
+};
 
-#define KONEPURE_SYSFS_RW(thingy, THINGY) \
-KONEPURE_SYSFS_W(thingy, THINGY) \
-KONEPURE_SYSFS_R(thingy, THINGY)
-
-#define KONEPURE_BIN_ATTRIBUTE_RW(thingy, THINGY) \
-KONEPURE_SYSFS_RW(thingy, THINGY); \
-static struct bin_attribute bin_attr_##thingy = { \
-       .attr = { .name = #thingy, .mode = 0660 }, \
-       .size = KONEPURE_SIZE_ ## THINGY, \
-       .read = konepure_sysfs_read_ ## thingy, \
-       .write = konepure_sysfs_write_ ## thingy \
-}
+struct konepure_mouse_report_button {
+       uint8_t report_number; /* always KONEPURE_MOUSE_REPORT_NUMBER_BUTTON */
+       uint8_t zero;
+       uint8_t type;
+       uint8_t data1;
+       uint8_t data2;
+       uint8_t zero2;
+       uint8_t unknown[2];
+} __packed;
 
-#define KONEPURE_BIN_ATTRIBUTE_R(thingy, THINGY) \
-KONEPURE_SYSFS_R(thingy, THINGY); \
-static struct bin_attribute bin_attr_##thingy = { \
-       .attr = { .name = #thingy, .mode = 0440 }, \
-       .size = KONEPURE_SIZE_ ## THINGY, \
-       .read = konepure_sysfs_read_ ## thingy, \
-}
-
-#define KONEPURE_BIN_ATTRIBUTE_W(thingy, THINGY) \
-KONEPURE_SYSFS_W(thingy, THINGY); \
-static struct bin_attribute bin_attr_##thingy = { \
-       .attr = { .name = #thingy, .mode = 0220 }, \
-       .size = KONEPURE_SIZE_ ## THINGY, \
-       .write = konepure_sysfs_write_ ## thingy \
-}
+static struct class *konepure_class;
 
-KONEPURE_BIN_ATTRIBUTE_RW(actual_profile, ACTUAL_PROFILE);
-KONEPURE_BIN_ATTRIBUTE_RW(info, INFO);
-KONEPURE_BIN_ATTRIBUTE_RW(sensor, SENSOR);
-KONEPURE_BIN_ATTRIBUTE_RW(tcu, TCU);
-KONEPURE_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
-KONEPURE_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
-KONEPURE_BIN_ATTRIBUTE_W(control, CONTROL);
-KONEPURE_BIN_ATTRIBUTE_W(talk, TALK);
-KONEPURE_BIN_ATTRIBUTE_W(macro, MACRO);
-KONEPURE_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE);
-
-static struct bin_attribute *konepure_bin_attributes[] = {
+ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(actual_profile, 0x05, 0x03);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile_settings, 0x06, 0x1f);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile_buttons, 0x07, 0x3b);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_W(macro, 0x08, 0x0822);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(info, 0x09, 0x06);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(tcu, 0x0c, 0x04);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_R(tcu_image, 0x0c, 0x0404);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0x0f, 0x06);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_W(talk, 0x10, 0x10);
+
+static struct bin_attribute *konepure_bin_attrs[] = {
        &bin_attr_actual_profile,
+       &bin_attr_control,
        &bin_attr_info,
+       &bin_attr_talk,
+       &bin_attr_macro,
        &bin_attr_sensor,
        &bin_attr_tcu,
+       &bin_attr_tcu_image,
        &bin_attr_profile_settings,
        &bin_attr_profile_buttons,
-       &bin_attr_control,
-       &bin_attr_talk,
-       &bin_attr_macro,
-       &bin_attr_tcu_image,
        NULL,
 };
 
 static const struct attribute_group konepure_group = {
-       .bin_attrs = konepure_bin_attributes,
+       .bin_attrs = konepure_bin_attrs,
 };
 
 static const struct attribute_group *konepure_groups[] = {
@@ -152,20 +75,11 @@ static const struct attribute_group *konepure_groups[] = {
        NULL,
 };
 
-
-static int konepure_init_konepure_device_struct(struct usb_device *usb_dev,
-               struct konepure_device *konepure)
-{
-       mutex_init(&konepure->konepure_lock);
-
-       return 0;
-}
-
 static int konepure_init_specials(struct hid_device *hdev)
 {
        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
        struct usb_device *usb_dev = interface_to_usbdev(intf);
-       struct konepure_device *konepure;
+       struct roccat_common2_device *konepure;
        int retval;
 
        if (intf->cur_altsetting->desc.bInterfaceProtocol
@@ -181,9 +95,9 @@ static int konepure_init_specials(struct hid_device *hdev)
        }
        hid_set_drvdata(hdev, konepure);
 
-       retval = konepure_init_konepure_device_struct(usb_dev, konepure);
+       retval = roccat_common2_device_init_struct(usb_dev, konepure);
        if (retval) {
-               hid_err(hdev, "couldn't init struct konepure_device\n");
+               hid_err(hdev, "couldn't init KonePure device\n");
                goto exit_free;
        }
 
@@ -205,7 +119,7 @@ exit_free:
 static void konepure_remove_specials(struct hid_device *hdev)
 {
        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
-       struct konepure_device *konepure;
+       struct roccat_common2_device *konepure;
 
        if (intf->cur_altsetting->desc.bInterfaceProtocol
                        != USB_INTERFACE_PROTOCOL_MOUSE)
@@ -258,7 +172,7 @@ static int konepure_raw_event(struct hid_device *hdev,
                struct hid_report *report, u8 *data, int size)
 {
        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
-       struct konepure_device *konepure = hid_get_drvdata(hdev);
+       struct roccat_common2_device *konepure = hid_get_drvdata(hdev);
 
        if (intf->cur_altsetting->desc.bInterfaceProtocol
                        != USB_INTERFACE_PROTOCOL_MOUSE)
diff --git a/drivers/hid/hid-roccat-konepure.h b/drivers/hid/hid-roccat-konepure.h
deleted file mode 100644 (file)
index 2cd24e9..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-#ifndef __HID_ROCCAT_KONEPURE_H
-#define __HID_ROCCAT_KONEPURE_H
-
-/*
- * Copyright (c) 2012 Stefan Achatz <erazor_de@users.sourceforge.net>
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- */
-
-#include <linux/types.h>
-
-enum {
-       KONEPURE_SIZE_ACTUAL_PROFILE = 0x03,
-       KONEPURE_SIZE_CONTROL = 0x03,
-       KONEPURE_SIZE_FIRMWARE_WRITE = 0x0402,
-       KONEPURE_SIZE_INFO = 0x06,
-       KONEPURE_SIZE_MACRO = 0x0822,
-       KONEPURE_SIZE_PROFILE_SETTINGS = 0x1f,
-       KONEPURE_SIZE_PROFILE_BUTTONS = 0x3b,
-       KONEPURE_SIZE_SENSOR = 0x06,
-       KONEPURE_SIZE_TALK = 0x10,
-       KONEPURE_SIZE_TCU = 0x04,
-       KONEPURE_SIZE_TCU_IMAGE = 0x0404,
-};
-
-enum konepure_control_requests {
-       KONEPURE_CONTROL_REQUEST_GENERAL = 0x80,
-       KONEPURE_CONTROL_REQUEST_BUTTONS = 0x90,
-};
-
-enum konepure_commands {
-       KONEPURE_COMMAND_CONTROL = 0x04,
-       KONEPURE_COMMAND_ACTUAL_PROFILE = 0x05,
-       KONEPURE_COMMAND_PROFILE_SETTINGS = 0x06,
-       KONEPURE_COMMAND_PROFILE_BUTTONS = 0x07,
-       KONEPURE_COMMAND_MACRO = 0x08,
-       KONEPURE_COMMAND_INFO = 0x09,
-       KONEPURE_COMMAND_TCU = 0x0c,
-       KONEPURE_COMMAND_TCU_IMAGE = 0x0c,
-       KONEPURE_COMMAND_E = 0x0e,
-       KONEPURE_COMMAND_SENSOR = 0x0f,
-       KONEPURE_COMMAND_TALK = 0x10,
-       KONEPURE_COMMAND_FIRMWARE_WRITE = 0x1b,
-       KONEPURE_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c,
-};
-
-enum {
-       KONEPURE_MOUSE_REPORT_NUMBER_BUTTON = 3,
-};
-
-struct konepure_mouse_report_button {
-       uint8_t report_number; /* always KONEPURE_MOUSE_REPORT_NUMBER_BUTTON */
-       uint8_t zero;
-       uint8_t type;
-       uint8_t data1;
-       uint8_t data2;
-       uint8_t zero2;
-       uint8_t unknown[2];
-} __packed;
-
-struct konepure_device {
-       int roccat_claimed;
-       int chrdev_minor;
-       struct mutex konepure_lock;
-};
-
-#endif
index 0c8e1ef..9660477 100644 (file)
@@ -554,9 +554,13 @@ static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus,
                break;
        case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI:
                kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1);
+               break;
        case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY:
                kovaplus->actual_x_sensitivity = button_report->data1;
                kovaplus->actual_y_sensitivity = button_report->data2;
+               break;
+       default:
+               break;
        }
 }
 
diff --git a/drivers/hid/hid-roccat-ryos.c b/drivers/hid/hid-roccat-ryos.c
new file mode 100644 (file)
index 0000000..47cc8f3
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Roccat Ryos driver for Linux
+ *
+ * Copyright (c) 2013 Stefan Achatz <erazor_de@users.sourceforge.net>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/hid-roccat.h>
+#include "hid-ids.h"
+#include "hid-roccat-common.h"
+
+enum {
+       RYOS_REPORT_NUMBER_SPECIAL = 3,
+       RYOS_USB_INTERFACE_PROTOCOL = 0,
+};
+
+struct ryos_report_special {
+       uint8_t number; /* RYOS_REPORT_NUMBER_SPECIAL */
+       uint8_t data[4];
+} __packed;
+
+static struct class *ryos_class;
+
+ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x05, 0x03);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_primary, 0x06, 0x7d);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_function, 0x07, 0x5f);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_macro, 0x08, 0x23);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_thumbster, 0x09, 0x17);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_extra, 0x0a, 0x08);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_easyzone, 0x0b, 0x126);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(key_mask, 0x0c, 0x06);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(light, 0x0d, 0x10);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x0e, 0x7d2);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_R(info, 0x0f, 0x08);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_W(reset, 0x11, 0x03);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_W(light_control, 0x13, 0x08);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_W(talk, 0x16, 0x10);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(stored_lights, 0x17, 0x0566);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_W(custom_lights, 0x18, 0x14);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(light_macro, 0x19, 0x07d2);
+
+static struct bin_attribute *ryos_bin_attrs[] = {
+       &bin_attr_control,
+       &bin_attr_profile,
+       &bin_attr_keys_primary,
+       &bin_attr_keys_function,
+       &bin_attr_keys_macro,
+       &bin_attr_keys_thumbster,
+       &bin_attr_keys_extra,
+       &bin_attr_keys_easyzone,
+       &bin_attr_key_mask,
+       &bin_attr_light,
+       &bin_attr_macro,
+       &bin_attr_info,
+       &bin_attr_reset,
+       &bin_attr_light_control,
+       &bin_attr_talk,
+       &bin_attr_stored_lights,
+       &bin_attr_custom_lights,
+       &bin_attr_light_macro,
+       NULL,
+};
+
+static const struct attribute_group ryos_group = {
+       .bin_attrs = ryos_bin_attrs,
+};
+
+static const struct attribute_group *ryos_groups[] = {
+       &ryos_group,
+       NULL,
+};
+
+static int ryos_init_specials(struct hid_device *hdev)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct usb_device *usb_dev = interface_to_usbdev(intf);
+       struct roccat_common2_device *ryos;
+       int retval;
+
+       if (intf->cur_altsetting->desc.bInterfaceProtocol
+                       != RYOS_USB_INTERFACE_PROTOCOL) {
+               hid_set_drvdata(hdev, NULL);
+               return 0;
+       }
+
+       ryos = kzalloc(sizeof(*ryos), GFP_KERNEL);
+       if (!ryos) {
+               hid_err(hdev, "can't alloc device descriptor\n");
+               return -ENOMEM;
+       }
+       hid_set_drvdata(hdev, ryos);
+
+       retval = roccat_common2_device_init_struct(usb_dev, ryos);
+       if (retval) {
+               hid_err(hdev, "couldn't init Ryos device\n");
+               goto exit_free;
+       }
+
+       retval = roccat_connect(ryos_class, hdev,
+                       sizeof(struct ryos_report_special));
+       if (retval < 0) {
+               hid_err(hdev, "couldn't init char dev\n");
+       } else {
+               ryos->chrdev_minor = retval;
+               ryos->roccat_claimed = 1;
+       }
+
+       return 0;
+exit_free:
+       kfree(ryos);
+       return retval;
+}
+
+static void ryos_remove_specials(struct hid_device *hdev)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct roccat_common2_device *ryos;
+
+       if (intf->cur_altsetting->desc.bInterfaceProtocol
+                       != RYOS_USB_INTERFACE_PROTOCOL)
+               return;
+
+       ryos = hid_get_drvdata(hdev);
+       if (ryos->roccat_claimed)
+               roccat_disconnect(ryos->chrdev_minor);
+       kfree(ryos);
+}
+
+static int ryos_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       int retval;
+
+       retval = hid_parse(hdev);
+       if (retval) {
+               hid_err(hdev, "parse failed\n");
+               goto exit;
+       }
+
+       retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (retval) {
+               hid_err(hdev, "hw start failed\n");
+               goto exit;
+       }
+
+       retval = ryos_init_specials(hdev);
+       if (retval) {
+               hid_err(hdev, "couldn't install mouse\n");
+               goto exit_stop;
+       }
+
+       return 0;
+
+exit_stop:
+       hid_hw_stop(hdev);
+exit:
+       return retval;
+}
+
+static void ryos_remove(struct hid_device *hdev)
+{
+       ryos_remove_specials(hdev);
+       hid_hw_stop(hdev);
+}
+
+static int ryos_raw_event(struct hid_device *hdev,
+               struct hid_report *report, u8 *data, int size)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct roccat_common2_device *ryos = hid_get_drvdata(hdev);
+
+       if (intf->cur_altsetting->desc.bInterfaceProtocol
+                       != RYOS_USB_INTERFACE_PROTOCOL)
+               return 0;
+
+       if (data[0] != RYOS_REPORT_NUMBER_SPECIAL)
+               return 0;
+
+       if (ryos != NULL && ryos->roccat_claimed)
+               roccat_report_event(ryos->chrdev_minor, data);
+
+       return 0;
+}
+
+static const struct hid_device_id ryos_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) },
+       { }
+};
+
+MODULE_DEVICE_TABLE(hid, ryos_devices);
+
+static struct hid_driver ryos_driver = {
+               .name = "ryos",
+               .id_table = ryos_devices,
+               .probe = ryos_probe,
+               .remove = ryos_remove,
+               .raw_event = ryos_raw_event
+};
+
+static int __init ryos_init(void)
+{
+       int retval;
+
+       ryos_class = class_create(THIS_MODULE, "ryos");
+       if (IS_ERR(ryos_class))
+               return PTR_ERR(ryos_class);
+       ryos_class->dev_groups = ryos_groups;
+
+       retval = hid_register_driver(&ryos_driver);
+       if (retval)
+               class_destroy(ryos_class);
+       return retval;
+}
+
+static void __exit ryos_exit(void)
+{
+       hid_unregister_driver(&ryos_driver);
+       class_destroy(ryos_class);
+}
+
+module_init(ryos_init);
+module_exit(ryos_exit);
+
+MODULE_AUTHOR("Stefan Achatz");
+MODULE_DESCRIPTION("USB Roccat Ryos MK/Glow/Pro driver");
+MODULE_LICENSE("GPL v2");
index 0332267..6dbf6e0 100644 (file)
 
 static struct class *savu_class;
 
-static ssize_t savu_sysfs_read(struct file *fp, struct kobject *kobj,
-               char *buf, loff_t off, size_t count,
-               size_t real_size, uint command)
-{
-       struct device *dev =
-                       container_of(kobj, struct device, kobj)->parent->parent;
-       struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev));
-       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
-       int retval;
-
-       if (off >= real_size)
-               return 0;
-
-       if (off != 0 || count != real_size)
-               return -EINVAL;
-
-       mutex_lock(&savu->savu_lock);
-       retval = roccat_common2_receive(usb_dev, command, buf, real_size);
-       mutex_unlock(&savu->savu_lock);
-
-       return retval ? retval : real_size;
-}
-
-static ssize_t savu_sysfs_write(struct file *fp, struct kobject *kobj,
-               void const *buf, loff_t off, size_t count,
-               size_t real_size, uint command)
-{
-       struct device *dev =
-                       container_of(kobj, struct device, kobj)->parent->parent;
-       struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev));
-       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
-       int retval;
-
-       if (off != 0 || count != real_size)
-               return -EINVAL;
-
-       mutex_lock(&savu->savu_lock);
-       retval = roccat_common2_send_with_status(usb_dev, command,
-                       (void *)buf, real_size);
-       mutex_unlock(&savu->savu_lock);
-
-       return retval ? retval : real_size;
-}
-
-#define SAVU_SYSFS_W(thingy, THINGY) \
-static ssize_t savu_sysfs_write_ ## thingy(struct file *fp, \
-               struct kobject *kobj, struct bin_attribute *attr, char *buf, \
-               loff_t off, size_t count) \
-{ \
-       return savu_sysfs_write(fp, kobj, buf, off, count, \
-                       SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \
-}
-
-#define SAVU_SYSFS_R(thingy, THINGY) \
-static ssize_t savu_sysfs_read_ ## thingy(struct file *fp, \
-               struct kobject *kobj, struct bin_attribute *attr, char *buf, \
-               loff_t off, size_t count) \
-{ \
-       return savu_sysfs_read(fp, kobj, buf, off, count, \
-                       SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \
-}
-
-#define SAVU_SYSFS_RW(thingy, THINGY) \
-SAVU_SYSFS_W(thingy, THINGY) \
-SAVU_SYSFS_R(thingy, THINGY)
-
-#define SAVU_BIN_ATTRIBUTE_RW(thingy, THINGY) \
-SAVU_SYSFS_RW(thingy, THINGY); \
-static struct bin_attribute bin_attr_##thingy = { \
-       .attr = { .name = #thingy, .mode = 0660 }, \
-       .size = SAVU_SIZE_ ## THINGY, \
-       .read = savu_sysfs_read_ ## thingy, \
-       .write = savu_sysfs_write_ ## thingy \
-}
-
-#define SAVU_BIN_ATTRIBUTE_W(thingy, THINGY) \
-SAVU_SYSFS_W(thingy, THINGY); \
-static struct bin_attribute bin_attr_##thingy = { \
-       .attr = { .name = #thingy, .mode = 0220 }, \
-       .size = SAVU_SIZE_ ## THINGY, \
-       .write = savu_sysfs_write_ ## thingy \
-}
-
-SAVU_BIN_ATTRIBUTE_W(control, CONTROL);
-SAVU_BIN_ATTRIBUTE_RW(profile, PROFILE);
-SAVU_BIN_ATTRIBUTE_RW(general, GENERAL);
-SAVU_BIN_ATTRIBUTE_RW(buttons, BUTTONS);
-SAVU_BIN_ATTRIBUTE_RW(macro, MACRO);
-SAVU_BIN_ATTRIBUTE_RW(info, INFO);
-SAVU_BIN_ATTRIBUTE_RW(sensor, SENSOR);
-
-static struct bin_attribute *savu_bin_attributes[] = {
+ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x4, 0x03);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x5, 0x03);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(general, 0x6, 0x10);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(buttons, 0x7, 0x2f);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x8, 0x0823);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(info, 0x9, 0x08);
+ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0xc, 0x04);
+
+static struct bin_attribute *savu_bin_attrs[] = {
        &bin_attr_control,
        &bin_attr_profile,
        &bin_attr_general,
@@ -130,7 +47,7 @@ static struct bin_attribute *savu_bin_attributes[] = {
 };
 
 static const struct attribute_group savu_group = {
-       .bin_attrs = savu_bin_attributes,
+       .bin_attrs = savu_bin_attrs,
 };
 
 static const struct attribute_group *savu_groups[] = {
@@ -138,19 +55,11 @@ static const struct attribute_group *savu_groups[] = {
        NULL,
 };
 
-static int savu_init_savu_device_struct(struct usb_device *usb_dev,
-               struct savu_device *savu)
-{
-       mutex_init(&savu->savu_lock);
-
-       return 0;
-}
-
 static int savu_init_specials(struct hid_device *hdev)
 {
        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
        struct usb_device *usb_dev = interface_to_usbdev(intf);
-       struct savu_device *savu;
+       struct roccat_common2_device *savu;
        int retval;
 
        if (intf->cur_altsetting->desc.bInterfaceProtocol
@@ -166,9 +75,9 @@ static int savu_init_specials(struct hid_device *hdev)
        }
        hid_set_drvdata(hdev, savu);
 
-       retval = savu_init_savu_device_struct(usb_dev, savu);
+       retval = roccat_common2_device_init_struct(usb_dev, savu);
        if (retval) {
-               hid_err(hdev, "couldn't init struct savu_device\n");
+               hid_err(hdev, "couldn't init Savu device\n");
                goto exit_free;
        }
 
@@ -190,7 +99,7 @@ exit_free:
 static void savu_remove_specials(struct hid_device *hdev)
 {
        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
-       struct savu_device *savu;
+       struct roccat_common2_device *savu;
 
        if (intf->cur_altsetting->desc.bInterfaceProtocol
                        != USB_INTERFACE_PROTOCOL_MOUSE)
@@ -239,7 +148,7 @@ static void savu_remove(struct hid_device *hdev)
        hid_hw_stop(hdev);
 }
 
-static void savu_report_to_chrdev(struct savu_device const *savu,
+static void savu_report_to_chrdev(struct roccat_common2_device const *savu,
                u8 const *data)
 {
        struct savu_roccat_report roccat_report;
@@ -261,7 +170,7 @@ static int savu_raw_event(struct hid_device *hdev,
                struct hid_report *report, u8 *data, int size)
 {
        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
-       struct savu_device *savu = hid_get_drvdata(hdev);
+       struct roccat_common2_device *savu = hid_get_drvdata(hdev);
 
        if (intf->cur_altsetting->desc.bInterfaceProtocol
                        != USB_INTERFACE_PROTOCOL_MOUSE)
index 9120ba7..d23217b 100644 (file)
 
 #include <linux/types.h>
 
-enum {
-       SAVU_SIZE_CONTROL = 0x03,
-       SAVU_SIZE_PROFILE = 0x03,
-       SAVU_SIZE_GENERAL = 0x10,
-       SAVU_SIZE_BUTTONS = 0x2f,
-       SAVU_SIZE_MACRO = 0x0823,
-       SAVU_SIZE_INFO = 0x08,
-       SAVU_SIZE_SENSOR = 0x04,
-};
-
-enum savu_control_requests {
-       SAVU_CONTROL_REQUEST_GENERAL = 0x80,
-       SAVU_CONTROL_REQUEST_BUTTONS = 0x90,
-};
-
-enum savu_commands {
-       SAVU_COMMAND_CONTROL = 0x4,
-       SAVU_COMMAND_PROFILE = 0x5,
-       SAVU_COMMAND_GENERAL = 0x6,
-       SAVU_COMMAND_BUTTONS = 0x7,
-       SAVU_COMMAND_MACRO = 0x8,
-       SAVU_COMMAND_INFO = 0x9,
-       SAVU_COMMAND_SENSOR = 0xc,
-};
-
 struct savu_mouse_report_special {
        uint8_t report_number; /* always 3 */
        uint8_t zero;
@@ -77,11 +52,4 @@ struct savu_roccat_report {
        uint8_t data[2];
 } __packed;
 
-struct savu_device {
-       int roccat_claimed;
-       int chrdev_minor;
-
-       struct mutex savu_lock;
-};
-
 #endif
index 88fc5ae..a184e19 100644 (file)
@@ -326,7 +326,8 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
                                field->logical == attr_usage_id) {
                                sensor_hub_fill_attr_info(info, i, report->id,
                                        field->unit, field->unit_exponent,
-                                       field->report_size);
+                                       field->report_size *
+                                                       field->report_count);
                                ret = 0;
                        } else {
                                for (j = 0; j < field->maxusage; ++j) {
@@ -338,7 +339,8 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
                                                        i, report->id,
                                                        field->unit,
                                                        field->unit_exponent,
-                                                       field->report_size);
+                                                       field->report_size *
+                                                       field->report_count);
                                                ret = 0;
                                                break;
                                        }
@@ -425,9 +427,10 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
                hid_dbg(hdev, "%d collection_index:%x hid:%x sz:%x\n",
                                i, report->field[i]->usage->collection_index,
                                report->field[i]->usage->hid,
-                               report->field[i]->report_size/8);
-
-               sz = report->field[i]->report_size/8;
+                               (report->field[i]->report_size *
+                                       report->field[i]->report_count)/8);
+               sz = (report->field[i]->report_size *
+                                       report->field[i]->report_count)/8;
                if (pdata->pending.status && pdata->pending.attr_usage_id ==
                                report->field[i]->usage->hid) {
                        hid_dbg(hdev, "data was pending ...\n");
index b18320d..da551d1 100644 (file)
@@ -419,21 +419,14 @@ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
  */
 static int sixaxis_set_operational_usb(struct hid_device *hdev)
 {
-       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
-       struct usb_device *dev = interface_to_usbdev(intf);
-       __u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
        int ret;
        char *buf = kmalloc(18, GFP_KERNEL);
 
        if (!buf)
                return -ENOMEM;
 
-       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-                                HID_REQ_GET_REPORT,
-                                USB_DIR_IN | USB_TYPE_CLASS |
-                                USB_RECIP_INTERFACE,
-                                (3 << 8) | 0xf2, ifnum, buf, 17,
-                                USB_CTRL_GET_TIMEOUT);
+       ret = hdev->hid_get_raw_report(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT);
+
        if (ret < 0)
                hid_err(hdev, "can't set operational mode\n");
 
@@ -621,6 +614,54 @@ static void buzz_remove(struct hid_device *hdev)
        drv_data->extra = NULL;
 }
 
+#ifdef CONFIG_SONY_FF
+static int sony_play_effect(struct input_dev *dev, void *data,
+                           struct ff_effect *effect)
+{
+       unsigned char buf[] = {
+               0x01,
+               0x00, 0xff, 0x00, 0xff, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x03,
+               0xff, 0x27, 0x10, 0x00, 0x32,
+               0xff, 0x27, 0x10, 0x00, 0x32,
+               0xff, 0x27, 0x10, 0x00, 0x32,
+               0xff, 0x27, 0x10, 0x00, 0x32,
+               0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       __u8 left;
+       __u8 right;
+       struct hid_device *hid = input_get_drvdata(dev);
+
+       if (effect->type != FF_RUMBLE)
+               return 0;
+
+       left = effect->u.rumble.strong_magnitude / 256;
+       right = effect->u.rumble.weak_magnitude ? 1 : 0;
+
+       buf[3] = right;
+       buf[5] = left;
+
+       return hid->hid_output_raw_report(hid, buf, sizeof(buf),
+                                         HID_OUTPUT_REPORT);
+}
+
+static int sony_init_ff(struct hid_device *hdev)
+{
+       struct hid_input *hidinput = list_entry(hdev->inputs.next,
+                                               struct hid_input, list);
+       struct input_dev *input_dev = hidinput->input;
+
+       input_set_capability(input_dev, EV_FF, FF_RUMBLE);
+       return input_ff_create_memless(input_dev, NULL, sony_play_effect);
+}
+
+#else
+static int sony_init_ff(struct hid_device *hdev)
+{
+       return 0;
+}
+#endif
+
 static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        int ret;
@@ -670,6 +711,10 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (ret < 0)
                goto err_stop;
 
+       ret = sony_init_ff(hdev);
+       if (ret < 0)
+               goto err_stop;
+
        return 0;
 err_stop:
        hid_hw_stop(hdev);
index 71adf9e..6b61f01 100644 (file)
@@ -1655,10 +1655,39 @@ static void wiimod_pro_in_ext(struct wiimote_data *wdata, const __u8 *ext)
        ly = (ext[4] & 0xff) | ((ext[5] & 0x0f) << 8);
        ry = (ext[6] & 0xff) | ((ext[7] & 0x0f) << 8);
 
-       input_report_abs(wdata->extension.input, ABS_X, lx - 0x800);
-       input_report_abs(wdata->extension.input, ABS_Y, ly - 0x800);
-       input_report_abs(wdata->extension.input, ABS_RX, rx - 0x800);
-       input_report_abs(wdata->extension.input, ABS_RY, ry - 0x800);
+       /* zero-point offsets */
+       lx -= 0x800;
+       ly = 0x800 - ly;
+       rx -= 0x800;
+       ry = 0x800 - ry;
+
+       /* Trivial automatic calibration. We don't know any calibration data
+        * in the EEPROM so we must use the first report to calibrate the
+        * null-position of the analog sticks. Users can retrigger calibration
+        * via sysfs, or set it explicitly. If data is off more than abs(500),
+        * we skip calibration as the sticks are likely to be moved already. */
+       if (!(wdata->state.flags & WIIPROTO_FLAG_PRO_CALIB_DONE)) {
+               wdata->state.flags |= WIIPROTO_FLAG_PRO_CALIB_DONE;
+               if (abs(lx) < 500)
+                       wdata->state.calib_pro_sticks[0] = -lx;
+               if (abs(ly) < 500)
+                       wdata->state.calib_pro_sticks[1] = -ly;
+               if (abs(rx) < 500)
+                       wdata->state.calib_pro_sticks[2] = -rx;
+               if (abs(ry) < 500)
+                       wdata->state.calib_pro_sticks[3] = -ry;
+       }
+
+       /* apply calibration data */
+       lx += wdata->state.calib_pro_sticks[0];
+       ly += wdata->state.calib_pro_sticks[1];
+       rx += wdata->state.calib_pro_sticks[2];
+       ry += wdata->state.calib_pro_sticks[3];
+
+       input_report_abs(wdata->extension.input, ABS_X, lx);
+       input_report_abs(wdata->extension.input, ABS_Y, ly);
+       input_report_abs(wdata->extension.input, ABS_RX, rx);
+       input_report_abs(wdata->extension.input, ABS_RY, ry);
 
        input_report_key(wdata->extension.input,
                         wiimod_pro_map[WIIMOD_PRO_KEY_RIGHT],
@@ -1766,12 +1795,70 @@ static int wiimod_pro_play(struct input_dev *dev, void *data,
        return 0;
 }
 
+static ssize_t wiimod_pro_calib_show(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *out)
+{
+       struct wiimote_data *wdata = dev_to_wii(dev);
+       int r;
+
+       r = 0;
+       r += sprintf(&out[r], "%+06hd:", wdata->state.calib_pro_sticks[0]);
+       r += sprintf(&out[r], "%+06hd ", wdata->state.calib_pro_sticks[1]);
+       r += sprintf(&out[r], "%+06hd:", wdata->state.calib_pro_sticks[2]);
+       r += sprintf(&out[r], "%+06hd\n", wdata->state.calib_pro_sticks[3]);
+
+       return r;
+}
+
+static ssize_t wiimod_pro_calib_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       struct wiimote_data *wdata = dev_to_wii(dev);
+       int r;
+       s16 x1, y1, x2, y2;
+
+       if (!strncmp(buf, "scan\n", 5)) {
+               spin_lock_irq(&wdata->state.lock);
+               wdata->state.flags &= ~WIIPROTO_FLAG_PRO_CALIB_DONE;
+               spin_unlock_irq(&wdata->state.lock);
+       } else {
+               r = sscanf(buf, "%hd:%hd %hd:%hd", &x1, &y1, &x2, &y2);
+               if (r != 4)
+                       return -EINVAL;
+
+               spin_lock_irq(&wdata->state.lock);
+               wdata->state.flags |= WIIPROTO_FLAG_PRO_CALIB_DONE;
+               spin_unlock_irq(&wdata->state.lock);
+
+               wdata->state.calib_pro_sticks[0] = x1;
+               wdata->state.calib_pro_sticks[1] = y1;
+               wdata->state.calib_pro_sticks[2] = x2;
+               wdata->state.calib_pro_sticks[3] = y2;
+       }
+
+       return strnlen(buf, PAGE_SIZE);
+}
+
+static DEVICE_ATTR(pro_calib, S_IRUGO|S_IWUSR|S_IWGRP, wiimod_pro_calib_show,
+                  wiimod_pro_calib_store);
+
 static int wiimod_pro_probe(const struct wiimod_ops *ops,
                            struct wiimote_data *wdata)
 {
        int ret, i;
+       unsigned long flags;
 
        INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker);
+       wdata->state.calib_pro_sticks[0] = 0;
+       wdata->state.calib_pro_sticks[1] = 0;
+       wdata->state.calib_pro_sticks[2] = 0;
+       wdata->state.calib_pro_sticks[3] = 0;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wdata->state.flags &= ~WIIPROTO_FLAG_PRO_CALIB_DONE;
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
 
        wdata->extension.input = input_allocate_device();
        if (!wdata->extension.input)
@@ -1786,6 +1873,13 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops,
                goto err_free;
        }
 
+       ret = device_create_file(&wdata->hdev->dev,
+                                &dev_attr_pro_calib);
+       if (ret) {
+               hid_err(wdata->hdev, "cannot create sysfs attribute\n");
+               goto err_free;
+       }
+
        wdata->extension.input->open = wiimod_pro_open;
        wdata->extension.input->close = wiimod_pro_close;
        wdata->extension.input->dev.parent = &wdata->hdev->dev;
@@ -1806,20 +1900,23 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops,
        set_bit(ABS_RX, wdata->extension.input->absbit);
        set_bit(ABS_RY, wdata->extension.input->absbit);
        input_set_abs_params(wdata->extension.input,
-                            ABS_X, -0x800, 0x800, 2, 4);
+                            ABS_X, -0x400, 0x400, 4, 100);
        input_set_abs_params(wdata->extension.input,
-                            ABS_Y, -0x800, 0x800, 2, 4);
+                            ABS_Y, -0x400, 0x400, 4, 100);
        input_set_abs_params(wdata->extension.input,
-                            ABS_RX, -0x800, 0x800, 2, 4);
+                            ABS_RX, -0x400, 0x400, 4, 100);
        input_set_abs_params(wdata->extension.input,
-                            ABS_RY, -0x800, 0x800, 2, 4);
+                            ABS_RY, -0x400, 0x400, 4, 100);
 
        ret = input_register_device(wdata->extension.input);
        if (ret)
-               goto err_free;
+               goto err_file;
 
        return 0;
 
+err_file:
+       device_remove_file(&wdata->hdev->dev,
+                          &dev_attr_pro_calib);
 err_free:
        input_free_device(wdata->extension.input);
        wdata->extension.input = NULL;
@@ -1837,6 +1934,8 @@ static void wiimod_pro_remove(const struct wiimod_ops *ops,
        input_unregister_device(wdata->extension.input);
        wdata->extension.input = NULL;
        cancel_work_sync(&wdata->rumble_worker);
+       device_remove_file(&wdata->hdev->dev,
+                          &dev_attr_pro_calib);
 
        spin_lock_irqsave(&wdata->state.lock, flags);
        wiiproto_req_rumble(wdata, 0);
index 75db0c4..10934aa 100644 (file)
@@ -46,6 +46,7 @@
 #define WIIPROTO_FLAG_DRM_LOCKED       0x8000
 #define WIIPROTO_FLAG_BUILTIN_MP       0x010000
 #define WIIPROTO_FLAG_NO_MP            0x020000
+#define WIIPROTO_FLAG_PRO_CALIB_DONE   0x040000
 
 #define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
                                        WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
@@ -135,6 +136,7 @@ struct wiimote_state {
 
        /* calibration/cache data */
        __u16 calib_bboard[4][3];
+       __s16 calib_pro_sticks[4];
        __u8 cache_rumble;
 };
 
@@ -327,7 +329,7 @@ static inline void wiimote_cmd_acquire_noint(struct wiimote_data *wdata)
 static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd,
                                                                __u32 opt)
 {
-       INIT_COMPLETION(wdata->state.ready);
+       reinit_completion(&wdata->state.ready);
        wdata->state.cmd = cmd;
        wdata->state.opt = opt;
 }
index fd7ce37..5f7e55f 100644 (file)
@@ -455,10 +455,6 @@ static void i2c_hid_init_reports(struct hid_device *hid)
        }
 
        list_for_each_entry(report,
-               &hid->report_enum[HID_INPUT_REPORT].report_list, list)
-               i2c_hid_init_report(report, inbuf, ihid->bufsize);
-
-       list_for_each_entry(report,
                &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
                i2c_hid_init_report(report, inbuf, ihid->bufsize);
 
@@ -1012,7 +1008,7 @@ static int i2c_hid_probe(struct i2c_client *client,
        hid->hid_get_raw_report = i2c_hid_get_raw_report;
        hid->hid_output_raw_report = i2c_hid_output_raw_report;
        hid->dev.parent = &client->dev;
-       ACPI_HANDLE_SET(&hid->dev, ACPI_HANDLE(&client->dev));
+       ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
        hid->bus = BUS_I2C;
        hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
        hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
index 3fca3be..0db9a67 100644 (file)
@@ -84,6 +84,8 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_REALTEK, USB_DEVICE_ID_REALTEK_READER, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_SIS2_TOUCH, USB_DEVICE_ID_SIS9200_TOUCH, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_SIS2_TOUCH, USB_DEVICE_ID_SIS817_TOUCH, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_1, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET },
index b3ab9d4..52d548f 100644 (file)
@@ -656,6 +656,7 @@ config SENSORS_LM75
 
                - Analog Devices ADT75
                - Dallas Semiconductor DS75, DS1775 and DS7505
+               - Global Mixed-mode Technology (GMT) G751
                - Maxim MAX6625 and MAX6626
                - Microchip MCP980x
                - National Semiconductor LM75, LM75A
index 8d40da3..6a34f7f 100644 (file)
@@ -602,9 +602,8 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
 
                /* Create a symlink to domain objects */
                resource->domain_devices[i] = NULL;
-               status = acpi_bus_get_device(element->reference.handle,
-                                            &resource->domain_devices[i]);
-               if (ACPI_FAILURE(status))
+               if (acpi_bus_get_device(element->reference.handle,
+                                       &resource->domain_devices[i]))
                        continue;
 
                obj = resource->domain_devices[i];
index e0d66b9..a183e48 100644 (file)
@@ -66,7 +66,7 @@ static ssize_t jz4740_hwmon_read_adcin(struct device *dev,
 
        mutex_lock(&hwmon->lock);
 
-       INIT_COMPLETION(*completion);
+       reinit_completion(completion);
 
        enable_irq(hwmon->irq);
        hwmon->cell->enable(to_platform_device(dev));
index c03b490..7e3ef13 100644 (file)
@@ -39,6 +39,7 @@ enum lm75_type {              /* keep sorted in alphabetical order */
        ds1775,
        ds75,
        ds7505,
+       g751,
        lm75,
        lm75a,
        max6625,
@@ -208,6 +209,7 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
                data->resolution = 12;
                data->sample_time = HZ / 4;
                break;
+       case g751:
        case lm75:
        case lm75a:
                data->resolution = 9;
@@ -296,6 +298,7 @@ static const struct i2c_device_id lm75_ids[] = {
        { "ds1775", ds1775, },
        { "ds75", ds75, },
        { "ds7505", ds7505, },
+       { "g751", g751, },
        { "lm75", lm75, },
        { "lm75a", lm75a, },
        { "max6625", max6625, },
index cdff742..4c4c142 100644 (file)
  * This driver also supports the G781 from GMT. This device is compatible
  * with the ADM1032.
  *
+ * This driver also supports TMP451 from Texas Instruments. This device is
+ * supported in both compatibility and extended mode. It's mostly compatible
+ * with ADT7461 except for local temperature low byte register and max
+ * conversion rate.
+ *
  * Since the LM90 was the first chipset supported by this driver, most
  * comments will refer to this chipset, but are actually general and
  * concern all supported chipsets, unless mentioned otherwise.
@@ -89,6 +94,8 @@
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
 
 /*
  * Addresses to scan
@@ -110,7 +117,7 @@ static const unsigned short normal_i2c[] = {
        0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
 
 enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
-       max6646, w83l771, max6696, sa56004, g781 };
+       max6646, w83l771, max6696, sa56004, g781, tmp451 };
 
 /*
  * The LM90 registers
@@ -167,6 +174,9 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
 #define LM90_DEF_CONVRATE_RVAL 6       /* Def conversion rate register value */
 #define LM90_MAX_CONVRATE_MS   16000   /* Maximum conversion rate in ms */
 
+/* TMP451 registers */
+#define TMP451_REG_R_LOCAL_TEMPL       0x15
+
 /*
  * Device flags
  */
@@ -179,6 +189,23 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
 #define LM90_HAVE_TEMP3                (1 << 6) /* 3rd temperature sensor      */
 #define LM90_HAVE_BROKEN_ALERT (1 << 7) /* Broken alert                */
 
+/* LM90 status */
+#define LM90_STATUS_LTHRM      (1 << 0) /* local THERM limit tripped */
+#define LM90_STATUS_RTHRM      (1 << 1) /* remote THERM limit tripped */
+#define LM90_STATUS_ROPEN      (1 << 2) /* remote is an open circuit */
+#define LM90_STATUS_RLOW       (1 << 3) /* remote low temp limit tripped */
+#define LM90_STATUS_RHIGH      (1 << 4) /* remote high temp limit tripped */
+#define LM90_STATUS_LLOW       (1 << 5) /* local low temp limit tripped */
+#define LM90_STATUS_LHIGH      (1 << 6) /* local high temp limit tripped */
+
+#define MAX6696_STATUS2_R2THRM (1 << 1) /* remote2 THERM limit tripped */
+#define MAX6696_STATUS2_R2OPEN (1 << 2) /* remote2 is an open circuit */
+#define MAX6696_STATUS2_R2LOW  (1 << 3) /* remote2 low temp limit tripped */
+#define MAX6696_STATUS2_R2HIGH (1 << 4) /* remote2 high temp limit tripped */
+#define MAX6696_STATUS2_ROT2   (1 << 5) /* remote emergency limit tripped */
+#define MAX6696_STATUS2_R2OT2  (1 << 6) /* remote2 emergency limit tripped */
+#define MAX6696_STATUS2_LOT2   (1 << 7) /* local emergency limit tripped */
+
 /*
  * Driver data (common to all clients)
  */
@@ -205,6 +232,7 @@ static const struct i2c_device_id lm90_id[] = {
        { "nct1008", adt7461 },
        { "w83l771", w83l771 },
        { "sa56004", sa56004 },
+       { "tmp451", tmp451 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm90_id);
@@ -278,7 +306,7 @@ static const struct lm90_params lm90_params[] = {
        [max6696] = {
                .flags = LM90_HAVE_EMERGENCY
                  | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3,
-               .alert_alarms = 0x187c,
+               .alert_alarms = 0x1c7c,
                .max_convrate = 6,
                .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
        },
@@ -293,6 +321,43 @@ static const struct lm90_params lm90_params[] = {
                .max_convrate = 9,
                .reg_local_ext = SA56004_REG_R_LOCAL_TEMPL,
        },
+       [tmp451] = {
+               .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
+                 | LM90_HAVE_BROKEN_ALERT,
+               .alert_alarms = 0x7c,
+               .max_convrate = 9,
+               .reg_local_ext = TMP451_REG_R_LOCAL_TEMPL,
+       }
+};
+
+/*
+ * TEMP8 register index
+ */
+enum lm90_temp8_reg_index {
+       LOCAL_LOW = 0,
+       LOCAL_HIGH,
+       LOCAL_CRIT,
+       REMOTE_CRIT,
+       LOCAL_EMERG,    /* max6659 and max6695/96 */
+       REMOTE_EMERG,   /* max6659 and max6695/96 */
+       REMOTE2_CRIT,   /* max6695/96 only */
+       REMOTE2_EMERG,  /* max6695/96 only */
+       TEMP8_REG_NUM
+};
+
+/*
+ * TEMP11 register index
+ */
+enum lm90_temp11_reg_index {
+       REMOTE_TEMP = 0,
+       REMOTE_LOW,
+       REMOTE_HIGH,
+       REMOTE_OFFSET,  /* except max6646, max6657/58/59, and max6695/96 */
+       LOCAL_TEMP,
+       REMOTE2_TEMP,   /* max6695/96 only */
+       REMOTE2_LOW,    /* max6695/96 only */
+       REMOTE2_HIGH,   /* max6695/96 only */
+       TEMP11_REG_NUM
 };
 
 /*
@@ -302,6 +367,7 @@ static const struct lm90_params lm90_params[] = {
 struct lm90_data {
        struct device *hwmon_dev;
        struct mutex update_lock;
+       struct regulator *regulator;
        char valid; /* zero until following fields are valid */
        unsigned long last_updated; /* in jiffies */
        int kind;
@@ -317,25 +383,8 @@ struct lm90_data {
        u8 reg_local_ext;       /* local extension register offset */
 
        /* registers values */
-       s8 temp8[8];    /* 0: local low limit
-                        * 1: local high limit
-                        * 2: local critical limit
-                        * 3: remote critical limit
-                        * 4: local emergency limit (max6659 and max6695/96)
-                        * 5: remote emergency limit (max6659 and max6695/96)
-                        * 6: remote 2 critical limit (max6695/96 only)
-                        * 7: remote 2 emergency limit (max6695/96 only)
-                        */
-       s16 temp11[8];  /* 0: remote input
-                        * 1: remote low limit
-                        * 2: remote high limit
-                        * 3: remote offset (except max6646, max6657/58/59,
-                        *                   and max6695/96)
-                        * 4: local input
-                        * 5: remote 2 input (max6695/96 only)
-                        * 6: remote 2 low limit (max6695/96 only)
-                        * 7: remote 2 high limit (max6695/96 only)
-                        */
+       s8 temp8[TEMP8_REG_NUM];
+       s16 temp11[TEMP11_REG_NUM];
        u8 temp_hyst;
        u16 alarms; /* bitvector (upper 8 bits for max6695/96) */
 };
@@ -477,37 +526,42 @@ static struct lm90_data *lm90_update_device(struct device *dev)
                u8 alarms;
 
                dev_dbg(&client->dev, "Updating lm90 data.\n");
-               lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]);
-               lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]);
-               lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]);
-               lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]);
+               lm90_read_reg(client, LM90_REG_R_LOCAL_LOW,
+                             &data->temp8[LOCAL_LOW]);
+               lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH,
+                             &data->temp8[LOCAL_HIGH]);
+               lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT,
+                             &data->temp8[LOCAL_CRIT]);
+               lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT,
+                             &data->temp8[REMOTE_CRIT]);
                lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst);
 
                if (data->reg_local_ext) {
                        lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
                                    data->reg_local_ext,
-                                   &data->temp11[4]);
+                                   &data->temp11[LOCAL_TEMP]);
                } else {
                        if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP,
                                          &h) == 0)
-                               data->temp11[4] = h << 8;
+                               data->temp11[LOCAL_TEMP] = h << 8;
                }
                lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
-                           LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]);
+                           LM90_REG_R_REMOTE_TEMPL,
+                           &data->temp11[REMOTE_TEMP]);
 
                if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) {
-                       data->temp11[1] = h << 8;
+                       data->temp11[REMOTE_LOW] = h << 8;
                        if ((data->flags & LM90_HAVE_REM_LIMIT_EXT)
                         && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL,
                                          &l) == 0)
-                               data->temp11[1] |= l;
+                               data->temp11[REMOTE_LOW] |= l;
                }
                if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) {
-                       data->temp11[2] = h << 8;
+                       data->temp11[REMOTE_HIGH] = h << 8;
                        if ((data->flags & LM90_HAVE_REM_LIMIT_EXT)
                         && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL,
                                          &l) == 0)
-                               data->temp11[2] |= l;
+                               data->temp11[REMOTE_HIGH] |= l;
                }
 
                if (data->flags & LM90_HAVE_OFFSET) {
@@ -515,13 +569,13 @@ static struct lm90_data *lm90_update_device(struct device *dev)
                                          &h) == 0
                         && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL,
                                          &l) == 0)
-                               data->temp11[3] = (h << 8) | l;
+                               data->temp11[REMOTE_OFFSET] = (h << 8) | l;
                }
                if (data->flags & LM90_HAVE_EMERGENCY) {
                        lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG,
-                                     &data->temp8[4]);
+                                     &data->temp8[LOCAL_EMERG]);
                        lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG,
-                                     &data->temp8[5]);
+                                     &data->temp8[REMOTE_EMERG]);
                }
                lm90_read_reg(client, LM90_REG_R_STATUS, &alarms);
                data->alarms = alarms;  /* save as 16 bit value */
@@ -529,15 +583,16 @@ static struct lm90_data *lm90_update_device(struct device *dev)
                if (data->kind == max6696) {
                        lm90_select_remote_channel(client, data, 1);
                        lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT,
-                                     &data->temp8[6]);
+                                     &data->temp8[REMOTE2_CRIT]);
                        lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG,
-                                     &data->temp8[7]);
+                                     &data->temp8[REMOTE2_EMERG]);
                        lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
-                                   LM90_REG_R_REMOTE_TEMPL, &data->temp11[5]);
+                                   LM90_REG_R_REMOTE_TEMPL,
+                                   &data->temp11[REMOTE2_TEMP]);
                        if (!lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h))
-                               data->temp11[6] = h << 8;
+                               data->temp11[REMOTE2_LOW] = h << 8;
                        if (!lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h))
-                               data->temp11[7] = h << 8;
+                               data->temp11[REMOTE2_HIGH] = h << 8;
                        lm90_select_remote_channel(client, data, 0);
 
                        if (!lm90_read_reg(client, MAX6696_REG_R_STATUS2,
@@ -709,7 +764,7 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
        struct lm90_data *data = lm90_update_device(dev);
        int temp;
 
-       if (data->kind == adt7461)
+       if (data->kind == adt7461 || data->kind == tmp451)
                temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
        else if (data->kind == max6646)
                temp = temp_from_u8(data->temp8[attr->index]);
@@ -726,7 +781,7 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
 static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
                         const char *buf, size_t count)
 {
-       static const u8 reg[8] = {
+       static const u8 reg[TEMP8_REG_NUM] = {
                LM90_REG_W_LOCAL_LOW,
                LM90_REG_W_LOCAL_HIGH,
                LM90_REG_W_LOCAL_CRIT,
@@ -753,7 +808,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
                val -= 16000;
 
        mutex_lock(&data->update_lock);
-       if (data->kind == adt7461)
+       if (data->kind == adt7461 || data->kind == tmp451)
                data->temp8[nr] = temp_to_u8_adt7461(data, val);
        else if (data->kind == max6646)
                data->temp8[nr] = temp_to_u8(val);
@@ -775,7 +830,7 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
        struct lm90_data *data = lm90_update_device(dev);
        int temp;
 
-       if (data->kind == adt7461)
+       if (data->kind == adt7461 || data->kind == tmp451)
                temp = temp_from_u16_adt7461(data, data->temp11[attr->index]);
        else if (data->kind == max6646)
                temp = temp_from_u16(data->temp11[attr->index]);
@@ -821,7 +876,7 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
                val -= 16000;
 
        mutex_lock(&data->update_lock);
-       if (data->kind == adt7461)
+       if (data->kind == adt7461 || data->kind == tmp451)
                data->temp11[index] = temp_to_u16_adt7461(data, val);
        else if (data->kind == max6646)
                data->temp11[index] = temp_to_u8(val) << 8;
@@ -850,7 +905,7 @@ static ssize_t show_temphyst(struct device *dev,
        struct lm90_data *data = lm90_update_device(dev);
        int temp;
 
-       if (data->kind == adt7461)
+       if (data->kind == adt7461 || data->kind == tmp451)
                temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
        else if (data->kind == max6646)
                temp = temp_from_u8(data->temp8[attr->index]);
@@ -878,12 +933,12 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
                return err;
 
        mutex_lock(&data->update_lock);
-       if (data->kind == adt7461)
-               temp = temp_from_u8_adt7461(data, data->temp8[2]);
+       if (data->kind == adt7461 || data->kind == tmp451)
+               temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]);
        else if (data->kind == max6646)
-               temp = temp_from_u8(data->temp8[2]);
+               temp = temp_from_u8(data->temp8[LOCAL_CRIT]);
        else
-               temp = temp_from_s8(data->temp8[2]);
+               temp = temp_from_s8(data->temp8[LOCAL_CRIT]);
 
        data->temp_hyst = hyst_to_reg(temp - val);
        i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
@@ -937,25 +992,28 @@ static ssize_t set_update_interval(struct device *dev,
        return count;
 }
 
-static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL, 0, 4);
-static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL,
+       0, LOCAL_TEMP);
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL,
+       0, REMOTE_TEMP);
 static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
-       set_temp8, 0);
+       set_temp8, LOCAL_LOW);
 static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
-       set_temp11, 0, 1);
+       set_temp11, 0, REMOTE_LOW);
 static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8,
-       set_temp8, 1);
+       set_temp8, LOCAL_HIGH);
 static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
-       set_temp11, 1, 2);
+       set_temp11, 1, REMOTE_HIGH);
 static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8,
-       set_temp8, 2);
+       set_temp8, LOCAL_CRIT);
 static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8,
-       set_temp8, 3);
+       set_temp8, REMOTE_CRIT);
 static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst,
-       set_temphyst, 2);
-static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3);
+       set_temphyst, LOCAL_CRIT);
+static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL,
+       REMOTE_CRIT);
 static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IWUSR | S_IRUGO, show_temp11,
-       set_temp11, 2, 3);
+       set_temp11, 2, REMOTE_OFFSET);
 
 /* Individual alarm files */
 static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
@@ -1003,13 +1061,13 @@ static const struct attribute_group lm90_group = {
  * Additional attributes for devices with emergency sensors
  */
 static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO, show_temp8,
-       set_temp8, 4);
+       set_temp8, LOCAL_EMERG);
 static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO, show_temp8,
-       set_temp8, 5);
+       set_temp8, REMOTE_EMERG);
 static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO, show_temphyst,
-                         NULL, 4);
+                         NULL, LOCAL_EMERG);
 static SENSOR_DEVICE_ATTR(temp2_emergency_hyst, S_IRUGO, show_temphyst,
-                         NULL, 5);
+                         NULL, REMOTE_EMERG);
 
 static struct attribute *lm90_emergency_attributes[] = {
        &sensor_dev_attr_temp1_emergency.dev_attr.attr,
@@ -1039,18 +1097,20 @@ static const struct attribute_group lm90_emergency_alarm_group = {
 /*
  * Additional attributes for devices with 3 temperature sensors
  */
-static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp11, NULL, 0, 5);
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp11, NULL,
+       0, REMOTE2_TEMP);
 static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp11,
-       set_temp11, 3, 6);
+       set_temp11, 3, REMOTE2_LOW);
 static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp11,
-       set_temp11, 4, 7);
+       set_temp11, 4, REMOTE2_HIGH);
 static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp8,
-       set_temp8, 6);
-static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL, 6);
+       set_temp8, REMOTE2_CRIT);
+static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL,
+       REMOTE2_CRIT);
 static SENSOR_DEVICE_ATTR(temp3_emergency, S_IWUSR | S_IRUGO, show_temp8,
-       set_temp8, 7);
+       set_temp8, REMOTE2_EMERG);
 static SENSOR_DEVICE_ATTR(temp3_emergency_hyst, S_IRUGO, show_temphyst,
-                         NULL, 7);
+                         NULL, REMOTE2_EMERG);
 
 static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9);
 static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 10);
@@ -1306,6 +1366,19 @@ static int lm90_detect(struct i2c_client *client,
                 && (config1 & 0x3F) == 0x00
                 && convrate <= 0x08)
                        name = "g781";
+       } else
+       if (address == 0x4C
+        && man_id == 0x55) { /* Texas Instruments */
+               int local_ext;
+
+               local_ext = i2c_smbus_read_byte_data(client,
+                                                    TMP451_REG_R_LOCAL_TEMPL);
+
+               if (chip_id == 0x00 /* TMP451 */
+                && (config1 & 0x1B) == 0x00
+                && convrate <= 0x09
+                && (local_ext & 0x0F) == 0x00)
+                       name = "tmp451";
        }
 
        if (!name) { /* identification failed */
@@ -1367,7 +1440,7 @@ static void lm90_init_client(struct i2c_client *client)
        data->config_orig = config;
 
        /* Check Temperature Range Select */
-       if (data->kind == adt7461) {
+       if (data->kind == adt7461 || data->kind == tmp451) {
                if (config & 0x04)
                        data->flags |= LM90_FLAG_ADT7461_EXT;
        }
@@ -1391,14 +1464,74 @@ static void lm90_init_client(struct i2c_client *client)
                i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config);
 }
 
+static bool lm90_is_tripped(struct i2c_client *client, u16 *status)
+{
+       struct lm90_data *data = i2c_get_clientdata(client);
+       u8 st, st2 = 0;
+
+       lm90_read_reg(client, LM90_REG_R_STATUS, &st);
+
+       if (data->kind == max6696)
+               lm90_read_reg(client, MAX6696_REG_R_STATUS2, &st2);
+
+       *status = st | (st2 << 8);
+
+       if ((st & 0x7f) == 0 && (st2 & 0xfe) == 0)
+               return false;
+
+       if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) ||
+           (st2 & MAX6696_STATUS2_LOT2))
+               dev_warn(&client->dev,
+                        "temp%d out of range, please check!\n", 1);
+       if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) ||
+           (st2 & MAX6696_STATUS2_ROT2))
+               dev_warn(&client->dev,
+                        "temp%d out of range, please check!\n", 2);
+       if (st & LM90_STATUS_ROPEN)
+               dev_warn(&client->dev,
+                        "temp%d diode open, please check!\n", 2);
+       if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH |
+                  MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2))
+               dev_warn(&client->dev,
+                        "temp%d out of range, please check!\n", 3);
+       if (st2 & MAX6696_STATUS2_R2OPEN)
+               dev_warn(&client->dev,
+                        "temp%d diode open, please check!\n", 3);
+
+       return true;
+}
+
+static irqreturn_t lm90_irq_thread(int irq, void *dev_id)
+{
+       struct i2c_client *client = dev_id;
+       u16 status;
+
+       if (lm90_is_tripped(client, &status))
+               return IRQ_HANDLED;
+       else
+               return IRQ_NONE;
+}
+
 static int lm90_probe(struct i2c_client *client,
                      const struct i2c_device_id *id)
 {
        struct device *dev = &client->dev;
        struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
        struct lm90_data *data;
+       struct regulator *regulator;
        int err;
 
+       regulator = devm_regulator_get(dev, "vcc");
+       if (IS_ERR(regulator))
+               return PTR_ERR(regulator);
+
+       err = regulator_enable(regulator);
+       if (err < 0) {
+               dev_err(&client->dev,
+                       "Failed to enable regulator: %d\n", err);
+               return err;
+       }
+
        data = devm_kzalloc(&client->dev, sizeof(struct lm90_data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
@@ -1406,6 +1539,8 @@ static int lm90_probe(struct i2c_client *client,
        i2c_set_clientdata(client, data);
        mutex_init(&data->update_lock);
 
+       data->regulator = regulator;
+
        /* Set the device type */
        data->kind = id->driver_data;
        if (data->kind == adm1032) {
@@ -1467,12 +1602,26 @@ static int lm90_probe(struct i2c_client *client,
                goto exit_remove_files;
        }
 
+       if (client->irq) {
+               dev_dbg(dev, "IRQ: %d\n", client->irq);
+               err = devm_request_threaded_irq(dev, client->irq,
+                                               NULL, lm90_irq_thread,
+                                               IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                               "lm90", client);
+               if (err < 0) {
+                       dev_err(dev, "cannot request IRQ %d\n", client->irq);
+                       goto exit_remove_files;
+               }
+       }
+
        return 0;
 
 exit_remove_files:
        lm90_remove_files(client, data);
 exit_restore:
        lm90_restore_conf(client, data);
+       regulator_disable(data->regulator);
+
        return err;
 }
 
@@ -1483,49 +1632,33 @@ static int lm90_remove(struct i2c_client *client)
        hwmon_device_unregister(data->hwmon_dev);
        lm90_remove_files(client, data);
        lm90_restore_conf(client, data);
+       regulator_disable(data->regulator);
 
        return 0;
 }
 
 static void lm90_alert(struct i2c_client *client, unsigned int flag)
 {
-       struct lm90_data *data = i2c_get_clientdata(client);
-       u8 config, alarms, alarms2 = 0;
-
-       lm90_read_reg(client, LM90_REG_R_STATUS, &alarms);
-
-       if (data->kind == max6696)
-               lm90_read_reg(client, MAX6696_REG_R_STATUS2, &alarms2);
-
-       if ((alarms & 0x7f) == 0 && (alarms2 & 0xfe) == 0) {
-               dev_info(&client->dev, "Everything OK\n");
-       } else {
-               if (alarms & 0x61)
-                       dev_warn(&client->dev,
-                                "temp%d out of range, please check!\n", 1);
-               if (alarms & 0x1a)
-                       dev_warn(&client->dev,
-                                "temp%d out of range, please check!\n", 2);
-               if (alarms & 0x04)
-                       dev_warn(&client->dev,
-                                "temp%d diode open, please check!\n", 2);
-
-               if (alarms2 & 0x18)
-                       dev_warn(&client->dev,
-                                "temp%d out of range, please check!\n", 3);
+       u16 alarms;
 
+       if (lm90_is_tripped(client, &alarms)) {
                /*
                 * Disable ALERT# output, because these chips don't implement
                 * SMBus alert correctly; they should only hold the alert line
                 * low briefly.
                 */
+               struct lm90_data *data = i2c_get_clientdata(client);
+
                if ((data->flags & LM90_HAVE_BROKEN_ALERT)
                 && (alarms & data->alert_alarms)) {
+                       u8 config;
                        dev_dbg(&client->dev, "Disabling ALERT#\n");
                        lm90_read_reg(client, LM90_REG_R_CONFIG1, &config);
                        i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
                                                  config | 0x80);
                }
+       } else {
+               dev_info(&client->dev, "Everything OK\n");
        }
 }
 
index d17325d..cf811c1 100644 (file)
@@ -274,6 +274,8 @@ static const u16 NCT6775_FAN_PULSE_SHIFT[] = { 0, 0, 0, 0, 0, 0 };
 static const u16 NCT6775_REG_TEMP[] = {
        0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d };
 
+static const u16 NCT6775_REG_TEMP_MON[] = { 0x73, 0x75, 0x77 };
+
 static const u16 NCT6775_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
        0, 0x152, 0x252, 0x628, 0x629, 0x62A };
 static const u16 NCT6775_REG_TEMP_HYST[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
@@ -454,6 +456,7 @@ static const u16 NCT6779_REG_CRITICAL_PWM[] = {
        0x137, 0x237, 0x337, 0x837, 0x937, 0xa37 };
 
 static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 };
+static const u16 NCT6779_REG_TEMP_MON[] = { 0x73, 0x75, 0x77, 0x79, 0x7b };
 static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
        0x18, 0x152 };
 static const u16 NCT6779_REG_TEMP_HYST[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
@@ -507,6 +510,13 @@ static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1]
 
 #define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE    0x28
 
+static const u16 NCT6791_REG_WEIGHT_TEMP_SEL[6] = { 0, 0x239 };
+static const u16 NCT6791_REG_WEIGHT_TEMP_STEP[6] = { 0, 0x23a };
+static const u16 NCT6791_REG_WEIGHT_TEMP_STEP_TOL[6] = { 0, 0x23b };
+static const u16 NCT6791_REG_WEIGHT_DUTY_STEP[6] = { 0, 0x23c };
+static const u16 NCT6791_REG_WEIGHT_TEMP_BASE[6] = { 0, 0x23d };
+static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[6] = { 0, 0x23e };
+
 static const u16 NCT6791_REG_ALARM[NUM_REG_ALARM] = {
        0x459, 0x45A, 0x45B, 0x568, 0x45D };
 
@@ -534,6 +544,7 @@ static const u16 NCT6106_REG_IN[] = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x09 };
 
 static const u16 NCT6106_REG_TEMP[] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 };
+static const u16 NCT6106_REG_TEMP_MON[] = { 0x18, 0x19, 0x1a };
 static const u16 NCT6106_REG_TEMP_HYST[] = {
        0xc3, 0xc7, 0xcb, 0xcf, 0xd3, 0xd7 };
 static const u16 NCT6106_REG_TEMP_OVER[] = {
@@ -1307,6 +1318,9 @@ static void nct6775_update_pwm(struct device *dev)
                if (reg & 0x80)
                        data->pwm[2][i] = 0;
 
+               if (!data->REG_WEIGHT_TEMP_SEL[i])
+                       continue;
+
                reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i]);
                data->pwm_weight_temp_sel[i] = reg & 0x1f;
                /* If weight is disabled, report weight source as 0 */
@@ -2852,6 +2866,9 @@ static umode_t nct6775_pwm_is_visible(struct kobject *kobj,
        if (!(data->has_pwm & (1 << pwm)))
                return 0;
 
+       if ((nr >= 14 && nr <= 18) || nr == 21)   /* weight */
+               if (!data->REG_WEIGHT_TEMP_SEL[pwm])
+                       return 0;
        if (nr == 19 && data->REG_PWM[3] == NULL) /* pwm_max */
                return 0;
        if (nr == 20 && data->REG_PWM[4] == NULL) /* pwm_step */
@@ -2945,11 +2962,11 @@ static struct sensor_device_template *nct6775_attributes_pwm_template[] = {
        &sensor_dev_template_pwm_step_down_time,
        &sensor_dev_template_pwm_start,
        &sensor_dev_template_pwm_floor,
-       &sensor_dev_template_pwm_weight_temp_sel,
+       &sensor_dev_template_pwm_weight_temp_sel,       /* 14 */
        &sensor_dev_template_pwm_weight_temp_step,
        &sensor_dev_template_pwm_weight_temp_step_tol,
        &sensor_dev_template_pwm_weight_temp_step_base,
-       &sensor_dev_template_pwm_weight_duty_step,
+       &sensor_dev_template_pwm_weight_duty_step,      /* 18 */
        &sensor_dev_template_pwm_max,                   /* 19 */
        &sensor_dev_template_pwm_step,                  /* 20 */
        &sensor_dev_template_pwm_weight_duty_base,      /* 21 */
@@ -3253,9 +3270,9 @@ static int nct6775_probe(struct platform_device *pdev)
        int i, s, err = 0;
        int src, mask, available;
        const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config;
-       const u16 *reg_temp_alternate, *reg_temp_crit;
+       const u16 *reg_temp_mon, *reg_temp_alternate, *reg_temp_crit;
        const u16 *reg_temp_crit_l = NULL, *reg_temp_crit_h = NULL;
-       int num_reg_temp;
+       int num_reg_temp, num_reg_temp_mon;
        u8 cr2a;
        struct attribute_group *group;
        struct device *hwmon_dev;
@@ -3338,7 +3355,9 @@ static int nct6775_probe(struct platform_device *pdev)
                data->BEEP_BITS = NCT6106_BEEP_BITS;
 
                reg_temp = NCT6106_REG_TEMP;
+               reg_temp_mon = NCT6106_REG_TEMP_MON;
                num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP);
+               num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON);
                reg_temp_over = NCT6106_REG_TEMP_OVER;
                reg_temp_hyst = NCT6106_REG_TEMP_HYST;
                reg_temp_config = NCT6106_REG_TEMP_CONFIG;
@@ -3410,7 +3429,9 @@ static int nct6775_probe(struct platform_device *pdev)
                data->REG_BEEP = NCT6775_REG_BEEP;
 
                reg_temp = NCT6775_REG_TEMP;
+               reg_temp_mon = NCT6775_REG_TEMP_MON;
                num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP);
+               num_reg_temp_mon = ARRAY_SIZE(NCT6775_REG_TEMP_MON);
                reg_temp_over = NCT6775_REG_TEMP_OVER;
                reg_temp_hyst = NCT6775_REG_TEMP_HYST;
                reg_temp_config = NCT6775_REG_TEMP_CONFIG;
@@ -3480,7 +3501,9 @@ static int nct6775_probe(struct platform_device *pdev)
                data->REG_BEEP = NCT6776_REG_BEEP;
 
                reg_temp = NCT6775_REG_TEMP;
+               reg_temp_mon = NCT6775_REG_TEMP_MON;
                num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP);
+               num_reg_temp_mon = ARRAY_SIZE(NCT6775_REG_TEMP_MON);
                reg_temp_over = NCT6775_REG_TEMP_OVER;
                reg_temp_hyst = NCT6775_REG_TEMP_HYST;
                reg_temp_config = NCT6776_REG_TEMP_CONFIG;
@@ -3554,7 +3577,9 @@ static int nct6775_probe(struct platform_device *pdev)
                data->REG_BEEP = NCT6776_REG_BEEP;
 
                reg_temp = NCT6779_REG_TEMP;
+               reg_temp_mon = NCT6779_REG_TEMP_MON;
                num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
+               num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
                reg_temp_over = NCT6779_REG_TEMP_OVER;
                reg_temp_hyst = NCT6779_REG_TEMP_HYST;
                reg_temp_config = NCT6779_REG_TEMP_CONFIG;
@@ -3603,8 +3628,8 @@ static int nct6775_probe(struct platform_device *pdev)
                data->REG_PWM[0] = NCT6775_REG_PWM;
                data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
                data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
-               data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
-               data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE;
+               data->REG_PWM[5] = NCT6791_REG_WEIGHT_DUTY_STEP;
+               data->REG_PWM[6] = NCT6791_REG_WEIGHT_DUTY_BASE;
                data->REG_PWM_READ = NCT6775_REG_PWM_READ;
                data->REG_PWM_MODE = NCT6776_REG_PWM_MODE;
                data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK;
@@ -3620,15 +3645,17 @@ static int nct6775_probe(struct platform_device *pdev)
                data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET;
                data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
                data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
-               data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
-               data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
-               data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
-               data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
+               data->REG_WEIGHT_TEMP_SEL = NCT6791_REG_WEIGHT_TEMP_SEL;
+               data->REG_WEIGHT_TEMP[0] = NCT6791_REG_WEIGHT_TEMP_STEP;
+               data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL;
+               data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE;
                data->REG_ALARM = NCT6791_REG_ALARM;
                data->REG_BEEP = NCT6776_REG_BEEP;
 
                reg_temp = NCT6779_REG_TEMP;
+               reg_temp_mon = NCT6779_REG_TEMP_MON;
                num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
+               num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
                reg_temp_over = NCT6779_REG_TEMP_OVER;
                reg_temp_hyst = NCT6779_REG_TEMP_HYST;
                reg_temp_config = NCT6779_REG_TEMP_CONFIG;
@@ -3729,6 +3756,50 @@ static int nct6775_probe(struct platform_device *pdev)
                s++;
        }
 
+       /*
+        * Repeat with temperatures used for fan control.
+        * This set of registers does not support limits.
+        */
+       for (i = 0; i < num_reg_temp_mon; i++) {
+               if (reg_temp_mon[i] == 0)
+                       continue;
+
+               src = nct6775_read_value(data, data->REG_TEMP_SEL[i]) & 0x1f;
+               if (!src || (mask & (1 << src)))
+                       continue;
+
+               if (src >= data->temp_label_num ||
+                   !strlen(data->temp_label[src])) {
+                       dev_info(dev,
+                                "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n",
+                                src, i, data->REG_TEMP_SEL[i],
+                                reg_temp_mon[i]);
+                       continue;
+               }
+
+               mask |= 1 << src;
+
+               /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
+               if (src <= data->temp_fixed_num) {
+                       if (data->have_temp & (1 << (src - 1)))
+                               continue;
+                       data->have_temp |= 1 << (src - 1);
+                       data->have_temp_fixed |= 1 << (src - 1);
+                       data->reg_temp[0][src - 1] = reg_temp_mon[i];
+                       data->temp_src[src - 1] = src;
+                       continue;
+               }
+
+               if (s >= NUM_TEMP)
+                       continue;
+
+               /* Use dynamic index for other sources */
+               data->have_temp |= 1 << s;
+               data->reg_temp[0][s] = reg_temp_mon[i];
+               data->temp_src[s] = src;
+               s++;
+       }
+
 #ifdef USE_ALTERNATE
        /*
         * Go through the list of alternate temp registers and enable
index cdcbd83..3b26129 100644 (file)
@@ -109,6 +109,7 @@ config I2C_I801
            Avoton (SOC)
            Wellsburg (PCH)
            Coleto Creek (PCH)
+           Wildcat Point-LP (PCH)
 
          This driver can also be built as a module.  If so, the module
          will be called i2c-i801.
@@ -345,6 +346,16 @@ config I2C_BCM2835
          This support is also available as a module.  If so, the module
          will be called i2c-bcm2835.
 
+config I2C_BCM_KONA
+       tristate "BCM Kona I2C adapter"
+       depends on ARCH_BCM_MOBILE
+       default y
+       help
+         If you say yes to this option, support will be included for the
+         I2C interface on the Broadcom Kona family of processors.
+
+         If you do not need KONA I2C inteface, say N.
+
 config I2C_BLACKFIN_TWI
        tristate "Blackfin TWI I2C support"
        depends on BLACKFIN
@@ -436,6 +447,13 @@ config I2C_EG20T
          ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
          ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
 
+config I2C_EXYNOS5
+       tristate "Exynos5 high-speed I2C driver"
+       depends on ARCH_EXYNOS5 && OF
+       help
+         Say Y here to include support for high-speed I2C controller in the
+         Exynos5 based Samsung SoCs.
+
 config I2C_GPIO
        tristate "GPIO-based bitbanging I2C"
        depends on GPIOLIB
@@ -665,7 +683,7 @@ config I2C_SH7760
 
 config I2C_SH_MOBILE
        tristate "SuperH Mobile I2C Controller"
-       depends on SUPERH || ARCH_SHMOBILE
+       depends on SUPERH || ARM || COMPILE_TEST
        help
          If you say yes to this option, support will be included for the
          built-in I2C interface on the Renesas SH-Mobile processor.
@@ -695,6 +713,16 @@ config I2C_SIRF
          This driver can also be built as a module.  If so, the module
          will be called i2c-sirf.
 
+config I2C_ST
+       tristate "STMicroelectronics SSC I2C support"
+       depends on ARCH_STI
+       help
+         Enable this option to add support for STMicroelectronics SoCs
+         hardware SSC (Synchronous Serial Controller) as an I2C controller.
+
+         This driver can also be built as module. If so, the module
+         will be called i2c-st.
+
 config I2C_STU300
        tristate "ST Microelectronics DDC I2C interface"
        depends on MACH_U300
@@ -768,7 +796,7 @@ config I2C_XLR
 
 config I2C_RCAR
        tristate "Renesas R-Car I2C Controller"
-       depends on ARCH_SHMOBILE && I2C
+       depends on ARM || COMPILE_TEST
        help
          If you say yes to this option, support will be included for the
          R-Car I2C controller.
index d00997f..c73eb0e 100644 (file)
@@ -42,6 +42,7 @@ i2c-designware-platform-objs := i2c-designware-platdrv.o
 obj-$(CONFIG_I2C_DESIGNWARE_PCI)       += i2c-designware-pci.o
 i2c-designware-pci-objs := i2c-designware-pcidrv.o
 obj-$(CONFIG_I2C_EG20T)                += i2c-eg20t.o
+obj-$(CONFIG_I2C_EXYNOS5)      += i2c-exynos5.o
 obj-$(CONFIG_I2C_GPIO)         += i2c-gpio.o
 obj-$(CONFIG_I2C_HIGHLANDER)   += i2c-highlander.o
 obj-$(CONFIG_I2C_IBM_IIC)      += i2c-ibm_iic.o
@@ -68,6 +69,7 @@ obj-$(CONFIG_I2C_SH7760)      += i2c-sh7760.o
 obj-$(CONFIG_I2C_SH_MOBILE)    += i2c-sh_mobile.o
 obj-$(CONFIG_I2C_SIMTEC)       += i2c-simtec.o
 obj-$(CONFIG_I2C_SIRF)         += i2c-sirf.o
+obj-$(CONFIG_I2C_ST)           += i2c-st.o
 obj-$(CONFIG_I2C_STU300)       += i2c-stu300.o
 obj-$(CONFIG_I2C_TEGRA)                += i2c-tegra.o
 obj-$(CONFIG_I2C_VERSATILE)    += i2c-versatile.o
@@ -87,6 +89,7 @@ obj-$(CONFIG_I2C_VIPERBOARD)  += i2c-viperboard.o
 
 # Other I2C/SMBus bus drivers
 obj-$(CONFIG_I2C_ACORN)                += i2c-acorn.o
+obj-$(CONFIG_I2C_BCM_KONA)     += i2c-bcm-kona.o
 obj-$(CONFIG_I2C_ELEKTOR)      += i2c-elektor.o
 obj-$(CONFIG_I2C_PCA_ISA)      += i2c-pca-isa.o
 obj-$(CONFIG_I2C_SIBYTE)       += i2c-sibyte.o
index fd05930..8edba9d 100644 (file)
@@ -371,7 +371,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
        dev_dbg(dev->dev, "transfer: %s %d bytes.\n",
                (dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len);
 
-       INIT_COMPLETION(dev->cmd_complete);
+       reinit_completion(&dev->cmd_complete);
        dev->transfer_status = 0;
 
        if (!dev->buf_len) {
diff --git a/drivers/i2c/busses/i2c-bcm-kona.c b/drivers/i2c/busses/i2c-bcm-kona.c
new file mode 100644 (file)
index 0000000..036cf03
--- /dev/null
@@ -0,0 +1,909 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation 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/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+/* Hardware register offsets and field defintions */
+#define CS_OFFSET                              0x00000020
+#define CS_ACK_SHIFT                           3
+#define CS_ACK_MASK                            0x00000008
+#define CS_ACK_CMD_GEN_START                   0x00000000
+#define CS_ACK_CMD_GEN_RESTART                 0x00000001
+#define CS_CMD_SHIFT                           1
+#define CS_CMD_CMD_NO_ACTION                   0x00000000
+#define CS_CMD_CMD_START_RESTART               0x00000001
+#define CS_CMD_CMD_STOP                                0x00000002
+#define CS_EN_SHIFT                            0
+#define CS_EN_CMD_ENABLE_BSC                   0x00000001
+
+#define TIM_OFFSET                             0x00000024
+#define TIM_PRESCALE_SHIFT                     6
+#define TIM_P_SHIFT                            3
+#define TIM_NO_DIV_SHIFT                       2
+#define TIM_DIV_SHIFT                          0
+
+#define DAT_OFFSET                             0x00000028
+
+#define TOUT_OFFSET                            0x0000002c
+
+#define TXFCR_OFFSET                           0x0000003c
+#define TXFCR_FIFO_FLUSH_MASK                  0x00000080
+#define TXFCR_FIFO_EN_MASK                     0x00000040
+
+#define IER_OFFSET                             0x00000044
+#define IER_READ_COMPLETE_INT_MASK             0x00000010
+#define IER_I2C_INT_EN_MASK                    0x00000008
+#define IER_FIFO_INT_EN_MASK                   0x00000002
+#define IER_NOACK_EN_MASK                      0x00000001
+
+#define ISR_OFFSET                             0x00000048
+#define ISR_RESERVED_MASK                      0xffffff60
+#define ISR_CMDBUSY_MASK                       0x00000080
+#define ISR_READ_COMPLETE_MASK                 0x00000010
+#define ISR_SES_DONE_MASK                      0x00000008
+#define ISR_ERR_MASK                           0x00000004
+#define ISR_TXFIFOEMPTY_MASK                   0x00000002
+#define ISR_NOACK_MASK                         0x00000001
+
+#define CLKEN_OFFSET                           0x0000004C
+#define CLKEN_AUTOSENSE_OFF_MASK               0x00000080
+#define CLKEN_M_SHIFT                          4
+#define CLKEN_N_SHIFT                          1
+#define CLKEN_CLKEN_MASK                       0x00000001
+
+#define FIFO_STATUS_OFFSET                     0x00000054
+#define FIFO_STATUS_RXFIFO_EMPTY_MASK          0x00000004
+#define FIFO_STATUS_TXFIFO_EMPTY_MASK          0x00000010
+
+#define HSTIM_OFFSET                           0x00000058
+#define HSTIM_HS_MODE_MASK                     0x00008000
+#define HSTIM_HS_HOLD_SHIFT                    10
+#define HSTIM_HS_HIGH_PHASE_SHIFT              5
+#define HSTIM_HS_SETUP_SHIFT                   0
+
+#define PADCTL_OFFSET                          0x0000005c
+#define PADCTL_PAD_OUT_EN_MASK                 0x00000004
+
+#define RXFCR_OFFSET                           0x00000068
+#define RXFCR_NACK_EN_SHIFT                    7
+#define RXFCR_READ_COUNT_SHIFT                 0
+#define RXFIFORDOUT_OFFSET                     0x0000006c
+
+/* Locally used constants */
+#define MAX_RX_FIFO_SIZE               64U /* bytes */
+#define MAX_TX_FIFO_SIZE               64U /* bytes */
+
+#define STD_EXT_CLK_FREQ               13000000UL
+#define HS_EXT_CLK_FREQ                        104000000UL
+
+#define MASTERCODE                     0x08 /* Mastercodes are 0000_1xxxb */
+
+#define I2C_TIMEOUT                    100 /* msecs */
+
+/* Operations that can be commanded to the controller */
+enum bcm_kona_cmd_t {
+       BCM_CMD_NOACTION = 0,
+       BCM_CMD_START,
+       BCM_CMD_RESTART,
+       BCM_CMD_STOP,
+};
+
+enum bus_speed_index {
+       BCM_SPD_100K = 0,
+       BCM_SPD_400K,
+       BCM_SPD_1MHZ,
+};
+
+enum hs_bus_speed_index {
+       BCM_SPD_3P4MHZ = 0,
+};
+
+/* Internal divider settings for standard mode, fast mode and fast mode plus */
+struct bus_speed_cfg {
+       uint8_t time_m;         /* Number of cycles for setup time */
+       uint8_t time_n;         /* Number of cycles for hold time */
+       uint8_t prescale;       /* Prescale divider */
+       uint8_t time_p;         /* Timing coefficient */
+       uint8_t no_div;         /* Disable clock divider */
+       uint8_t time_div;       /* Post-prescale divider */
+};
+
+/* Internal divider settings for high-speed mode */
+struct hs_bus_speed_cfg {
+       uint8_t hs_hold;        /* Number of clock cycles SCL stays low until
+                                  the end of bit period */
+       uint8_t hs_high_phase;  /* Number of clock cycles SCL stays high
+                                  before it falls */
+       uint8_t hs_setup;       /* Number of clock cycles SCL stays low
+                                  before it rises  */
+       uint8_t prescale;       /* Prescale divider */
+       uint8_t time_p;         /* Timing coefficient */
+       uint8_t no_div;         /* Disable clock divider */
+       uint8_t time_div;       /* Post-prescale divider */
+};
+
+static const struct bus_speed_cfg std_cfg_table[] = {
+       [BCM_SPD_100K] = {0x01, 0x01, 0x03, 0x06, 0x00, 0x02},
+       [BCM_SPD_400K] = {0x05, 0x01, 0x03, 0x05, 0x01, 0x02},
+       [BCM_SPD_1MHZ] = {0x01, 0x01, 0x03, 0x01, 0x01, 0x03},
+};
+
+static const struct hs_bus_speed_cfg hs_cfg_table[] = {
+       [BCM_SPD_3P4MHZ] = {0x01, 0x08, 0x14, 0x00, 0x06, 0x01, 0x00},
+};
+
+struct bcm_kona_i2c_dev {
+       struct device *device;
+
+       void __iomem *base;
+       int irq;
+       struct clk *external_clk;
+
+       struct i2c_adapter adapter;
+
+       struct completion done;
+
+       const struct bus_speed_cfg *std_cfg;
+       const struct hs_bus_speed_cfg *hs_cfg;
+};
+
+static void bcm_kona_i2c_send_cmd_to_ctrl(struct bcm_kona_i2c_dev *dev,
+                                         enum bcm_kona_cmd_t cmd)
+{
+       dev_dbg(dev->device, "%s, %d\n", __func__, cmd);
+
+       switch (cmd) {
+       case BCM_CMD_NOACTION:
+               writel((CS_CMD_CMD_NO_ACTION << CS_CMD_SHIFT) |
+                      (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT),
+                      dev->base + CS_OFFSET);
+               break;
+
+       case BCM_CMD_START:
+               writel((CS_ACK_CMD_GEN_START << CS_ACK_SHIFT) |
+                      (CS_CMD_CMD_START_RESTART << CS_CMD_SHIFT) |
+                      (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT),
+                      dev->base + CS_OFFSET);
+               break;
+
+       case BCM_CMD_RESTART:
+               writel((CS_ACK_CMD_GEN_RESTART << CS_ACK_SHIFT) |
+                      (CS_CMD_CMD_START_RESTART << CS_CMD_SHIFT) |
+                      (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT),
+                      dev->base + CS_OFFSET);
+               break;
+
+       case BCM_CMD_STOP:
+               writel((CS_CMD_CMD_STOP << CS_CMD_SHIFT) |
+                      (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT),
+                      dev->base + CS_OFFSET);
+               break;
+
+       default:
+               dev_err(dev->device, "Unknown command %d\n", cmd);
+       }
+}
+
+static void bcm_kona_i2c_enable_clock(struct bcm_kona_i2c_dev *dev)
+{
+       writel(readl(dev->base + CLKEN_OFFSET) | CLKEN_CLKEN_MASK,
+              dev->base + CLKEN_OFFSET);
+}
+
+static void bcm_kona_i2c_disable_clock(struct bcm_kona_i2c_dev *dev)
+{
+       writel(readl(dev->base + CLKEN_OFFSET) & ~CLKEN_CLKEN_MASK,
+              dev->base + CLKEN_OFFSET);
+}
+
+static irqreturn_t bcm_kona_i2c_isr(int irq, void *devid)
+{
+       struct bcm_kona_i2c_dev *dev = devid;
+       uint32_t status = readl(dev->base + ISR_OFFSET);
+
+       if ((status & ~ISR_RESERVED_MASK) == 0)
+               return IRQ_NONE;
+
+       /* Must flush the TX FIFO when NAK detected */
+       if (status & ISR_NOACK_MASK)
+               writel(TXFCR_FIFO_FLUSH_MASK | TXFCR_FIFO_EN_MASK,
+                      dev->base + TXFCR_OFFSET);
+
+       writel(status & ~ISR_RESERVED_MASK, dev->base + ISR_OFFSET);
+       complete_all(&dev->done);
+
+       return IRQ_HANDLED;
+}
+
+/* Wait for ISR_CMDBUSY_MASK to go low before writing to CS, DAT, or RCD */
+static int bcm_kona_i2c_wait_if_busy(struct bcm_kona_i2c_dev *dev)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT);
+
+       while (readl(dev->base + ISR_OFFSET) & ISR_CMDBUSY_MASK)
+               if (time_after(jiffies, timeout)) {
+                       dev_err(dev->device, "CMDBUSY timeout\n");
+                       return -ETIMEDOUT;
+               }
+
+       return 0;
+}
+
+/* Send command to I2C bus */
+static int bcm_kona_send_i2c_cmd(struct bcm_kona_i2c_dev *dev,
+                                enum bcm_kona_cmd_t cmd)
+{
+       int rc;
+       unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT);
+
+       /* Make sure the hardware is ready */
+       rc = bcm_kona_i2c_wait_if_busy(dev);
+       if (rc < 0)
+               return rc;
+
+       /* Unmask the session done interrupt */
+       writel(IER_I2C_INT_EN_MASK, dev->base + IER_OFFSET);
+
+       /* Mark as incomplete before sending the command */
+       reinit_completion(&dev->done);
+
+       /* Send the command */
+       bcm_kona_i2c_send_cmd_to_ctrl(dev, cmd);
+
+       /* Wait for transaction to finish or timeout */
+       time_left = wait_for_completion_timeout(&dev->done, time_left);
+
+       /* Mask all interrupts */
+       writel(0, dev->base + IER_OFFSET);
+
+       if (!time_left) {
+               dev_err(dev->device, "controller timed out\n");
+               rc = -ETIMEDOUT;
+       }
+
+       /* Clear command */
+       bcm_kona_i2c_send_cmd_to_ctrl(dev, BCM_CMD_NOACTION);
+
+       return rc;
+}
+
+/* Read a single RX FIFO worth of data from the i2c bus */
+static int bcm_kona_i2c_read_fifo_single(struct bcm_kona_i2c_dev *dev,
+                                        uint8_t *buf, unsigned int len,
+                                        unsigned int last_byte_nak)
+{
+       unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT);
+
+       /* Mark as incomplete before starting the RX FIFO */
+       reinit_completion(&dev->done);
+
+       /* Unmask the read complete interrupt */
+       writel(IER_READ_COMPLETE_INT_MASK, dev->base + IER_OFFSET);
+
+       /* Start the RX FIFO */
+       writel((last_byte_nak << RXFCR_NACK_EN_SHIFT) |
+              (len << RXFCR_READ_COUNT_SHIFT),
+               dev->base + RXFCR_OFFSET);
+
+       /* Wait for FIFO read to complete */
+       time_left = wait_for_completion_timeout(&dev->done, time_left);
+
+       /* Mask all interrupts */
+       writel(0, dev->base + IER_OFFSET);
+
+       if (!time_left) {
+               dev_err(dev->device, "RX FIFO time out\n");
+               return -EREMOTEIO;
+       }
+
+       /* Read data from FIFO */
+       for (; len > 0; len--, buf++)
+               *buf = readl(dev->base + RXFIFORDOUT_OFFSET);
+
+       return 0;
+}
+
+/* Read any amount of data using the RX FIFO from the i2c bus */
+static int bcm_kona_i2c_read_fifo(struct bcm_kona_i2c_dev *dev,
+                                 struct i2c_msg *msg)
+{
+       unsigned int bytes_to_read = MAX_RX_FIFO_SIZE;
+       unsigned int last_byte_nak = 0;
+       unsigned int bytes_read = 0;
+       int rc;
+
+       uint8_t *tmp_buf = msg->buf;
+
+       while (bytes_read < msg->len) {
+               if (msg->len - bytes_read <= MAX_RX_FIFO_SIZE) {
+                       last_byte_nak = 1; /* NAK last byte of transfer */
+                       bytes_to_read = msg->len - bytes_read;
+               }
+
+               rc = bcm_kona_i2c_read_fifo_single(dev, tmp_buf, bytes_to_read,
+                                                  last_byte_nak);
+               if (rc < 0)
+                       return -EREMOTEIO;
+
+               bytes_read += bytes_to_read;
+               tmp_buf += bytes_to_read;
+       }
+
+       return 0;
+}
+
+/* Write a single byte of data to the i2c bus */
+static int bcm_kona_i2c_write_byte(struct bcm_kona_i2c_dev *dev, uint8_t data,
+                                  unsigned int nak_expected)
+{
+       int rc;
+       unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT);
+       unsigned int nak_received;
+
+       /* Make sure the hardware is ready */
+       rc = bcm_kona_i2c_wait_if_busy(dev);
+       if (rc < 0)
+               return rc;
+
+       /* Clear pending session done interrupt */
+       writel(ISR_SES_DONE_MASK, dev->base + ISR_OFFSET);
+
+       /* Unmask the session done interrupt */
+       writel(IER_I2C_INT_EN_MASK, dev->base + IER_OFFSET);
+
+       /* Mark as incomplete before sending the data */
+       reinit_completion(&dev->done);
+
+       /* Send one byte of data */
+       writel(data, dev->base + DAT_OFFSET);
+
+       /* Wait for byte to be written */
+       time_left = wait_for_completion_timeout(&dev->done, time_left);
+
+       /* Mask all interrupts */
+       writel(0, dev->base + IER_OFFSET);
+
+       if (!time_left) {
+               dev_dbg(dev->device, "controller timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       nak_received = readl(dev->base + CS_OFFSET) & CS_ACK_MASK ? 1 : 0;
+
+       if (nak_received ^ nak_expected) {
+               dev_dbg(dev->device, "unexpected NAK/ACK\n");
+               return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+/* Write a single TX FIFO worth of data to the i2c bus */
+static int bcm_kona_i2c_write_fifo_single(struct bcm_kona_i2c_dev *dev,
+                                         uint8_t *buf, unsigned int len)
+{
+       int k;
+       unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT);
+       unsigned int fifo_status;
+
+       /* Mark as incomplete before sending data to the TX FIFO */
+       reinit_completion(&dev->done);
+
+       /* Unmask the fifo empty and nak interrupt */
+       writel(IER_FIFO_INT_EN_MASK | IER_NOACK_EN_MASK,
+              dev->base + IER_OFFSET);
+
+       /* Disable IRQ to load a FIFO worth of data without interruption */
+       disable_irq(dev->irq);
+
+       /* Write data into FIFO */
+       for (k = 0; k < len; k++)
+               writel(buf[k], (dev->base + DAT_OFFSET));
+
+       /* Enable IRQ now that data has been loaded */
+       enable_irq(dev->irq);
+
+       /* Wait for FIFO to empty */
+       do {
+               time_left = wait_for_completion_timeout(&dev->done, time_left);
+               fifo_status = readl(dev->base + FIFO_STATUS_OFFSET);
+       } while (time_left && !(fifo_status & FIFO_STATUS_TXFIFO_EMPTY_MASK));
+
+       /* Mask all interrupts */
+       writel(0, dev->base + IER_OFFSET);
+
+       /* Check if there was a NAK */
+       if (readl(dev->base + CS_OFFSET) & CS_ACK_MASK) {
+               dev_err(dev->device, "unexpected NAK\n");
+               return -EREMOTEIO;
+       }
+
+       /* Check if a timeout occured */
+       if (!time_left) {
+               dev_err(dev->device, "completion timed out\n");
+               return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+
+/* Write any amount of data using TX FIFO to the i2c bus */
+static int bcm_kona_i2c_write_fifo(struct bcm_kona_i2c_dev *dev,
+                                  struct i2c_msg *msg)
+{
+       unsigned int bytes_to_write = MAX_TX_FIFO_SIZE;
+       unsigned int bytes_written = 0;
+       int rc;
+
+       uint8_t *tmp_buf = msg->buf;
+
+       while (bytes_written < msg->len) {
+               if (msg->len - bytes_written <= MAX_TX_FIFO_SIZE)
+                       bytes_to_write = msg->len - bytes_written;
+
+               rc = bcm_kona_i2c_write_fifo_single(dev, tmp_buf,
+                                                   bytes_to_write);
+               if (rc < 0)
+                       return -EREMOTEIO;
+
+               bytes_written += bytes_to_write;
+               tmp_buf += bytes_to_write;
+       }
+
+       return 0;
+}
+
+/* Send i2c address */
+static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev,
+                                    struct i2c_msg *msg)
+{
+       unsigned char addr;
+
+       if (msg->flags & I2C_M_TEN) {
+               /* First byte is 11110XX0 where XX is upper 2 bits */
+               addr = 0xF0 | ((msg->addr & 0x300) >> 7);
+               if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
+                       return -EREMOTEIO;
+
+               /* Second byte is the remaining 8 bits */
+               addr = msg->addr & 0xFF;
+               if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
+                       return -EREMOTEIO;
+
+               if (msg->flags & I2C_M_RD) {
+                       /* For read, send restart command */
+                       if (bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART) < 0)
+                               return -EREMOTEIO;
+
+                       /* Then re-send the first byte with the read bit set */
+                       addr = 0xF0 | ((msg->addr & 0x300) >> 7) | 0x01;
+                       if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
+                               return -EREMOTEIO;
+               }
+       } else {
+               addr = msg->addr << 1;
+
+               if (msg->flags & I2C_M_RD)
+                       addr |= 1;
+
+               if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
+                       return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+static void bcm_kona_i2c_enable_autosense(struct bcm_kona_i2c_dev *dev)
+{
+       writel(readl(dev->base + CLKEN_OFFSET) & ~CLKEN_AUTOSENSE_OFF_MASK,
+              dev->base + CLKEN_OFFSET);
+}
+
+static void bcm_kona_i2c_config_timing(struct bcm_kona_i2c_dev *dev)
+{
+       writel(readl(dev->base + HSTIM_OFFSET) & ~HSTIM_HS_MODE_MASK,
+              dev->base + HSTIM_OFFSET);
+
+       writel((dev->std_cfg->prescale << TIM_PRESCALE_SHIFT) |
+              (dev->std_cfg->time_p << TIM_P_SHIFT) |
+              (dev->std_cfg->no_div << TIM_NO_DIV_SHIFT) |
+              (dev->std_cfg->time_div  << TIM_DIV_SHIFT),
+              dev->base + TIM_OFFSET);
+
+       writel((dev->std_cfg->time_m << CLKEN_M_SHIFT) |
+              (dev->std_cfg->time_n << CLKEN_N_SHIFT) |
+              CLKEN_CLKEN_MASK,
+              dev->base + CLKEN_OFFSET);
+}
+
+static void bcm_kona_i2c_config_timing_hs(struct bcm_kona_i2c_dev *dev)
+{
+       writel((dev->hs_cfg->prescale << TIM_PRESCALE_SHIFT) |
+              (dev->hs_cfg->time_p << TIM_P_SHIFT) |
+              (dev->hs_cfg->no_div << TIM_NO_DIV_SHIFT) |
+              (dev->hs_cfg->time_div << TIM_DIV_SHIFT),
+              dev->base + TIM_OFFSET);
+
+       writel((dev->hs_cfg->hs_hold << HSTIM_HS_HOLD_SHIFT) |
+              (dev->hs_cfg->hs_high_phase << HSTIM_HS_HIGH_PHASE_SHIFT) |
+              (dev->hs_cfg->hs_setup << HSTIM_HS_SETUP_SHIFT),
+              dev->base + HSTIM_OFFSET);
+
+       writel(readl(dev->base + HSTIM_OFFSET) | HSTIM_HS_MODE_MASK,
+              dev->base + HSTIM_OFFSET);
+}
+
+static int bcm_kona_i2c_switch_to_hs(struct bcm_kona_i2c_dev *dev)
+{
+       int rc;
+
+       /* Send mastercode at standard speed */
+       rc = bcm_kona_i2c_write_byte(dev, MASTERCODE, 1);
+       if (rc < 0) {
+               pr_err("High speed handshake failed\n");
+               return rc;
+       }
+
+       /* Configure external clock to higher frequency */
+       rc = clk_set_rate(dev->external_clk, HS_EXT_CLK_FREQ);
+       if (rc) {
+               dev_err(dev->device, "%s: clk_set_rate returned %d\n",
+                       __func__, rc);
+               return rc;
+       }
+
+       /* Reconfigure internal dividers */
+       bcm_kona_i2c_config_timing_hs(dev);
+
+       /* Send a restart command */
+       rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART);
+       if (rc < 0)
+               dev_err(dev->device, "High speed restart command failed\n");
+
+       return rc;
+}
+
+static int bcm_kona_i2c_switch_to_std(struct bcm_kona_i2c_dev *dev)
+{
+       int rc;
+
+       /* Reconfigure internal dividers */
+       bcm_kona_i2c_config_timing(dev);
+
+       /* Configure external clock to lower frequency */
+       rc = clk_set_rate(dev->external_clk, STD_EXT_CLK_FREQ);
+       if (rc) {
+               dev_err(dev->device, "%s: clk_set_rate returned %d\n",
+                       __func__, rc);
+       }
+
+       return rc;
+}
+
+/* Master transfer function */
+static int bcm_kona_i2c_xfer(struct i2c_adapter *adapter,
+                            struct i2c_msg msgs[], int num)
+{
+       struct bcm_kona_i2c_dev *dev = i2c_get_adapdata(adapter);
+       struct i2c_msg *pmsg;
+       int rc = 0;
+       int i;
+
+       rc = clk_prepare_enable(dev->external_clk);
+       if (rc) {
+               dev_err(dev->device, "%s: peri clock enable failed. err %d\n",
+                       __func__, rc);
+               return rc;
+       }
+
+       /* Enable pad output */
+       writel(0, dev->base + PADCTL_OFFSET);
+
+       /* Enable internal clocks */
+       bcm_kona_i2c_enable_clock(dev);
+
+       /* Send start command */
+       rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_START);
+       if (rc < 0) {
+               dev_err(dev->device, "Start command failed rc = %d\n", rc);
+               goto xfer_disable_pad;
+       }
+
+       /* Switch to high speed if applicable */
+       if (dev->hs_cfg) {
+               rc = bcm_kona_i2c_switch_to_hs(dev);
+               if (rc < 0)
+                       goto xfer_send_stop;
+       }
+
+       /* Loop through all messages */
+       for (i = 0; i < num; i++) {
+               pmsg = &msgs[i];
+
+               /* Send restart for subsequent messages */
+               if ((i != 0) && ((pmsg->flags & I2C_M_NOSTART) == 0)) {
+                       rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART);
+                       if (rc < 0) {
+                               dev_err(dev->device,
+                                       "restart cmd failed rc = %d\n", rc);
+                                       goto xfer_send_stop;
+                       }
+               }
+
+               /* Send slave address */
+               if (!(pmsg->flags & I2C_M_NOSTART)) {
+                       rc = bcm_kona_i2c_do_addr(dev, pmsg);
+                       if (rc < 0) {
+                               dev_err(dev->device,
+                                       "NAK from addr %2.2x msg#%d rc = %d\n",
+                                       pmsg->addr, i, rc);
+                               goto xfer_send_stop;
+                       }
+               }
+
+               /* Perform data transfer */
+               if (pmsg->flags & I2C_M_RD) {
+                       rc = bcm_kona_i2c_read_fifo(dev, pmsg);
+                       if (rc < 0) {
+                               dev_err(dev->device, "read failure\n");
+                               goto xfer_send_stop;
+                       }
+               } else {
+                       rc = bcm_kona_i2c_write_fifo(dev, pmsg);
+                       if (rc < 0) {
+                               dev_err(dev->device, "write failure");
+                               goto xfer_send_stop;
+                       }
+               }
+       }
+
+       rc = num;
+
+xfer_send_stop:
+       /* Send a STOP command */
+       bcm_kona_send_i2c_cmd(dev, BCM_CMD_STOP);
+
+       /* Return from high speed if applicable */
+       if (dev->hs_cfg) {
+               int hs_rc = bcm_kona_i2c_switch_to_std(dev);
+
+               if (hs_rc)
+                       rc = hs_rc;
+       }
+
+xfer_disable_pad:
+       /* Disable pad output */
+       writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET);
+
+       /* Stop internal clock */
+       bcm_kona_i2c_disable_clock(dev);
+
+       clk_disable_unprepare(dev->external_clk);
+
+       return rc;
+}
+
+static uint32_t bcm_kona_i2c_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+           I2C_FUNC_NOSTART;
+}
+
+static const struct i2c_algorithm bcm_algo = {
+       .master_xfer = bcm_kona_i2c_xfer,
+       .functionality = bcm_kona_i2c_functionality,
+};
+
+static int bcm_kona_i2c_assign_bus_speed(struct bcm_kona_i2c_dev *dev)
+{
+       unsigned int bus_speed;
+       int ret = of_property_read_u32(dev->device->of_node, "clock-frequency",
+                                      &bus_speed);
+       if (ret < 0) {
+               dev_err(dev->device, "missing clock-frequency property\n");
+               return -ENODEV;
+       }
+
+       switch (bus_speed) {
+       case 100000:
+               dev->std_cfg = &std_cfg_table[BCM_SPD_100K];
+               break;
+       case 400000:
+               dev->std_cfg = &std_cfg_table[BCM_SPD_400K];
+               break;
+       case 1000000:
+               dev->std_cfg = &std_cfg_table[BCM_SPD_1MHZ];
+               break;
+       case 3400000:
+               /* Send mastercode at 100k */
+               dev->std_cfg = &std_cfg_table[BCM_SPD_100K];
+               dev->hs_cfg = &hs_cfg_table[BCM_SPD_3P4MHZ];
+               break;
+       default:
+               pr_err("%d hz bus speed not supported\n", bus_speed);
+               pr_err("Valid speeds are 100khz, 400khz, 1mhz, and 3.4mhz\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int bcm_kona_i2c_probe(struct platform_device *pdev)
+{
+       int rc = 0;
+       struct bcm_kona_i2c_dev *dev;
+       struct i2c_adapter *adap;
+       struct resource *iomem;
+
+       /* Allocate memory for private data structure */
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, dev);
+       dev->device = &pdev->dev;
+       init_completion(&dev->done);
+
+       /* Map hardware registers */
+       iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dev->base = devm_ioremap_resource(dev->device, iomem);
+       if (IS_ERR(dev->base))
+               return -ENOMEM;
+
+       /* Get and enable external clock */
+       dev->external_clk = devm_clk_get(dev->device, NULL);
+       if (IS_ERR(dev->external_clk)) {
+               dev_err(dev->device, "couldn't get clock\n");
+               return -ENODEV;
+       }
+
+       rc = clk_set_rate(dev->external_clk, STD_EXT_CLK_FREQ);
+       if (rc) {
+               dev_err(dev->device, "%s: clk_set_rate returned %d\n",
+                       __func__, rc);
+               return rc;
+       }
+
+       rc = clk_prepare_enable(dev->external_clk);
+       if (rc) {
+               dev_err(dev->device, "couldn't enable clock\n");
+               return rc;
+       }
+
+       /* Parse bus speed */
+       rc = bcm_kona_i2c_assign_bus_speed(dev);
+       if (rc)
+               goto probe_disable_clk;
+
+       /* Enable internal clocks */
+       bcm_kona_i2c_enable_clock(dev);
+
+       /* Configure internal dividers */
+       bcm_kona_i2c_config_timing(dev);
+
+       /* Disable timeout */
+       writel(0, dev->base + TOUT_OFFSET);
+
+       /* Enable autosense */
+       bcm_kona_i2c_enable_autosense(dev);
+
+       /* Enable TX FIFO */
+       writel(TXFCR_FIFO_FLUSH_MASK | TXFCR_FIFO_EN_MASK,
+              dev->base + TXFCR_OFFSET);
+
+       /* Mask all interrupts */
+       writel(0, dev->base + IER_OFFSET);
+
+       /* Clear all pending interrupts */
+       writel(ISR_CMDBUSY_MASK |
+              ISR_READ_COMPLETE_MASK |
+              ISR_SES_DONE_MASK |
+              ISR_ERR_MASK |
+              ISR_TXFIFOEMPTY_MASK |
+              ISR_NOACK_MASK,
+              dev->base + ISR_OFFSET);
+
+       /* Get the interrupt number */
+       dev->irq = platform_get_irq(pdev, 0);
+       if (dev->irq < 0) {
+               dev_err(dev->device, "no irq resource\n");
+               rc = -ENODEV;
+               goto probe_disable_clk;
+       }
+
+       /* register the ISR handler */
+       rc = devm_request_irq(&pdev->dev, dev->irq, bcm_kona_i2c_isr,
+                             IRQF_SHARED, pdev->name, dev);
+       if (rc) {
+               dev_err(dev->device, "failed to request irq %i\n", dev->irq);
+               goto probe_disable_clk;
+       }
+
+       /* Enable the controller but leave it idle */
+       bcm_kona_i2c_send_cmd_to_ctrl(dev, BCM_CMD_NOACTION);
+
+       /* Disable pad output */
+       writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET);
+
+       /* Disable internal clock */
+       bcm_kona_i2c_disable_clock(dev);
+
+       /* Disable external clock */
+       clk_disable_unprepare(dev->external_clk);
+
+       /* Add the i2c adapter */
+       adap = &dev->adapter;
+       i2c_set_adapdata(adap, dev);
+       adap->owner = THIS_MODULE;
+       strlcpy(adap->name, "Broadcom I2C adapter", sizeof(adap->name));
+       adap->algo = &bcm_algo;
+       adap->dev.parent = &pdev->dev;
+       adap->dev.of_node = pdev->dev.of_node;
+
+       rc = i2c_add_adapter(adap);
+       if (rc) {
+               dev_err(dev->device, "failed to add adapter\n");
+               return rc;
+       }
+
+       dev_info(dev->device, "device registered successfully\n");
+
+       return 0;
+
+probe_disable_clk:
+       bcm_kona_i2c_disable_clock(dev);
+       clk_disable_unprepare(dev->external_clk);
+
+       return rc;
+}
+
+static int bcm_kona_i2c_remove(struct platform_device *pdev)
+{
+       struct bcm_kona_i2c_dev *dev = platform_get_drvdata(pdev);
+
+       i2c_del_adapter(&dev->adapter);
+
+       return 0;
+}
+
+static const struct of_device_id bcm_kona_i2c_of_match[] = {
+       {.compatible = "brcm,kona-i2c",},
+       {},
+};
+MODULE_DEVICE_TABLE(of, kona_i2c_of_match);
+
+static struct platform_driver bcm_kona_i2c_driver = {
+       .driver = {
+                  .name = "bcm-kona-i2c",
+                  .owner = THIS_MODULE,
+                  .of_match_table = bcm_kona_i2c_of_match,
+                  },
+       .probe = bcm_kona_i2c_probe,
+       .remove = bcm_kona_i2c_remove,
+};
+module_platform_driver(bcm_kona_i2c_driver);
+
+MODULE_AUTHOR("Tim Kryger <tkryger@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Kona I2C Driver");
+MODULE_LICENSE("GPL v2");
index ea4b08f..d7e8600 100644 (file)
@@ -151,7 +151,7 @@ static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev,
 
        i2c_dev->msg_buf = msg->buf;
        i2c_dev->msg_buf_remaining = msg->len;
-       INIT_COMPLETION(i2c_dev->completion);
+       reinit_completion(&i2c_dev->completion);
 
        bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
 
index 35a473b..3b9bd9a 100644 (file)
@@ -675,7 +675,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
        p_adap->retries = 3;
 
        rc = peripheral_request_list(
-                       (unsigned short *)dev_get_platdata(&pdev->dev),
+                       dev_get_platdata(&pdev->dev),
                        "i2c-bfin-twi");
        if (rc) {
                dev_err(&pdev->dev, "Can't setup pin mux!\n");
@@ -723,7 +723,7 @@ out_error_add_adapter:
        free_irq(iface->irq, iface);
 out_error_req_irq:
 out_error_no_irq:
-       peripheral_free_list((unsigned short *)dev_get_platdata(&pdev->dev));
+       peripheral_free_list(dev_get_platdata(&pdev->dev));
 out_error_pin_mux:
        iounmap(iface->regs_base);
 out_error_ioremap:
@@ -739,7 +739,7 @@ static int i2c_bfin_twi_remove(struct platform_device *pdev)
 
        i2c_del_adapter(&(iface->adap));
        free_irq(iface->irq, iface);
-       peripheral_free_list((unsigned short *)dev_get_platdata(&pdev->dev));
+       peripheral_free_list(dev_get_platdata(&pdev->dev));
        iounmap(iface->regs_base);
        kfree(iface);
 
index 2d46f13..ce7ffba 100644 (file)
@@ -246,6 +246,7 @@ static int cbus_i2c_probe(struct platform_device *pdev)
        adapter->owner          = THIS_MODULE;
        adapter->class          = I2C_CLASS_HWMON;
        adapter->dev.parent     = &pdev->dev;
+       adapter->dev.of_node    = pdev->dev.of_node;
        adapter->nr             = pdev->id;
        adapter->timeout        = HZ;
        adapter->algo           = &cbus_i2c_algo;
@@ -289,6 +290,7 @@ static struct platform_driver cbus_i2c_driver = {
        .driver = {
                .owner  = THIS_MODULE,
                .name   = "i2c-cbus-gpio",
+               .of_match_table = of_match_ptr(i2c_cbus_dt_ids),
        },
 };
 module_platform_driver(cbus_i2c_driver);
index 132369f..ff05d9f 100644 (file)
@@ -323,7 +323,7 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
 
        davinci_i2c_write_reg(dev, DAVINCI_I2C_CNT_REG, dev->buf_len);
 
-       INIT_COMPLETION(dev->cmd_complete);
+       reinit_completion(&dev->cmd_complete);
        dev->cmd_err = 0;
 
        /* Take I2C out of reset and configure it as master */
@@ -795,7 +795,7 @@ static struct platform_driver davinci_i2c_driver = {
                .name   = "i2c_davinci",
                .owner  = THIS_MODULE,
                .pm     = davinci_i2c_pm_ops,
-               .of_match_table = of_match_ptr(davinci_i2c_of_match),
+               .of_match_table = davinci_i2c_of_match,
        },
 };
 
index 5888fee..e89e3e2 100644 (file)
@@ -613,7 +613,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
        mutex_lock(&dev->lock);
        pm_runtime_get_sync(dev->dev);
 
-       INIT_COMPLETION(dev->cmd_complete);
+       reinit_completion(&dev->cmd_complete);
        dev->msgs = msgs;
        dev->msgs_num = num;
        dev->cmd_err = 0;
index 0aa0113..d0bdac0 100644 (file)
@@ -103,6 +103,8 @@ static int dw_i2c_acpi_configure(struct platform_device *pdev)
 static const struct acpi_device_id dw_i2c_acpi_match[] = {
        { "INT33C2", 0 },
        { "INT33C3", 0 },
+       { "INT3432", 0 },
+       { "INT3433", 0 },
        { "80860F41", 0 },
        { }
 };
index 0f37529..ff15ae9 100644 (file)
@@ -312,24 +312,6 @@ static void pch_i2c_start(struct i2c_algo_pch_data *adap)
 }
 
 /**
- * pch_i2c_getack() - to confirm ACK/NACK
- * @adap:      Pointer to struct i2c_algo_pch_data.
- */
-static s32 pch_i2c_getack(struct i2c_algo_pch_data *adap)
-{
-       u32 reg_val;
-       void __iomem *p = adap->pch_base_address;
-       reg_val = ioread32(p + PCH_I2CSR) & PCH_GETACK;
-
-       if (reg_val != 0) {
-               pch_err(adap, "return%d\n", -EPROTO);
-               return -EPROTO;
-       }
-
-       return 0;
-}
-
-/**
  * pch_i2c_stop() - generate stop condition in normal mode.
  * @adap:      Pointer to struct i2c_algo_pch_data.
  */
@@ -344,6 +326,7 @@ static void pch_i2c_stop(struct i2c_algo_pch_data *adap)
 static int pch_i2c_wait_for_check_xfer(struct i2c_algo_pch_data *adap)
 {
        long ret;
+       void __iomem *p = adap->pch_base_address;
 
        ret = wait_event_timeout(pch_event,
                        (adap->pch_event_flag != 0), msecs_to_jiffies(1000));
@@ -366,10 +349,9 @@ static int pch_i2c_wait_for_check_xfer(struct i2c_algo_pch_data *adap)
 
        adap->pch_event_flag = 0;
 
-       if (pch_i2c_getack(adap)) {
-               pch_dbg(adap, "Receive NACK for slave address"
-                       "setting\n");
-               return -EIO;
+       if (ioread32(p + PCH_I2CSR) & PCH_GETACK) {
+               pch_dbg(adap, "Receive NACK for slave address setting\n");
+               return -ENXIO;
        }
 
        return 0;
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
new file mode 100644 (file)
index 0000000..c1ef228
--- /dev/null
@@ -0,0 +1,769 @@
+/**
+ * i2c-exynos5.c - Samsung Exynos5 I2C Controller Driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/spinlock.h>
+
+/*
+ * HSI2C controller from Samsung supports 2 modes of operation
+ * 1. Auto mode: Where in master automatically controls the whole transaction
+ * 2. Manual mode: Software controls the transaction by issuing commands
+ *    START, READ, WRITE, STOP, RESTART in I2C_MANUAL_CMD register.
+ *
+ * Operation mode can be selected by setting AUTO_MODE bit in I2C_CONF register
+ *
+ * Special bits are available for both modes of operation to set commands
+ * and for checking transfer status
+ */
+
+/* Register Map */
+#define HSI2C_CTL              0x00
+#define HSI2C_FIFO_CTL         0x04
+#define HSI2C_TRAILIG_CTL      0x08
+#define HSI2C_CLK_CTL          0x0C
+#define HSI2C_CLK_SLOT         0x10
+#define HSI2C_INT_ENABLE       0x20
+#define HSI2C_INT_STATUS       0x24
+#define HSI2C_ERR_STATUS       0x2C
+#define HSI2C_FIFO_STATUS      0x30
+#define HSI2C_TX_DATA          0x34
+#define HSI2C_RX_DATA          0x38
+#define HSI2C_CONF             0x40
+#define HSI2C_AUTO_CONF                0x44
+#define HSI2C_TIMEOUT          0x48
+#define HSI2C_MANUAL_CMD       0x4C
+#define HSI2C_TRANS_STATUS     0x50
+#define HSI2C_TIMING_HS1       0x54
+#define HSI2C_TIMING_HS2       0x58
+#define HSI2C_TIMING_HS3       0x5C
+#define HSI2C_TIMING_FS1       0x60
+#define HSI2C_TIMING_FS2       0x64
+#define HSI2C_TIMING_FS3       0x68
+#define HSI2C_TIMING_SLA       0x6C
+#define HSI2C_ADDR             0x70
+
+/* I2C_CTL Register bits */
+#define HSI2C_FUNC_MODE_I2C                    (1u << 0)
+#define HSI2C_MASTER                           (1u << 3)
+#define HSI2C_RXCHON                           (1u << 6)
+#define HSI2C_TXCHON                           (1u << 7)
+#define HSI2C_SW_RST                           (1u << 31)
+
+/* I2C_FIFO_CTL Register bits */
+#define HSI2C_RXFIFO_EN                                (1u << 0)
+#define HSI2C_TXFIFO_EN                                (1u << 1)
+#define HSI2C_RXFIFO_TRIGGER_LEVEL(x)          ((x) << 4)
+#define HSI2C_TXFIFO_TRIGGER_LEVEL(x)          ((x) << 16)
+
+/* As per user manual FIFO max depth is 64bytes */
+#define HSI2C_FIFO_MAX                         0x40
+/* default trigger levels for Tx and Rx FIFOs */
+#define HSI2C_DEF_TXFIFO_LVL                   (HSI2C_FIFO_MAX - 0x30)
+#define HSI2C_DEF_RXFIFO_LVL                   (HSI2C_FIFO_MAX - 0x10)
+
+/* I2C_TRAILING_CTL Register bits */
+#define HSI2C_TRAILING_COUNT                   (0xf)
+
+/* I2C_INT_EN Register bits */
+#define HSI2C_INT_TX_ALMOSTEMPTY_EN            (1u << 0)
+#define HSI2C_INT_RX_ALMOSTFULL_EN             (1u << 1)
+#define HSI2C_INT_TRAILING_EN                  (1u << 6)
+#define HSI2C_INT_I2C_EN                       (1u << 9)
+
+/* I2C_INT_STAT Register bits */
+#define HSI2C_INT_TX_ALMOSTEMPTY               (1u << 0)
+#define HSI2C_INT_RX_ALMOSTFULL                        (1u << 1)
+#define HSI2C_INT_TX_UNDERRUN                  (1u << 2)
+#define HSI2C_INT_TX_OVERRUN                   (1u << 3)
+#define HSI2C_INT_RX_UNDERRUN                  (1u << 4)
+#define HSI2C_INT_RX_OVERRUN                   (1u << 5)
+#define HSI2C_INT_TRAILING                     (1u << 6)
+#define HSI2C_INT_I2C                          (1u << 9)
+
+/* I2C_FIFO_STAT Register bits */
+#define HSI2C_RX_FIFO_EMPTY                    (1u << 24)
+#define HSI2C_RX_FIFO_FULL                     (1u << 23)
+#define HSI2C_RX_FIFO_LVL(x)                   ((x >> 16) & 0x7f)
+#define HSI2C_TX_FIFO_EMPTY                    (1u << 8)
+#define HSI2C_TX_FIFO_FULL                     (1u << 7)
+#define HSI2C_TX_FIFO_LVL(x)                   ((x >> 0) & 0x7f)
+
+/* I2C_CONF Register bits */
+#define HSI2C_AUTO_MODE                                (1u << 31)
+#define HSI2C_10BIT_ADDR_MODE                  (1u << 30)
+#define HSI2C_HS_MODE                          (1u << 29)
+
+/* I2C_AUTO_CONF Register bits */
+#define HSI2C_READ_WRITE                       (1u << 16)
+#define HSI2C_STOP_AFTER_TRANS                 (1u << 17)
+#define HSI2C_MASTER_RUN                       (1u << 31)
+
+/* I2C_TIMEOUT Register bits */
+#define HSI2C_TIMEOUT_EN                       (1u << 31)
+#define HSI2C_TIMEOUT_MASK                     0xff
+
+/* I2C_TRANS_STATUS register bits */
+#define HSI2C_MASTER_BUSY                      (1u << 17)
+#define HSI2C_SLAVE_BUSY                       (1u << 16)
+#define HSI2C_TIMEOUT_AUTO                     (1u << 4)
+#define HSI2C_NO_DEV                           (1u << 3)
+#define HSI2C_NO_DEV_ACK                       (1u << 2)
+#define HSI2C_TRANS_ABORT                      (1u << 1)
+#define HSI2C_TRANS_DONE                       (1u << 0)
+
+/* I2C_ADDR register bits */
+#define HSI2C_SLV_ADDR_SLV(x)                  ((x & 0x3ff) << 0)
+#define HSI2C_SLV_ADDR_MAS(x)                  ((x & 0x3ff) << 10)
+#define HSI2C_MASTER_ID(x)                     ((x & 0xff) << 24)
+#define MASTER_ID(x)                           ((x & 0x7) + 0x08)
+
+/*
+ * Controller operating frequency, timing values for operation
+ * are calculated against this frequency
+ */
+#define HSI2C_HS_TX_CLOCK      1000000
+#define HSI2C_FS_TX_CLOCK      100000
+#define HSI2C_HIGH_SPD         1
+#define HSI2C_FAST_SPD         0
+
+#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+struct exynos5_i2c {
+       struct i2c_adapter      adap;
+       unsigned int            suspended:1;
+
+       struct i2c_msg          *msg;
+       struct completion       msg_complete;
+       unsigned int            msg_ptr;
+
+       unsigned int            irq;
+
+       void __iomem            *regs;
+       struct clk              *clk;
+       struct device           *dev;
+       int                     state;
+
+       spinlock_t              lock;           /* IRQ synchronization */
+
+       /*
+        * Since the TRANS_DONE bit is cleared on read, and we may read it
+        * either during an IRQ or after a transaction, keep track of its
+        * state here.
+        */
+       int                     trans_done;
+
+       /* Controller operating frequency */
+       unsigned int            fs_clock;
+       unsigned int            hs_clock;
+
+       /*
+        * HSI2C Controller can operate in
+        * 1. High speed upto 3.4Mbps
+        * 2. Fast speed upto 1Mbps
+        */
+       int                     speed_mode;
+};
+
+static const struct of_device_id exynos5_i2c_match[] = {
+       { .compatible = "samsung,exynos5-hsi2c" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
+
+static void exynos5_i2c_clr_pend_irq(struct exynos5_i2c *i2c)
+{
+       writel(readl(i2c->regs + HSI2C_INT_STATUS),
+                               i2c->regs + HSI2C_INT_STATUS);
+}
+
+/*
+ * exynos5_i2c_set_timing: updates the registers with appropriate
+ * timing values calculated
+ *
+ * Returns 0 on success, -EINVAL if the cycle length cannot
+ * be calculated.
+ */
+static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, int mode)
+{
+       u32 i2c_timing_s1;
+       u32 i2c_timing_s2;
+       u32 i2c_timing_s3;
+       u32 i2c_timing_sla;
+       unsigned int t_start_su, t_start_hd;
+       unsigned int t_stop_su;
+       unsigned int t_data_su, t_data_hd;
+       unsigned int t_scl_l, t_scl_h;
+       unsigned int t_sr_release;
+       unsigned int t_ftl_cycle;
+       unsigned int clkin = clk_get_rate(i2c->clk);
+       unsigned int div, utemp0 = 0, utemp1 = 0, clk_cycle;
+       unsigned int op_clk = (mode == HSI2C_HIGH_SPD) ?
+                               i2c->hs_clock : i2c->fs_clock;
+
+       /*
+        * FPCLK / FI2C =
+        * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE
+        * utemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2)
+        * utemp1 = (TSCLK_L + TSCLK_H + 2)
+        */
+       t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7;
+       utemp0 = (clkin / op_clk) - 8 - 2 * t_ftl_cycle;
+
+       /* CLK_DIV max is 256 */
+       for (div = 0; div < 256; div++) {
+               utemp1 = utemp0 / (div + 1);
+
+               /*
+                * SCL_L and SCL_H each has max value of 255
+                * Hence, For the clk_cycle to the have right value
+                * utemp1 has to be less then 512 and more than 4.
+                */
+               if ((utemp1 < 512) && (utemp1 > 4)) {
+                       clk_cycle = utemp1 - 2;
+                       break;
+               } else if (div == 255) {
+                       dev_warn(i2c->dev, "Failed to calculate divisor");
+                       return -EINVAL;
+               }
+       }
+
+       t_scl_l = clk_cycle / 2;
+       t_scl_h = clk_cycle / 2;
+       t_start_su = t_scl_l;
+       t_start_hd = t_scl_l;
+       t_stop_su = t_scl_l;
+       t_data_su = t_scl_l / 2;
+       t_data_hd = t_scl_l / 2;
+       t_sr_release = clk_cycle;
+
+       i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8;
+       i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0;
+       i2c_timing_s3 = div << 16 | t_sr_release << 0;
+       i2c_timing_sla = t_data_hd << 0;
+
+       dev_dbg(i2c->dev, "tSTART_SU: %X, tSTART_HD: %X, tSTOP_SU: %X\n",
+               t_start_su, t_start_hd, t_stop_su);
+       dev_dbg(i2c->dev, "tDATA_SU: %X, tSCL_L: %X, tSCL_H: %X\n",
+               t_data_su, t_scl_l, t_scl_h);
+       dev_dbg(i2c->dev, "nClkDiv: %X, tSR_RELEASE: %X\n",
+               div, t_sr_release);
+       dev_dbg(i2c->dev, "tDATA_HD: %X\n", t_data_hd);
+
+       if (mode == HSI2C_HIGH_SPD) {
+               writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_HS1);
+               writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_HS2);
+               writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_HS3);
+       } else {
+               writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_FS1);
+               writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_FS2);
+               writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_FS3);
+       }
+       writel(i2c_timing_sla, i2c->regs + HSI2C_TIMING_SLA);
+
+       return 0;
+}
+
+static int exynos5_hsi2c_clock_setup(struct exynos5_i2c *i2c)
+{
+       /*
+        * Configure the Fast speed timing values
+        * Even the High Speed mode initially starts with Fast mode
+        */
+       if (exynos5_i2c_set_timing(i2c, HSI2C_FAST_SPD)) {
+               dev_err(i2c->dev, "HSI2C FS Clock set up failed\n");
+               return -EINVAL;
+       }
+
+       /* configure the High speed timing values */
+       if (i2c->speed_mode == HSI2C_HIGH_SPD) {
+               if (exynos5_i2c_set_timing(i2c, HSI2C_HIGH_SPD)) {
+                       dev_err(i2c->dev, "HSI2C HS Clock set up failed\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * exynos5_i2c_init: configures the controller for I2C functionality
+ * Programs I2C controller for Master mode operation
+ */
+static void exynos5_i2c_init(struct exynos5_i2c *i2c)
+{
+       u32 i2c_conf = readl(i2c->regs + HSI2C_CONF);
+       u32 i2c_timeout = readl(i2c->regs + HSI2C_TIMEOUT);
+
+       /* Clear to disable Timeout */
+       i2c_timeout &= ~HSI2C_TIMEOUT_EN;
+       writel(i2c_timeout, i2c->regs + HSI2C_TIMEOUT);
+
+       writel((HSI2C_FUNC_MODE_I2C | HSI2C_MASTER),
+                                       i2c->regs + HSI2C_CTL);
+       writel(HSI2C_TRAILING_COUNT, i2c->regs + HSI2C_TRAILIG_CTL);
+
+       if (i2c->speed_mode == HSI2C_HIGH_SPD) {
+               writel(HSI2C_MASTER_ID(MASTER_ID(i2c->adap.nr)),
+                                       i2c->regs + HSI2C_ADDR);
+               i2c_conf |= HSI2C_HS_MODE;
+       }
+
+       writel(i2c_conf | HSI2C_AUTO_MODE, i2c->regs + HSI2C_CONF);
+}
+
+static void exynos5_i2c_reset(struct exynos5_i2c *i2c)
+{
+       u32 i2c_ctl;
+
+       /* Set and clear the bit for reset */
+       i2c_ctl = readl(i2c->regs + HSI2C_CTL);
+       i2c_ctl |= HSI2C_SW_RST;
+       writel(i2c_ctl, i2c->regs + HSI2C_CTL);
+
+       i2c_ctl = readl(i2c->regs + HSI2C_CTL);
+       i2c_ctl &= ~HSI2C_SW_RST;
+       writel(i2c_ctl, i2c->regs + HSI2C_CTL);
+
+       /* We don't expect calculations to fail during the run */
+       exynos5_hsi2c_clock_setup(i2c);
+       /* Initialize the configure registers */
+       exynos5_i2c_init(i2c);
+}
+
+/*
+ * exynos5_i2c_irq: top level IRQ servicing routine
+ *
+ * INT_STATUS registers gives the interrupt details. Further,
+ * FIFO_STATUS or TRANS_STATUS registers are to be check for detailed
+ * state of the bus.
+ */
+static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
+{
+       struct exynos5_i2c *i2c = dev_id;
+       u32 fifo_level, int_status, fifo_status, trans_status;
+       unsigned char byte;
+       int len = 0;
+
+       i2c->state = -EINVAL;
+
+       spin_lock(&i2c->lock);
+
+       int_status = readl(i2c->regs + HSI2C_INT_STATUS);
+       writel(int_status, i2c->regs + HSI2C_INT_STATUS);
+       fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS);
+
+       /* handle interrupt related to the transfer status */
+       if (int_status & HSI2C_INT_I2C) {
+               trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
+               if (trans_status & HSI2C_NO_DEV_ACK) {
+                       dev_dbg(i2c->dev, "No ACK from device\n");
+                       i2c->state = -ENXIO;
+                       goto stop;
+               } else if (trans_status & HSI2C_NO_DEV) {
+                       dev_dbg(i2c->dev, "No device\n");
+                       i2c->state = -ENXIO;
+                       goto stop;
+               } else if (trans_status & HSI2C_TRANS_ABORT) {
+                       dev_dbg(i2c->dev, "Deal with arbitration lose\n");
+                       i2c->state = -EAGAIN;
+                       goto stop;
+               } else if (trans_status & HSI2C_TIMEOUT_AUTO) {
+                       dev_dbg(i2c->dev, "Accessing device timed out\n");
+                       i2c->state = -EAGAIN;
+                       goto stop;
+               } else if (trans_status & HSI2C_TRANS_DONE) {
+                       i2c->trans_done = 1;
+                       i2c->state = 0;
+               }
+       }
+
+       if ((i2c->msg->flags & I2C_M_RD) && (int_status &
+                       (HSI2C_INT_TRAILING | HSI2C_INT_RX_ALMOSTFULL))) {
+               fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS);
+               fifo_level = HSI2C_RX_FIFO_LVL(fifo_status);
+               len = min(fifo_level, i2c->msg->len - i2c->msg_ptr);
+
+               while (len > 0) {
+                       byte = (unsigned char)
+                               readl(i2c->regs + HSI2C_RX_DATA);
+                       i2c->msg->buf[i2c->msg_ptr++] = byte;
+                       len--;
+               }
+               i2c->state = 0;
+       } else if (int_status & HSI2C_INT_TX_ALMOSTEMPTY) {
+               fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS);
+               fifo_level = HSI2C_TX_FIFO_LVL(fifo_status);
+
+               len = HSI2C_FIFO_MAX - fifo_level;
+               if (len > (i2c->msg->len - i2c->msg_ptr))
+                       len = i2c->msg->len - i2c->msg_ptr;
+
+               while (len > 0) {
+                       byte = i2c->msg->buf[i2c->msg_ptr++];
+                       writel(byte, i2c->regs + HSI2C_TX_DATA);
+                       len--;
+               }
+               i2c->state = 0;
+       }
+
+ stop:
+       if ((i2c->trans_done && (i2c->msg->len == i2c->msg_ptr)) ||
+           (i2c->state < 0)) {
+               writel(0, i2c->regs + HSI2C_INT_ENABLE);
+               exynos5_i2c_clr_pend_irq(i2c);
+               complete(&i2c->msg_complete);
+       }
+
+       spin_unlock(&i2c->lock);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * exynos5_i2c_wait_bus_idle
+ *
+ * Wait for the bus to go idle, indicated by the MASTER_BUSY bit being
+ * cleared.
+ *
+ * Returns -EBUSY if the bus cannot be bought to idle
+ */
+static int exynos5_i2c_wait_bus_idle(struct exynos5_i2c *i2c)
+{
+       unsigned long stop_time;
+       u32 trans_status;
+
+       /* wait for 100 milli seconds for the bus to be idle */
+       stop_time = jiffies + msecs_to_jiffies(100) + 1;
+       do {
+               trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
+               if (!(trans_status & HSI2C_MASTER_BUSY))
+                       return 0;
+
+               usleep_range(50, 200);
+       } while (time_before(jiffies, stop_time));
+
+       return -EBUSY;
+}
+
+/*
+ * exynos5_i2c_message_start: Configures the bus and starts the xfer
+ * i2c: struct exynos5_i2c pointer for the current bus
+ * stop: Enables stop after transfer if set. Set for last transfer of
+ *       in the list of messages.
+ *
+ * Configures the bus for read/write function
+ * Sets chip address to talk to, message length to be sent.
+ * Enables appropriate interrupts and sends start xfer command.
+ */
+static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
+{
+       u32 i2c_ctl;
+       u32 int_en = HSI2C_INT_I2C_EN;
+       u32 i2c_auto_conf = 0;
+       u32 fifo_ctl;
+       unsigned long flags;
+
+       i2c_ctl = readl(i2c->regs + HSI2C_CTL);
+       i2c_ctl &= ~(HSI2C_TXCHON | HSI2C_RXCHON);
+       fifo_ctl = HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN;
+
+       if (i2c->msg->flags & I2C_M_RD) {
+               i2c_ctl |= HSI2C_RXCHON;
+
+               i2c_auto_conf = HSI2C_READ_WRITE;
+
+               fifo_ctl |= HSI2C_RXFIFO_TRIGGER_LEVEL(HSI2C_DEF_TXFIFO_LVL);
+               int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN |
+                       HSI2C_INT_TRAILING_EN);
+       } else {
+               i2c_ctl |= HSI2C_TXCHON;
+
+               fifo_ctl |= HSI2C_TXFIFO_TRIGGER_LEVEL(HSI2C_DEF_RXFIFO_LVL);
+               int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN;
+       }
+
+       writel(HSI2C_SLV_ADDR_MAS(i2c->msg->addr), i2c->regs + HSI2C_ADDR);
+
+       writel(fifo_ctl, i2c->regs + HSI2C_FIFO_CTL);
+       writel(i2c_ctl, i2c->regs + HSI2C_CTL);
+
+
+       /*
+        * Enable interrupts before starting the transfer so that we don't
+        * miss any INT_I2C interrupts.
+        */
+       spin_lock_irqsave(&i2c->lock, flags);
+       writel(int_en, i2c->regs + HSI2C_INT_ENABLE);
+
+       if (stop == 1)
+               i2c_auto_conf |= HSI2C_STOP_AFTER_TRANS;
+       i2c_auto_conf |= i2c->msg->len;
+       i2c_auto_conf |= HSI2C_MASTER_RUN;
+       writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF);
+       spin_unlock_irqrestore(&i2c->lock, flags);
+}
+
+static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
+                             struct i2c_msg *msgs, int stop)
+{
+       unsigned long timeout;
+       int ret;
+
+       i2c->msg = msgs;
+       i2c->msg_ptr = 0;
+       i2c->trans_done = 0;
+
+       reinit_completion(&i2c->msg_complete);
+
+       exynos5_i2c_message_start(i2c, stop);
+
+       timeout = wait_for_completion_timeout(&i2c->msg_complete,
+                                             EXYNOS5_I2C_TIMEOUT);
+       if (timeout == 0)
+               ret = -ETIMEDOUT;
+       else
+               ret = i2c->state;
+
+       /*
+        * If this is the last message to be transfered (stop == 1)
+        * Then check if the bus can be brought back to idle.
+        */
+       if (ret == 0 && stop)
+               ret = exynos5_i2c_wait_bus_idle(i2c);
+
+       if (ret < 0) {
+               exynos5_i2c_reset(i2c);
+               if (ret == -ETIMEDOUT)
+                       dev_warn(i2c->dev, "%s timeout\n",
+                                (msgs->flags & I2C_M_RD) ? "rx" : "tx");
+       }
+
+       /* Return the state as in interrupt routine */
+       return ret;
+}
+
+static int exynos5_i2c_xfer(struct i2c_adapter *adap,
+                       struct i2c_msg *msgs, int num)
+{
+       struct exynos5_i2c *i2c = (struct exynos5_i2c *)adap->algo_data;
+       int i = 0, ret = 0, stop = 0;
+
+       if (i2c->suspended) {
+               dev_err(i2c->dev, "HS-I2C is not initialzed.\n");
+               return -EIO;
+       }
+
+       clk_prepare_enable(i2c->clk);
+
+       for (i = 0; i < num; i++, msgs++) {
+               stop = (i == num - 1);
+
+               ret = exynos5_i2c_xfer_msg(i2c, msgs, stop);
+
+               if (ret < 0)
+                       goto out;
+       }
+
+       if (i == num) {
+               ret = num;
+       } else {
+               /* Only one message, cannot access the device */
+               if (i == 1)
+                       ret = -EREMOTEIO;
+               else
+                       ret = i;
+
+               dev_warn(i2c->dev, "xfer message failed\n");
+       }
+
+ out:
+       clk_disable_unprepare(i2c->clk);
+       return ret;
+}
+
+static u32 exynos5_i2c_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm exynos5_i2c_algorithm = {
+       .master_xfer            = exynos5_i2c_xfer,
+       .functionality          = exynos5_i2c_func,
+};
+
+static int exynos5_i2c_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct exynos5_i2c *i2c;
+       struct resource *mem;
+       unsigned int op_clock;
+       int ret;
+
+       i2c = devm_kzalloc(&pdev->dev, sizeof(struct exynos5_i2c), GFP_KERNEL);
+       if (!i2c) {
+               dev_err(&pdev->dev, "no memory for state\n");
+               return -ENOMEM;
+       }
+
+       if (of_property_read_u32(np, "clock-frequency", &op_clock)) {
+               i2c->speed_mode = HSI2C_FAST_SPD;
+               i2c->fs_clock = HSI2C_FS_TX_CLOCK;
+       } else {
+               if (op_clock >= HSI2C_HS_TX_CLOCK) {
+                       i2c->speed_mode = HSI2C_HIGH_SPD;
+                       i2c->fs_clock = HSI2C_FS_TX_CLOCK;
+                       i2c->hs_clock = op_clock;
+               } else {
+                       i2c->speed_mode = HSI2C_FAST_SPD;
+                       i2c->fs_clock = op_clock;
+               }
+       }
+
+       strlcpy(i2c->adap.name, "exynos5-i2c", sizeof(i2c->adap.name));
+       i2c->adap.owner   = THIS_MODULE;
+       i2c->adap.algo    = &exynos5_i2c_algorithm;
+       i2c->adap.retries = 3;
+
+       i2c->dev = &pdev->dev;
+       i2c->clk = devm_clk_get(&pdev->dev, "hsi2c");
+       if (IS_ERR(i2c->clk)) {
+               dev_err(&pdev->dev, "cannot get clock\n");
+               return -ENOENT;
+       }
+
+       clk_prepare_enable(i2c->clk);
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(i2c->regs)) {
+               ret = PTR_ERR(i2c->regs);
+               goto err_clk;
+       }
+
+       i2c->adap.dev.of_node = np;
+       i2c->adap.algo_data = i2c;
+       i2c->adap.dev.parent = &pdev->dev;
+
+       /* Clear pending interrupts from u-boot or misc causes */
+       exynos5_i2c_clr_pend_irq(i2c);
+
+       spin_lock_init(&i2c->lock);
+       init_completion(&i2c->msg_complete);
+
+       i2c->irq = ret = platform_get_irq(pdev, 0);
+       if (ret <= 0) {
+               dev_err(&pdev->dev, "cannot find HS-I2C IRQ\n");
+               ret = -EINVAL;
+               goto err_clk;
+       }
+
+       ret = devm_request_irq(&pdev->dev, i2c->irq, exynos5_i2c_irq,
+                               IRQF_NO_SUSPEND | IRQF_ONESHOT,
+                               dev_name(&pdev->dev), i2c);
+
+       if (ret != 0) {
+               dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n", i2c->irq);
+               goto err_clk;
+       }
+
+       ret = exynos5_hsi2c_clock_setup(i2c);
+       if (ret)
+               goto err_clk;
+
+       exynos5_i2c_init(i2c);
+
+       ret = i2c_add_adapter(&i2c->adap);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to add bus to i2c core\n");
+               goto err_clk;
+       }
+
+       platform_set_drvdata(pdev, i2c);
+
+ err_clk:
+       clk_disable_unprepare(i2c->clk);
+       return ret;
+}
+
+static int exynos5_i2c_remove(struct platform_device *pdev)
+{
+       struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+
+       i2c_del_adapter(&i2c->adap);
+
+       return 0;
+}
+
+static int exynos5_i2c_suspend_noirq(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+
+       i2c->suspended = 1;
+
+       return 0;
+}
+
+static int exynos5_i2c_resume_noirq(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+       int ret = 0;
+
+       clk_prepare_enable(i2c->clk);
+
+       ret = exynos5_hsi2c_clock_setup(i2c);
+       if (ret) {
+               clk_disable_unprepare(i2c->clk);
+               return ret;
+       }
+
+       exynos5_i2c_init(i2c);
+       clk_disable_unprepare(i2c->clk);
+       i2c->suspended = 0;
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(exynos5_i2c_dev_pm_ops, exynos5_i2c_suspend_noirq,
+                        exynos5_i2c_resume_noirq);
+
+static struct platform_driver exynos5_i2c_driver = {
+       .probe          = exynos5_i2c_probe,
+       .remove         = exynos5_i2c_remove,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "exynos5-hsi2c",
+               .pm     = &exynos5_i2c_dev_pm_ops,
+               .of_match_table = exynos5_i2c_match,
+       },
+};
+
+module_platform_driver(exynos5_i2c_driver);
+
+MODULE_DESCRIPTION("Exynos5 HS-I2C Bus driver");
+MODULE_AUTHOR("Naveen Krishna Chatradhi, <ch.naveen@samsung.com>");
+MODULE_AUTHOR("Taekgyun Ko, <taeggyun.ko@samsung.com>");
+MODULE_LICENSE("GPL v2");
index bfa02c6..d9f7e18 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
+#include <linux/of.h>
 #include <linux/of_gpio.h>
 
 struct i2c_gpio_private_data {
index 4296d17..737e298 100644 (file)
@@ -59,6 +59,7 @@
   Wellsburg (PCH) MS    0x8d7e     32     hard     yes     yes     yes
   Wellsburg (PCH) MS    0x8d7f     32     hard     yes     yes     yes
   Coleto Creek (PCH)    0x23b0     32     hard     yes     yes     yes
+  Wildcat Point-LP (PCH)   0x9ca2     32     hard     yes     yes     yes
 
   Features supported by this driver:
   Software PEC                     no
 #define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1        0x8d7e
 #define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2        0x8d7f
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
+#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS      0x9ca2
 
 struct i801_mux_config {
        char *gpio_chip;
@@ -819,6 +821,7 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = {
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS) },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS) },
        { 0, }
 };
 
index 1672eff..0043ede 100644 (file)
@@ -541,7 +541,7 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
                desc->dptr_high = upper_32_bits(dma_addr);
        }
 
-       INIT_COMPLETION(priv->cmp);
+       reinit_completion(&priv->cmp);
 
        /* Add the descriptor */
        ismt_submit_desc(priv);
index d3e9cc3..8be7e42 100644 (file)
@@ -911,7 +911,7 @@ static struct platform_driver mv64xxx_i2c_driver = {
        .driver = {
                .owner  = THIS_MODULE,
                .name   = MV64XXX_I2C_CTLR_NAME,
-               .of_match_table = of_match_ptr(mv64xxx_i2c_of_match_table),
+               .of_match_table = mv64xxx_i2c_of_match_table,
        },
 };
 
index b7c8577..0cde4e6 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Freescale MXS I2C bus driver
  *
+ * Copyright (C) 2012-2013 Marek Vasut <marex@denx.de>
  * Copyright (C) 2011-2012 Wolfram Sang, Pengutronix e.K.
  *
  * based on a (non-working) driver which was:
 
 #define MXS_I2C_CTRL0          (0x00)
 #define MXS_I2C_CTRL0_SET      (0x04)
+#define MXS_I2C_CTRL0_CLR      (0x08)
 
 #define MXS_I2C_CTRL0_SFTRST                   0x80000000
 #define MXS_I2C_CTRL0_RUN                      0x20000000
 #define MXS_I2C_CTRL0_SEND_NAK_ON_LAST         0x02000000
+#define MXS_I2C_CTRL0_PIO_MODE                 0x01000000
 #define MXS_I2C_CTRL0_RETAIN_CLOCK             0x00200000
 #define MXS_I2C_CTRL0_POST_SEND_STOP           0x00100000
 #define MXS_I2C_CTRL0_PRE_SEND_START           0x00080000
 #define MXS_I2C_CTRL1_SLAVE_IRQ                        0x01
 
 #define MXS_I2C_STAT           (0x50)
+#define MXS_I2C_STAT_GOT_A_NAK                 0x10000000
 #define MXS_I2C_STAT_BUS_BUSY                  0x00000800
 #define MXS_I2C_STAT_CLK_GEN_BUSY              0x00000400
 
-#define MXS_I2C_DATA           (0xa0)
+#define MXS_I2C_DATA(i2c)      ((i2c->dev_type == MXS_I2C_V1) ? 0x60 : 0xa0)
 
-#define MXS_I2C_DEBUG0         (0xb0)
-#define MXS_I2C_DEBUG0_CLR     (0xb8)
+#define MXS_I2C_DEBUG0_CLR(i2c)        ((i2c->dev_type == MXS_I2C_V1) ? 0x78 : 0xb8)
 
 #define MXS_I2C_DEBUG0_DMAREQ  0x80000000
 
 #define MXS_CMD_I2C_READ       (MXS_I2C_CTRL0_SEND_NAK_ON_LAST | \
                                 MXS_I2C_CTRL0_MASTER_MODE)
 
+enum mxs_i2c_devtype {
+       MXS_I2C_UNKNOWN = 0,
+       MXS_I2C_V1,
+       MXS_I2C_V2,
+};
+
 /**
  * struct mxs_i2c_dev - per device, private MXS-I2C data
  *
  * @dev: driver model device node
+ * @dev_type: distinguish i.MX23/i.MX28 features
  * @regs: IO registers pointer
  * @cmd_complete: completion object for transaction wait
  * @cmd_err: error code for last transaction
  */
 struct mxs_i2c_dev {
        struct device *dev;
+       enum mxs_i2c_devtype dev_type;
        void __iomem *regs;
        struct completion cmd_complete;
        int cmd_err;
@@ -291,48 +302,11 @@ write_init_pio_fail:
        return -EINVAL;
 }
 
-static int mxs_i2c_pio_wait_dmareq(struct mxs_i2c_dev *i2c)
+static int mxs_i2c_pio_wait_xfer_end(struct mxs_i2c_dev *i2c)
 {
        unsigned long timeout = jiffies + msecs_to_jiffies(1000);
 
-       while (!(readl(i2c->regs + MXS_I2C_DEBUG0) &
-               MXS_I2C_DEBUG0_DMAREQ)) {
-               if (time_after(jiffies, timeout))
-                       return -ETIMEDOUT;
-               cond_resched();
-       }
-
-       return 0;
-}
-
-static int mxs_i2c_pio_wait_cplt(struct mxs_i2c_dev *i2c, int last)
-{
-       unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-
-       /*
-        * We do not use interrupts in the PIO mode. Due to the
-        * maximum transfer length being 8 bytes in PIO mode, the
-        * overhead of interrupt would be too large and this would
-        * neglect the gain from using the PIO mode.
-        */
-
-       while (!(readl(i2c->regs + MXS_I2C_CTRL1) &
-               MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ)) {
-               if (time_after(jiffies, timeout))
-                       return -ETIMEDOUT;
-               cond_resched();
-       }
-
-       writel(MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ,
-               i2c->regs + MXS_I2C_CTRL1_CLR);
-
-       /*
-        * When ending a transfer with a stop, we have to wait for the bus to
-        * go idle before we report the transfer as completed. Otherwise the
-        * start of the next transfer may race with the end of the current one.
-        */
-       while (last && (readl(i2c->regs + MXS_I2C_STAT) &
-                       (MXS_I2C_STAT_BUS_BUSY | MXS_I2C_STAT_CLK_GEN_BUSY))) {
+       while (readl(i2c->regs + MXS_I2C_CTRL0) & MXS_I2C_CTRL0_RUN) {
                if (time_after(jiffies, timeout))
                        return -ETIMEDOUT;
                cond_resched();
@@ -370,106 +344,215 @@ static void mxs_i2c_pio_trigger_cmd(struct mxs_i2c_dev *i2c, u32 cmd)
        writel(reg, i2c->regs + MXS_I2C_CTRL0);
 }
 
+/*
+ * Start WRITE transaction on the I2C bus. By studying i.MX23 datasheet,
+ * CTRL0::PIO_MODE bit description clarifies the order in which the registers
+ * must be written during PIO mode operation. First, the CTRL0 register has
+ * to be programmed with all the necessary bits but the RUN bit. Then the
+ * payload has to be written into the DATA register. Finally, the transmission
+ * is executed by setting the RUN bit in CTRL0.
+ */
+static void mxs_i2c_pio_trigger_write_cmd(struct mxs_i2c_dev *i2c, u32 cmd,
+                                         u32 data)
+{
+       writel(cmd, i2c->regs + MXS_I2C_CTRL0);
+
+       if (i2c->dev_type == MXS_I2C_V1)
+               writel(MXS_I2C_CTRL0_PIO_MODE, i2c->regs + MXS_I2C_CTRL0_SET);
+
+       writel(data, i2c->regs + MXS_I2C_DATA(i2c));
+       writel(MXS_I2C_CTRL0_RUN, i2c->regs + MXS_I2C_CTRL0_SET);
+}
+
 static int mxs_i2c_pio_setup_xfer(struct i2c_adapter *adap,
                        struct i2c_msg *msg, uint32_t flags)
 {
        struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
        uint32_t addr_data = msg->addr << 1;
        uint32_t data = 0;
-       int i, shifts_left, ret;
+       int i, ret, xlen = 0, xmit = 0;
+       uint32_t start;
 
        /* Mute IRQs coming from this block. */
        writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_CLR);
 
+       /*
+        * MX23 idea:
+        * - Enable CTRL0::PIO_MODE (1 << 24)
+        * - Enable CTRL1::ACK_MODE (1 << 27)
+        *
+        * WARNING! The MX23 is broken in some way, even if it claims
+        * to support PIO, when we try to transfer any amount of data
+        * that is not aligned to 4 bytes, the DMA engine will have
+        * bits in DEBUG1::DMA_BYTES_ENABLES still set even after the
+        * transfer. This in turn will mess up the next transfer as
+        * the block it emit one byte write onto the bus terminated
+        * with a NAK+STOP. A possible workaround is to reset the IP
+        * block after every PIO transmission, which might just work.
+        *
+        * NOTE: The CTRL0::PIO_MODE description is important, since
+        * it outlines how the PIO mode is really supposed to work.
+        */
        if (msg->flags & I2C_M_RD) {
+               /*
+                * PIO READ transfer:
+                *
+                * This transfer MUST be limited to 4 bytes maximum. It is not
+                * possible to transfer more than four bytes via PIO, since we
+                * can not in any way make sure we can read the data from the
+                * DATA register fast enough. Besides, the RX FIFO is only four
+                * bytes deep, thus we can only really read up to four bytes at
+                * time. Finally, there is no bit indicating us that new data
+                * arrived at the FIFO and can thus be fetched from the DATA
+                * register.
+                */
+               BUG_ON(msg->len > 4);
+
                addr_data |= I2C_SMBUS_READ;
 
                /* SELECT command. */
-               mxs_i2c_pio_trigger_cmd(i2c, MXS_CMD_I2C_SELECT);
-
-               ret = mxs_i2c_pio_wait_dmareq(i2c);
-               if (ret)
-                       return ret;
-
-               writel(addr_data, i2c->regs + MXS_I2C_DATA);
-               writel(MXS_I2C_DEBUG0_DMAREQ, i2c->regs + MXS_I2C_DEBUG0_CLR);
+               mxs_i2c_pio_trigger_write_cmd(i2c, MXS_CMD_I2C_SELECT,
+                                             addr_data);
 
-               ret = mxs_i2c_pio_wait_cplt(i2c, 0);
-               if (ret)
-                       return ret;
-
-               if (mxs_i2c_pio_check_error_state(i2c))
+               ret = mxs_i2c_pio_wait_xfer_end(i2c);
+               if (ret) {
+                       dev_err(i2c->dev,
+                               "PIO: Failed to send SELECT command!\n");
                        goto cleanup;
+               }
 
                /* READ command. */
                mxs_i2c_pio_trigger_cmd(i2c,
                                        MXS_CMD_I2C_READ | flags |
                                        MXS_I2C_CTRL0_XFER_COUNT(msg->len));
 
+               ret = mxs_i2c_pio_wait_xfer_end(i2c);
+               if (ret) {
+                       dev_err(i2c->dev,
+                               "PIO: Failed to send SELECT command!\n");
+                       goto cleanup;
+               }
+
+               data = readl(i2c->regs + MXS_I2C_DATA(i2c));
                for (i = 0; i < msg->len; i++) {
-                       if ((i & 3) == 0) {
-                               ret = mxs_i2c_pio_wait_dmareq(i2c);
-                               if (ret)
-                                       return ret;
-                               data = readl(i2c->regs + MXS_I2C_DATA);
-                               writel(MXS_I2C_DEBUG0_DMAREQ,
-                                      i2c->regs + MXS_I2C_DEBUG0_CLR);
-                       }
                        msg->buf[i] = data & 0xff;
                        data >>= 8;
                }
        } else {
+               /*
+                * PIO WRITE transfer:
+                *
+                * The code below implements clock stretching to circumvent
+                * the possibility of kernel not being able to supply data
+                * fast enough. It is possible to transfer arbitrary amount
+                * of data using PIO write.
+                */
                addr_data |= I2C_SMBUS_WRITE;
 
-               /* WRITE command. */
-               mxs_i2c_pio_trigger_cmd(i2c,
-                                       MXS_CMD_I2C_WRITE | flags |
-                                       MXS_I2C_CTRL0_XFER_COUNT(msg->len + 1));
-
                /*
                 * The LSB of data buffer is the first byte blasted across
                 * the bus. Higher order bytes follow. Thus the following
                 * filling schematic.
                 */
+
                data = addr_data << 24;
+
+               /* Start the transfer with START condition. */
+               start = MXS_I2C_CTRL0_PRE_SEND_START;
+
+               /* If the transfer is long, use clock stretching. */
+               if (msg->len > 3)
+                       start |= MXS_I2C_CTRL0_RETAIN_CLOCK;
+
                for (i = 0; i < msg->len; i++) {
                        data >>= 8;
                        data |= (msg->buf[i] << 24);
-                       if ((i & 3) == 2) {
-                               ret = mxs_i2c_pio_wait_dmareq(i2c);
-                               if (ret)
-                                       return ret;
-                               writel(data, i2c->regs + MXS_I2C_DATA);
-                               writel(MXS_I2C_DEBUG0_DMAREQ,
-                                      i2c->regs + MXS_I2C_DEBUG0_CLR);
+
+                       xmit = 0;
+
+                       /* This is the last transfer of the message. */
+                       if (i + 1 == msg->len) {
+                               /* Add optional STOP flag. */
+                               start |= flags;
+                               /* Remove RETAIN_CLOCK bit. */
+                               start &= ~MXS_I2C_CTRL0_RETAIN_CLOCK;
+                               xmit = 1;
                        }
-               }
 
-               shifts_left = 24 - (i & 3) * 8;
-               if (shifts_left) {
-                       data >>= shifts_left;
-                       ret = mxs_i2c_pio_wait_dmareq(i2c);
-                       if (ret)
-                               return ret;
-                       writel(data, i2c->regs + MXS_I2C_DATA);
+                       /* Four bytes are ready in the "data" variable. */
+                       if ((i & 3) == 2)
+                               xmit = 1;
+
+                       /* Nothing interesting happened, continue stuffing. */
+                       if (!xmit)
+                               continue;
+
+                       /*
+                        * Compute the size of the transfer and shift the
+                        * data accordingly.
+                        *
+                        * i = (4k + 0) .... xlen = 2
+                        * i = (4k + 1) .... xlen = 3
+                        * i = (4k + 2) .... xlen = 4
+                        * i = (4k + 3) .... xlen = 1
+                        */
+
+                       if ((i % 4) == 3)
+                               xlen = 1;
+                       else
+                               xlen = (i % 4) + 2;
+
+                       data >>= (4 - xlen) * 8;
+
+                       dev_dbg(i2c->dev,
+                               "PIO: len=%i pos=%i total=%i [W%s%s%s]\n",
+                               xlen, i, msg->len,
+                               start & MXS_I2C_CTRL0_PRE_SEND_START ? "S" : "",
+                               start & MXS_I2C_CTRL0_POST_SEND_STOP ? "E" : "",
+                               start & MXS_I2C_CTRL0_RETAIN_CLOCK ? "C" : "");
+
                        writel(MXS_I2C_DEBUG0_DMAREQ,
-                              i2c->regs + MXS_I2C_DEBUG0_CLR);
+                              i2c->regs + MXS_I2C_DEBUG0_CLR(i2c));
+
+                       mxs_i2c_pio_trigger_write_cmd(i2c,
+                               start | MXS_I2C_CTRL0_MASTER_MODE |
+                               MXS_I2C_CTRL0_DIRECTION |
+                               MXS_I2C_CTRL0_XFER_COUNT(xlen), data);
+
+                       /* The START condition is sent only once. */
+                       start &= ~MXS_I2C_CTRL0_PRE_SEND_START;
+
+                       /* Wait for the end of the transfer. */
+                       ret = mxs_i2c_pio_wait_xfer_end(i2c);
+                       if (ret) {
+                               dev_err(i2c->dev,
+                                       "PIO: Failed to finish WRITE cmd!\n");
+                               break;
+                       }
+
+                       /* Check NAK here. */
+                       ret = readl(i2c->regs + MXS_I2C_STAT) &
+                                   MXS_I2C_STAT_GOT_A_NAK;
+                       if (ret) {
+                               ret = -ENXIO;
+                               goto cleanup;
+                       }
                }
        }
 
-       ret = mxs_i2c_pio_wait_cplt(i2c, flags & MXS_I2C_CTRL0_POST_SEND_STOP);
-       if (ret)
-               return ret;
-
        /* make sure we capture any occurred error into cmd_err */
-       mxs_i2c_pio_check_error_state(i2c);
+       ret = mxs_i2c_pio_check_error_state(i2c);
 
 cleanup:
        /* Clear any dangling IRQs and re-enable interrupts. */
        writel(MXS_I2C_IRQ_MASK, i2c->regs + MXS_I2C_CTRL1_CLR);
        writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET);
 
-       return 0;
+       /* Clear the PIO_MODE on i.MX23 */
+       if (i2c->dev_type == MXS_I2C_V1)
+               writel(MXS_I2C_CTRL0_PIO_MODE, i2c->regs + MXS_I2C_CTRL0_CLR);
+
+       return ret;
 }
 
 /*
@@ -479,8 +562,9 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
                                int stop)
 {
        struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
-       int ret, err;
+       int ret;
        int flags;
+       int use_pio = 0;
 
        flags = stop ? MXS_I2C_CTRL0_POST_SEND_STOP : 0;
 
@@ -491,21 +575,23 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
                return -EINVAL;
 
        /*
-        * The current boundary to select between PIO/DMA transfer method
-        * is set to 8 bytes, transfers shorter than 8 bytes are transfered
-        * using PIO mode while longer transfers use DMA. The 8 byte border is
-        * based on this empirical measurement and a lot of previous frobbing.
+        * The MX28 I2C IP block can only do PIO READ for transfer of to up
+        * 4 bytes of length. The write transfer is not limited as it can use
+        * clock stretching to avoid FIFO underruns.
         */
+       if ((msg->flags & I2C_M_RD) && (msg->len <= 4))
+               use_pio = 1;
+       if (!(msg->flags & I2C_M_RD) && (msg->len < 7))
+               use_pio = 1;
+
        i2c->cmd_err = 0;
-       if (0) {        /* disable PIO mode until a proper fix is made */
+       if (use_pio) {
                ret = mxs_i2c_pio_setup_xfer(adap, msg, flags);
-               if (ret) {
-                       err = mxs_i2c_reset(i2c);
-                       if (err)
-                               return err;
-               }
+               /* No need to reset the block if NAK was received. */
+               if (ret && (ret != -ENXIO))
+                       mxs_i2c_reset(i2c);
        } else {
-               INIT_COMPLETION(i2c->cmd_complete);
+               reinit_completion(&i2c->cmd_complete);
                ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
                if (ret)
                        return ret;
@@ -514,9 +600,11 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
                                                msecs_to_jiffies(1000));
                if (ret == 0)
                        goto timeout;
+
+               ret = i2c->cmd_err;
        }
 
-       if (i2c->cmd_err == -ENXIO) {
+       if (ret == -ENXIO) {
                /*
                 * If the transfer fails with a NAK from the slave the
                 * controller halts until it gets told to return to idle state.
@@ -525,7 +613,19 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
                       i2c->regs + MXS_I2C_CTRL1_SET);
        }
 
-       ret = i2c->cmd_err;
+       /*
+        * WARNING!
+        * The i.MX23 is strange. After each and every operation, it's I2C IP
+        * block must be reset, otherwise the IP block will misbehave. This can
+        * be observed on the bus by the block sending out one single byte onto
+        * the bus. In case such an error happens, bit 27 will be set in the
+        * DEBUG0 register. This bit is not documented in the i.MX23 datasheet
+        * and is marked as "TBD" instead. To reset this bit to a correct state,
+        * reset the whole block. Since the block reset does not take long, do
+        * reset the block after every transfer to play safe.
+        */
+       if (i2c->dev_type == MXS_I2C_V1)
+               mxs_i2c_reset(i2c);
 
        dev_dbg(i2c->dev, "Done with err=%d\n", ret);
 
@@ -680,8 +780,28 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
        return 0;
 }
 
+static struct platform_device_id mxs_i2c_devtype[] = {
+       {
+               .name = "imx23-i2c",
+               .driver_data = MXS_I2C_V1,
+       }, {
+               .name = "imx28-i2c",
+               .driver_data = MXS_I2C_V2,
+       }, { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, mxs_i2c_devtype);
+
+static const struct of_device_id mxs_i2c_dt_ids[] = {
+       { .compatible = "fsl,imx23-i2c", .data = &mxs_i2c_devtype[0], },
+       { .compatible = "fsl,imx28-i2c", .data = &mxs_i2c_devtype[1], },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_i2c_dt_ids);
+
 static int mxs_i2c_probe(struct platform_device *pdev)
 {
+       const struct of_device_id *of_id =
+                               of_match_device(mxs_i2c_dt_ids, &pdev->dev);
        struct device *dev = &pdev->dev;
        struct mxs_i2c_dev *i2c;
        struct i2c_adapter *adap;
@@ -693,6 +813,11 @@ static int mxs_i2c_probe(struct platform_device *pdev)
        if (!i2c)
                return -ENOMEM;
 
+       if (of_id) {
+               const struct platform_device_id *device_id = of_id->data;
+               i2c->dev_type = device_id->driver_data;
+       }
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        irq = platform_get_irq(pdev, 0);
 
@@ -768,12 +893,6 @@ static int mxs_i2c_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id mxs_i2c_dt_ids[] = {
-       { .compatible = "fsl,imx28-i2c", },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, mxs_i2c_dt_ids);
-
 static struct platform_driver mxs_i2c_driver = {
        .driver = {
                   .name = DRIVER_NAME,
@@ -796,6 +915,7 @@ static void __exit mxs_i2c_exit(void)
 }
 module_exit(mxs_i2c_exit);
 
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
 MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
 MODULE_DESCRIPTION("MXS I2C Bus Driver");
 MODULE_LICENSE("GPL");
index 9967a6f..a6a891d 100644 (file)
@@ -543,7 +543,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);
+       reinit_completion(&dev->cmd_complete);
        dev->cmd_err = 0;
 
        w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
index 1a9ea25..c9a352f 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #define I2C_PNX_TIMEOUT_DEFAULT                10 /* msec */
 #define I2C_PNX_SPEED_KHZ_DEFAULT      100
index d2fe11d..2c2fd7c 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/i2c/i2c-rcar.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
@@ -102,8 +103,8 @@ enum {
 #define ID_NACK                (1 << 4)
 
 enum rcar_i2c_type {
-       I2C_RCAR_H1,
-       I2C_RCAR_H2,
+       I2C_RCAR_GEN1,
+       I2C_RCAR_GEN2,
 };
 
 struct rcar_i2c_priv {
@@ -226,22 +227,23 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
                                    u32 bus_speed,
                                    struct device *dev)
 {
-       struct clk *clkp = clk_get(NULL, "peripheral_clk");
+       struct clk *clkp = clk_get(dev, NULL);
        u32 scgd, cdf;
        u32 round, ick;
        u32 scl;
        u32 cdf_width;
+       unsigned long rate;
 
-       if (!clkp) {
-               dev_err(dev, "there is no peripheral_clk\n");
-               return -EIO;
+       if (IS_ERR(clkp)) {
+               dev_err(dev, "couldn't get clock\n");
+               return PTR_ERR(clkp);
        }
 
        switch (priv->devtype) {
-       case I2C_RCAR_H1:
+       case I2C_RCAR_GEN1:
                cdf_width = 2;
                break;
-       case I2C_RCAR_H2:
+       case I2C_RCAR_GEN2:
                cdf_width = 3;
                break;
        default:
@@ -264,15 +266,14 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
         * clkp : peripheral_clk
         * F[]  : integer up-valuation
         */
-       for (cdf = 0; cdf < (1 << cdf_width); cdf++) {
-               ick = clk_get_rate(clkp) / (1 + cdf);
-               if (ick < 20000000)
-                       goto ick_find;
+       rate = clk_get_rate(clkp);
+       cdf = rate / 20000000;
+       if (cdf >= 1 << cdf_width) {
+               dev_err(dev, "Input clock %lu too high\n", rate);
+               return -EIO;
        }
-       dev_err(dev, "there is no best CDF\n");
-       return -EIO;
+       ick = rate / (cdf + 1);
 
-ick_find:
        /*
         * it is impossible to calculate large scale
         * number on u32. separate it
@@ -290,6 +291,12 @@ ick_find:
         *
         * Calculation result (= SCL) should be less than
         * bus_speed for hardware safety
+        *
+        * We could use something along the lines of
+        *      div = ick / (bus_speed + 1) + 1;
+        *      scgd = (div - 20 - round + 7) / 8;
+        *      scl = ick / (20 + (scgd * 8) + round);
+        * (not fully verified) but that would get pretty involved
         */
        for (scgd = 0; scgd < 0x40; scgd++) {
                scl = ick / (20 + (scgd * 8) + round);
@@ -306,7 +313,7 @@ scgd_find:
        /*
         * keep icccr value
         */
-       priv->icccr = (scgd << (cdf_width) | cdf);
+       priv->icccr = scgd << cdf_width | cdf;
 
        return 0;
 }
@@ -632,6 +639,15 @@ static const struct i2c_algorithm rcar_i2c_algo = {
        .functionality  = rcar_i2c_func,
 };
 
+static const struct of_device_id rcar_i2c_dt_ids[] = {
+       { .compatible = "renesas,i2c-rcar", .data = (void *)I2C_RCAR_GEN1 },
+       { .compatible = "renesas,i2c-r8a7778", .data = (void *)I2C_RCAR_GEN1 },
+       { .compatible = "renesas,i2c-r8a7779", .data = (void *)I2C_RCAR_GEN1 },
+       { .compatible = "renesas,i2c-r8a7790", .data = (void *)I2C_RCAR_GEN2 },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);
+
 static int rcar_i2c_probe(struct platform_device *pdev)
 {
        struct i2c_rcar_platform_data *pdata = dev_get_platdata(&pdev->dev);
@@ -649,10 +665,15 @@ static int rcar_i2c_probe(struct platform_device *pdev)
        }
 
        bus_speed = 100000; /* default 100 kHz */
-       if (pdata && pdata->bus_speed)
+       ret = of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed);
+       if (ret < 0 && pdata && pdata->bus_speed)
                bus_speed = pdata->bus_speed;
 
-       priv->devtype = platform_get_device_id(pdev)->driver_data;
+       if (pdev->dev.of_node)
+               priv->devtype = (long)of_match_device(rcar_i2c_dt_ids,
+                                                     dev)->data;
+       else
+               priv->devtype = platform_get_device_id(pdev)->driver_data;
 
        ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
        if (ret < 0)
@@ -673,6 +694,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
        adap->class             = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        adap->retries           = 3;
        adap->dev.parent        = dev;
+       adap->dev.of_node       = dev->of_node;
        i2c_set_adapdata(adap, priv);
        strlcpy(adap->name, pdev->name, sizeof(adap->name));
 
@@ -709,9 +731,9 @@ static int rcar_i2c_remove(struct platform_device *pdev)
 }
 
 static struct platform_device_id rcar_i2c_id_table[] = {
-       { "i2c-rcar",           I2C_RCAR_H1 },
-       { "i2c-rcar_h1",        I2C_RCAR_H1 },
-       { "i2c-rcar_h2",        I2C_RCAR_H2 },
+       { "i2c-rcar",           I2C_RCAR_GEN1 },
+       { "i2c-rcar_gen1",      I2C_RCAR_GEN1 },
+       { "i2c-rcar_gen2",      I2C_RCAR_GEN2 },
        {},
 };
 MODULE_DEVICE_TABLE(platform, rcar_i2c_id_table);
@@ -720,6 +742,7 @@ static struct platform_driver rcar_i2c_driver = {
        .driver = {
                .name   = "i2c-rcar",
                .owner  = THIS_MODULE,
+               .of_match_table = rcar_i2c_dt_ids,
        },
        .probe          = rcar_i2c_probe,
        .remove         = rcar_i2c_remove,
index 3747b9b..bf8fb94 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/cpufreq.h>
 #include <linux/slab.h>
 #include <linux/io.h>
+#include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/pinctrl/consumer.h>
 
index c447e8d..5992355 100644 (file)
@@ -223,7 +223,7 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
                goto out;
 
        obj = pkg->package.elements + 1;
-       if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
+       if (obj->type != ACPI_TYPE_INTEGER) {
                ACPI_ERROR((AE_INFO, "Invalid argument type"));
                result = -EIO;
                goto out;
@@ -235,7 +235,7 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
        case I2C_SMBUS_BYTE:
        case I2C_SMBUS_BYTE_DATA:
        case I2C_SMBUS_WORD_DATA:
-               if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
+               if (obj->type != ACPI_TYPE_INTEGER) {
                        ACPI_ERROR((AE_INFO, "Invalid argument type"));
                        result = -EIO;
                        goto out;
@@ -246,7 +246,7 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
                        data->byte = obj->integer.value;
                break;
        case I2C_SMBUS_BLOCK_DATA:
-               if (obj == NULL || obj->type != ACPI_TYPE_BUFFER) {
+               if (obj->type != ACPI_TYPE_BUFFER) {
                        ACPI_ERROR((AE_INFO, "Invalid argument type"));
                        result = -EIO;
                        goto out;
index 55110dd..1d79585 100644 (file)
@@ -235,7 +235,7 @@ static void sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)
        int offset;
 
        /* Get clock rate after clock is enabled */
-       clk_enable(pd->clk);
+       clk_prepare_enable(pd->clk);
        i2c_clk_khz = clk_get_rate(pd->clk) / 1000;
        i2c_clk_khz /= pd->clks_per_count;
 
@@ -270,14 +270,14 @@ static void sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)
                pd->icic &= ~ICIC_ICCHB8;
 
 out:
-       clk_disable(pd->clk);
+       clk_disable_unprepare(pd->clk);
 }
 
 static void activate_ch(struct sh_mobile_i2c_data *pd)
 {
        /* Wake up device and enable clock */
        pm_runtime_get_sync(pd->dev);
-       clk_enable(pd->clk);
+       clk_prepare_enable(pd->clk);
 
        /* Enable channel and configure rx ack */
        iic_set_clr(pd, ICCR, ICCR_ICE, 0);
@@ -300,7 +300,7 @@ static void deactivate_ch(struct sh_mobile_i2c_data *pd)
        iic_set_clr(pd, ICCR, 0, ICCR_ICE);
 
        /* Disable clock and mark device as idle */
-       clk_disable(pd->clk);
+       clk_disable_unprepare(pd->clk);
        pm_runtime_put_sync(pd->dev);
 }
 
diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c
new file mode 100644 (file)
index 0000000..9cf715d
--- /dev/null
@@ -0,0 +1,872 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics
+ *
+ * I2C master mode controller driver, used in STMicroelectronics devices.
+ *
+ * Author: Maxime Coquelin <maxime.coquelin@st.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/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+/* SSC registers */
+#define SSC_BRG                                0x000
+#define SSC_TBUF                       0x004
+#define SSC_RBUF                       0x008
+#define SSC_CTL                                0x00C
+#define SSC_IEN                                0x010
+#define SSC_STA                                0x014
+#define SSC_I2C                                0x018
+#define SSC_SLAD                       0x01C
+#define SSC_REP_START_HOLD             0x020
+#define SSC_START_HOLD                 0x024
+#define SSC_REP_START_SETUP            0x028
+#define SSC_DATA_SETUP                 0x02C
+#define SSC_STOP_SETUP                 0x030
+#define SSC_BUS_FREE                   0x034
+#define SSC_TX_FSTAT                   0x038
+#define SSC_RX_FSTAT                   0x03C
+#define SSC_PRE_SCALER_BRG             0x040
+#define SSC_CLR                                0x080
+#define SSC_NOISE_SUPP_WIDTH           0x100
+#define SSC_PRSCALER                   0x104
+#define SSC_NOISE_SUPP_WIDTH_DATAOUT   0x108
+#define SSC_PRSCALER_DATAOUT           0x10c
+
+/* SSC Control */
+#define SSC_CTL_DATA_WIDTH_9           0x8
+#define SSC_CTL_DATA_WIDTH_MSK         0xf
+#define SSC_CTL_BM                     0xf
+#define SSC_CTL_HB                     BIT(4)
+#define SSC_CTL_PH                     BIT(5)
+#define SSC_CTL_PO                     BIT(6)
+#define SSC_CTL_SR                     BIT(7)
+#define SSC_CTL_MS                     BIT(8)
+#define SSC_CTL_EN                     BIT(9)
+#define SSC_CTL_LPB                    BIT(10)
+#define SSC_CTL_EN_TX_FIFO             BIT(11)
+#define SSC_CTL_EN_RX_FIFO             BIT(12)
+#define SSC_CTL_EN_CLST_RX             BIT(13)
+
+/* SSC Interrupt Enable */
+#define SSC_IEN_RIEN                   BIT(0)
+#define SSC_IEN_TIEN                   BIT(1)
+#define SSC_IEN_TEEN                   BIT(2)
+#define SSC_IEN_REEN                   BIT(3)
+#define SSC_IEN_PEEN                   BIT(4)
+#define SSC_IEN_AASEN                  BIT(6)
+#define SSC_IEN_STOPEN                 BIT(7)
+#define SSC_IEN_ARBLEN                 BIT(8)
+#define SSC_IEN_NACKEN                 BIT(10)
+#define SSC_IEN_REPSTRTEN              BIT(11)
+#define SSC_IEN_TX_FIFO_HALF           BIT(12)
+#define SSC_IEN_RX_FIFO_HALF_FULL      BIT(14)
+
+/* SSC Status */
+#define SSC_STA_RIR                    BIT(0)
+#define SSC_STA_TIR                    BIT(1)
+#define SSC_STA_TE                     BIT(2)
+#define SSC_STA_RE                     BIT(3)
+#define SSC_STA_PE                     BIT(4)
+#define SSC_STA_CLST                   BIT(5)
+#define SSC_STA_AAS                    BIT(6)
+#define SSC_STA_STOP                   BIT(7)
+#define SSC_STA_ARBL                   BIT(8)
+#define SSC_STA_BUSY                   BIT(9)
+#define SSC_STA_NACK                   BIT(10)
+#define SSC_STA_REPSTRT                        BIT(11)
+#define SSC_STA_TX_FIFO_HALF           BIT(12)
+#define SSC_STA_TX_FIFO_FULL           BIT(13)
+#define SSC_STA_RX_FIFO_HALF           BIT(14)
+
+/* SSC I2C Control */
+#define SSC_I2C_I2CM                   BIT(0)
+#define SSC_I2C_STRTG                  BIT(1)
+#define SSC_I2C_STOPG                  BIT(2)
+#define SSC_I2C_ACKG                   BIT(3)
+#define SSC_I2C_AD10                   BIT(4)
+#define SSC_I2C_TXENB                  BIT(5)
+#define SSC_I2C_REPSTRTG               BIT(11)
+#define SSC_I2C_SLAVE_DISABLE          BIT(12)
+
+/* SSC Tx FIFO Status */
+#define SSC_TX_FSTAT_STATUS            0x07
+
+/* SSC Rx FIFO Status */
+#define SSC_RX_FSTAT_STATUS            0x07
+
+/* SSC Clear bit operation */
+#define SSC_CLR_SSCAAS                 BIT(6)
+#define SSC_CLR_SSCSTOP                        BIT(7)
+#define SSC_CLR_SSCARBL                        BIT(8)
+#define SSC_CLR_NACK                   BIT(10)
+#define SSC_CLR_REPSTRT                        BIT(11)
+
+/* SSC Clock Prescaler */
+#define SSC_PRSC_VALUE                 0x0f
+
+
+#define SSC_TXFIFO_SIZE                        0x8
+#define SSC_RXFIFO_SIZE                        0x8
+
+enum st_i2c_mode {
+       I2C_MODE_STANDARD,
+       I2C_MODE_FAST,
+       I2C_MODE_END,
+};
+
+/**
+ * struct st_i2c_timings - per-Mode tuning parameters
+ * @rate: I2C bus rate
+ * @rep_start_hold: I2C repeated start hold time requirement
+ * @rep_start_setup: I2C repeated start set up time requirement
+ * @start_hold: I2C start hold time requirement
+ * @data_setup_time: I2C data set up time requirement
+ * @stop_setup_time: I2C stop set up time requirement
+ * @bus_free_time: I2C bus free time requirement
+ * @sda_pulse_min_limit: I2C SDA pulse mini width limit
+ */
+struct st_i2c_timings {
+       u32 rate;
+       u32 rep_start_hold;
+       u32 rep_start_setup;
+       u32 start_hold;
+       u32 data_setup_time;
+       u32 stop_setup_time;
+       u32 bus_free_time;
+       u32 sda_pulse_min_limit;
+};
+
+/**
+ * struct st_i2c_client - client specific data
+ * @addr: 8-bit slave addr, including r/w bit
+ * @count: number of bytes to be transfered
+ * @xfered: number of bytes already transferred
+ * @buf: data buffer
+ * @result: result of the transfer
+ * @stop: last I2C msg to be sent, i.e. STOP to be generated
+ */
+struct st_i2c_client {
+       u8      addr;
+       u32     count;
+       u32     xfered;
+       u8      *buf;
+       int     result;
+       bool    stop;
+};
+
+/**
+ * struct st_i2c_dev - private data of the controller
+ * @adap: I2C adapter for this controller
+ * @dev: device for this controller
+ * @base: virtual memory area
+ * @complete: completion of I2C message
+ * @irq: interrupt line for th controller
+ * @clk: hw ssc block clock
+ * @mode: I2C mode of the controller. Standard or Fast only supported
+ * @scl_min_width_us: SCL line minimum pulse width in us
+ * @sda_min_width_us: SDA line minimum pulse width in us
+ * @client: I2C transfert information
+ * @busy: I2C transfer on-going
+ */
+struct st_i2c_dev {
+       struct i2c_adapter      adap;
+       struct device           *dev;
+       void __iomem            *base;
+       struct completion       complete;
+       int                     irq;
+       struct clk              *clk;
+       int                     mode;
+       u32                     scl_min_width_us;
+       u32                     sda_min_width_us;
+       struct st_i2c_client    client;
+       bool                    busy;
+};
+
+static inline void st_i2c_set_bits(void __iomem *reg, u32 mask)
+{
+       writel_relaxed(readl_relaxed(reg) | mask, reg);
+}
+
+static inline void st_i2c_clr_bits(void __iomem *reg, u32 mask)
+{
+       writel_relaxed(readl_relaxed(reg) & ~mask, reg);
+}
+
+/* From I2C Specifications v0.5 */
+static struct st_i2c_timings i2c_timings[] = {
+       [I2C_MODE_STANDARD] = {
+               .rate                   = 100000,
+               .rep_start_hold         = 4000,
+               .rep_start_setup        = 4700,
+               .start_hold             = 4000,
+               .data_setup_time        = 250,
+               .stop_setup_time        = 4000,
+               .bus_free_time          = 4700,
+       },
+       [I2C_MODE_FAST] = {
+               .rate                   = 400000,
+               .rep_start_hold         = 600,
+               .rep_start_setup        = 600,
+               .start_hold             = 600,
+               .data_setup_time        = 100,
+               .stop_setup_time        = 600,
+               .bus_free_time          = 1300,
+       },
+};
+
+static void st_i2c_flush_rx_fifo(struct st_i2c_dev *i2c_dev)
+{
+       int count, i;
+
+       /*
+        * Counter only counts up to 7 but fifo size is 8...
+        * When fifo is full, counter is 0 and RIR bit of status register is
+        * set
+        */
+       if (readl_relaxed(i2c_dev->base + SSC_STA) & SSC_STA_RIR)
+               count = SSC_RXFIFO_SIZE;
+       else
+               count = readl_relaxed(i2c_dev->base + SSC_RX_FSTAT) &
+                       SSC_RX_FSTAT_STATUS;
+
+       for (i = 0; i < count; i++)
+               readl_relaxed(i2c_dev->base + SSC_RBUF);
+}
+
+static void st_i2c_soft_reset(struct st_i2c_dev *i2c_dev)
+{
+       /*
+        * FIFO needs to be emptied before reseting the IP,
+        * else the controller raises a BUSY error.
+        */
+       st_i2c_flush_rx_fifo(i2c_dev);
+
+       st_i2c_set_bits(i2c_dev->base + SSC_CTL, SSC_CTL_SR);
+       st_i2c_clr_bits(i2c_dev->base + SSC_CTL, SSC_CTL_SR);
+}
+
+/**
+ * st_i2c_hw_config() - Prepare SSC block, calculate and apply tuning timings
+ * @i2c_dev: Controller's private data
+ */
+static void st_i2c_hw_config(struct st_i2c_dev *i2c_dev)
+{
+       unsigned long rate;
+       u32 val, ns_per_clk;
+       struct st_i2c_timings *t = &i2c_timings[i2c_dev->mode];
+
+       st_i2c_soft_reset(i2c_dev);
+
+       val = SSC_CLR_REPSTRT | SSC_CLR_NACK | SSC_CLR_SSCARBL |
+               SSC_CLR_SSCAAS | SSC_CLR_SSCSTOP;
+       writel_relaxed(val, i2c_dev->base + SSC_CLR);
+
+       /* SSC Control register setup */
+       val = SSC_CTL_PO | SSC_CTL_PH | SSC_CTL_HB | SSC_CTL_DATA_WIDTH_9;
+       writel_relaxed(val, i2c_dev->base + SSC_CTL);
+
+       rate = clk_get_rate(i2c_dev->clk);
+       ns_per_clk = 1000000000 / rate;
+
+       /* Baudrate */
+       val = rate / (2 * t->rate);
+       writel_relaxed(val, i2c_dev->base + SSC_BRG);
+
+       /* Pre-scaler baudrate */
+       writel_relaxed(1, i2c_dev->base + SSC_PRE_SCALER_BRG);
+
+       /* Enable I2C mode */
+       writel_relaxed(SSC_I2C_I2CM, i2c_dev->base + SSC_I2C);
+
+       /* Repeated start hold time */
+       val = t->rep_start_hold / ns_per_clk;
+       writel_relaxed(val, i2c_dev->base + SSC_REP_START_HOLD);
+
+       /* Repeated start set up time */
+       val = t->rep_start_setup / ns_per_clk;
+       writel_relaxed(val, i2c_dev->base + SSC_REP_START_SETUP);
+
+       /* Start hold time */
+       val = t->start_hold / ns_per_clk;
+       writel_relaxed(val, i2c_dev->base + SSC_START_HOLD);
+
+       /* Data set up time */
+       val = t->data_setup_time / ns_per_clk;
+       writel_relaxed(val, i2c_dev->base + SSC_DATA_SETUP);
+
+       /* Stop set up time */
+       val = t->stop_setup_time / ns_per_clk;
+       writel_relaxed(val, i2c_dev->base + SSC_STOP_SETUP);
+
+       /* Bus free time */
+       val = t->bus_free_time / ns_per_clk;
+       writel_relaxed(val, i2c_dev->base + SSC_BUS_FREE);
+
+       /* Prescalers set up */
+       val = rate / 10000000;
+       writel_relaxed(val, i2c_dev->base + SSC_PRSCALER);
+       writel_relaxed(val, i2c_dev->base + SSC_PRSCALER_DATAOUT);
+
+       /* Noise suppression witdh */
+       val = i2c_dev->scl_min_width_us * rate / 100000000;
+       writel_relaxed(val, i2c_dev->base + SSC_NOISE_SUPP_WIDTH);
+
+       /* Noise suppression max output data delay width */
+       val = i2c_dev->sda_min_width_us * rate / 100000000;
+       writel_relaxed(val, i2c_dev->base + SSC_NOISE_SUPP_WIDTH_DATAOUT);
+}
+
+static int st_i2c_wait_free_bus(struct st_i2c_dev *i2c_dev)
+{
+       u32 sta;
+       int i;
+
+       for (i = 0; i < 10; i++) {
+               sta = readl_relaxed(i2c_dev->base + SSC_STA);
+               if (!(sta & SSC_STA_BUSY))
+                       return 0;
+
+               usleep_range(2000, 4000);
+       }
+
+       dev_err(i2c_dev->dev, "bus not free (status = 0x%08x)\n", sta);
+
+       return -EBUSY;
+}
+
+/**
+ * st_i2c_write_tx_fifo() - Write a byte in the Tx FIFO
+ * @i2c_dev: Controller's private data
+ * @byte: Data to write in the Tx FIFO
+ */
+static inline void st_i2c_write_tx_fifo(struct st_i2c_dev *i2c_dev, u8 byte)
+{
+       u16 tbuf = byte << 1;
+
+       writel_relaxed(tbuf | 1, i2c_dev->base + SSC_TBUF);
+}
+
+/**
+ * st_i2c_wr_fill_tx_fifo() - Fill the Tx FIFO in write mode
+ * @i2c_dev: Controller's private data
+ *
+ * This functions fills the Tx FIFO with I2C transfert buffer when
+ * in write mode.
+ */
+static void st_i2c_wr_fill_tx_fifo(struct st_i2c_dev *i2c_dev)
+{
+       struct st_i2c_client *c = &i2c_dev->client;
+       u32 tx_fstat, sta;
+       int i;
+
+       sta = readl_relaxed(i2c_dev->base + SSC_STA);
+       if (sta & SSC_STA_TX_FIFO_FULL)
+               return;
+
+       tx_fstat = readl_relaxed(i2c_dev->base + SSC_TX_FSTAT);
+       tx_fstat &= SSC_TX_FSTAT_STATUS;
+
+       if (c->count < (SSC_TXFIFO_SIZE - tx_fstat))
+               i = c->count;
+       else
+               i = SSC_TXFIFO_SIZE - tx_fstat;
+
+       for (; i > 0; i--, c->count--, c->buf++)
+               st_i2c_write_tx_fifo(i2c_dev, *c->buf);
+}
+
+/**
+ * st_i2c_rd_fill_tx_fifo() - Fill the Tx FIFO in read mode
+ * @i2c_dev: Controller's private data
+ *
+ * This functions fills the Tx FIFO with fixed pattern when
+ * in read mode to trigger clock.
+ */
+static void st_i2c_rd_fill_tx_fifo(struct st_i2c_dev *i2c_dev, int max)
+{
+       struct st_i2c_client *c = &i2c_dev->client;
+       u32 tx_fstat, sta;
+       int i;
+
+       sta = readl_relaxed(i2c_dev->base + SSC_STA);
+       if (sta & SSC_STA_TX_FIFO_FULL)
+               return;
+
+       tx_fstat = readl_relaxed(i2c_dev->base + SSC_TX_FSTAT);
+       tx_fstat &= SSC_TX_FSTAT_STATUS;
+
+       if (max < (SSC_TXFIFO_SIZE - tx_fstat))
+               i = max;
+       else
+               i = SSC_TXFIFO_SIZE - tx_fstat;
+
+       for (; i > 0; i--, c->xfered++)
+               st_i2c_write_tx_fifo(i2c_dev, 0xff);
+}
+
+static void st_i2c_read_rx_fifo(struct st_i2c_dev *i2c_dev)
+{
+       struct st_i2c_client *c = &i2c_dev->client;
+       u32 i, sta;
+       u16 rbuf;
+
+       sta = readl_relaxed(i2c_dev->base + SSC_STA);
+       if (sta & SSC_STA_RIR) {
+               i = SSC_RXFIFO_SIZE;
+       } else {
+               i = readl_relaxed(i2c_dev->base + SSC_RX_FSTAT);
+               i &= SSC_RX_FSTAT_STATUS;
+       }
+
+       for (; (i > 0) && (c->count > 0); i--, c->count--) {
+               rbuf = readl_relaxed(i2c_dev->base + SSC_RBUF) >> 1;
+               *c->buf++ = (u8)rbuf & 0xff;
+       }
+
+       if (i) {
+               dev_err(i2c_dev->dev, "Unexpected %d bytes in rx fifo\n", i);
+               st_i2c_flush_rx_fifo(i2c_dev);
+       }
+}
+
+/**
+ * st_i2c_terminate_xfer() - Send either STOP or REPSTART condition
+ * @i2c_dev: Controller's private data
+ */
+static void st_i2c_terminate_xfer(struct st_i2c_dev *i2c_dev)
+{
+       struct st_i2c_client *c = &i2c_dev->client;
+
+       st_i2c_clr_bits(i2c_dev->base + SSC_IEN, SSC_IEN_TEEN);
+       st_i2c_clr_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STRTG);
+
+       if (c->stop) {
+               st_i2c_set_bits(i2c_dev->base + SSC_IEN, SSC_IEN_STOPEN);
+               st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STOPG);
+       } else {
+               st_i2c_set_bits(i2c_dev->base + SSC_IEN, SSC_IEN_REPSTRTEN);
+               st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_REPSTRTG);
+       }
+}
+
+/**
+ * st_i2c_handle_write() - Handle FIFO empty interrupt in case of write
+ * @i2c_dev: Controller's private data
+ */
+static void st_i2c_handle_write(struct st_i2c_dev *i2c_dev)
+{
+       struct st_i2c_client *c = &i2c_dev->client;
+
+       st_i2c_flush_rx_fifo(i2c_dev);
+
+       if (!c->count)
+               /* End of xfer, send stop or repstart */
+               st_i2c_terminate_xfer(i2c_dev);
+       else
+               st_i2c_wr_fill_tx_fifo(i2c_dev);
+}
+
+/**
+ * st_i2c_handle_write() - Handle FIFO enmpty interrupt in case of read
+ * @i2c_dev: Controller's private data
+ */
+static void st_i2c_handle_read(struct st_i2c_dev *i2c_dev)
+{
+       struct st_i2c_client *c = &i2c_dev->client;
+       u32 ien;
+
+       /* Trash the address read back */
+       if (!c->xfered) {
+               readl_relaxed(i2c_dev->base + SSC_RBUF);
+               st_i2c_clr_bits(i2c_dev->base + SSC_I2C, SSC_I2C_TXENB);
+       } else {
+               st_i2c_read_rx_fifo(i2c_dev);
+       }
+
+       if (!c->count) {
+               /* End of xfer, send stop or repstart */
+               st_i2c_terminate_xfer(i2c_dev);
+       } else if (c->count == 1) {
+               /* Penultimate byte to xfer, disable ACK gen. */
+               st_i2c_clr_bits(i2c_dev->base + SSC_I2C, SSC_I2C_ACKG);
+
+               /* Last received byte is to be handled by NACK interrupt */
+               ien = SSC_IEN_NACKEN | SSC_IEN_ARBLEN;
+               writel_relaxed(ien, i2c_dev->base + SSC_IEN);
+
+               st_i2c_rd_fill_tx_fifo(i2c_dev, c->count);
+       } else {
+               st_i2c_rd_fill_tx_fifo(i2c_dev, c->count - 1);
+       }
+}
+
+/**
+ * st_i2c_isr() - Interrupt routine
+ * @irq: interrupt number
+ * @data: Controller's private data
+ */
+static irqreturn_t st_i2c_isr_thread(int irq, void *data)
+{
+       struct st_i2c_dev *i2c_dev = data;
+       struct st_i2c_client *c = &i2c_dev->client;
+       u32 sta, ien;
+       int it;
+
+       ien = readl_relaxed(i2c_dev->base + SSC_IEN);
+       sta = readl_relaxed(i2c_dev->base + SSC_STA);
+
+       /* Use __fls() to check error bits first */
+       it = __fls(sta & ien);
+       if (it < 0) {
+               dev_dbg(i2c_dev->dev, "spurious it (sta=0x%04x, ien=0x%04x)\n",
+                               sta, ien);
+               return IRQ_NONE;
+       }
+
+       switch (1 << it) {
+       case SSC_STA_TE:
+               if (c->addr & I2C_M_RD)
+                       st_i2c_handle_read(i2c_dev);
+               else
+                       st_i2c_handle_write(i2c_dev);
+               break;
+
+       case SSC_STA_STOP:
+       case SSC_STA_REPSTRT:
+               writel_relaxed(0, i2c_dev->base + SSC_IEN);
+               complete(&i2c_dev->complete);
+               break;
+
+       case SSC_STA_NACK:
+               writel_relaxed(SSC_CLR_NACK, i2c_dev->base + SSC_CLR);
+
+               /* Last received byte handled by NACK interrupt */
+               if ((c->addr & I2C_M_RD) && (c->count == 1) && (c->xfered)) {
+                       st_i2c_handle_read(i2c_dev);
+                       break;
+               }
+
+               it = SSC_IEN_STOPEN | SSC_IEN_ARBLEN;
+               writel_relaxed(it, i2c_dev->base + SSC_IEN);
+
+               st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STOPG);
+               c->result = -EIO;
+               break;
+
+       case SSC_STA_ARBL:
+               writel_relaxed(SSC_CLR_SSCARBL, i2c_dev->base + SSC_CLR);
+
+               it = SSC_IEN_STOPEN | SSC_IEN_ARBLEN;
+               writel_relaxed(it, i2c_dev->base + SSC_IEN);
+
+               st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STOPG);
+               c->result = -EIO;
+               break;
+
+       default:
+               dev_err(i2c_dev->dev,
+                               "it %d unhandled (sta=0x%04x)\n", it, sta);
+       }
+
+       /*
+        * Read IEN register to ensure interrupt mask write is effective
+        * before re-enabling interrupt at GIC level, and thus avoid spurious
+        * interrupts.
+        */
+       readl(i2c_dev->base + SSC_IEN);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * st_i2c_xfer_msg() - Transfer a single I2C message
+ * @i2c_dev: Controller's private data
+ * @msg: I2C message to transfer
+ * @is_first: first message of the sequence
+ * @is_last: last message of the sequence
+ */
+static int st_i2c_xfer_msg(struct st_i2c_dev *i2c_dev, struct i2c_msg *msg,
+                           bool is_first, bool is_last)
+{
+       struct st_i2c_client *c = &i2c_dev->client;
+       u32 ctl, i2c, it;
+       unsigned long timeout;
+       int ret;
+
+       c->addr         = (u8)(msg->addr << 1);
+       c->addr         |= (msg->flags & I2C_M_RD);
+       c->buf          = msg->buf;
+       c->count        = msg->len;
+       c->xfered       = 0;
+       c->result       = 0;
+       c->stop         = is_last;
+
+       reinit_completion(&i2c_dev->complete);
+
+       ctl = SSC_CTL_EN | SSC_CTL_MS | SSC_CTL_EN_RX_FIFO | SSC_CTL_EN_TX_FIFO;
+       st_i2c_set_bits(i2c_dev->base + SSC_CTL, ctl);
+
+       i2c = SSC_I2C_TXENB;
+       if (c->addr & I2C_M_RD)
+               i2c |= SSC_I2C_ACKG;
+       st_i2c_set_bits(i2c_dev->base + SSC_I2C, i2c);
+
+       /* Write slave address */
+       st_i2c_write_tx_fifo(i2c_dev, c->addr);
+
+       /* Pre-fill Tx fifo with data in case of write */
+       if (!(c->addr & I2C_M_RD))
+               st_i2c_wr_fill_tx_fifo(i2c_dev);
+
+       it = SSC_IEN_NACKEN | SSC_IEN_TEEN | SSC_IEN_ARBLEN;
+       writel_relaxed(it, i2c_dev->base + SSC_IEN);
+
+       if (is_first) {
+               ret = st_i2c_wait_free_bus(i2c_dev);
+               if (ret)
+                       return ret;
+
+               st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STRTG);
+       }
+
+       timeout = wait_for_completion_timeout(&i2c_dev->complete,
+                       i2c_dev->adap.timeout);
+       ret = c->result;
+
+       if (!timeout) {
+               dev_err(i2c_dev->dev, "Write to slave 0x%x timed out\n",
+                               c->addr);
+               ret = -ETIMEDOUT;
+       }
+
+       i2c = SSC_I2C_STOPG | SSC_I2C_REPSTRTG;
+       st_i2c_clr_bits(i2c_dev->base + SSC_I2C, i2c);
+
+       writel_relaxed(SSC_CLR_SSCSTOP | SSC_CLR_REPSTRT,
+                       i2c_dev->base + SSC_CLR);
+
+       return ret;
+}
+
+/**
+ * st_i2c_xfer() - Transfer a single I2C message
+ * @i2c_adap: Adapter pointer to the controller
+ * @msgs: Pointer to data to be written.
+ * @num: Number of messages to be executed
+ */
+static int st_i2c_xfer(struct i2c_adapter *i2c_adap,
+                       struct i2c_msg msgs[], int num)
+{
+       struct st_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
+       int ret, i;
+
+       i2c_dev->busy = true;
+
+       ret = clk_prepare_enable(i2c_dev->clk);
+       if (ret) {
+               dev_err(i2c_dev->dev, "Failed to prepare_enable clock\n");
+               return ret;
+       }
+
+       pinctrl_pm_select_default_state(i2c_dev->dev);
+
+       st_i2c_hw_config(i2c_dev);
+
+       for (i = 0; (i < num) && !ret; i++)
+               ret = st_i2c_xfer_msg(i2c_dev, &msgs[i], i == 0, i == num - 1);
+
+       pinctrl_pm_select_idle_state(i2c_dev->dev);
+
+       clk_disable_unprepare(i2c_dev->clk);
+
+       i2c_dev->busy = false;
+
+       return (ret < 0) ? ret : i;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int st_i2c_suspend(struct device *dev)
+{
+       struct platform_device *pdev =
+               container_of(dev, struct platform_device, dev);
+       struct st_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+       if (i2c_dev->busy)
+               return -EBUSY;
+
+       pinctrl_pm_select_sleep_state(dev);
+
+       return 0;
+}
+
+static int st_i2c_resume(struct device *dev)
+{
+       pinctrl_pm_select_default_state(dev);
+       /* Go in idle state if available */
+       pinctrl_pm_select_idle_state(dev);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(st_i2c_pm, st_i2c_suspend, st_i2c_resume);
+#define ST_I2C_PM      (&st_i2c_pm)
+#else
+#define ST_I2C_PM      NULL
+#endif
+
+static u32 st_i2c_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm st_i2c_algo = {
+       .master_xfer = st_i2c_xfer,
+       .functionality = st_i2c_func,
+};
+
+static int st_i2c_of_get_deglitch(struct device_node *np,
+               struct st_i2c_dev *i2c_dev)
+{
+       int ret;
+
+       ret = of_property_read_u32(np, "st,i2c-min-scl-pulse-width-us",
+                       &i2c_dev->scl_min_width_us);
+       if ((ret == -ENODATA) || (ret == -EOVERFLOW)) {
+               dev_err(i2c_dev->dev, "st,i2c-min-scl-pulse-width-us invalid\n");
+               return ret;
+       }
+
+       ret = of_property_read_u32(np, "st,i2c-min-sda-pulse-width-us",
+                       &i2c_dev->sda_min_width_us);
+       if ((ret == -ENODATA) || (ret == -EOVERFLOW)) {
+               dev_err(i2c_dev->dev, "st,i2c-min-sda-pulse-width-us invalid\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int st_i2c_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct st_i2c_dev *i2c_dev;
+       struct resource *res;
+       u32 clk_rate;
+       struct i2c_adapter *adap;
+       int ret;
+
+       i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+       if (!i2c_dev)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(i2c_dev->base))
+               return PTR_ERR(i2c_dev->base);
+
+       i2c_dev->irq = irq_of_parse_and_map(np, 0);
+       if (!i2c_dev->irq) {
+               dev_err(&pdev->dev, "IRQ missing or invalid\n");
+               return -EINVAL;
+       }
+
+       i2c_dev->clk = of_clk_get_by_name(np, "ssc");
+       if (IS_ERR(i2c_dev->clk)) {
+               dev_err(&pdev->dev, "Unable to request clock\n");
+               return PTR_ERR(i2c_dev->clk);
+       }
+
+       i2c_dev->mode = I2C_MODE_STANDARD;
+       ret = of_property_read_u32(np, "clock-frequency", &clk_rate);
+       if ((!ret) && (clk_rate == 400000))
+               i2c_dev->mode = I2C_MODE_FAST;
+
+       i2c_dev->dev = &pdev->dev;
+
+       ret = devm_request_threaded_irq(&pdev->dev, i2c_dev->irq,
+                       NULL, st_i2c_isr_thread,
+                       IRQF_ONESHOT, pdev->name, i2c_dev);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
+               return ret;
+       }
+
+       pinctrl_pm_select_default_state(i2c_dev->dev);
+       /* In case idle state available, select it */
+       pinctrl_pm_select_idle_state(i2c_dev->dev);
+
+       ret = st_i2c_of_get_deglitch(np, i2c_dev);
+       if (ret)
+               return ret;
+
+       adap = &i2c_dev->adap;
+       i2c_set_adapdata(adap, i2c_dev);
+       snprintf(adap->name, sizeof(adap->name), "ST I2C(0x%x)", res->start);
+       adap->owner = THIS_MODULE;
+       adap->timeout = 2 * HZ;
+       adap->retries = 0;
+       adap->algo = &st_i2c_algo;
+       adap->dev.parent = &pdev->dev;
+       adap->dev.of_node = pdev->dev.of_node;
+
+       init_completion(&i2c_dev->complete);
+
+       ret = i2c_add_adapter(adap);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to add adapter\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, i2c_dev);
+
+       dev_info(i2c_dev->dev, "%s initialized\n", adap->name);
+
+       return 0;
+}
+
+static int st_i2c_remove(struct platform_device *pdev)
+{
+       struct st_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+       i2c_del_adapter(&i2c_dev->adap);
+
+       return 0;
+}
+
+static struct of_device_id st_i2c_match[] = {
+       { .compatible = "st,comms-ssc-i2c", },
+       { .compatible = "st,comms-ssc4-i2c", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, st_i2c_match);
+
+static struct platform_driver st_i2c_driver = {
+       .driver = {
+               .name = "st-i2c",
+               .owner = THIS_MODULE,
+               .of_match_table = st_i2c_match,
+               .pm = ST_I2C_PM,
+       },
+       .probe = st_i2c_probe,
+       .remove = st_i2c_remove,
+};
+
+module_platform_driver(st_i2c_driver);
+
+MODULE_AUTHOR("Maxime Coquelin <maxime.coquelin@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics I2C driver");
+MODULE_LICENSE("GPL v2");
index c457cb4..e661ede 100644 (file)
@@ -544,7 +544,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
        i2c_dev->msg_buf_remaining = msg->len;
        i2c_dev->msg_err = I2C_ERR_NONE;
        i2c_dev->msg_read = (msg->flags & I2C_M_RD);
-       INIT_COMPLETION(i2c_dev->msg_complete);
+       reinit_completion(&i2c_dev->msg_complete);
 
        packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
                        PACKET_HEADER0_PROTOCOL_I2C |
index c65da3d..2c8a3e4 100644 (file)
@@ -158,7 +158,7 @@ static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
                writew(val, i2c_dev->base + REG_CR);
        }
 
-       INIT_COMPLETION(i2c_dev->complete);
+       reinit_completion(&i2c_dev->complete);
 
        if (i2c_dev->mode == I2C_MODE_STANDARD)
                tcr_val = TCR_STANDARD_MODE;
@@ -247,7 +247,7 @@ static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
                writew(val, i2c_dev->base + REG_CR);
        }
 
-       INIT_COMPLETION(i2c_dev->complete);
+       reinit_completion(&i2c_dev->complete);
 
        if (i2c_dev->mode == I2C_MODE_STANDARD)
                tcr_val = TCR_STANDARD_MODE;
@@ -349,6 +349,7 @@ static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
        err = clk_set_rate(i2c_dev->clk, 20000000);
        if (err) {
                dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n");
+               clk_disable_unprepare(i2c_dev->clk);
                return err;
        }
 
index 4c8b368..fc2716a 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/i2c-xiic.h>
 #include <linux/io.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #define DRIVER_NAME "xiic-i2c"
 
@@ -702,7 +703,7 @@ static int xiic_i2c_probe(struct platform_device *pdev)
        if (irq < 0)
                goto resource_missing;
 
-       pdata = (struct xiic_i2c_platform_data *)dev_get_platdata(&pdev->dev);
+       pdata = dev_get_platdata(&pdev->dev);
 
        i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
        if (!i2c)
index 75ba860..d74c0b3 100644 (file)
@@ -248,7 +248,7 @@ static int i2c_device_probe(struct device *dev)
        driver = to_i2c_driver(dev->driver);
        if (!driver->probe || !driver->id_table)
                return -ENODEV;
-       client->driver = driver;
+
        if (!device_can_wakeup(&client->dev))
                device_init_wakeup(&client->dev,
                                        client->flags & I2C_CLIENT_WAKE);
@@ -257,7 +257,6 @@ static int i2c_device_probe(struct device *dev)
        acpi_dev_pm_attach(&client->dev, true);
        status = driver->probe(client, i2c_match_id(driver->id_table, client));
        if (status) {
-               client->driver = NULL;
                i2c_set_clientdata(client, NULL);
                acpi_dev_pm_detach(&client->dev, true);
        }
@@ -281,10 +280,8 @@ static int i2c_device_remove(struct device *dev)
                dev->driver = NULL;
                status = 0;
        }
-       if (status == 0) {
-               client->driver = NULL;
+       if (status == 0)
                i2c_set_clientdata(client, NULL);
-       }
        acpi_dev_pm_detach(&client->dev, true);
        return status;
 }
@@ -618,6 +615,22 @@ void i2c_unlock_adapter(struct i2c_adapter *adapter)
 }
 EXPORT_SYMBOL_GPL(i2c_unlock_adapter);
 
+static void i2c_dev_set_name(struct i2c_adapter *adap,
+                            struct i2c_client *client)
+{
+       struct acpi_device *adev = ACPI_COMPANION(&client->dev);
+
+       if (adev) {
+               dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev));
+               return;
+       }
+
+       /* For 10-bit clients, add an arbitrary offset to avoid collisions */
+       dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
+                    client->addr | ((client->flags & I2C_CLIENT_TEN)
+                                    ? 0xa000 : 0));
+}
+
 /**
  * i2c_new_device - instantiate an i2c device
  * @adap: the adapter managing the device
@@ -674,12 +687,9 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
        client->dev.bus = &i2c_bus_type;
        client->dev.type = &i2c_client_type;
        client->dev.of_node = info->of_node;
-       ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);
+       ACPI_COMPANION_SET(&client->dev, info->acpi_node.companion);
 
-       /* For 10-bit clients, add an arbitrary offset to avoid collisions */
-       dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
-                    client->addr | ((client->flags & I2C_CLIENT_TEN)
-                                    ? 0xa000 : 0));
+       i2c_dev_set_name(adap, client);
        status = device_register(&client->dev);
        if (status)
                goto out_err;
@@ -1103,7 +1113,7 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
                return AE_OK;
 
        memset(&info, 0, sizeof(info));
-       info.acpi_node.handle = handle;
+       info.acpi_node.companion = adev;
        info.irq = -1;
 
        INIT_LIST_HEAD(&resource_list);
@@ -1614,9 +1624,14 @@ static int i2c_cmd(struct device *dev, void *_arg)
 {
        struct i2c_client       *client = i2c_verify_client(dev);
        struct i2c_cmd_arg      *arg = _arg;
+       struct i2c_driver       *driver;
+
+       if (!client || !client->dev.driver)
+               return 0;
 
-       if (client && client->driver && client->driver->command)
-               client->driver->command(client, arg->cmd, arg->arg);
+       driver = to_i2c_driver(client->dev.driver);
+       if (driver->command)
+               driver->command(client, arg->cmd, arg->arg);
        return 0;
 }
 
index c3ccdea..80b47e8 100644 (file)
@@ -102,8 +102,8 @@ static void return_i2c_dev(struct i2c_dev *i2c_dev)
        kfree(i2c_dev);
 }
 
-static ssize_t show_adapter_name(struct device *dev,
-                                struct device_attribute *attr, char *buf)
+static ssize_t name_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
 {
        struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt));
 
@@ -111,7 +111,13 @@ static ssize_t show_adapter_name(struct device *dev,
                return -ENODEV;
        return sprintf(buf, "%s\n", i2c_dev->adap->name);
 }
-static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL);
+static DEVICE_ATTR_RO(name);
+
+static struct attribute *i2c_attrs[] = {
+       &dev_attr_name.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(i2c);
 
 /* ------------------------------------------------------------------------- */
 
@@ -562,15 +568,10 @@ static int i2cdev_attach_adapter(struct device *dev, void *dummy)
                res = PTR_ERR(i2c_dev->dev);
                goto error;
        }
-       res = device_create_file(i2c_dev->dev, &dev_attr_name);
-       if (res)
-               goto error_destroy;
 
        pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
                 adap->name, adap->nr);
        return 0;
-error_destroy:
-       device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
 error:
        return_i2c_dev(i2c_dev);
        return res;
@@ -589,7 +590,6 @@ static int i2cdev_detach_adapter(struct device *dev, void *dummy)
        if (!i2c_dev) /* attach_adapter must have failed */
                return 0;
 
-       device_remove_file(i2c_dev->dev, &dev_attr_name);
        return_i2c_dev(i2c_dev);
        device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
 
@@ -637,6 +637,7 @@ static int __init i2c_dev_init(void)
                res = PTR_ERR(i2c_dev_class);
                goto out_unreg_chrdev;
        }
+       i2c_dev_class->dev_groups = i2c_groups;
 
        /* Keep track of adapters which will be added or removed later */
        res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
index 44d4c60..c99b229 100644 (file)
@@ -46,6 +46,7 @@ static int smbus_do_alert(struct device *dev, void *addrp)
 {
        struct i2c_client *client = i2c_verify_client(dev);
        struct alert_data *data = addrp;
+       struct i2c_driver *driver;
 
        if (!client || client->addr != data->addr)
                return 0;
@@ -54,12 +55,13 @@ static int smbus_do_alert(struct device *dev, void *addrp)
 
        /*
         * Drivers should either disable alerts, or provide at least
-        * a minimal handler.  Lock so client->driver won't change.
+        * a minimal handler.  Lock so the driver won't change.
         */
        device_lock(dev);
-       if (client->driver) {
-               if (client->driver->alert)
-                       client->driver->alert(client, data->flag);
+       if (client->dev.driver) {
+               driver = to_i2c_driver(client->dev.driver);
+               if (driver->alert)
+                       driver->alert(client, data->flag);
                else
                        dev_warn(&client->dev, "no driver alert()!\n");
        } else
index 928656e..c58e093 100644 (file)
@@ -238,7 +238,7 @@ static struct platform_driver i2c_arbitrator_driver = {
        .driver = {
                .owner  = THIS_MODULE,
                .name   = "i2c-arb-gpio-challenge",
-               .of_match_table = of_match_ptr(i2c_arbitrator_of_match),
+               .of_match_table = i2c_arbitrator_of_match,
        },
 };
 
index a764da7..8a8c56f 100644 (file)
@@ -30,15 +30,15 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
        int i;
 
        for (i = 0; i < mux->data.n_gpios; i++)
-               gpio_set_value(mux->gpio_base + mux->data.gpios[i],
-                              val & (1 << i));
+               gpio_set_value_cansleep(mux->gpio_base + mux->data.gpios[i],
+                                       val & (1 << i));
 }
 
 static int i2c_mux_gpio_select(struct i2c_adapter *adap, void *data, u32 chan)
 {
        struct gpiomux *mux = data;
 
-       i2c_mux_gpio_set(mux, mux->data.values[chan]);
+       i2c_mux_gpio_set(mux, chan);
 
        return 0;
 }
@@ -228,7 +228,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
                unsigned int class = mux->data.classes ? mux->data.classes[i] : 0;
 
                mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr,
-                                                  i, class,
+                                                  mux->data.values[i], class,
                                                   i2c_mux_gpio_select, deselect);
                if (!mux->adap[i]) {
                        ret = -ENODEV;
@@ -283,7 +283,7 @@ static struct platform_driver i2c_mux_gpio_driver = {
        .driver = {
                .owner  = THIS_MODULE,
                .name   = "i2c-mux-gpio",
-               .of_match_table = of_match_ptr(i2c_mux_gpio_of_match),
+               .of_match_table = i2c_mux_gpio_of_match,
        },
 };
 
index 68a3715..d7978dc 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/i2c-mux-pinctrl.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 struct i2c_mux_pinctrl {
        struct device *dev;
index 24214ab..de9185d 100644 (file)
@@ -295,15 +295,7 @@ static struct pci_driver cs5536_pci_driver = {
        .resume         = ide_pci_resume,
 };
 
-static int __init cs5536_init(void)
-{
-       return pci_register_driver(&cs5536_pci_driver);
-}
-
-static void __exit cs5536_exit(void)
-{
-       pci_unregister_driver(&cs5536_pci_driver);
-}
+module_pci_driver(cs5536_pci_driver);
 
 MODULE_AUTHOR("Martin K. Petersen, Bartlomiej Zolnierkiewicz");
 MODULE_DESCRIPTION("low-level driver for the CS5536 IDE controller");
@@ -312,6 +304,3 @@ MODULE_DEVICE_TABLE(pci, cs5536_pci_tbl);
 
 module_param_named(msr, use_msr, int, 0644);
 MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)");
-
-module_init(cs5536_init);
-module_exit(cs5536_exit);
index 140c8ef..d9e1f7c 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 2006 Hannes Reinecke
  */
 
+#include <linux/acpi.h>
 #include <linux/ata.h>
 #include <linux/delay.h>
 #include <linux/device.h>
@@ -19,8 +20,6 @@
 #include <linux/dmi.h>
 #include <linux/module.h>
 
-#include <acpi/acpi_bus.h>
-
 #define REGS_PER_GTF           7
 
 struct GTM_buffer {
@@ -128,7 +127,7 @@ static int ide_get_dev_handle(struct device *dev, acpi_handle *handle,
 
        DEBPRINT("ENTER: pci %02x:%02x.%01x\n", bus, devnum, func);
 
-       dev_handle = DEVICE_ACPI_HANDLE(dev);
+       dev_handle = ACPI_HANDLE(dev);
        if (!dev_handle) {
                DEBPRINT("no acpi handle for device\n");
                goto err;
index bf83d7b..2db803c 100644 (file)
@@ -416,8 +416,7 @@ static int pmac_ide_init_dma(ide_hwif_t *, const struct ide_port_info *);
 static void pmac_ide_apply_timings(ide_drive_t *drive)
 {
        ide_hwif_t *hwif = drive->hwif;
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
 
        if (drive->dn & 1)
                writel(pmif->timings[1], PMAC_IDE_REG(IDE_TIMING_CONFIG));
@@ -434,8 +433,7 @@ static void pmac_ide_apply_timings(ide_drive_t *drive)
 static void pmac_ide_kauai_apply_timings(ide_drive_t *drive)
 {
        ide_hwif_t *hwif = drive->hwif;
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
 
        if (drive->dn & 1) {
                writel(pmif->timings[1], PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG));
@@ -454,8 +452,7 @@ static void
 pmac_ide_do_update_timings(ide_drive_t *drive)
 {
        ide_hwif_t *hwif = drive->hwif;
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
 
        if (pmif->kind == controller_sh_ata6 ||
            pmif->kind == controller_un_ata6 ||
@@ -500,8 +497,7 @@ static void pmac_write_devctl(ide_hwif_t *hwif, u8 ctl)
  */
 static void pmac_ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
        const u8 pio = drive->pio_mode - XFER_PIO_0;
        struct ide_timing *tim = ide_timing_find_mode(XFER_PIO_0 + pio);
        u32 *timings, t;
@@ -781,8 +777,7 @@ set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2,
 
 static void pmac_ide_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
        int ret = 0;
        u32 *timings, *timings2, tl[2];
        u8 unit = drive->dn & 1;
@@ -919,8 +914,7 @@ static int pmac_ide_do_resume(pmac_ide_hwif_t *pmif)
 
 static u8 pmac_ide_cable_detect(ide_hwif_t *hwif)
 {
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
        struct device_node *np = pmif->node;
        const char *cable = of_get_property(np, "cable-type", NULL);
        struct device_node *root = of_find_node_by_path("/");
@@ -951,8 +945,7 @@ static u8 pmac_ide_cable_detect(ide_hwif_t *hwif)
 static void pmac_ide_init_dev(ide_drive_t *drive)
 {
        ide_hwif_t *hwif = drive->hwif;
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
 
        if (on_media_bay(pmif)) {
                if (check_media_bay(pmif->mdev->media_bay) == MB_CD) {
@@ -1228,8 +1221,7 @@ out_free_pmif:
 static int
 pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg)
 {
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(&mdev->ofdev.dev);
        int rc = 0;
 
        if (mesg.event != mdev->ofdev.dev.power.power_state.event
@@ -1245,8 +1237,7 @@ pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg)
 static int
 pmac_ide_macio_resume(struct macio_dev *mdev)
 {
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(&mdev->ofdev.dev);
        int rc = 0;
 
        if (mdev->ofdev.dev.power.power_state.event != PM_EVENT_ON) {
@@ -1318,7 +1309,6 @@ static int pmac_ide_pci_attach(struct pci_dev *pdev,
        rc = pmac_ide_setup_device(pmif, &hw);
        if (rc != 0) {
                /* The inteface is released to the common IDE layer */
-               pci_set_drvdata(pdev, NULL);
                iounmap(base);
                pci_release_regions(pdev);
                kfree(pmif);
@@ -1365,8 +1355,7 @@ pmac_ide_pci_resume(struct pci_dev *pdev)
 #ifdef CONFIG_PMAC_MEDIABAY
 static void pmac_ide_macio_mb_event(struct macio_dev* mdev, int mb_state)
 {
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(&mdev->ofdev.dev);
 
        switch(mb_state) {
        case MB_CD:
@@ -1468,8 +1457,7 @@ out:
 static int pmac_ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd)
 {
        ide_hwif_t *hwif = drive->hwif;
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
        struct dbdma_cmd *table;
        volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
        struct scatterlist *sg;
@@ -1546,8 +1534,7 @@ static int pmac_ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd)
 static int pmac_ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
 {
        ide_hwif_t *hwif = drive->hwif;
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
        u8 unit = drive->dn & 1, ata4 = (pmif->kind == controller_kl_ata4);
        u8 write = !!(cmd->tf_flags & IDE_TFLAG_WRITE);
 
@@ -1572,8 +1559,7 @@ static void
 pmac_ide_dma_start(ide_drive_t *drive)
 {
        ide_hwif_t *hwif = drive->hwif;
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
        volatile struct dbdma_regs __iomem *dma;
 
        dma = pmif->dma_regs;
@@ -1590,8 +1576,7 @@ static int
 pmac_ide_dma_end (ide_drive_t *drive)
 {
        ide_hwif_t *hwif = drive->hwif;
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
        volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
        u32 dstat;
 
@@ -1615,8 +1600,7 @@ static int
 pmac_ide_dma_test_irq (ide_drive_t *drive)
 {
        ide_hwif_t *hwif = drive->hwif;
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
        volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
        unsigned long status, timeout;
 
@@ -1670,8 +1654,7 @@ static void
 pmac_ide_dma_lost_irq (ide_drive_t *drive)
 {
        ide_hwif_t *hwif = drive->hwif;
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
        volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
        unsigned long status = readl(&dma->status);
 
@@ -1693,8 +1676,7 @@ static const struct ide_dma_ops pmac_dma_ops = {
  */
 static int pmac_ide_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
 {
-       pmac_ide_hwif_t *pmif =
-               (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent);
        struct pci_dev *dev = to_pci_dev(hwif->dev);
 
        /* We won't need pci_dev if we switch to generic consistent
index 3226ce9..cbd4e9a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * intel_idle.c - native hardware idle loop for modern Intel processors
  *
- * Copyright (c) 2010, Intel Corporation.
+ * Copyright (c) 2013, Intel Corporation.
  * Len Brown <len.brown@intel.com>
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -329,6 +329,22 @@ static struct cpuidle_state atom_cstates[] __initdata = {
        {
                .enter = NULL }
 };
+static struct cpuidle_state avn_cstates[CPUIDLE_STATE_MAX] = {
+       {
+               .name = "C1-AVN",
+               .desc = "MWAIT 0x00",
+               .flags = MWAIT2flg(0x00) | CPUIDLE_FLAG_TIME_VALID,
+               .exit_latency = 2,
+               .target_residency = 2,
+               .enter = &intel_idle },
+       {
+               .name = "C6-AVN",
+               .desc = "MWAIT 0x51",
+               .flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+               .exit_latency = 15,
+               .target_residency = 45,
+               .enter = &intel_idle },
+};
 
 /**
  * intel_idle
@@ -462,6 +478,11 @@ static const struct idle_cpu idle_cpu_hsw = {
        .disable_promotion_to_c1e = true,
 };
 
+static const struct idle_cpu idle_cpu_avn = {
+       .state_table = avn_cstates,
+       .disable_promotion_to_c1e = true,
+};
+
 #define ICPU(model, cpu) \
        { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&cpu }
 
@@ -483,6 +504,7 @@ static const struct x86_cpu_id intel_idle_ids[] = {
        ICPU(0x3f, idle_cpu_hsw),
        ICPU(0x45, idle_cpu_hsw),
        ICPU(0x46, idle_cpu_hsw),
+       ICPU(0x4D, idle_cpu_avn),
        {}
 };
 MODULE_DEVICE_TABLE(x86cpu, intel_idle_ids);
index e6fbd3e..9a4e0e3 100644 (file)
@@ -188,7 +188,7 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
 
        spi_bus_lock(sigma_delta->spi->master);
        sigma_delta->bus_locked = true;
-       INIT_COMPLETION(sigma_delta->completion);
+       reinit_completion(&sigma_delta->completion);
 
        ret = ad_sigma_delta_set_mode(sigma_delta, mode);
        if (ret < 0)
@@ -259,7 +259,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
 
        spi_bus_lock(sigma_delta->spi->master);
        sigma_delta->bus_locked = true;
-       INIT_COMPLETION(sigma_delta->completion);
+       reinit_completion(&sigma_delta->completion);
 
        ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
 
@@ -343,7 +343,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
 {
        struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
 
-       INIT_COMPLETION(sigma_delta->completion);
+       reinit_completion(&sigma_delta->completion);
        wait_for_completion_timeout(&sigma_delta->completion, HZ);
 
        if (!sigma_delta->irq_dis) {
index 54c5bab..e525aa6 100644 (file)
@@ -190,7 +190,7 @@ static int nau7802_read_irq(struct iio_dev *indio_dev,
        struct nau7802_state *st = iio_priv(indio_dev);
        int ret;
 
-       INIT_COMPLETION(st->value_ok);
+       reinit_completion(&st->value_ok);
        enable_irq(st->client->irq);
 
        nau7802_sync(st);
index dac15b9..c10eab6 100644 (file)
@@ -56,7 +56,7 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
                ev.id = ev_code;
                ev.timestamp = timestamp;
 
-               copied = kfifo_put(&ev_int->det_events, &ev);
+               copied = kfifo_put(&ev_int->det_events, ev);
                if (copied != 0)
                        wake_up_locked_poll(&ev_int->wait, POLLIN);
        }
index b84791f..5ceda71 100644 (file)
@@ -31,17 +31,6 @@ config INFINIBAND_USER_ACCESS
          libibverbs, libibcm and a hardware driver library from
          <http://www.openfabrics.org/git/>.
 
-config INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
-       bool "Experimental and unstable ABI for userspace access to flow steering verbs"
-       depends on INFINIBAND_USER_ACCESS
-       depends on STAGING
-       ---help---
-         The final ABI for userspace access to flow steering verbs
-         has not been defined.  To use the current ABI, *WHICH WILL
-         CHANGE IN THE FUTURE*, say Y here.
-
-         If unsure, say N.
-
 config INFINIBAND_USER_MEM
        bool
        depends on INFINIBAND_USER_ACCESS != n
index 784b97c..f2ef7ef 100644 (file)
@@ -383,14 +383,11 @@ static int cm_alloc_id(struct cm_id_private *cm_id_priv)
 {
        unsigned long flags;
        int id;
-       static int next_id;
 
        idr_preload(GFP_KERNEL);
        spin_lock_irqsave(&cm.lock, flags);
 
-       id = idr_alloc(&cm.local_id_table, cm_id_priv, next_id, 0, GFP_NOWAIT);
-       if (id >= 0)
-               next_id = max(id + 1, 0);
+       id = idr_alloc_cyclic(&cm.local_id_table, cm_id_priv, 0, 0, GFP_NOWAIT);
 
        spin_unlock_irqrestore(&cm.lock, flags);
        idr_preload_end();
index d2172e7..8e49db6 100644 (file)
@@ -328,28 +328,6 @@ static int cma_set_qkey(struct rdma_id_private *id_priv, u32 qkey)
        return ret;
 }
 
-static int find_gid_port(struct ib_device *device, union ib_gid *gid, u8 port_num)
-{
-       int i;
-       int err;
-       struct ib_port_attr props;
-       union ib_gid tmp;
-
-       err = ib_query_port(device, port_num, &props);
-       if (err)
-               return err;
-
-       for (i = 0; i < props.gid_tbl_len; ++i) {
-               err = ib_query_gid(device, port_num, i, &tmp);
-               if (err)
-                       return err;
-               if (!memcmp(&tmp, gid, sizeof tmp))
-                       return 0;
-       }
-
-       return -EADDRNOTAVAIL;
-}
-
 static void cma_translate_ib(struct sockaddr_ib *sib, struct rdma_dev_addr *dev_addr)
 {
        dev_addr->dev_type = ARPHRD_INFINIBAND;
@@ -371,13 +349,14 @@ static int cma_translate_addr(struct sockaddr *addr, struct rdma_dev_addr *dev_a
        return ret;
 }
 
-static int cma_acquire_dev(struct rdma_id_private *id_priv)
+static int cma_acquire_dev(struct rdma_id_private *id_priv,
+                          struct rdma_id_private *listen_id_priv)
 {
        struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
        struct cma_device *cma_dev;
        union ib_gid gid, iboe_gid;
        int ret = -ENODEV;
-       u8 port;
+       u8 port, found_port;
        enum rdma_link_layer dev_ll = dev_addr->dev_type == ARPHRD_INFINIBAND ?
                IB_LINK_LAYER_INFINIBAND : IB_LINK_LAYER_ETHERNET;
 
@@ -389,17 +368,39 @@ static int cma_acquire_dev(struct rdma_id_private *id_priv)
        iboe_addr_get_sgid(dev_addr, &iboe_gid);
        memcpy(&gid, dev_addr->src_dev_addr +
               rdma_addr_gid_offset(dev_addr), sizeof gid);
+       if (listen_id_priv &&
+           rdma_port_get_link_layer(listen_id_priv->id.device,
+                                    listen_id_priv->id.port_num) == dev_ll) {
+               cma_dev = listen_id_priv->cma_dev;
+               port = listen_id_priv->id.port_num;
+               if (rdma_node_get_transport(cma_dev->device->node_type) == RDMA_TRANSPORT_IB &&
+                   rdma_port_get_link_layer(cma_dev->device, port) == IB_LINK_LAYER_ETHERNET)
+                       ret = ib_find_cached_gid(cma_dev->device, &iboe_gid,
+                                                &found_port, NULL);
+               else
+                       ret = ib_find_cached_gid(cma_dev->device, &gid,
+                                                &found_port, NULL);
+
+               if (!ret && (port  == found_port)) {
+                       id_priv->id.port_num = found_port;
+                       goto out;
+               }
+       }
        list_for_each_entry(cma_dev, &dev_list, list) {
                for (port = 1; port <= cma_dev->device->phys_port_cnt; ++port) {
+                       if (listen_id_priv &&
+                           listen_id_priv->cma_dev == cma_dev &&
+                           listen_id_priv->id.port_num == port)
+                               continue;
                        if (rdma_port_get_link_layer(cma_dev->device, port) == dev_ll) {
                                if (rdma_node_get_transport(cma_dev->device->node_type) == RDMA_TRANSPORT_IB &&
                                    rdma_port_get_link_layer(cma_dev->device, port) == IB_LINK_LAYER_ETHERNET)
-                                       ret = find_gid_port(cma_dev->device, &iboe_gid, port);
+                                       ret = ib_find_cached_gid(cma_dev->device, &iboe_gid, &found_port, NULL);
                                else
-                                       ret = find_gid_port(cma_dev->device, &gid, port);
+                                       ret = ib_find_cached_gid(cma_dev->device, &gid, &found_port, NULL);
 
-                               if (!ret) {
-                                       id_priv->id.port_num = port;
+                               if (!ret && (port == found_port)) {
+                                       id_priv->id.port_num = found_port;
                                        goto out;
                                }
                        }
@@ -1292,7 +1293,7 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
        }
 
        mutex_lock_nested(&conn_id->handler_mutex, SINGLE_DEPTH_NESTING);
-       ret = cma_acquire_dev(conn_id);
+       ret = cma_acquire_dev(conn_id, listen_id);
        if (ret)
                goto err2;
 
@@ -1451,7 +1452,6 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
 {
        struct rdma_cm_id *new_cm_id;
        struct rdma_id_private *listen_id, *conn_id;
-       struct net_device *dev = NULL;
        struct rdma_cm_event event;
        int ret;
        struct ib_device_attr attr;
@@ -1481,7 +1481,7 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
                goto out;
        }
 
-       ret = cma_acquire_dev(conn_id);
+       ret = cma_acquire_dev(conn_id, listen_id);
        if (ret) {
                mutex_unlock(&conn_id->handler_mutex);
                rdma_destroy_id(new_cm_id);
@@ -1529,8 +1529,6 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
        cma_deref_id(conn_id);
 
 out:
-       if (dev)
-               dev_put(dev);
        mutex_unlock(&listen_id->handler_mutex);
        return ret;
 }
@@ -2066,7 +2064,7 @@ static void addr_handler(int status, struct sockaddr *src_addr,
                goto out;
 
        if (!status && !id_priv->cma_dev)
-               status = cma_acquire_dev(id_priv);
+               status = cma_acquire_dev(id_priv, NULL);
 
        if (status) {
                if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_RESOLVED,
@@ -2563,7 +2561,7 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)
                if (ret)
                        goto err1;
 
-               ret = cma_acquire_dev(id_priv);
+               ret = cma_acquire_dev(id_priv, NULL);
                if (ret)
                        goto err1;
        }
index da06abd..a1e9cba 100644 (file)
@@ -148,7 +148,7 @@ static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        list_for_each_entry(client, &client_list, list) {
                if (client->index == index) {
                        if (op < 0 || op >= client->nops ||
-                           !client->cb_table[RDMA_NL_GET_OP(op)].dump)
+                           !client->cb_table[op].dump)
                                return -EINVAL;
 
                        {
index cde1e7b..faad2ca 100644 (file)
@@ -612,6 +612,7 @@ static ssize_t show_node_type(struct device *device,
        switch (dev->node_type) {
        case RDMA_NODE_IB_CA:     return sprintf(buf, "%d: CA\n", dev->node_type);
        case RDMA_NODE_RNIC:      return sprintf(buf, "%d: RNIC\n", dev->node_type);
+       case RDMA_NODE_USNIC:     return sprintf(buf, "%d: usNIC\n", dev->node_type);
        case RDMA_NODE_IB_SWITCH: return sprintf(buf, "%d: switch\n", dev->node_type);
        case RDMA_NODE_IB_ROUTER: return sprintf(buf, "%d: router\n", dev->node_type);
        default:                  return sprintf(buf, "%d: <unknown>\n", dev->node_type);
index b0f189b..ab8b1c3 100644 (file)
@@ -57,7 +57,7 @@ MODULE_LICENSE("Dual BSD/GPL");
 static unsigned int max_backlog = 1024;
 
 static struct ctl_table_header *ucma_ctl_table_hdr;
-static ctl_table ucma_ctl_table[] = {
+static struct ctl_table ucma_ctl_table[] = {
        {
                .procname       = "max_backlog",
                .data           = &max_backlog,
@@ -271,7 +271,7 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id,
                        goto out;
                }
                ctx->backlog--;
-       } else if (!ctx->uid) {
+       } else if (!ctx->uid || ctx->cm_id != cm_id) {
                /*
                 * We ignore events for new connections until userspace has set
                 * their context.  This can only happen if an error occurs on a
index d8f9c6c..bdc842e 100644 (file)
 #include <rdma/ib_umem.h>
 #include <rdma/ib_user_verbs.h>
 
+#define INIT_UDATA(udata, ibuf, obuf, ilen, olen)                      \
+       do {                                                            \
+               (udata)->inbuf  = (void __user *) (ibuf);               \
+               (udata)->outbuf = (void __user *) (obuf);               \
+               (udata)->inlen  = (ilen);                               \
+               (udata)->outlen = (olen);                               \
+       } while (0)
+
 /*
  * Our lifetime rules for these structs are the following:
  *
@@ -178,6 +186,22 @@ void ib_uverbs_event_handler(struct ib_event_handler *handler,
                             struct ib_event *event);
 void ib_uverbs_dealloc_xrcd(struct ib_uverbs_device *dev, struct ib_xrcd *xrcd);
 
+struct ib_uverbs_flow_spec {
+       union {
+               union {
+                       struct ib_uverbs_flow_spec_hdr hdr;
+                       struct {
+                               __u32 type;
+                               __u16 size;
+                               __u16 reserved;
+                       };
+               };
+               struct ib_uverbs_flow_spec_eth     eth;
+               struct ib_uverbs_flow_spec_ipv4    ipv4;
+               struct ib_uverbs_flow_spec_tcp_udp tcp_udp;
+       };
+};
+
 #define IB_UVERBS_DECLARE_CMD(name)                                    \
        ssize_t ib_uverbs_##name(struct ib_uverbs_file *file,           \
                                 const char __user *buf, int in_len,    \
@@ -217,9 +241,13 @@ IB_UVERBS_DECLARE_CMD(destroy_srq);
 IB_UVERBS_DECLARE_CMD(create_xsrq);
 IB_UVERBS_DECLARE_CMD(open_xrcd);
 IB_UVERBS_DECLARE_CMD(close_xrcd);
-#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
-IB_UVERBS_DECLARE_CMD(create_flow);
-IB_UVERBS_DECLARE_CMD(destroy_flow);
-#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
+
+#define IB_UVERBS_DECLARE_EX_CMD(name)                         \
+       int ib_uverbs_ex_##name(struct ib_uverbs_file *file,    \
+                               struct ib_udata *ucore,         \
+                               struct ib_udata *uhw)
+
+IB_UVERBS_DECLARE_EX_CMD(create_flow);
+IB_UVERBS_DECLARE_EX_CMD(destroy_flow);
 
 #endif /* UVERBS_H */
index 2f0f01b..65f6e7d 100644 (file)
@@ -54,17 +54,7 @@ static struct uverbs_lock_class qp_lock_class        = { .name = "QP-uobj" };
 static struct uverbs_lock_class ah_lock_class  = { .name = "AH-uobj" };
 static struct uverbs_lock_class srq_lock_class = { .name = "SRQ-uobj" };
 static struct uverbs_lock_class xrcd_lock_class = { .name = "XRCD-uobj" };
-#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 static struct uverbs_lock_class rule_lock_class = { .name = "RULE-uobj" };
-#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
-
-#define INIT_UDATA(udata, ibuf, obuf, ilen, olen)                      \
-       do {                                                            \
-               (udata)->inbuf  = (void __user *) (ibuf);               \
-               (udata)->outbuf = (void __user *) (obuf);               \
-               (udata)->inlen  = (ilen);                               \
-               (udata)->outlen = (olen);                               \
-       } while (0)
 
 /*
  * The ib_uobject locking scheme is as follows:
@@ -939,13 +929,9 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,
        if ((cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK))
                return -EINVAL;
 
-       /*
-        * Local write permission is required if remote write or
-        * remote atomic permission is also requested.
-        */
-       if (cmd.access_flags & (IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_REMOTE_WRITE) &&
-           !(cmd.access_flags & IB_ACCESS_LOCAL_WRITE))
-               return -EINVAL;
+       ret = ib_check_mr_access(cmd.access_flags);
+       if (ret)
+               return ret;
 
        uobj = kmalloc(sizeof *uobj, GFP_KERNEL);
        if (!uobj)
@@ -2128,6 +2114,9 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
                        }
                        next->wr.ud.remote_qpn  = user_wr->wr.ud.remote_qpn;
                        next->wr.ud.remote_qkey = user_wr->wr.ud.remote_qkey;
+                       if (next->opcode == IB_WR_SEND_WITH_IMM)
+                               next->ex.imm_data =
+                                       (__be32 __force) user_wr->ex.imm_data;
                } else {
                        switch (next->opcode) {
                        case IB_WR_RDMA_WRITE_WITH_IMM:
@@ -2601,8 +2590,7 @@ out_put:
        return ret ? ret : in_len;
 }
 
-#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
-static int kern_spec_to_ib_spec(struct ib_kern_spec *kern_spec,
+static int kern_spec_to_ib_spec(struct ib_uverbs_flow_spec *kern_spec,
                                union ib_flow_spec *ib_spec)
 {
        ib_spec->type = kern_spec->type;
@@ -2642,28 +2630,31 @@ static int kern_spec_to_ib_spec(struct ib_kern_spec *kern_spec,
        return 0;
 }
 
-ssize_t ib_uverbs_create_flow(struct ib_uverbs_file *file,
-                             const char __user *buf, int in_len,
-                             int out_len)
+int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
+                            struct ib_udata *ucore,
+                            struct ib_udata *uhw)
 {
        struct ib_uverbs_create_flow      cmd;
        struct ib_uverbs_create_flow_resp resp;
        struct ib_uobject                 *uobj;
        struct ib_flow                    *flow_id;
-       struct ib_kern_flow_attr          *kern_flow_attr;
+       struct ib_uverbs_flow_attr        *kern_flow_attr;
        struct ib_flow_attr               *flow_attr;
        struct ib_qp                      *qp;
        int err = 0;
        void *kern_spec;
        void *ib_spec;
        int i;
-       int kern_attr_size;
 
-       if (out_len < sizeof(resp))
+       if (ucore->outlen < sizeof(resp))
                return -ENOSPC;
 
-       if (copy_from_user(&cmd, buf, sizeof(cmd)))
-               return -EFAULT;
+       err = ib_copy_from_udata(&cmd, ucore, sizeof(cmd));
+       if (err)
+               return err;
+
+       ucore->inbuf += sizeof(cmd);
+       ucore->inlen -= sizeof(cmd);
 
        if (cmd.comp_mask)
                return -EINVAL;
@@ -2672,32 +2663,27 @@ ssize_t ib_uverbs_create_flow(struct ib_uverbs_file *file,
             !capable(CAP_NET_ADMIN)) || !capable(CAP_NET_RAW))
                return -EPERM;
 
-       if (cmd.flow_attr.num_of_specs < 0 ||
-           cmd.flow_attr.num_of_specs > IB_FLOW_SPEC_SUPPORT_LAYERS)
+       if (cmd.flow_attr.num_of_specs > IB_FLOW_SPEC_SUPPORT_LAYERS)
                return -EINVAL;
 
-       kern_attr_size = cmd.flow_attr.size - sizeof(cmd) -
-                        sizeof(struct ib_uverbs_cmd_hdr_ex);
-
-       if (cmd.flow_attr.size < 0 || cmd.flow_attr.size > in_len ||
-           kern_attr_size < 0 || kern_attr_size >
-           (cmd.flow_attr.num_of_specs * sizeof(struct ib_kern_spec)))
+       if (cmd.flow_attr.size > ucore->inlen ||
+           cmd.flow_attr.size >
+           (cmd.flow_attr.num_of_specs * sizeof(struct ib_uverbs_flow_spec)))
                return -EINVAL;
 
        if (cmd.flow_attr.num_of_specs) {
-               kern_flow_attr = kmalloc(cmd.flow_attr.size, GFP_KERNEL);
+               kern_flow_attr = kmalloc(sizeof(*kern_flow_attr) + cmd.flow_attr.size,
+                                        GFP_KERNEL);
                if (!kern_flow_attr)
                        return -ENOMEM;
 
                memcpy(kern_flow_attr, &cmd.flow_attr, sizeof(*kern_flow_attr));
-               if (copy_from_user(kern_flow_attr + 1, buf + sizeof(cmd),
-                                  kern_attr_size)) {
-                       err = -EFAULT;
+               err = ib_copy_from_udata(kern_flow_attr + 1, ucore,
+                                        cmd.flow_attr.size);
+               if (err)
                        goto err_free_attr;
-               }
        } else {
                kern_flow_attr = &cmd.flow_attr;
-               kern_attr_size = sizeof(cmd.flow_attr);
        }
 
        uobj = kmalloc(sizeof(*uobj), GFP_KERNEL);
@@ -2714,7 +2700,7 @@ ssize_t ib_uverbs_create_flow(struct ib_uverbs_file *file,
                goto err_uobj;
        }
 
-       flow_attr = kmalloc(cmd.flow_attr.size, GFP_KERNEL);
+       flow_attr = kmalloc(sizeof(*flow_attr) + cmd.flow_attr.size, GFP_KERNEL);
        if (!flow_attr) {
                err = -ENOMEM;
                goto err_put;
@@ -2729,19 +2715,22 @@ ssize_t ib_uverbs_create_flow(struct ib_uverbs_file *file,
 
        kern_spec = kern_flow_attr + 1;
        ib_spec = flow_attr + 1;
-       for (i = 0; i < flow_attr->num_of_specs && kern_attr_size > 0; i++) {
+       for (i = 0; i < flow_attr->num_of_specs &&
+            cmd.flow_attr.size > offsetof(struct ib_uverbs_flow_spec, reserved) &&
+            cmd.flow_attr.size >=
+            ((struct ib_uverbs_flow_spec *)kern_spec)->size; i++) {
                err = kern_spec_to_ib_spec(kern_spec, ib_spec);
                if (err)
                        goto err_free;
                flow_attr->size +=
                        ((union ib_flow_spec *) ib_spec)->size;
-               kern_attr_size -= ((struct ib_kern_spec *) kern_spec)->size;
-               kern_spec += ((struct ib_kern_spec *) kern_spec)->size;
+               cmd.flow_attr.size -= ((struct ib_uverbs_flow_spec *)kern_spec)->size;
+               kern_spec += ((struct ib_uverbs_flow_spec *) kern_spec)->size;
                ib_spec += ((union ib_flow_spec *) ib_spec)->size;
        }
-       if (kern_attr_size) {
-               pr_warn("create flow failed, %d bytes left from uverb cmd\n",
-                       kern_attr_size);
+       if (cmd.flow_attr.size || (i != flow_attr->num_of_specs)) {
+               pr_warn("create flow failed, flow %d: %d bytes left from uverb cmd\n",
+                       i, cmd.flow_attr.size);
                goto err_free;
        }
        flow_id = ib_create_flow(qp, flow_attr, IB_FLOW_DOMAIN_USER);
@@ -2760,11 +2749,10 @@ ssize_t ib_uverbs_create_flow(struct ib_uverbs_file *file,
        memset(&resp, 0, sizeof(resp));
        resp.flow_handle = uobj->id;
 
-       if (copy_to_user((void __user *)(unsigned long) cmd.response,
-                        &resp, sizeof(resp))) {
-               err = -EFAULT;
+       err = ib_copy_to_udata(ucore,
+                              &resp, sizeof(resp));
+       if (err)
                goto err_copy;
-       }
 
        put_qp_read(qp);
        mutex_lock(&file->mutex);
@@ -2777,7 +2765,7 @@ ssize_t ib_uverbs_create_flow(struct ib_uverbs_file *file,
        kfree(flow_attr);
        if (cmd.flow_attr.num_of_specs)
                kfree(kern_flow_attr);
-       return in_len;
+       return 0;
 err_copy:
        idr_remove_uobj(&ib_uverbs_rule_idr, uobj);
 destroy_flow:
@@ -2794,16 +2782,18 @@ err_free_attr:
        return err;
 }
 
-ssize_t ib_uverbs_destroy_flow(struct ib_uverbs_file *file,
-                              const char __user *buf, int in_len,
-                              int out_len) {
+int ib_uverbs_ex_destroy_flow(struct ib_uverbs_file *file,
+                             struct ib_udata *ucore,
+                             struct ib_udata *uhw)
+{
        struct ib_uverbs_destroy_flow   cmd;
        struct ib_flow                  *flow_id;
        struct ib_uobject               *uobj;
        int                             ret;
 
-       if (copy_from_user(&cmd, buf, sizeof(cmd)))
-               return -EFAULT;
+       ret = ib_copy_from_udata(&cmd, ucore, sizeof(cmd));
+       if (ret)
+               return ret;
 
        uobj = idr_write_uobj(&ib_uverbs_rule_idr, cmd.flow_handle,
                              file->ucontext);
@@ -2825,9 +2815,8 @@ ssize_t ib_uverbs_destroy_flow(struct ib_uverbs_file *file,
 
        put_uobj(uobj);
 
-       return ret ? ret : in_len;
+       return ret;
 }
-#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
                                struct ib_uverbs_create_xsrq *cmd,
index 2df31f6..3438694 100644 (file)
@@ -115,10 +115,13 @@ static ssize_t (*uverbs_cmd_table[])(struct ib_uverbs_file *file,
        [IB_USER_VERBS_CMD_CLOSE_XRCD]          = ib_uverbs_close_xrcd,
        [IB_USER_VERBS_CMD_CREATE_XSRQ]         = ib_uverbs_create_xsrq,
        [IB_USER_VERBS_CMD_OPEN_QP]             = ib_uverbs_open_qp,
-#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
-       [IB_USER_VERBS_CMD_CREATE_FLOW]         = ib_uverbs_create_flow,
-       [IB_USER_VERBS_CMD_DESTROY_FLOW]        = ib_uverbs_destroy_flow
-#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
+};
+
+static int (*uverbs_ex_cmd_table[])(struct ib_uverbs_file *file,
+                                   struct ib_udata *ucore,
+                                   struct ib_udata *uhw) = {
+       [IB_USER_VERBS_EX_CMD_CREATE_FLOW]      = ib_uverbs_ex_create_flow,
+       [IB_USER_VERBS_EX_CMD_DESTROY_FLOW]     = ib_uverbs_ex_destroy_flow
 };
 
 static void ib_uverbs_add_one(struct ib_device *device);
@@ -589,6 +592,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
 {
        struct ib_uverbs_file *file = filp->private_data;
        struct ib_uverbs_cmd_hdr hdr;
+       __u32 flags;
 
        if (count < sizeof hdr)
                return -EINVAL;
@@ -596,45 +600,105 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
        if (copy_from_user(&hdr, buf, sizeof hdr))
                return -EFAULT;
 
-       if (hdr.command >= ARRAY_SIZE(uverbs_cmd_table) ||
-           !uverbs_cmd_table[hdr.command])
-               return -EINVAL;
+       flags = (hdr.command &
+                IB_USER_VERBS_CMD_FLAGS_MASK) >> IB_USER_VERBS_CMD_FLAGS_SHIFT;
 
-       if (!file->ucontext &&
-           hdr.command != IB_USER_VERBS_CMD_GET_CONTEXT)
-               return -EINVAL;
+       if (!flags) {
+               __u32 command;
 
-       if (!(file->device->ib_dev->uverbs_cmd_mask & (1ull << hdr.command)))
-               return -ENOSYS;
+               if (hdr.command & ~(__u32)(IB_USER_VERBS_CMD_FLAGS_MASK |
+                                          IB_USER_VERBS_CMD_COMMAND_MASK))
+                       return -EINVAL;
 
-#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
-       if (hdr.command >= IB_USER_VERBS_CMD_THRESHOLD) {
-               struct ib_uverbs_cmd_hdr_ex hdr_ex;
+               command = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK;
 
-               if (copy_from_user(&hdr_ex, buf, sizeof(hdr_ex)))
-                       return -EFAULT;
+               if (command >= ARRAY_SIZE(uverbs_cmd_table) ||
+                   !uverbs_cmd_table[command])
+                       return -EINVAL;
 
-               if (((hdr_ex.in_words + hdr_ex.provider_in_words) * 4) != count)
+               if (!file->ucontext &&
+                   command != IB_USER_VERBS_CMD_GET_CONTEXT)
                        return -EINVAL;
 
-               return uverbs_cmd_table[hdr.command](file,
-                                                    buf + sizeof(hdr_ex),
-                                                    (hdr_ex.in_words +
-                                                     hdr_ex.provider_in_words) * 4,
-                                                    (hdr_ex.out_words +
-                                                     hdr_ex.provider_out_words) * 4);
-       } else {
-#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
+               if (!(file->device->ib_dev->uverbs_cmd_mask & (1ull << command)))
+                       return -ENOSYS;
+
                if (hdr.in_words * 4 != count)
                        return -EINVAL;
 
-               return uverbs_cmd_table[hdr.command](file,
-                                                    buf + sizeof(hdr),
-                                                    hdr.in_words * 4,
-                                                    hdr.out_words * 4);
-#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
+               return uverbs_cmd_table[command](file,
+                                                buf + sizeof(hdr),
+                                                hdr.in_words * 4,
+                                                hdr.out_words * 4);
+
+       } else if (flags == IB_USER_VERBS_CMD_FLAG_EXTENDED) {
+               __u32 command;
+
+               struct ib_uverbs_ex_cmd_hdr ex_hdr;
+               struct ib_udata ucore;
+               struct ib_udata uhw;
+               int err;
+               size_t written_count = count;
+
+               if (hdr.command & ~(__u32)(IB_USER_VERBS_CMD_FLAGS_MASK |
+                                          IB_USER_VERBS_CMD_COMMAND_MASK))
+                       return -EINVAL;
+
+               command = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK;
+
+               if (command >= ARRAY_SIZE(uverbs_ex_cmd_table) ||
+                   !uverbs_ex_cmd_table[command])
+                       return -ENOSYS;
+
+               if (!file->ucontext)
+                       return -EINVAL;
+
+               if (!(file->device->ib_dev->uverbs_ex_cmd_mask & (1ull << command)))
+                       return -ENOSYS;
+
+               if (count < (sizeof(hdr) + sizeof(ex_hdr)))
+                       return -EINVAL;
+
+               if (copy_from_user(&ex_hdr, buf + sizeof(hdr), sizeof(ex_hdr)))
+                       return -EFAULT;
+
+               count -= sizeof(hdr) + sizeof(ex_hdr);
+               buf += sizeof(hdr) + sizeof(ex_hdr);
+
+               if ((hdr.in_words + ex_hdr.provider_in_words) * 8 != count)
+                       return -EINVAL;
+
+               if (ex_hdr.response) {
+                       if (!hdr.out_words && !ex_hdr.provider_out_words)
+                               return -EINVAL;
+               } else {
+                       if (hdr.out_words || ex_hdr.provider_out_words)
+                               return -EINVAL;
+               }
+
+               INIT_UDATA(&ucore,
+                          (hdr.in_words) ? buf : 0,
+                          (unsigned long)ex_hdr.response,
+                          hdr.in_words * 8,
+                          hdr.out_words * 8);
+
+               INIT_UDATA(&uhw,
+                          (ex_hdr.provider_in_words) ? buf + ucore.inlen : 0,
+                          (ex_hdr.provider_out_words) ? (unsigned long)ex_hdr.response + ucore.outlen : 0,
+                          ex_hdr.provider_in_words * 8,
+                          ex_hdr.provider_out_words * 8);
+
+               err = uverbs_ex_cmd_table[command](file,
+                                                  &ucore,
+                                                  &uhw);
+
+               if (err)
+                       return err;
+
+               return written_count;
        }
-#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
+
+       return -ENOSYS;
 }
 
 static int ib_uverbs_mmap(struct file *filp, struct vm_area_struct *vma)
index a321df2..d4f6ddf 100644 (file)
@@ -114,6 +114,8 @@ rdma_node_get_transport(enum rdma_node_type node_type)
                return RDMA_TRANSPORT_IB;
        case RDMA_NODE_RNIC:
                return RDMA_TRANSPORT_IWARP;
+       case RDMA_NODE_USNIC:
+               return RDMA_TRANSPORT_USNIC;
        default:
                BUG();
                return 0;
@@ -130,6 +132,7 @@ enum rdma_link_layer rdma_port_get_link_layer(struct ib_device *device, u8 port_
        case RDMA_TRANSPORT_IB:
                return IB_LINK_LAYER_INFINIBAND;
        case RDMA_TRANSPORT_IWARP:
+       case RDMA_TRANSPORT_USNIC:
                return IB_LINK_LAYER_ETHERNET;
        default:
                return IB_LINK_LAYER_UNSPECIFIED;
@@ -958,6 +961,11 @@ EXPORT_SYMBOL(ib_resize_cq);
 struct ib_mr *ib_get_dma_mr(struct ib_pd *pd, int mr_access_flags)
 {
        struct ib_mr *mr;
+       int err;
+
+       err = ib_check_mr_access(mr_access_flags);
+       if (err)
+               return ERR_PTR(err);
 
        mr = pd->device->get_dma_mr(pd, mr_access_flags);
 
@@ -980,6 +988,11 @@ struct ib_mr *ib_reg_phys_mr(struct ib_pd *pd,
                             u64 *iova_start)
 {
        struct ib_mr *mr;
+       int err;
+
+       err = ib_check_mr_access(mr_access_flags);
+       if (err)
+               return ERR_PTR(err);
 
        if (!pd->device->reg_phys_mr)
                return ERR_PTR(-ENOSYS);
@@ -1010,6 +1023,10 @@ int ib_rereg_phys_mr(struct ib_mr *mr,
        struct ib_pd *old_pd;
        int ret;
 
+       ret = ib_check_mr_access(mr_access_flags);
+       if (ret)
+               return ret;
+
        if (!mr->device->rereg_phys_mr)
                return -ENOSYS;
 
index 33d2cc6..4a03385 100644 (file)
@@ -602,10 +602,10 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev)
             rdev->lldi.vr->qp.size,
             rdev->lldi.vr->cq.start,
             rdev->lldi.vr->cq.size);
-       PDBG("udb len 0x%x udb base %p db_reg %p gts_reg %p qpshift %lu "
+       PDBG("udb len 0x%x udb base %llx db_reg %p gts_reg %p qpshift %lu "
             "qpmask 0x%x cqshift %lu cqmask 0x%x\n",
             (unsigned)pci_resource_len(rdev->lldi.pdev, 2),
-            (void *)(unsigned long)pci_resource_start(rdev->lldi.pdev, 2),
+            (u64)pci_resource_start(rdev->lldi.pdev, 2),
             rdev->lldi.db_reg,
             rdev->lldi.gts_reg,
             rdev->qpshift, rdev->qpmask,
index f5cb13b..cc04b7b 100644 (file)
@@ -280,9 +280,7 @@ static int ipath_user_sdma_pin_pages(const struct ipath_devdata *dd,
        int j;
        int ret;
 
-       ret = get_user_pages(current, current->mm, addr,
-                            npages, 0, 1, pages, NULL);
-
+       ret = get_user_pages_fast(addr, npages, 0, pages);
        if (ret != npages) {
                int i;
 
@@ -811,10 +809,7 @@ int ipath_user_sdma_writev(struct ipath_devdata *dd,
        while (dim) {
                const int mxp = 8;
 
-               down_write(&current->mm->mmap_sem);
                ret = ipath_user_sdma_queue_pkts(dd, pq, &list, iov, dim, mxp);
-               up_write(&current->mm->mmap_sem);
-
                if (ret <= 0)
                        goto done_unlock;
                else {
index d5e60f4..66dbf80 100644 (file)
@@ -324,7 +324,7 @@ static int mlx4_ib_get_outstanding_cqes(struct mlx4_ib_cq *cq)
        u32 i;
 
        i = cq->mcq.cons_index;
-       while (get_sw_cqe(cq, i & cq->ibcq.cqe))
+       while (get_sw_cqe(cq, i))
                ++i;
 
        return i - cq->mcq.cons_index;
@@ -365,7 +365,7 @@ int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata)
 
        mutex_lock(&cq->resize_mutex);
 
-       if (entries < 1 || entries > dev->dev->caps.max_cqes) {
+       if (entries < 1) {
                err = -EINVAL;
                goto out;
        }
@@ -376,6 +376,11 @@ int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata)
                goto out;
        }
 
+       if (entries > dev->dev->caps.max_cqes) {
+               err = -EINVAL;
+               goto out;
+       }
+
        if (ibcq->uobject) {
                err = mlx4_alloc_resize_umem(dev, cq, entries, udata);
                if (err)
index 6a0a0d2..1958c5c 100644 (file)
@@ -1685,11 +1685,9 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
                ibdev->ib_dev.create_flow       = mlx4_ib_create_flow;
                ibdev->ib_dev.destroy_flow      = mlx4_ib_destroy_flow;
 
-#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
-               ibdev->ib_dev.uverbs_cmd_mask   |=
-                       (1ull << IB_USER_VERBS_CMD_CREATE_FLOW) |
-                       (1ull << IB_USER_VERBS_CMD_DESTROY_FLOW);
-#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
+               ibdev->ib_dev.uverbs_ex_cmd_mask        |=
+                       (1ull << IB_USER_VERBS_EX_CMD_CREATE_FLOW) |
+                       (1ull << IB_USER_VERBS_EX_CMD_DESTROY_FLOW);
        }
 
        mlx4_ib_alloc_eqs(dev, ibdev);
index 344ab03..b726274 100644 (file)
@@ -556,7 +556,7 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata,
                goto err_db;
        }
        mlx5_ib_populate_pas(dev, cq->buf.umem, page_shift, (*cqb)->pas, 0);
-       (*cqb)->ctx.log_pg_sz = page_shift - PAGE_SHIFT;
+       (*cqb)->ctx.log_pg_sz = page_shift - MLX5_ADAPTER_PAGE_SHIFT;
 
        *index = to_mucontext(context)->uuari.uars[0].index;
 
@@ -620,7 +620,7 @@ static int create_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
        }
        mlx5_fill_page_array(&cq->buf.buf, (*cqb)->pas);
 
-       (*cqb)->ctx.log_pg_sz = cq->buf.buf.page_shift - PAGE_SHIFT;
+       (*cqb)->ctx.log_pg_sz = cq->buf.buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT;
        *index = dev->mdev.priv.uuari.uars[0].index;
 
        return 0;
@@ -653,8 +653,11 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
        int eqn;
        int err;
 
+       if (entries < 0)
+               return ERR_PTR(-EINVAL);
+
        entries = roundup_pow_of_two(entries + 1);
-       if (entries < 1 || entries > dev->mdev.caps.max_cqes)
+       if (entries > dev->mdev.caps.max_cqes)
                return ERR_PTR(-EINVAL);
 
        cq = kzalloc(sizeof(*cq), GFP_KERNEL);
@@ -747,17 +750,9 @@ int mlx5_ib_destroy_cq(struct ib_cq *cq)
        return 0;
 }
 
-static int is_equal_rsn(struct mlx5_cqe64 *cqe64, struct mlx5_ib_srq *srq,
-                       u32 rsn)
+static int is_equal_rsn(struct mlx5_cqe64 *cqe64, u32 rsn)
 {
-       u32 lrsn;
-
-       if (srq)
-               lrsn = be32_to_cpu(cqe64->srqn) & 0xffffff;
-       else
-               lrsn = be32_to_cpu(cqe64->sop_drop_qpn) & 0xffffff;
-
-       return rsn == lrsn;
+       return rsn == (ntohl(cqe64->sop_drop_qpn) & 0xffffff);
 }
 
 void __mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 rsn, struct mlx5_ib_srq *srq)
@@ -787,8 +782,8 @@ void __mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 rsn, struct mlx5_ib_srq *srq)
        while ((int) --prod_index - (int) cq->mcq.cons_index >= 0) {
                cqe = get_cqe(cq, prod_index & cq->ibcq.cqe);
                cqe64 = (cq->mcq.cqe_sz == 64) ? cqe : cqe + 64;
-               if (is_equal_rsn(cqe64, srq, rsn)) {
-                       if (srq)
+               if (is_equal_rsn(cqe64, rsn)) {
+                       if (srq && (ntohl(cqe64->srqn) & 0xffffff))
                                mlx5_ib_free_srq_wqe(srq, be16_to_cpu(cqe64->wqe_counter));
                        ++nfreed;
                } else if (nfreed) {
index b1a6cb3..3065341 100644 (file)
@@ -745,7 +745,8 @@ static int alloc_pa_mkey(struct mlx5_ib_dev *dev, u32 *key, u32 pdn)
        seg->qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
        seg->start_addr = 0;
 
-       err = mlx5_core_create_mkey(&dev->mdev, &mr, in, sizeof(*in));
+       err = mlx5_core_create_mkey(&dev->mdev, &mr, in, sizeof(*in),
+                                   NULL, NULL, NULL);
        if (err) {
                mlx5_ib_warn(dev, "failed to create mkey, %d\n", err);
                goto err_in;
index 836be91..4c134d9 100644 (file)
@@ -262,6 +262,9 @@ struct mlx5_ib_mr {
        int                     npages;
        struct completion       done;
        enum ib_wc_status       status;
+       struct mlx5_ib_dev     *dev;
+       struct mlx5_create_mkey_mbox_out out;
+       unsigned long           start;
 };
 
 struct mlx5_ib_fast_reg_page_list {
@@ -323,6 +326,7 @@ struct mlx5_cache_ent {
        struct mlx5_ib_dev     *dev;
        struct work_struct      work;
        struct delayed_work     dwork;
+       int                     pending;
 };
 
 struct mlx5_mr_cache {
@@ -358,6 +362,8 @@ struct mlx5_ib_dev {
        spinlock_t                      mr_lock;
        struct mlx5_ib_resources        devr;
        struct mlx5_mr_cache            cache;
+       struct timer_list               delay_timer;
+       int                             fill_delay;
 };
 
 static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq)
index 3453580..039c3e4 100644 (file)
 #include <linux/random.h>
 #include <linux/debugfs.h>
 #include <linux/export.h>
+#include <linux/delay.h>
 #include <rdma/ib_umem.h>
 #include "mlx5_ib.h"
 
 enum {
-       DEF_CACHE_SIZE  = 10,
+       MAX_PENDING_REG_MR = 8,
 };
 
 enum {
@@ -63,6 +64,51 @@ static int order2idx(struct mlx5_ib_dev *dev, int order)
                return order - cache->ent[0].order;
 }
 
+static void reg_mr_callback(int status, void *context)
+{
+       struct mlx5_ib_mr *mr = context;
+       struct mlx5_ib_dev *dev = mr->dev;
+       struct mlx5_mr_cache *cache = &dev->cache;
+       int c = order2idx(dev, mr->order);
+       struct mlx5_cache_ent *ent = &cache->ent[c];
+       u8 key;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ent->lock, flags);
+       ent->pending--;
+       spin_unlock_irqrestore(&ent->lock, flags);
+       if (status) {
+               mlx5_ib_warn(dev, "async reg mr failed. status %d\n", status);
+               kfree(mr);
+               dev->fill_delay = 1;
+               mod_timer(&dev->delay_timer, jiffies + HZ);
+               return;
+       }
+
+       if (mr->out.hdr.status) {
+               mlx5_ib_warn(dev, "failed - status %d, syndorme 0x%x\n",
+                            mr->out.hdr.status,
+                            be32_to_cpu(mr->out.hdr.syndrome));
+               kfree(mr);
+               dev->fill_delay = 1;
+               mod_timer(&dev->delay_timer, jiffies + HZ);
+               return;
+       }
+
+       spin_lock_irqsave(&dev->mdev.priv.mkey_lock, flags);
+       key = dev->mdev.priv.mkey_key++;
+       spin_unlock_irqrestore(&dev->mdev.priv.mkey_lock, flags);
+       mr->mmr.key = mlx5_idx_to_mkey(be32_to_cpu(mr->out.mkey) & 0xffffff) | key;
+
+       cache->last_add = jiffies;
+
+       spin_lock_irqsave(&ent->lock, flags);
+       list_add_tail(&mr->list, &ent->head);
+       ent->cur++;
+       ent->size++;
+       spin_unlock_irqrestore(&ent->lock, flags);
+}
+
 static int add_keys(struct mlx5_ib_dev *dev, int c, int num)
 {
        struct mlx5_mr_cache *cache = &dev->cache;
@@ -78,36 +124,39 @@ static int add_keys(struct mlx5_ib_dev *dev, int c, int num)
                return -ENOMEM;
 
        for (i = 0; i < num; i++) {
+               if (ent->pending >= MAX_PENDING_REG_MR) {
+                       err = -EAGAIN;
+                       break;
+               }
+
                mr = kzalloc(sizeof(*mr), GFP_KERNEL);
                if (!mr) {
                        err = -ENOMEM;
-                       goto out;
+                       break;
                }
                mr->order = ent->order;
                mr->umred = 1;
+               mr->dev = dev;
                in->seg.status = 1 << 6;
                in->seg.xlt_oct_size = cpu_to_be32((npages + 1) / 2);
                in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
                in->seg.flags = MLX5_ACCESS_MODE_MTT | MLX5_PERM_UMR_EN;
                in->seg.log2_page_size = 12;
 
+               spin_lock_irq(&ent->lock);
+               ent->pending++;
+               spin_unlock_irq(&ent->lock);
+               mr->start = jiffies;
                err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in,
-                                           sizeof(*in));
+                                           sizeof(*in), reg_mr_callback,
+                                           mr, &mr->out);
                if (err) {
                        mlx5_ib_warn(dev, "create mkey failed %d\n", err);
                        kfree(mr);
-                       goto out;
+                       break;
                }
-               cache->last_add = jiffies;
-
-               spin_lock(&ent->lock);
-               list_add_tail(&mr->list, &ent->head);
-               ent->cur++;
-               ent->size++;
-               spin_unlock(&ent->lock);
        }
 
-out:
        kfree(in);
        return err;
 }
@@ -121,16 +170,16 @@ static void remove_keys(struct mlx5_ib_dev *dev, int c, int num)
        int i;
 
        for (i = 0; i < num; i++) {
-               spin_lock(&ent->lock);
+               spin_lock_irq(&ent->lock);
                if (list_empty(&ent->head)) {
-                       spin_unlock(&ent->lock);
+                       spin_unlock_irq(&ent->lock);
                        return;
                }
                mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list);
                list_del(&mr->list);
                ent->cur--;
                ent->size--;
-               spin_unlock(&ent->lock);
+               spin_unlock_irq(&ent->lock);
                err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr);
                if (err)
                        mlx5_ib_warn(dev, "failed destroy mkey\n");
@@ -162,9 +211,13 @@ static ssize_t size_write(struct file *filp, const char __user *buf,
                return -EINVAL;
 
        if (var > ent->size) {
-               err = add_keys(dev, c, var - ent->size);
-               if (err)
-                       return err;
+               do {
+                       err = add_keys(dev, c, var - ent->size);
+                       if (err && err != -EAGAIN)
+                               return err;
+
+                       usleep_range(3000, 5000);
+               } while (err);
        } else if (var < ent->size) {
                remove_keys(dev, c, ent->size - var);
        }
@@ -280,23 +333,37 @@ static void __cache_work_func(struct mlx5_cache_ent *ent)
        struct mlx5_ib_dev *dev = ent->dev;
        struct mlx5_mr_cache *cache = &dev->cache;
        int i = order2idx(dev, ent->order);
+       int err;
 
        if (cache->stopped)
                return;
 
        ent = &dev->cache.ent[i];
-       if (ent->cur < 2 * ent->limit) {
-               add_keys(dev, i, 1);
-               if (ent->cur < 2 * ent->limit)
-                       queue_work(cache->wq, &ent->work);
+       if (ent->cur < 2 * ent->limit && !dev->fill_delay) {
+               err = add_keys(dev, i, 1);
+               if (ent->cur < 2 * ent->limit) {
+                       if (err == -EAGAIN) {
+                               mlx5_ib_dbg(dev, "returned eagain, order %d\n",
+                                           i + 2);
+                               queue_delayed_work(cache->wq, &ent->dwork,
+                                                  msecs_to_jiffies(3));
+                       } else if (err) {
+                               mlx5_ib_warn(dev, "command failed order %d, err %d\n",
+                                            i + 2, err);
+                               queue_delayed_work(cache->wq, &ent->dwork,
+                                                  msecs_to_jiffies(1000));
+                       } else {
+                               queue_work(cache->wq, &ent->work);
+                       }
+               }
        } else if (ent->cur > 2 * ent->limit) {
                if (!someone_adding(cache) &&
-                   time_after(jiffies, cache->last_add + 60 * HZ)) {
+                   time_after(jiffies, cache->last_add + 300 * HZ)) {
                        remove_keys(dev, i, 1);
                        if (ent->cur > ent->limit)
                                queue_work(cache->wq, &ent->work);
                } else {
-                       queue_delayed_work(cache->wq, &ent->dwork, 60 * HZ);
+                       queue_delayed_work(cache->wq, &ent->dwork, 300 * HZ);
                }
        }
 }
@@ -336,18 +403,18 @@ static struct mlx5_ib_mr *alloc_cached_mr(struct mlx5_ib_dev *dev, int order)
 
                mlx5_ib_dbg(dev, "order %d, cache index %d\n", ent->order, i);
 
-               spin_lock(&ent->lock);
+               spin_lock_irq(&ent->lock);
                if (!list_empty(&ent->head)) {
                        mr = list_first_entry(&ent->head, struct mlx5_ib_mr,
                                              list);
                        list_del(&mr->list);
                        ent->cur--;
-                       spin_unlock(&ent->lock);
+                       spin_unlock_irq(&ent->lock);
                        if (ent->cur < ent->limit)
                                queue_work(cache->wq, &ent->work);
                        break;
                }
-               spin_unlock(&ent->lock);
+               spin_unlock_irq(&ent->lock);
 
                queue_work(cache->wq, &ent->work);
 
@@ -374,12 +441,12 @@ static void free_cached_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
                return;
        }
        ent = &cache->ent[c];
-       spin_lock(&ent->lock);
+       spin_lock_irq(&ent->lock);
        list_add_tail(&mr->list, &ent->head);
        ent->cur++;
        if (ent->cur > 2 * ent->limit)
                shrink = 1;
-       spin_unlock(&ent->lock);
+       spin_unlock_irq(&ent->lock);
 
        if (shrink)
                queue_work(cache->wq, &ent->work);
@@ -394,16 +461,16 @@ static void clean_keys(struct mlx5_ib_dev *dev, int c)
 
        cancel_delayed_work(&ent->dwork);
        while (1) {
-               spin_lock(&ent->lock);
+               spin_lock_irq(&ent->lock);
                if (list_empty(&ent->head)) {
-                       spin_unlock(&ent->lock);
+                       spin_unlock_irq(&ent->lock);
                        return;
                }
                mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list);
                list_del(&mr->list);
                ent->cur--;
                ent->size--;
-               spin_unlock(&ent->lock);
+               spin_unlock_irq(&ent->lock);
                err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr);
                if (err)
                        mlx5_ib_warn(dev, "failed destroy mkey\n");
@@ -464,12 +531,18 @@ static void mlx5_mr_cache_debugfs_cleanup(struct mlx5_ib_dev *dev)
        debugfs_remove_recursive(dev->cache.root);
 }
 
+static void delay_time_func(unsigned long ctx)
+{
+       struct mlx5_ib_dev *dev = (struct mlx5_ib_dev *)ctx;
+
+       dev->fill_delay = 0;
+}
+
 int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
 {
        struct mlx5_mr_cache *cache = &dev->cache;
        struct mlx5_cache_ent *ent;
        int limit;
-       int size;
        int err;
        int i;
 
@@ -479,6 +552,7 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
                return -ENOMEM;
        }
 
+       setup_timer(&dev->delay_timer, delay_time_func, (unsigned long)dev);
        for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
                INIT_LIST_HEAD(&cache->ent[i].head);
                spin_lock_init(&cache->ent[i].lock);
@@ -489,13 +563,11 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
                ent->order = i + 2;
                ent->dev = dev;
 
-               if (dev->mdev.profile->mask & MLX5_PROF_MASK_MR_CACHE) {
-                       size = dev->mdev.profile->mr_cache[i].size;
+               if (dev->mdev.profile->mask & MLX5_PROF_MASK_MR_CACHE)
                        limit = dev->mdev.profile->mr_cache[i].limit;
-               } else {
-                       size = DEF_CACHE_SIZE;
+               else
                        limit = 0;
-               }
+
                INIT_WORK(&ent->work, cache_work_func);
                INIT_DELAYED_WORK(&ent->dwork, delayed_cache_work_func);
                ent->limit = limit;
@@ -522,6 +594,7 @@ int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
                clean_keys(dev, i);
 
        destroy_workqueue(dev->cache.wq);
+       del_timer_sync(&dev->delay_timer);
 
        return 0;
 }
@@ -551,7 +624,8 @@ struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc)
        seg->qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
        seg->start_addr = 0;
 
-       err = mlx5_core_create_mkey(mdev, &mr->mmr, in, sizeof(*in));
+       err = mlx5_core_create_mkey(mdev, &mr->mmr, in, sizeof(*in), NULL, NULL,
+                                   NULL);
        if (err)
                goto err_in;
 
@@ -660,14 +734,14 @@ static struct mlx5_ib_mr *reg_umr(struct ib_pd *pd, struct ib_umem *umem,
        int err;
        int i;
 
-       for (i = 0; i < 10; i++) {
+       for (i = 0; i < 1; i++) {
                mr = alloc_cached_mr(dev, order);
                if (mr)
                        break;
 
                err = add_keys(dev, order2idx(dev, order), 1);
-               if (err) {
-                       mlx5_ib_warn(dev, "add_keys failed\n");
+               if (err && err != -EAGAIN) {
+                       mlx5_ib_warn(dev, "add_keys failed, err %d\n", err);
                        break;
                }
        }
@@ -759,8 +833,10 @@ static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, u64 virt_addr,
        in->seg.xlt_oct_size = cpu_to_be32(get_octo_len(virt_addr, length, 1 << page_shift));
        in->seg.log2_page_size = page_shift;
        in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
-       in->xlat_oct_act_size = cpu_to_be32(get_octo_len(virt_addr, length, 1 << page_shift));
-       err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, inlen);
+       in->xlat_oct_act_size = cpu_to_be32(get_octo_len(virt_addr, length,
+                                                        1 << page_shift));
+       err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, inlen, NULL,
+                                   NULL, NULL);
        if (err) {
                mlx5_ib_warn(dev, "create mkey failed\n");
                goto err_2;
@@ -944,7 +1020,8 @@ struct ib_mr *mlx5_ib_alloc_fast_reg_mr(struct ib_pd *pd,
         * TBD not needed - issue 197292 */
        in->seg.log2_page_size = PAGE_SHIFT;
 
-       err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, sizeof(*in));
+       err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, sizeof(*in), NULL,
+                                   NULL, NULL);
        kfree(in);
        if (err)
                goto err_free;
index 5659ea8..7c6b4ba 100644 (file)
@@ -551,7 +551,7 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd,
        }
        mlx5_ib_populate_pas(dev, qp->umem, page_shift, (*in)->pas, 0);
        (*in)->ctx.log_pg_sz_remote_qpn =
-               cpu_to_be32((page_shift - PAGE_SHIFT) << 24);
+               cpu_to_be32((page_shift - MLX5_ADAPTER_PAGE_SHIFT) << 24);
        (*in)->ctx.params2 = cpu_to_be32(offset << 6);
 
        (*in)->ctx.qp_counter_set_usr_page = cpu_to_be32(uar_index);
@@ -648,7 +648,8 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev,
                goto err_buf;
        }
        (*in)->ctx.qp_counter_set_usr_page = cpu_to_be32(uar_index);
-       (*in)->ctx.log_pg_sz_remote_qpn = cpu_to_be32((qp->buf.page_shift - PAGE_SHIFT) << 24);
+       (*in)->ctx.log_pg_sz_remote_qpn =
+               cpu_to_be32((qp->buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT) << 24);
        /* Set "fast registration enabled" for all kernel QPs */
        (*in)->ctx.params1 |= cpu_to_be32(1 << 11);
        (*in)->ctx.sq_crq_size |= cpu_to_be16(1 << 4);
@@ -1317,9 +1318,11 @@ static enum mlx5_qp_optpar opt_mask[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE][MLX5_Q
                                          MLX5_QP_OPTPAR_RAE            |
                                          MLX5_QP_OPTPAR_RWE            |
                                          MLX5_QP_OPTPAR_RNR_TIMEOUT    |
-                                         MLX5_QP_OPTPAR_PM_STATE,
+                                         MLX5_QP_OPTPAR_PM_STATE       |
+                                         MLX5_QP_OPTPAR_ALT_ADDR_PATH,
                        [MLX5_QP_ST_UC] = MLX5_QP_OPTPAR_RWE            |
-                                         MLX5_QP_OPTPAR_PM_STATE,
+                                         MLX5_QP_OPTPAR_PM_STATE       |
+                                         MLX5_QP_OPTPAR_ALT_ADDR_PATH,
                        [MLX5_QP_ST_UD] = MLX5_QP_OPTPAR_Q_KEY          |
                                          MLX5_QP_OPTPAR_SRQN           |
                                          MLX5_QP_OPTPAR_CQN_RCV,
@@ -1550,7 +1553,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
        mlx5_cur = to_mlx5_state(cur_state);
        mlx5_new = to_mlx5_state(new_state);
        mlx5_st = to_mlx5_st(ibqp->qp_type);
-       if (mlx5_cur < 0 || mlx5_new < 0 || mlx5_st < 0)
+       if (mlx5_st < 0)
                goto out;
 
        optpar = ib_mask_to_mlx5_opt(attr_mask);
@@ -1744,6 +1747,7 @@ static void set_reg_umr_segment(struct mlx5_wqe_umr_ctrl_seg *umr,
                        MLX5_MKEY_MASK_PD               |
                        MLX5_MKEY_MASK_LR               |
                        MLX5_MKEY_MASK_LW               |
+                       MLX5_MKEY_MASK_KEY              |
                        MLX5_MKEY_MASK_RR               |
                        MLX5_MKEY_MASK_RW               |
                        MLX5_MKEY_MASK_A                |
@@ -1800,7 +1804,8 @@ static void set_reg_mkey_segment(struct mlx5_mkey_seg *seg, struct ib_send_wr *w
        seg->start_addr = cpu_to_be64(wr->wr.fast_reg.iova_start);
        seg->len = cpu_to_be64(wr->wr.fast_reg.length);
        seg->log2_page_size = wr->wr.fast_reg.page_shift;
-       seg->qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
+       seg->qpn_mkey7_0 = cpu_to_be32(0xffffff00 |
+                                      mlx5_mkey_variant(wr->wr.fast_reg.rkey));
 }
 
 static void set_frwr_pages(struct mlx5_wqe_data_seg *dseg,
@@ -1913,6 +1918,10 @@ static int set_frwr_li_wr(void **seg, struct ib_send_wr *wr, int *size,
        if (unlikely((*seg == qp->sq.qend)))
                *seg = mlx5_get_send_wqe(qp, 0);
        if (!li) {
+               if (unlikely(wr->wr.fast_reg.page_list_len >
+                            wr->wr.fast_reg.page_list->max_page_list_len))
+                       return  -ENOMEM;
+
                set_frwr_pages(*seg, wr, mdev, pd, writ);
                *seg += sizeof(struct mlx5_wqe_data_seg);
                *size += (sizeof(struct mlx5_wqe_data_seg) / 16);
index 0aa478b..210b3ea 100644 (file)
@@ -123,7 +123,7 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq,
                goto err_in;
        }
 
-       (*in)->ctx.log_pg_sz = page_shift - PAGE_SHIFT;
+       (*in)->ctx.log_pg_sz = page_shift - MLX5_ADAPTER_PAGE_SHIFT;
        (*in)->ctx.pgoff_cqn = cpu_to_be32(offset << 26);
 
        return 0;
@@ -192,7 +192,7 @@ static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq,
        }
        srq->wq_sig = !!srq_signature;
 
-       (*in)->ctx.log_pg_sz = page_shift - PAGE_SHIFT;
+       (*in)->ctx.log_pg_sz = page_shift - MLX5_ADAPTER_PAGE_SHIFT;
 
        return 0;
 
@@ -390,9 +390,7 @@ int mlx5_ib_destroy_srq(struct ib_srq *srq)
                mlx5_ib_db_unmap_user(to_mucontext(srq->uobject->context), &msrq->db);
                ib_umem_release(msrq->umem);
        } else {
-               kfree(msrq->wrid);
-               mlx5_buf_free(&dev->mdev, &msrq->buf);
-               mlx5_db_free(&dev->mdev, &msrq->db);
+               destroy_srq_kernel(dev, msrq);
        }
 
        kfree(srq);
index 5b53ca5..8308e36 100644 (file)
@@ -2834,7 +2834,7 @@ static int nes_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
        init_attr->qp_context = nesqp->ibqp.qp_context;
        init_attr->send_cq = nesqp->ibqp.send_cq;
        init_attr->recv_cq = nesqp->ibqp.recv_cq;
-       init_attr->srq = nesqp->ibqp.srq = nesqp->ibqp.srq;
+       init_attr->srq = nesqp->ibqp.srq;
        init_attr->cap = attr->cap;
 
        return 0;
index adc11d1..294dd27 100644 (file)
@@ -122,6 +122,32 @@ struct mqe_ctx {
        bool cmd_done;
 };
 
+struct ocrdma_hw_mr {
+       u32 lkey;
+       u8 fr_mr;
+       u8 remote_atomic;
+       u8 remote_rd;
+       u8 remote_wr;
+       u8 local_rd;
+       u8 local_wr;
+       u8 mw_bind;
+       u8 rsvd;
+       u64 len;
+       struct ocrdma_pbl *pbl_table;
+       u32 num_pbls;
+       u32 num_pbes;
+       u32 pbl_size;
+       u32 pbe_size;
+       u64 fbo;
+       u64 va;
+};
+
+struct ocrdma_mr {
+       struct ib_mr ibmr;
+       struct ib_umem *umem;
+       struct ocrdma_hw_mr hwmr;
+};
+
 struct ocrdma_dev {
        struct ib_device ibdev;
        struct ocrdma_dev_attr attr;
@@ -169,7 +195,7 @@ struct ocrdma_dev {
        struct list_head entry;
        struct rcu_head rcu;
        int id;
-       u64 stag_arr[OCRDMA_MAX_STAG];
+       struct ocrdma_mr *stag_arr[OCRDMA_MAX_STAG];
        u16 pvid;
 };
 
@@ -294,31 +320,6 @@ struct ocrdma_qp {
        u16 db_cache;
 };
 
-struct ocrdma_hw_mr {
-       u32 lkey;
-       u8 fr_mr;
-       u8 remote_atomic;
-       u8 remote_rd;
-       u8 remote_wr;
-       u8 local_rd;
-       u8 local_wr;
-       u8 mw_bind;
-       u8 rsvd;
-       u64 len;
-       struct ocrdma_pbl *pbl_table;
-       u32 num_pbls;
-       u32 num_pbes;
-       u32 pbl_size;
-       u32 pbe_size;
-       u64 fbo;
-       u64 va;
-};
-
-struct ocrdma_mr {
-       struct ib_mr ibmr;
-       struct ib_umem *umem;
-       struct ocrdma_hw_mr hwmr;
-};
 
 struct ocrdma_ucontext {
        struct ib_ucontext ibucontext;
index 50219ab..56bf32f 100644 (file)
@@ -1783,7 +1783,7 @@ static int ocrdma_set_create_qp_sq_cmd(struct ocrdma_create_qp_req *cmd,
        u32 max_sges = attrs->cap.max_send_sge;
 
        /* QP1 may exceed 127 */
-       max_wqe_allocated = min_t(int, attrs->cap.max_send_wr + 1,
+       max_wqe_allocated = min_t(u32, attrs->cap.max_send_wr + 1,
                                dev->attr.max_wqe);
 
        status = ocrdma_build_q_conf(&max_wqe_allocated,
index 0ce7674..91443bc 100644 (file)
@@ -452,9 +452,6 @@ static void ocrdma_remove_free(struct rcu_head *rcu)
 {
        struct ocrdma_dev *dev = container_of(rcu, struct ocrdma_dev, rcu);
 
-       ocrdma_free_resources(dev);
-       ocrdma_cleanup_hw(dev);
-
        idr_remove(&ocrdma_dev_id, dev->id);
        kfree(dev->mbx_cmd);
        ib_dealloc_device(&dev->ibdev);
@@ -470,6 +467,10 @@ static void ocrdma_remove(struct ocrdma_dev *dev)
        spin_lock(&ocrdma_devlist_lock);
        list_del_rcu(&dev->entry);
        spin_unlock(&ocrdma_devlist_lock);
+
+       ocrdma_free_resources(dev);
+       ocrdma_cleanup_hw(dev);
+
        call_rcu(&dev->rcu, ocrdma_remove_free);
 }
 
index 69f1d12..7686dce 100644 (file)
@@ -1981,9 +1981,7 @@ static int ocrdma_build_fr(struct ocrdma_qp *qp, struct ocrdma_hdr_wqe *hdr,
 
        wqe_size = roundup(wqe_size, OCRDMA_WQE_ALIGN_BYTES);
 
-       if ((wr->wr.fast_reg.page_list_len >
-               qp->dev->attr.max_pages_per_frmr) ||
-               (wr->wr.fast_reg.length > 0xffffffffULL))
+       if (wr->wr.fast_reg.page_list_len > qp->dev->attr.max_pages_per_frmr)
                return -EINVAL;
 
        hdr->cw |= (OCRDMA_FR_MR << OCRDMA_WQE_OPCODE_SHIFT);
@@ -2839,7 +2837,7 @@ struct ib_mr *ocrdma_alloc_frmr(struct ib_pd *ibpd, int max_page_list_len)
                goto mbx_err;
        mr->ibmr.rkey = mr->hwmr.lkey;
        mr->ibmr.lkey = mr->hwmr.lkey;
-       dev->stag_arr[(mr->hwmr.lkey >> 8) & (OCRDMA_MAX_STAG - 1)] = (unsigned long) mr;
+       dev->stag_arr[(mr->hwmr.lkey >> 8) & (OCRDMA_MAX_STAG - 1)] = mr;
        return &mr->ibmr;
 mbx_err:
        ocrdma_free_mr_pbl_tbl(dev, &mr->hwmr);
index 016e742..5bfc02f 100644 (file)
@@ -6190,21 +6190,20 @@ static int setup_txselect(const char *str, struct kernel_param *kp)
 {
        struct qib_devdata *dd;
        unsigned long val;
-       int ret;
-
+       char *n;
        if (strlen(str) >= MAX_ATTEN_LEN) {
                pr_info("txselect_values string too long\n");
                return -ENOSPC;
        }
-       ret = kstrtoul(str, 0, &val);
-       if (ret || val >= (TXDDS_TABLE_SZ + TXDDS_EXTRA_SZ +
+       val = simple_strtoul(str, &n, 0);
+       if (n == str || val >= (TXDDS_TABLE_SZ + TXDDS_EXTRA_SZ +
                                TXDDS_MFG_SZ)) {
                pr_info("txselect_values must start with a number < %d\n",
                        TXDDS_TABLE_SZ + TXDDS_EXTRA_SZ + TXDDS_MFG_SZ);
-               return ret ? ret : -EINVAL;
+               return -EINVAL;
        }
-
        strcpy(txselect_list, str);
+
        list_for_each_entry(dd, &qib_dev_list, list)
                if (dd->deviceid == PCI_DEVICE_ID_QLOGIC_IB_7322)
                        set_no_qsfp_atten(dd, 1);
index 28874f8..941d4d5 100644 (file)
@@ -54,7 +54,7 @@ struct ib_node_info {
        __be32 revision;
        u8 local_port_num;
        u8 vendor_id[3];
-} __attribute__ ((packed));
+} __packed;
 
 struct ib_mad_notice_attr {
        u8 generic_type;
@@ -73,7 +73,7 @@ struct ib_mad_notice_attr {
                        __be16  reserved;
                        __be16  lid;            /* where violation happened */
                        u8      port_num;       /* where violation happened */
-               } __attribute__ ((packed)) ntc_129_131;
+               } __packed ntc_129_131;
 
                struct {
                        __be16  reserved;
@@ -83,14 +83,14 @@ struct ib_mad_notice_attr {
                        __be32  new_cap_mask;   /* new capability mask */
                        u8      reserved3;
                        u8      change_flags;   /* low 3 bits only */
-               } __attribute__ ((packed)) ntc_144;
+               } __packed ntc_144;
 
                struct {
                        __be16  reserved;
                        __be16  lid;            /* lid where sys guid changed */
                        __be16  reserved2;
                        __be64  new_sys_guid;
-               } __attribute__ ((packed)) ntc_145;
+               } __packed ntc_145;
 
                struct {
                        __be16  reserved;
@@ -104,7 +104,7 @@ struct ib_mad_notice_attr {
                        u8      reserved3;
                        u8      dr_trunc_hop;
                        u8      dr_rtn_path[30];
-               } __attribute__ ((packed)) ntc_256;
+               } __packed ntc_256;
 
                struct {
                        __be16          reserved;
@@ -115,7 +115,7 @@ struct ib_mad_notice_attr {
                        __be32          qp2;    /* high 8 bits reserved */
                        union ib_gid    gid1;
                        union ib_gid    gid2;
-               } __attribute__ ((packed)) ntc_257_258;
+               } __packed ntc_257_258;
 
        } details;
 };
@@ -209,7 +209,7 @@ struct ib_pma_portcounters_cong {
        __be64 port_rcv_packets;
        __be64 port_xmit_wait;
        __be64 port_adr_events;
-} __attribute__ ((packed));
+} __packed;
 
 #define IB_PMA_CONG_HW_CONTROL_TIMER            0x00
 #define IB_PMA_CONG_HW_CONTROL_SAMPLE           0x01
index d0a0ea0..165aee2 100644 (file)
@@ -594,8 +594,7 @@ static int qib_user_sdma_pin_pages(const struct qib_devdata *dd,
                else
                        j = npages;
 
-               ret = get_user_pages(current, current->mm, addr,
-                            j, 0, 1, pages, NULL);
+               ret = get_user_pages_fast(addr, j, 0, pages);
                if (ret != j) {
                        i = 0;
                        j = ret;
@@ -1294,11 +1293,8 @@ int qib_user_sdma_writev(struct qib_ctxtdata *rcd,
                int mxp = 8;
                int ndesc = 0;
 
-               down_write(&current->mm->mmap_sem);
                ret = qib_user_sdma_queue_pkts(dd, ppd, pq,
                                iov, dim, &list, &mxp, &ndesc);
-               up_write(&current->mm->mmap_sem);
-
                if (ret < 0)
                        goto done_unlock;
                else {
index 012e2c7..a01c7d2 100644 (file)
@@ -150,14 +150,14 @@ struct ib_reth {
        __be64 vaddr;
        __be32 rkey;
        __be32 length;
-} __attribute__ ((packed));
+} __packed;
 
 struct ib_atomic_eth {
        __be32 vaddr[2];        /* unaligned so access as 2 32-bit words */
        __be32 rkey;
        __be64 swap_data;
        __be64 compare_data;
-} __attribute__ ((packed));
+} __packed;
 
 struct qib_other_headers {
        __be32 bth[3];
@@ -178,7 +178,7 @@ struct qib_other_headers {
                __be32 aeth;
                struct ib_atomic_eth atomic_eth;
        } u;
-} __attribute__ ((packed));
+} __packed;
 
 /*
  * Note that UD packets with a GRH header are 8+40+12+8 = 68 bytes
@@ -195,12 +195,12 @@ struct qib_ib_header {
                } l;
                struct qib_other_headers oth;
        } u;
-} __attribute__ ((packed));
+} __packed;
 
 struct qib_pio_header {
        __le32 pbc[2];
        struct qib_ib_header hdr;
-} __attribute__ ((packed));
+} __packed;
 
 /*
  * There is one struct qib_mcast for each multicast GID.
index eb71aaa..c639f90 100644 (file)
@@ -101,6 +101,7 @@ enum {
        IPOIB_MCAST_FLAG_SENDONLY = 1,
        IPOIB_MCAST_FLAG_BUSY     = 2,  /* joining or already joined */
        IPOIB_MCAST_FLAG_ATTACHED = 3,
+       IPOIB_MCAST_JOIN_STARTED  = 4,
 
        MAX_SEND_CQE              = 16,
        IPOIB_CM_COPYBREAK        = 256,
@@ -151,6 +152,7 @@ struct ipoib_mcast {
        struct sk_buff_head pkt_queue;
 
        struct net_device *dev;
+       struct completion done;
 };
 
 struct ipoib_rx_buf {
@@ -299,7 +301,7 @@ struct ipoib_dev_priv {
 
        unsigned long flags;
 
-       struct mutex vlan_mutex;
+       struct rw_semaphore vlan_rwsem;
 
        struct rb_root  path_tree;
        struct list_head path_list;
index 7a31754..1377f85 100644 (file)
@@ -140,7 +140,8 @@ static int ipoib_cm_post_receive_nonsrq(struct net_device *dev,
 static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev,
                                             struct ipoib_cm_rx_buf *rx_ring,
                                             int id, int frags,
-                                            u64 mapping[IPOIB_CM_RX_SG])
+                                            u64 mapping[IPOIB_CM_RX_SG],
+                                            gfp_t gfp)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
        struct sk_buff *skb;
@@ -164,7 +165,7 @@ static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev,
        }
 
        for (i = 0; i < frags; i++) {
-               struct page *page = alloc_page(GFP_ATOMIC);
+               struct page *page = alloc_page(gfp);
 
                if (!page)
                        goto partial_error;
@@ -382,7 +383,8 @@ static int ipoib_cm_nonsrq_init_rx(struct net_device *dev, struct ib_cm_id *cm_i
 
        for (i = 0; i < ipoib_recvq_size; ++i) {
                if (!ipoib_cm_alloc_rx_skb(dev, rx->rx_ring, i, IPOIB_CM_RX_SG - 1,
-                                          rx->rx_ring[i].mapping)) {
+                                          rx->rx_ring[i].mapping,
+                                          GFP_KERNEL)) {
                        ipoib_warn(priv, "failed to allocate receive buffer %d\n", i);
                                ret = -ENOMEM;
                                goto err_count;
@@ -639,7 +641,8 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
        frags = PAGE_ALIGN(wc->byte_len - min(wc->byte_len,
                                              (unsigned)IPOIB_CM_HEAD_SIZE)) / PAGE_SIZE;
 
-       newskb = ipoib_cm_alloc_rx_skb(dev, rx_ring, wr_id, frags, mapping);
+       newskb = ipoib_cm_alloc_rx_skb(dev, rx_ring, wr_id, frags,
+                                      mapping, GFP_ATOMIC);
        if (unlikely(!newskb)) {
                /*
                 * If we can't allocate a new RX buffer, dump
@@ -1556,7 +1559,8 @@ int ipoib_cm_dev_init(struct net_device *dev)
                for (i = 0; i < ipoib_recvq_size; ++i) {
                        if (!ipoib_cm_alloc_rx_skb(dev, priv->cm.srq_ring, i,
                                                   priv->cm.num_frags - 1,
-                                                  priv->cm.srq_ring[i].mapping)) {
+                                                  priv->cm.srq_ring[i].mapping,
+                                                  GFP_KERNEL)) {
                                ipoib_warn(priv, "failed to allocate "
                                           "receive buffer %d\n", i);
                                ipoib_cm_dev_cleanup(dev);
index 196b1d1..6a7003d 100644 (file)
@@ -685,15 +685,13 @@ int ipoib_ib_dev_open(struct net_device *dev)
        ret = ipoib_ib_post_receives(dev);
        if (ret) {
                ipoib_warn(priv, "ipoib_ib_post_receives returned %d\n", ret);
-               ipoib_ib_dev_stop(dev, 1);
-               return -1;
+               goto dev_stop;
        }
 
        ret = ipoib_cm_dev_open(dev);
        if (ret) {
                ipoib_warn(priv, "ipoib_cm_dev_open returned %d\n", ret);
-               ipoib_ib_dev_stop(dev, 1);
-               return -1;
+               goto dev_stop;
        }
 
        clear_bit(IPOIB_STOP_REAPER, &priv->flags);
@@ -704,6 +702,11 @@ int ipoib_ib_dev_open(struct net_device *dev)
                napi_enable(&priv->napi);
 
        return 0;
+dev_stop:
+       if (!test_and_set_bit(IPOIB_FLAG_INITIALIZED, &priv->flags))
+               napi_enable(&priv->napi);
+       ipoib_ib_dev_stop(dev, 1);
+       return -1;
 }
 
 static void ipoib_pkey_dev_check_presence(struct net_device *dev)
@@ -746,10 +749,8 @@ int ipoib_ib_dev_down(struct net_device *dev, int flush)
        if (!test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags)) {
                mutex_lock(&pkey_mutex);
                set_bit(IPOIB_PKEY_STOP, &priv->flags);
-               cancel_delayed_work(&priv->pkey_poll_task);
+               cancel_delayed_work_sync(&priv->pkey_poll_task);
                mutex_unlock(&pkey_mutex);
-               if (flush)
-                       flush_workqueue(ipoib_workqueue);
        }
 
        ipoib_mcast_stop_thread(dev, flush);
@@ -974,7 +975,7 @@ static void __ipoib_ib_dev_flush(struct ipoib_dev_priv *priv,
        u16 new_index;
        int result;
 
-       mutex_lock(&priv->vlan_mutex);
+       down_read(&priv->vlan_rwsem);
 
        /*
         * Flush any child interfaces too -- they might be up even if
@@ -983,7 +984,7 @@ static void __ipoib_ib_dev_flush(struct ipoib_dev_priv *priv,
        list_for_each_entry(cpriv, &priv->child_intfs, list)
                __ipoib_ib_dev_flush(cpriv, level);
 
-       mutex_unlock(&priv->vlan_mutex);
+       up_read(&priv->vlan_rwsem);
 
        if (!test_bit(IPOIB_FLAG_INITIALIZED, &priv->flags)) {
                /* for non-child devices must check/update the pkey value here */
@@ -1081,6 +1082,11 @@ void ipoib_ib_dev_cleanup(struct net_device *dev)
        struct ipoib_dev_priv *priv = netdev_priv(dev);
 
        ipoib_dbg(priv, "cleaning up ib_dev\n");
+       /*
+        * We must make sure there are no more (path) completions
+        * that may wish to touch priv fields that are no longer valid
+        */
+       ipoib_flush_paths(dev);
 
        ipoib_mcast_stop_thread(dev, 1);
        ipoib_mcast_dev_flush(dev);
index 82cec1a..d64ed05 100644 (file)
@@ -119,7 +119,7 @@ int ipoib_open(struct net_device *dev)
                struct ipoib_dev_priv *cpriv;
 
                /* Bring up any child interfaces too */
-               mutex_lock(&priv->vlan_mutex);
+               down_read(&priv->vlan_rwsem);
                list_for_each_entry(cpriv, &priv->child_intfs, list) {
                        int flags;
 
@@ -129,7 +129,7 @@ int ipoib_open(struct net_device *dev)
 
                        dev_change_flags(cpriv->dev, flags | IFF_UP);
                }
-               mutex_unlock(&priv->vlan_mutex);
+               up_read(&priv->vlan_rwsem);
        }
 
        netif_start_queue(dev);
@@ -162,7 +162,7 @@ static int ipoib_stop(struct net_device *dev)
                struct ipoib_dev_priv *cpriv;
 
                /* Bring down any child interfaces too */
-               mutex_lock(&priv->vlan_mutex);
+               down_read(&priv->vlan_rwsem);
                list_for_each_entry(cpriv, &priv->child_intfs, list) {
                        int flags;
 
@@ -172,7 +172,7 @@ static int ipoib_stop(struct net_device *dev)
 
                        dev_change_flags(cpriv->dev, flags & ~IFF_UP);
                }
-               mutex_unlock(&priv->vlan_mutex);
+               up_read(&priv->vlan_rwsem);
        }
 
        return 0;
@@ -1350,7 +1350,7 @@ void ipoib_setup(struct net_device *dev)
 
        ipoib_set_ethtool_ops(dev);
 
-       netif_napi_add(dev, &priv->napi, ipoib_poll, 100);
+       netif_napi_add(dev, &priv->napi, ipoib_poll, NAPI_POLL_WEIGHT);
 
        dev->watchdog_timeo      = HZ;
 
@@ -1372,7 +1372,7 @@ void ipoib_setup(struct net_device *dev)
 
        spin_lock_init(&priv->lock);
 
-       mutex_init(&priv->vlan_mutex);
+       init_rwsem(&priv->vlan_rwsem);
 
        INIT_LIST_HEAD(&priv->path_list);
        INIT_LIST_HEAD(&priv->child_intfs);
index cecb98a..d4e0057 100644 (file)
@@ -386,8 +386,10 @@ static int ipoib_mcast_join_complete(int status,
                        mcast->mcmember.mgid.raw, status);
 
        /* We trap for port events ourselves. */
-       if (status == -ENETRESET)
-               return 0;
+       if (status == -ENETRESET) {
+               status = 0;
+               goto out;
+       }
 
        if (!status)
                status = ipoib_mcast_join_finish(mcast, &multicast->rec);
@@ -407,7 +409,8 @@ static int ipoib_mcast_join_complete(int status,
                if (mcast == priv->broadcast)
                        queue_work(ipoib_workqueue, &priv->carrier_on_task);
 
-               return 0;
+               status = 0;
+               goto out;
        }
 
        if (mcast->logcount++ < 20) {
@@ -434,7 +437,8 @@ static int ipoib_mcast_join_complete(int status,
                                   mcast->backoff * HZ);
        spin_unlock_irq(&priv->lock);
        mutex_unlock(&mcast_mutex);
-
+out:
+       complete(&mcast->done);
        return status;
 }
 
@@ -484,11 +488,15 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast,
        }
 
        set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+       init_completion(&mcast->done);
+       set_bit(IPOIB_MCAST_JOIN_STARTED, &mcast->flags);
+
        mcast->mc = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port,
                                         &rec, comp_mask, GFP_KERNEL,
                                         ipoib_mcast_join_complete, mcast);
        if (IS_ERR(mcast->mc)) {
                clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+               complete(&mcast->done);
                ret = PTR_ERR(mcast->mc);
                ipoib_warn(priv, "ib_sa_join_multicast failed, status %d\n", ret);
 
@@ -510,10 +518,18 @@ void ipoib_mcast_join_task(struct work_struct *work)
        struct ipoib_dev_priv *priv =
                container_of(work, struct ipoib_dev_priv, mcast_task.work);
        struct net_device *dev = priv->dev;
+       struct ib_port_attr port_attr;
 
        if (!test_bit(IPOIB_MCAST_RUN, &priv->flags))
                return;
 
+       if (ib_query_port(priv->ca, priv->port, &port_attr) ||
+           port_attr.state != IB_PORT_ACTIVE) {
+               ipoib_dbg(priv, "port state is not ACTIVE (state = %d) suspending join task\n",
+                         port_attr.state);
+               return;
+       }
+
        if (ib_query_gid(priv->ca, priv->port, 0, &priv->local_gid))
                ipoib_warn(priv, "ib_query_gid() failed\n");
        else
@@ -751,6 +767,11 @@ void ipoib_mcast_dev_flush(struct net_device *dev)
 
        spin_unlock_irqrestore(&priv->lock, flags);
 
+       /* seperate between the wait to the leave*/
+       list_for_each_entry_safe(mcast, tmcast, &remove_list, list)
+               if (test_bit(IPOIB_MCAST_JOIN_STARTED, &mcast->flags))
+                       wait_for_completion(&mcast->done);
+
        list_for_each_entry_safe(mcast, tmcast, &remove_list, list) {
                ipoib_mcast_leave(dev, mcast);
                ipoib_mcast_free(mcast);
index f81abe1..c29b5c8 100644 (file)
@@ -142,10 +142,10 @@ static void ipoib_unregister_child_dev(struct net_device *dev, struct list_head
        priv = netdev_priv(dev);
        ppriv = netdev_priv(priv->parent);
 
-       mutex_lock(&ppriv->vlan_mutex);
+       down_write(&ppriv->vlan_rwsem);
        unregister_netdevice_queue(dev, head);
        list_del(&priv->list);
-       mutex_unlock(&ppriv->vlan_mutex);
+       up_write(&ppriv->vlan_rwsem);
 }
 
 static size_t ipoib_get_size(const struct net_device *dev)
index 8292554..9fad7b5 100644 (file)
@@ -140,7 +140,7 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
        if (!rtnl_trylock())
                return restart_syscall();
 
-       mutex_lock(&ppriv->vlan_mutex);
+       down_write(&ppriv->vlan_rwsem);
 
        /*
         * First ensure this isn't a duplicate. We check the parent device and
@@ -163,7 +163,7 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
        result = __ipoib_vlan_add(ppriv, priv, pkey, IPOIB_LEGACY_CHILD);
 
 out:
-       mutex_unlock(&ppriv->vlan_mutex);
+       up_write(&ppriv->vlan_rwsem);
 
        if (result)
                free_netdev(priv->dev);
@@ -185,7 +185,8 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
 
        if (!rtnl_trylock())
                return restart_syscall();
-       mutex_lock(&ppriv->vlan_mutex);
+
+       down_write(&ppriv->vlan_rwsem);
        list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {
                if (priv->pkey == pkey &&
                    priv->child_type == IPOIB_LEGACY_CHILD) {
@@ -195,7 +196,8 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
                        break;
                }
        }
-       mutex_unlock(&ppriv->vlan_mutex);
+       up_write(&ppriv->vlan_rwsem);
+
        rtnl_unlock();
 
        if (dev) {
index ce3fd32..02f9759 100644 (file)
@@ -1,5 +1,5 @@
 config INFINIBAND_ISERT
-       tristate "iSCSI Extentions for RDMA (iSER) target support"
+       tristate "iSCSI Extensions for RDMA (iSER) target support"
        depends on INET && INFINIBAND_ADDR_TRANS && TARGET_CORE && ISCSI_TARGET
        ---help---
-       Support for iSCSI Extentions for RDMA (iSER) Target on Infiniband fabrics.
+       Support for iSCSI Extensions for RDMA (iSER) Target on Infiniband fabrics.
index 6df2350..6be57c3 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/socket.h>
 #include <linux/in.h>
 #include <linux/in6.h>
+#include <linux/llist.h>
 #include <rdma/ib_verbs.h>
 #include <rdma/rdma_cm.h>
 #include <target/target_core_base.h>
@@ -489,6 +490,7 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
        kref_init(&isert_conn->conn_kref);
        kref_get(&isert_conn->conn_kref);
        mutex_init(&isert_conn->conn_mutex);
+       mutex_init(&isert_conn->conn_comp_mutex);
        spin_lock_init(&isert_conn->conn_lock);
 
        cma_id->context = isert_conn;
@@ -843,14 +845,32 @@ isert_init_tx_hdrs(struct isert_conn *isert_conn,
 }
 
 static void
-isert_init_send_wr(struct isert_cmd *isert_cmd, struct ib_send_wr *send_wr)
+isert_init_send_wr(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd,
+                  struct ib_send_wr *send_wr, bool coalesce)
 {
+       struct iser_tx_desc *tx_desc = &isert_cmd->tx_desc;
+
        isert_cmd->rdma_wr.iser_ib_op = ISER_IB_SEND;
        send_wr->wr_id = (unsigned long)&isert_cmd->tx_desc;
        send_wr->opcode = IB_WR_SEND;
-       send_wr->send_flags = IB_SEND_SIGNALED;
-       send_wr->sg_list = &isert_cmd->tx_desc.tx_sg[0];
+       send_wr->sg_list = &tx_desc->tx_sg[0];
        send_wr->num_sge = isert_cmd->tx_desc.num_sge;
+       /*
+        * Coalesce send completion interrupts by only setting IB_SEND_SIGNALED
+        * bit for every ISERT_COMP_BATCH_COUNT number of ib_post_send() calls.
+        */
+       mutex_lock(&isert_conn->conn_comp_mutex);
+       if (coalesce &&
+           ++isert_conn->conn_comp_batch < ISERT_COMP_BATCH_COUNT) {
+               llist_add(&tx_desc->comp_llnode, &isert_conn->conn_comp_llist);
+               mutex_unlock(&isert_conn->conn_comp_mutex);
+               return;
+       }
+       isert_conn->conn_comp_batch = 0;
+       tx_desc->comp_llnode_batch = llist_del_all(&isert_conn->conn_comp_llist);
+       mutex_unlock(&isert_conn->conn_comp_mutex);
+
+       send_wr->send_flags = IB_SEND_SIGNALED;
 }
 
 static int
@@ -1582,8 +1602,8 @@ isert_response_completion(struct iser_tx_desc *tx_desc,
 }
 
 static void
-isert_send_completion(struct iser_tx_desc *tx_desc,
-                     struct isert_conn *isert_conn)
+__isert_send_completion(struct iser_tx_desc *tx_desc,
+                       struct isert_conn *isert_conn)
 {
        struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
        struct isert_cmd *isert_cmd = tx_desc->isert_cmd;
@@ -1624,6 +1644,24 @@ isert_send_completion(struct iser_tx_desc *tx_desc,
 }
 
 static void
+isert_send_completion(struct iser_tx_desc *tx_desc,
+                     struct isert_conn *isert_conn)
+{
+       struct llist_node *llnode = tx_desc->comp_llnode_batch;
+       struct iser_tx_desc *t;
+       /*
+        * Drain coalesced completion llist starting from comp_llnode_batch
+        * setup in isert_init_send_wr(), and then complete trailing tx_desc.
+        */
+       while (llnode) {
+               t = llist_entry(llnode, struct iser_tx_desc, comp_llnode);
+               llnode = llist_next(llnode);
+               __isert_send_completion(t, isert_conn);
+       }
+       __isert_send_completion(tx_desc, isert_conn);
+}
+
+static void
 isert_cq_comp_err(struct iser_tx_desc *tx_desc, struct isert_conn *isert_conn)
 {
        struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
@@ -1793,7 +1831,7 @@ isert_put_response(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
                isert_cmd->tx_desc.num_sge = 2;
        }
 
-       isert_init_send_wr(isert_cmd, send_wr);
+       isert_init_send_wr(isert_conn, isert_cmd, send_wr, true);
 
        pr_debug("Posting SCSI Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n");
 
@@ -1813,7 +1851,7 @@ isert_put_nopin(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
                               &isert_cmd->tx_desc.iscsi_header,
                               nopout_response);
        isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc);
-       isert_init_send_wr(isert_cmd, send_wr);
+       isert_init_send_wr(isert_conn, isert_cmd, send_wr, false);
 
        pr_debug("Posting NOPIN Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n");
 
@@ -1831,7 +1869,7 @@ isert_put_logout_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
        iscsit_build_logout_rsp(cmd, conn, (struct iscsi_logout_rsp *)
                                &isert_cmd->tx_desc.iscsi_header);
        isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc);
-       isert_init_send_wr(isert_cmd, send_wr);
+       isert_init_send_wr(isert_conn, isert_cmd, send_wr, false);
 
        pr_debug("Posting Logout Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n");
 
@@ -1849,7 +1887,7 @@ isert_put_tm_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
        iscsit_build_task_mgt_rsp(cmd, conn, (struct iscsi_tm_rsp *)
                                  &isert_cmd->tx_desc.iscsi_header);
        isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc);
-       isert_init_send_wr(isert_cmd, send_wr);
+       isert_init_send_wr(isert_conn, isert_cmd, send_wr, false);
 
        pr_debug("Posting Task Management Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n");
 
@@ -1881,7 +1919,7 @@ isert_put_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
        tx_dsg->lkey    = isert_conn->conn_mr->lkey;
        isert_cmd->tx_desc.num_sge = 2;
 
-       isert_init_send_wr(isert_cmd, send_wr);
+       isert_init_send_wr(isert_conn, isert_cmd, send_wr, false);
 
        pr_debug("Posting Reject IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n");
 
@@ -1921,7 +1959,7 @@ isert_put_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
                tx_dsg->lkey    = isert_conn->conn_mr->lkey;
                isert_cmd->tx_desc.num_sge = 2;
        }
-       isert_init_send_wr(isert_cmd, send_wr);
+       isert_init_send_wr(isert_conn, isert_cmd, send_wr, false);
 
        pr_debug("Posting Text Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n");
 
@@ -1991,8 +2029,6 @@ isert_map_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
 
        if (wr->iser_ib_op == ISER_IB_RDMA_WRITE) {
                data_left = se_cmd->data_length;
-               iscsit_increment_maxcmdsn(cmd, conn->sess);
-               cmd->stat_sn = conn->stat_sn++;
        } else {
                sg_off = cmd->write_data_done / PAGE_SIZE;
                data_left = se_cmd->data_length - cmd->write_data_done;
@@ -2204,8 +2240,6 @@ isert_reg_rdma_frwr(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
 
        if (wr->iser_ib_op == ISER_IB_RDMA_WRITE) {
                data_left = se_cmd->data_length;
-               iscsit_increment_maxcmdsn(cmd, conn->sess);
-               cmd->stat_sn = conn->stat_sn++;
        } else {
                sg_off = cmd->write_data_done / PAGE_SIZE;
                data_left = se_cmd->data_length - cmd->write_data_done;
@@ -2259,18 +2293,26 @@ isert_reg_rdma_frwr(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
        data_len = min(data_left, rdma_write_max);
        wr->cur_rdma_length = data_len;
 
-       spin_lock_irqsave(&isert_conn->conn_lock, flags);
-       fr_desc = list_first_entry(&isert_conn->conn_frwr_pool,
-                                  struct fast_reg_descriptor, list);
-       list_del(&fr_desc->list);
-       spin_unlock_irqrestore(&isert_conn->conn_lock, flags);
-       wr->fr_desc = fr_desc;
+       /* if there is a single dma entry, dma mr is sufficient */
+       if (count == 1) {
+               ib_sge->addr = ib_sg_dma_address(ib_dev, &sg_start[0]);
+               ib_sge->length = ib_sg_dma_len(ib_dev, &sg_start[0]);
+               ib_sge->lkey = isert_conn->conn_mr->lkey;
+               wr->fr_desc = NULL;
+       } else {
+               spin_lock_irqsave(&isert_conn->conn_lock, flags);
+               fr_desc = list_first_entry(&isert_conn->conn_frwr_pool,
+                                          struct fast_reg_descriptor, list);
+               list_del(&fr_desc->list);
+               spin_unlock_irqrestore(&isert_conn->conn_lock, flags);
+               wr->fr_desc = fr_desc;
 
-       ret = isert_fast_reg_mr(fr_desc, isert_cmd, isert_conn,
-                         ib_sge, offset, data_len);
-       if (ret) {
-               list_add_tail(&fr_desc->list, &isert_conn->conn_frwr_pool);
-               goto unmap_sg;
+               ret = isert_fast_reg_mr(fr_desc, isert_cmd, isert_conn,
+                                 ib_sge, offset, data_len);
+               if (ret) {
+                       list_add_tail(&fr_desc->list, &isert_conn->conn_frwr_pool);
+                       goto unmap_sg;
+               }
        }
 
        return 0;
@@ -2306,10 +2348,11 @@ isert_put_datain(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
         * Build isert_conn->tx_desc for iSCSI response PDU and attach
         */
        isert_create_send_desc(isert_conn, isert_cmd, &isert_cmd->tx_desc);
-       iscsit_build_rsp_pdu(cmd, conn, false, (struct iscsi_scsi_rsp *)
+       iscsit_build_rsp_pdu(cmd, conn, true, (struct iscsi_scsi_rsp *)
                             &isert_cmd->tx_desc.iscsi_header);
        isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc);
-       isert_init_send_wr(isert_cmd, &isert_cmd->tx_desc.send_wr);
+       isert_init_send_wr(isert_conn, isert_cmd,
+                          &isert_cmd->tx_desc.send_wr, true);
 
        atomic_inc(&isert_conn->post_send_buf_count);
 
index 631f209..691f90f 100644 (file)
@@ -43,6 +43,8 @@ struct iser_tx_desc {
        struct ib_sge   tx_sg[2];
        int             num_sge;
        struct isert_cmd *isert_cmd;
+       struct llist_node *comp_llnode_batch;
+       struct llist_node comp_llnode;
        struct ib_send_wr send_wr;
 } __packed;
 
@@ -121,6 +123,10 @@ struct isert_conn {
        int                     conn_frwr_pool_size;
        /* lock to protect frwr_pool */
        spinlock_t              conn_lock;
+#define ISERT_COMP_BATCH_COUNT 8
+       int                     conn_comp_batch;
+       struct llist_head       conn_comp_llist;
+       struct mutex            conn_comp_mutex;
 };
 
 #define ISERT_MAX_CQ 64
index f93baf8..a886319 100644 (file)
@@ -46,6 +46,7 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_dbg.h>
+#include <scsi/scsi_tcq.h>
 #include <scsi/srp.h>
 #include <scsi/scsi_transport_srp.h>
 
@@ -86,6 +87,32 @@ module_param(topspin_workarounds, int, 0444);
 MODULE_PARM_DESC(topspin_workarounds,
                 "Enable workarounds for Topspin/Cisco SRP target bugs if != 0");
 
+static struct kernel_param_ops srp_tmo_ops;
+
+static int srp_reconnect_delay = 10;
+module_param_cb(reconnect_delay, &srp_tmo_ops, &srp_reconnect_delay,
+               S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(reconnect_delay, "Time between successive reconnect attempts");
+
+static int srp_fast_io_fail_tmo = 15;
+module_param_cb(fast_io_fail_tmo, &srp_tmo_ops, &srp_fast_io_fail_tmo,
+               S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(fast_io_fail_tmo,
+                "Number of seconds between the observation of a transport"
+                " layer error and failing all I/O. \"off\" means that this"
+                " functionality is disabled.");
+
+static int srp_dev_loss_tmo = 600;
+module_param_cb(dev_loss_tmo, &srp_tmo_ops, &srp_dev_loss_tmo,
+               S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dev_loss_tmo,
+                "Maximum number of seconds that the SRP transport should"
+                " insulate transport layer errors. After this time has been"
+                " exceeded the SCSI host is removed. Should be"
+                " between 1 and " __stringify(SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
+                " if fast_io_fail_tmo has not been set. \"off\" means that"
+                " this functionality is disabled.");
+
 static void srp_add_one(struct ib_device *device);
 static void srp_remove_one(struct ib_device *device);
 static void srp_recv_completion(struct ib_cq *cq, void *target_ptr);
@@ -102,6 +129,48 @@ static struct ib_client srp_client = {
 
 static struct ib_sa_client srp_sa_client;
 
+static int srp_tmo_get(char *buffer, const struct kernel_param *kp)
+{
+       int tmo = *(int *)kp->arg;
+
+       if (tmo >= 0)
+               return sprintf(buffer, "%d", tmo);
+       else
+               return sprintf(buffer, "off");
+}
+
+static int srp_tmo_set(const char *val, const struct kernel_param *kp)
+{
+       int tmo, res;
+
+       if (strncmp(val, "off", 3) != 0) {
+               res = kstrtoint(val, 0, &tmo);
+               if (res)
+                       goto out;
+       } else {
+               tmo = -1;
+       }
+       if (kp->arg == &srp_reconnect_delay)
+               res = srp_tmo_valid(tmo, srp_fast_io_fail_tmo,
+                                   srp_dev_loss_tmo);
+       else if (kp->arg == &srp_fast_io_fail_tmo)
+               res = srp_tmo_valid(srp_reconnect_delay, tmo, srp_dev_loss_tmo);
+       else
+               res = srp_tmo_valid(srp_reconnect_delay, srp_fast_io_fail_tmo,
+                                   tmo);
+       if (res)
+               goto out;
+       *(int *)kp->arg = tmo;
+
+out:
+       return res;
+}
+
+static struct kernel_param_ops srp_tmo_ops = {
+       .get = srp_tmo_get,
+       .set = srp_tmo_set,
+};
+
 static inline struct srp_target_port *host_to_target(struct Scsi_Host *host)
 {
        return (struct srp_target_port *) host->hostdata;
@@ -231,16 +300,16 @@ static int srp_create_target_ib(struct srp_target_port *target)
                return -ENOMEM;
 
        recv_cq = ib_create_cq(target->srp_host->srp_dev->dev,
-                              srp_recv_completion, NULL, target, SRP_RQ_SIZE,
-                              target->comp_vector);
+                              srp_recv_completion, NULL, target,
+                              target->queue_size, target->comp_vector);
        if (IS_ERR(recv_cq)) {
                ret = PTR_ERR(recv_cq);
                goto err;
        }
 
        send_cq = ib_create_cq(target->srp_host->srp_dev->dev,
-                              srp_send_completion, NULL, target, SRP_SQ_SIZE,
-                              target->comp_vector);
+                              srp_send_completion, NULL, target,
+                              target->queue_size, target->comp_vector);
        if (IS_ERR(send_cq)) {
                ret = PTR_ERR(send_cq);
                goto err_recv_cq;
@@ -249,8 +318,8 @@ static int srp_create_target_ib(struct srp_target_port *target)
        ib_req_notify_cq(recv_cq, IB_CQ_NEXT_COMP);
 
        init_attr->event_handler       = srp_qp_event;
-       init_attr->cap.max_send_wr     = SRP_SQ_SIZE;
-       init_attr->cap.max_recv_wr     = SRP_RQ_SIZE;
+       init_attr->cap.max_send_wr     = target->queue_size;
+       init_attr->cap.max_recv_wr     = target->queue_size;
        init_attr->cap.max_recv_sge    = 1;
        init_attr->cap.max_send_sge    = 1;
        init_attr->sq_sig_type         = IB_SIGNAL_ALL_WR;
@@ -296,6 +365,10 @@ err:
        return ret;
 }
 
+/*
+ * Note: this function may be called without srp_alloc_iu_bufs() having been
+ * invoked. Hence the target->[rt]x_ring checks.
+ */
 static void srp_free_target_ib(struct srp_target_port *target)
 {
        int i;
@@ -307,10 +380,18 @@ static void srp_free_target_ib(struct srp_target_port *target)
        target->qp = NULL;
        target->send_cq = target->recv_cq = NULL;
 
-       for (i = 0; i < SRP_RQ_SIZE; ++i)
-               srp_free_iu(target->srp_host, target->rx_ring[i]);
-       for (i = 0; i < SRP_SQ_SIZE; ++i)
-               srp_free_iu(target->srp_host, target->tx_ring[i]);
+       if (target->rx_ring) {
+               for (i = 0; i < target->queue_size; ++i)
+                       srp_free_iu(target->srp_host, target->rx_ring[i]);
+               kfree(target->rx_ring);
+               target->rx_ring = NULL;
+       }
+       if (target->tx_ring) {
+               for (i = 0; i < target->queue_size; ++i)
+                       srp_free_iu(target->srp_host, target->tx_ring[i]);
+               kfree(target->tx_ring);
+               target->tx_ring = NULL;
+       }
 }
 
 static void srp_path_rec_completion(int status,
@@ -390,7 +471,7 @@ static int srp_send_req(struct srp_target_port *target)
        req->param.responder_resources        = 4;
        req->param.remote_cm_response_timeout = 20;
        req->param.local_cm_response_timeout  = 20;
-       req->param.retry_count                = 7;
+       req->param.retry_count                = target->tl_retry_count;
        req->param.rnr_retry_count            = 7;
        req->param.max_cm_retries             = 15;
 
@@ -496,7 +577,11 @@ static void srp_free_req_data(struct srp_target_port *target)
        struct srp_request *req;
        int i;
 
-       for (i = 0, req = target->req_ring; i < SRP_CMD_SQ_SIZE; ++i, ++req) {
+       if (!target->req_ring)
+               return;
+
+       for (i = 0; i < target->req_ring_size; ++i) {
+               req = &target->req_ring[i];
                kfree(req->fmr_list);
                kfree(req->map_page);
                if (req->indirect_dma_addr) {
@@ -506,6 +591,50 @@ static void srp_free_req_data(struct srp_target_port *target)
                }
                kfree(req->indirect_desc);
        }
+
+       kfree(target->req_ring);
+       target->req_ring = NULL;
+}
+
+static int srp_alloc_req_data(struct srp_target_port *target)
+{
+       struct srp_device *srp_dev = target->srp_host->srp_dev;
+       struct ib_device *ibdev = srp_dev->dev;
+       struct srp_request *req;
+       dma_addr_t dma_addr;
+       int i, ret = -ENOMEM;
+
+       INIT_LIST_HEAD(&target->free_reqs);
+
+       target->req_ring = kzalloc(target->req_ring_size *
+                                  sizeof(*target->req_ring), GFP_KERNEL);
+       if (!target->req_ring)
+               goto out;
+
+       for (i = 0; i < target->req_ring_size; ++i) {
+               req = &target->req_ring[i];
+               req->fmr_list = kmalloc(target->cmd_sg_cnt * sizeof(void *),
+                                       GFP_KERNEL);
+               req->map_page = kmalloc(SRP_FMR_SIZE * sizeof(void *),
+                                       GFP_KERNEL);
+               req->indirect_desc = kmalloc(target->indirect_size, GFP_KERNEL);
+               if (!req->fmr_list || !req->map_page || !req->indirect_desc)
+                       goto out;
+
+               dma_addr = ib_dma_map_single(ibdev, req->indirect_desc,
+                                            target->indirect_size,
+                                            DMA_TO_DEVICE);
+               if (ib_dma_mapping_error(ibdev, dma_addr))
+                       goto out;
+
+               req->indirect_dma_addr = dma_addr;
+               req->index = i;
+               list_add_tail(&req->list, &target->free_reqs);
+       }
+       ret = 0;
+
+out:
+       return ret;
 }
 
 /**
@@ -528,12 +657,20 @@ static void srp_remove_target(struct srp_target_port *target)
        WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED);
 
        srp_del_scsi_host_attr(target->scsi_host);
+       srp_rport_get(target->rport);
        srp_remove_host(target->scsi_host);
        scsi_remove_host(target->scsi_host);
        srp_disconnect_target(target);
        ib_destroy_cm_id(target->cm_id);
        srp_free_target_ib(target);
+       cancel_work_sync(&target->tl_err_work);
+       srp_rport_put(target->rport);
        srp_free_req_data(target);
+
+       spin_lock(&target->srp_host->target_lock);
+       list_del(&target->list);
+       spin_unlock(&target->srp_host->target_lock);
+
        scsi_host_put(target->scsi_host);
 }
 
@@ -545,10 +682,6 @@ static void srp_remove_work(struct work_struct *work)
        WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED);
 
        srp_remove_target(target);
-
-       spin_lock(&target->srp_host->target_lock);
-       list_del(&target->list);
-       spin_unlock(&target->srp_host->target_lock);
 }
 
 static void srp_rport_delete(struct srp_rport *rport)
@@ -686,23 +819,42 @@ static void srp_free_req(struct srp_target_port *target,
        spin_unlock_irqrestore(&target->lock, flags);
 }
 
-static void srp_reset_req(struct srp_target_port *target, struct srp_request *req)
+static void srp_finish_req(struct srp_target_port *target,
+                          struct srp_request *req, int result)
 {
        struct scsi_cmnd *scmnd = srp_claim_req(target, req, NULL);
 
        if (scmnd) {
                srp_free_req(target, req, scmnd, 0);
-               scmnd->result = DID_RESET << 16;
+               scmnd->result = result;
                scmnd->scsi_done(scmnd);
        }
 }
 
-static int srp_reconnect_target(struct srp_target_port *target)
+static void srp_terminate_io(struct srp_rport *rport)
 {
-       struct Scsi_Host *shost = target->scsi_host;
-       int i, ret;
+       struct srp_target_port *target = rport->lld_data;
+       int i;
 
-       scsi_target_block(&shost->shost_gendev);
+       for (i = 0; i < target->req_ring_size; ++i) {
+               struct srp_request *req = &target->req_ring[i];
+               srp_finish_req(target, req, DID_TRANSPORT_FAILFAST << 16);
+       }
+}
+
+/*
+ * It is up to the caller to ensure that srp_rport_reconnect() calls are
+ * serialized and that no concurrent srp_queuecommand(), srp_abort(),
+ * srp_reset_device() or srp_reset_host() calls will occur while this function
+ * is in progress. One way to realize that is not to call this function
+ * directly but to call srp_reconnect_rport() instead since that last function
+ * serializes calls of this function via rport->mutex and also blocks
+ * srp_queuecommand() calls before invoking this function.
+ */
+static int srp_rport_reconnect(struct srp_rport *rport)
+{
+       struct srp_target_port *target = rport->lld_data;
+       int i, ret;
 
        srp_disconnect_target(target);
        /*
@@ -721,41 +873,21 @@ static int srp_reconnect_target(struct srp_target_port *target)
        else
                srp_create_target_ib(target);
 
-       for (i = 0; i < SRP_CMD_SQ_SIZE; ++i) {
+       for (i = 0; i < target->req_ring_size; ++i) {
                struct srp_request *req = &target->req_ring[i];
-               if (req->scmnd)
-                       srp_reset_req(target, req);
+               srp_finish_req(target, req, DID_RESET << 16);
        }
 
        INIT_LIST_HEAD(&target->free_tx);
-       for (i = 0; i < SRP_SQ_SIZE; ++i)
+       for (i = 0; i < target->queue_size; ++i)
                list_add(&target->tx_ring[i]->list, &target->free_tx);
 
        if (ret == 0)
                ret = srp_connect_target(target);
 
-       scsi_target_unblock(&shost->shost_gendev, ret == 0 ? SDEV_RUNNING :
-                           SDEV_TRANSPORT_OFFLINE);
-       target->transport_offline = !!ret;
-
-       if (ret)
-               goto err;
-
-       shost_printk(KERN_INFO, target->scsi_host, PFX "reconnect succeeded\n");
-
-       return ret;
-
-err:
-       shost_printk(KERN_ERR, target->scsi_host,
-                    PFX "reconnect failed (%d), removing target port.\n", ret);
-
-       /*
-        * We couldn't reconnect, so kill our target port off.
-        * However, we have to defer the real removal because we
-        * are in the context of the SCSI error handler now, which
-        * will deadlock if we call scsi_remove_host().
-        */
-       srp_queue_remove_work(target);
+       if (ret == 0)
+               shost_printk(KERN_INFO, target->scsi_host,
+                            PFX "reconnect succeeded\n");
 
        return ret;
 }
@@ -1302,15 +1434,30 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
                             PFX "Recv failed with error code %d\n", res);
 }
 
-static void srp_handle_qp_err(enum ib_wc_status wc_status,
-                             enum ib_wc_opcode wc_opcode,
+/**
+ * srp_tl_err_work() - handle a transport layer error
+ *
+ * Note: This function may get invoked before the rport has been created,
+ * hence the target->rport test.
+ */
+static void srp_tl_err_work(struct work_struct *work)
+{
+       struct srp_target_port *target;
+
+       target = container_of(work, struct srp_target_port, tl_err_work);
+       if (target->rport)
+               srp_start_tl_fail_timers(target->rport);
+}
+
+static void srp_handle_qp_err(enum ib_wc_status wc_status, bool send_err,
                              struct srp_target_port *target)
 {
        if (target->connected && !target->qp_in_error) {
                shost_printk(KERN_ERR, target->scsi_host,
                             PFX "failed %s status %d\n",
-                            wc_opcode & IB_WC_RECV ? "receive" : "send",
+                            send_err ? "send" : "receive",
                             wc_status);
+               queue_work(system_long_wq, &target->tl_err_work);
        }
        target->qp_in_error = true;
 }
@@ -1325,7 +1472,7 @@ static void srp_recv_completion(struct ib_cq *cq, void *target_ptr)
                if (likely(wc.status == IB_WC_SUCCESS)) {
                        srp_handle_recv(target, &wc);
                } else {
-                       srp_handle_qp_err(wc.status, wc.opcode, target);
+                       srp_handle_qp_err(wc.status, false, target);
                }
        }
 }
@@ -1341,7 +1488,7 @@ static void srp_send_completion(struct ib_cq *cq, void *target_ptr)
                        iu = (struct srp_iu *) (uintptr_t) wc.wr_id;
                        list_add(&iu->list, &target->free_tx);
                } else {
-                       srp_handle_qp_err(wc.status, wc.opcode, target);
+                       srp_handle_qp_err(wc.status, true, target);
                }
        }
 }
@@ -1349,17 +1496,29 @@ static void srp_send_completion(struct ib_cq *cq, void *target_ptr)
 static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd)
 {
        struct srp_target_port *target = host_to_target(shost);
+       struct srp_rport *rport = target->rport;
        struct srp_request *req;
        struct srp_iu *iu;
        struct srp_cmd *cmd;
        struct ib_device *dev;
        unsigned long flags;
-       int len;
+       int len, result;
+       const bool in_scsi_eh = !in_interrupt() && current == shost->ehandler;
+
+       /*
+        * The SCSI EH thread is the only context from which srp_queuecommand()
+        * can get invoked for blocked devices (SDEV_BLOCK /
+        * SDEV_CREATED_BLOCK). Avoid racing with srp_reconnect_rport() by
+        * locking the rport mutex if invoked from inside the SCSI EH.
+        */
+       if (in_scsi_eh)
+               mutex_lock(&rport->mutex);
 
-       if (unlikely(target->transport_offline)) {
-               scmnd->result = DID_NO_CONNECT << 16;
+       result = srp_chkready(target->rport);
+       if (unlikely(result)) {
+               scmnd->result = result;
                scmnd->scsi_done(scmnd);
-               return 0;
+               goto unlock_rport;
        }
 
        spin_lock_irqsave(&target->lock, flags);
@@ -1404,6 +1563,10 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd)
                goto err_unmap;
        }
 
+unlock_rport:
+       if (in_scsi_eh)
+               mutex_unlock(&rport->mutex);
+
        return 0;
 
 err_unmap:
@@ -1418,14 +1581,30 @@ err_iu:
 err_unlock:
        spin_unlock_irqrestore(&target->lock, flags);
 
+       if (in_scsi_eh)
+               mutex_unlock(&rport->mutex);
+
        return SCSI_MLQUEUE_HOST_BUSY;
 }
 
+/*
+ * Note: the resources allocated in this function are freed in
+ * srp_free_target_ib().
+ */
 static int srp_alloc_iu_bufs(struct srp_target_port *target)
 {
        int i;
 
-       for (i = 0; i < SRP_RQ_SIZE; ++i) {
+       target->rx_ring = kzalloc(target->queue_size * sizeof(*target->rx_ring),
+                                 GFP_KERNEL);
+       if (!target->rx_ring)
+               goto err_no_ring;
+       target->tx_ring = kzalloc(target->queue_size * sizeof(*target->tx_ring),
+                                 GFP_KERNEL);
+       if (!target->tx_ring)
+               goto err_no_ring;
+
+       for (i = 0; i < target->queue_size; ++i) {
                target->rx_ring[i] = srp_alloc_iu(target->srp_host,
                                                  target->max_ti_iu_len,
                                                  GFP_KERNEL, DMA_FROM_DEVICE);
@@ -1433,7 +1612,7 @@ static int srp_alloc_iu_bufs(struct srp_target_port *target)
                        goto err;
        }
 
-       for (i = 0; i < SRP_SQ_SIZE; ++i) {
+       for (i = 0; i < target->queue_size; ++i) {
                target->tx_ring[i] = srp_alloc_iu(target->srp_host,
                                                  target->max_iu_len,
                                                  GFP_KERNEL, DMA_TO_DEVICE);
@@ -1446,16 +1625,18 @@ static int srp_alloc_iu_bufs(struct srp_target_port *target)
        return 0;
 
 err:
-       for (i = 0; i < SRP_RQ_SIZE; ++i) {
+       for (i = 0; i < target->queue_size; ++i) {
                srp_free_iu(target->srp_host, target->rx_ring[i]);
-               target->rx_ring[i] = NULL;
-       }
-
-       for (i = 0; i < SRP_SQ_SIZE; ++i) {
                srp_free_iu(target->srp_host, target->tx_ring[i]);
-               target->tx_ring[i] = NULL;
        }
 
+
+err_no_ring:
+       kfree(target->tx_ring);
+       target->tx_ring = NULL;
+       kfree(target->rx_ring);
+       target->rx_ring = NULL;
+
        return -ENOMEM;
 }
 
@@ -1506,6 +1687,9 @@ static void srp_cm_rep_handler(struct ib_cm_id *cm_id,
                target->scsi_host->can_queue
                        = min(target->req_lim - SRP_TSK_MGMT_SQ_SIZE,
                              target->scsi_host->can_queue);
+               target->scsi_host->cmd_per_lun
+                       = min_t(int, target->scsi_host->can_queue,
+                               target->scsi_host->cmd_per_lun);
        } else {
                shost_printk(KERN_WARNING, target->scsi_host,
                             PFX "Unhandled RSP opcode %#x\n", lrsp->opcode);
@@ -1513,7 +1697,7 @@ static void srp_cm_rep_handler(struct ib_cm_id *cm_id,
                goto error;
        }
 
-       if (!target->rx_ring[0]) {
+       if (!target->rx_ring) {
                ret = srp_alloc_iu_bufs(target);
                if (ret)
                        goto error;
@@ -1533,7 +1717,7 @@ static void srp_cm_rep_handler(struct ib_cm_id *cm_id,
        if (ret)
                goto error_free;
 
-       for (i = 0; i < SRP_RQ_SIZE; i++) {
+       for (i = 0; i < target->queue_size; i++) {
                struct srp_iu *iu = target->rx_ring[i];
                ret = srp_post_recv(target, iu);
                if (ret)
@@ -1672,6 +1856,7 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
                if (ib_send_cm_drep(cm_id, NULL, 0))
                        shost_printk(KERN_ERR, target->scsi_host,
                                     PFX "Sending CM DREP failed\n");
+               queue_work(system_long_wq, &target->tl_err_work);
                break;
 
        case IB_CM_TIMEWAIT_EXIT:
@@ -1698,9 +1883,61 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
        return 0;
 }
 
+/**
+ * srp_change_queue_type - changing device queue tag type
+ * @sdev: scsi device struct
+ * @tag_type: requested tag type
+ *
+ * Returns queue tag type.
+ */
+static int
+srp_change_queue_type(struct scsi_device *sdev, int tag_type)
+{
+       if (sdev->tagged_supported) {
+               scsi_set_tag_type(sdev, tag_type);
+               if (tag_type)
+                       scsi_activate_tcq(sdev, sdev->queue_depth);
+               else
+                       scsi_deactivate_tcq(sdev, sdev->queue_depth);
+       } else
+               tag_type = 0;
+
+       return tag_type;
+}
+
+/**
+ * srp_change_queue_depth - setting device queue depth
+ * @sdev: scsi device struct
+ * @qdepth: requested queue depth
+ * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP
+ * (see include/scsi/scsi_host.h for definition)
+ *
+ * Returns queue depth.
+ */
+static int
+srp_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
+{
+       struct Scsi_Host *shost = sdev->host;
+       int max_depth;
+       if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) {
+               max_depth = shost->can_queue;
+               if (!sdev->tagged_supported)
+                       max_depth = 1;
+               if (qdepth > max_depth)
+                       qdepth = max_depth;
+               scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+       } else if (reason == SCSI_QDEPTH_QFULL)
+               scsi_track_queue_full(sdev, qdepth);
+       else
+               return -EOPNOTSUPP;
+
+       return sdev->queue_depth;
+}
+
 static int srp_send_tsk_mgmt(struct srp_target_port *target,
                             u64 req_tag, unsigned int lun, u8 func)
 {
+       struct srp_rport *rport = target->rport;
        struct ib_device *dev = target->srp_host->srp_dev->dev;
        struct srp_iu *iu;
        struct srp_tsk_mgmt *tsk_mgmt;
@@ -1710,12 +1947,20 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target,
 
        init_completion(&target->tsk_mgmt_done);
 
+       /*
+        * Lock the rport mutex to avoid that srp_create_target_ib() is
+        * invoked while a task management function is being sent.
+        */
+       mutex_lock(&rport->mutex);
        spin_lock_irq(&target->lock);
        iu = __srp_get_tx_iu(target, SRP_IU_TSK_MGMT);
        spin_unlock_irq(&target->lock);
 
-       if (!iu)
+       if (!iu) {
+               mutex_unlock(&rport->mutex);
+
                return -1;
+       }
 
        ib_dma_sync_single_for_cpu(dev, iu->dma, sizeof *tsk_mgmt,
                                   DMA_TO_DEVICE);
@@ -1732,8 +1977,11 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target,
                                      DMA_TO_DEVICE);
        if (srp_post_send(target, iu, sizeof *tsk_mgmt)) {
                srp_put_tx_iu(target, iu, SRP_IU_TSK_MGMT);
+               mutex_unlock(&rport->mutex);
+
                return -1;
        }
+       mutex_unlock(&rport->mutex);
 
        if (!wait_for_completion_timeout(&target->tsk_mgmt_done,
                                         msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS)))
@@ -1751,11 +1999,11 @@ static int srp_abort(struct scsi_cmnd *scmnd)
        shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n");
 
        if (!req || !srp_claim_req(target, req, scmnd))
-               return FAILED;
+               return SUCCESS;
        if (srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
                              SRP_TSK_ABORT_TASK) == 0)
                ret = SUCCESS;
-       else if (target->transport_offline)
+       else if (target->rport->state == SRP_RPORT_LOST)
                ret = FAST_IO_FAIL;
        else
                ret = FAILED;
@@ -1779,10 +2027,10 @@ static int srp_reset_device(struct scsi_cmnd *scmnd)
        if (target->tsk_mgmt_status)
                return FAILED;
 
-       for (i = 0; i < SRP_CMD_SQ_SIZE; ++i) {
+       for (i = 0; i < target->req_ring_size; ++i) {
                struct srp_request *req = &target->req_ring[i];
                if (req->scmnd && req->scmnd->device == scmnd->device)
-                       srp_reset_req(target, req);
+                       srp_finish_req(target, req, DID_RESET << 16);
        }
 
        return SUCCESS;
@@ -1791,14 +2039,10 @@ static int srp_reset_device(struct scsi_cmnd *scmnd)
 static int srp_reset_host(struct scsi_cmnd *scmnd)
 {
        struct srp_target_port *target = host_to_target(scmnd->device->host);
-       int ret = FAILED;
 
        shost_printk(KERN_ERR, target->scsi_host, PFX "SRP reset_host called\n");
 
-       if (!srp_reconnect_target(target))
-               ret = SUCCESS;
-
-       return ret;
+       return srp_reconnect_rport(target->rport) == 0 ? SUCCESS : FAILED;
 }
 
 static int srp_slave_configure(struct scsi_device *sdev)
@@ -1851,6 +2095,14 @@ static ssize_t show_pkey(struct device *dev, struct device_attribute *attr,
        return sprintf(buf, "0x%04x\n", be16_to_cpu(target->path.pkey));
 }
 
+static ssize_t show_sgid(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct srp_target_port *target = host_to_target(class_to_shost(dev));
+
+       return sprintf(buf, "%pI6\n", target->path.sgid.raw);
+}
+
 static ssize_t show_dgid(struct device *dev, struct device_attribute *attr,
                         char *buf)
 {
@@ -1907,6 +2159,14 @@ static ssize_t show_comp_vector(struct device *dev,
        return sprintf(buf, "%d\n", target->comp_vector);
 }
 
+static ssize_t show_tl_retry_count(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct srp_target_port *target = host_to_target(class_to_shost(dev));
+
+       return sprintf(buf, "%d\n", target->tl_retry_count);
+}
+
 static ssize_t show_cmd_sg_entries(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
@@ -1927,6 +2187,7 @@ static DEVICE_ATTR(id_ext,            S_IRUGO, show_id_ext,          NULL);
 static DEVICE_ATTR(ioc_guid,       S_IRUGO, show_ioc_guid,        NULL);
 static DEVICE_ATTR(service_id,     S_IRUGO, show_service_id,      NULL);
 static DEVICE_ATTR(pkey,           S_IRUGO, show_pkey,            NULL);
+static DEVICE_ATTR(sgid,           S_IRUGO, show_sgid,            NULL);
 static DEVICE_ATTR(dgid,           S_IRUGO, show_dgid,            NULL);
 static DEVICE_ATTR(orig_dgid,      S_IRUGO, show_orig_dgid,       NULL);
 static DEVICE_ATTR(req_lim,         S_IRUGO, show_req_lim,         NULL);
@@ -1934,6 +2195,7 @@ static DEVICE_ATTR(zero_req_lim,    S_IRUGO, show_zero_req_lim,      NULL);
 static DEVICE_ATTR(local_ib_port,   S_IRUGO, show_local_ib_port,   NULL);
 static DEVICE_ATTR(local_ib_device, S_IRUGO, show_local_ib_device, NULL);
 static DEVICE_ATTR(comp_vector,     S_IRUGO, show_comp_vector,     NULL);
+static DEVICE_ATTR(tl_retry_count,  S_IRUGO, show_tl_retry_count,  NULL);
 static DEVICE_ATTR(cmd_sg_entries,  S_IRUGO, show_cmd_sg_entries,  NULL);
 static DEVICE_ATTR(allow_ext_sg,    S_IRUGO, show_allow_ext_sg,    NULL);
 
@@ -1942,6 +2204,7 @@ static struct device_attribute *srp_host_attrs[] = {
        &dev_attr_ioc_guid,
        &dev_attr_service_id,
        &dev_attr_pkey,
+       &dev_attr_sgid,
        &dev_attr_dgid,
        &dev_attr_orig_dgid,
        &dev_attr_req_lim,
@@ -1949,6 +2212,7 @@ static struct device_attribute *srp_host_attrs[] = {
        &dev_attr_local_ib_port,
        &dev_attr_local_ib_device,
        &dev_attr_comp_vector,
+       &dev_attr_tl_retry_count,
        &dev_attr_cmd_sg_entries,
        &dev_attr_allow_ext_sg,
        NULL
@@ -1961,14 +2225,16 @@ static struct scsi_host_template srp_template = {
        .slave_configure                = srp_slave_configure,
        .info                           = srp_target_info,
        .queuecommand                   = srp_queuecommand,
+       .change_queue_depth             = srp_change_queue_depth,
+       .change_queue_type              = srp_change_queue_type,
        .eh_abort_handler               = srp_abort,
        .eh_device_reset_handler        = srp_reset_device,
        .eh_host_reset_handler          = srp_reset_host,
        .skip_settle_delay              = true,
        .sg_tablesize                   = SRP_DEF_SG_TABLESIZE,
-       .can_queue                      = SRP_CMD_SQ_SIZE,
+       .can_queue                      = SRP_DEFAULT_CMD_SQ_SIZE,
        .this_id                        = -1,
-       .cmd_per_lun                    = SRP_CMD_SQ_SIZE,
+       .cmd_per_lun                    = SRP_DEFAULT_CMD_SQ_SIZE,
        .use_clustering                 = ENABLE_CLUSTERING,
        .shost_attrs                    = srp_host_attrs
 };
@@ -1994,6 +2260,7 @@ static int srp_add_target(struct srp_host *host, struct srp_target_port *target)
        }
 
        rport->lld_data = target;
+       target->rport = rport;
 
        spin_lock(&host->target_lock);
        list_add_tail(&target->list, &host->target_list);
@@ -2073,6 +2340,8 @@ enum {
        SRP_OPT_ALLOW_EXT_SG    = 1 << 10,
        SRP_OPT_SG_TABLESIZE    = 1 << 11,
        SRP_OPT_COMP_VECTOR     = 1 << 12,
+       SRP_OPT_TL_RETRY_COUNT  = 1 << 13,
+       SRP_OPT_QUEUE_SIZE      = 1 << 14,
        SRP_OPT_ALL             = (SRP_OPT_ID_EXT       |
                                   SRP_OPT_IOC_GUID     |
                                   SRP_OPT_DGID         |
@@ -2094,6 +2363,8 @@ static const match_table_t srp_opt_tokens = {
        { SRP_OPT_ALLOW_EXT_SG,         "allow_ext_sg=%u"       },
        { SRP_OPT_SG_TABLESIZE,         "sg_tablesize=%u"       },
        { SRP_OPT_COMP_VECTOR,          "comp_vector=%u"        },
+       { SRP_OPT_TL_RETRY_COUNT,       "tl_retry_count=%u"     },
+       { SRP_OPT_QUEUE_SIZE,           "queue_size=%d"         },
        { SRP_OPT_ERR,                  NULL                    }
 };
 
@@ -2188,13 +2459,25 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target)
                        target->scsi_host->max_sectors = token;
                        break;
 
+               case SRP_OPT_QUEUE_SIZE:
+                       if (match_int(args, &token) || token < 1) {
+                               pr_warn("bad queue_size parameter '%s'\n", p);
+                               goto out;
+                       }
+                       target->scsi_host->can_queue = token;
+                       target->queue_size = token + SRP_RSP_SQ_SIZE +
+                                            SRP_TSK_MGMT_SQ_SIZE;
+                       if (!(opt_mask & SRP_OPT_MAX_CMD_PER_LUN))
+                               target->scsi_host->cmd_per_lun = token;
+                       break;
+
                case SRP_OPT_MAX_CMD_PER_LUN:
-                       if (match_int(args, &token)) {
+                       if (match_int(args, &token) || token < 1) {
                                pr_warn("bad max cmd_per_lun parameter '%s'\n",
                                        p);
                                goto out;
                        }
-                       target->scsi_host->cmd_per_lun = min(token, SRP_CMD_SQ_SIZE);
+                       target->scsi_host->cmd_per_lun = token;
                        break;
 
                case SRP_OPT_IO_CLASS:
@@ -2257,6 +2540,15 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target)
                        target->comp_vector = token;
                        break;
 
+               case SRP_OPT_TL_RETRY_COUNT:
+                       if (match_int(args, &token) || token < 2 || token > 7) {
+                               pr_warn("bad tl_retry_count parameter '%s' (must be a number between 2 and 7)\n",
+                                       p);
+                               goto out;
+                       }
+                       target->tl_retry_count = token;
+                       break;
+
                default:
                        pr_warn("unknown parameter or missing value '%s' in target creation request\n",
                                p);
@@ -2273,6 +2565,12 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target)
                                pr_warn("target creation request is missing parameter '%s'\n",
                                        srp_opt_tokens[i].pattern);
 
+       if (target->scsi_host->cmd_per_lun > target->scsi_host->can_queue
+           && (opt_mask & SRP_OPT_MAX_CMD_PER_LUN))
+               pr_warn("cmd_per_lun = %d > queue_size = %d\n",
+                       target->scsi_host->cmd_per_lun,
+                       target->scsi_host->can_queue);
+
 out:
        kfree(options);
        return ret;
@@ -2287,8 +2585,7 @@ static ssize_t srp_create_target(struct device *dev,
        struct Scsi_Host *target_host;
        struct srp_target_port *target;
        struct ib_device *ibdev = host->srp_dev->dev;
-       dma_addr_t dma_addr;
-       int i, ret;
+       int ret;
 
        target_host = scsi_host_alloc(&srp_template,
                                      sizeof (struct srp_target_port));
@@ -2311,11 +2608,15 @@ static ssize_t srp_create_target(struct device *dev,
        target->cmd_sg_cnt      = cmd_sg_entries;
        target->sg_tablesize    = indirect_sg_entries ? : cmd_sg_entries;
        target->allow_ext_sg    = allow_ext_sg;
+       target->tl_retry_count  = 7;
+       target->queue_size      = SRP_DEFAULT_QUEUE_SIZE;
 
        ret = srp_parse_options(buf, target);
        if (ret)
                goto err;
 
+       target->req_ring_size = target->queue_size - SRP_TSK_MGMT_SQ_SIZE;
+
        if (!srp_conn_unique(target->srp_host, target)) {
                shost_printk(KERN_INFO, target->scsi_host,
                             PFX "Already connected to target port with id_ext=%016llx;ioc_guid=%016llx;initiator_ext=%016llx\n",
@@ -2339,31 +2640,13 @@ static ssize_t srp_create_target(struct device *dev,
                             sizeof (struct srp_indirect_buf) +
                             target->cmd_sg_cnt * sizeof (struct srp_direct_buf);
 
+       INIT_WORK(&target->tl_err_work, srp_tl_err_work);
        INIT_WORK(&target->remove_work, srp_remove_work);
        spin_lock_init(&target->lock);
        INIT_LIST_HEAD(&target->free_tx);
-       INIT_LIST_HEAD(&target->free_reqs);
-       for (i = 0; i < SRP_CMD_SQ_SIZE; ++i) {
-               struct srp_request *req = &target->req_ring[i];
-
-               req->fmr_list = kmalloc(target->cmd_sg_cnt * sizeof (void *),
-                                       GFP_KERNEL);
-               req->map_page = kmalloc(SRP_FMR_SIZE * sizeof (void *),
-                                       GFP_KERNEL);
-               req->indirect_desc = kmalloc(target->indirect_size, GFP_KERNEL);
-               if (!req->fmr_list || !req->map_page || !req->indirect_desc)
-                       goto err_free_mem;
-
-               dma_addr = ib_dma_map_single(ibdev, req->indirect_desc,
-                                            target->indirect_size,
-                                            DMA_TO_DEVICE);
-               if (ib_dma_mapping_error(ibdev, dma_addr))
-                       goto err_free_mem;
-
-               req->indirect_dma_addr = dma_addr;
-               req->index = i;
-               list_add_tail(&req->list, &target->free_reqs);
-       }
+       ret = srp_alloc_req_data(target);
+       if (ret)
+               goto err_free_mem;
 
        ib_query_gid(ibdev, host->port, 0, &target->path.sgid);
 
@@ -2612,7 +2895,14 @@ static void srp_remove_one(struct ib_device *device)
 }
 
 static struct srp_function_template ib_srp_transport_functions = {
+       .has_rport_state         = true,
+       .reset_timer_if_blocked  = true,
+       .reconnect_delay         = &srp_reconnect_delay,
+       .fast_io_fail_tmo        = &srp_fast_io_fail_tmo,
+       .dev_loss_tmo            = &srp_dev_loss_tmo,
+       .reconnect               = srp_rport_reconnect,
        .rport_delete            = srp_rport_delete,
+       .terminate_rport_io      = srp_terminate_io,
 };
 
 static int __init srp_init_module(void)
index e641088..5756810 100644 (file)
@@ -57,14 +57,11 @@ enum {
        SRP_MAX_LUN             = 512,
        SRP_DEF_SG_TABLESIZE    = 12,
 
-       SRP_RQ_SHIFT            = 6,
-       SRP_RQ_SIZE             = 1 << SRP_RQ_SHIFT,
-
-       SRP_SQ_SIZE             = SRP_RQ_SIZE,
+       SRP_DEFAULT_QUEUE_SIZE  = 1 << 6,
        SRP_RSP_SQ_SIZE         = 1,
-       SRP_REQ_SQ_SIZE         = SRP_SQ_SIZE - SRP_RSP_SQ_SIZE,
        SRP_TSK_MGMT_SQ_SIZE    = 1,
-       SRP_CMD_SQ_SIZE         = SRP_REQ_SQ_SIZE - SRP_TSK_MGMT_SQ_SIZE,
+       SRP_DEFAULT_CMD_SQ_SIZE = SRP_DEFAULT_QUEUE_SIZE - SRP_RSP_SQ_SIZE -
+                                 SRP_TSK_MGMT_SQ_SIZE,
 
        SRP_TAG_NO_REQ          = ~0U,
        SRP_TAG_TSK_MGMT        = 1U << 31,
@@ -140,7 +137,6 @@ struct srp_target_port {
        unsigned int            cmd_sg_cnt;
        unsigned int            indirect_size;
        bool                    allow_ext_sg;
-       bool                    transport_offline;
 
        /* Everything above this point is used in the hot path of
         * command processing. Try to keep them packed into cachelines.
@@ -153,10 +149,14 @@ struct srp_target_port {
        u16                     io_class;
        struct srp_host        *srp_host;
        struct Scsi_Host       *scsi_host;
+       struct srp_rport       *rport;
        char                    target_name[32];
        unsigned int            scsi_id;
        unsigned int            sg_tablesize;
+       int                     queue_size;
+       int                     req_ring_size;
        int                     comp_vector;
+       int                     tl_retry_count;
 
        struct ib_sa_path_rec   path;
        __be16                  orig_dgid[8];
@@ -172,10 +172,11 @@ struct srp_target_port {
 
        int                     zero_req_lim;
 
-       struct srp_iu          *tx_ring[SRP_SQ_SIZE];
-       struct srp_iu          *rx_ring[SRP_RQ_SIZE];
-       struct srp_request      req_ring[SRP_CMD_SQ_SIZE];
+       struct srp_iu          **tx_ring;
+       struct srp_iu          **rx_ring;
+       struct srp_request      *req_ring;
 
+       struct work_struct      tl_err_work;
        struct work_struct      remove_work;
 
        struct list_head        list;
index 6c923c7..520a7e5 100644 (file)
@@ -1352,11 +1352,8 @@ static int srpt_abort_cmd(struct srpt_send_ioctx *ioctx)
 
                /* XXX(hch): this is a horrible layering violation.. */
                spin_lock_irqsave(&ioctx->cmd.t_state_lock, flags);
-               ioctx->cmd.transport_state |= CMD_T_LUN_STOP;
                ioctx->cmd.transport_state &= ~CMD_T_ACTIVE;
                spin_unlock_irqrestore(&ioctx->cmd.t_state_lock, flags);
-
-               complete(&ioctx->cmd.transport_lun_stop_comp);
                break;
        case SRPT_STATE_CMD_RSP_SENT:
                /*
@@ -1364,9 +1361,6 @@ static int srpt_abort_cmd(struct srpt_send_ioctx *ioctx)
                 * not been received in time.
                 */
                srpt_unmap_sg_to_ib_sge(ioctx->ch, ioctx);
-               spin_lock_irqsave(&ioctx->cmd.t_state_lock, flags);
-               ioctx->cmd.transport_state |= CMD_T_LUN_STOP;
-               spin_unlock_irqrestore(&ioctx->cmd.t_state_lock, flags);
                target_put_sess_cmd(ioctx->ch->sess, &ioctx->cmd);
                break;
        case SRPT_STATE_MGMT_RSP_SENT:
@@ -1476,7 +1470,6 @@ static void srpt_handle_rdma_err_comp(struct srpt_rdma_ch *ch,
 {
        struct se_cmd *cmd;
        enum srpt_command_state state;
-       unsigned long flags;
 
        cmd = &ioctx->cmd;
        state = srpt_get_cmd_state(ioctx);
@@ -1496,9 +1489,6 @@ static void srpt_handle_rdma_err_comp(struct srpt_rdma_ch *ch,
                               __func__, __LINE__, state);
                break;
        case SRPT_RDMA_WRITE_LAST:
-               spin_lock_irqsave(&ioctx->cmd.t_state_lock, flags);
-               ioctx->cmd.transport_state |= CMD_T_LUN_STOP;
-               spin_unlock_irqrestore(&ioctx->cmd.t_state_lock, flags);
                break;
        default:
                printk(KERN_ERR "%s[%d]: opcode = %u\n", __func__,
index 38b523a..a11ff74 100644 (file)
@@ -80,7 +80,7 @@ config INPUT_MATRIXKMAP
 comment "Userland interfaces"
 
 config INPUT_MOUSEDEV
-       tristate "Mouse interface" if EXPERT
+       tristate "Mouse interface"
        default y
        help
          Say Y here if you want your mouse to be accessible as char devices
index b6ded17..a06e125 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/poll.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/input/mt.h>
@@ -369,7 +371,11 @@ static int evdev_release(struct inode *inode, struct file *file)
        mutex_unlock(&evdev->mutex);
 
        evdev_detach_client(evdev, client);
-       kfree(client);
+
+       if (is_vmalloc_addr(client))
+               vfree(client);
+       else
+               kfree(client);
 
        evdev_close_device(evdev);
 
@@ -389,12 +395,14 @@ static int evdev_open(struct inode *inode, struct file *file)
 {
        struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
        unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
+       unsigned int size = sizeof(struct evdev_client) +
+                                       bufsize * sizeof(struct input_event);
        struct evdev_client *client;
        int error;
 
-       client = kzalloc(sizeof(struct evdev_client) +
-                               bufsize * sizeof(struct input_event),
-                        GFP_KERNEL);
+       client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
+       if (!client)
+               client = vzalloc(size);
        if (!client)
                return -ENOMEM;
 
index e75d015..846ccdd 100644 (file)
@@ -2052,7 +2052,7 @@ int input_register_device(struct input_dev *dev)
        if (dev->hint_events_per_packet < packet_size)
                dev->hint_events_per_packet = packet_size;
 
-       dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2;
+       dev->max_vals = dev->hint_events_per_packet + 2;
        dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
        if (!dev->vals) {
                error = -ENOMEM;
index c1edd39..bb174c1 100644 (file)
@@ -2,7 +2,7 @@
 # Input core configuration
 #
 menuconfig INPUT_KEYBOARD
-       bool "Keyboards" if EXPERT || !X86
+       bool "Keyboards"
        default y
        help
          Say Y here, and a list of supported keyboards will be displayed.
@@ -67,7 +67,7 @@ config KEYBOARD_ATARI
          module will be called atakbd.
 
 config KEYBOARD_ATKBD
-       tristate "AT keyboard" if EXPERT || !X86
+       tristate "AT keyboard"
        default y
        select SERIO
        select SERIO_LIBPS2
@@ -525,7 +525,7 @@ config KEYBOARD_SUNKBD
 
 config KEYBOARD_SH_KEYSC
        tristate "SuperH KEYSC keypad support"
-       depends on SUPERH || ARCH_SHMOBILE
+       depends on SUPERH || ARM || COMPILE_TEST
        help
          Say Y here if you want to use a keypad attached to the KEYSC block
          on SuperH processors such as sh7722 and sh7343.
index 440ce32..2db1324 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/gpio_keys.h>
 #include <linux/workqueue.h>
 #include <linux/gpio.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
 #include <linux/spinlock.h>
index cd5ed9e..4e42819 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
 #include <linux/gpio_keys.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
 
index 4218143..8b1b013 100644 (file)
@@ -383,7 +383,7 @@ static struct platform_driver lpc32xx_kscan_driver = {
                .name   = DRV_NAME,
                .owner  = THIS_MODULE,
                .pm     = &lpc32xx_kscan_pm_ops,
-               .of_match_table = of_match_ptr(lpc32xx_kscan_match),
+               .of_match_table = lpc32xx_kscan_match,
        }
 };
 
index b3e3eda..b310649 100644 (file)
@@ -143,8 +143,10 @@ static int nspire_keypad_open(struct input_dev *input)
                return error;
 
        error = nspire_keypad_chip_init(keypad);
-       if (error)
+       if (error) {
+               clk_disable_unprepare(keypad->clk);
                return error;
+       }
 
        return 0;
 }
@@ -267,7 +269,7 @@ static struct platform_driver nspire_keypad_driver = {
        .driver = {
                .name = "nspire-keypad",
                .owner = THIS_MODULE,
-               .of_match_table = of_match_ptr(nspire_keypad_dt_match),
+               .of_match_table = nspire_keypad_dt_match,
        },
        .probe = nspire_keypad_probe,
 };
index a2e758d..186138c 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/err.h>
 #include <linux/input/matrix_keypad.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index 9cd20e6..8508879 100644 (file)
@@ -614,7 +614,7 @@ static int tegra_kbc_probe(struct platform_device *pdev)
        unsigned int keymap_rows;
        const struct of_device_id *match;
 
-       match = of_match_device(of_match_ptr(tegra_kbc_of_match), &pdev->dev);
+       match = of_match_device(tegra_kbc_of_match, &pdev->dev);
 
        kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL);
        if (!kbc) {
index 5f7b427..8bd24d5 100644 (file)
@@ -60,8 +60,8 @@ struct keypad_data {
        struct clk                      *clk;
        struct device                   *dev;
        spinlock_t                      lock;
-       u32                             irq_press;
-       u32                             irq_release;
+       int                             irq_press;
+       int                             irq_release;
        int                             rows, cols, row_shift;
        int                             debounce_ms, active_low;
        u32                             prev_keys[3];
index aa51baa..5f4967d 100644 (file)
@@ -156,7 +156,7 @@ config INPUT_MAX8925_ONKEY
 
 config INPUT_MAX8997_HAPTIC
        tristate "MAXIM MAX8997 haptic controller support"
-       depends on HAVE_PWM && MFD_MAX8997
+       depends on PWM && HAVE_PWM && MFD_MAX8997
        select INPUT_FF_MEMLESS
        help
          This option enables device driver support for the haptic controller
@@ -461,7 +461,7 @@ config INPUT_PCF8574
 
 config INPUT_PWM_BEEPER
        tristate "PWM beeper support"
-       depends on HAVE_PWM || PWM
+       depends on PWM && HAVE_PWM
        help
          Say Y here to get support for PWM based beeper devices.
 
index 6189148..3a90b71 100644 (file)
@@ -108,7 +108,6 @@ static int ad714x_spi_remove(struct spi_device *spi)
        struct ad714x_chip *chip = spi_get_drvdata(spi);
 
        ad714x_remove(chip);
-       spi_set_drvdata(spi, NULL);
 
        return 0;
 }
index 4f77f87..b5d71d2 100644 (file)
@@ -131,7 +131,6 @@ static int cobalt_buttons_probe(struct platform_device *pdev)
  err_free_mem:
        input_free_polled_device(poll_dev);
        kfree(bdev);
-       dev_set_drvdata(&pdev->dev, NULL);
        return error;
 }
 
@@ -144,7 +143,6 @@ static int cobalt_buttons_remove(struct platform_device *pdev)
        input_free_polled_device(bdev->poll_dev);
        iounmap(bdev->reg);
        kfree(bdev);
-       dev_set_drvdata(dev, NULL);
 
        return 0;
 }
index f330969..59d4dcd 100644 (file)
@@ -168,7 +168,7 @@ static void mma8450_close(struct input_polled_dev *dev)
  * I2C init/probing/exit functions
  */
 static int mma8450_probe(struct i2c_client *c,
-                                  const struct i2c_device_id *id)
+                        const struct i2c_device_id *id)
 {
        struct input_polled_dev *idev;
        struct mma8450 *m;
@@ -204,6 +204,8 @@ static int mma8450_probe(struct i2c_client *c,
                goto err_free_mem;
        }
 
+       i2c_set_clientdata(c, m);
+
        return 0;
 
 err_free_mem:
index dce0d95..6983ffb 100644 (file)
@@ -383,6 +383,7 @@ static int mpu3050_probe(struct i2c_client *client,
 
        pm_runtime_enable(&client->dev);
        pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY);
+       i2c_set_clientdata(client, sensor);
 
        return 0;
 
index 2ff4d1c..940566e 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/input.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/slab.h>
index fb4f8ac..83fff38 100644 (file)
@@ -87,7 +87,6 @@ static int rb532_button_remove(struct platform_device *pdev)
 
        input_unregister_polled_device(poll_dev);
        input_free_polled_device(poll_dev);
-       dev_set_drvdata(&pdev->dev, NULL);
 
        return 0;
 }
index 5b1aff8..f920ba7 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/gpio.h>
 #include <linux/rotary_encoder.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
 
index 0621c36..7b8b03e 100644 (file)
@@ -153,7 +153,7 @@ static struct platform_driver sirfsoc_pwrc_driver = {
                .name   = "sirfsoc-pwrc",
                .owner  = THIS_MODULE,
                .pm     = &sirfsoc_pwrc_pm_ops,
-               .of_match_table = of_match_ptr(sirfsoc_pwrc_of_match),
+               .of_match_table = sirfsoc_pwrc_of_match,
        }
 };
 
index a0a4bba..7728359 100644 (file)
@@ -430,20 +430,30 @@ static int uinput_setup_device(struct uinput_device *udev,
        return retval;
 }
 
-static ssize_t uinput_inject_event(struct uinput_device *udev,
-                                  const char __user *buffer, size_t count)
+static ssize_t uinput_inject_events(struct uinput_device *udev,
+                                   const char __user *buffer, size_t count)
 {
        struct input_event ev;
+       size_t bytes = 0;
 
-       if (count < input_event_size())
+       if (count != 0 && count < input_event_size())
                return -EINVAL;
 
-       if (input_event_from_user(buffer, &ev))
-               return -EFAULT;
+       while (bytes + input_event_size() <= count) {
+               /*
+                * Note that even if some events were fetched successfully
+                * we are still going to return EFAULT instead of partial
+                * count to let userspace know that it got it's buffers
+                * all wrong.
+                */
+               if (input_event_from_user(buffer + bytes, &ev))
+                       return -EFAULT;
 
-       input_event(udev->dev, ev.type, ev.code, ev.value);
+               input_event(udev->dev, ev.type, ev.code, ev.value);
+               bytes += input_event_size();
+       }
 
-       return input_event_size();
+       return bytes;
 }
 
 static ssize_t uinput_write(struct file *file, const char __user *buffer,
@@ -460,7 +470,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer,
                return retval;
 
        retval = udev->state == UIST_CREATED ?
-                       uinput_inject_event(udev, buffer, count) :
+                       uinput_inject_events(udev, buffer, count) :
                        uinput_setup_device(udev, buffer, count);
 
        mutex_unlock(&udev->mutex);
index 8365847..ca7a26f 100644 (file)
@@ -103,7 +103,6 @@ static const struct alps_model_info alps_model_data[] = {
        /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
        { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf,
                ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },
-       { { 0x73, 0x00, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_DUALPOINT },              /* Dell XT2 */
        { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS },           /* Dell Vostro 1400 */
        { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff,
                ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },                            /* Toshiba Tecra A11-11L */
@@ -1793,7 +1792,7 @@ int alps_init(struct psmouse *psmouse)
        snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
        dev2->phys = priv->phys;
        dev2->name = (priv->flags & ALPS_DUALPOINT) ?
-                    "DualPoint Stick" : "PS/2 Mouse";
+                    "DualPoint Stick" : "ALPS PS/2 Device";
        dev2->id.bustype = BUS_I8042;
        dev2->id.vendor  = 0x0002;
        dev2->id.product = PSMOUSE_ALPS;
index f51765f..a5869a8 100644 (file)
@@ -439,7 +439,7 @@ static int cypress_get_finger_count(unsigned char header_byte)
                        case 2: return 5;
                        default:
                                /* Invalid contact (e.g. palm). Ignore it. */
-                               return -1;
+                               return 0;
                }
        }
 
@@ -452,17 +452,10 @@ static int cypress_parse_packet(struct psmouse *psmouse,
 {
        unsigned char *packet = psmouse->packet;
        unsigned char header_byte = packet[0];
-       int contact_cnt;
 
        memset(report_data, 0, sizeof(struct cytp_report_data));
 
-       contact_cnt = cypress_get_finger_count(header_byte);
-
-       if (contact_cnt < 0) /* e.g. palm detect */
-               return -EINVAL;
-
-       report_data->contact_cnt = contact_cnt;
-
+       report_data->contact_cnt = cypress_get_finger_count(header_byte);
        report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
 
        if (report_data->contact_cnt == 1) {
@@ -535,11 +528,9 @@ static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
        int slots[CYTP_MAX_MT_SLOTS];
        int n;
 
-       if (cypress_parse_packet(psmouse, cytp, &report_data))
-               return;
+       cypress_parse_packet(psmouse, cytp, &report_data);
 
        n = report_data.contact_cnt;
-
        if (n > CYTP_MAX_MT_SLOTS)
                n = CYTP_MAX_MT_SLOTS;
 
@@ -605,10 +596,6 @@ static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
                return PSMOUSE_BAD_DATA;
 
        contact_cnt = cypress_get_finger_count(packet[0]);
-
-       if (contact_cnt < 0)
-               return PSMOUSE_BAD_DATA;
-
        if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE)
                cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4);
        else
@@ -679,15 +666,15 @@ int cypress_init(struct psmouse *psmouse)
 {
        struct cytp_data *cytp;
 
-       cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
-       psmouse->private = (void *)cytp;
-       if (cytp == NULL)
+       cytp = kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
+       if (!cytp)
                return -ENOMEM;
 
-       cypress_reset(psmouse);
-
+       psmouse->private = cytp;
        psmouse->pktsize = 8;
 
+       cypress_reset(psmouse);
+
        if (cypress_query_hardware(psmouse)) {
                psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
                goto err_exit;
index 1de1e5f..8541f94 100644 (file)
@@ -2,7 +2,7 @@
 # Input core configuration
 #
 config SERIO
-       tristate "Serial I/O support" if EXPERT || !X86
+       tristate "Serial I/O support"
        default y
        help
          Say Yes here if you have any input device that uses serial I/O to
@@ -19,7 +19,7 @@ config SERIO
 if SERIO
 
 config SERIO_I8042
-       tristate "i8042 PC Keyboard controller" if EXPERT || !X86
+       tristate "i8042 PC Keyboard controller"
        default y
        depends on !PARISC && (!ARM || FOOTBRIDGE_HOST) && \
                   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390 && \
@@ -170,7 +170,7 @@ config SERIO_MACEPS2
          module will be called maceps2.
 
 config SERIO_LIBPS2
-       tristate "PS/2 driver library" if EXPERT
+       tristate "PS/2 driver library"
        depends on SERIO_I8042 || SERIO_I8042=n
        help
          Say Y here if you are using a driver for device connected
@@ -266,4 +266,14 @@ config SERIO_OLPC_APSP
          To compile this driver as a module, choose M here: the module will
          be called olpc_apsp.
 
+config HYPERV_KEYBOARD
+       tristate "Microsoft Synthetic Keyboard driver"
+       depends on HYPERV
+       default HYPERV
+       help
+         Select this option to enable the Hyper-V Keyboard driver.
+
+         To compile this driver as a module, choose M here: the module will
+         be called hyperv_keyboard.
+
 endif
index 12298b1..815d874 100644 (file)
@@ -28,3 +28,4 @@ obj-$(CONFIG_SERIO_ALTERA_PS2)        += altera_ps2.o
 obj-$(CONFIG_SERIO_ARC_PS2)    += arc_ps2.o
 obj-$(CONFIG_SERIO_APBPS2)     += apbps2.o
 obj-$(CONFIG_SERIO_OLPC_APSP)  += olpc_apsp.o
+obj-$(CONFIG_HYPERV_KEYBOARD)  += hyperv-keyboard.o
diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c
new file mode 100644 (file)
index 0000000..3a83c3c
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ *  Copyright (c) 2013, Microsoft Corporation.
+ *
+ *  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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/completion.h>
+#include <linux/hyperv.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+
+/*
+ * Current version 1.0
+ *
+ */
+#define SYNTH_KBD_VERSION_MAJOR 1
+#define SYNTH_KBD_VERSION_MINOR        0
+#define SYNTH_KBD_VERSION              (SYNTH_KBD_VERSION_MINOR | \
+                                        (SYNTH_KBD_VERSION_MAJOR << 16))
+
+
+/*
+ * Message types in the synthetic input protocol
+ */
+enum synth_kbd_msg_type {
+       SYNTH_KBD_PROTOCOL_REQUEST = 1,
+       SYNTH_KBD_PROTOCOL_RESPONSE = 2,
+       SYNTH_KBD_EVENT = 3,
+       SYNTH_KBD_LED_INDICATORS = 4,
+};
+
+/*
+ * Basic message structures.
+ */
+struct synth_kbd_msg_hdr {
+       __le32 type;
+};
+
+struct synth_kbd_msg {
+       struct synth_kbd_msg_hdr header;
+       char data[]; /* Enclosed message */
+};
+
+union synth_kbd_version {
+       __le32 version;
+};
+
+/*
+ * Protocol messages
+ */
+struct synth_kbd_protocol_request {
+       struct synth_kbd_msg_hdr header;
+       union synth_kbd_version version_requested;
+};
+
+#define PROTOCOL_ACCEPTED      BIT(0)
+struct synth_kbd_protocol_response {
+       struct synth_kbd_msg_hdr header;
+       __le32 proto_status;
+};
+
+#define IS_UNICODE     BIT(0)
+#define IS_BREAK       BIT(1)
+#define IS_E0          BIT(2)
+#define IS_E1          BIT(3)
+struct synth_kbd_keystroke {
+       struct synth_kbd_msg_hdr header;
+       __le16 make_code;
+       __le16 reserved0;
+       __le32 info; /* Additional information */
+};
+
+
+#define HK_MAXIMUM_MESSAGE_SIZE 256
+
+#define KBD_VSC_SEND_RING_BUFFER_SIZE          (10 * PAGE_SIZE)
+#define KBD_VSC_RECV_RING_BUFFER_SIZE          (10 * PAGE_SIZE)
+
+#define XTKBD_EMUL0     0xe0
+#define XTKBD_EMUL1     0xe1
+#define XTKBD_RELEASE   0x80
+
+
+/*
+ * Represents a keyboard device
+ */
+struct hv_kbd_dev {
+       struct hv_device *hv_dev;
+       struct serio *hv_serio;
+       struct synth_kbd_protocol_request protocol_req;
+       struct synth_kbd_protocol_response protocol_resp;
+       /* Synchronize the request/response if needed */
+       struct completion wait_event;
+       spinlock_t lock; /* protects 'started' field */
+       bool started;
+};
+
+static void hv_kbd_on_receive(struct hv_device *hv_dev,
+                             struct synth_kbd_msg *msg, u32 msg_length)
+{
+       struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
+       struct synth_kbd_keystroke *ks_msg;
+       unsigned long flags;
+       u32 msg_type = __le32_to_cpu(msg->header.type);
+       u32 info;
+       u16 scan_code;
+
+       switch (msg_type) {
+       case SYNTH_KBD_PROTOCOL_RESPONSE:
+               /*
+                * Validate the information provided by the host.
+                * If the host is giving us a bogus packet,
+                * drop the packet (hoping the problem
+                * goes away).
+                */
+               if (msg_length < sizeof(struct synth_kbd_protocol_response)) {
+                       dev_err(&hv_dev->device,
+                               "Illegal protocol response packet (len: %d)\n",
+                               msg_length);
+                       break;
+               }
+
+               memcpy(&kbd_dev->protocol_resp, msg,
+                       sizeof(struct synth_kbd_protocol_response));
+               complete(&kbd_dev->wait_event);
+               break;
+
+       case SYNTH_KBD_EVENT:
+               /*
+                * Validate the information provided by the host.
+                * If the host is giving us a bogus packet,
+                * drop the packet (hoping the problem
+                * goes away).
+                */
+               if (msg_length < sizeof(struct  synth_kbd_keystroke)) {
+                       dev_err(&hv_dev->device,
+                               "Illegal keyboard event packet (len: %d)\n",
+                               msg_length);
+                       break;
+               }
+
+               ks_msg = (struct synth_kbd_keystroke *)msg;
+               info = __le32_to_cpu(ks_msg->info);
+
+               /*
+                * Inject the information through the serio interrupt.
+                */
+               spin_lock_irqsave(&kbd_dev->lock, flags);
+               if (kbd_dev->started) {
+                       if (info & IS_E0)
+                               serio_interrupt(kbd_dev->hv_serio,
+                                               XTKBD_EMUL0, 0);
+
+                       scan_code = __le16_to_cpu(ks_msg->make_code);
+                       if (info & IS_BREAK)
+                               scan_code |= XTKBD_RELEASE;
+
+                       serio_interrupt(kbd_dev->hv_serio, scan_code, 0);
+               }
+               spin_unlock_irqrestore(&kbd_dev->lock, flags);
+               break;
+
+       default:
+               dev_err(&hv_dev->device,
+                       "unhandled message type %d\n", msg_type);
+       }
+}
+
+static void hv_kbd_handle_received_packet(struct hv_device *hv_dev,
+                                         struct vmpacket_descriptor *desc,
+                                         u32 bytes_recvd,
+                                         u64 req_id)
+{
+       struct synth_kbd_msg *msg;
+       u32 msg_sz;
+
+       switch (desc->type) {
+       case VM_PKT_COMP:
+               break;
+
+       case VM_PKT_DATA_INBAND:
+               /*
+                * We have a packet that has "inband" data. The API used
+                * for retrieving the packet guarantees that the complete
+                * packet is read. So, minimally, we should be able to
+                * parse the payload header safely (assuming that the host
+                * can be trusted.  Trusting the host seems to be a
+                * reasonable assumption because in a virtualized
+                * environment there is not whole lot you can do if you
+                * don't trust the host.
+                *
+                * Nonetheless, let us validate if the host can be trusted
+                * (in a trivial way).  The interesting aspect of this
+                * validation is how do you recover if we discover that the
+                * host is not to be trusted? Simply dropping the packet, I
+                * don't think is an appropriate recovery.  In the interest
+                * of failing fast, it may be better to crash the guest.
+                * For now, I will just drop the packet!
+                */
+
+               msg_sz = bytes_recvd - (desc->offset8 << 3);
+               if (msg_sz <= sizeof(struct synth_kbd_msg_hdr)) {
+                       /*
+                        * Drop the packet and hope
+                        * the problem magically goes away.
+                        */
+                       dev_err(&hv_dev->device,
+                               "Illegal packet (type: %d, tid: %llx, size: %d)\n",
+                               desc->type, req_id, msg_sz);
+                       break;
+               }
+
+               msg = (void *)desc + (desc->offset8 << 3);
+               hv_kbd_on_receive(hv_dev, msg, msg_sz);
+               break;
+
+       default:
+               dev_err(&hv_dev->device,
+                       "unhandled packet type %d, tid %llx len %d\n",
+                       desc->type, req_id, bytes_recvd);
+               break;
+       }
+}
+
+static void hv_kbd_on_channel_callback(void *context)
+{
+       struct hv_device *hv_dev = context;
+       void *buffer;
+       int bufferlen = 0x100; /* Start with sensible size */
+       u32 bytes_recvd;
+       u64 req_id;
+       int error;
+
+       buffer = kmalloc(bufferlen, GFP_ATOMIC);
+       if (!buffer)
+               return;
+
+       while (1) {
+               error = vmbus_recvpacket_raw(hv_dev->channel, buffer, bufferlen,
+                                            &bytes_recvd, &req_id);
+               switch (error) {
+               case 0:
+                       if (bytes_recvd == 0) {
+                               kfree(buffer);
+                               return;
+                       }
+
+                       hv_kbd_handle_received_packet(hv_dev, buffer,
+                                                     bytes_recvd, req_id);
+                       break;
+
+               case -ENOBUFS:
+                       kfree(buffer);
+                       /* Handle large packet */
+                       bufferlen = bytes_recvd;
+                       buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
+                       if (!buffer)
+                               return;
+                       break;
+               }
+       }
+}
+
+static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev)
+{
+       struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
+       struct synth_kbd_protocol_request *request;
+       struct synth_kbd_protocol_response *response;
+       u32 proto_status;
+       int error;
+
+       request = &kbd_dev->protocol_req;
+       memset(request, 0, sizeof(struct synth_kbd_protocol_request));
+       request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST);
+       request->version_requested.version = __cpu_to_le32(SYNTH_KBD_VERSION);
+
+       error = vmbus_sendpacket(hv_dev->channel, request,
+                                sizeof(struct synth_kbd_protocol_request),
+                                (unsigned long)request,
+                                VM_PKT_DATA_INBAND,
+                                VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (error)
+               return error;
+
+       if (!wait_for_completion_timeout(&kbd_dev->wait_event, 10 * HZ))
+               return -ETIMEDOUT;
+
+       response = &kbd_dev->protocol_resp;
+       proto_status = __le32_to_cpu(response->proto_status);
+       if (!(proto_status & PROTOCOL_ACCEPTED)) {
+               dev_err(&hv_dev->device,
+                       "synth_kbd protocol request failed (version %d)\n",
+                       SYNTH_KBD_VERSION);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int hv_kbd_start(struct serio *serio)
+{
+       struct hv_kbd_dev *kbd_dev = serio->port_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&kbd_dev->lock, flags);
+       kbd_dev->started = true;
+       spin_unlock_irqrestore(&kbd_dev->lock, flags);
+
+       return 0;
+}
+
+static void hv_kbd_stop(struct serio *serio)
+{
+       struct hv_kbd_dev *kbd_dev = serio->port_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&kbd_dev->lock, flags);
+       kbd_dev->started = false;
+       spin_unlock_irqrestore(&kbd_dev->lock, flags);
+}
+
+static int hv_kbd_probe(struct hv_device *hv_dev,
+                       const struct hv_vmbus_device_id *dev_id)
+{
+       struct hv_kbd_dev *kbd_dev;
+       struct serio *hv_serio;
+       int error;
+
+       kbd_dev = kzalloc(sizeof(struct hv_kbd_dev), GFP_KERNEL);
+       hv_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+       if (!kbd_dev || !hv_serio) {
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       kbd_dev->hv_dev = hv_dev;
+       kbd_dev->hv_serio = hv_serio;
+       spin_lock_init(&kbd_dev->lock);
+       init_completion(&kbd_dev->wait_event);
+       hv_set_drvdata(hv_dev, kbd_dev);
+
+       hv_serio->dev.parent  = &hv_dev->device;
+       hv_serio->id.type = SERIO_8042_XL;
+       hv_serio->port_data = kbd_dev;
+       strlcpy(hv_serio->name, dev_name(&hv_dev->device),
+               sizeof(hv_serio->name));
+       strlcpy(hv_serio->phys, dev_name(&hv_dev->device),
+               sizeof(hv_serio->phys));
+
+       hv_serio->start = hv_kbd_start;
+       hv_serio->stop = hv_kbd_stop;
+
+       error = vmbus_open(hv_dev->channel,
+                          KBD_VSC_SEND_RING_BUFFER_SIZE,
+                          KBD_VSC_RECV_RING_BUFFER_SIZE,
+                          NULL, 0,
+                          hv_kbd_on_channel_callback,
+                          hv_dev);
+       if (error)
+               goto err_free_mem;
+
+       error = hv_kbd_connect_to_vsp(hv_dev);
+       if (error)
+               goto err_close_vmbus;
+
+       serio_register_port(kbd_dev->hv_serio);
+       return 0;
+
+err_close_vmbus:
+       vmbus_close(hv_dev->channel);
+err_free_mem:
+       kfree(hv_serio);
+       kfree(kbd_dev);
+       return error;
+}
+
+static int hv_kbd_remove(struct hv_device *hv_dev)
+{
+       struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
+
+       serio_unregister_port(kbd_dev->hv_serio);
+       vmbus_close(hv_dev->channel);
+       kfree(kbd_dev);
+
+       hv_set_drvdata(hv_dev, NULL);
+
+       return 0;
+}
+
+/*
+ * Keyboard GUID
+ * {f912ad6d-2b17-48ea-bd65-f927a61c7684}
+ */
+#define HV_KBD_GUID \
+       .guid = { \
+                       0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48, \
+                       0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 \
+       }
+
+static const struct hv_vmbus_device_id id_table[] = {
+       /* Keyboard guid */
+       { HV_KBD_GUID, },
+       { },
+};
+
+MODULE_DEVICE_TABLE(vmbus, id_table);
+
+static struct  hv_driver hv_kbd_drv = {
+       .name = KBUILD_MODNAME,
+       .id_table = id_table,
+       .probe = hv_kbd_probe,
+       .remove = hv_kbd_remove,
+};
+
+static int __init hv_kbd_init(void)
+{
+       return vmbus_driver_register(&hv_kbd_drv);
+}
+
+static void __exit hv_kbd_exit(void)
+{
+       vmbus_driver_unregister(&hv_kbd_drv);
+}
+
+MODULE_LICENSE("GPL");
+module_init(hv_kbd_init);
+module_exit(hv_kbd_exit);
index 5f306f7..0ec9abb 100644 (file)
@@ -765,6 +765,7 @@ static struct pnp_device_id pnp_kbd_devids[] = {
        { .id = "CPQA0D7", .driver_data = 0 },
        { .id = "", },
 };
+MODULE_DEVICE_TABLE(pnp, pnp_kbd_devids);
 
 static struct pnp_driver i8042_pnp_kbd_driver = {
        .name           = "i8042 kbd",
@@ -786,6 +787,7 @@ static struct pnp_device_id pnp_aux_devids[] = {
        { .id = "SYN0801", .driver_data = 0 },
        { .id = "", },
 };
+MODULE_DEVICE_TABLE(pnp, pnp_aux_devids);
 
 static struct pnp_driver i8042_pnp_aux_driver = {
        .name           = "i8042 aux",
index 52c9ebf..020053f 100644 (file)
@@ -1036,7 +1036,7 @@ static void i8042_controller_reset(bool force_reset)
 /*
  * i8042_panic_blink() will turn the keyboard LEDs on or off and is called
  * when kernel panics. Flashing LEDs is useful for users running X who may
- * not see the console and will help distingushing panics from "real"
+ * not see the console and will help distinguishing panics from "real"
  * lockups.
  *
  * Note that DELAY has a limit of 10ms so we will not get stuck here
index e53416a..867e7c3 100644 (file)
@@ -524,9 +524,6 @@ static int wacom_set_device_mode(struct usb_interface *intf, int report_id, int
 
                error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
                                         report_id, rep_data, length, 1);
-               if (error >= 0)
-                       error = wacom_get_report(intf, WAC_HID_FEATURE_REPORT,
-                                                report_id, rep_data, length, 1);
        } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES);
 
        kfree(rep_data);
@@ -548,7 +545,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
                        /* MT Tablet PC touch */
                        return wacom_set_device_mode(intf, 3, 4, 4);
                }
-               else if (features->type == WACOM_24HDT) {
+               else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) {
                        return wacom_set_device_mode(intf, 18, 3, 2);
                }
        } else if (features->device_type == BTN_TOOL_PEN) {
@@ -719,7 +716,7 @@ static int wacom_led_control(struct wacom *wacom)
                return -ENOMEM;
 
        if (wacom->wacom_wac.features.type >= INTUOS5S &&
-           wacom->wacom_wac.features.type <= INTUOS5L) {
+           wacom->wacom_wac.features.type <= INTUOSPL) {
                /*
                 * Touch Ring and crop mark LED luminance may take on
                 * one of four values:
@@ -981,14 +978,20 @@ static int wacom_initialize_leds(struct wacom *wacom)
        case INTUOS5S:
        case INTUOS5:
        case INTUOS5L:
-               wacom->led.select[0] = 0;
-               wacom->led.select[1] = 0;
-               wacom->led.llv = 32;
-               wacom->led.hlv = 0;
-               wacom->led.img_lum = 0;
-
-               error = sysfs_create_group(&wacom->intf->dev.kobj,
-                                          &intuos5_led_attr_group);
+       case INTUOSPS:
+       case INTUOSPM:
+       case INTUOSPL:
+               if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) {
+                       wacom->led.select[0] = 0;
+                       wacom->led.select[1] = 0;
+                       wacom->led.llv = 32;
+                       wacom->led.hlv = 0;
+                       wacom->led.img_lum = 0;
+
+                       error = sysfs_create_group(&wacom->intf->dev.kobj,
+                                                 &intuos5_led_attr_group);
+               } else
+                       return 0;
                break;
 
        default:
@@ -1024,8 +1027,12 @@ static void wacom_destroy_leds(struct wacom *wacom)
        case INTUOS5S:
        case INTUOS5:
        case INTUOS5L:
-               sysfs_remove_group(&wacom->intf->dev.kobj,
-                                  &intuos5_led_attr_group);
+       case INTUOSPS:
+       case INTUOSPM:
+       case INTUOSPL:
+               if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN)
+                       sysfs_remove_group(&wacom->intf->dev.kobj,
+                                          &intuos5_led_attr_group);
                break;
        }
 }
@@ -1185,34 +1192,47 @@ static void wacom_wireless_work(struct work_struct *work)
                wacom_wac1->features =
                        *((struct wacom_features *)id->driver_info);
                wacom_wac1->features.device_type = BTN_TOOL_PEN;
+               snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen",
+                        wacom_wac1->features.name);
                error = wacom_register_input(wacom1);
                if (error)
-                       goto fail1;
+                       goto fail;
 
                /* Touch interface */
-               wacom_wac2->features =
-                       *((struct wacom_features *)id->driver_info);
-               wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
-               wacom_wac2->features.device_type = BTN_TOOL_FINGER;
-               wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
-               error = wacom_register_input(wacom2);
-               if (error)
-                       goto fail2;
+               if (wacom_wac1->features.touch_max) {
+                       wacom_wac2->features =
+                               *((struct wacom_features *)id->driver_info);
+                       wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
+                       wacom_wac2->features.device_type = BTN_TOOL_FINGER;
+                       wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
+                       if (wacom_wac2->features.touch_max)
+                               snprintf(wacom_wac2->name, WACOM_NAME_MAX,
+                                        "%s (WL) Finger",wacom_wac2->features.name);
+                       else
+                               snprintf(wacom_wac2->name, WACOM_NAME_MAX,
+                                        "%s (WL) Pad",wacom_wac2->features.name);
+                       error = wacom_register_input(wacom2);
+                       if (error)
+                               goto fail;
+               }
 
                error = wacom_initialize_battery(wacom);
                if (error)
-                       goto fail3;
+                       goto fail;
        }
 
        return;
 
-fail3:
-       input_unregister_device(wacom_wac2->input);
-       wacom_wac2->input = NULL;
-fail2:
-       input_unregister_device(wacom_wac1->input);
-       wacom_wac1->input = NULL;
-fail1:
+fail:
+       if (wacom_wac2->input) {
+               input_unregister_device(wacom_wac2->input);
+               wacom_wac2->input = NULL;
+       }
+
+       if (wacom_wac1->input) {
+               input_unregister_device(wacom_wac1->input);
+               wacom_wac1->input = NULL;
+       }
        return;
 }
 
@@ -1302,7 +1322,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
         * HID descriptor. If this is the touch interface (wMaxPacketSize
         * of WACOM_PKGLEN_BBTOUCH3), override the table values.
         */
-       if (features->type >= INTUOS5S && features->type <= INTUOS5L) {
+       if (features->type >= INTUOS5S && features->type <= INTUOSPL) {
                if (endpoint->wMaxPacketSize == WACOM_PKGLEN_BBTOUCH3) {
                        features->device_type = BTN_TOOL_FINGER;
                        features->pktlen = WACOM_PKGLEN_BBTOUCH3;
@@ -1329,10 +1349,12 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
                struct usb_device *other_dev;
 
                /* Append the device type to the name */
-               strlcat(wacom_wac->name,
-                       features->device_type == BTN_TOOL_PEN ?
-                               " Pen" : " Finger",
-                       sizeof(wacom_wac->name));
+               if (features->device_type != BTN_TOOL_FINGER)
+                       strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX);
+               else if (features->touch_max)
+                       strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX);
+               else
+                       strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX);
 
                other_dev = wacom_get_sibling(dev, features->oVid, features->oPid);
                if (other_dev == NULL || wacom_get_usbdev_data(other_dev) == NULL)
index c59b797..782c253 100644 (file)
@@ -427,6 +427,13 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
                        (features->type == WACOM_21UX2))
                return 1;
 
+       /* Range Report */
+       if ((data[1] & 0xfe) == 0x20) {
+               input_report_key(input, BTN_TOUCH, 0);
+               input_report_abs(input, ABS_PRESSURE, 0);
+               input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max);
+       }
+
        /* Exit report */
        if ((data[1] & 0xfe) == 0x80) {
                if (features->quirks == WACOM_QUIRK_MULTI_INPUT)
@@ -477,7 +484,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
        /* general pen packet */
        if ((data[1] & 0xb8) == 0xa0) {
                t = (data[6] << 2) | ((data[7] >> 6) & 3);
-               if (features->type >= INTUOS4S && features->type <= WACOM_24HD) {
+               if (features->type >= INTUOS4S && features->type <= CINTIQ_HYBRID) {
                        t = (t << 1) | (data[1] & 1);
                }
                input_report_abs(input, ABS_PRESSURE, t);
@@ -621,14 +628,30 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
                        } else {
                                input_report_abs(input, ABS_MISC, 0);
                        }
-               } else if (features->type >= INTUOS5S && features->type <= INTUOS5L) {
+               } else if (features->type == CINTIQ_HYBRID) {
+                       /*
+                        * Do not send hardware buttons under Android. They
+                        * are already sent to the system through GPIO (and
+                        * have different meaning).
+                        */
+                       input_report_key(input, BTN_1, (data[4] & 0x01));
+                       input_report_key(input, BTN_2, (data[4] & 0x02));
+                       input_report_key(input, BTN_3, (data[4] & 0x04));
+                       input_report_key(input, BTN_4, (data[4] & 0x08));
+
+                       input_report_key(input, BTN_5, (data[4] & 0x10));  /* Right  */
+                       input_report_key(input, BTN_6, (data[4] & 0x20));  /* Up     */
+                       input_report_key(input, BTN_7, (data[4] & 0x40));  /* Left   */
+                       input_report_key(input, BTN_8, (data[4] & 0x80));  /* Down   */
+                       input_report_key(input, BTN_0, (data[3] & 0x01));  /* Center */
+               } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) {
                        int i;
 
                        /* Touch ring mode switch has no capacitive sensor */
                        input_report_key(input, BTN_0, (data[3] & 0x01));
 
                        /*
-                        * ExpressKeys on Intuos5 have a capacitive sensor in
+                        * ExpressKeys on Intuos5/Intuos Pro have a capacitive sensor in
                         * addition to the mechanical switch. Switch data is
                         * stored in data[4], capacitive data in data[5].
                         */
@@ -716,7 +739,9 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
             features->type == INTUOS4 ||
             features->type == INTUOS4S ||
             features->type == INTUOS5 ||
-            features->type == INTUOS5S)) {
+            features->type == INTUOS5S ||
+            features->type == INTUOSPM ||
+            features->type == INTUOSPS)) {
 
                return 0;
        }
@@ -769,8 +794,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
 
                } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
                        /* I4 mouse */
-                       if ((features->type >= INTUOS4S && features->type <= INTUOS4L) ||
-                           (features->type >= INTUOS5S && features->type <= INTUOS5L)) {
+                       if (features->type >= INTUOS4S && features->type <= INTUOSPL) {
                                input_report_key(input, BTN_LEFT,   data[6] & 0x01);
                                input_report_key(input, BTN_MIDDLE, data[6] & 0x02);
                                input_report_key(input, BTN_RIGHT,  data[6] & 0x04);
@@ -797,7 +821,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
                                }
                        }
                } else if ((features->type < INTUOS3S || features->type == INTUOS3L ||
-                               features->type == INTUOS4L || features->type == INTUOS5L) &&
+                               features->type == INTUOS4L || features->type == INTUOS5L ||
+                               features->type == INTUOSPL) &&
                           wacom->tool[idx] == BTN_TOOL_LENS) {
                        /* Lens cursor packets */
                        input_report_key(input, BTN_LEFT,   data[8] & 0x01);
@@ -1107,6 +1132,7 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
 
 static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
 {
+       struct wacom_features *features = &wacom->features;
        struct input_dev *input = wacom->input;
        bool touch = data[1] & 0x80;
        int slot = input_mt_get_slot_by_key(input, data[0]);
@@ -1122,14 +1148,23 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
        if (touch) {
                int x = (data[2] << 4) | (data[4] >> 4);
                int y = (data[3] << 4) | (data[4] & 0x0f);
-               int a = data[5];
+               int width, height;
 
-               // "a" is a scaled-down area which we assume is roughly
-               // circular and which can be described as: a=(pi*r^2)/C.
-               int x_res  = input_abs_get_res(input, ABS_X);
-               int y_res  = input_abs_get_res(input, ABS_Y);
-               int width  = 2 * int_sqrt(a * WACOM_CONTACT_AREA_SCALE);
-               int height = width * y_res / x_res;
+               if (features->type >= INTUOSPS && features->type <= INTUOSPL) {
+                       width  = data[5];
+                       height = data[6];
+               } else {
+                       /*
+                        * "a" is a scaled-down area which we assume is
+                        * roughly circular and which can be described as:
+                        * a=(pi*r^2)/C.
+                        */
+                       int a = data[5];
+                       int x_res  = input_abs_get_res(input, ABS_X);
+                       int y_res  = input_abs_get_res(input, ABS_Y);
+                       width  = 2 * int_sqrt(a * WACOM_CONTACT_AREA_SCALE);
+                       height = width * y_res / x_res;
+               }
 
                input_report_abs(input, ABS_MT_POSITION_X, x);
                input_report_abs(input, ABS_MT_POSITION_Y, y);
@@ -1327,6 +1362,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
        case WACOM_22HD:
        case WACOM_24HD:
        case DTK:
+       case CINTIQ_HYBRID:
                sync = wacom_intuos_irq(wacom_wac);
                break;
 
@@ -1337,6 +1373,9 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
        case INTUOS5S:
        case INTUOS5:
        case INTUOS5L:
+       case INTUOSPS:
+       case INTUOSPM:
+       case INTUOSPL:
                if (len == WACOM_PKGLEN_BBTOUCH3)
                        sync = wacom_bpt3_touch(wacom_wac);
                else
@@ -1420,7 +1459,7 @@ void wacom_setup_device_quirks(struct wacom_features *features)
 
        /* these device have multiple inputs */
        if (features->type >= WIRELESS ||
-           (features->type >= INTUOS5S && features->type <= INTUOS5L) ||
+           (features->type >= INTUOS5S && features->type <= INTUOSPL) ||
            (features->oVid && features->oPid))
                features->quirks |= WACOM_QUIRK_MULTI_INPUT;
 
@@ -1627,6 +1666,8 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
 
        case INTUOS5:
        case INTUOS5L:
+       case INTUOSPM:
+       case INTUOSPL:
                if (features->device_type == BTN_TOOL_PEN) {
                        __set_bit(BTN_7, input_dev->keybit);
                        __set_bit(BTN_8, input_dev->keybit);
@@ -1634,6 +1675,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
                /* fall through */
 
        case INTUOS5S:
+       case INTUOSPS:
                __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 
                if (features->device_type == BTN_TOOL_PEN) {
@@ -1765,6 +1807,24 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
                                              0, 0);
                }
                break;
+
+       case CINTIQ_HYBRID:
+               __set_bit(BTN_1, input_dev->keybit);
+               __set_bit(BTN_2, input_dev->keybit);
+               __set_bit(BTN_3, input_dev->keybit);
+               __set_bit(BTN_4, input_dev->keybit);
+
+               __set_bit(BTN_5, input_dev->keybit);
+               __set_bit(BTN_6, input_dev->keybit);
+               __set_bit(BTN_7, input_dev->keybit);
+               __set_bit(BTN_8, input_dev->keybit);
+               __set_bit(BTN_0, input_dev->keybit);
+
+               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+               wacom_setup_cintiq(wacom_wac);
+               break;
        }
        return 0;
 }
@@ -1952,6 +2012,18 @@ static const struct wacom_features wacom_features_0x29 =
 static const struct wacom_features wacom_features_0x2A =
        { "Wacom Intuos5 M", WACOM_PKGLEN_INTUOS,  44704, 27940, 2047,
          63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0x314 =
+       { "Wacom Intuos Pro S", WACOM_PKGLEN_INTUOS,  31496, 19685, 2047,
+         63, INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         .touch_max = 16 };
+static const struct wacom_features wacom_features_0x315 =
+       { "Wacom Intuos Pro M", WACOM_PKGLEN_INTUOS,  44704, 27940, 2047,
+         63, INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         .touch_max = 16 };
+static const struct wacom_features wacom_features_0x317 =
+       { "Wacom Intuos Pro L", WACOM_PKGLEN_INTUOS,  65024, 40640, 2047,
+         63, INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         .touch_max = 16 };
 static const struct wacom_features wacom_features_0xF4 =
        { "Wacom Cintiq 24HD",       WACOM_PKGLEN_INTUOS,   104480, 65600, 2047,
          63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
@@ -2131,6 +2203,13 @@ static const struct wacom_features wacom_features_0x301 =
 static const struct wacom_features wacom_features_0x6004 =
        { "ISD-V4",               WACOM_PKGLEN_GRAPHIRE,  12800,  8000,  255,
          0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x0307 =
+       { "Wacom ISDv5 307", WACOM_PKGLEN_INTUOS,  59552,  33848, 2047,
+         63, CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 };
+static const struct wacom_features wacom_features_0x0309 =
+       { "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */
+         .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10 };
 
 #define USB_DEVICE_WACOM(prod)                                 \
        USB_DEVICE(USB_VENDOR_ID_WACOM, prod),                  \
@@ -2259,12 +2338,17 @@ const struct usb_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0x300) },
        { USB_DEVICE_WACOM(0x301) },
        { USB_DEVICE_WACOM(0x304) },
+       { USB_DEVICE_DETAILED(0x314, USB_CLASS_HID, 0, 0) },
+       { USB_DEVICE_DETAILED(0x315, USB_CLASS_HID, 0, 0) },
+       { USB_DEVICE_DETAILED(0x317, USB_CLASS_HID, 0, 0) },
        { USB_DEVICE_WACOM(0x4001) },
        { USB_DEVICE_WACOM(0x47) },
        { USB_DEVICE_WACOM(0xF4) },
        { USB_DEVICE_WACOM(0xF8) },
        { USB_DEVICE_DETAILED(0xF6, USB_CLASS_HID, 0, 0) },
        { USB_DEVICE_WACOM(0xFA) },
+       { USB_DEVICE_WACOM(0x0307) },
+       { USB_DEVICE_DETAILED(0x0309, USB_CLASS_HID, 0, 0) },
        { USB_DEVICE_LENOVO(0x6004) },
        { }
 };
index dfc9e08..fd23a37 100644 (file)
@@ -14,6 +14,8 @@
 /* maximum packet length for USB devices */
 #define WACOM_PKGLEN_MAX       64
 
+#define WACOM_NAME_MAX         64
+
 /* packet length for individual models */
 #define WACOM_PKGLEN_PENPRTN    7
 #define WACOM_PKGLEN_GRAPHIRE   8
@@ -76,10 +78,14 @@ enum {
        INTUOS5S,
        INTUOS5,
        INTUOS5L,
+       INTUOSPS,
+       INTUOSPM,
+       INTUOSPL,
        WACOM_21UX2,
        WACOM_22HD,
        DTK,
        WACOM_24HD,
+       CINTIQ_HYBRID,
        CINTIQ,
        WACOM_BEE,
        WACOM_13HD,
@@ -126,7 +132,7 @@ struct wacom_shared {
 };
 
 struct wacom_wac {
-       char name[64];
+       char name[WACOM_NAME_MAX];
        unsigned char *data;
        int tool[2];
        int id[2];
index e09ec67..00d1e54 100644 (file)
@@ -919,4 +919,17 @@ config TOUCHSCREEN_TPS6507X
          To compile this driver as a module, choose M here: the
          module will be called tps6507x_ts.
 
+config TOUCHSCREEN_ZFORCE
+       tristate "Neonode zForce infrared touchscreens"
+       depends on I2C
+       depends on GPIOLIB
+       help
+         Say Y here if you have a touchscreen using the zforce
+         infraread technology from Neonode.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called zforce_ts.
+
 endif
index f5216c1..7587883 100644 (file)
@@ -75,3 +75,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)    += mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)      += zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)      += w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_ZFORCE)       += zforce_ts.o
index f3a174a..69834dd 100644 (file)
@@ -806,7 +806,6 @@ err_free_irq:
 err_free_mem:
        input_free_device(input_dev);
        kfree(ts);
-       spi_set_drvdata(spi, NULL);
        return err;
 }
 
@@ -823,7 +822,6 @@ static int ad7877_remove(struct spi_device *spi)
        kfree(ts);
 
        dev_dbg(&spi->dev, "unregistered touchscreen\n");
-       spi_set_drvdata(spi, NULL);
 
        return 0;
 }
index 606da5b..1a7b114 100644 (file)
@@ -142,7 +142,6 @@ static int ad7879_spi_remove(struct spi_device *spi)
        struct ad7879 *ts = spi_get_drvdata(spi);
 
        ad7879_remove(ts);
-       spi_set_drvdata(spi, NULL);
 
        return 0;
 }
index d038575..42d830e 100644 (file)
@@ -2113,7 +2113,6 @@ error_startup:
 error_request_irq:
        if (cd->cpdata->init)
                cd->cpdata->init(cd->cpdata, 0, dev);
-       dev_set_drvdata(dev, NULL);
 error_free_xfer:
        kfree(cd->xfer_buf);
 error_free_cd:
@@ -2151,7 +2150,6 @@ int cyttsp4_remove(struct cyttsp4 *cd)
        free_irq(cd->irq, cd);
        if (cd->cpdata->init)
                cd->cpdata->init(cd->cpdata, 0, dev);
-       dev_set_drvdata(dev, NULL);
        cyttsp4_free_si_ptrs(cd);
        kfree(cd);
        return 0;
index a71e114..b19434c 100644 (file)
@@ -171,10 +171,7 @@ static int cyttsp4_spi_probe(struct spi_device *spi)
        ts = cyttsp4_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq,
                          CY_SPI_DATA_BUF_SIZE);
 
-       if (IS_ERR(ts))
-               return PTR_ERR(ts);
-
-       return 0;
+       return PTR_ERR_OR_ZERO(ts);
 }
 
 static int cyttsp4_spi_remove(struct spi_device *spi)
index d53e0b7..4204841 100644 (file)
@@ -242,7 +242,7 @@ static int cyttsp_soft_reset(struct cyttsp *ts)
        int retval;
 
        /* wait for interrupt to set ready completion */
-       INIT_COMPLETION(ts->bl_ready);
+       reinit_completion(&ts->bl_ready);
        ts->state = CY_BL_STATE;
 
        enable_irq(ts->irq);
index ef5fcb0..054d225 100644 (file)
@@ -273,7 +273,7 @@ static struct i2c_driver egalax_ts_driver = {
                .name   = "egalax_ts",
                .owner  = THIS_MODULE,
                .pm     = &egalax_ts_pm_ops,
-               .of_match_table = of_match_ptr(egalax_ts_dt_ids),
+               .of_match_table = egalax_ts_dt_ids,
        },
        .id_table       = egalax_ts_id,
        .probe          = egalax_ts_probe,
index 6650085..92e2243 100644 (file)
@@ -186,8 +186,6 @@ static int htcpen_isa_remove(struct device *dev, unsigned int id)
        release_region(HTCPEN_PORT_INIT, 1);
        release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
 
-       dev_set_drvdata(dev, NULL);
-
        return 0;
 }
 
index 1740a24..2f03b2f 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/pm_qos.h>
 #include <linux/slab.h>
index 24e625c..68beada 100644 (file)
@@ -354,9 +354,16 @@ static int titsc_parse_dt(struct platform_device *pdev,
        if (err < 0)
                return err;
 
-       err = of_property_read_u32(node, "ti,coordiante-readouts",
+       /*
+        * Try with the new binding first. If it fails, try again with
+        * bogus, miss-spelled version.
+        */
+       err = of_property_read_u32(node, "ti,coordinate-readouts",
                        &ts_dev->coordinate_readouts);
        if (err < 0)
+               err = of_property_read_u32(node, "ti,coordiante-readouts",
+                               &ts_dev->coordinate_readouts);
+       if (err < 0)
                return err;
 
        return of_property_read_u32_array(node, "ti,wire-config",
@@ -511,7 +518,7 @@ static struct platform_driver ti_tsc_driver = {
                .name   = "TI-am335x-tsc",
                .owner  = THIS_MODULE,
                .pm     = TITSC_PM_OPS,
-               .of_match_table = of_match_ptr(ti_tsc_dt_ids),
+               .of_match_table = ti_tsc_dt_ids,
        },
 };
 module_platform_driver(ti_tsc_driver);
index 7213e8b..8113533 100644 (file)
@@ -678,7 +678,6 @@ static int tsc2005_probe(struct spi_device *spi)
 err_remove_sysfs:
        sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group);
 err_clear_drvdata:
-       spi_set_drvdata(spi, NULL);
        free_irq(spi->irq, ts);
 err_free_mem:
        input_free_device(input_dev);
@@ -696,7 +695,6 @@ static int tsc2005_remove(struct spi_device *spi)
        input_unregister_device(ts->idev);
        kfree(ts);
 
-       spi_set_drvdata(spi, NULL);
        return 0;
 }
 
index 721fdb3..ae4b6b9 100644 (file)
@@ -146,12 +146,10 @@ enum {
 
 #define USB_DEVICE_HID_CLASS(vend, prod) \
        .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS \
-               | USB_DEVICE_ID_MATCH_INT_PROTOCOL \
                | USB_DEVICE_ID_MATCH_DEVICE, \
        .idVendor = (vend), \
        .idProduct = (prod), \
-       .bInterfaceClass = USB_INTERFACE_CLASS_HID, \
-       .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE
+       .bInterfaceClass = USB_INTERFACE_CLASS_HID
 
 static const struct usb_device_id usbtouch_devices[] = {
 #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
new file mode 100644 (file)
index 0000000..75762d6
--- /dev/null
@@ -0,0 +1,836 @@
+/*
+ * Copyright (C) 2012-2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * based in parts on Nook zforce driver
+ *
+ * Copyright (C) 2010 Barnes & Noble, Inc.
+ * Author: Pieter Truter<ptruter@intrinsyc.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/hrtimer.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/input/mt.h>
+#include <linux/platform_data/zforce_ts.h>
+
+#define WAIT_TIMEOUT           msecs_to_jiffies(1000)
+
+#define FRAME_START            0xee
+
+/* Offsets of the different parts of the payload the controller sends */
+#define PAYLOAD_HEADER         0
+#define PAYLOAD_LENGTH         1
+#define PAYLOAD_BODY           2
+
+/* Response offsets */
+#define RESPONSE_ID            0
+#define RESPONSE_DATA          1
+
+/* Commands */
+#define COMMAND_DEACTIVATE     0x00
+#define COMMAND_INITIALIZE     0x01
+#define COMMAND_RESOLUTION     0x02
+#define COMMAND_SETCONFIG      0x03
+#define COMMAND_DATAREQUEST    0x04
+#define COMMAND_SCANFREQ       0x08
+#define COMMAND_STATUS         0X1e
+
+/*
+ * Responses the controller sends as a result of
+ * command requests
+ */
+#define RESPONSE_DEACTIVATE    0x00
+#define RESPONSE_INITIALIZE    0x01
+#define RESPONSE_RESOLUTION    0x02
+#define RESPONSE_SETCONFIG     0x03
+#define RESPONSE_SCANFREQ      0x08
+#define RESPONSE_STATUS                0X1e
+
+/*
+ * Notifications are send by the touch controller without
+ * being requested by the driver and include for example
+ * touch indications
+ */
+#define NOTIFICATION_TOUCH             0x04
+#define NOTIFICATION_BOOTCOMPLETE      0x07
+#define NOTIFICATION_OVERRUN           0x25
+#define NOTIFICATION_PROXIMITY         0x26
+#define NOTIFICATION_INVALID_COMMAND   0xfe
+
+#define ZFORCE_REPORT_POINTS           2
+#define ZFORCE_MAX_AREA                        0xff
+
+#define STATE_DOWN                     0
+#define STATE_MOVE                     1
+#define STATE_UP                       2
+
+#define SETCONFIG_DUALTOUCH            (1 << 0)
+
+struct zforce_point {
+       int coord_x;
+       int coord_y;
+       int state;
+       int id;
+       int area_major;
+       int area_minor;
+       int orientation;
+       int pressure;
+       int prblty;
+};
+
+/*
+ * @client             the i2c_client
+ * @input              the input device
+ * @suspending         in the process of going to suspend (don't emit wakeup
+ *                     events for commands executed to suspend the device)
+ * @suspended          device suspended
+ * @access_mutex       serialize i2c-access, to keep multipart reads together
+ * @command_done       completion to wait for the command result
+ * @command_mutex      serialize commands send to the ic
+ * @command_waiting    the id of the command that that is currently waiting
+ *                     for a result
+ * @command_result     returned result of the command
+ */
+struct zforce_ts {
+       struct i2c_client       *client;
+       struct input_dev        *input;
+       const struct zforce_ts_platdata *pdata;
+       char                    phys[32];
+
+       bool                    suspending;
+       bool                    suspended;
+       bool                    boot_complete;
+
+       /* Firmware version information */
+       u16                     version_major;
+       u16                     version_minor;
+       u16                     version_build;
+       u16                     version_rev;
+
+       struct mutex            access_mutex;
+
+       struct completion       command_done;
+       struct mutex            command_mutex;
+       int                     command_waiting;
+       int                     command_result;
+};
+
+static int zforce_command(struct zforce_ts *ts, u8 cmd)
+{
+       struct i2c_client *client = ts->client;
+       char buf[3];
+       int ret;
+
+       dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
+
+       buf[0] = FRAME_START;
+       buf[1] = 1; /* data size, command only */
+       buf[2] = cmd;
+
+       mutex_lock(&ts->access_mutex);
+       ret = i2c_master_send(client, &buf[0], ARRAY_SIZE(buf));
+       mutex_unlock(&ts->access_mutex);
+       if (ret < 0) {
+               dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len)
+{
+       struct i2c_client *client = ts->client;
+       int ret;
+
+       ret = mutex_trylock(&ts->command_mutex);
+       if (!ret) {
+               dev_err(&client->dev, "already waiting for a command\n");
+               return -EBUSY;
+       }
+
+       dev_dbg(&client->dev, "sending %d bytes for command 0x%x\n",
+               buf[1], buf[2]);
+
+       ts->command_waiting = buf[2];
+
+       mutex_lock(&ts->access_mutex);
+       ret = i2c_master_send(client, buf, len);
+       mutex_unlock(&ts->access_mutex);
+       if (ret < 0) {
+               dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+               goto unlock;
+       }
+
+       dev_dbg(&client->dev, "waiting for result for command 0x%x\n", buf[2]);
+
+       if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) {
+               ret = -ETIME;
+               goto unlock;
+       }
+
+       ret = ts->command_result;
+
+unlock:
+       mutex_unlock(&ts->command_mutex);
+       return ret;
+}
+
+static int zforce_command_wait(struct zforce_ts *ts, u8 cmd)
+{
+       struct i2c_client *client = ts->client;
+       char buf[3];
+       int ret;
+
+       dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
+
+       buf[0] = FRAME_START;
+       buf[1] = 1; /* data size, command only */
+       buf[2] = cmd;
+
+       ret = zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+       if (ret < 0) {
+               dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int zforce_resolution(struct zforce_ts *ts, u16 x, u16 y)
+{
+       struct i2c_client *client = ts->client;
+       char buf[7] = { FRAME_START, 5, COMMAND_RESOLUTION,
+                       (x & 0xff), ((x >> 8) & 0xff),
+                       (y & 0xff), ((y >> 8) & 0xff) };
+
+       dev_dbg(&client->dev, "set resolution to (%d,%d)\n", x, y);
+
+       return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_scan_frequency(struct zforce_ts *ts, u16 idle, u16 finger,
+                                u16 stylus)
+{
+       struct i2c_client *client = ts->client;
+       char buf[9] = { FRAME_START, 7, COMMAND_SCANFREQ,
+                       (idle & 0xff), ((idle >> 8) & 0xff),
+                       (finger & 0xff), ((finger >> 8) & 0xff),
+                       (stylus & 0xff), ((stylus >> 8) & 0xff) };
+
+       dev_dbg(&client->dev, "set scan frequency to (idle: %d, finger: %d, stylus: %d)\n",
+               idle, finger, stylus);
+
+       return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_setconfig(struct zforce_ts *ts, char b1)
+{
+       struct i2c_client *client = ts->client;
+       char buf[7] = { FRAME_START, 5, COMMAND_SETCONFIG,
+                       b1, 0, 0, 0 };
+
+       dev_dbg(&client->dev, "set config to (%d)\n", b1);
+
+       return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_start(struct zforce_ts *ts)
+{
+       struct i2c_client *client = ts->client;
+       const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+       int ret;
+
+       dev_dbg(&client->dev, "starting device\n");
+
+       ret = zforce_command_wait(ts, COMMAND_INITIALIZE);
+       if (ret) {
+               dev_err(&client->dev, "Unable to initialize, %d\n", ret);
+               return ret;
+       }
+
+       ret = zforce_resolution(ts, pdata->x_max, pdata->y_max);
+       if (ret) {
+               dev_err(&client->dev, "Unable to set resolution, %d\n", ret);
+               goto error;
+       }
+
+       ret = zforce_scan_frequency(ts, 10, 50, 50);
+       if (ret) {
+               dev_err(&client->dev, "Unable to set scan frequency, %d\n",
+                       ret);
+               goto error;
+       }
+
+       if (zforce_setconfig(ts, SETCONFIG_DUALTOUCH)) {
+               dev_err(&client->dev, "Unable to set config\n");
+               goto error;
+       }
+
+       /* start sending touch events */
+       ret = zforce_command(ts, COMMAND_DATAREQUEST);
+       if (ret) {
+               dev_err(&client->dev, "Unable to request data\n");
+               goto error;
+       }
+
+       /*
+        * Per NN, initial cal. take max. of 200msec.
+        * Allow time to complete this calibration
+        */
+       msleep(200);
+
+       return 0;
+
+error:
+       zforce_command_wait(ts, COMMAND_DEACTIVATE);
+       return ret;
+}
+
+static int zforce_stop(struct zforce_ts *ts)
+{
+       struct i2c_client *client = ts->client;
+       int ret;
+
+       dev_dbg(&client->dev, "stopping device\n");
+
+       /* Deactivates touch sensing and puts the device into sleep. */
+       ret = zforce_command_wait(ts, COMMAND_DEACTIVATE);
+       if (ret != 0) {
+               dev_err(&client->dev, "could not deactivate device, %d\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
+{
+       struct i2c_client *client = ts->client;
+       const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+       struct zforce_point point;
+       int count, i, num = 0;
+
+       count = payload[0];
+       if (count > ZFORCE_REPORT_POINTS) {
+               dev_warn(&client->dev, "to many coordinates %d, expected max %d\n",
+                        count, ZFORCE_REPORT_POINTS);
+               count = ZFORCE_REPORT_POINTS;
+       }
+
+       for (i = 0; i < count; i++) {
+               point.coord_x =
+                       payload[9 * i + 2] << 8 | payload[9 * i + 1];
+               point.coord_y =
+                       payload[9 * i + 4] << 8 | payload[9 * i + 3];
+
+               if (point.coord_x > pdata->x_max ||
+                   point.coord_y > pdata->y_max) {
+                       dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
+                               point.coord_x, point.coord_y);
+                       point.coord_x = point.coord_y = 0;
+               }
+
+               point.state = payload[9 * i + 5] & 0x03;
+               point.id = (payload[9 * i + 5] & 0xfc) >> 2;
+
+               /* determine touch major, minor and orientation */
+               point.area_major = max(payload[9 * i + 6],
+                                         payload[9 * i + 7]);
+               point.area_minor = min(payload[9 * i + 6],
+                                         payload[9 * i + 7]);
+               point.orientation = payload[9 * i + 6] > payload[9 * i + 7];
+
+               point.pressure = payload[9 * i + 8];
+               point.prblty = payload[9 * i + 9];
+
+               dev_dbg(&client->dev,
+                       "point %d/%d: state %d, id %d, pressure %d, prblty %d, x %d, y %d, amajor %d, aminor %d, ori %d\n",
+                       i, count, point.state, point.id,
+                       point.pressure, point.prblty,
+                       point.coord_x, point.coord_y,
+                       point.area_major, point.area_minor,
+                       point.orientation);
+
+               /* the zforce id starts with "1", so needs to be decreased */
+               input_mt_slot(ts->input, point.id - 1);
+
+               input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,
+                                               point.state != STATE_UP);
+
+               if (point.state != STATE_UP) {
+                       input_report_abs(ts->input, ABS_MT_POSITION_X,
+                                        point.coord_x);
+                       input_report_abs(ts->input, ABS_MT_POSITION_Y,
+                                        point.coord_y);
+                       input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+                                        point.area_major);
+                       input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+                                        point.area_minor);
+                       input_report_abs(ts->input, ABS_MT_ORIENTATION,
+                                        point.orientation);
+                       num++;
+               }
+       }
+
+       input_mt_sync_frame(ts->input);
+
+       input_mt_report_finger_count(ts->input, num);
+
+       input_sync(ts->input);
+
+       return 0;
+}
+
+static int zforce_read_packet(struct zforce_ts *ts, u8 *buf)
+{
+       struct i2c_client *client = ts->client;
+       int ret;
+
+       mutex_lock(&ts->access_mutex);
+
+       /* read 2 byte message header */
+       ret = i2c_master_recv(client, buf, 2);
+       if (ret < 0) {
+               dev_err(&client->dev, "error reading header: %d\n", ret);
+               goto unlock;
+       }
+
+       if (buf[PAYLOAD_HEADER] != FRAME_START) {
+               dev_err(&client->dev, "invalid frame start: %d\n", buf[0]);
+               ret = -EIO;
+               goto unlock;
+       }
+
+       if (buf[PAYLOAD_LENGTH] <= 0 || buf[PAYLOAD_LENGTH] > 255) {
+               dev_err(&client->dev, "invalid payload length: %d\n",
+                       buf[PAYLOAD_LENGTH]);
+               ret = -EIO;
+               goto unlock;
+       }
+
+       /* read the message */
+       ret = i2c_master_recv(client, &buf[PAYLOAD_BODY], buf[PAYLOAD_LENGTH]);
+       if (ret < 0) {
+               dev_err(&client->dev, "error reading payload: %d\n", ret);
+               goto unlock;
+       }
+
+       dev_dbg(&client->dev, "read %d bytes for response command 0x%x\n",
+               buf[PAYLOAD_LENGTH], buf[PAYLOAD_BODY]);
+
+unlock:
+       mutex_unlock(&ts->access_mutex);
+       return ret;
+}
+
+static void zforce_complete(struct zforce_ts *ts, int cmd, int result)
+{
+       struct i2c_client *client = ts->client;
+
+       if (ts->command_waiting == cmd) {
+               dev_dbg(&client->dev, "completing command 0x%x\n", cmd);
+               ts->command_result = result;
+               complete(&ts->command_done);
+       } else {
+               dev_dbg(&client->dev, "command %d not for us\n", cmd);
+       }
+}
+
+static irqreturn_t zforce_interrupt(int irq, void *dev_id)
+{
+       struct zforce_ts *ts = dev_id;
+       struct i2c_client *client = ts->client;
+       const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+       int ret;
+       u8 payload_buffer[512];
+       u8 *payload;
+
+       /*
+        * When suspended, emit a wakeup signal if necessary and return.
+        * Due to the level-interrupt we will get re-triggered later.
+        */
+       if (ts->suspended) {
+               if (device_may_wakeup(&client->dev))
+                       pm_wakeup_event(&client->dev, 500);
+               msleep(20);
+               return IRQ_HANDLED;
+       }
+
+       dev_dbg(&client->dev, "handling interrupt\n");
+
+       /* Don't emit wakeup events from commands run by zforce_suspend */
+       if (!ts->suspending && device_may_wakeup(&client->dev))
+               pm_stay_awake(&client->dev);
+
+       while (!gpio_get_value(pdata->gpio_int)) {
+               ret = zforce_read_packet(ts, payload_buffer);
+               if (ret < 0) {
+                       dev_err(&client->dev, "could not read packet, ret: %d\n",
+                               ret);
+                       break;
+               }
+
+               payload =  &payload_buffer[PAYLOAD_BODY];
+
+               switch (payload[RESPONSE_ID]) {
+               case NOTIFICATION_TOUCH:
+                       /*
+                        * Always report touch-events received while
+                        * suspending, when being a wakeup source
+                        */
+                       if (ts->suspending && device_may_wakeup(&client->dev))
+                               pm_wakeup_event(&client->dev, 500);
+                       zforce_touch_event(ts, &payload[RESPONSE_DATA]);
+                       break;
+
+               case NOTIFICATION_BOOTCOMPLETE:
+                       ts->boot_complete = payload[RESPONSE_DATA];
+                       zforce_complete(ts, payload[RESPONSE_ID], 0);
+                       break;
+
+               case RESPONSE_INITIALIZE:
+               case RESPONSE_DEACTIVATE:
+               case RESPONSE_SETCONFIG:
+               case RESPONSE_RESOLUTION:
+               case RESPONSE_SCANFREQ:
+                       zforce_complete(ts, payload[RESPONSE_ID],
+                                       payload[RESPONSE_DATA]);
+                       break;
+
+               case RESPONSE_STATUS:
+                       /*
+                        * Version Payload Results
+                        * [2:major] [2:minor] [2:build] [2:rev]
+                        */
+                       ts->version_major = (payload[RESPONSE_DATA + 1] << 8) |
+                                               payload[RESPONSE_DATA];
+                       ts->version_minor = (payload[RESPONSE_DATA + 3] << 8) |
+                                               payload[RESPONSE_DATA + 2];
+                       ts->version_build = (payload[RESPONSE_DATA + 5] << 8) |
+                                               payload[RESPONSE_DATA + 4];
+                       ts->version_rev   = (payload[RESPONSE_DATA + 7] << 8) |
+                                               payload[RESPONSE_DATA + 6];
+                       dev_dbg(&ts->client->dev, "Firmware Version %04x:%04x %04x:%04x\n",
+                               ts->version_major, ts->version_minor,
+                               ts->version_build, ts->version_rev);
+
+                       zforce_complete(ts, payload[RESPONSE_ID], 0);
+                       break;
+
+               case NOTIFICATION_INVALID_COMMAND:
+                       dev_err(&ts->client->dev, "invalid command: 0x%x\n",
+                               payload[RESPONSE_DATA]);
+                       break;
+
+               default:
+                       dev_err(&ts->client->dev, "unrecognized response id: 0x%x\n",
+                               payload[RESPONSE_ID]);
+                       break;
+               }
+       }
+
+       if (!ts->suspending && device_may_wakeup(&client->dev))
+               pm_relax(&client->dev);
+
+       dev_dbg(&client->dev, "finished interrupt\n");
+
+       return IRQ_HANDLED;
+}
+
+static int zforce_input_open(struct input_dev *dev)
+{
+       struct zforce_ts *ts = input_get_drvdata(dev);
+       int ret;
+
+       ret = zforce_start(ts);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void zforce_input_close(struct input_dev *dev)
+{
+       struct zforce_ts *ts = input_get_drvdata(dev);
+       struct i2c_client *client = ts->client;
+       int ret;
+
+       ret = zforce_stop(ts);
+       if (ret)
+               dev_warn(&client->dev, "stopping zforce failed\n");
+
+       return;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int zforce_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct zforce_ts *ts = i2c_get_clientdata(client);
+       struct input_dev *input = ts->input;
+       int ret = 0;
+
+       mutex_lock(&input->mutex);
+       ts->suspending = true;
+
+       /*
+        * When configured as a wakeup source device should always wake
+        * the system, therefore start device if necessary.
+        */
+       if (device_may_wakeup(&client->dev)) {
+               dev_dbg(&client->dev, "suspend while being a wakeup source\n");
+
+               /* Need to start device, if not open, to be a wakeup source. */
+               if (!input->users) {
+                       ret = zforce_start(ts);
+                       if (ret)
+                               goto unlock;
+               }
+
+               enable_irq_wake(client->irq);
+       } else if (input->users) {
+               dev_dbg(&client->dev, "suspend without being a wakeup source\n");
+
+               ret = zforce_stop(ts);
+               if (ret)
+                       goto unlock;
+
+               disable_irq(client->irq);
+       }
+
+       ts->suspended = true;
+
+unlock:
+       ts->suspending = false;
+       mutex_unlock(&input->mutex);
+
+       return ret;
+}
+
+static int zforce_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct zforce_ts *ts = i2c_get_clientdata(client);
+       struct input_dev *input = ts->input;
+       int ret = 0;
+
+       mutex_lock(&input->mutex);
+
+       ts->suspended = false;
+
+       if (device_may_wakeup(&client->dev)) {
+               dev_dbg(&client->dev, "resume from being a wakeup source\n");
+
+               disable_irq_wake(client->irq);
+
+               /* need to stop device if it was not open on suspend */
+               if (!input->users) {
+                       ret = zforce_stop(ts);
+                       if (ret)
+                               goto unlock;
+               }
+       } else if (input->users) {
+               dev_dbg(&client->dev, "resume without being a wakeup source\n");
+
+               enable_irq(client->irq);
+
+               ret = zforce_start(ts);
+               if (ret < 0)
+                       goto unlock;
+       }
+
+unlock:
+       mutex_unlock(&input->mutex);
+
+       return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(zforce_pm_ops, zforce_suspend, zforce_resume);
+
+static void zforce_reset(void *data)
+{
+       struct zforce_ts *ts = data;
+
+       gpio_set_value(ts->pdata->gpio_rst, 0);
+}
+
+static int zforce_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+       struct zforce_ts *ts;
+       struct input_dev *input_dev;
+       int ret;
+
+       if (!pdata)
+               return -EINVAL;
+
+       ts = devm_kzalloc(&client->dev, sizeof(struct zforce_ts), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       ret = devm_gpio_request_one(&client->dev, pdata->gpio_int, GPIOF_IN,
+                                   "zforce_ts_int");
+       if (ret) {
+               dev_err(&client->dev, "request of gpio %d failed, %d\n",
+                       pdata->gpio_int, ret);
+               return ret;
+       }
+
+       ret = devm_gpio_request_one(&client->dev, pdata->gpio_rst,
+                                   GPIOF_OUT_INIT_LOW, "zforce_ts_rst");
+       if (ret) {
+               dev_err(&client->dev, "request of gpio %d failed, %d\n",
+                       pdata->gpio_rst, ret);
+               return ret;
+       }
+
+       ret = devm_add_action(&client->dev, zforce_reset, ts);
+       if (ret) {
+               dev_err(&client->dev, "failed to register reset action, %d\n",
+                       ret);
+               return ret;
+       }
+
+       snprintf(ts->phys, sizeof(ts->phys),
+                "%s/input0", dev_name(&client->dev));
+
+       input_dev = devm_input_allocate_device(&client->dev);
+       if (!input_dev) {
+               dev_err(&client->dev, "could not allocate input device\n");
+               return -ENOMEM;
+       }
+
+       mutex_init(&ts->access_mutex);
+       mutex_init(&ts->command_mutex);
+
+       ts->pdata = pdata;
+       ts->client = client;
+       ts->input = input_dev;
+
+       input_dev->name = "Neonode zForce touchscreen";
+       input_dev->phys = ts->phys;
+       input_dev->id.bustype = BUS_I2C;
+
+       input_dev->open = zforce_input_open;
+       input_dev->close = zforce_input_close;
+
+       __set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(EV_SYN, input_dev->evbit);
+       __set_bit(EV_ABS, input_dev->evbit);
+
+       /* For multi touch */
+       input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+                            pdata->x_max, 0, 0);
+       input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+                            pdata->y_max, 0, 0);
+
+       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+                            ZFORCE_MAX_AREA, 0, 0);
+       input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
+                            ZFORCE_MAX_AREA, 0, 0);
+       input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+       input_mt_init_slots(input_dev, ZFORCE_REPORT_POINTS, INPUT_MT_DIRECT);
+
+       input_set_drvdata(ts->input, ts);
+
+       init_completion(&ts->command_done);
+
+       /*
+        * The zforce pulls the interrupt low when it has data ready.
+        * After it is triggered the isr thread runs until all the available
+        * packets have been read and the interrupt is high again.
+        * Therefore we can trigger the interrupt anytime it is low and do
+        * not need to limit it to the interrupt edge.
+        */
+       ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+                                       zforce_interrupt,
+                                       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                       input_dev->name, ts);
+       if (ret) {
+               dev_err(&client->dev, "irq %d request failed\n", client->irq);
+               return ret;
+       }
+
+       i2c_set_clientdata(client, ts);
+
+       /* let the controller boot */
+       gpio_set_value(pdata->gpio_rst, 1);
+
+       ts->command_waiting = NOTIFICATION_BOOTCOMPLETE;
+       if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0)
+               dev_warn(&client->dev, "bootcomplete timed out\n");
+
+       /* need to start device to get version information */
+       ret = zforce_command_wait(ts, COMMAND_INITIALIZE);
+       if (ret) {
+               dev_err(&client->dev, "unable to initialize, %d\n", ret);
+               return ret;
+       }
+
+       /* this gets the firmware version among other informations */
+       ret = zforce_command_wait(ts, COMMAND_STATUS);
+       if (ret < 0) {
+               dev_err(&client->dev, "couldn't get status, %d\n", ret);
+               zforce_stop(ts);
+               return ret;
+       }
+
+       /* stop device and put it into sleep until it is opened */
+       ret = zforce_stop(ts);
+       if (ret < 0)
+               return ret;
+
+       device_set_wakeup_capable(&client->dev, true);
+
+       ret = input_register_device(input_dev);
+       if (ret) {
+               dev_err(&client->dev, "could not register input device, %d\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct i2c_device_id zforce_idtable[] = {
+       { "zforce-ts", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, zforce_idtable);
+
+static struct i2c_driver zforce_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "zforce-ts",
+               .pm     = &zforce_pm_ops,
+       },
+       .probe          = zforce_probe,
+       .id_table       = zforce_idtable,
+};
+
+module_i2c_driver(zforce_driver);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("zForce TouchScreen Driver");
+MODULE_LICENSE("GPL");
index c880eba..3e7fdbb 100644 (file)
@@ -206,7 +206,7 @@ config SHMOBILE_IPMMU_TLB
 config SHMOBILE_IOMMU
        bool "IOMMU for Renesas IPMMU/IPMMUI"
        default n
-       depends on (ARM && ARCH_SHMOBILE)
+       depends on ARM
        select IOMMU_API
        select ARM_DMA_USE_IOMMU
        select SHMOBILE_IPMMU
index 14c1f47..5d58bf1 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_IOMMU_API) += iommu.o
+obj-$(CONFIG_IOMMU_API) += iommu-traces.o
 obj-$(CONFIG_OF_IOMMU) += of_iommu.o
 obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
 obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
index 181c9ba..1abfb56 100644 (file)
@@ -590,6 +590,9 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
                ret = IRQ_HANDLED;
                resume = RESUME_RETRY;
        } else {
+               dev_err_ratelimited(smmu->dev,
+                   "Unhandled context fault: iova=0x%08lx, fsynr=0x%x, cb=%d\n",
+                   iova, fsynr, root_cfg->cbndx);
                ret = IRQ_NONE;
                resume = RESUME_TERMINATE;
        }
@@ -778,7 +781,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
 #ifdef __BIG_ENDIAN
        reg |= SCTLR_E;
 #endif
-       writel(reg, cb_base + ARM_SMMU_CB_SCTLR);
+       writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR);
 }
 
 static int arm_smmu_init_domain_context(struct iommu_domain *domain,
@@ -1212,7 +1215,10 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
 
                arm_smmu_flush_pgtable(smmu, page_address(table),
                                       ARM_SMMU_PTE_HWTABLE_SIZE);
-               pgtable_page_ctor(table);
+               if (!pgtable_page_ctor(table)) {
+                       __free_page(table);
+                       return -ENOMEM;
+               }
                pmd_populate(NULL, pmd, table);
                arm_smmu_flush_pgtable(smmu, pmd, sizeof(*pmd));
        }
@@ -1559,9 +1565,13 @@ static struct iommu_ops arm_smmu_ops = {
 static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 {
        void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
-       void __iomem *sctlr_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB_SCTLR;
+       void __iomem *cb_base;
        int i = 0;
-       u32 scr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0);
+       u32 reg;
+
+       /* Clear Global FSR */
+       reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
+       writel(reg, gr0_base + ARM_SMMU_GR0_sGFSR);
 
        /* Mark all SMRn as invalid and all S2CRn as bypass */
        for (i = 0; i < smmu->num_mapping_groups; ++i) {
@@ -1569,33 +1579,38 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
                writel_relaxed(S2CR_TYPE_BYPASS, gr0_base + ARM_SMMU_GR0_S2CR(i));
        }
 
-       /* Make sure all context banks are disabled */
-       for (i = 0; i < smmu->num_context_banks; ++i)
-               writel_relaxed(0, sctlr_base + ARM_SMMU_CB(smmu, i));
+       /* Make sure all context banks are disabled and clear CB_FSR  */
+       for (i = 0; i < smmu->num_context_banks; ++i) {
+               cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, i);
+               writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
+               writel_relaxed(FSR_FAULT, cb_base + ARM_SMMU_CB_FSR);
+       }
 
        /* Invalidate the TLB, just in case */
        writel_relaxed(0, gr0_base + ARM_SMMU_GR0_STLBIALL);
        writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH);
        writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH);
 
+       reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0);
+
        /* Enable fault reporting */
-       scr0 |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE);
+       reg |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE);
 
        /* Disable TLB broadcasting. */
-       scr0 |= (sCR0_VMIDPNE | sCR0_PTM);
+       reg |= (sCR0_VMIDPNE | sCR0_PTM);
 
        /* Enable client access, but bypass when no mapping is found */
-       scr0 &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
+       reg &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
 
        /* Disable forced broadcasting */
-       scr0 &= ~sCR0_FB;
+       reg &= ~sCR0_FB;
 
        /* Don't upgrade barriers */
-       scr0 &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT);
+       reg &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT);
 
        /* Push the button */
        arm_smmu_tlb_sync(smmu);
-       writel(scr0, gr0_base + ARM_SMMU_GR0_sCR0);
+       writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_sCR0);
 }
 
 static int arm_smmu_id_size_to_bits(int size)
@@ -1700,13 +1715,12 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
        id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID1);
        smmu->pagesize = (id & ID1_PAGESIZE) ? SZ_64K : SZ_4K;
 
-       /* Check that we ioremapped enough */
+       /* Check for size mismatch of SMMU address space from mapped region */
        size = 1 << (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1);
        size *= (smmu->pagesize << 1);
-       if (smmu->size < size)
-               dev_warn(smmu->dev,
-                        "device is 0x%lx bytes but only mapped 0x%lx!\n",
-                        size, smmu->size);
+       if (smmu->size != size)
+               dev_warn(smmu->dev, "SMMU address space size (0x%lx) differs "
+                       "from mapped region size (0x%lx)!\n", size, smmu->size);
 
        smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) &
                                      ID1_NUMS2CB_MASK;
@@ -1781,15 +1795,10 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
        smmu->dev = dev;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "missing base address/size\n");
-               return -ENODEV;
-       }
-
+       smmu->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(smmu->base))
+               return PTR_ERR(smmu->base);
        smmu->size = resource_size(res);
-       smmu->base = devm_request_and_ioremap(dev, res);
-       if (!smmu->base)
-               return -EADDRNOTAVAIL;
 
        if (of_property_read_u32(dev->of_node, "#global-interrupts",
                                 &smmu->num_global_irqs)) {
@@ -1804,12 +1813,11 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
                        smmu->num_context_irqs++;
        }
 
-       if (num_irqs < smmu->num_global_irqs) {
-               dev_warn(dev, "found %d interrupts but expected at least %d\n",
-                        num_irqs, smmu->num_global_irqs);
-               smmu->num_global_irqs = num_irqs;
+       if (!smmu->num_context_irqs) {
+               dev_err(dev, "found %d interrupts but expected at least %d\n",
+                       num_irqs, smmu->num_global_irqs + 1);
+               return -ENODEV;
        }
-       smmu->num_context_irqs = num_irqs - smmu->num_global_irqs;
 
        smmu->irqs = devm_kzalloc(dev, sizeof(*smmu->irqs) * num_irqs,
                                  GFP_KERNEL);
@@ -1933,7 +1941,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
                free_irq(smmu->irqs[i], smmu);
 
        /* Turn the thing off */
-       writel(sCR0_CLIENTPD, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_sCR0);
+       writel_relaxed(sCR0_CLIENTPD, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_sCR0);
        return 0;
 }
 
@@ -1981,7 +1989,7 @@ static void __exit arm_smmu_exit(void)
        return platform_driver_unregister(&arm_smmu_driver);
 }
 
-module_init(arm_smmu_init);
+subsys_initcall(arm_smmu_init);
 module_exit(arm_smmu_exit);
 
 MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations");
index 9009469..8b452c9 100644 (file)
@@ -403,7 +403,7 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev)
 
        dev = pci_physfn(dev);
 
-       list_for_each_entry(dmaru, &dmar_drhd_units, list) {
+       for_each_drhd_unit(dmaru) {
                drhd = container_of(dmaru->hdr,
                                    struct acpi_dmar_hardware_unit,
                                    header);
index 15e9b57..43b9bfe 100644 (file)
@@ -782,7 +782,11 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
        int offset;
 
        BUG_ON(!domain->pgd);
-       BUG_ON(addr_width < BITS_PER_LONG && pfn >> addr_width);
+
+       if (addr_width < BITS_PER_LONG && pfn >> addr_width)
+               /* Address beyond IOMMU's addressing capabilities. */
+               return NULL;
+
        parent = domain->pgd;
 
        while (level > 0) {
@@ -3777,11 +3781,10 @@ static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
 static void domain_remove_one_dev_info(struct dmar_domain *domain,
                                          struct pci_dev *pdev)
 {
-       struct device_domain_info *info;
+       struct device_domain_info *info, *tmp;
        struct intel_iommu *iommu;
        unsigned long flags;
        int found = 0;
-       struct list_head *entry, *tmp;
 
        iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number,
                                pdev->devfn);
@@ -3789,8 +3792,7 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain,
                return;
 
        spin_lock_irqsave(&device_domain_lock, flags);
-       list_for_each_safe(entry, tmp, &domain->devices) {
-               info = list_entry(entry, struct device_domain_info, link);
+       list_for_each_entry_safe(info, tmp, &domain->devices, link) {
                if (info->segment == pci_domain_nr(pdev->bus) &&
                    info->bus == pdev->bus->number &&
                    info->devfn == pdev->devfn) {
index ab86902..bab10b1 100644 (file)
@@ -525,12 +525,13 @@ static int __init intel_irq_remapping_supported(void)
        if (disable_irq_remap)
                return 0;
        if (irq_remap_broken) {
-               WARN_TAINT(1, TAINT_FIRMWARE_WORKAROUND,
-                          "This system BIOS has enabled interrupt remapping\n"
-                          "on a chipset that contains an erratum making that\n"
-                          "feature unstable.  To maintain system stability\n"
-                          "interrupt remapping is being disabled.  Please\n"
-                          "contact your BIOS vendor for an update\n");
+               printk(KERN_WARNING
+                       "This system BIOS has enabled interrupt remapping\n"
+                       "on a chipset that contains an erratum making that\n"
+                       "feature unstable.  To maintain system stability\n"
+                       "interrupt remapping is being disabled.  Please\n"
+                       "contact your BIOS vendor for an update\n");
+               add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
                disable_irq_remap = 1;
                return 0;
        }
diff --git a/drivers/iommu/iommu-traces.c b/drivers/iommu/iommu-traces.c
new file mode 100644 (file)
index 0000000..bf3b317
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * iommu trace points
+ *
+ * Copyright (C) 2013 Shuah Khan <shuah.kh@samsung.com>
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/types.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/iommu.h>
+
+/* iommu_group_event */
+EXPORT_TRACEPOINT_SYMBOL_GPL(add_device_to_group);
+EXPORT_TRACEPOINT_SYMBOL_GPL(remove_device_from_group);
+
+/* iommu_device_event */
+EXPORT_TRACEPOINT_SYMBOL_GPL(attach_device_to_domain);
+EXPORT_TRACEPOINT_SYMBOL_GPL(detach_device_from_domain);
+
+/* iommu_map_unmap */
+EXPORT_TRACEPOINT_SYMBOL_GPL(map);
+EXPORT_TRACEPOINT_SYMBOL_GPL(unmap);
+
+/* iommu_error */
+EXPORT_TRACEPOINT_SYMBOL_GPL(io_page_fault);
index fbe9ca7..e5555fc 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/idr.h>
 #include <linux/notifier.h>
 #include <linux/err.h>
+#include <trace/events/iommu.h>
 
 static struct kset *iommu_group_kset;
 static struct ida iommu_group_ida;
@@ -363,6 +364,8 @@ rename:
        /* Notify any listeners about change to group. */
        blocking_notifier_call_chain(&group->notifier,
                                     IOMMU_GROUP_NOTIFY_ADD_DEVICE, dev);
+
+       trace_add_device_to_group(group->id, dev);
        return 0;
 }
 EXPORT_SYMBOL_GPL(iommu_group_add_device);
@@ -399,6 +402,8 @@ void iommu_group_remove_device(struct device *dev)
        sysfs_remove_link(group->devices_kobj, device->name);
        sysfs_remove_link(&dev->kobj, "iommu_group");
 
+       trace_remove_device_from_group(group->id, dev);
+
        kfree(device->name);
        kfree(device);
        dev->iommu_group = NULL;
@@ -680,10 +685,14 @@ EXPORT_SYMBOL_GPL(iommu_domain_free);
 
 int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
 {
+       int ret;
        if (unlikely(domain->ops->attach_dev == NULL))
                return -ENODEV;
 
-       return domain->ops->attach_dev(domain, dev);
+       ret = domain->ops->attach_dev(domain, dev);
+       if (!ret)
+               trace_attach_device_to_domain(dev);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(iommu_attach_device);
 
@@ -693,6 +702,7 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
                return;
 
        domain->ops->detach_dev(domain, dev);
+       trace_detach_device_from_domain(dev);
 }
 EXPORT_SYMBOL_GPL(iommu_detach_device);
 
@@ -807,17 +817,17 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
         * size of the smallest page supported by the hardware
         */
        if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
-               pr_err("unaligned: iova 0x%lx pa 0x%pa size 0x%zx min_pagesz 0x%x\n",
+               pr_err("unaligned: iova 0x%lx pa %pa size 0x%zx min_pagesz 0x%x\n",
                       iova, &paddr, size, min_pagesz);
                return -EINVAL;
        }
 
-       pr_debug("map: iova 0x%lx pa 0x%pa size 0x%zx\n", iova, &paddr, size);
+       pr_debug("map: iova 0x%lx pa %pa size 0x%zx\n", iova, &paddr, size);
 
        while (size) {
                size_t pgsize = iommu_pgsize(domain, iova | paddr, size);
 
-               pr_debug("mapping: iova 0x%lx pa 0x%pa pgsize 0x%zx\n",
+               pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
                         iova, &paddr, pgsize);
 
                ret = domain->ops->map(domain, iova, paddr, pgsize, prot);
@@ -832,6 +842,8 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
        /* unroll mapping in case something went wrong */
        if (ret)
                iommu_unmap(domain, orig_iova, orig_size - size);
+       else
+               trace_map(iova, paddr, size);
 
        return ret;
 }
@@ -880,6 +892,7 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
                unmapped += unmapped_page;
        }
 
+       trace_unmap(iova, 0, size);
        return unmapped;
 }
 EXPORT_SYMBOL_GPL(iommu_unmap);
index 108c0e9..dba1a9f 100644 (file)
@@ -252,7 +252,7 @@ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova,
        spin_lock_irqsave(&gart->pte_lock, flags);
        pfn = __phys_to_pfn(pa);
        if (!pfn_valid(pfn)) {
-               dev_err(gart->dev, "Invalid page: %08x\n", pa);
+               dev_err(gart->dev, "Invalid page: %pa\n", &pa);
                spin_unlock_irqrestore(&gart->pte_lock, flags);
                return -EINVAL;
        }
@@ -295,8 +295,8 @@ static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain,
 
        pa = (pte & GART_PAGE_MASK);
        if (!pfn_valid(__phys_to_pfn(pa))) {
-               dev_err(gart->dev, "No entry for %08llx:%08x\n",
-                        (unsigned long long)iova, pa);
+               dev_err(gart->dev, "No entry for %08llx:%pa\n",
+                        (unsigned long long)iova, &pa);
                gart_dump_table(gart);
                return -EINVAL;
        }
@@ -351,7 +351,6 @@ static int tegra_gart_probe(struct platform_device *pdev)
        struct gart_device *gart;
        struct resource *res, *res_remap;
        void __iomem *gart_regs;
-       int err;
        struct device *dev = &pdev->dev;
 
        if (gart_handle)
@@ -376,8 +375,7 @@ static int tegra_gart_probe(struct platform_device *pdev)
        gart_regs = devm_ioremap(dev, res->start, resource_size(res));
        if (!gart_regs) {
                dev_err(dev, "failed to remap GART registers\n");
-               err = -ENXIO;
-               goto fail;
+               return -ENXIO;
        }
 
        gart->dev = &pdev->dev;
@@ -391,8 +389,7 @@ static int tegra_gart_probe(struct platform_device *pdev)
        gart->savedata = vmalloc(sizeof(u32) * gart->page_count);
        if (!gart->savedata) {
                dev_err(dev, "failed to allocate context save area\n");
-               err = -ENOMEM;
-               goto fail;
+               return -ENOMEM;
        }
 
        platform_set_drvdata(pdev, gart);
@@ -401,32 +398,20 @@ static int tegra_gart_probe(struct platform_device *pdev)
        gart_handle = gart;
        bus_set_iommu(&platform_bus_type, &gart_iommu_ops);
        return 0;
-
-fail:
-       if (gart_regs)
-               devm_iounmap(dev, gart_regs);
-       if (gart && gart->savedata)
-               vfree(gart->savedata);
-       devm_kfree(dev, gart);
-       return err;
 }
 
 static int tegra_gart_remove(struct platform_device *pdev)
 {
        struct gart_device *gart = platform_get_drvdata(pdev);
-       struct device *dev = gart->dev;
 
        writel(0, gart->regs + GART_CONFIG);
        if (gart->savedata)
                vfree(gart->savedata);
-       if (gart->regs)
-               devm_iounmap(dev, gart->regs);
-       devm_kfree(dev, gart);
        gart_handle = NULL;
        return 0;
 }
 
-const struct dev_pm_ops tegra_gart_pm_ops = {
+static const struct dev_pm_ops tegra_gart_pm_ops = {
        .suspend        = tegra_gart_suspend,
        .resume         = tegra_gart_resume,
 };
index e066560..605b5b4 100644 (file)
@@ -731,7 +731,7 @@ static int smmu_iommu_map(struct iommu_domain *domain, unsigned long iova,
        unsigned long pfn = __phys_to_pfn(pa);
        unsigned long flags;
 
-       dev_dbg(as->smmu->dev, "[%d] %08lx:%08x\n", as->asid, iova, pa);
+       dev_dbg(as->smmu->dev, "[%d] %08lx:%pa\n", as->asid, iova, &pa);
 
        if (!pfn_valid(pfn))
                return -ENOMEM;
@@ -1254,7 +1254,7 @@ static int tegra_smmu_remove(struct platform_device *pdev)
        return 0;
 }
 
-const struct dev_pm_ops tegra_smmu_pm_ops = {
+static const struct dev_pm_ops tegra_smmu_pm_ops = {
        .suspend        = tegra_smmu_suspend,
        .resume         = tegra_smmu_resume,
 };
index baf2686..02125e6 100644 (file)
@@ -1083,8 +1083,10 @@ isdnloop_start(isdnloop_card *card, isdnloop_sdef *sdefp)
                        spin_unlock_irqrestore(&card->isdnloop_lock, flags);
                        return -ENOMEM;
                }
-               for (i = 0; i < 3; i++)
-                       strcpy(card->s0num[i], sdef.num[i]);
+               for (i = 0; i < 3; i++) {
+                       strlcpy(card->s0num[i], sdef.num[i],
+                               sizeof(card->s0num[0]));
+               }
                break;
        case ISDN_PTYPE_1TR6:
                if (isdnloop_fake(card, "DRV1.04TC-1TR6-CAPI-CNS-BASIS-29.11.95",
@@ -1097,7 +1099,7 @@ isdnloop_start(isdnloop_card *card, isdnloop_sdef *sdefp)
                        spin_unlock_irqrestore(&card->isdnloop_lock, flags);
                        return -ENOMEM;
                }
-               strcpy(card->s0num[0], sdef.num[0]);
+               strlcpy(card->s0num[0], sdef.num[0], sizeof(card->s0num[0]));
                card->s0num[1][0] = '\0';
                card->s0num[2][0] = '\0';
                break;
index e47dcb9..5cefb47 100644 (file)
@@ -117,7 +117,6 @@ mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 {
        struct sk_buff          *skb;
        struct sock             *sk = sock->sk;
-       struct sockaddr_mISDN   *maddr;
 
        int             copied, err;
 
@@ -135,9 +134,9 @@ mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (!skb)
                return err;
 
-       if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
-               msg->msg_namelen = sizeof(struct sockaddr_mISDN);
-               maddr = (struct sockaddr_mISDN *)msg->msg_name;
+       if (msg->msg_name) {
+               struct sockaddr_mISDN *maddr = msg->msg_name;
+
                maddr->family = AF_ISDN;
                maddr->dev = _pms(sk)->dev->id;
                if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
@@ -150,11 +149,7 @@ mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
                        maddr->sapi = _pms(sk)->ch.addr & 0xFF;
                        maddr->tei =  (_pms(sk)->ch.addr >> 8) & 0xFF;
                }
-       } else {
-               if (msg->msg_namelen)
-                       printk(KERN_WARNING "%s: too small namelen %d\n",
-                              __func__, msg->msg_namelen);
-               msg->msg_namelen = 0;
+               msg->msg_namelen = sizeof(*maddr);
        }
 
        copied = skb->len + MISDN_HEADER_LEN;
index b3256ff..d0a1d8a 100644 (file)
@@ -229,7 +229,7 @@ struct lguest_vq_info {
  * make a hypercall.  We hand the physical address of the virtqueue so the Host
  * knows which virtqueue we're talking about.
  */
-static void lg_notify(struct virtqueue *vq)
+static bool lg_notify(struct virtqueue *vq)
 {
        /*
         * We store our virtqueue information in the "priv" pointer of the
@@ -238,6 +238,7 @@ static void lg_notify(struct virtqueue *vq)
        struct lguest_vq_info *lvq = vq->priv;
 
        hcall(LHCALL_NOTIFY, lvq->config.pfn << PAGE_SHIFT, 0, 0, 0);
+       return true;
 }
 
 /* An extern declaration inside a C file is bad form.  Don't do it. */
index 5169239..922a1ac 100644 (file)
@@ -157,7 +157,7 @@ static void run_guest_once(struct lg_cpu *cpu, struct lguest_pages *pages)
         * stack, then the address of this call.  This stack layout happens to
         * exactly match the stack layout created by an interrupt...
         */
-       asm volatile("pushf; lcall *lguest_entry"
+       asm volatile("pushf; lcall *%4"
                     /*
                      * This is how we tell GCC that %eax ("a") and %ebx ("b")
                      * are changed by this routine.  The "=" means output.
@@ -169,7 +169,9 @@ static void run_guest_once(struct lg_cpu *cpu, struct lguest_pages *pages)
                      * physical address of the Guest's top-level page
                      * directory.
                      */
-                    : "0"(pages), "1"(__pa(cpu->lg->pgdirs[cpu->cpu_pgd].pgdir))
+                    : "0"(pages), 
+                      "1"(__pa(cpu->lg->pgdirs[cpu->cpu_pgd].pgdir)),
+                      "m"(lguest_entry)
                     /*
                      * We tell gcc that all these registers could change,
                      * which means we don't have to save and restore them in
index f950c9d..2638417 100644 (file)
@@ -13,15 +13,8 @@ config BCACHE_DEBUG
        ---help---
        Don't select this option unless you're a developer
 
-       Enables extra debugging tools (primarily a fuzz tester)
-
-config BCACHE_EDEBUG
-       bool "Extended runtime checks"
-       depends on BCACHE
-       ---help---
-       Don't select this option unless you're a developer
-
-       Enables extra runtime checks which significantly affect performance
+       Enables extra debugging tools, allows expensive runtime checks to be
+       turned on.
 
 config BCACHE_CLOSURES_DEBUG
        bool "Debug closures"
index e45f557..2b46bf1 100644 (file)
 #include "bcache.h"
 #include "btree.h"
 
+#include <linux/blkdev.h>
 #include <linux/freezer.h>
 #include <linux/kthread.h>
 #include <linux/random.h>
 #include <trace/events/bcache.h>
 
-#define MAX_IN_FLIGHT_DISCARDS         8U
-
 /* Bucket heap / gen */
 
 uint8_t bch_inc_gen(struct cache *ca, struct bucket *b)
@@ -121,75 +120,6 @@ void bch_rescale_priorities(struct cache_set *c, int sectors)
        mutex_unlock(&c->bucket_lock);
 }
 
-/* Discard/TRIM */
-
-struct discard {
-       struct list_head        list;
-       struct work_struct      work;
-       struct cache            *ca;
-       long                    bucket;
-
-       struct bio              bio;
-       struct bio_vec          bv;
-};
-
-static void discard_finish(struct work_struct *w)
-{
-       struct discard *d = container_of(w, struct discard, work);
-       struct cache *ca = d->ca;
-       char buf[BDEVNAME_SIZE];
-
-       if (!test_bit(BIO_UPTODATE, &d->bio.bi_flags)) {
-               pr_notice("discard error on %s, disabling",
-                        bdevname(ca->bdev, buf));
-               d->ca->discard = 0;
-       }
-
-       mutex_lock(&ca->set->bucket_lock);
-
-       fifo_push(&ca->free, d->bucket);
-       list_add(&d->list, &ca->discards);
-       atomic_dec(&ca->discards_in_flight);
-
-       mutex_unlock(&ca->set->bucket_lock);
-
-       closure_wake_up(&ca->set->bucket_wait);
-       wake_up_process(ca->alloc_thread);
-
-       closure_put(&ca->set->cl);
-}
-
-static void discard_endio(struct bio *bio, int error)
-{
-       struct discard *d = container_of(bio, struct discard, bio);
-       schedule_work(&d->work);
-}
-
-static void do_discard(struct cache *ca, long bucket)
-{
-       struct discard *d = list_first_entry(&ca->discards,
-                                            struct discard, list);
-
-       list_del(&d->list);
-       d->bucket = bucket;
-
-       atomic_inc(&ca->discards_in_flight);
-       closure_get(&ca->set->cl);
-
-       bio_init(&d->bio);
-
-       d->bio.bi_sector        = bucket_to_sector(ca->set, d->bucket);
-       d->bio.bi_bdev          = ca->bdev;
-       d->bio.bi_rw            = REQ_WRITE|REQ_DISCARD;
-       d->bio.bi_max_vecs      = 1;
-       d->bio.bi_io_vec        = d->bio.bi_inline_vecs;
-       d->bio.bi_size          = bucket_bytes(ca);
-       d->bio.bi_end_io        = discard_endio;
-       bio_set_prio(&d->bio, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
-
-       submit_bio(0, &d->bio);
-}
-
 /* Allocation */
 
 static inline bool can_inc_bucket_gen(struct bucket *b)
@@ -280,7 +210,7 @@ static void invalidate_buckets_lru(struct cache *ca)
                         * multiple times when it can't do anything
                         */
                        ca->invalidate_needs_gc = 1;
-                       bch_queue_gc(ca->set);
+                       wake_up_gc(ca->set);
                        return;
                }
 
@@ -305,7 +235,7 @@ static void invalidate_buckets_fifo(struct cache *ca)
 
                if (++checked >= ca->sb.nbuckets) {
                        ca->invalidate_needs_gc = 1;
-                       bch_queue_gc(ca->set);
+                       wake_up_gc(ca->set);
                        return;
                }
        }
@@ -330,7 +260,7 @@ static void invalidate_buckets_random(struct cache *ca)
 
                if (++checked >= ca->sb.nbuckets / 2) {
                        ca->invalidate_needs_gc = 1;
-                       bch_queue_gc(ca->set);
+                       wake_up_gc(ca->set);
                        return;
                }
        }
@@ -398,16 +328,18 @@ static int bch_allocator_thread(void *arg)
                        else
                                break;
 
-                       allocator_wait(ca, (int) fifo_free(&ca->free) >
-                                      atomic_read(&ca->discards_in_flight));
-
                        if (ca->discard) {
-                               allocator_wait(ca, !list_empty(&ca->discards));
-                               do_discard(ca, bucket);
-                       } else {
-                               fifo_push(&ca->free, bucket);
-                               closure_wake_up(&ca->set->bucket_wait);
+                               mutex_unlock(&ca->set->bucket_lock);
+                               blkdev_issue_discard(ca->bdev,
+                                       bucket_to_sector(ca->set, bucket),
+                                       ca->sb.block_size, GFP_KERNEL, 0);
+                               mutex_lock(&ca->set->bucket_lock);
                        }
+
+                       allocator_wait(ca, !fifo_full(&ca->free));
+
+                       fifo_push(&ca->free, bucket);
+                       wake_up(&ca->set->bucket_wait);
                }
 
                /*
@@ -433,16 +365,40 @@ static int bch_allocator_thread(void *arg)
        }
 }
 
-long bch_bucket_alloc(struct cache *ca, unsigned watermark, struct closure *cl)
+long bch_bucket_alloc(struct cache *ca, unsigned watermark, bool wait)
 {
-       long r = -1;
-again:
+       DEFINE_WAIT(w);
+       struct bucket *b;
+       long r;
+
+       /* fastpath */
+       if (fifo_used(&ca->free) > ca->watermark[watermark]) {
+               fifo_pop(&ca->free, r);
+               goto out;
+       }
+
+       if (!wait)
+               return -1;
+
+       while (1) {
+               if (fifo_used(&ca->free) > ca->watermark[watermark]) {
+                       fifo_pop(&ca->free, r);
+                       break;
+               }
+
+               prepare_to_wait(&ca->set->bucket_wait, &w,
+                               TASK_UNINTERRUPTIBLE);
+
+               mutex_unlock(&ca->set->bucket_lock);
+               schedule();
+               mutex_lock(&ca->set->bucket_lock);
+       }
+
+       finish_wait(&ca->set->bucket_wait, &w);
+out:
        wake_up_process(ca->alloc_thread);
 
-       if (fifo_used(&ca->free) > ca->watermark[watermark] &&
-           fifo_pop(&ca->free, r)) {
-               struct bucket *b = ca->buckets + r;
-#ifdef CONFIG_BCACHE_EDEBUG
+       if (expensive_debug_checks(ca->set)) {
                size_t iter;
                long i;
 
@@ -455,36 +411,23 @@ again:
                        BUG_ON(i == r);
                fifo_for_each(i, &ca->unused, iter)
                        BUG_ON(i == r);
-#endif
-               BUG_ON(atomic_read(&b->pin) != 1);
-
-               SET_GC_SECTORS_USED(b, ca->sb.bucket_size);
-
-               if (watermark <= WATERMARK_METADATA) {
-                       SET_GC_MARK(b, GC_MARK_METADATA);
-                       b->prio = BTREE_PRIO;
-               } else {
-                       SET_GC_MARK(b, GC_MARK_RECLAIMABLE);
-                       b->prio = INITIAL_PRIO;
-               }
-
-               return r;
        }
 
-       trace_bcache_alloc_fail(ca);
+       b = ca->buckets + r;
 
-       if (cl) {
-               closure_wait(&ca->set->bucket_wait, cl);
+       BUG_ON(atomic_read(&b->pin) != 1);
 
-               if (closure_blocking(cl)) {
-                       mutex_unlock(&ca->set->bucket_lock);
-                       closure_sync(cl);
-                       mutex_lock(&ca->set->bucket_lock);
-                       goto again;
-               }
+       SET_GC_SECTORS_USED(b, ca->sb.bucket_size);
+
+       if (watermark <= WATERMARK_METADATA) {
+               SET_GC_MARK(b, GC_MARK_METADATA);
+               b->prio = BTREE_PRIO;
+       } else {
+               SET_GC_MARK(b, GC_MARK_RECLAIMABLE);
+               b->prio = INITIAL_PRIO;
        }
 
-       return -1;
+       return r;
 }
 
 void bch_bucket_free(struct cache_set *c, struct bkey *k)
@@ -501,7 +444,7 @@ void bch_bucket_free(struct cache_set *c, struct bkey *k)
 }
 
 int __bch_bucket_alloc_set(struct cache_set *c, unsigned watermark,
-                          struct bkey *k, int n, struct closure *cl)
+                          struct bkey *k, int n, bool wait)
 {
        int i;
 
@@ -514,7 +457,7 @@ int __bch_bucket_alloc_set(struct cache_set *c, unsigned watermark,
 
        for (i = 0; i < n; i++) {
                struct cache *ca = c->cache_by_alloc[i];
-               long b = bch_bucket_alloc(ca, watermark, cl);
+               long b = bch_bucket_alloc(ca, watermark, wait);
 
                if (b == -1)
                        goto err;
@@ -529,22 +472,202 @@ int __bch_bucket_alloc_set(struct cache_set *c, unsigned watermark,
        return 0;
 err:
        bch_bucket_free(c, k);
-       __bkey_put(c, k);
+       bkey_put(c, k);
        return -1;
 }
 
 int bch_bucket_alloc_set(struct cache_set *c, unsigned watermark,
-                        struct bkey *k, int n, struct closure *cl)
+                        struct bkey *k, int n, bool wait)
 {
        int ret;
        mutex_lock(&c->bucket_lock);
-       ret = __bch_bucket_alloc_set(c, watermark, k, n, cl);
+       ret = __bch_bucket_alloc_set(c, watermark, k, n, wait);
        mutex_unlock(&c->bucket_lock);
        return ret;
 }
 
+/* Sector allocator */
+
+struct open_bucket {
+       struct list_head        list;
+       unsigned                last_write_point;
+       unsigned                sectors_free;
+       BKEY_PADDED(key);
+};
+
+/*
+ * We keep multiple buckets open for writes, and try to segregate different
+ * write streams for better cache utilization: first we look for a bucket where
+ * the last write to it was sequential with the current write, and failing that
+ * we look for a bucket that was last used by the same task.
+ *
+ * The ideas is if you've got multiple tasks pulling data into the cache at the
+ * same time, you'll get better cache utilization if you try to segregate their
+ * data and preserve locality.
+ *
+ * For example, say you've starting Firefox at the same time you're copying a
+ * bunch of files. Firefox will likely end up being fairly hot and stay in the
+ * cache awhile, but the data you copied might not be; if you wrote all that
+ * data to the same buckets it'd get invalidated at the same time.
+ *
+ * Both of those tasks will be doing fairly random IO so we can't rely on
+ * detecting sequential IO to segregate their data, but going off of the task
+ * should be a sane heuristic.
+ */
+static struct open_bucket *pick_data_bucket(struct cache_set *c,
+                                           const struct bkey *search,
+                                           unsigned write_point,
+                                           struct bkey *alloc)
+{
+       struct open_bucket *ret, *ret_task = NULL;
+
+       list_for_each_entry_reverse(ret, &c->data_buckets, list)
+               if (!bkey_cmp(&ret->key, search))
+                       goto found;
+               else if (ret->last_write_point == write_point)
+                       ret_task = ret;
+
+       ret = ret_task ?: list_first_entry(&c->data_buckets,
+                                          struct open_bucket, list);
+found:
+       if (!ret->sectors_free && KEY_PTRS(alloc)) {
+               ret->sectors_free = c->sb.bucket_size;
+               bkey_copy(&ret->key, alloc);
+               bkey_init(alloc);
+       }
+
+       if (!ret->sectors_free)
+               ret = NULL;
+
+       return ret;
+}
+
+/*
+ * Allocates some space in the cache to write to, and k to point to the newly
+ * allocated space, and updates KEY_SIZE(k) and KEY_OFFSET(k) (to point to the
+ * end of the newly allocated space).
+ *
+ * May allocate fewer sectors than @sectors, KEY_SIZE(k) indicates how many
+ * sectors were actually allocated.
+ *
+ * If s->writeback is true, will not fail.
+ */
+bool bch_alloc_sectors(struct cache_set *c, struct bkey *k, unsigned sectors,
+                      unsigned write_point, unsigned write_prio, bool wait)
+{
+       struct open_bucket *b;
+       BKEY_PADDED(key) alloc;
+       unsigned i;
+
+       /*
+        * We might have to allocate a new bucket, which we can't do with a
+        * spinlock held. So if we have to allocate, we drop the lock, allocate
+        * and then retry. KEY_PTRS() indicates whether alloc points to
+        * allocated bucket(s).
+        */
+
+       bkey_init(&alloc.key);
+       spin_lock(&c->data_bucket_lock);
+
+       while (!(b = pick_data_bucket(c, k, write_point, &alloc.key))) {
+               unsigned watermark = write_prio
+                       ? WATERMARK_MOVINGGC
+                       : WATERMARK_NONE;
+
+               spin_unlock(&c->data_bucket_lock);
+
+               if (bch_bucket_alloc_set(c, watermark, &alloc.key, 1, wait))
+                       return false;
+
+               spin_lock(&c->data_bucket_lock);
+       }
+
+       /*
+        * If we had to allocate, we might race and not need to allocate the
+        * second time we call find_data_bucket(). If we allocated a bucket but
+        * didn't use it, drop the refcount bch_bucket_alloc_set() took:
+        */
+       if (KEY_PTRS(&alloc.key))
+               bkey_put(c, &alloc.key);
+
+       for (i = 0; i < KEY_PTRS(&b->key); i++)
+               EBUG_ON(ptr_stale(c, &b->key, i));
+
+       /* Set up the pointer to the space we're allocating: */
+
+       for (i = 0; i < KEY_PTRS(&b->key); i++)
+               k->ptr[i] = b->key.ptr[i];
+
+       sectors = min(sectors, b->sectors_free);
+
+       SET_KEY_OFFSET(k, KEY_OFFSET(k) + sectors);
+       SET_KEY_SIZE(k, sectors);
+       SET_KEY_PTRS(k, KEY_PTRS(&b->key));
+
+       /*
+        * Move b to the end of the lru, and keep track of what this bucket was
+        * last used for:
+        */
+       list_move_tail(&b->list, &c->data_buckets);
+       bkey_copy_key(&b->key, k);
+       b->last_write_point = write_point;
+
+       b->sectors_free -= sectors;
+
+       for (i = 0; i < KEY_PTRS(&b->key); i++) {
+               SET_PTR_OFFSET(&b->key, i, PTR_OFFSET(&b->key, i) + sectors);
+
+               atomic_long_add(sectors,
+                               &PTR_CACHE(c, &b->key, i)->sectors_written);
+       }
+
+       if (b->sectors_free < c->sb.block_size)
+               b->sectors_free = 0;
+
+       /*
+        * k takes refcounts on the buckets it points to until it's inserted
+        * into the btree, but if we're done with this bucket we just transfer
+        * get_data_bucket()'s refcount.
+        */
+       if (b->sectors_free)
+               for (i = 0; i < KEY_PTRS(&b->key); i++)
+                       atomic_inc(&PTR_BUCKET(c, &b->key, i)->pin);
+
+       spin_unlock(&c->data_bucket_lock);
+       return true;
+}
+
 /* Init */
 
+void bch_open_buckets_free(struct cache_set *c)
+{
+       struct open_bucket *b;
+
+       while (!list_empty(&c->data_buckets)) {
+               b = list_first_entry(&c->data_buckets,
+                                    struct open_bucket, list);
+               list_del(&b->list);
+               kfree(b);
+       }
+}
+
+int bch_open_buckets_alloc(struct cache_set *c)
+{
+       int i;
+
+       spin_lock_init(&c->data_bucket_lock);
+
+       for (i = 0; i < 6; i++) {
+               struct open_bucket *b = kzalloc(sizeof(*b), GFP_KERNEL);
+               if (!b)
+                       return -ENOMEM;
+
+               list_add(&b->list, &c->data_buckets);
+       }
+
+       return 0;
+}
+
 int bch_cache_allocator_start(struct cache *ca)
 {
        struct task_struct *k = kthread_run(bch_allocator_thread,
@@ -556,22 +679,8 @@ int bch_cache_allocator_start(struct cache *ca)
        return 0;
 }
 
-void bch_cache_allocator_exit(struct cache *ca)
-{
-       struct discard *d;
-
-       while (!list_empty(&ca->discards)) {
-               d = list_first_entry(&ca->discards, struct discard, list);
-               cancel_work_sync(&d->work);
-               list_del(&d->list);
-               kfree(d);
-       }
-}
-
 int bch_cache_allocator_init(struct cache *ca)
 {
-       unsigned i;
-
        /*
         * Reserve:
         * Prio/gen writes first
@@ -589,15 +698,5 @@ int bch_cache_allocator_init(struct cache *ca)
        ca->watermark[WATERMARK_NONE] = ca->free.size / 2 +
                ca->watermark[WATERMARK_MOVINGGC];
 
-       for (i = 0; i < MAX_IN_FLIGHT_DISCARDS; i++) {
-               struct discard *d = kzalloc(sizeof(*d), GFP_KERNEL);
-               if (!d)
-                       return -ENOMEM;
-
-               d->ca = ca;
-               INIT_WORK(&d->work, discard_finish);
-               list_add(&d->list, &ca->discards);
-       }
-
        return 0;
 }
index 0f12382..4beb55a 100644 (file)
 
 #define pr_fmt(fmt) "bcache: %s() " fmt "\n", __func__
 
+#include <linux/bcache.h>
 #include <linux/bio.h>
 #include <linux/kobject.h>
 #include <linux/list.h>
@@ -210,168 +211,6 @@ BITMASK(GC_MARK,   struct bucket, gc_mark, 0, 2);
 #define GC_MARK_METADATA       2
 BITMASK(GC_SECTORS_USED, struct bucket, gc_mark, 2, 14);
 
-struct bkey {
-       uint64_t        high;
-       uint64_t        low;
-       uint64_t        ptr[];
-};
-
-/* Enough for a key with 6 pointers */
-#define BKEY_PAD               8
-
-#define BKEY_PADDED(key)                                       \
-       union { struct bkey key; uint64_t key ## _pad[BKEY_PAD]; }
-
-/* Version 0: Cache device
- * Version 1: Backing device
- * Version 2: Seed pointer into btree node checksum
- * Version 3: Cache device with new UUID format
- * Version 4: Backing device with data offset
- */
-#define BCACHE_SB_VERSION_CDEV                 0
-#define BCACHE_SB_VERSION_BDEV                 1
-#define BCACHE_SB_VERSION_CDEV_WITH_UUID       3
-#define BCACHE_SB_VERSION_BDEV_WITH_OFFSET     4
-#define BCACHE_SB_MAX_VERSION                  4
-
-#define SB_SECTOR              8
-#define SB_SIZE                        4096
-#define SB_LABEL_SIZE          32
-#define SB_JOURNAL_BUCKETS     256U
-/* SB_JOURNAL_BUCKETS must be divisible by BITS_PER_LONG */
-#define MAX_CACHES_PER_SET     8
-
-#define BDEV_DATA_START_DEFAULT        16      /* sectors */
-
-struct cache_sb {
-       uint64_t                csum;
-       uint64_t                offset; /* sector where this sb was written */
-       uint64_t                version;
-
-       uint8_t                 magic[16];
-
-       uint8_t                 uuid[16];
-       union {
-               uint8_t         set_uuid[16];
-               uint64_t        set_magic;
-       };
-       uint8_t                 label[SB_LABEL_SIZE];
-
-       uint64_t                flags;
-       uint64_t                seq;
-       uint64_t                pad[8];
-
-       union {
-       struct {
-               /* Cache devices */
-               uint64_t        nbuckets;       /* device size */
-
-               uint16_t        block_size;     /* sectors */
-               uint16_t        bucket_size;    /* sectors */
-
-               uint16_t        nr_in_set;
-               uint16_t        nr_this_dev;
-       };
-       struct {
-               /* Backing devices */
-               uint64_t        data_offset;
-
-               /*
-                * block_size from the cache device section is still used by
-                * backing devices, so don't add anything here until we fix
-                * things to not need it for backing devices anymore
-                */
-       };
-       };
-
-       uint32_t                last_mount;     /* time_t */
-
-       uint16_t                first_bucket;
-       union {
-               uint16_t        njournal_buckets;
-               uint16_t        keys;
-       };
-       uint64_t                d[SB_JOURNAL_BUCKETS];  /* journal buckets */
-};
-
-BITMASK(CACHE_SYNC,            struct cache_sb, flags, 0, 1);
-BITMASK(CACHE_DISCARD,         struct cache_sb, flags, 1, 1);
-BITMASK(CACHE_REPLACEMENT,     struct cache_sb, flags, 2, 3);
-#define CACHE_REPLACEMENT_LRU  0U
-#define CACHE_REPLACEMENT_FIFO 1U
-#define CACHE_REPLACEMENT_RANDOM 2U
-
-BITMASK(BDEV_CACHE_MODE,       struct cache_sb, flags, 0, 4);
-#define CACHE_MODE_WRITETHROUGH        0U
-#define CACHE_MODE_WRITEBACK   1U
-#define CACHE_MODE_WRITEAROUND 2U
-#define CACHE_MODE_NONE                3U
-BITMASK(BDEV_STATE,            struct cache_sb, flags, 61, 2);
-#define BDEV_STATE_NONE                0U
-#define BDEV_STATE_CLEAN       1U
-#define BDEV_STATE_DIRTY       2U
-#define BDEV_STATE_STALE       3U
-
-/* Version 1: Seed pointer into btree node checksum
- */
-#define BCACHE_BSET_VERSION    1
-
-/*
- * This is the on disk format for btree nodes - a btree node on disk is a list
- * of these; within each set the keys are sorted
- */
-struct bset {
-       uint64_t                csum;
-       uint64_t                magic;
-       uint64_t                seq;
-       uint32_t                version;
-       uint32_t                keys;
-
-       union {
-               struct bkey     start[0];
-               uint64_t        d[0];
-       };
-};
-
-/*
- * On disk format for priorities and gens - see super.c near prio_write() for
- * more.
- */
-struct prio_set {
-       uint64_t                csum;
-       uint64_t                magic;
-       uint64_t                seq;
-       uint32_t                version;
-       uint32_t                pad;
-
-       uint64_t                next_bucket;
-
-       struct bucket_disk {
-               uint16_t        prio;
-               uint8_t         gen;
-       } __attribute((packed)) data[];
-};
-
-struct uuid_entry {
-       union {
-               struct {
-                       uint8_t         uuid[16];
-                       uint8_t         label[32];
-                       uint32_t        first_reg;
-                       uint32_t        last_reg;
-                       uint32_t        invalidated;
-
-                       uint32_t        flags;
-                       /* Size of flash only volumes */
-                       uint64_t        sectors;
-               };
-
-               uint8_t pad[128];
-       };
-};
-
-BITMASK(UUID_FLASH_ONLY,       struct uuid_entry, flags, 0, 1);
-
 #include "journal.h"
 #include "stats.h"
 struct search;
@@ -384,8 +223,6 @@ struct keybuf_key {
        void                    *private;
 };
 
-typedef bool (keybuf_pred_fn)(struct keybuf *, struct bkey *);
-
 struct keybuf {
        struct bkey             last_scanned;
        spinlock_t              lock;
@@ -400,7 +237,7 @@ struct keybuf {
 
        struct rb_root          keys;
 
-#define KEYBUF_NR              100
+#define KEYBUF_NR              500
        DECLARE_ARRAY_ALLOCATOR(struct keybuf_key, freelist, KEYBUF_NR);
 };
 
@@ -429,16 +266,15 @@ struct bcache_device {
 
        struct gendisk          *disk;
 
-       /* If nonzero, we're closing */
-       atomic_t                closing;
-
-       /* If nonzero, we're detaching/unregistering from cache set */
-       atomic_t                detaching;
-       int                     flush_done;
+       unsigned long           flags;
+#define BCACHE_DEV_CLOSING     0
+#define BCACHE_DEV_DETACHING   1
+#define BCACHE_DEV_UNLINK_DONE 2
 
-       uint64_t                nr_stripes;
-       unsigned                stripe_size_bits;
+       unsigned                nr_stripes;
+       unsigned                stripe_size;
        atomic_t                *stripe_sectors_dirty;
+       unsigned long           *full_dirty_stripes;
 
        unsigned long           sectors_dirty_last;
        long                    sectors_dirty_derivative;
@@ -509,7 +345,7 @@ struct cached_dev {
 
        /* Limit number of writeback bios in flight */
        struct semaphore        in_flight;
-       struct closure_with_timer writeback;
+       struct task_struct      *writeback_thread;
 
        struct keybuf           writeback_keys;
 
@@ -527,8 +363,8 @@ struct cached_dev {
        unsigned                sequential_cutoff;
        unsigned                readahead;
 
-       unsigned                sequential_merge:1;
        unsigned                verify:1;
+       unsigned                bypass_torture_test:1;
 
        unsigned                partial_stripes_expensive:1;
        unsigned                writeback_metadata:1;
@@ -620,15 +456,6 @@ struct cache {
 
        bool                    discard; /* Get rid of? */
 
-       /*
-        * We preallocate structs for issuing discards to buckets, and keep them
-        * on this list when they're not in use; do_discard() issues discards
-        * whenever there's work to do and is called by free_some_buckets() and
-        * when a discard finishes.
-        */
-       atomic_t                discards_in_flight;
-       struct list_head        discards;
-
        struct journal_device   journal;
 
        /* The rest of this all shows up in sysfs */
@@ -649,7 +476,6 @@ struct gc_stat {
 
        size_t                  nkeys;
        uint64_t                data;   /* sectors */
-       uint64_t                dirty;  /* sectors */
        unsigned                in_use; /* percent */
 };
 
@@ -744,8 +570,8 @@ struct cache_set {
         * basically a lock for this that we can wait on asynchronously. The
         * btree_root() macro releases the lock when it returns.
         */
-       struct closure          *try_harder;
-       struct closure_waitlist try_wait;
+       struct task_struct      *try_harder;
+       wait_queue_head_t       try_wait;
        uint64_t                try_harder_start;
 
        /*
@@ -759,7 +585,7 @@ struct cache_set {
         * written.
         */
        atomic_t                prio_blocked;
-       struct closure_waitlist bucket_wait;
+       wait_queue_head_t       bucket_wait;
 
        /*
         * For any bio we don't skip we subtract the number of sectors from
@@ -782,7 +608,7 @@ struct cache_set {
        struct gc_stat          gc_stats;
        size_t                  nbuckets;
 
-       struct closure_with_waitlist gc;
+       struct task_struct      *gc_thread;
        /* Where in the btree gc currently is */
        struct bkey             gc_done;
 
@@ -795,11 +621,10 @@ struct cache_set {
        /* Counts how many sectors bio_insert has added to the cache */
        atomic_t                sectors_to_gc;
 
-       struct closure          moving_gc;
-       struct closure_waitlist moving_gc_wait;
+       wait_queue_head_t       moving_gc_wait;
        struct keybuf           moving_gc_keys;
        /* Number of moving GC bios in flight */
-       atomic_t                in_flight;
+       struct semaphore        moving_in_flight;
 
        struct btree            *root;
 
@@ -841,22 +666,27 @@ struct cache_set {
        unsigned                congested_read_threshold_us;
        unsigned                congested_write_threshold_us;
 
-       spinlock_t              sort_time_lock;
        struct time_stats       sort_time;
        struct time_stats       btree_gc_time;
        struct time_stats       btree_split_time;
-       spinlock_t              btree_read_time_lock;
        struct time_stats       btree_read_time;
        struct time_stats       try_harder_time;
 
        atomic_long_t           cache_read_races;
        atomic_long_t           writeback_keys_done;
        atomic_long_t           writeback_keys_failed;
+
+       enum                    {
+               ON_ERROR_UNREGISTER,
+               ON_ERROR_PANIC,
+       }                       on_error;
        unsigned                error_limit;
        unsigned                error_decay;
+
        unsigned short          journal_delay_ms;
        unsigned                verify:1;
        unsigned                key_merging_disabled:1;
+       unsigned                expensive_debug_checks:1;
        unsigned                gc_always_rewrite:1;
        unsigned                shrinker_disabled:1;
        unsigned                copy_gc_enabled:1;
@@ -865,21 +695,6 @@ struct cache_set {
        struct hlist_head       bucket_hash[1 << BUCKET_HASH_BITS];
 };
 
-static inline bool key_merging_disabled(struct cache_set *c)
-{
-#ifdef CONFIG_BCACHE_DEBUG
-       return c->key_merging_disabled;
-#else
-       return 0;
-#endif
-}
-
-static inline bool SB_IS_BDEV(const struct cache_sb *sb)
-{
-       return sb->version == BCACHE_SB_VERSION_BDEV
-               || sb->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET;
-}
-
 struct bbio {
        unsigned                submit_time_us;
        union {
@@ -933,59 +748,6 @@ static inline unsigned local_clock_us(void)
 #define prio_buckets(c)                                        \
        DIV_ROUND_UP((size_t) (c)->sb.nbuckets, prios_per_bucket(c))
 
-#define JSET_MAGIC             0x245235c1a3625032ULL
-#define PSET_MAGIC             0x6750e15f87337f91ULL
-#define BSET_MAGIC             0x90135c78b99e07f5ULL
-
-#define jset_magic(c)          ((c)->sb.set_magic ^ JSET_MAGIC)
-#define pset_magic(c)          ((c)->sb.set_magic ^ PSET_MAGIC)
-#define bset_magic(c)          ((c)->sb.set_magic ^ BSET_MAGIC)
-
-/* Bkey fields: all units are in sectors */
-
-#define KEY_FIELD(name, field, offset, size)                           \
-       BITMASK(name, struct bkey, field, offset, size)
-
-#define PTR_FIELD(name, offset, size)                                  \
-       static inline uint64_t name(const struct bkey *k, unsigned i)   \
-       { return (k->ptr[i] >> offset) & ~(((uint64_t) ~0) << size); }  \
-                                                                       \
-       static inline void SET_##name(struct bkey *k, unsigned i, uint64_t v)\
-       {                                                               \
-               k->ptr[i] &= ~(~((uint64_t) ~0 << size) << offset);     \
-               k->ptr[i] |= v << offset;                               \
-       }
-
-KEY_FIELD(KEY_PTRS,    high, 60, 3)
-KEY_FIELD(HEADER_SIZE, high, 58, 2)
-KEY_FIELD(KEY_CSUM,    high, 56, 2)
-KEY_FIELD(KEY_PINNED,  high, 55, 1)
-KEY_FIELD(KEY_DIRTY,   high, 36, 1)
-
-KEY_FIELD(KEY_SIZE,    high, 20, 16)
-KEY_FIELD(KEY_INODE,   high, 0,  20)
-
-/* Next time I change the on disk format, KEY_OFFSET() won't be 64 bits */
-
-static inline uint64_t KEY_OFFSET(const struct bkey *k)
-{
-       return k->low;
-}
-
-static inline void SET_KEY_OFFSET(struct bkey *k, uint64_t v)
-{
-       k->low = v;
-}
-
-PTR_FIELD(PTR_DEV,             51, 12)
-PTR_FIELD(PTR_OFFSET,          8,  43)
-PTR_FIELD(PTR_GEN,             0,  8)
-
-#define PTR_CHECK_DEV          ((1 << 12) - 1)
-
-#define PTR(gen, offset, dev)                                          \
-       ((((uint64_t) dev) << 51) | ((uint64_t) offset) << 8 | gen)
-
 static inline size_t sector_to_bucket(struct cache_set *c, sector_t s)
 {
        return s >> c->bucket_bits;
@@ -1024,27 +786,11 @@ static inline struct bucket *PTR_BUCKET(struct cache_set *c,
 
 /* Btree key macros */
 
-/*
- * The high bit being set is a relic from when we used it to do binary
- * searches - it told you where a key started. It's not used anymore,
- * and can probably be safely dropped.
- */
-#define KEY(dev, sector, len)                                          \
-((struct bkey) {                                                       \
-       .high = (1ULL << 63) | ((uint64_t) (len) << 20) | (dev),        \
-       .low = (sector)                                                 \
-})
-
 static inline void bkey_init(struct bkey *k)
 {
-       *k = KEY(0, 0, 0);
+       *k = ZERO_KEY;
 }
 
-#define KEY_START(k)           (KEY_OFFSET(k) - KEY_SIZE(k))
-#define START_KEY(k)           KEY(KEY_INODE(k), KEY_START(k), 0)
-#define MAX_KEY                        KEY(~(~0 << 20), ((uint64_t) ~0) >> 1, 0)
-#define ZERO_KEY               KEY(0, 0, 0)
-
 /*
  * This is used for various on disk data structures - cache_sb, prio_set, bset,
  * jset: The checksum is _always_ the first 8 bytes of these structs
@@ -1094,14 +840,6 @@ do {                                                                      \
        for (b = (ca)->buckets + (ca)->sb.first_bucket;                 \
             b < (ca)->buckets + (ca)->sb.nbuckets; b++)
 
-static inline void __bkey_put(struct cache_set *c, struct bkey *k)
-{
-       unsigned i;
-
-       for (i = 0; i < KEY_PTRS(k); i++)
-               atomic_dec_bug(&PTR_BUCKET(c, k, i)->pin);
-}
-
 static inline void cached_dev_put(struct cached_dev *dc)
 {
        if (atomic_dec_and_test(&dc->count))
@@ -1173,13 +911,15 @@ uint8_t bch_inc_gen(struct cache *, struct bucket *);
 void bch_rescale_priorities(struct cache_set *, int);
 bool bch_bucket_add_unused(struct cache *, struct bucket *);
 
-long bch_bucket_alloc(struct cache *, unsigned, struct closure *);
+long bch_bucket_alloc(struct cache *, unsigned, bool);
 void bch_bucket_free(struct cache_set *, struct bkey *);
 
 int __bch_bucket_alloc_set(struct cache_set *, unsigned,
-                          struct bkey *, int, struct closure *);
+                          struct bkey *, int, bool);
 int bch_bucket_alloc_set(struct cache_set *, unsigned,
-                        struct bkey *, int, struct closure *);
+                        struct bkey *, int, bool);
+bool bch_alloc_sectors(struct cache_set *, struct bkey *, unsigned,
+                      unsigned, unsigned, bool);
 
 __printf(2, 3)
 bool bch_cache_set_error(struct cache_set *, const char *, ...);
@@ -1187,7 +927,7 @@ bool bch_cache_set_error(struct cache_set *, const char *, ...);
 void bch_prio_write(struct cache *);
 void bch_write_bdev_super(struct cached_dev *, struct closure *);
 
-extern struct workqueue_struct *bcache_wq, *bch_gc_wq;
+extern struct workqueue_struct *bcache_wq;
 extern const char * const bch_cache_modes[];
 extern struct mutex bch_register_lock;
 extern struct list_head bch_cache_sets;
@@ -1220,15 +960,14 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *);
 void bch_btree_cache_free(struct cache_set *);
 int bch_btree_cache_alloc(struct cache_set *);
 void bch_moving_init_cache_set(struct cache_set *);
+int bch_open_buckets_alloc(struct cache_set *);
+void bch_open_buckets_free(struct cache_set *);
 
 int bch_cache_allocator_start(struct cache *ca);
-void bch_cache_allocator_exit(struct cache *ca);
 int bch_cache_allocator_init(struct cache *ca);
 
 void bch_debug_exit(void);
 int bch_debug_init(struct kobject *);
-void bch_writeback_exit(void);
-int bch_writeback_init(void);
 void bch_request_exit(void);
 int bch_request_init(void);
 void bch_btree_exit(void);
index 22d1ae7..7d388b8 100644 (file)
 
 /* Keylists */
 
-void bch_keylist_copy(struct keylist *dest, struct keylist *src)
-{
-       *dest = *src;
-
-       if (src->list == src->d) {
-               size_t n = (uint64_t *) src->top - src->d;
-               dest->top = (struct bkey *) &dest->d[n];
-               dest->list = dest->d;
-       }
-}
-
 int bch_keylist_realloc(struct keylist *l, int nptrs, struct cache_set *c)
 {
-       unsigned oldsize = (uint64_t *) l->top - l->list;
-       unsigned newsize = oldsize + 2 + nptrs;
-       uint64_t *new;
+       size_t oldsize = bch_keylist_nkeys(l);
+       size_t newsize = oldsize + 2 + nptrs;
+       uint64_t *old_keys = l->keys_p == l->inline_keys ? NULL : l->keys_p;
+       uint64_t *new_keys;
 
        /* The journalling code doesn't handle the case where the keys to insert
         * is bigger than an empty write: If we just return -ENOMEM here,
@@ -45,24 +35,23 @@ int bch_keylist_realloc(struct keylist *l, int nptrs, struct cache_set *c)
            roundup_pow_of_two(oldsize) == newsize)
                return 0;
 
-       new = krealloc(l->list == l->d ? NULL : l->list,
-                      sizeof(uint64_t) * newsize, GFP_NOIO);
+       new_keys = krealloc(old_keys, sizeof(uint64_t) * newsize, GFP_NOIO);
 
-       if (!new)
+       if (!new_keys)
                return -ENOMEM;
 
-       if (l->list == l->d)
-               memcpy(new, l->list, sizeof(uint64_t) * KEYLIST_INLINE);
+       if (!old_keys)
+               memcpy(new_keys, l->inline_keys, sizeof(uint64_t) * oldsize);
 
-       l->list = new;
-       l->top = (struct bkey *) (&l->list[oldsize]);
+       l->keys_p = new_keys;
+       l->top_p = new_keys + oldsize;
 
        return 0;
 }
 
 struct bkey *bch_keylist_pop(struct keylist *l)
 {
-       struct bkey *k = l->bottom;
+       struct bkey *k = l->keys;
 
        if (k == l->top)
                return NULL;
@@ -73,21 +62,20 @@ struct bkey *bch_keylist_pop(struct keylist *l)
        return l->top = k;
 }
 
-/* Pointer validation */
-
-bool __bch_ptr_invalid(struct cache_set *c, int level, const struct bkey *k)
+void bch_keylist_pop_front(struct keylist *l)
 {
-       unsigned i;
-       char buf[80];
+       l->top_p -= bkey_u64s(l->keys);
 
-       if (level && (!KEY_PTRS(k) || !KEY_SIZE(k) || KEY_DIRTY(k)))
-               goto bad;
+       memmove(l->keys,
+               bkey_next(l->keys),
+               bch_keylist_bytes(l));
+}
 
-       if (!level && KEY_SIZE(k) > KEY_OFFSET(k))
-               goto bad;
+/* Pointer validation */
 
-       if (!KEY_SIZE(k))
-               return true;
+static bool __ptr_invalid(struct cache_set *c, const struct bkey *k)
+{
+       unsigned i;
 
        for (i = 0; i < KEY_PTRS(k); i++)
                if (ptr_available(c, k, i)) {
@@ -98,13 +86,83 @@ bool __bch_ptr_invalid(struct cache_set *c, int level, const struct bkey *k)
                        if (KEY_SIZE(k) + r > c->sb.bucket_size ||
                            bucket <  ca->sb.first_bucket ||
                            bucket >= ca->sb.nbuckets)
-                               goto bad;
+                               return true;
                }
 
        return false;
+}
+
+bool bch_btree_ptr_invalid(struct cache_set *c, const struct bkey *k)
+{
+       char buf[80];
+
+       if (!KEY_PTRS(k) || !KEY_SIZE(k) || KEY_DIRTY(k))
+               goto bad;
+
+       if (__ptr_invalid(c, k))
+               goto bad;
+
+       return false;
+bad:
+       bch_bkey_to_text(buf, sizeof(buf), k);
+       cache_bug(c, "spotted btree ptr %s: %s", buf, bch_ptr_status(c, k));
+       return true;
+}
+
+bool bch_extent_ptr_invalid(struct cache_set *c, const struct bkey *k)
+{
+       char buf[80];
+
+       if (!KEY_SIZE(k))
+               return true;
+
+       if (KEY_SIZE(k) > KEY_OFFSET(k))
+               goto bad;
+
+       if (__ptr_invalid(c, k))
+               goto bad;
+
+       return false;
 bad:
        bch_bkey_to_text(buf, sizeof(buf), k);
-       cache_bug(c, "spotted bad key %s: %s", buf, bch_ptr_status(c, k));
+       cache_bug(c, "spotted extent %s: %s", buf, bch_ptr_status(c, k));
+       return true;
+}
+
+static bool ptr_bad_expensive_checks(struct btree *b, const struct bkey *k,
+                                    unsigned ptr)
+{
+       struct bucket *g = PTR_BUCKET(b->c, k, ptr);
+       char buf[80];
+
+       if (mutex_trylock(&b->c->bucket_lock)) {
+               if (b->level) {
+                       if (KEY_DIRTY(k) ||
+                           g->prio != BTREE_PRIO ||
+                           (b->c->gc_mark_valid &&
+                            GC_MARK(g) != GC_MARK_METADATA))
+                               goto err;
+
+               } else {
+                       if (g->prio == BTREE_PRIO)
+                               goto err;
+
+                       if (KEY_DIRTY(k) &&
+                           b->c->gc_mark_valid &&
+                           GC_MARK(g) != GC_MARK_DIRTY)
+                               goto err;
+               }
+               mutex_unlock(&b->c->bucket_lock);
+       }
+
+       return false;
+err:
+       mutex_unlock(&b->c->bucket_lock);
+       bch_bkey_to_text(buf, sizeof(buf), k);
+       btree_bug(b,
+"inconsistent pointer %s: bucket %zu pin %i prio %i gen %i last_gc %i mark %llu gc_gen %i",
+                 buf, PTR_BUCKET_NR(b->c, k, ptr), atomic_read(&g->pin),
+                 g->prio, g->gen, g->last_gc, GC_MARK(g), g->gc_gen);
        return true;
 }
 
@@ -118,64 +176,29 @@ bool bch_ptr_bad(struct btree *b, const struct bkey *k)
            bch_ptr_invalid(b, k))
                return true;
 
-       if (KEY_PTRS(k) && PTR_DEV(k, 0) == PTR_CHECK_DEV)
-               return true;
+       for (i = 0; i < KEY_PTRS(k); i++) {
+               if (!ptr_available(b->c, k, i))
+                       return true;
 
-       for (i = 0; i < KEY_PTRS(k); i++)
-               if (ptr_available(b->c, k, i)) {
-                       g = PTR_BUCKET(b->c, k, i);
-                       stale = ptr_stale(b->c, k, i);
+               g = PTR_BUCKET(b->c, k, i);
+               stale = ptr_stale(b->c, k, i);
 
-                       btree_bug_on(stale > 96, b,
-                                    "key too stale: %i, need_gc %u",
-                                    stale, b->c->need_gc);
+               btree_bug_on(stale > 96, b,
+                            "key too stale: %i, need_gc %u",
+                            stale, b->c->need_gc);
 
-                       btree_bug_on(stale && KEY_DIRTY(k) && KEY_SIZE(k),
-                                    b, "stale dirty pointer");
+               btree_bug_on(stale && KEY_DIRTY(k) && KEY_SIZE(k),
+                            b, "stale dirty pointer");
 
-                       if (stale)
-                               return true;
+               if (stale)
+                       return true;
 
-#ifdef CONFIG_BCACHE_EDEBUG
-                       if (!mutex_trylock(&b->c->bucket_lock))
-                               continue;
-
-                       if (b->level) {
-                               if (KEY_DIRTY(k) ||
-                                   g->prio != BTREE_PRIO ||
-                                   (b->c->gc_mark_valid &&
-                                    GC_MARK(g) != GC_MARK_METADATA))
-                                       goto bug;
-
-                       } else {
-                               if (g->prio == BTREE_PRIO)
-                                       goto bug;
-
-                               if (KEY_DIRTY(k) &&
-                                   b->c->gc_mark_valid &&
-                                   GC_MARK(g) != GC_MARK_DIRTY)
-                                       goto bug;
-                       }
-                       mutex_unlock(&b->c->bucket_lock);
-#endif
-               }
+               if (expensive_debug_checks(b->c) &&
+                   ptr_bad_expensive_checks(b, k, i))
+                       return true;
+       }
 
        return false;
-#ifdef CONFIG_BCACHE_EDEBUG
-bug:
-       mutex_unlock(&b->c->bucket_lock);
-
-       {
-               char buf[80];
-
-               bch_bkey_to_text(buf, sizeof(buf), k);
-               btree_bug(b,
-"inconsistent pointer %s: bucket %zu pin %i prio %i gen %i last_gc %i mark %llu gc_gen %i",
-                         buf, PTR_BUCKET_NR(b->c, k, i), atomic_read(&g->pin),
-                         g->prio, g->gen, g->last_gc, GC_MARK(g), g->gc_gen);
-       }
-       return true;
-#endif
 }
 
 /* Key/pointer manipulation */
@@ -458,16 +481,8 @@ static struct bkey *table_to_bkey(struct bset_tree *t, unsigned cacheline)
 
 static inline uint64_t shrd128(uint64_t high, uint64_t low, uint8_t shift)
 {
-#ifdef CONFIG_X86_64
-       asm("shrd %[shift],%[high],%[low]"
-           : [low] "+Rm" (low)
-           : [high] "R" (high),
-           [shift] "ci" (shift)
-           : "cc");
-#else
        low >>= shift;
        low  |= (high << 1) << (63U - shift);
-#endif
        return low;
 }
 
@@ -686,7 +701,7 @@ void bch_bset_init_next(struct btree *b)
        } else
                get_random_bytes(&i->seq, sizeof(uint64_t));
 
-       i->magic        = bset_magic(b->c);
+       i->magic        = bset_magic(&b->c->sb);
        i->version      = 0;
        i->keys         = 0;
 
@@ -824,16 +839,16 @@ struct bkey *__bch_bset_search(struct btree *b, struct bset_tree *t,
        } else
                i = bset_search_write_set(b, t, search);
 
-#ifdef CONFIG_BCACHE_EDEBUG
-       BUG_ON(bset_written(b, t) &&
-              i.l != t->data->start &&
-              bkey_cmp(tree_to_prev_bkey(t,
-                 inorder_to_tree(bkey_to_cacheline(t, i.l), t)),
-                       search) > 0);
+       if (expensive_debug_checks(b->c)) {
+               BUG_ON(bset_written(b, t) &&
+                      i.l != t->data->start &&
+                      bkey_cmp(tree_to_prev_bkey(t,
+                         inorder_to_tree(bkey_to_cacheline(t, i.l), t)),
+                               search) > 0);
 
-       BUG_ON(i.r != end(t->data) &&
-              bkey_cmp(i.r, search) <= 0);
-#endif
+               BUG_ON(i.r != end(t->data) &&
+                      bkey_cmp(i.r, search) <= 0);
+       }
 
        while (likely(i.l != i.r) &&
               bkey_cmp(i.l, search) <= 0)
@@ -844,6 +859,13 @@ struct bkey *__bch_bset_search(struct btree *b, struct bset_tree *t,
 
 /* Btree iterator */
 
+/*
+ * Returns true if l > r - unless l == r, in which case returns true if l is
+ * older than r.
+ *
+ * Necessary for btree_sort_fixup() - if there are multiple keys that compare
+ * equal in different sets, we have to process them newest to oldest.
+ */
 static inline bool btree_iter_cmp(struct btree_iter_set l,
                                  struct btree_iter_set r)
 {
@@ -867,12 +889,16 @@ void bch_btree_iter_push(struct btree_iter *iter, struct bkey *k,
 }
 
 struct bkey *__bch_btree_iter_init(struct btree *b, struct btree_iter *iter,
-                              struct bkey *search, struct bset_tree *start)
+                                  struct bkey *search, struct bset_tree *start)
 {
        struct bkey *ret = NULL;
        iter->size = ARRAY_SIZE(iter->data);
        iter->used = 0;
 
+#ifdef CONFIG_BCACHE_DEBUG
+       iter->b = b;
+#endif
+
        for (; start <= &b->sets[b->nsets]; start++) {
                ret = bch_bset_search(b, start, search);
                bch_btree_iter_push(iter, ret, end(start->data));
@@ -887,6 +913,8 @@ struct bkey *bch_btree_iter_next(struct btree_iter *iter)
        struct bkey *ret = NULL;
 
        if (!btree_iter_end(iter)) {
+               bch_btree_iter_next_check(iter);
+
                ret = iter->data->k;
                iter->data->k = bkey_next(iter->data->k);
 
@@ -916,14 +944,6 @@ struct bkey *bch_btree_iter_next_filter(struct btree_iter *iter,
        return ret;
 }
 
-struct bkey *bch_next_recurse_key(struct btree *b, struct bkey *search)
-{
-       struct btree_iter iter;
-
-       bch_btree_iter_init(b, &iter, search);
-       return bch_btree_iter_next_filter(&iter, b, bch_ptr_bad);
-}
-
 /* Mergesort */
 
 static void sort_key_next(struct btree_iter *iter,
@@ -998,7 +1018,6 @@ static void btree_mergesort(struct btree *b, struct bset *out,
        out->keys = last ? (uint64_t *) bkey_next(last) - out->d : 0;
 
        pr_debug("sorted %i keys", out->keys);
-       bch_check_key_order(b, out);
 }
 
 static void __btree_sort(struct btree *b, struct btree_iter *iter,
@@ -1029,7 +1048,7 @@ static void __btree_sort(struct btree *b, struct btree_iter *iter,
                 * memcpy()
                 */
 
-               out->magic      = bset_magic(b->c);
+               out->magic      = bset_magic(&b->c->sb);
                out->seq        = b->sets[0].data->seq;
                out->version    = b->sets[0].data->version;
                swap(out, b->sets[0].data);
@@ -1050,24 +1069,21 @@ static void __btree_sort(struct btree *b, struct btree_iter *iter,
        if (b->written)
                bset_build_written_tree(b);
 
-       if (!start) {
-               spin_lock(&b->c->sort_time_lock);
+       if (!start)
                bch_time_stats_update(&b->c->sort_time, start_time);
-               spin_unlock(&b->c->sort_time_lock);
-       }
 }
 
 void bch_btree_sort_partial(struct btree *b, unsigned start)
 {
-       size_t oldsize = 0, order = b->page_order, keys = 0;
+       size_t order = b->page_order, keys = 0;
        struct btree_iter iter;
+       int oldsize = bch_count_data(b);
+
        __bch_btree_iter_init(b, &iter, NULL, &b->sets[start]);
 
        BUG_ON(b->sets[b->nsets].data == write_block(b) &&
               (b->sets[b->nsets].size || b->nsets));
 
-       if (b->written)
-               oldsize = bch_count_data(b);
 
        if (start) {
                unsigned i;
@@ -1083,7 +1099,7 @@ void bch_btree_sort_partial(struct btree *b, unsigned start)
 
        __btree_sort(b, &iter, start, order, false);
 
-       EBUG_ON(b->written && bch_count_data(b) != oldsize);
+       EBUG_ON(b->written && oldsize >= 0 && bch_count_data(b) != oldsize);
 }
 
 void bch_btree_sort_and_fix_extents(struct btree *b, struct btree_iter *iter)
@@ -1101,9 +1117,7 @@ void bch_btree_sort_into(struct btree *b, struct btree *new)
 
        btree_mergesort(b, new->sets->data, &iter, false, true);
 
-       spin_lock(&b->c->sort_time_lock);
        bch_time_stats_update(&b->c->sort_time, start_time);
-       spin_unlock(&b->c->sort_time_lock);
 
        bkey_copy_key(&new->key, &b->key);
        new->sets->size = 0;
@@ -1148,16 +1162,16 @@ out:
 /* Sysfs stuff */
 
 struct bset_stats {
+       struct btree_op op;
        size_t nodes;
        size_t sets_written, sets_unwritten;
        size_t bytes_written, bytes_unwritten;
        size_t floats, failed;
 };
 
-static int bch_btree_bset_stats(struct btree *b, struct btree_op *op,
-                           struct bset_stats *stats)
+static int btree_bset_stats(struct btree_op *op, struct btree *b)
 {
-       struct bkey *k;
+       struct bset_stats *stats = container_of(op, struct bset_stats, op);
        unsigned i;
 
        stats->nodes++;
@@ -1182,30 +1196,19 @@ static int bch_btree_bset_stats(struct btree *b, struct btree_op *op,
                }
        }
 
-       if (b->level) {
-               struct btree_iter iter;
-
-               for_each_key_filter(b, k, &iter, bch_ptr_bad) {
-                       int ret = btree(bset_stats, k, b, op, stats);
-                       if (ret)
-                               return ret;
-               }
-       }
-
-       return 0;
+       return MAP_CONTINUE;
 }
 
 int bch_bset_print_stats(struct cache_set *c, char *buf)
 {
-       struct btree_op op;
        struct bset_stats t;
        int ret;
 
-       bch_btree_op_init_stack(&op);
        memset(&t, 0, sizeof(struct bset_stats));
+       bch_btree_op_init(&t.op, -1);
 
-       ret = btree_root(bset_stats, c, &op, &t);
-       if (ret)
+       ret = bch_btree_map_nodes(&t.op, c, &ZERO_KEY, btree_bset_stats);
+       if (ret < 0)
                return ret;
 
        return snprintf(buf, PAGE_SIZE,
index ae115a2..1d3c24f 100644 (file)
 
 struct btree_iter {
        size_t size, used;
+#ifdef CONFIG_BCACHE_DEBUG
+       struct btree *b;
+#endif
        struct btree_iter_set {
                struct bkey *k, *end;
        } data[MAX_BSETS];
@@ -193,54 +196,26 @@ static __always_inline int64_t bkey_cmp(const struct bkey *l,
                : (int64_t) KEY_OFFSET(l) - (int64_t) KEY_OFFSET(r);
 }
 
-static inline size_t bkey_u64s(const struct bkey *k)
-{
-       BUG_ON(KEY_CSUM(k) > 1);
-       return 2 + KEY_PTRS(k) + (KEY_CSUM(k) ? 1 : 0);
-}
-
-static inline size_t bkey_bytes(const struct bkey *k)
-{
-       return bkey_u64s(k) * sizeof(uint64_t);
-}
-
-static inline void bkey_copy(struct bkey *dest, const struct bkey *src)
-{
-       memcpy(dest, src, bkey_bytes(src));
-}
-
-static inline void bkey_copy_key(struct bkey *dest, const struct bkey *src)
-{
-       if (!src)
-               src = &KEY(0, 0, 0);
-
-       SET_KEY_INODE(dest, KEY_INODE(src));
-       SET_KEY_OFFSET(dest, KEY_OFFSET(src));
-}
-
-static inline struct bkey *bkey_next(const struct bkey *k)
-{
-       uint64_t *d = (void *) k;
-       return (struct bkey *) (d + bkey_u64s(k));
-}
-
 /* Keylists */
 
 struct keylist {
-       struct bkey             *top;
        union {
-               uint64_t                *list;
-               struct bkey             *bottom;
+               struct bkey             *keys;
+               uint64_t                *keys_p;
+       };
+       union {
+               struct bkey             *top;
+               uint64_t                *top_p;
        };
 
        /* Enough room for btree_split's keys without realloc */
 #define KEYLIST_INLINE         16
-       uint64_t                d[KEYLIST_INLINE];
+       uint64_t                inline_keys[KEYLIST_INLINE];
 };
 
 static inline void bch_keylist_init(struct keylist *l)
 {
-       l->top = (void *) (l->list = l->d);
+       l->top_p = l->keys_p = l->inline_keys;
 }
 
 static inline void bch_keylist_push(struct keylist *l)
@@ -256,17 +231,32 @@ static inline void bch_keylist_add(struct keylist *l, struct bkey *k)
 
 static inline bool bch_keylist_empty(struct keylist *l)
 {
-       return l->top == (void *) l->list;
+       return l->top == l->keys;
+}
+
+static inline void bch_keylist_reset(struct keylist *l)
+{
+       l->top = l->keys;
 }
 
 static inline void bch_keylist_free(struct keylist *l)
 {
-       if (l->list != l->d)
-               kfree(l->list);
+       if (l->keys_p != l->inline_keys)
+               kfree(l->keys_p);
+}
+
+static inline size_t bch_keylist_nkeys(struct keylist *l)
+{
+       return l->top_p - l->keys_p;
+}
+
+static inline size_t bch_keylist_bytes(struct keylist *l)
+{
+       return bch_keylist_nkeys(l) * sizeof(uint64_t);
 }
 
-void bch_keylist_copy(struct keylist *, struct keylist *);
 struct bkey *bch_keylist_pop(struct keylist *);
+void bch_keylist_pop_front(struct keylist *);
 int bch_keylist_realloc(struct keylist *, int, struct cache_set *);
 
 void bch_bkey_copy_single_ptr(struct bkey *, const struct bkey *,
@@ -287,7 +277,9 @@ static inline bool bch_cut_back(const struct bkey *where, struct bkey *k)
 }
 
 const char *bch_ptr_status(struct cache_set *, const struct bkey *);
-bool __bch_ptr_invalid(struct cache_set *, int level, const struct bkey *);
+bool bch_btree_ptr_invalid(struct cache_set *, const struct bkey *);
+bool bch_extent_ptr_invalid(struct cache_set *, const struct bkey *);
+
 bool bch_ptr_bad(struct btree *, const struct bkey *);
 
 static inline uint8_t gen_after(uint8_t a, uint8_t b)
@@ -311,7 +303,6 @@ static inline bool ptr_available(struct cache_set *c, const struct bkey *k,
 
 typedef bool (*ptr_filter_fn)(struct btree *, const struct bkey *);
 
-struct bkey *bch_next_recurse_key(struct btree *, struct bkey *);
 struct bkey *bch_btree_iter_next(struct btree_iter *);
 struct bkey *bch_btree_iter_next_filter(struct btree_iter *,
                                        struct btree *, ptr_filter_fn);
@@ -361,12 +352,30 @@ void bch_bset_fix_lookup_table(struct btree *, struct bkey *);
 struct bkey *__bch_bset_search(struct btree *, struct bset_tree *,
                           const struct bkey *);
 
+/*
+ * Returns the first key that is strictly greater than search
+ */
 static inline struct bkey *bch_bset_search(struct btree *b, struct bset_tree *t,
                                           const struct bkey *search)
 {
        return search ? __bch_bset_search(b, t, search) : t->data->start;
 }
 
+#define PRECEDING_KEY(_k)                                      \
+({                                                             \
+       struct bkey *_ret = NULL;                               \
+                                                               \
+       if (KEY_INODE(_k) || KEY_OFFSET(_k)) {                  \
+               _ret = &KEY(KEY_INODE(_k), KEY_OFFSET(_k), 0);  \
+                                                               \
+               if (!_ret->low)                                 \
+                       _ret->high--;                           \
+               _ret->low--;                                    \
+       }                                                       \
+                                                               \
+       _ret;                                                   \
+})
+
 bool bch_bkey_try_merge(struct btree *, struct bkey *, struct bkey *);
 void bch_btree_sort_lazy(struct btree *);
 void bch_btree_sort_into(struct btree *, struct btree *);
index f42fc7e..5e2765a 100644 (file)
 #include "bcache.h"
 #include "btree.h"
 #include "debug.h"
-#include "request.h"
 #include "writeback.h"
 
 #include <linux/slab.h>
 #include <linux/bitops.h>
+#include <linux/freezer.h>
 #include <linux/hash.h>
+#include <linux/kthread.h>
 #include <linux/prefetch.h>
 #include <linux/random.h>
 #include <linux/rcupdate.h>
  * Test module load/unload
  */
 
-static const char * const op_types[] = {
-       "insert", "replace"
+enum {
+       BTREE_INSERT_STATUS_INSERT,
+       BTREE_INSERT_STATUS_BACK_MERGE,
+       BTREE_INSERT_STATUS_OVERWROTE,
+       BTREE_INSERT_STATUS_FRONT_MERGE,
 };
 
-static const char *op_type(struct btree_op *op)
-{
-       return op_types[op->type];
-}
-
 #define MAX_NEED_GC            64
 #define MAX_SAVE_PRIO          72
 
@@ -105,23 +104,89 @@ static const char *op_type(struct btree_op *op)
 #define PTR_HASH(c, k)                                                 \
        (((k)->ptr[0] >> c->bucket_bits) | PTR_GEN(k, 0))
 
-struct workqueue_struct *bch_gc_wq;
 static struct workqueue_struct *btree_io_wq;
 
-void bch_btree_op_init_stack(struct btree_op *op)
+static inline bool should_split(struct btree *b)
 {
-       memset(op, 0, sizeof(struct btree_op));
-       closure_init_stack(&op->cl);
-       op->lock = -1;
-       bch_keylist_init(&op->keys);
+       struct bset *i = write_block(b);
+       return b->written >= btree_blocks(b) ||
+               (b->written + __set_blocks(i, i->keys + 15, b->c)
+                > btree_blocks(b));
 }
 
+#define insert_lock(s, b)      ((b)->level <= (s)->lock)
+
+/*
+ * These macros are for recursing down the btree - they handle the details of
+ * locking and looking up nodes in the cache for you. They're best treated as
+ * mere syntax when reading code that uses them.
+ *
+ * op->lock determines whether we take a read or a write lock at a given depth.
+ * If you've got a read lock and find that you need a write lock (i.e. you're
+ * going to have to split), set op->lock and return -EINTR; btree_root() will
+ * call you again and you'll have the correct lock.
+ */
+
+/**
+ * btree - recurse down the btree on a specified key
+ * @fn:                function to call, which will be passed the child node
+ * @key:       key to recurse on
+ * @b:         parent btree node
+ * @op:                pointer to struct btree_op
+ */
+#define btree(fn, key, b, op, ...)                                     \
+({                                                                     \
+       int _r, l = (b)->level - 1;                                     \
+       bool _w = l <= (op)->lock;                                      \
+       struct btree *_child = bch_btree_node_get((b)->c, key, l, _w);  \
+       if (!IS_ERR(_child)) {                                          \
+               _child->parent = (b);                                   \
+               _r = bch_btree_ ## fn(_child, op, ##__VA_ARGS__);       \
+               rw_unlock(_w, _child);                                  \
+       } else                                                          \
+               _r = PTR_ERR(_child);                                   \
+       _r;                                                             \
+})
+
+/**
+ * btree_root - call a function on the root of the btree
+ * @fn:                function to call, which will be passed the child node
+ * @c:         cache set
+ * @op:                pointer to struct btree_op
+ */
+#define btree_root(fn, c, op, ...)                                     \
+({                                                                     \
+       int _r = -EINTR;                                                \
+       do {                                                            \
+               struct btree *_b = (c)->root;                           \
+               bool _w = insert_lock(op, _b);                          \
+               rw_lock(_w, _b, _b->level);                             \
+               if (_b == (c)->root &&                                  \
+                   _w == insert_lock(op, _b)) {                        \
+                       _b->parent = NULL;                              \
+                       _r = bch_btree_ ## fn(_b, op, ##__VA_ARGS__);   \
+               }                                                       \
+               rw_unlock(_w, _b);                                      \
+               bch_cannibalize_unlock(c);                              \
+               if (_r == -ENOSPC) {                                    \
+                       wait_event((c)->try_wait,                       \
+                                  !(c)->try_harder);                   \
+                       _r = -EINTR;                                    \
+               }                                                       \
+       } while (_r == -EINTR);                                         \
+                                                                       \
+       _r;                                                             \
+})
+
 /* Btree key manipulation */
 
-static void bkey_put(struct cache_set *c, struct bkey *k, int level)
+void bkey_put(struct cache_set *c, struct bkey *k)
 {
-       if ((level && KEY_OFFSET(k)) || !level)
-               __bkey_put(c, k);
+       unsigned i;
+
+       for (i = 0; i < KEY_PTRS(k); i++)
+               if (ptr_available(c, k, i))
+                       atomic_dec_bug(&PTR_BUCKET(c, k, i)->pin);
 }
 
 /* Btree IO */
@@ -145,6 +210,10 @@ static void bch_btree_node_read_done(struct btree *b)
        iter->size = b->c->sb.bucket_size / b->c->sb.block_size;
        iter->used = 0;
 
+#ifdef CONFIG_BCACHE_DEBUG
+       iter->b = b;
+#endif
+
        if (!i->seq)
                goto err;
 
@@ -160,7 +229,7 @@ static void bch_btree_node_read_done(struct btree *b)
                        goto err;
 
                err = "bad magic";
-               if (i->magic != bset_magic(b->c))
+               if (i->magic != bset_magic(&b->c->sb))
                        goto err;
 
                err = "bad checksum";
@@ -248,10 +317,7 @@ void bch_btree_node_read(struct btree *b)
                goto err;
 
        bch_btree_node_read_done(b);
-
-       spin_lock(&b->c->btree_read_time_lock);
        bch_time_stats_update(&b->c->btree_read_time, start_time);
-       spin_unlock(&b->c->btree_read_time_lock);
 
        return;
 err:
@@ -327,7 +393,7 @@ static void do_btree_node_write(struct btree *b)
        b->bio = bch_bbio_alloc(b->c);
 
        b->bio->bi_end_io       = btree_node_write_endio;
-       b->bio->bi_private      = &b->io.cl;
+       b->bio->bi_private      = cl;
        b->bio->bi_rw           = REQ_META|WRITE_SYNC|REQ_FUA;
        b->bio->bi_size         = set_blocks(i, b->c) * block_bytes(b->c);
        bch_bio_map(b->bio, i);
@@ -383,7 +449,7 @@ void bch_btree_node_write(struct btree *b, struct closure *parent)
        BUG_ON(b->written >= btree_blocks(b));
        BUG_ON(b->written && !i->keys);
        BUG_ON(b->sets->data->seq != i->seq);
-       bch_check_key_order(b, i);
+       bch_check_keys(b, "writing");
 
        cancel_delayed_work(&b->work);
 
@@ -405,6 +471,15 @@ void bch_btree_node_write(struct btree *b, struct closure *parent)
                bch_bset_init_next(b);
 }
 
+static void bch_btree_node_write_sync(struct btree *b)
+{
+       struct closure cl;
+
+       closure_init_stack(&cl);
+       bch_btree_node_write(b, &cl);
+       closure_sync(&cl);
+}
+
 static void btree_node_write_work(struct work_struct *w)
 {
        struct btree *b = container_of(to_delayed_work(w), struct btree, work);
@@ -416,7 +491,7 @@ static void btree_node_write_work(struct work_struct *w)
        rw_unlock(true, b);
 }
 
-static void bch_btree_leaf_dirty(struct btree *b, struct btree_op *op)
+static void bch_btree_leaf_dirty(struct btree *b, atomic_t *journal_ref)
 {
        struct bset *i = b->sets[b->nsets].data;
        struct btree_write *w = btree_current_write(b);
@@ -429,15 +504,15 @@ static void bch_btree_leaf_dirty(struct btree *b, struct btree_op *op)
 
        set_btree_node_dirty(b);
 
-       if (op && op->journal) {
+       if (journal_ref) {
                if (w->journal &&
-                   journal_pin_cmp(b->c, w, op)) {
+                   journal_pin_cmp(b->c, w->journal, journal_ref)) {
                        atomic_dec_bug(w->journal);
                        w->journal = NULL;
                }
 
                if (!w->journal) {
-                       w->journal = op->journal;
+                       w->journal = journal_ref;
                        atomic_inc(w->journal);
                }
        }
@@ -566,33 +641,32 @@ static struct btree *mca_bucket_alloc(struct cache_set *c,
        return b;
 }
 
-static int mca_reap(struct btree *b, struct closure *cl, unsigned min_order)
+static int mca_reap(struct btree *b, unsigned min_order, bool flush)
 {
+       struct closure cl;
+
+       closure_init_stack(&cl);
        lockdep_assert_held(&b->c->bucket_lock);
 
        if (!down_write_trylock(&b->lock))
                return -ENOMEM;
 
-       if (b->page_order < min_order) {
+       BUG_ON(btree_node_dirty(b) && !b->sets[0].data);
+
+       if (b->page_order < min_order ||
+           (!flush &&
+            (btree_node_dirty(b) ||
+             atomic_read(&b->io.cl.remaining) != -1))) {
                rw_unlock(true, b);
                return -ENOMEM;
        }
 
-       BUG_ON(btree_node_dirty(b) && !b->sets[0].data);
-
-       if (cl && btree_node_dirty(b))
-               bch_btree_node_write(b, NULL);
-
-       if (cl)
-               closure_wait_event_async(&b->io.wait, cl,
-                        atomic_read(&b->io.cl.remaining) == -1);
+       if (btree_node_dirty(b))
+               bch_btree_node_write_sync(b);
 
-       if (btree_node_dirty(b) ||
-           !closure_is_unlocked(&b->io.cl) ||
-           work_pending(&b->work.work)) {
-               rw_unlock(true, b);
-               return -EAGAIN;
-       }
+       /* wait for any in flight btree write */
+       closure_wait_event(&b->io.wait, &cl,
+                          atomic_read(&b->io.cl.remaining) == -1);
 
        return 0;
 }
@@ -633,7 +707,7 @@ static unsigned long bch_mca_scan(struct shrinker *shrink,
                        break;
 
                if (++i > 3 &&
-                   !mca_reap(b, NULL, 0)) {
+                   !mca_reap(b, 0, false)) {
                        mca_data_free(b);
                        rw_unlock(true, b);
                        freed++;
@@ -652,7 +726,7 @@ static unsigned long bch_mca_scan(struct shrinker *shrink,
                list_rotate_left(&c->btree_cache);
 
                if (!b->accessed &&
-                   !mca_reap(b, NULL, 0)) {
+                   !mca_reap(b, 0, false)) {
                        mca_bucket_free(b);
                        mca_data_free(b);
                        rw_unlock(true, b);
@@ -723,12 +797,9 @@ int bch_btree_cache_alloc(struct cache_set *c)
 {
        unsigned i;
 
-       /* XXX: doesn't check for errors */
-
-       closure_init_unlocked(&c->gc);
-
        for (i = 0; i < mca_reserve(c); i++)
-               mca_bucket_alloc(c, &ZERO_KEY, GFP_KERNEL);
+               if (!mca_bucket_alloc(c, &ZERO_KEY, GFP_KERNEL))
+                       return -ENOMEM;
 
        list_splice_init(&c->btree_cache,
                         &c->btree_cache_freeable);
@@ -775,52 +846,27 @@ out:
        return b;
 }
 
-static struct btree *mca_cannibalize(struct cache_set *c, struct bkey *k,
-                                    int level, struct closure *cl)
+static struct btree *mca_cannibalize(struct cache_set *c, struct bkey *k)
 {
-       int ret = -ENOMEM;
-       struct btree *i;
+       struct btree *b;
 
        trace_bcache_btree_cache_cannibalize(c);
 
-       if (!cl)
-               return ERR_PTR(-ENOMEM);
-
-       /*
-        * Trying to free up some memory - i.e. reuse some btree nodes - may
-        * require initiating IO to flush the dirty part of the node. If we're
-        * running under generic_make_request(), that IO will never finish and
-        * we would deadlock. Returning -EAGAIN causes the cache lookup code to
-        * punt to workqueue and retry.
-        */
-       if (current->bio_list)
-               return ERR_PTR(-EAGAIN);
-
-       if (c->try_harder && c->try_harder != cl) {
-               closure_wait_event_async(&c->try_wait, cl, !c->try_harder);
-               return ERR_PTR(-EAGAIN);
-       }
+       if (!c->try_harder) {
+               c->try_harder = current;
+               c->try_harder_start = local_clock();
+       } else if (c->try_harder != current)
+               return ERR_PTR(-ENOSPC);
 
-       c->try_harder = cl;
-       c->try_harder_start = local_clock();
-retry:
-       list_for_each_entry_reverse(i, &c->btree_cache, list) {
-               int r = mca_reap(i, cl, btree_order(k));
-               if (!r)
-                       return i;
-               if (r != -ENOMEM)
-                       ret = r;
-       }
+       list_for_each_entry_reverse(b, &c->btree_cache, list)
+               if (!mca_reap(b, btree_order(k), false))
+                       return b;
 
-       if (ret == -EAGAIN &&
-           closure_blocking(cl)) {
-               mutex_unlock(&c->bucket_lock);
-               closure_sync(cl);
-               mutex_lock(&c->bucket_lock);
-               goto retry;
-       }
+       list_for_each_entry_reverse(b, &c->btree_cache, list)
+               if (!mca_reap(b, btree_order(k), true))
+                       return b;
 
-       return ERR_PTR(ret);
+       return ERR_PTR(-ENOMEM);
 }
 
 /*
@@ -829,20 +875,21 @@ retry:
  * cannibalize_bucket() will take. This means every time we unlock the root of
  * the btree, we need to release this lock if we have it held.
  */
-void bch_cannibalize_unlock(struct cache_set *c, struct closure *cl)
+static void bch_cannibalize_unlock(struct cache_set *c)
 {
-       if (c->try_harder == cl) {
+       if (c->try_harder == current) {
                bch_time_stats_update(&c->try_harder_time, c->try_harder_start);
                c->try_harder = NULL;
-               __closure_wake_up(&c->try_wait);
+               wake_up(&c->try_wait);
        }
 }
 
-static struct btree *mca_alloc(struct cache_set *c, struct bkey *k,
-                              int level, struct closure *cl)
+static struct btree *mca_alloc(struct cache_set *c, struct bkey *k, int level)
 {
        struct btree *b;
 
+       BUG_ON(current->bio_list);
+
        lockdep_assert_held(&c->bucket_lock);
 
        if (mca_find(c, k))
@@ -852,14 +899,14 @@ static struct btree *mca_alloc(struct cache_set *c, struct bkey *k,
         * the list. Check if there's any freed nodes there:
         */
        list_for_each_entry(b, &c->btree_cache_freeable, list)
-               if (!mca_reap(b, NULL, btree_order(k)))
+               if (!mca_reap(b, btree_order(k), false))
                        goto out;
 
        /* We never free struct btree itself, just the memory that holds the on
         * disk node. Check the freed list before allocating a new one:
         */
        list_for_each_entry(b, &c->btree_cache_freed, list)
-               if (!mca_reap(b, NULL, 0)) {
+               if (!mca_reap(b, 0, false)) {
                        mca_data_alloc(b, k, __GFP_NOWARN|GFP_NOIO);
                        if (!b->sets[0].data)
                                goto err;
@@ -884,6 +931,7 @@ out:
 
        lock_set_subclass(&b->lock.dep_map, level + 1, _THIS_IP_);
        b->level        = level;
+       b->parent       = (void *) ~0UL;
 
        mca_reinit(b);
 
@@ -892,7 +940,7 @@ err:
        if (b)
                rw_unlock(true, b);
 
-       b = mca_cannibalize(c, k, level, cl);
+       b = mca_cannibalize(c, k);
        if (!IS_ERR(b))
                goto out;
 
@@ -903,17 +951,15 @@ err:
  * bch_btree_node_get - find a btree node in the cache and lock it, reading it
  * in from disk if necessary.
  *
- * If IO is necessary, it uses the closure embedded in struct btree_op to wait;
- * if that closure is in non blocking mode, will return -EAGAIN.
+ * If IO is necessary and running under generic_make_request, returns -EAGAIN.
  *
  * The btree node will have either a read or a write lock held, depending on
  * level and op->lock.
  */
 struct btree *bch_btree_node_get(struct cache_set *c, struct bkey *k,
-                                int level, struct btree_op *op)
+                                int level, bool write)
 {
        int i = 0;
-       bool write = level <= op->lock;
        struct btree *b;
 
        BUG_ON(level < 0);
@@ -925,7 +971,7 @@ retry:
                        return ERR_PTR(-EAGAIN);
 
                mutex_lock(&c->bucket_lock);
-               b = mca_alloc(c, k, level, &op->cl);
+               b = mca_alloc(c, k, level);
                mutex_unlock(&c->bucket_lock);
 
                if (!b)
@@ -971,7 +1017,7 @@ static void btree_node_prefetch(struct cache_set *c, struct bkey *k, int level)
        struct btree *b;
 
        mutex_lock(&c->bucket_lock);
-       b = mca_alloc(c, k, level, NULL);
+       b = mca_alloc(c, k, level);
        mutex_unlock(&c->bucket_lock);
 
        if (!IS_ERR_OR_NULL(b)) {
@@ -982,17 +1028,12 @@ static void btree_node_prefetch(struct cache_set *c, struct bkey *k, int level)
 
 /* Btree alloc */
 
-static void btree_node_free(struct btree *b, struct btree_op *op)
+static void btree_node_free(struct btree *b)
 {
        unsigned i;
 
        trace_bcache_btree_node_free(b);
 
-       /*
-        * The BUG_ON() in btree_node_get() implies that we must have a write
-        * lock on parent to free or even invalidate a node
-        */
-       BUG_ON(op->lock <= b->level);
        BUG_ON(b == b->c->root);
 
        if (btree_node_dirty(b))
@@ -1015,27 +1056,26 @@ static void btree_node_free(struct btree *b, struct btree_op *op)
        mutex_unlock(&b->c->bucket_lock);
 }
 
-struct btree *bch_btree_node_alloc(struct cache_set *c, int level,
-                                  struct closure *cl)
+struct btree *bch_btree_node_alloc(struct cache_set *c, int level, bool wait)
 {
        BKEY_PADDED(key) k;
        struct btree *b = ERR_PTR(-EAGAIN);
 
        mutex_lock(&c->bucket_lock);
 retry:
-       if (__bch_bucket_alloc_set(c, WATERMARK_METADATA, &k.key, 1, cl))
+       if (__bch_bucket_alloc_set(c, WATERMARK_METADATA, &k.key, 1, wait))
                goto err;
 
+       bkey_put(c, &k.key);
        SET_KEY_SIZE(&k.key, c->btree_pages * PAGE_SECTORS);
 
-       b = mca_alloc(c, &k.key, level, cl);
+       b = mca_alloc(c, &k.key, level);
        if (IS_ERR(b))
                goto err_free;
 
        if (!b) {
                cache_bug(c,
                        "Tried to allocate bucket that was in btree cache");
-               __bkey_put(c, &k.key);
                goto retry;
        }
 
@@ -1048,7 +1088,6 @@ retry:
        return b;
 err_free:
        bch_bucket_free(c, &k.key);
-       __bkey_put(c, &k.key);
 err:
        mutex_unlock(&c->bucket_lock);
 
@@ -1056,16 +1095,31 @@ err:
        return b;
 }
 
-static struct btree *btree_node_alloc_replacement(struct btree *b,
-                                                 struct closure *cl)
+static struct btree *btree_node_alloc_replacement(struct btree *b, bool wait)
 {
-       struct btree *n = bch_btree_node_alloc(b->c, b->level, cl);
+       struct btree *n = bch_btree_node_alloc(b->c, b->level, wait);
        if (!IS_ERR_OR_NULL(n))
                bch_btree_sort_into(b, n);
 
        return n;
 }
 
+static void make_btree_freeing_key(struct btree *b, struct bkey *k)
+{
+       unsigned i;
+
+       bkey_copy(k, &b->key);
+       bkey_copy_key(k, &ZERO_KEY);
+
+       for (i = 0; i < KEY_PTRS(k); i++) {
+               uint8_t g = PTR_BUCKET(b->c, k, i)->gen + 1;
+
+               SET_PTR_GEN(k, i, g);
+       }
+
+       atomic_inc(&b->c->prio_blocked);
+}
+
 /* Garbage collection */
 
 uint8_t __bch_btree_mark_key(struct cache_set *c, int level, struct bkey *k)
@@ -1119,12 +1173,10 @@ uint8_t __bch_btree_mark_key(struct cache_set *c, int level, struct bkey *k)
 
 #define btree_mark_key(b, k)   __bch_btree_mark_key(b->c, b->level, k)
 
-static int btree_gc_mark_node(struct btree *b, unsigned *keys,
-                             struct gc_stat *gc)
+static bool btree_gc_mark_node(struct btree *b, struct gc_stat *gc)
 {
        uint8_t stale = 0;
-       unsigned last_dev = -1;
-       struct bcache_device *d = NULL;
+       unsigned keys = 0, good_keys = 0;
        struct bkey *k;
        struct btree_iter iter;
        struct bset_tree *t;
@@ -1132,27 +1184,17 @@ static int btree_gc_mark_node(struct btree *b, unsigned *keys,
        gc->nodes++;
 
        for_each_key_filter(b, k, &iter, bch_ptr_invalid) {
-               if (last_dev != KEY_INODE(k)) {
-                       last_dev = KEY_INODE(k);
-
-                       d = KEY_INODE(k) < b->c->nr_uuids
-                               ? b->c->devices[last_dev]
-                               : NULL;
-               }
-
                stale = max(stale, btree_mark_key(b, k));
+               keys++;
 
                if (bch_ptr_bad(b, k))
                        continue;
 
-               *keys += bkey_u64s(k);
-
                gc->key_bytes += bkey_u64s(k);
                gc->nkeys++;
+               good_keys++;
 
                gc->data += KEY_SIZE(k);
-               if (KEY_DIRTY(k))
-                       gc->dirty += KEY_SIZE(k);
        }
 
        for (t = b->sets; t <= &b->sets[b->nsets]; t++)
@@ -1161,78 +1203,74 @@ static int btree_gc_mark_node(struct btree *b, unsigned *keys,
                             bkey_cmp(&b->key, &t->end) < 0,
                             b, "found short btree key in gc");
 
-       return stale;
-}
-
-static struct btree *btree_gc_alloc(struct btree *b, struct bkey *k,
-                                   struct btree_op *op)
-{
-       /*
-        * We block priorities from being written for the duration of garbage
-        * collection, so we can't sleep in btree_alloc() ->
-        * bch_bucket_alloc_set(), or we'd risk deadlock - so we don't pass it
-        * our closure.
-        */
-       struct btree *n = btree_node_alloc_replacement(b, NULL);
-
-       if (!IS_ERR_OR_NULL(n)) {
-               swap(b, n);
-               __bkey_put(b->c, &b->key);
+       if (b->c->gc_always_rewrite)
+               return true;
 
-               memcpy(k->ptr, b->key.ptr,
-                      sizeof(uint64_t) * KEY_PTRS(&b->key));
+       if (stale > 10)
+               return true;
 
-               btree_node_free(n, op);
-               up_write(&n->lock);
-       }
+       if ((keys - good_keys) * 2 > keys)
+               return true;
 
-       return b;
+       return false;
 }
 
-/*
- * Leaving this at 2 until we've got incremental garbage collection done; it
- * could be higher (and has been tested with 4) except that garbage collection
- * could take much longer, adversely affecting latency.
- */
-#define GC_MERGE_NODES 2U
+#define GC_MERGE_NODES 4U
 
 struct gc_merge_info {
        struct btree    *b;
-       struct bkey     *k;
        unsigned        keys;
 };
 
-static void btree_gc_coalesce(struct btree *b, struct btree_op *op,
-                             struct gc_stat *gc, struct gc_merge_info *r)
+static int bch_btree_insert_node(struct btree *, struct btree_op *,
+                                struct keylist *, atomic_t *, struct bkey *);
+
+static int btree_gc_coalesce(struct btree *b, struct btree_op *op,
+                            struct keylist *keylist, struct gc_stat *gc,
+                            struct gc_merge_info *r)
 {
-       unsigned nodes = 0, keys = 0, blocks;
-       int i;
+       unsigned i, nodes = 0, keys = 0, blocks;
+       struct btree *new_nodes[GC_MERGE_NODES];
+       struct closure cl;
+       struct bkey *k;
+
+       memset(new_nodes, 0, sizeof(new_nodes));
+       closure_init_stack(&cl);
 
-       while (nodes < GC_MERGE_NODES && r[nodes].b)
+       while (nodes < GC_MERGE_NODES && !IS_ERR_OR_NULL(r[nodes].b))
                keys += r[nodes++].keys;
 
        blocks = btree_default_blocks(b->c) * 2 / 3;
 
        if (nodes < 2 ||
            __set_blocks(b->sets[0].data, keys, b->c) > blocks * (nodes - 1))
-               return;
-
-       for (i = nodes - 1; i >= 0; --i) {
-               if (r[i].b->written)
-                       r[i].b = btree_gc_alloc(r[i].b, r[i].k, op);
+               return 0;
 
-               if (r[i].b->written)
-                       return;
+       for (i = 0; i < nodes; i++) {
+               new_nodes[i] = btree_node_alloc_replacement(r[i].b, false);
+               if (IS_ERR_OR_NULL(new_nodes[i]))
+                       goto out_nocoalesce;
        }
 
        for (i = nodes - 1; i > 0; --i) {
-               struct bset *n1 = r[i].b->sets->data;
-               struct bset *n2 = r[i - 1].b->sets->data;
+               struct bset *n1 = new_nodes[i]->sets->data;
+               struct bset *n2 = new_nodes[i - 1]->sets->data;
                struct bkey *k, *last = NULL;
 
                keys = 0;
 
-               if (i == 1) {
+               if (i > 1) {
+                       for (k = n2->start;
+                            k < end(n2);
+                            k = bkey_next(k)) {
+                               if (__set_blocks(n1, n1->keys + keys +
+                                                bkey_u64s(k), b->c) > blocks)
+                                       break;
+
+                               last = k;
+                               keys += bkey_u64s(k);
+                       }
+               } else {
                        /*
                         * Last node we're not getting rid of - we're getting
                         * rid of the node at r[0]. Have to try and fit all of
@@ -1241,37 +1279,27 @@ static void btree_gc_coalesce(struct btree *b, struct btree_op *op,
                         * length keys (shouldn't be possible in practice,
                         * though)
                         */
-                       if (__set_blocks(n1, n1->keys + r->keys,
-                                        b->c) > btree_blocks(r[i].b))
-                               return;
+                       if (__set_blocks(n1, n1->keys + n2->keys,
+                                        b->c) > btree_blocks(new_nodes[i]))
+                               goto out_nocoalesce;
 
                        keys = n2->keys;
+                       /* Take the key of the node we're getting rid of */
                        last = &r->b->key;
-               } else
-                       for (k = n2->start;
-                            k < end(n2);
-                            k = bkey_next(k)) {
-                               if (__set_blocks(n1, n1->keys + keys +
-                                                bkey_u64s(k), b->c) > blocks)
-                                       break;
-
-                               last = k;
-                               keys += bkey_u64s(k);
-                       }
+               }
 
                BUG_ON(__set_blocks(n1, n1->keys + keys,
-                                   b->c) > btree_blocks(r[i].b));
+                                   b->c) > btree_blocks(new_nodes[i]));
 
-               if (last) {
-                       bkey_copy_key(&r[i].b->key, last);
-                       bkey_copy_key(r[i].k, last);
-               }
+               if (last)
+                       bkey_copy_key(&new_nodes[i]->key, last);
 
                memcpy(end(n1),
                       n2->start,
                       (void *) node(n2, keys) - (void *) n2->start);
 
                n1->keys += keys;
+               r[i].keys = n1->keys;
 
                memmove(n2->start,
                        node(n2, keys),
@@ -1279,95 +1307,176 @@ static void btree_gc_coalesce(struct btree *b, struct btree_op *op,
 
                n2->keys -= keys;
 
-               r[i].keys       = n1->keys;
-               r[i - 1].keys   = n2->keys;
+               if (bch_keylist_realloc(keylist,
+                                       KEY_PTRS(&new_nodes[i]->key), b->c))
+                       goto out_nocoalesce;
+
+               bch_btree_node_write(new_nodes[i], &cl);
+               bch_keylist_add(keylist, &new_nodes[i]->key);
        }
 
-       btree_node_free(r->b, op);
-       up_write(&r->b->lock);
+       for (i = 0; i < nodes; i++) {
+               if (bch_keylist_realloc(keylist, KEY_PTRS(&r[i].b->key), b->c))
+                       goto out_nocoalesce;
 
-       trace_bcache_btree_gc_coalesce(nodes);
+               make_btree_freeing_key(r[i].b, keylist->top);
+               bch_keylist_push(keylist);
+       }
+
+       /* We emptied out this node */
+       BUG_ON(new_nodes[0]->sets->data->keys);
+       btree_node_free(new_nodes[0]);
+       rw_unlock(true, new_nodes[0]);
+
+       closure_sync(&cl);
+
+       for (i = 0; i < nodes; i++) {
+               btree_node_free(r[i].b);
+               rw_unlock(true, r[i].b);
+
+               r[i].b = new_nodes[i];
+       }
+
+       bch_btree_insert_node(b, op, keylist, NULL, NULL);
+       BUG_ON(!bch_keylist_empty(keylist));
+
+       memmove(r, r + 1, sizeof(r[0]) * (nodes - 1));
+       r[nodes - 1].b = ERR_PTR(-EINTR);
 
+       trace_bcache_btree_gc_coalesce(nodes);
        gc->nodes--;
-       nodes--;
 
-       memmove(&r[0], &r[1], sizeof(struct gc_merge_info) * nodes);
-       memset(&r[nodes], 0, sizeof(struct gc_merge_info));
+       /* Invalidated our iterator */
+       return -EINTR;
+
+out_nocoalesce:
+       closure_sync(&cl);
+
+       while ((k = bch_keylist_pop(keylist)))
+               if (!bkey_cmp(k, &ZERO_KEY))
+                       atomic_dec(&b->c->prio_blocked);
+
+       for (i = 0; i < nodes; i++)
+               if (!IS_ERR_OR_NULL(new_nodes[i])) {
+                       btree_node_free(new_nodes[i]);
+                       rw_unlock(true, new_nodes[i]);
+               }
+       return 0;
 }
 
-static int btree_gc_recurse(struct btree *b, struct btree_op *op,
-                           struct closure *writes, struct gc_stat *gc)
+static unsigned btree_gc_count_keys(struct btree *b)
 {
-       void write(struct btree *r)
-       {
-               if (!r->written)
-                       bch_btree_node_write(r, &op->cl);
-               else if (btree_node_dirty(r))
-                       bch_btree_node_write(r, writes);
+       struct bkey *k;
+       struct btree_iter iter;
+       unsigned ret = 0;
 
-               up_write(&r->lock);
-       }
+       for_each_key_filter(b, k, &iter, bch_ptr_bad)
+               ret += bkey_u64s(k);
+
+       return ret;
+}
 
-       int ret = 0, stale;
+static int btree_gc_recurse(struct btree *b, struct btree_op *op,
+                           struct closure *writes, struct gc_stat *gc)
+{
        unsigned i;
+       int ret = 0;
+       bool should_rewrite;
+       struct btree *n;
+       struct bkey *k;
+       struct keylist keys;
+       struct btree_iter iter;
        struct gc_merge_info r[GC_MERGE_NODES];
+       struct gc_merge_info *last = r + GC_MERGE_NODES - 1;
 
-       memset(r, 0, sizeof(r));
+       bch_keylist_init(&keys);
+       bch_btree_iter_init(b, &iter, &b->c->gc_done);
 
-       while ((r->k = bch_next_recurse_key(b, &b->c->gc_done))) {
-               r->b = bch_btree_node_get(b->c, r->k, b->level - 1, op);
+       for (i = 0; i < GC_MERGE_NODES; i++)
+               r[i].b = ERR_PTR(-EINTR);
 
-               if (IS_ERR(r->b)) {
-                       ret = PTR_ERR(r->b);
-                       break;
+       while (1) {
+               k = bch_btree_iter_next_filter(&iter, b, bch_ptr_bad);
+               if (k) {
+                       r->b = bch_btree_node_get(b->c, k, b->level - 1, true);
+                       if (IS_ERR(r->b)) {
+                               ret = PTR_ERR(r->b);
+                               break;
+                       }
+
+                       r->keys = btree_gc_count_keys(r->b);
+
+                       ret = btree_gc_coalesce(b, op, &keys, gc, r);
+                       if (ret)
+                               break;
                }
 
-               r->keys = 0;
-               stale = btree_gc_mark_node(r->b, &r->keys, gc);
+               if (!last->b)
+                       break;
 
-               if (!b->written &&
-                   (r->b->level || stale > 10 ||
-                    b->c->gc_always_rewrite))
-                       r->b = btree_gc_alloc(r->b, r->k, op);
+               if (!IS_ERR(last->b)) {
+                       should_rewrite = btree_gc_mark_node(last->b, gc);
+                       if (should_rewrite) {
+                               n = btree_node_alloc_replacement(last->b,
+                                                                false);
 
-               if (r->b->level)
-                       ret = btree_gc_recurse(r->b, op, writes, gc);
+                               if (!IS_ERR_OR_NULL(n)) {
+                                       bch_btree_node_write_sync(n);
+                                       bch_keylist_add(&keys, &n->key);
 
-               if (ret) {
-                       write(r->b);
-                       break;
-               }
+                                       make_btree_freeing_key(last->b,
+                                                              keys.top);
+                                       bch_keylist_push(&keys);
+
+                                       btree_node_free(last->b);
+
+                                       bch_btree_insert_node(b, op, &keys,
+                                                             NULL, NULL);
+                                       BUG_ON(!bch_keylist_empty(&keys));
 
-               bkey_copy_key(&b->c->gc_done, r->k);
+                                       rw_unlock(true, last->b);
+                                       last->b = n;
 
-               if (!b->written)
-                       btree_gc_coalesce(b, op, gc, r);
+                                       /* Invalidated our iterator */
+                                       ret = -EINTR;
+                                       break;
+                               }
+                       }
 
-               if (r[GC_MERGE_NODES - 1].b)
-                       write(r[GC_MERGE_NODES - 1].b);
+                       if (last->b->level) {
+                               ret = btree_gc_recurse(last->b, op, writes, gc);
+                               if (ret)
+                                       break;
+                       }
 
-               memmove(&r[1], &r[0],
-                       sizeof(struct gc_merge_info) * (GC_MERGE_NODES - 1));
+                       bkey_copy_key(&b->c->gc_done, &last->b->key);
+
+                       /*
+                        * Must flush leaf nodes before gc ends, since replace
+                        * operations aren't journalled
+                        */
+                       if (btree_node_dirty(last->b))
+                               bch_btree_node_write(last->b, writes);
+                       rw_unlock(true, last->b);
+               }
+
+               memmove(r + 1, r, sizeof(r[0]) * (GC_MERGE_NODES - 1));
+               r->b = NULL;
 
-               /* When we've got incremental GC working, we'll want to do
-                * if (should_resched())
-                *      return -EAGAIN;
-                */
-               cond_resched();
-#if 0
                if (need_resched()) {
                        ret = -EAGAIN;
                        break;
                }
-#endif
        }
 
-       for (i = 1; i < GC_MERGE_NODES && r[i].b; i++)
-               write(r[i].b);
+       for (i = 0; i < GC_MERGE_NODES; i++)
+               if (!IS_ERR_OR_NULL(r[i].b)) {
+                       if (btree_node_dirty(r[i].b))
+                               bch_btree_node_write(r[i].b, writes);
+                       rw_unlock(true, r[i].b);
+               }
 
-       /* Might have freed some children, must remove their keys */
-       if (!b->written)
-               bch_btree_sort(b);
+       bch_keylist_free(&keys);
 
        return ret;
 }
@@ -1376,29 +1485,31 @@ static int bch_btree_gc_root(struct btree *b, struct btree_op *op,
                             struct closure *writes, struct gc_stat *gc)
 {
        struct btree *n = NULL;
-       unsigned keys = 0;
-       int ret = 0, stale = btree_gc_mark_node(b, &keys, gc);
-
-       if (b->level || stale > 10)
-               n = btree_node_alloc_replacement(b, NULL);
+       int ret = 0;
+       bool should_rewrite;
 
-       if (!IS_ERR_OR_NULL(n))
-               swap(b, n);
+       should_rewrite = btree_gc_mark_node(b, gc);
+       if (should_rewrite) {
+               n = btree_node_alloc_replacement(b, false);
 
-       if (b->level)
-               ret = btree_gc_recurse(b, op, writes, gc);
+               if (!IS_ERR_OR_NULL(n)) {
+                       bch_btree_node_write_sync(n);
+                       bch_btree_set_root(n);
+                       btree_node_free(b);
+                       rw_unlock(true, n);
 
-       if (!b->written || btree_node_dirty(b)) {
-               bch_btree_node_write(b, n ? &op->cl : NULL);
+                       return -EINTR;
+               }
        }
 
-       if (!IS_ERR_OR_NULL(n)) {
-               closure_sync(&op->cl);
-               bch_btree_set_root(b);
-               btree_node_free(n, op);
-               rw_unlock(true, b);
+       if (b->level) {
+               ret = btree_gc_recurse(b, op, writes, gc);
+               if (ret)
+                       return ret;
        }
 
+       bkey_copy_key(&b->c->gc_done, &b->key);
+
        return ret;
 }
 
@@ -1479,9 +1590,8 @@ size_t bch_btree_gc_finish(struct cache_set *c)
        return available;
 }
 
-static void bch_btree_gc(struct closure *cl)
+static void bch_btree_gc(struct cache_set *c)
 {
-       struct cache_set *c = container_of(cl, struct cache_set, gc.cl);
        int ret;
        unsigned long available;
        struct gc_stat stats;
@@ -1493,47 +1603,73 @@ static void bch_btree_gc(struct closure *cl)
 
        memset(&stats, 0, sizeof(struct gc_stat));
        closure_init_stack(&writes);
-       bch_btree_op_init_stack(&op);
-       op.lock = SHRT_MAX;
+       bch_btree_op_init(&op, SHRT_MAX);
 
        btree_gc_start(c);
 
-       atomic_inc(&c->prio_blocked);
-
-       ret = btree_root(gc_root, c, &op, &writes, &stats);
-       closure_sync(&op.cl);
-       closure_sync(&writes);
-
-       if (ret) {
-               pr_warn("gc failed!");
-               continue_at(cl, bch_btree_gc, bch_gc_wq);
-       }
+       do {
+               ret = btree_root(gc_root, c, &op, &writes, &stats);
+               closure_sync(&writes);
 
-       /* Possibly wait for new UUIDs or whatever to hit disk */
-       bch_journal_meta(c, &op.cl);
-       closure_sync(&op.cl);
+               if (ret && ret != -EAGAIN)
+                       pr_warn("gc failed!");
+       } while (ret);
 
        available = bch_btree_gc_finish(c);
-
-       atomic_dec(&c->prio_blocked);
        wake_up_allocators(c);
 
        bch_time_stats_update(&c->btree_gc_time, start_time);
 
        stats.key_bytes *= sizeof(uint64_t);
-       stats.dirty     <<= 9;
        stats.data      <<= 9;
        stats.in_use    = (c->nbuckets - available) * 100 / c->nbuckets;
        memcpy(&c->gc_stats, &stats, sizeof(struct gc_stat));
 
        trace_bcache_gc_end(c);
 
-       continue_at(cl, bch_moving_gc, bch_gc_wq);
+       bch_moving_gc(c);
+}
+
+static int bch_gc_thread(void *arg)
+{
+       struct cache_set *c = arg;
+       struct cache *ca;
+       unsigned i;
+
+       while (1) {
+again:
+               bch_btree_gc(c);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (kthread_should_stop())
+                       break;
+
+               mutex_lock(&c->bucket_lock);
+
+               for_each_cache(ca, c, i)
+                       if (ca->invalidate_needs_gc) {
+                               mutex_unlock(&c->bucket_lock);
+                               set_current_state(TASK_RUNNING);
+                               goto again;
+                       }
+
+               mutex_unlock(&c->bucket_lock);
+
+               try_to_freeze();
+               schedule();
+       }
+
+       return 0;
 }
 
-void bch_queue_gc(struct cache_set *c)
+int bch_gc_thread_start(struct cache_set *c)
 {
-       closure_trylock_call(&c->gc.cl, bch_btree_gc, bch_gc_wq, &c->cl);
+       c->gc_thread = kthread_create(bch_gc_thread, c, "bcache_gc");
+       if (IS_ERR(c->gc_thread))
+               return PTR_ERR(c->gc_thread);
+
+       set_task_state(c->gc_thread, TASK_INTERRUPTIBLE);
+       return 0;
 }
 
 /* Initial partial gc */
@@ -1541,9 +1677,9 @@ void bch_queue_gc(struct cache_set *c)
 static int bch_btree_check_recurse(struct btree *b, struct btree_op *op,
                                   unsigned long **seen)
 {
-       int ret;
+       int ret = 0;
        unsigned i;
-       struct bkey *k;
+       struct bkey *k, *p = NULL;
        struct bucket *g;
        struct btree_iter iter;
 
@@ -1570,31 +1706,32 @@ static int bch_btree_check_recurse(struct btree *b, struct btree_op *op,
        }
 
        if (b->level) {
-               k = bch_next_recurse_key(b, &ZERO_KEY);
+               bch_btree_iter_init(b, &iter, NULL);
 
-               while (k) {
-                       struct bkey *p = bch_next_recurse_key(b, k);
-                       if (p)
-                               btree_node_prefetch(b->c, p, b->level - 1);
+               do {
+                       k = bch_btree_iter_next_filter(&iter, b, bch_ptr_bad);
+                       if (k)
+                               btree_node_prefetch(b->c, k, b->level - 1);
 
-                       ret = btree(check_recurse, k, b, op, seen);
-                       if (ret)
-                               return ret;
+                       if (p)
+                               ret = btree(check_recurse, p, b, op, seen);
 
-                       k = p;
-               }
+                       p = k;
+               } while (p && !ret);
        }
 
        return 0;
 }
 
-int bch_btree_check(struct cache_set *c, struct btree_op *op)
+int bch_btree_check(struct cache_set *c)
 {
        int ret = -ENOMEM;
        unsigned i;
        unsigned long *seen[MAX_CACHES_PER_SET];
+       struct btree_op op;
 
        memset(seen, 0, sizeof(seen));
+       bch_btree_op_init(&op, SHRT_MAX);
 
        for (i = 0; c->cache[i]; i++) {
                size_t n = DIV_ROUND_UP(c->cache[i]->sb.nbuckets, 8);
@@ -1606,7 +1743,7 @@ int bch_btree_check(struct cache_set *c, struct btree_op *op)
                memset(seen[i], 0xFF, n);
        }
 
-       ret = btree_root(check_recurse, c, op, seen);
+       ret = btree_root(check_recurse, c, &op, seen);
 err:
        for (i = 0; i < MAX_CACHES_PER_SET; i++)
                kfree(seen[i]);
@@ -1628,10 +1765,9 @@ static void shift_keys(struct btree *b, struct bkey *where, struct bkey *insert)
        bch_bset_fix_lookup_table(b, where);
 }
 
-static bool fix_overlapping_extents(struct btree *b,
-                                   struct bkey *insert,
+static bool fix_overlapping_extents(struct btree *b, struct bkey *insert,
                                    struct btree_iter *iter,
-                                   struct btree_op *op)
+                                   struct bkey *replace_key)
 {
        void subtract_dirty(struct bkey *k, uint64_t offset, int sectors)
        {
@@ -1659,39 +1795,38 @@ static bool fix_overlapping_extents(struct btree *b,
                 * We might overlap with 0 size extents; we can't skip these
                 * because if they're in the set we're inserting to we have to
                 * adjust them so they don't overlap with the key we're
-                * inserting. But we don't want to check them for BTREE_REPLACE
+                * inserting. But we don't want to check them for replace
                 * operations.
                 */
 
-               if (op->type == BTREE_REPLACE &&
-                   KEY_SIZE(k)) {
+               if (replace_key && KEY_SIZE(k)) {
                        /*
                         * k might have been split since we inserted/found the
                         * key we're replacing
                         */
                        unsigned i;
                        uint64_t offset = KEY_START(k) -
-                               KEY_START(&op->replace);
+                               KEY_START(replace_key);
 
                        /* But it must be a subset of the replace key */
-                       if (KEY_START(k) < KEY_START(&op->replace) ||
-                           KEY_OFFSET(k) > KEY_OFFSET(&op->replace))
+                       if (KEY_START(k) < KEY_START(replace_key) ||
+                           KEY_OFFSET(k) > KEY_OFFSET(replace_key))
                                goto check_failed;
 
                        /* We didn't find a key that we were supposed to */
                        if (KEY_START(k) > KEY_START(insert) + sectors_found)
                                goto check_failed;
 
-                       if (KEY_PTRS(&op->replace) != KEY_PTRS(k))
+                       if (KEY_PTRS(replace_key) != KEY_PTRS(k))
                                goto check_failed;
 
                        /* skip past gen */
                        offset <<= 8;
 
-                       BUG_ON(!KEY_PTRS(&op->replace));
+                       BUG_ON(!KEY_PTRS(replace_key));
 
-                       for (i = 0; i < KEY_PTRS(&op->replace); i++)
-                               if (k->ptr[i] != op->replace.ptr[i] + offset)
+                       for (i = 0; i < KEY_PTRS(replace_key); i++)
+                               if (k->ptr[i] != replace_key->ptr[i] + offset)
                                        goto check_failed;
 
                        sectors_found = KEY_OFFSET(k) - KEY_START(insert);
@@ -1742,6 +1877,9 @@ static bool fix_overlapping_extents(struct btree *b,
                if (bkey_cmp(insert, k) < 0) {
                        bch_cut_front(insert, k);
                } else {
+                       if (bkey_cmp(&START_KEY(insert), &START_KEY(k)) > 0)
+                               old_offset = KEY_START(insert);
+
                        if (bkey_written(b, k) &&
                            bkey_cmp(&START_KEY(insert), &START_KEY(k)) <= 0) {
                                /*
@@ -1759,9 +1897,8 @@ static bool fix_overlapping_extents(struct btree *b,
        }
 
 check_failed:
-       if (op->type == BTREE_REPLACE) {
+       if (replace_key) {
                if (!sectors_found) {
-                       op->insert_collision = true;
                        return true;
                } else if (sectors_found < KEY_SIZE(insert)) {
                        SET_KEY_OFFSET(insert, KEY_OFFSET(insert) -
@@ -1774,7 +1911,7 @@ check_failed:
 }
 
 static bool btree_insert_key(struct btree *b, struct btree_op *op,
-                            struct bkey *k)
+                            struct bkey *k, struct bkey *replace_key)
 {
        struct bset *i = b->sets[b->nsets].data;
        struct bkey *m, *prev;
@@ -1786,22 +1923,23 @@ static bool btree_insert_key(struct btree *b, struct btree_op *op,
 
        if (!b->level) {
                struct btree_iter iter;
-               struct bkey search = KEY(KEY_INODE(k), KEY_START(k), 0);
 
                /*
                 * bset_search() returns the first key that is strictly greater
                 * than the search key - but for back merging, we want to find
-                * the first key that is greater than or equal to KEY_START(k) -
-                * unless KEY_START(k) is 0.
+                * the previous key.
                 */
-               if (KEY_OFFSET(&search))
-                       SET_KEY_OFFSET(&search, KEY_OFFSET(&search) - 1);
-
                prev = NULL;
-               m = bch_btree_iter_init(b, &iter, &search);
+               m = bch_btree_iter_init(b, &iter, PRECEDING_KEY(&START_KEY(k)));
 
-               if (fix_overlapping_extents(b, k, &iter, op))
+               if (fix_overlapping_extents(b, k, &iter, replace_key)) {
+                       op->insert_collision = true;
                        return false;
+               }
+
+               if (KEY_DIRTY(k))
+                       bcache_dev_sectors_dirty_add(b->c, KEY_INODE(k),
+                                                    KEY_START(k), KEY_SIZE(k));
 
                while (m != end(i) &&
                       bkey_cmp(k, &START_KEY(m)) > 0)
@@ -1825,84 +1963,80 @@ static bool btree_insert_key(struct btree *b, struct btree_op *op,
                if (m != end(i) &&
                    bch_bkey_try_merge(b, k, m))
                        goto copy;
-       } else
+       } else {
+               BUG_ON(replace_key);
                m = bch_bset_search(b, &b->sets[b->nsets], k);
+       }
 
 insert:        shift_keys(b, m, k);
 copy:  bkey_copy(m, k);
 merged:
-       if (KEY_DIRTY(k))
-               bcache_dev_sectors_dirty_add(b->c, KEY_INODE(k),
-                                            KEY_START(k), KEY_SIZE(k));
-
-       bch_check_keys(b, "%u for %s", status, op_type(op));
+       bch_check_keys(b, "%u for %s", status,
+                      replace_key ? "replace" : "insert");
 
        if (b->level && !KEY_OFFSET(k))
                btree_current_write(b)->prio_blocked++;
 
-       trace_bcache_btree_insert_key(b, k, op->type, status);
+       trace_bcache_btree_insert_key(b, k, replace_key != NULL, status);
 
        return true;
 }
 
-static bool bch_btree_insert_keys(struct btree *b, struct btree_op *op)
+static bool bch_btree_insert_keys(struct btree *b, struct btree_op *op,
+                                 struct keylist *insert_keys,
+                                 struct bkey *replace_key)
 {
        bool ret = false;
-       struct bkey *k;
-       unsigned oldsize = bch_count_data(b);
-
-       while ((k = bch_keylist_pop(&op->keys))) {
-               bkey_put(b->c, k, b->level);
-               ret |= btree_insert_key(b, op, k);
-       }
-
-       BUG_ON(bch_count_data(b) < oldsize);
-       return ret;
-}
+       int oldsize = bch_count_data(b);
 
-bool bch_btree_insert_check_key(struct btree *b, struct btree_op *op,
-                                  struct bio *bio)
-{
-       bool ret = false;
-       uint64_t btree_ptr = b->key.ptr[0];
-       unsigned long seq = b->seq;
-       BKEY_PADDED(k) tmp;
+       while (!bch_keylist_empty(insert_keys)) {
+               struct bset *i = write_block(b);
+               struct bkey *k = insert_keys->keys;
 
-       rw_unlock(false, b);
-       rw_lock(true, b, b->level);
+               if (b->written + __set_blocks(i, i->keys + bkey_u64s(k), b->c)
+                   > btree_blocks(b))
+                       break;
 
-       if (b->key.ptr[0] != btree_ptr ||
-           b->seq != seq + 1 ||
-           should_split(b))
-               goto out;
+               if (bkey_cmp(k, &b->key) <= 0) {
+                       if (!b->level)
+                               bkey_put(b->c, k);
 
-       op->replace = KEY(op->inode, bio_end_sector(bio), bio_sectors(bio));
+                       ret |= btree_insert_key(b, op, k, replace_key);
+                       bch_keylist_pop_front(insert_keys);
+               } else if (bkey_cmp(&START_KEY(k), &b->key) < 0) {
+                       BKEY_PADDED(key) temp;
+                       bkey_copy(&temp.key, insert_keys->keys);
 
-       SET_KEY_PTRS(&op->replace, 1);
-       get_random_bytes(&op->replace.ptr[0], sizeof(uint64_t));
+                       bch_cut_back(&b->key, &temp.key);
+                       bch_cut_front(&b->key, insert_keys->keys);
 
-       SET_PTR_DEV(&op->replace, 0, PTR_CHECK_DEV);
+                       ret |= btree_insert_key(b, op, &temp.key, replace_key);
+                       break;
+               } else {
+                       break;
+               }
+       }
 
-       bkey_copy(&tmp.k, &op->replace);
+       BUG_ON(!bch_keylist_empty(insert_keys) && b->level);
 
-       BUG_ON(op->type != BTREE_INSERT);
-       BUG_ON(!btree_insert_key(b, op, &tmp.k));
-       ret = true;
-out:
-       downgrade_write(&b->lock);
+       BUG_ON(bch_count_data(b) < oldsize);
        return ret;
 }
 
-static int btree_split(struct btree *b, struct btree_op *op)
+static int btree_split(struct btree *b, struct btree_op *op,
+                      struct keylist *insert_keys,
+                      struct bkey *replace_key)
 {
-       bool split, root = b == b->c->root;
+       bool split;
        struct btree *n1, *n2 = NULL, *n3 = NULL;
        uint64_t start_time = local_clock();
+       struct closure cl;
+       struct keylist parent_keys;
 
-       if (b->level)
-               set_closure_blocking(&op->cl);
+       closure_init_stack(&cl);
+       bch_keylist_init(&parent_keys);
 
-       n1 = btree_node_alloc_replacement(b, &op->cl);
+       n1 = btree_node_alloc_replacement(b, true);
        if (IS_ERR(n1))
                goto err;
 
@@ -1913,19 +2047,20 @@ static int btree_split(struct btree *b, struct btree_op *op)
 
                trace_bcache_btree_node_split(b, n1->sets[0].data->keys);
 
-               n2 = bch_btree_node_alloc(b->c, b->level, &op->cl);
+               n2 = bch_btree_node_alloc(b->c, b->level, true);
                if (IS_ERR(n2))
                        goto err_free1;
 
-               if (root) {
-                       n3 = bch_btree_node_alloc(b->c, b->level + 1, &op->cl);
+               if (!b->parent) {
+                       n3 = bch_btree_node_alloc(b->c, b->level + 1, true);
                        if (IS_ERR(n3))
                                goto err_free2;
                }
 
-               bch_btree_insert_keys(n1, op);
+               bch_btree_insert_keys(n1, op, insert_keys, replace_key);
 
-               /* Has to be a linear search because we don't have an auxiliary
+               /*
+                * Has to be a linear search because we don't have an auxiliary
                 * search tree yet
                 */
 
@@ -1944,60 +2079,57 @@ static int btree_split(struct btree *b, struct btree_op *op)
 
                bkey_copy_key(&n2->key, &b->key);
 
-               bch_keylist_add(&op->keys, &n2->key);
-               bch_btree_node_write(n2, &op->cl);
+               bch_keylist_add(&parent_keys, &n2->key);
+               bch_btree_node_write(n2, &cl);
                rw_unlock(true, n2);
        } else {
                trace_bcache_btree_node_compact(b, n1->sets[0].data->keys);
 
-               bch_btree_insert_keys(n1, op);
+               bch_btree_insert_keys(n1, op, insert_keys, replace_key);
        }
 
-       bch_keylist_add(&op->keys, &n1->key);
-       bch_btree_node_write(n1, &op->cl);
+       bch_keylist_add(&parent_keys, &n1->key);
+       bch_btree_node_write(n1, &cl);
 
        if (n3) {
+               /* Depth increases, make a new root */
                bkey_copy_key(&n3->key, &MAX_KEY);
-               bch_btree_insert_keys(n3, op);
-               bch_btree_node_write(n3, &op->cl);
+               bch_btree_insert_keys(n3, op, &parent_keys, NULL);
+               bch_btree_node_write(n3, &cl);
 
-               closure_sync(&op->cl);
+               closure_sync(&cl);
                bch_btree_set_root(n3);
                rw_unlock(true, n3);
-       } else if (root) {
-               op->keys.top = op->keys.bottom;
-               closure_sync(&op->cl);
-               bch_btree_set_root(n1);
-       } else {
-               unsigned i;
 
-               bkey_copy(op->keys.top, &b->key);
-               bkey_copy_key(op->keys.top, &ZERO_KEY);
+               btree_node_free(b);
+       } else if (!b->parent) {
+               /* Root filled up but didn't need to be split */
+               closure_sync(&cl);
+               bch_btree_set_root(n1);
 
-               for (i = 0; i < KEY_PTRS(&b->key); i++) {
-                       uint8_t g = PTR_BUCKET(b->c, &b->key, i)->gen + 1;
+               btree_node_free(b);
+       } else {
+               /* Split a non root node */
+               closure_sync(&cl);
+               make_btree_freeing_key(b, parent_keys.top);
+               bch_keylist_push(&parent_keys);
 
-                       SET_PTR_GEN(op->keys.top, i, g);
-               }
+               btree_node_free(b);
 
-               bch_keylist_push(&op->keys);
-               closure_sync(&op->cl);
-               atomic_inc(&b->c->prio_blocked);
+               bch_btree_insert_node(b->parent, op, &parent_keys, NULL, NULL);
+               BUG_ON(!bch_keylist_empty(&parent_keys));
        }
 
        rw_unlock(true, n1);
-       btree_node_free(b, op);
 
        bch_time_stats_update(&b->c->btree_split_time, start_time);
 
        return 0;
 err_free2:
-       __bkey_put(n2->c, &n2->key);
-       btree_node_free(n2, op);
+       btree_node_free(n2);
        rw_unlock(true, n2);
 err_free1:
-       __bkey_put(n1->c, &n1->key);
-       btree_node_free(n1, op);
+       btree_node_free(n1);
        rw_unlock(true, n1);
 err:
        if (n3 == ERR_PTR(-EAGAIN) ||
@@ -2009,116 +2141,126 @@ err:
        return -ENOMEM;
 }
 
-static int bch_btree_insert_recurse(struct btree *b, struct btree_op *op,
-                                   struct keylist *stack_keys)
+static int bch_btree_insert_node(struct btree *b, struct btree_op *op,
+                                struct keylist *insert_keys,
+                                atomic_t *journal_ref,
+                                struct bkey *replace_key)
 {
-       if (b->level) {
-               int ret;
-               struct bkey *insert = op->keys.bottom;
-               struct bkey *k = bch_next_recurse_key(b, &START_KEY(insert));
-
-               if (!k) {
-                       btree_bug(b, "no key to recurse on at level %i/%i",
-                                 b->level, b->c->root->level);
+       BUG_ON(b->level && replace_key);
 
-                       op->keys.top = op->keys.bottom;
-                       return -EIO;
+       if (should_split(b)) {
+               if (current->bio_list) {
+                       op->lock = b->c->root->level + 1;
+                       return -EAGAIN;
+               } else if (op->lock <= b->c->root->level) {
+                       op->lock = b->c->root->level + 1;
+                       return -EINTR;
+               } else {
+                       /* Invalidated all iterators */
+                       return btree_split(b, op, insert_keys, replace_key) ?:
+                               -EINTR;
                }
+       } else {
+               BUG_ON(write_block(b) != b->sets[b->nsets].data);
 
-               if (bkey_cmp(insert, k) > 0) {
-                       unsigned i;
-
-                       if (op->type == BTREE_REPLACE) {
-                               __bkey_put(b->c, insert);
-                               op->keys.top = op->keys.bottom;
-                               op->insert_collision = true;
-                               return 0;
-                       }
+               if (bch_btree_insert_keys(b, op, insert_keys, replace_key)) {
+                       if (!b->level)
+                               bch_btree_leaf_dirty(b, journal_ref);
+                       else
+                               bch_btree_node_write_sync(b);
+               }
 
-                       for (i = 0; i < KEY_PTRS(insert); i++)
-                               atomic_inc(&PTR_BUCKET(b->c, insert, i)->pin);
+               return 0;
+       }
+}
 
-                       bkey_copy(stack_keys->top, insert);
+int bch_btree_insert_check_key(struct btree *b, struct btree_op *op,
+                              struct bkey *check_key)
+{
+       int ret = -EINTR;
+       uint64_t btree_ptr = b->key.ptr[0];
+       unsigned long seq = b->seq;
+       struct keylist insert;
+       bool upgrade = op->lock == -1;
 
-                       bch_cut_back(k, insert);
-                       bch_cut_front(k, stack_keys->top);
+       bch_keylist_init(&insert);
 
-                       bch_keylist_push(stack_keys);
-               }
+       if (upgrade) {
+               rw_unlock(false, b);
+               rw_lock(true, b, b->level);
 
-               ret = btree(insert_recurse, k, b, op, stack_keys);
-               if (ret)
-                       return ret;
+               if (b->key.ptr[0] != btree_ptr ||
+                   b->seq != seq + 1)
+                       goto out;
        }
 
-       if (!bch_keylist_empty(&op->keys)) {
-               if (should_split(b)) {
-                       if (op->lock <= b->c->root->level) {
-                               BUG_ON(b->level);
-                               op->lock = b->c->root->level + 1;
-                               return -EINTR;
-                       }
-                       return btree_split(b, op);
-               }
+       SET_KEY_PTRS(check_key, 1);
+       get_random_bytes(&check_key->ptr[0], sizeof(uint64_t));
 
-               BUG_ON(write_block(b) != b->sets[b->nsets].data);
+       SET_PTR_DEV(check_key, 0, PTR_CHECK_DEV);
 
-               if (bch_btree_insert_keys(b, op)) {
-                       if (!b->level)
-                               bch_btree_leaf_dirty(b, op);
-                       else
-                               bch_btree_node_write(b, &op->cl);
-               }
-       }
+       bch_keylist_add(&insert, check_key);
 
-       return 0;
+       ret = bch_btree_insert_node(b, op, &insert, NULL, NULL);
+
+       BUG_ON(!ret && !bch_keylist_empty(&insert));
+out:
+       if (upgrade)
+               downgrade_write(&b->lock);
+       return ret;
 }
 
-int bch_btree_insert(struct btree_op *op, struct cache_set *c)
+struct btree_insert_op {
+       struct btree_op op;
+       struct keylist  *keys;
+       atomic_t        *journal_ref;
+       struct bkey     *replace_key;
+};
+
+int btree_insert_fn(struct btree_op *b_op, struct btree *b)
 {
-       int ret = 0;
-       struct keylist stack_keys;
+       struct btree_insert_op *op = container_of(b_op,
+                                       struct btree_insert_op, op);
 
-       /*
-        * Don't want to block with the btree locked unless we have to,
-        * otherwise we get deadlocks with try_harder and between split/gc
-        */
-       clear_closure_blocking(&op->cl);
-
-       BUG_ON(bch_keylist_empty(&op->keys));
-       bch_keylist_copy(&stack_keys, &op->keys);
-       bch_keylist_init(&op->keys);
-
-       while (!bch_keylist_empty(&stack_keys) ||
-              !bch_keylist_empty(&op->keys)) {
-               if (bch_keylist_empty(&op->keys)) {
-                       bch_keylist_add(&op->keys,
-                                       bch_keylist_pop(&stack_keys));
-                       op->lock = 0;
-               }
+       int ret = bch_btree_insert_node(b, &op->op, op->keys,
+                                       op->journal_ref, op->replace_key);
+       if (ret && !bch_keylist_empty(op->keys))
+               return ret;
+       else
+               return MAP_DONE;
+}
 
-               ret = btree_root(insert_recurse, c, op, &stack_keys);
+int bch_btree_insert(struct cache_set *c, struct keylist *keys,
+                    atomic_t *journal_ref, struct bkey *replace_key)
+{
+       struct btree_insert_op op;
+       int ret = 0;
 
-               if (ret == -EAGAIN) {
-                       ret = 0;
-                       closure_sync(&op->cl);
-               } else if (ret) {
-                       struct bkey *k;
+       BUG_ON(current->bio_list);
+       BUG_ON(bch_keylist_empty(keys));
+
+       bch_btree_op_init(&op.op, 0);
+       op.keys         = keys;
+       op.journal_ref  = journal_ref;
+       op.replace_key  = replace_key;
+
+       while (!ret && !bch_keylist_empty(keys)) {
+               op.op.lock = 0;
+               ret = bch_btree_map_leaf_nodes(&op.op, c,
+                                              &START_KEY(keys->keys),
+                                              btree_insert_fn);
+       }
 
-                       pr_err("error %i trying to insert key for %s",
-                              ret, op_type(op));
+       if (ret) {
+               struct bkey *k;
 
-                       while ((k = bch_keylist_pop(&stack_keys) ?:
-                                   bch_keylist_pop(&op->keys)))
-                               bkey_put(c, k, 0);
-               }
-       }
+               pr_err("error %i", ret);
 
-       bch_keylist_free(&stack_keys);
+               while ((k = bch_keylist_pop(keys)))
+                       bkey_put(c, k);
+       } else if (op.op.insert_collision)
+               ret = -ESRCH;
 
-       if (op->journal)
-               atomic_dec_bug(op->journal);
-       op->journal = NULL;
        return ret;
 }
 
@@ -2141,132 +2283,81 @@ void bch_btree_set_root(struct btree *b)
        mutex_unlock(&b->c->bucket_lock);
 
        b->c->root = b;
-       __bkey_put(b->c, &b->key);
 
        bch_journal_meta(b->c, &cl);
        closure_sync(&cl);
 }
 
-/* Cache lookup */
+/* Map across nodes or keys */
 
-static int submit_partial_cache_miss(struct btree *b, struct btree_op *op,
-                                    struct bkey *k)
+static int bch_btree_map_nodes_recurse(struct btree *b, struct btree_op *op,
+                                      struct bkey *from,
+                                      btree_map_nodes_fn *fn, int flags)
 {
-       struct search *s = container_of(op, struct search, op);
-       struct bio *bio = &s->bio.bio;
-       int ret = 0;
+       int ret = MAP_CONTINUE;
+
+       if (b->level) {
+               struct bkey *k;
+               struct btree_iter iter;
 
-       while (!ret &&
-              !op->lookup_done) {
-               unsigned sectors = INT_MAX;
+               bch_btree_iter_init(b, &iter, from);
 
-               if (KEY_INODE(k) == op->inode) {
-                       if (KEY_START(k) <= bio->bi_sector)
-                               break;
+               while ((k = bch_btree_iter_next_filter(&iter, b,
+                                                      bch_ptr_bad))) {
+                       ret = btree(map_nodes_recurse, k, b,
+                                   op, from, fn, flags);
+                       from = NULL;
 
-                       sectors = min_t(uint64_t, sectors,
-                                       KEY_START(k) - bio->bi_sector);
+                       if (ret != MAP_CONTINUE)
+                               return ret;
                }
-
-               ret = s->d->cache_miss(b, s, bio, sectors);
        }
 
+       if (!b->level || flags == MAP_ALL_NODES)
+               ret = fn(op, b);
+
        return ret;
 }
 
-/*
- * Read from a single key, handling the initial cache miss if the key starts in
- * the middle of the bio
- */
-static int submit_partial_cache_hit(struct btree *b, struct btree_op *op,
-                                   struct bkey *k)
+int __bch_btree_map_nodes(struct btree_op *op, struct cache_set *c,
+                         struct bkey *from, btree_map_nodes_fn *fn, int flags)
 {
-       struct search *s = container_of(op, struct search, op);
-       struct bio *bio = &s->bio.bio;
-       unsigned ptr;
-       struct bio *n;
-
-       int ret = submit_partial_cache_miss(b, op, k);
-       if (ret || op->lookup_done)
-               return ret;
-
-       /* XXX: figure out best pointer - for multiple cache devices */
-       ptr = 0;
-
-       PTR_BUCKET(b->c, k, ptr)->prio = INITIAL_PRIO;
-
-       while (!op->lookup_done &&
-              KEY_INODE(k) == op->inode &&
-              bio->bi_sector < KEY_OFFSET(k)) {
-               struct bkey *bio_key;
-               sector_t sector = PTR_OFFSET(k, ptr) +
-                       (bio->bi_sector - KEY_START(k));
-               unsigned sectors = min_t(uint64_t, INT_MAX,
-                                        KEY_OFFSET(k) - bio->bi_sector);
-
-               n = bch_bio_split(bio, sectors, GFP_NOIO, s->d->bio_split);
-               if (n == bio)
-                       op->lookup_done = true;
-
-               bio_key = &container_of(n, struct bbio, bio)->key;
-
-               /*
-                * The bucket we're reading from might be reused while our bio
-                * is in flight, and we could then end up reading the wrong
-                * data.
-                *
-                * We guard against this by checking (in cache_read_endio()) if
-                * the pointer is stale again; if so, we treat it as an error
-                * and reread from the backing device (but we don't pass that
-                * error up anywhere).
-                */
-
-               bch_bkey_copy_single_ptr(bio_key, k, ptr);
-               SET_PTR_OFFSET(bio_key, 0, sector);
-
-               n->bi_end_io    = bch_cache_read_endio;
-               n->bi_private   = &s->cl;
-
-               __bch_submit_bbio(n, b->c);
-       }
-
-       return 0;
+       return btree_root(map_nodes_recurse, c, op, from, fn, flags);
 }
 
-int bch_btree_search_recurse(struct btree *b, struct btree_op *op)
+static int bch_btree_map_keys_recurse(struct btree *b, struct btree_op *op,
+                                     struct bkey *from, btree_map_keys_fn *fn,
+                                     int flags)
 {
-       struct search *s = container_of(op, struct search, op);
-       struct bio *bio = &s->bio.bio;
-
-       int ret = 0;
+       int ret = MAP_CONTINUE;
        struct bkey *k;
        struct btree_iter iter;
-       bch_btree_iter_init(b, &iter, &KEY(op->inode, bio->bi_sector, 0));
 
-       do {
-               k = bch_btree_iter_next_filter(&iter, b, bch_ptr_bad);
-               if (!k) {
-                       /*
-                        * b->key would be exactly what we want, except that
-                        * pointers to btree nodes have nonzero size - we
-                        * wouldn't go far enough
-                        */
+       bch_btree_iter_init(b, &iter, from);
 
-                       ret = submit_partial_cache_miss(b, op,
-                                       &KEY(KEY_INODE(&b->key),
-                                            KEY_OFFSET(&b->key), 0));
-                       break;
-               }
+       while ((k = bch_btree_iter_next_filter(&iter, b, bch_ptr_bad))) {
+               ret = !b->level
+                       ? fn(op, b, k)
+                       : btree(map_keys_recurse, k, b, op, from, fn, flags);
+               from = NULL;
+
+               if (ret != MAP_CONTINUE)
+                       return ret;
+       }
 
-               ret = b->level
-                       ? btree(search_recurse, k, b, op)
-                       : submit_partial_cache_hit(b, op, k);
-       } while (!ret &&
-                !op->lookup_done);
+       if (!b->level && (flags & MAP_END_KEY))
+               ret = fn(op, b, &KEY(KEY_INODE(&b->key),
+                                    KEY_OFFSET(&b->key), 0));
 
        return ret;
 }
 
+int bch_btree_map_keys(struct btree_op *op, struct cache_set *c,
+                      struct bkey *from, btree_map_keys_fn *fn, int flags)
+{
+       return btree_root(map_keys_recurse, c, op, from, fn, flags);
+}
+
 /* Keybuf code */
 
 static inline int keybuf_cmp(struct keybuf_key *l, struct keybuf_key *r)
@@ -2285,80 +2376,79 @@ static inline int keybuf_nonoverlapping_cmp(struct keybuf_key *l,
        return clamp_t(int64_t, bkey_cmp(&l->key, &r->key), -1, 1);
 }
 
-static int bch_btree_refill_keybuf(struct btree *b, struct btree_op *op,
-                                  struct keybuf *buf, struct bkey *end,
-                                  keybuf_pred_fn *pred)
-{
-       struct btree_iter iter;
-       bch_btree_iter_init(b, &iter, &buf->last_scanned);
-
-       while (!array_freelist_empty(&buf->freelist)) {
-               struct bkey *k = bch_btree_iter_next_filter(&iter, b,
-                                                           bch_ptr_bad);
-
-               if (!b->level) {
-                       if (!k) {
-                               buf->last_scanned = b->key;
-                               break;
-                       }
+struct refill {
+       struct btree_op op;
+       unsigned        nr_found;
+       struct keybuf   *buf;
+       struct bkey     *end;
+       keybuf_pred_fn  *pred;
+};
 
-                       buf->last_scanned = *k;
-                       if (bkey_cmp(&buf->last_scanned, end) >= 0)
-                               break;
+static int refill_keybuf_fn(struct btree_op *op, struct btree *b,
+                           struct bkey *k)
+{
+       struct refill *refill = container_of(op, struct refill, op);
+       struct keybuf *buf = refill->buf;
+       int ret = MAP_CONTINUE;
 
-                       if (pred(buf, k)) {
-                               struct keybuf_key *w;
+       if (bkey_cmp(k, refill->end) >= 0) {
+               ret = MAP_DONE;
+               goto out;
+       }
 
-                               spin_lock(&buf->lock);
+       if (!KEY_SIZE(k)) /* end key */
+               goto out;
 
-                               w = array_alloc(&buf->freelist);
+       if (refill->pred(buf, k)) {
+               struct keybuf_key *w;
 
-                               w->private = NULL;
-                               bkey_copy(&w->key, k);
+               spin_lock(&buf->lock);
 
-                               if (RB_INSERT(&buf->keys, w, node, keybuf_cmp))
-                                       array_free(&buf->freelist, w);
+               w = array_alloc(&buf->freelist);
+               if (!w) {
+                       spin_unlock(&buf->lock);
+                       return MAP_DONE;
+               }
 
-                               spin_unlock(&buf->lock);
-                       }
-               } else {
-                       if (!k)
-                               break;
+               w->private = NULL;
+               bkey_copy(&w->key, k);
 
-                       btree(refill_keybuf, k, b, op, buf, end, pred);
-                       /*
-                        * Might get an error here, but can't really do anything
-                        * and it'll get logged elsewhere. Just read what we
-                        * can.
-                        */
+               if (RB_INSERT(&buf->keys, w, node, keybuf_cmp))
+                       array_free(&buf->freelist, w);
+               else
+                       refill->nr_found++;
 
-                       if (bkey_cmp(&buf->last_scanned, end) >= 0)
-                               break;
+               if (array_freelist_empty(&buf->freelist))
+                       ret = MAP_DONE;
 
-                       cond_resched();
-               }
+               spin_unlock(&buf->lock);
        }
-
-       return 0;
+out:
+       buf->last_scanned = *k;
+       return ret;
 }
 
 void bch_refill_keybuf(struct cache_set *c, struct keybuf *buf,
                       struct bkey *end, keybuf_pred_fn *pred)
 {
        struct bkey start = buf->last_scanned;
-       struct btree_op op;
-       bch_btree_op_init_stack(&op);
+       struct refill refill;
 
        cond_resched();
 
-       btree_root(refill_keybuf, c, &op, buf, end, pred);
-       closure_sync(&op.cl);
+       bch_btree_op_init(&refill.op, -1);
+       refill.nr_found = 0;
+       refill.buf      = buf;
+       refill.end      = end;
+       refill.pred     = pred;
+
+       bch_btree_map_keys(&refill.op, c, &buf->last_scanned,
+                          refill_keybuf_fn, MAP_END_KEY);
 
-       pr_debug("found %s keys from %llu:%llu to %llu:%llu",
-                RB_EMPTY_ROOT(&buf->keys) ? "no" :
-                array_freelist_empty(&buf->freelist) ? "some" : "a few",
-                KEY_INODE(&start), KEY_OFFSET(&start),
-                KEY_INODE(&buf->last_scanned), KEY_OFFSET(&buf->last_scanned));
+       trace_bcache_keyscan(refill.nr_found,
+                            KEY_INODE(&start), KEY_OFFSET(&start),
+                            KEY_INODE(&buf->last_scanned),
+                            KEY_OFFSET(&buf->last_scanned));
 
        spin_lock(&buf->lock);
 
@@ -2436,9 +2526,9 @@ struct keybuf_key *bch_keybuf_next(struct keybuf *buf)
 }
 
 struct keybuf_key *bch_keybuf_next_rescan(struct cache_set *c,
-                                            struct keybuf *buf,
-                                            struct bkey *end,
-                                            keybuf_pred_fn *pred)
+                                         struct keybuf *buf,
+                                         struct bkey *end,
+                                         keybuf_pred_fn *pred)
 {
        struct keybuf_key *ret;
 
@@ -2471,14 +2561,12 @@ void bch_btree_exit(void)
 {
        if (btree_io_wq)
                destroy_workqueue(btree_io_wq);
-       if (bch_gc_wq)
-               destroy_workqueue(bch_gc_wq);
 }
 
 int __init bch_btree_init(void)
 {
-       if (!(bch_gc_wq = create_singlethread_workqueue("bch_btree_gc")) ||
-           !(btree_io_wq = create_singlethread_workqueue("bch_btree_io")))
+       btree_io_wq = create_singlethread_workqueue("bch_btree_io");
+       if (!btree_io_wq)
                return -ENOMEM;
 
        return 0;
index 3333d37..767e755 100644 (file)
@@ -125,6 +125,7 @@ struct btree {
        unsigned long           seq;
        struct rw_semaphore     lock;
        struct cache_set        *c;
+       struct btree            *parent;
 
        unsigned long           flags;
        uint16_t                written;        /* would be nice to kill */
@@ -200,12 +201,7 @@ static inline bool bkey_written(struct btree *b, struct bkey *k)
 
 static inline void set_gc_sectors(struct cache_set *c)
 {
-       atomic_set(&c->sectors_to_gc, c->sb.bucket_size * c->nbuckets / 8);
-}
-
-static inline bool bch_ptr_invalid(struct btree *b, const struct bkey *k)
-{
-       return __bch_ptr_invalid(b->c, b->level, k);
+       atomic_set(&c->sectors_to_gc, c->sb.bucket_size * c->nbuckets / 16);
 }
 
 static inline struct bkey *bch_btree_iter_init(struct btree *b,
@@ -215,6 +211,16 @@ static inline struct bkey *bch_btree_iter_init(struct btree *b,
        return __bch_btree_iter_init(b, iter, search, b->sets);
 }
 
+static inline bool bch_ptr_invalid(struct btree *b, const struct bkey *k)
+{
+       if (b->level)
+               return bch_btree_ptr_invalid(b->c, k);
+       else
+               return bch_extent_ptr_invalid(b->c, k);
+}
+
+void bkey_put(struct cache_set *c, struct bkey *k);
+
 /* Looping macros */
 
 #define for_each_cached_btree(b, c, iter)                              \
@@ -234,51 +240,17 @@ static inline struct bkey *bch_btree_iter_init(struct btree *b,
 /* Recursing down the btree */
 
 struct btree_op {
-       struct closure          cl;
-       struct cache_set        *c;
-
-       /* Journal entry we have a refcount on */
-       atomic_t                *journal;
-
-       /* Bio to be inserted into the cache */
-       struct bio              *cache_bio;
-
-       unsigned                inode;
-
-       uint16_t                write_prio;
-
        /* Btree level at which we start taking write locks */
        short                   lock;
 
-       /* Btree insertion type */
-       enum {
-               BTREE_INSERT,
-               BTREE_REPLACE
-       } type:8;
-
-       unsigned                csum:1;
-       unsigned                skip:1;
-       unsigned                flush_journal:1;
-
-       unsigned                insert_data_done:1;
-       unsigned                lookup_done:1;
        unsigned                insert_collision:1;
-
-       /* Anything after this point won't get zeroed in do_bio_hook() */
-
-       /* Keys to be inserted */
-       struct keylist          keys;
-       BKEY_PADDED(replace);
 };
 
-enum {
-       BTREE_INSERT_STATUS_INSERT,
-       BTREE_INSERT_STATUS_BACK_MERGE,
-       BTREE_INSERT_STATUS_OVERWROTE,
-       BTREE_INSERT_STATUS_FRONT_MERGE,
-};
-
-void bch_btree_op_init_stack(struct btree_op *);
+static inline void bch_btree_op_init(struct btree_op *op, int write_lock_level)
+{
+       memset(op, 0, sizeof(struct btree_op));
+       op->lock = write_lock_level;
+}
 
 static inline void rw_lock(bool w, struct btree *b, int level)
 {
@@ -290,108 +262,71 @@ static inline void rw_lock(bool w, struct btree *b, int level)
 
 static inline void rw_unlock(bool w, struct btree *b)
 {
-#ifdef CONFIG_BCACHE_EDEBUG
-       unsigned i;
-
-       if (w && b->key.ptr[0])
-               for (i = 0; i <= b->nsets; i++)
-                       bch_check_key_order(b, b->sets[i].data);
-#endif
-
        if (w)
                b->seq++;
        (w ? up_write : up_read)(&b->lock);
 }
 
-#define insert_lock(s, b)      ((b)->level <= (s)->lock)
+void bch_btree_node_read(struct btree *);
+void bch_btree_node_write(struct btree *, struct closure *);
 
-/*
- * These macros are for recursing down the btree - they handle the details of
- * locking and looking up nodes in the cache for you. They're best treated as
- * mere syntax when reading code that uses them.
- *
- * op->lock determines whether we take a read or a write lock at a given depth.
- * If you've got a read lock and find that you need a write lock (i.e. you're
- * going to have to split), set op->lock and return -EINTR; btree_root() will
- * call you again and you'll have the correct lock.
- */
+void bch_btree_set_root(struct btree *);
+struct btree *bch_btree_node_alloc(struct cache_set *, int, bool);
+struct btree *bch_btree_node_get(struct cache_set *, struct bkey *, int, bool);
 
-/**
- * btree - recurse down the btree on a specified key
- * @fn:                function to call, which will be passed the child node
- * @key:       key to recurse on
- * @b:         parent btree node
- * @op:                pointer to struct btree_op
- */
-#define btree(fn, key, b, op, ...)                                     \
-({                                                                     \
-       int _r, l = (b)->level - 1;                                     \
-       bool _w = l <= (op)->lock;                                      \
-       struct btree *_b = bch_btree_node_get((b)->c, key, l, op);      \
-       if (!IS_ERR(_b)) {                                              \
-               _r = bch_btree_ ## fn(_b, op, ##__VA_ARGS__);           \
-               rw_unlock(_w, _b);                                      \
-       } else                                                          \
-               _r = PTR_ERR(_b);                                       \
-       _r;                                                             \
-})
-
-/**
- * btree_root - call a function on the root of the btree
- * @fn:                function to call, which will be passed the child node
- * @c:         cache set
- * @op:                pointer to struct btree_op
- */
-#define btree_root(fn, c, op, ...)                                     \
-({                                                                     \
-       int _r = -EINTR;                                                \
-       do {                                                            \
-               struct btree *_b = (c)->root;                           \
-               bool _w = insert_lock(op, _b);                          \
-               rw_lock(_w, _b, _b->level);                             \
-               if (_b == (c)->root &&                                  \
-                   _w == insert_lock(op, _b))                          \
-                       _r = bch_btree_ ## fn(_b, op, ##__VA_ARGS__);   \
-               rw_unlock(_w, _b);                                      \
-               bch_cannibalize_unlock(c, &(op)->cl);           \
-       } while (_r == -EINTR);                                         \
-                                                                       \
-       _r;                                                             \
-})
+int bch_btree_insert_check_key(struct btree *, struct btree_op *,
+                              struct bkey *);
+int bch_btree_insert(struct cache_set *, struct keylist *,
+                    atomic_t *, struct bkey *);
+
+int bch_gc_thread_start(struct cache_set *);
+size_t bch_btree_gc_finish(struct cache_set *);
+void bch_moving_gc(struct cache_set *);
+int bch_btree_check(struct cache_set *);
+uint8_t __bch_btree_mark_key(struct cache_set *, int, struct bkey *);
 
-static inline bool should_split(struct btree *b)
+static inline void wake_up_gc(struct cache_set *c)
 {
-       struct bset *i = write_block(b);
-       return b->written >= btree_blocks(b) ||
-               (i->seq == b->sets[0].data->seq &&
-                b->written + __set_blocks(i, i->keys + 15, b->c)
-                > btree_blocks(b));
+       if (c->gc_thread)
+               wake_up_process(c->gc_thread);
 }
 
-void bch_btree_node_read(struct btree *);
-void bch_btree_node_write(struct btree *, struct closure *);
+#define MAP_DONE       0
+#define MAP_CONTINUE   1
 
-void bch_cannibalize_unlock(struct cache_set *, struct closure *);
-void bch_btree_set_root(struct btree *);
-struct btree *bch_btree_node_alloc(struct cache_set *, int, struct closure *);
-struct btree *bch_btree_node_get(struct cache_set *, struct bkey *,
-                               int, struct btree_op *);
+#define MAP_ALL_NODES  0
+#define MAP_LEAF_NODES 1
 
-bool bch_btree_insert_check_key(struct btree *, struct btree_op *,
-                                  struct bio *);
-int bch_btree_insert(struct btree_op *, struct cache_set *);
+#define MAP_END_KEY    1
 
-int bch_btree_search_recurse(struct btree *, struct btree_op *);
+typedef int (btree_map_nodes_fn)(struct btree_op *, struct btree *);
+int __bch_btree_map_nodes(struct btree_op *, struct cache_set *,
+                         struct bkey *, btree_map_nodes_fn *, int);
 
-void bch_queue_gc(struct cache_set *);
-size_t bch_btree_gc_finish(struct cache_set *);
-void bch_moving_gc(struct closure *);
-int bch_btree_check(struct cache_set *, struct btree_op *);
-uint8_t __bch_btree_mark_key(struct cache_set *, int, struct bkey *);
+static inline int bch_btree_map_nodes(struct btree_op *op, struct cache_set *c,
+                                     struct bkey *from, btree_map_nodes_fn *fn)
+{
+       return __bch_btree_map_nodes(op, c, from, fn, MAP_ALL_NODES);
+}
+
+static inline int bch_btree_map_leaf_nodes(struct btree_op *op,
+                                          struct cache_set *c,
+                                          struct bkey *from,
+                                          btree_map_nodes_fn *fn)
+{
+       return __bch_btree_map_nodes(op, c, from, fn, MAP_LEAF_NODES);
+}
+
+typedef int (btree_map_keys_fn)(struct btree_op *, struct btree *,
+                               struct bkey *);
+int bch_btree_map_keys(struct btree_op *, struct cache_set *,
+                      struct bkey *, btree_map_keys_fn *, int);
+
+typedef bool (keybuf_pred_fn)(struct keybuf *, struct bkey *);
 
 void bch_keybuf_init(struct keybuf *);
-void bch_refill_keybuf(struct cache_set *, struct keybuf *, struct bkey *,
-                      keybuf_pred_fn *);
+void bch_refill_keybuf(struct cache_set *, struct keybuf *,
+                      struct bkey *, keybuf_pred_fn *);
 bool bch_keybuf_check_overlapping(struct keybuf *, struct bkey *,
                                  struct bkey *);
 void bch_keybuf_del(struct keybuf *, struct keybuf_key *);
index 9aba201..dfff241 100644 (file)
 
 #include "closure.h"
 
-void closure_queue(struct closure *cl)
-{
-       struct workqueue_struct *wq = cl->wq;
-       if (wq) {
-               INIT_WORK(&cl->work, cl->work.func);
-               BUG_ON(!queue_work(wq, &cl->work));
-       } else
-               cl->fn(cl);
-}
-EXPORT_SYMBOL_GPL(closure_queue);
-
 #define CL_FIELD(type, field)                                  \
        case TYPE_ ## type:                                     \
        return &container_of(cl, struct type, cl)->field
@@ -30,17 +19,6 @@ static struct closure_waitlist *closure_waitlist(struct closure *cl)
 {
        switch (cl->type) {
                CL_FIELD(closure_with_waitlist, wait);
-               CL_FIELD(closure_with_waitlist_and_timer, wait);
-       default:
-               return NULL;
-       }
-}
-
-static struct timer_list *closure_timer(struct closure *cl)
-{
-       switch (cl->type) {
-               CL_FIELD(closure_with_timer, timer);
-               CL_FIELD(closure_with_waitlist_and_timer, timer);
        default:
                return NULL;
        }
@@ -51,7 +29,7 @@ static inline void closure_put_after_sub(struct closure *cl, int flags)
        int r = flags & CLOSURE_REMAINING_MASK;
 
        BUG_ON(flags & CLOSURE_GUARD_MASK);
-       BUG_ON(!r && (flags & ~(CLOSURE_DESTRUCTOR|CLOSURE_BLOCKING)));
+       BUG_ON(!r && (flags & ~CLOSURE_DESTRUCTOR));
 
        /* Must deliver precisely one wakeup */
        if (r == 1 && (flags & CLOSURE_SLEEPING))
@@ -59,7 +37,6 @@ static inline void closure_put_after_sub(struct closure *cl, int flags)
 
        if (!r) {
                if (cl->fn && !(flags & CLOSURE_DESTRUCTOR)) {
-                       /* CLOSURE_BLOCKING might be set - clear it */
                        atomic_set(&cl->remaining,
                                   CLOSURE_REMAINING_INITIALIZER);
                        closure_queue(cl);
@@ -90,13 +67,13 @@ void closure_sub(struct closure *cl, int v)
 {
        closure_put_after_sub(cl, atomic_sub_return(v, &cl->remaining));
 }
-EXPORT_SYMBOL_GPL(closure_sub);
+EXPORT_SYMBOL(closure_sub);
 
 void closure_put(struct closure *cl)
 {
        closure_put_after_sub(cl, atomic_dec_return(&cl->remaining));
 }
-EXPORT_SYMBOL_GPL(closure_put);
+EXPORT_SYMBOL(closure_put);
 
 static void set_waiting(struct closure *cl, unsigned long f)
 {
@@ -133,7 +110,7 @@ void __closure_wake_up(struct closure_waitlist *wait_list)
                closure_sub(cl, CLOSURE_WAITING + 1);
        }
 }
-EXPORT_SYMBOL_GPL(__closure_wake_up);
+EXPORT_SYMBOL(__closure_wake_up);
 
 bool closure_wait(struct closure_waitlist *list, struct closure *cl)
 {
@@ -146,7 +123,7 @@ bool closure_wait(struct closure_waitlist *list, struct closure *cl)
 
        return true;
 }
-EXPORT_SYMBOL_GPL(closure_wait);
+EXPORT_SYMBOL(closure_wait);
 
 /**
  * closure_sync() - sleep until a closure a closure has nothing left to wait on
@@ -169,7 +146,7 @@ void closure_sync(struct closure *cl)
 
        __closure_end_sleep(cl);
 }
-EXPORT_SYMBOL_GPL(closure_sync);
+EXPORT_SYMBOL(closure_sync);
 
 /**
  * closure_trylock() - try to acquire the closure, without waiting
@@ -183,17 +160,17 @@ bool closure_trylock(struct closure *cl, struct closure *parent)
                           CLOSURE_REMAINING_INITIALIZER) != -1)
                return false;
 
-       closure_set_ret_ip(cl);
-
        smp_mb();
+
        cl->parent = parent;
        if (parent)
                closure_get(parent);
 
+       closure_set_ret_ip(cl);
        closure_debug_create(cl);
        return true;
 }
-EXPORT_SYMBOL_GPL(closure_trylock);
+EXPORT_SYMBOL(closure_trylock);
 
 void __closure_lock(struct closure *cl, struct closure *parent,
                    struct closure_waitlist *wait_list)
@@ -205,57 +182,11 @@ void __closure_lock(struct closure *cl, struct closure *parent,
                if (closure_trylock(cl, parent))
                        return;
 
-               closure_wait_event_sync(wait_list, &wait,
-                                       atomic_read(&cl->remaining) == -1);
+               closure_wait_event(wait_list, &wait,
+                                  atomic_read(&cl->remaining) == -1);
        }
 }
-EXPORT_SYMBOL_GPL(__closure_lock);
-
-static void closure_delay_timer_fn(unsigned long data)
-{
-       struct closure *cl = (struct closure *) data;
-       closure_sub(cl, CLOSURE_TIMER + 1);
-}
-
-void do_closure_timer_init(struct closure *cl)
-{
-       struct timer_list *timer = closure_timer(cl);
-
-       init_timer(timer);
-       timer->data     = (unsigned long) cl;
-       timer->function = closure_delay_timer_fn;
-}
-EXPORT_SYMBOL_GPL(do_closure_timer_init);
-
-bool __closure_delay(struct closure *cl, unsigned long delay,
-                    struct timer_list *timer)
-{
-       if (atomic_read(&cl->remaining) & CLOSURE_TIMER)
-               return false;
-
-       BUG_ON(timer_pending(timer));
-
-       timer->expires  = jiffies + delay;
-
-       atomic_add(CLOSURE_TIMER + 1, &cl->remaining);
-       add_timer(timer);
-       return true;
-}
-EXPORT_SYMBOL_GPL(__closure_delay);
-
-void __closure_flush(struct closure *cl, struct timer_list *timer)
-{
-       if (del_timer(timer))
-               closure_sub(cl, CLOSURE_TIMER + 1);
-}
-EXPORT_SYMBOL_GPL(__closure_flush);
-
-void __closure_flush_sync(struct closure *cl, struct timer_list *timer)
-{
-       if (del_timer_sync(timer))
-               closure_sub(cl, CLOSURE_TIMER + 1);
-}
-EXPORT_SYMBOL_GPL(__closure_flush_sync);
+EXPORT_SYMBOL(__closure_lock);
 
 #ifdef CONFIG_BCACHE_CLOSURES_DEBUG
 
@@ -273,7 +204,7 @@ void closure_debug_create(struct closure *cl)
        list_add(&cl->all, &closure_list);
        spin_unlock_irqrestore(&closure_list_lock, flags);
 }
-EXPORT_SYMBOL_GPL(closure_debug_create);
+EXPORT_SYMBOL(closure_debug_create);
 
 void closure_debug_destroy(struct closure *cl)
 {
@@ -286,7 +217,7 @@ void closure_debug_destroy(struct closure *cl)
        list_del(&cl->all);
        spin_unlock_irqrestore(&closure_list_lock, flags);
 }
-EXPORT_SYMBOL_GPL(closure_debug_destroy);
+EXPORT_SYMBOL(closure_debug_destroy);
 
 static struct dentry *debug;
 
@@ -304,14 +235,12 @@ static int debug_seq_show(struct seq_file *f, void *data)
                           cl, (void *) cl->ip, cl->fn, cl->parent,
                           r & CLOSURE_REMAINING_MASK);
 
-               seq_printf(f, "%s%s%s%s%s%s\n",
+               seq_printf(f, "%s%s%s%s\n",
                           test_bit(WORK_STRUCT_PENDING,
                                    work_data_bits(&cl->work)) ? "Q" : "",
                           r & CLOSURE_RUNNING  ? "R" : "",
-                          r & CLOSURE_BLOCKING ? "B" : "",
                           r & CLOSURE_STACK    ? "S" : "",
-                          r & CLOSURE_SLEEPING ? "Sl" : "",
-                          r & CLOSURE_TIMER    ? "T" : "");
+                          r & CLOSURE_SLEEPING ? "Sl" : "");
 
                if (r & CLOSURE_WAITING)
                        seq_printf(f, " W %pF\n",
index 0003992..9762f1b 100644 (file)
  * delayed_work embeds a work item and a timer_list. The important thing is, use
  * it exactly like you would a regular closure and closure_put() will magically
  * handle everything for you.
- *
- * We've got closures that embed timers, too. They're called, appropriately
- * enough:
- * struct closure_with_timer;
- *
- * This gives you access to closure_delay(). It takes a refcount for a specified
- * number of jiffies - you could then call closure_sync() (for a slightly
- * convoluted version of msleep()) or continue_at() - which gives you the same
- * effect as using a delayed work item, except you can reuse the work_struct
- * already embedded in struct closure.
- *
- * Lastly, there's struct closure_with_waitlist_and_timer. It does what you
- * probably expect, if you happen to need the features of both. (You don't
- * really want to know how all this is implemented, but if I've done my job
- * right you shouldn't have to care).
  */
 
 struct closure;
@@ -182,16 +167,11 @@ struct closure_waitlist {
 enum closure_type {
        TYPE_closure                            = 0,
        TYPE_closure_with_waitlist              = 1,
-       TYPE_closure_with_timer                 = 2,
-       TYPE_closure_with_waitlist_and_timer    = 3,
-       MAX_CLOSURE_TYPE                        = 3,
+       MAX_CLOSURE_TYPE                        = 1,
 };
 
 enum closure_state {
        /*
-        * CLOSURE_BLOCKING: Causes closure_wait_event() to block, instead of
-        * waiting asynchronously
-        *
         * CLOSURE_WAITING: Set iff the closure is on a waitlist. Must be set by
         * the thread that owns the closure, and cleared by the thread that's
         * waking up the closure.
@@ -200,10 +180,6 @@ enum closure_state {
         * - indicates that cl->task is valid and closure_put() may wake it up.
         * Only set or cleared by the thread that owns the closure.
         *
-        * CLOSURE_TIMER: Analagous to CLOSURE_WAITING, indicates that a closure
-        * has an outstanding timer. Must be set by the thread that owns the
-        * closure, and cleared by the timer function when the timer goes off.
-        *
         * The rest are for debugging and don't affect behaviour:
         *
         * CLOSURE_RUNNING: Set when a closure is running (i.e. by
@@ -218,19 +194,17 @@ enum closure_state {
         * closure with this flag set
         */
 
-       CLOSURE_BITS_START      = (1 << 19),
-       CLOSURE_DESTRUCTOR      = (1 << 19),
-       CLOSURE_BLOCKING        = (1 << 21),
-       CLOSURE_WAITING         = (1 << 23),
-       CLOSURE_SLEEPING        = (1 << 25),
-       CLOSURE_TIMER           = (1 << 27),
+       CLOSURE_BITS_START      = (1 << 23),
+       CLOSURE_DESTRUCTOR      = (1 << 23),
+       CLOSURE_WAITING         = (1 << 25),
+       CLOSURE_SLEEPING        = (1 << 27),
        CLOSURE_RUNNING         = (1 << 29),
        CLOSURE_STACK           = (1 << 31),
 };
 
 #define CLOSURE_GUARD_MASK                                     \
-       ((CLOSURE_DESTRUCTOR|CLOSURE_BLOCKING|CLOSURE_WAITING|  \
-         CLOSURE_SLEEPING|CLOSURE_TIMER|CLOSURE_RUNNING|CLOSURE_STACK) << 1)
+       ((CLOSURE_DESTRUCTOR|CLOSURE_WAITING|CLOSURE_SLEEPING|  \
+         CLOSURE_RUNNING|CLOSURE_STACK) << 1)
 
 #define CLOSURE_REMAINING_MASK         (CLOSURE_BITS_START - 1)
 #define CLOSURE_REMAINING_INITIALIZER  (1|CLOSURE_RUNNING)
@@ -268,17 +242,6 @@ struct closure_with_waitlist {
        struct closure_waitlist wait;
 };
 
-struct closure_with_timer {
-       struct closure          cl;
-       struct timer_list       timer;
-};
-
-struct closure_with_waitlist_and_timer {
-       struct closure          cl;
-       struct closure_waitlist wait;
-       struct timer_list       timer;
-};
-
 extern unsigned invalid_closure_type(void);
 
 #define __CLOSURE_TYPE(cl, _t)                                         \
@@ -289,14 +252,11 @@ extern unsigned invalid_closure_type(void);
 (                                                                      \
        __CLOSURE_TYPE(cl, closure)                                     \
        __CLOSURE_TYPE(cl, closure_with_waitlist)                       \
-       __CLOSURE_TYPE(cl, closure_with_timer)                          \
-       __CLOSURE_TYPE(cl, closure_with_waitlist_and_timer)             \
        invalid_closure_type()                                          \
 )
 
 void closure_sub(struct closure *cl, int v);
 void closure_put(struct closure *cl);
-void closure_queue(struct closure *cl);
 void __closure_wake_up(struct closure_waitlist *list);
 bool closure_wait(struct closure_waitlist *list, struct closure *cl);
 void closure_sync(struct closure *cl);
@@ -305,12 +265,6 @@ bool closure_trylock(struct closure *cl, struct closure *parent);
 void __closure_lock(struct closure *cl, struct closure *parent,
                    struct closure_waitlist *wait_list);
 
-void do_closure_timer_init(struct closure *cl);
-bool __closure_delay(struct closure *cl, unsigned long delay,
-                    struct timer_list *timer);
-void __closure_flush(struct closure *cl, struct timer_list *timer);
-void __closure_flush_sync(struct closure *cl, struct timer_list *timer);
-
 #ifdef CONFIG_BCACHE_CLOSURES_DEBUG
 
 void closure_debug_init(void);
@@ -354,11 +308,6 @@ static inline void closure_set_stopped(struct closure *cl)
        atomic_sub(CLOSURE_RUNNING, &cl->remaining);
 }
 
-static inline bool closure_is_stopped(struct closure *cl)
-{
-       return !(atomic_read(&cl->remaining) & CLOSURE_RUNNING);
-}
-
 static inline bool closure_is_unlocked(struct closure *cl)
 {
        return atomic_read(&cl->remaining) == -1;
@@ -367,14 +316,6 @@ static inline bool closure_is_unlocked(struct closure *cl)
 static inline void do_closure_init(struct closure *cl, struct closure *parent,
                                   bool running)
 {
-       switch (cl->type) {
-       case TYPE_closure_with_timer:
-       case TYPE_closure_with_waitlist_and_timer:
-               do_closure_timer_init(cl);
-       default:
-               break;
-       }
-
        cl->parent = parent;
        if (parent)
                closure_get(parent);
@@ -429,8 +370,7 @@ do {                                                                \
 static inline void closure_init_stack(struct closure *cl)
 {
        memset(cl, 0, sizeof(struct closure));
-       atomic_set(&cl->remaining, CLOSURE_REMAINING_INITIALIZER|
-                  CLOSURE_BLOCKING|CLOSURE_STACK);
+       atomic_set(&cl->remaining, CLOSURE_REMAINING_INITIALIZER|CLOSURE_STACK);
 }
 
 /**
@@ -461,24 +401,6 @@ do {                                                               \
 #define closure_lock(cl, parent)                               \
        __closure_lock(__to_internal_closure(cl), parent, &(cl)->wait)
 
-/**
- * closure_delay() - delay some number of jiffies
- * @cl:                the closure that will sleep
- * @delay:     the delay in jiffies
- *
- * Takes a refcount on @cl which will be released after @delay jiffies; this may
- * be used to have a function run after a delay with continue_at(), or
- * closure_sync() may be used for a convoluted version of msleep().
- */
-#define closure_delay(cl, delay)                       \
-       __closure_delay(__to_internal_closure(cl), delay, &(cl)->timer)
-
-#define closure_flush(cl)                              \
-       __closure_flush(__to_internal_closure(cl), &(cl)->timer)
-
-#define closure_flush_sync(cl)                         \
-       __closure_flush_sync(__to_internal_closure(cl), &(cl)->timer)
-
 static inline void __closure_end_sleep(struct closure *cl)
 {
        __set_current_state(TASK_RUNNING);
@@ -498,40 +420,6 @@ static inline void __closure_start_sleep(struct closure *cl)
 }
 
 /**
- * closure_blocking() - returns true if the closure is in blocking mode.
- *
- * If a closure is in blocking mode, closure_wait_event() will sleep until the
- * condition is true instead of waiting asynchronously.
- */
-static inline bool closure_blocking(struct closure *cl)
-{
-       return atomic_read(&cl->remaining) & CLOSURE_BLOCKING;
-}
-
-/**
- * set_closure_blocking() - put a closure in blocking mode.
- *
- * If a closure is in blocking mode, closure_wait_event() will sleep until the
- * condition is true instead of waiting asynchronously.
- *
- * Not thread safe - can only be called by the thread running the closure.
- */
-static inline void set_closure_blocking(struct closure *cl)
-{
-       if (!closure_blocking(cl))
-               atomic_add(CLOSURE_BLOCKING, &cl->remaining);
-}
-
-/*
- * Not thread safe - can only be called by the thread running the closure.
- */
-static inline void clear_closure_blocking(struct closure *cl)
-{
-       if (closure_blocking(cl))
-               atomic_sub(CLOSURE_BLOCKING, &cl->remaining);
-}
-
-/**
  * closure_wake_up() - wake up all closures on a wait list.
  */
 static inline void closure_wake_up(struct closure_waitlist *list)
@@ -561,63 +449,36 @@ static inline void closure_wake_up(struct closure_waitlist *list)
  * refcount on our closure. If this was a stack allocated closure, that would be
  * bad.
  */
-#define __closure_wait_event(list, cl, condition, _block)              \
+#define closure_wait_event(list, cl, condition)                                \
 ({                                                                     \
-       bool block = _block;                                            \
        typeof(condition) ret;                                          \
                                                                        \
        while (1) {                                                     \
                ret = (condition);                                      \
                if (ret) {                                              \
                        __closure_wake_up(list);                        \
-                       if (block)                                      \
-                               closure_sync(cl);                       \
-                                                                       \
+                       closure_sync(cl);                               \
                        break;                                          \
                }                                                       \
                                                                        \
-               if (block)                                              \
-                       __closure_start_sleep(cl);                      \
-                                                                       \
-               if (!closure_wait(list, cl)) {                          \
-                       if (!block)                                     \
-                               break;                                  \
+               __closure_start_sleep(cl);                              \
                                                                        \
+               if (!closure_wait(list, cl))                            \
                        schedule();                                     \
-               }                                                       \
        }                                                               \
                                                                        \
        ret;                                                            \
 })
 
-/**
- * closure_wait_event() - wait on a condition, synchronously or asynchronously.
- * @list:      the wait list to wait on
- * @cl:                the closure that is doing the waiting
- * @condition: a C expression for the event to wait for
- *
- * If the closure is in blocking mode, sleeps until the @condition evaluates to
- * true - exactly like wait_event().
- *
- * If the closure is not in blocking mode, waits asynchronously; if the
- * condition is currently false the @cl is put onto @list and returns. @list
- * owns a refcount on @cl; closure_sync() or continue_at() may be used later to
- * wait for another thread to wake up @list, which drops the refcount on @cl.
- *
- * Returns the value of @condition; @cl will be on @list iff @condition was
- * false.
- *
- * closure_wake_up(@list) must be called after changing any variable that could
- * cause @condition to become true.
- */
-#define closure_wait_event(list, cl, condition)                                \
-       __closure_wait_event(list, cl, condition, closure_blocking(cl))
-
-#define closure_wait_event_async(list, cl, condition)                  \
-       __closure_wait_event(list, cl, condition, false)
-
-#define closure_wait_event_sync(list, cl, condition)                   \
-       __closure_wait_event(list, cl, condition, true)
+static inline void closure_queue(struct closure *cl)
+{
+       struct workqueue_struct *wq = cl->wq;
+       if (wq) {
+               INIT_WORK(&cl->work, cl->work.func);
+               BUG_ON(!queue_work(wq, &cl->work));
+       } else
+               cl->fn(cl);
+}
 
 static inline void set_closure_fn(struct closure *cl, closure_fn *fn,
                                  struct workqueue_struct *wq)
@@ -642,7 +503,7 @@ do {                                                                        \
 #define continue_at_nobarrier(_cl, _fn, _wq)                           \
 do {                                                                   \
        set_closure_fn(_cl, _fn, _wq);                                  \
-       closure_queue(cl);                                              \
+       closure_queue(_cl);                                             \
        return;                                                         \
 } while (0)
 
index 88e6411..264fcfb 100644 (file)
@@ -8,7 +8,6 @@
 #include "bcache.h"
 #include "btree.h"
 #include "debug.h"
-#include "request.h"
 
 #include <linux/console.h>
 #include <linux/debugfs.h>
@@ -77,29 +76,17 @@ int bch_bkey_to_text(char *buf, size_t size, const struct bkey *k)
        return out - buf;
 }
 
-int bch_btree_to_text(char *buf, size_t size, const struct btree *b)
-{
-       return scnprintf(buf, size, "%zu level %i/%i",
-                        PTR_BUCKET_NR(b->c, &b->key, 0),
-                        b->level, b->c->root ? b->c->root->level : -1);
-}
-
-#if defined(CONFIG_BCACHE_DEBUG) || defined(CONFIG_BCACHE_EDEBUG)
-
-static bool skipped_backwards(struct btree *b, struct bkey *k)
-{
-       return bkey_cmp(k, (!b->level)
-                       ? &START_KEY(bkey_next(k))
-                       : bkey_next(k)) > 0;
-}
+#ifdef CONFIG_BCACHE_DEBUG
 
 static void dump_bset(struct btree *b, struct bset *i)
 {
-       struct bkey *k;
+       struct bkey *k, *next;
        unsigned j;
        char buf[80];
 
-       for (k = i->start; k < end(i); k = bkey_next(k)) {
+       for (k = i->start; k < end(i); k = next) {
+               next = bkey_next(k);
+
                bch_bkey_to_text(buf, sizeof(buf), k);
                printk(KERN_ERR "block %zu key %zi/%u: %s", index(i, b),
                       (uint64_t *) k - i->d, i->keys, buf);
@@ -115,15 +102,21 @@ static void dump_bset(struct btree *b, struct bset *i)
 
                printk(" %s\n", bch_ptr_status(b->c, k));
 
-               if (bkey_next(k) < end(i) &&
-                   skipped_backwards(b, k))
+               if (next < end(i) &&
+                   bkey_cmp(k, !b->level ? &START_KEY(next) : next) > 0)
                        printk(KERN_ERR "Key skipped backwards\n");
        }
 }
 
-#endif
+static void bch_dump_bucket(struct btree *b)
+{
+       unsigned i;
 
-#ifdef CONFIG_BCACHE_DEBUG
+       console_lock();
+       for (i = 0; i <= b->nsets; i++)
+               dump_bset(b, b->sets[i].data);
+       console_unlock();
+}
 
 void bch_btree_verify(struct btree *b, struct bset *new)
 {
@@ -176,66 +169,44 @@ void bch_btree_verify(struct btree *b, struct bset *new)
        mutex_unlock(&b->c->verify_lock);
 }
 
-static void data_verify_endio(struct bio *bio, int error)
-{
-       struct closure *cl = bio->bi_private;
-       closure_put(cl);
-}
-
-void bch_data_verify(struct search *s)
+void bch_data_verify(struct cached_dev *dc, struct bio *bio)
 {
        char name[BDEVNAME_SIZE];
-       struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
-       struct closure *cl = &s->cl;
        struct bio *check;
        struct bio_vec *bv;
        int i;
 
-       if (!s->unaligned_bvec)
-               bio_for_each_segment(bv, s->orig_bio, i)
-                       bv->bv_offset = 0, bv->bv_len = PAGE_SIZE;
-
-       check = bio_clone(s->orig_bio, GFP_NOIO);
+       check = bio_clone(bio, GFP_NOIO);
        if (!check)
                return;
 
        if (bio_alloc_pages(check, GFP_NOIO))
                goto out_put;
 
-       check->bi_rw            = READ_SYNC;
-       check->bi_private       = cl;
-       check->bi_end_io        = data_verify_endio;
-
-       closure_bio_submit(check, cl, &dc->disk);
-       closure_sync(cl);
+       submit_bio_wait(READ_SYNC, check);
 
-       bio_for_each_segment(bv, s->orig_bio, i) {
-               void *p1 = kmap(bv->bv_page);
-               void *p2 = kmap(check->bi_io_vec[i].bv_page);
+       bio_for_each_segment(bv, bio, i) {
+               void *p1 = kmap_atomic(bv->bv_page);
+               void *p2 = page_address(check->bi_io_vec[i].bv_page);
 
-               if (memcmp(p1 + bv->bv_offset,
-                          p2 + bv->bv_offset,
-                          bv->bv_len))
-                       printk(KERN_ERR
-                              "bcache (%s): verify failed at sector %llu\n",
-                              bdevname(dc->bdev, name),
-                              (uint64_t) s->orig_bio->bi_sector);
+               cache_set_err_on(memcmp(p1 + bv->bv_offset,
+                                       p2 + bv->bv_offset,
+                                       bv->bv_len),
+                                dc->disk.c,
+                                "verify failed at dev %s sector %llu",
+                                bdevname(dc->bdev, name),
+                                (uint64_t) bio->bi_sector);
 
-               kunmap(bv->bv_page);
-               kunmap(check->bi_io_vec[i].bv_page);
+               kunmap_atomic(p1);
        }
 
-       __bio_for_each_segment(bv, check, i, 0)
+       bio_for_each_segment_all(bv, check, i)
                __free_page(bv->bv_page);
 out_put:
        bio_put(check);
 }
 
-#endif
-
-#ifdef CONFIG_BCACHE_EDEBUG
-
-unsigned bch_count_data(struct btree *b)
+int __bch_count_data(struct btree *b)
 {
        unsigned ret = 0;
        struct btree_iter iter;
@@ -247,72 +218,60 @@ unsigned bch_count_data(struct btree *b)
        return ret;
 }
 
-static void vdump_bucket_and_panic(struct btree *b, const char *fmt,
-                                  va_list args)
-{
-       unsigned i;
-       char buf[80];
-
-       console_lock();
-
-       for (i = 0; i <= b->nsets; i++)
-               dump_bset(b, b->sets[i].data);
-
-       vprintk(fmt, args);
-
-       console_unlock();
-
-       bch_btree_to_text(buf, sizeof(buf), b);
-       panic("at %s\n", buf);
-}
-
-void bch_check_key_order_msg(struct btree *b, struct bset *i,
-                            const char *fmt, ...)
-{
-       struct bkey *k;
-
-       if (!i->keys)
-               return;
-
-       for (k = i->start; bkey_next(k) < end(i); k = bkey_next(k))
-               if (skipped_backwards(b, k)) {
-                       va_list args;
-                       va_start(args, fmt);
-
-                       vdump_bucket_and_panic(b, fmt, args);
-                       va_end(args);
-               }
-}
-
-void bch_check_keys(struct btree *b, const char *fmt, ...)
+void __bch_check_keys(struct btree *b, const char *fmt, ...)
 {
        va_list args;
        struct bkey *k, *p = NULL;
        struct btree_iter iter;
-
-       if (b->level)
-               return;
+       const char *err;
 
        for_each_key(b, k, &iter) {
-               if (p && bkey_cmp(&START_KEY(p), &START_KEY(k)) > 0) {
-                       printk(KERN_ERR "Keys out of order:\n");
-                       goto bug;
-               }
-
-               if (bch_ptr_invalid(b, k))
-                       continue;
-
-               if (p && bkey_cmp(p, &START_KEY(k)) > 0) {
-                       printk(KERN_ERR "Overlapping keys:\n");
-                       goto bug;
+               if (!b->level) {
+                       err = "Keys out of order";
+                       if (p && bkey_cmp(&START_KEY(p), &START_KEY(k)) > 0)
+                               goto bug;
+
+                       if (bch_ptr_invalid(b, k))
+                               continue;
+
+                       err =  "Overlapping keys";
+                       if (p && bkey_cmp(p, &START_KEY(k)) > 0)
+                               goto bug;
+               } else {
+                       if (bch_ptr_bad(b, k))
+                               continue;
+
+                       err = "Duplicate keys";
+                       if (p && !bkey_cmp(p, k))
+                               goto bug;
                }
                p = k;
        }
+
+       err = "Key larger than btree node key";
+       if (p && bkey_cmp(p, &b->key) > 0)
+               goto bug;
+
        return;
 bug:
+       bch_dump_bucket(b);
+
        va_start(args, fmt);
-       vdump_bucket_and_panic(b, fmt, args);
+       vprintk(fmt, args);
        va_end(args);
+
+       panic("bcache error: %s:\n", err);
+}
+
+void bch_btree_iter_next_check(struct btree_iter *iter)
+{
+       struct bkey *k = iter->data->k, *next = bkey_next(k);
+
+       if (next < iter->data->end &&
+           bkey_cmp(k, iter->b->level ? next : &START_KEY(next)) > 0) {
+               bch_dump_bucket(iter->b);
+               panic("Key skipped backwards\n");
+       }
 }
 
 #endif
index 1c39b5a..2ede60e 100644 (file)
@@ -4,40 +4,44 @@
 /* Btree/bkey debug printing */
 
 int bch_bkey_to_text(char *buf, size_t size, const struct bkey *k);
-int bch_btree_to_text(char *buf, size_t size, const struct btree *b);
-
-#ifdef CONFIG_BCACHE_EDEBUG
-
-unsigned bch_count_data(struct btree *);
-void bch_check_key_order_msg(struct btree *, struct bset *, const char *, ...);
-void bch_check_keys(struct btree *, const char *, ...);
-
-#define bch_check_key_order(b, i)                      \
-       bch_check_key_order_msg(b, i, "keys out of order")
-#define EBUG_ON(cond)          BUG_ON(cond)
-
-#else /* EDEBUG */
-
-#define bch_count_data(b)                              0
-#define bch_check_key_order(b, i)                      do {} while (0)
-#define bch_check_key_order_msg(b, i, ...)             do {} while (0)
-#define bch_check_keys(b, ...)                         do {} while (0)
-#define EBUG_ON(cond)                                  do {} while (0)
-
-#endif
 
 #ifdef CONFIG_BCACHE_DEBUG
 
 void bch_btree_verify(struct btree *, struct bset *);
-void bch_data_verify(struct search *);
+void bch_data_verify(struct cached_dev *, struct bio *);
+int __bch_count_data(struct btree *);
+void __bch_check_keys(struct btree *, const char *, ...);
+void bch_btree_iter_next_check(struct btree_iter *);
+
+#define EBUG_ON(cond)                  BUG_ON(cond)
+#define expensive_debug_checks(c)      ((c)->expensive_debug_checks)
+#define key_merging_disabled(c)                ((c)->key_merging_disabled)
+#define bypass_torture_test(d)         ((d)->bypass_torture_test)
 
 #else /* DEBUG */
 
 static inline void bch_btree_verify(struct btree *b, struct bset *i) {}
-static inline void bch_data_verify(struct search *s) {};
+static inline void bch_data_verify(struct cached_dev *dc, struct bio *bio) {}
+static inline int __bch_count_data(struct btree *b) { return -1; }
+static inline void __bch_check_keys(struct btree *b, const char *fmt, ...) {}
+static inline void bch_btree_iter_next_check(struct btree_iter *iter) {}
+
+#define EBUG_ON(cond)                  do { if (cond); } while (0)
+#define expensive_debug_checks(c)      0
+#define key_merging_disabled(c)                0
+#define bypass_torture_test(d)         0
 
 #endif
 
+#define bch_count_data(b)                                              \
+       (expensive_debug_checks((b)->c) ? __bch_count_data(b) : -1)
+
+#define bch_check_keys(b, ...)                                         \
+do {                                                                   \
+       if (expensive_debug_checks((b)->c))                             \
+               __bch_check_keys(b, __VA_ARGS__);                       \
+} while (0)
+
 #ifdef CONFIG_DEBUG_FS
 void bch_debug_init_cache_set(struct cache_set *);
 #else
index 8435f81..ecdaa67 100644 (file)
@@ -7,7 +7,6 @@
 #include "bcache.h"
 #include "btree.h"
 #include "debug.h"
-#include "request.h"
 
 #include <trace/events/bcache.h>
 
@@ -31,17 +30,20 @@ static void journal_read_endio(struct bio *bio, int error)
 }
 
 static int journal_read_bucket(struct cache *ca, struct list_head *list,
-                              struct btree_op *op, unsigned bucket_index)
+                              unsigned bucket_index)
 {
        struct journal_device *ja = &ca->journal;
        struct bio *bio = &ja->bio;
 
        struct journal_replay *i;
        struct jset *j, *data = ca->set->journal.w[0].data;
+       struct closure cl;
        unsigned len, left, offset = 0;
        int ret = 0;
        sector_t bucket = bucket_to_sector(ca->set, ca->sb.d[bucket_index]);
 
+       closure_init_stack(&cl);
+
        pr_debug("reading %llu", (uint64_t) bucket);
 
        while (offset < ca->sb.bucket_size) {
@@ -55,11 +57,11 @@ reread:             left = ca->sb.bucket_size - offset;
                bio->bi_size    = len << 9;
 
                bio->bi_end_io  = journal_read_endio;
-               bio->bi_private = &op->cl;
+               bio->bi_private = &cl;
                bch_bio_map(bio, data);
 
-               closure_bio_submit(bio, &op->cl, ca);
-               closure_sync(&op->cl);
+               closure_bio_submit(bio, &cl, ca);
+               closure_sync(&cl);
 
                /* This function could be simpler now since we no longer write
                 * journal entries that overlap bucket boundaries; this means
@@ -72,7 +74,7 @@ reread:               left = ca->sb.bucket_size - offset;
                        struct list_head *where;
                        size_t blocks, bytes = set_bytes(j);
 
-                       if (j->magic != jset_magic(ca->set))
+                       if (j->magic != jset_magic(&ca->sb))
                                return ret;
 
                        if (bytes > left << 9)
@@ -129,12 +131,11 @@ next_set:
        return ret;
 }
 
-int bch_journal_read(struct cache_set *c, struct list_head *list,
-                       struct btree_op *op)
+int bch_journal_read(struct cache_set *c, struct list_head *list)
 {
 #define read_bucket(b)                                                 \
        ({                                                              \
-               int ret = journal_read_bucket(ca, list, op, b);         \
+               int ret = journal_read_bucket(ca, list, b);             \
                __set_bit(b, bitmap);                                   \
                if (ret < 0)                                            \
                        return ret;                                     \
@@ -292,8 +293,7 @@ void bch_journal_mark(struct cache_set *c, struct list_head *list)
        }
 }
 
-int bch_journal_replay(struct cache_set *s, struct list_head *list,
-                         struct btree_op *op)
+int bch_journal_replay(struct cache_set *s, struct list_head *list)
 {
        int ret = 0, keys = 0, entries = 0;
        struct bkey *k;
@@ -301,31 +301,30 @@ int bch_journal_replay(struct cache_set *s, struct list_head *list,
                list_entry(list->prev, struct journal_replay, list);
 
        uint64_t start = i->j.last_seq, end = i->j.seq, n = start;
+       struct keylist keylist;
+
+       bch_keylist_init(&keylist);
 
        list_for_each_entry(i, list, list) {
                BUG_ON(i->pin && atomic_read(i->pin) != 1);
 
-               if (n != i->j.seq)
-                       pr_err(
-               "journal entries %llu-%llu missing! (replaying %llu-%llu)\n",
-               n, i->j.seq - 1, start, end);
+               cache_set_err_on(n != i->j.seq, s,
+"bcache: journal entries %llu-%llu missing! (replaying %llu-%llu)",
+                                n, i->j.seq - 1, start, end);
 
                for (k = i->j.start;
                     k < end(&i->j);
                     k = bkey_next(k)) {
                        trace_bcache_journal_replay_key(k);
 
-                       bkey_copy(op->keys.top, k);
-                       bch_keylist_push(&op->keys);
-
-                       op->journal = i->pin;
-                       atomic_inc(op->journal);
+                       bkey_copy(keylist.top, k);
+                       bch_keylist_push(&keylist);
 
-                       ret = bch_btree_insert(op, s);
+                       ret = bch_btree_insert(s, &keylist, i->pin, NULL);
                        if (ret)
                                goto err;
 
-                       BUG_ON(!bch_keylist_empty(&op->keys));
+                       BUG_ON(!bch_keylist_empty(&keylist));
                        keys++;
 
                        cond_resched();
@@ -339,14 +338,13 @@ int bch_journal_replay(struct cache_set *s, struct list_head *list,
 
        pr_info("journal replay done, %i keys in %i entries, seq %llu",
                keys, entries, end);
-
+err:
        while (!list_empty(list)) {
                i = list_first_entry(list, struct journal_replay, list);
                list_del(&i->list);
                kfree(i);
        }
-err:
-       closure_sync(&op->cl);
+
        return ret;
 }
 
@@ -358,48 +356,35 @@ static void btree_flush_write(struct cache_set *c)
         * Try to find the btree node with that references the oldest journal
         * entry, best is our current candidate and is locked if non NULL:
         */
-       struct btree *b, *best = NULL;
-       unsigned iter;
+       struct btree *b, *best;
+       unsigned i;
+retry:
+       best = NULL;
+
+       for_each_cached_btree(b, c, i)
+               if (btree_current_write(b)->journal) {
+                       if (!best)
+                               best = b;
+                       else if (journal_pin_cmp(c,
+                                       btree_current_write(best)->journal,
+                                       btree_current_write(b)->journal)) {
+                               best = b;
+                       }
+               }
 
-       for_each_cached_btree(b, c, iter) {
-               if (!down_write_trylock(&b->lock))
-                       continue;
+       b = best;
+       if (b) {
+               rw_lock(true, b, b->level);
 
-               if (!btree_node_dirty(b) ||
-                   !btree_current_write(b)->journal) {
+               if (!btree_current_write(b)->journal) {
                        rw_unlock(true, b);
-                       continue;
+                       /* We raced */
+                       goto retry;
                }
 
-               if (!best)
-                       best = b;
-               else if (journal_pin_cmp(c,
-                                        btree_current_write(best),
-                                        btree_current_write(b))) {
-                       rw_unlock(true, best);
-                       best = b;
-               } else
-                       rw_unlock(true, b);
+               bch_btree_node_write(b, NULL);
+               rw_unlock(true, b);
        }
-
-       if (best)
-               goto out;
-
-       /* We can't find the best btree node, just pick the first */
-       list_for_each_entry(b, &c->btree_cache, list)
-               if (!b->level && btree_node_dirty(b)) {
-                       best = b;
-                       rw_lock(true, best, best->level);
-                       goto found;
-               }
-
-out:
-       if (!best)
-               return;
-found:
-       if (btree_node_dirty(best))
-               bch_btree_node_write(best, NULL);
-       rw_unlock(true, best);
 }
 
 #define last_seq(j)    ((j)->seq - fifo_used(&(j)->pin) + 1)
@@ -495,7 +480,7 @@ static void journal_reclaim(struct cache_set *c)
                do_journal_discard(ca);
 
        if (c->journal.blocks_free)
-               return;
+               goto out;
 
        /*
         * Allocate:
@@ -521,7 +506,7 @@ static void journal_reclaim(struct cache_set *c)
 
        if (n)
                c->journal.blocks_free = c->sb.bucket_size >> c->block_bits;
-
+out:
        if (!journal_full(&c->journal))
                __closure_wake_up(&c->journal.wait);
 }
@@ -554,32 +539,26 @@ static void journal_write_endio(struct bio *bio, int error)
        struct journal_write *w = bio->bi_private;
 
        cache_set_err_on(error, w->c, "journal io error");
-       closure_put(&w->c->journal.io.cl);
+       closure_put(&w->c->journal.io);
 }
 
 static void journal_write(struct closure *);
 
 static void journal_write_done(struct closure *cl)
 {
-       struct journal *j = container_of(cl, struct journal, io.cl);
-       struct cache_set *c = container_of(j, struct cache_set, journal);
-
+       struct journal *j = container_of(cl, struct journal, io);
        struct journal_write *w = (j->cur == j->w)
                ? &j->w[1]
                : &j->w[0];
 
        __closure_wake_up(&w->wait);
-
-       if (c->journal_delay_ms)
-               closure_delay(&j->io, msecs_to_jiffies(c->journal_delay_ms));
-
-       continue_at(cl, journal_write, system_wq);
+       continue_at_nobarrier(cl, journal_write, system_wq);
 }
 
 static void journal_write_unlocked(struct closure *cl)
        __releases(c->journal.lock)
 {
-       struct cache_set *c = container_of(cl, struct cache_set, journal.io.cl);
+       struct cache_set *c = container_of(cl, struct cache_set, journal.io);
        struct cache *ca;
        struct journal_write *w = c->journal.cur;
        struct bkey *k = &c->journal.key;
@@ -617,7 +596,7 @@ static void journal_write_unlocked(struct closure *cl)
        for_each_cache(ca, c, i)
                w->data->prio_bucket[ca->sb.nr_this_dev] = ca->prio_buckets[0];
 
-       w->data->magic          = jset_magic(c);
+       w->data->magic          = jset_magic(&c->sb);
        w->data->version        = BCACHE_JSET_VERSION;
        w->data->last_seq       = last_seq(&c->journal);
        w->data->csum           = csum_set(w->data);
@@ -660,121 +639,134 @@ static void journal_write_unlocked(struct closure *cl)
 
 static void journal_write(struct closure *cl)
 {
-       struct cache_set *c = container_of(cl, struct cache_set, journal.io.cl);
+       struct cache_set *c = container_of(cl, struct cache_set, journal.io);
 
        spin_lock(&c->journal.lock);
        journal_write_unlocked(cl);
 }
 
-static void __journal_try_write(struct cache_set *c, bool noflush)
+static void journal_try_write(struct cache_set *c)
        __releases(c->journal.lock)
 {
-       struct closure *cl = &c->journal.io.cl;
+       struct closure *cl = &c->journal.io;
+       struct journal_write *w = c->journal.cur;
 
-       if (!closure_trylock(cl, &c->cl))
-               spin_unlock(&c->journal.lock);
-       else if (noflush && journal_full(&c->journal)) {
-               spin_unlock(&c->journal.lock);
-               continue_at(cl, journal_write, system_wq);
-       } else
+       w->need_write = true;
+
+       if (closure_trylock(cl, &c->cl))
                journal_write_unlocked(cl);
+       else
+               spin_unlock(&c->journal.lock);
 }
 
-#define journal_try_write(c)   __journal_try_write(c, false)
-
-void bch_journal_meta(struct cache_set *c, struct closure *cl)
+static struct journal_write *journal_wait_for_write(struct cache_set *c,
+                                                   unsigned nkeys)
 {
-       struct journal_write *w;
+       size_t sectors;
+       struct closure cl;
 
-       if (CACHE_SYNC(&c->sb)) {
-               spin_lock(&c->journal.lock);
+       closure_init_stack(&cl);
+
+       spin_lock(&c->journal.lock);
 
-               w = c->journal.cur;
-               w->need_write = true;
+       while (1) {
+               struct journal_write *w = c->journal.cur;
 
-               if (cl)
-                       BUG_ON(!closure_wait(&w->wait, cl));
+               sectors = __set_blocks(w->data, w->data->keys + nkeys,
+                                      c) * c->sb.block_size;
 
-               closure_flush(&c->journal.io);
-               __journal_try_write(c, true);
+               if (sectors <= min_t(size_t,
+                                    c->journal.blocks_free * c->sb.block_size,
+                                    PAGE_SECTORS << JSET_BITS))
+                       return w;
+
+               /* XXX: tracepoint */
+               if (!journal_full(&c->journal)) {
+                       trace_bcache_journal_entry_full(c);
+
+                       /*
+                        * XXX: If we were inserting so many keys that they
+                        * won't fit in an _empty_ journal write, we'll
+                        * deadlock. For now, handle this in
+                        * bch_keylist_realloc() - but something to think about.
+                        */
+                       BUG_ON(!w->data->keys);
+
+                       closure_wait(&w->wait, &cl);
+                       journal_try_write(c); /* unlocks */
+               } else {
+                       trace_bcache_journal_full(c);
+
+                       closure_wait(&c->journal.wait, &cl);
+                       journal_reclaim(c);
+                       spin_unlock(&c->journal.lock);
+
+                       btree_flush_write(c);
+               }
+
+               closure_sync(&cl);
+               spin_lock(&c->journal.lock);
        }
 }
 
+static void journal_write_work(struct work_struct *work)
+{
+       struct cache_set *c = container_of(to_delayed_work(work),
+                                          struct cache_set,
+                                          journal.work);
+       spin_lock(&c->journal.lock);
+       journal_try_write(c);
+}
+
 /*
  * Entry point to the journalling code - bio_insert() and btree_invalidate()
  * pass bch_journal() a list of keys to be journalled, and then
  * bch_journal() hands those same keys off to btree_insert_async()
  */
 
-void bch_journal(struct closure *cl)
+atomic_t *bch_journal(struct cache_set *c,
+                     struct keylist *keys,
+                     struct closure *parent)
 {
-       struct btree_op *op = container_of(cl, struct btree_op, cl);
-       struct cache_set *c = op->c;
        struct journal_write *w;
-       size_t b, n = ((uint64_t *) op->keys.top) - op->keys.list;
-
-       if (op->type != BTREE_INSERT ||
-           !CACHE_SYNC(&c->sb))
-               goto out;
+       atomic_t *ret;
 
-       /*
-        * If we're looping because we errored, might already be waiting on
-        * another journal write:
-        */
-       while (atomic_read(&cl->parent->remaining) & CLOSURE_WAITING)
-               closure_sync(cl->parent);
+       if (!CACHE_SYNC(&c->sb))
+               return NULL;
 
-       spin_lock(&c->journal.lock);
+       w = journal_wait_for_write(c, bch_keylist_nkeys(keys));
 
-       if (journal_full(&c->journal)) {
-               trace_bcache_journal_full(c);
+       memcpy(end(w->data), keys->keys, bch_keylist_bytes(keys));
+       w->data->keys += bch_keylist_nkeys(keys);
 
-               closure_wait(&c->journal.wait, cl);
+       ret = &fifo_back(&c->journal.pin);
+       atomic_inc(ret);
 
-               journal_reclaim(c);
+       if (parent) {
+               closure_wait(&w->wait, parent);
+               journal_try_write(c);
+       } else if (!w->need_write) {
+               schedule_delayed_work(&c->journal.work,
+                                     msecs_to_jiffies(c->journal_delay_ms));
+               spin_unlock(&c->journal.lock);
+       } else {
                spin_unlock(&c->journal.lock);
-
-               btree_flush_write(c);
-               continue_at(cl, bch_journal, bcache_wq);
        }
 
-       w = c->journal.cur;
-       w->need_write = true;
-       b = __set_blocks(w->data, w->data->keys + n, c);
-
-       if (b * c->sb.block_size > PAGE_SECTORS << JSET_BITS ||
-           b > c->journal.blocks_free) {
-               trace_bcache_journal_entry_full(c);
-
-               /*
-                * XXX: If we were inserting so many keys that they won't fit in
-                * an _empty_ journal write, we'll deadlock. For now, handle
-                * this in bch_keylist_realloc() - but something to think about.
-                */
-               BUG_ON(!w->data->keys);
-
-               BUG_ON(!closure_wait(&w->wait, cl));
-
-               closure_flush(&c->journal.io);
 
-               journal_try_write(c);
-               continue_at(cl, bch_journal, bcache_wq);
-       }
-
-       memcpy(end(w->data), op->keys.list, n * sizeof(uint64_t));
-       w->data->keys += n;
+       return ret;
+}
 
-       op->journal = &fifo_back(&c->journal.pin);
-       atomic_inc(op->journal);
+void bch_journal_meta(struct cache_set *c, struct closure *cl)
+{
+       struct keylist keys;
+       atomic_t *ref;
 
-       if (op->flush_journal) {
-               closure_flush(&c->journal.io);
-               closure_wait(&w->wait, cl->parent);
-       }
+       bch_keylist_init(&keys);
 
-       journal_try_write(c);
-out:
-       bch_btree_insert_async(cl);
+       ref = bch_journal(c, &keys, cl);
+       if (ref)
+               atomic_dec_bug(ref);
 }
 
 void bch_journal_free(struct cache_set *c)
@@ -790,6 +782,7 @@ int bch_journal_alloc(struct cache_set *c)
 
        closure_init_unlocked(&j->io);
        spin_lock_init(&j->lock);
+       INIT_DELAYED_WORK(&j->work, journal_write_work);
 
        c->journal_delay_ms = 100;
 
index 3d78512..a6472fd 100644 (file)
  * nodes that are pinning the oldest journal entries first.
  */
 
-#define BCACHE_JSET_VERSION_UUIDv1     1
-/* Always latest UUID format */
-#define BCACHE_JSET_VERSION_UUID       1
-#define BCACHE_JSET_VERSION            1
-
-/*
- * On disk format for a journal entry:
- * seq is monotonically increasing; every journal entry has its own unique
- * sequence number.
- *
- * last_seq is the oldest journal entry that still has keys the btree hasn't
- * flushed to disk yet.
- *
- * version is for on disk format changes.
- */
-struct jset {
-       uint64_t                csum;
-       uint64_t                magic;
-       uint64_t                seq;
-       uint32_t                version;
-       uint32_t                keys;
-
-       uint64_t                last_seq;
-
-       BKEY_PADDED(uuid_bucket);
-       BKEY_PADDED(btree_root);
-       uint16_t                btree_level;
-       uint16_t                pad[3];
-
-       uint64_t                prio_bucket[MAX_CACHES_PER_SET];
-
-       union {
-               struct bkey     start[0];
-               uint64_t        d[0];
-       };
-};
-
 /*
  * Only used for holding the journal entries we read in btree_journal_read()
  * during cache_registration
@@ -140,7 +103,8 @@ struct journal {
        spinlock_t              lock;
        /* used when waiting because the journal was full */
        struct closure_waitlist wait;
-       struct closure_with_timer io;
+       struct closure          io;
+       struct delayed_work     work;
 
        /* Number of blocks free in the bucket(s) we're currently writing to */
        unsigned                blocks_free;
@@ -188,8 +152,7 @@ struct journal_device {
 };
 
 #define journal_pin_cmp(c, l, r)                               \
-       (fifo_idx(&(c)->journal.pin, (l)->journal) >            \
-        fifo_idx(&(c)->journal.pin, (r)->journal))
+       (fifo_idx(&(c)->journal.pin, (l)) > fifo_idx(&(c)->journal.pin, (r)))
 
 #define JOURNAL_PIN    20000
 
@@ -199,15 +162,14 @@ struct journal_device {
 struct closure;
 struct cache_set;
 struct btree_op;
+struct keylist;
 
-void bch_journal(struct closure *);
+atomic_t *bch_journal(struct cache_set *, struct keylist *, struct closure *);
 void bch_journal_next(struct journal *);
 void bch_journal_mark(struct cache_set *, struct list_head *);
 void bch_journal_meta(struct cache_set *, struct closure *);
-int bch_journal_read(struct cache_set *, struct list_head *,
-                       struct btree_op *);
-int bch_journal_replay(struct cache_set *, struct list_head *,
-                         struct btree_op *);
+int bch_journal_read(struct cache_set *, struct list_head *);
+int bch_journal_replay(struct cache_set *, struct list_head *);
 
 void bch_journal_free(struct cache_set *);
 int bch_journal_alloc(struct cache_set *);
index 1a3b4f4..7c1275e 100644 (file)
@@ -12,8 +12,9 @@
 #include <trace/events/bcache.h>
 
 struct moving_io {
+       struct closure          cl;
        struct keybuf_key       *w;
-       struct search           s;
+       struct data_insert_op   op;
        struct bbio             bio;
 };
 
@@ -38,13 +39,13 @@ static bool moving_pred(struct keybuf *buf, struct bkey *k)
 
 static void moving_io_destructor(struct closure *cl)
 {
-       struct moving_io *io = container_of(cl, struct moving_io, s.cl);
+       struct moving_io *io = container_of(cl, struct moving_io, cl);
        kfree(io);
 }
 
 static void write_moving_finish(struct closure *cl)
 {
-       struct moving_io *io = container_of(cl, struct moving_io, s.cl);
+       struct moving_io *io = container_of(cl, struct moving_io, cl);
        struct bio *bio = &io->bio.bio;
        struct bio_vec *bv;
        int i;
@@ -52,13 +53,12 @@ static void write_moving_finish(struct closure *cl)
        bio_for_each_segment_all(bv, bio, i)
                __free_page(bv->bv_page);
 
-       if (io->s.op.insert_collision)
+       if (io->op.replace_collision)
                trace_bcache_gc_copy_collision(&io->w->key);
 
-       bch_keybuf_del(&io->s.op.c->moving_gc_keys, io->w);
+       bch_keybuf_del(&io->op.c->moving_gc_keys, io->w);
 
-       atomic_dec_bug(&io->s.op.c->in_flight);
-       closure_wake_up(&io->s.op.c->moving_gc_wait);
+       up(&io->op.c->moving_in_flight);
 
        closure_return_with_destructor(cl, moving_io_destructor);
 }
@@ -66,12 +66,12 @@ static void write_moving_finish(struct closure *cl)
 static void read_moving_endio(struct bio *bio, int error)
 {
        struct moving_io *io = container_of(bio->bi_private,
-                                           struct moving_io, s.cl);
+                                           struct moving_io, cl);
 
        if (error)
-               io->s.error = error;
+               io->op.error = error;
 
-       bch_bbio_endio(io->s.op.c, bio, error, "reading data to move");
+       bch_bbio_endio(io->op.c, bio, error, "reading data to move");
 }
 
 static void moving_init(struct moving_io *io)
@@ -85,54 +85,53 @@ static void moving_init(struct moving_io *io)
        bio->bi_size            = KEY_SIZE(&io->w->key) << 9;
        bio->bi_max_vecs        = DIV_ROUND_UP(KEY_SIZE(&io->w->key),
                                               PAGE_SECTORS);
-       bio->bi_private         = &io->s.cl;
+       bio->bi_private         = &io->cl;
        bio->bi_io_vec          = bio->bi_inline_vecs;
        bch_bio_map(bio, NULL);
 }
 
 static void write_moving(struct closure *cl)
 {
-       struct search *s = container_of(cl, struct search, cl);
-       struct moving_io *io = container_of(s, struct moving_io, s);
+       struct moving_io *io = container_of(cl, struct moving_io, cl);
+       struct data_insert_op *op = &io->op;
 
-       if (!s->error) {
+       if (!op->error) {
                moving_init(io);
 
-               io->bio.bio.bi_sector   = KEY_START(&io->w->key);
-               s->op.lock              = -1;
-               s->op.write_prio        = 1;
-               s->op.cache_bio         = &io->bio.bio;
+               io->bio.bio.bi_sector = KEY_START(&io->w->key);
+               op->write_prio          = 1;
+               op->bio                 = &io->bio.bio;
 
-               s->writeback            = KEY_DIRTY(&io->w->key);
-               s->op.csum              = KEY_CSUM(&io->w->key);
+               op->writeback           = KEY_DIRTY(&io->w->key);
+               op->csum                = KEY_CSUM(&io->w->key);
 
-               s->op.type = BTREE_REPLACE;
-               bkey_copy(&s->op.replace, &io->w->key);
+               bkey_copy(&op->replace_key, &io->w->key);
+               op->replace             = true;
 
-               closure_init(&s->op.cl, cl);
-               bch_insert_data(&s->op.cl);
+               closure_call(&op->cl, bch_data_insert, NULL, cl);
        }
 
-       continue_at(cl, write_moving_finish, NULL);
+       continue_at(cl, write_moving_finish, system_wq);
 }
 
 static void read_moving_submit(struct closure *cl)
 {
-       struct search *s = container_of(cl, struct search, cl);
-       struct moving_io *io = container_of(s, struct moving_io, s);
+       struct moving_io *io = container_of(cl, struct moving_io, cl);
        struct bio *bio = &io->bio.bio;
 
-       bch_submit_bbio(bio, s->op.c, &io->w->key, 0);
+       bch_submit_bbio(bio, io->op.c, &io->w->key, 0);
 
-       continue_at(cl, write_moving, bch_gc_wq);
+       continue_at(cl, write_moving, system_wq);
 }
 
-static void read_moving(struct closure *cl)
+static void read_moving(struct cache_set *c)
 {
-       struct cache_set *c = container_of(cl, struct cache_set, moving_gc);
        struct keybuf_key *w;
        struct moving_io *io;
        struct bio *bio;
+       struct closure cl;
+
+       closure_init_stack(&cl);
 
        /* XXX: if we error, background writeback could stall indefinitely */
 
@@ -150,8 +149,8 @@ static void read_moving(struct closure *cl)
 
                w->private      = io;
                io->w           = w;
-               io->s.op.inode  = KEY_INODE(&w->key);
-               io->s.op.c      = c;
+               io->op.inode    = KEY_INODE(&w->key);
+               io->op.c        = c;
 
                moving_init(io);
                bio = &io->bio.bio;
@@ -164,13 +163,8 @@ static void read_moving(struct closure *cl)
 
                trace_bcache_gc_copy(&w->key);
 
-               closure_call(&io->s.cl, read_moving_submit, NULL, &c->gc.cl);
-
-               if (atomic_inc_return(&c->in_flight) >= 64) {
-                       closure_wait_event(&c->moving_gc_wait, cl,
-                                          atomic_read(&c->in_flight) < 64);
-                       continue_at(cl, read_moving, bch_gc_wq);
-               }
+               down(&c->moving_in_flight);
+               closure_call(&io->cl, read_moving_submit, NULL, &cl);
        }
 
        if (0) {
@@ -180,7 +174,7 @@ err:                if (!IS_ERR_OR_NULL(w->private))
                bch_keybuf_del(&c->moving_gc_keys, w);
        }
 
-       closure_return(cl);
+       closure_sync(&cl);
 }
 
 static bool bucket_cmp(struct bucket *l, struct bucket *r)
@@ -193,15 +187,14 @@ static unsigned bucket_heap_top(struct cache *ca)
        return GC_SECTORS_USED(heap_peek(&ca->heap));
 }
 
-void bch_moving_gc(struct closure *cl)
+void bch_moving_gc(struct cache_set *c)
 {
-       struct cache_set *c = container_of(cl, struct cache_set, gc.cl);
        struct cache *ca;
        struct bucket *b;
        unsigned i;
 
        if (!c->copy_gc_enabled)
-               closure_return(cl);
+               return;
 
        mutex_lock(&c->bucket_lock);
 
@@ -242,13 +235,11 @@ void bch_moving_gc(struct closure *cl)
 
        c->moving_gc_keys.last_scanned = ZERO_KEY;
 
-       closure_init(&c->moving_gc, cl);
-       read_moving(&c->moving_gc);
-
-       closure_return(cl);
+       read_moving(c);
 }
 
 void bch_moving_init_cache_set(struct cache_set *c)
 {
        bch_keybuf_init(&c->moving_gc_keys);
+       sema_init(&c->moving_in_flight, 64);
 }
index 2a7f0dd..fbcc851 100644 (file)
@@ -25,7 +25,7 @@
 
 struct kmem_cache *bch_search_cache;
 
-static void check_should_skip(struct cached_dev *, struct search *);
+static void bch_data_insert_start(struct closure *);
 
 /* Cgroup interface */
 
@@ -213,221 +213,79 @@ static void bio_csum(struct bio *bio, struct bkey *k)
 
 /* Insert data into cache */
 
-static void bio_invalidate(struct closure *cl)
+static void bch_data_insert_keys(struct closure *cl)
 {
-       struct btree_op *op = container_of(cl, struct btree_op, cl);
-       struct bio *bio = op->cache_bio;
-
-       pr_debug("invalidating %i sectors from %llu",
-                bio_sectors(bio), (uint64_t) bio->bi_sector);
-
-       while (bio_sectors(bio)) {
-               unsigned len = min(bio_sectors(bio), 1U << 14);
-
-               if (bch_keylist_realloc(&op->keys, 0, op->c))
-                       goto out;
-
-               bio->bi_sector  += len;
-               bio->bi_size    -= len << 9;
-
-               bch_keylist_add(&op->keys,
-                               &KEY(op->inode, bio->bi_sector, len));
-       }
-
-       op->insert_data_done = true;
-       bio_put(bio);
-out:
-       continue_at(cl, bch_journal, bcache_wq);
-}
-
-struct open_bucket {
-       struct list_head        list;
-       struct task_struct      *last;
-       unsigned                sectors_free;
-       BKEY_PADDED(key);
-};
-
-void bch_open_buckets_free(struct cache_set *c)
-{
-       struct open_bucket *b;
-
-       while (!list_empty(&c->data_buckets)) {
-               b = list_first_entry(&c->data_buckets,
-                                    struct open_bucket, list);
-               list_del(&b->list);
-               kfree(b);
-       }
-}
-
-int bch_open_buckets_alloc(struct cache_set *c)
-{
-       int i;
-
-       spin_lock_init(&c->data_bucket_lock);
-
-       for (i = 0; i < 6; i++) {
-               struct open_bucket *b = kzalloc(sizeof(*b), GFP_KERNEL);
-               if (!b)
-                       return -ENOMEM;
-
-               list_add(&b->list, &c->data_buckets);
-       }
-
-       return 0;
-}
-
-/*
- * We keep multiple buckets open for writes, and try to segregate different
- * write streams for better cache utilization: first we look for a bucket where
- * the last write to it was sequential with the current write, and failing that
- * we look for a bucket that was last used by the same task.
- *
- * The ideas is if you've got multiple tasks pulling data into the cache at the
- * same time, you'll get better cache utilization if you try to segregate their
- * data and preserve locality.
- *
- * For example, say you've starting Firefox at the same time you're copying a
- * bunch of files. Firefox will likely end up being fairly hot and stay in the
- * cache awhile, but the data you copied might not be; if you wrote all that
- * data to the same buckets it'd get invalidated at the same time.
- *
- * Both of those tasks will be doing fairly random IO so we can't rely on
- * detecting sequential IO to segregate their data, but going off of the task
- * should be a sane heuristic.
- */
-static struct open_bucket *pick_data_bucket(struct cache_set *c,
-                                           const struct bkey *search,
-                                           struct task_struct *task,
-                                           struct bkey *alloc)
-{
-       struct open_bucket *ret, *ret_task = NULL;
-
-       list_for_each_entry_reverse(ret, &c->data_buckets, list)
-               if (!bkey_cmp(&ret->key, search))
-                       goto found;
-               else if (ret->last == task)
-                       ret_task = ret;
-
-       ret = ret_task ?: list_first_entry(&c->data_buckets,
-                                          struct open_bucket, list);
-found:
-       if (!ret->sectors_free && KEY_PTRS(alloc)) {
-               ret->sectors_free = c->sb.bucket_size;
-               bkey_copy(&ret->key, alloc);
-               bkey_init(alloc);
-       }
-
-       if (!ret->sectors_free)
-               ret = NULL;
-
-       return ret;
-}
-
-/*
- * Allocates some space in the cache to write to, and k to point to the newly
- * allocated space, and updates KEY_SIZE(k) and KEY_OFFSET(k) (to point to the
- * end of the newly allocated space).
- *
- * May allocate fewer sectors than @sectors, KEY_SIZE(k) indicates how many
- * sectors were actually allocated.
- *
- * If s->writeback is true, will not fail.
- */
-static bool bch_alloc_sectors(struct bkey *k, unsigned sectors,
-                             struct search *s)
-{
-       struct cache_set *c = s->op.c;
-       struct open_bucket *b;
-       BKEY_PADDED(key) alloc;
-       struct closure cl, *w = NULL;
-       unsigned i;
-
-       if (s->writeback) {
-               closure_init_stack(&cl);
-               w = &cl;
-       }
+       struct data_insert_op *op = container_of(cl, struct data_insert_op, cl);
+       atomic_t *journal_ref = NULL;
+       struct bkey *replace_key = op->replace ? &op->replace_key : NULL;
+       int ret;
 
        /*
-        * We might have to allocate a new bucket, which we can't do with a
-        * spinlock held. So if we have to allocate, we drop the lock, allocate
-        * and then retry. KEY_PTRS() indicates whether alloc points to
-        * allocated bucket(s).
+        * If we're looping, might already be waiting on
+        * another journal write - can't wait on more than one journal write at
+        * a time
+        *
+        * XXX: this looks wrong
         */
+#if 0
+       while (atomic_read(&s->cl.remaining) & CLOSURE_WAITING)
+               closure_sync(&s->cl);
+#endif
 
-       bkey_init(&alloc.key);
-       spin_lock(&c->data_bucket_lock);
-
-       while (!(b = pick_data_bucket(c, k, s->task, &alloc.key))) {
-               unsigned watermark = s->op.write_prio
-                       ? WATERMARK_MOVINGGC
-                       : WATERMARK_NONE;
-
-               spin_unlock(&c->data_bucket_lock);
-
-               if (bch_bucket_alloc_set(c, watermark, &alloc.key, 1, w))
-                       return false;
+       if (!op->replace)
+               journal_ref = bch_journal(op->c, &op->insert_keys,
+                                         op->flush_journal ? cl : NULL);
 
-               spin_lock(&c->data_bucket_lock);
+       ret = bch_btree_insert(op->c, &op->insert_keys,
+                              journal_ref, replace_key);
+       if (ret == -ESRCH) {
+               op->replace_collision = true;
+       } else if (ret) {
+               op->error               = -ENOMEM;
+               op->insert_data_done    = true;
        }
 
-       /*
-        * If we had to allocate, we might race and not need to allocate the
-        * second time we call find_data_bucket(). If we allocated a bucket but
-        * didn't use it, drop the refcount bch_bucket_alloc_set() took:
-        */
-       if (KEY_PTRS(&alloc.key))
-               __bkey_put(c, &alloc.key);
-
-       for (i = 0; i < KEY_PTRS(&b->key); i++)
-               EBUG_ON(ptr_stale(c, &b->key, i));
+       if (journal_ref)
+               atomic_dec_bug(journal_ref);
 
-       /* Set up the pointer to the space we're allocating: */
+       if (!op->insert_data_done)
+               continue_at(cl, bch_data_insert_start, bcache_wq);
 
-       for (i = 0; i < KEY_PTRS(&b->key); i++)
-               k->ptr[i] = b->key.ptr[i];
+       bch_keylist_free(&op->insert_keys);
+       closure_return(cl);
+}
 
-       sectors = min(sectors, b->sectors_free);
+static void bch_data_invalidate(struct closure *cl)
+{
+       struct data_insert_op *op = container_of(cl, struct data_insert_op, cl);
+       struct bio *bio = op->bio;
 
-       SET_KEY_OFFSET(k, KEY_OFFSET(k) + sectors);
-       SET_KEY_SIZE(k, sectors);
-       SET_KEY_PTRS(k, KEY_PTRS(&b->key));
+       pr_debug("invalidating %i sectors from %llu",
+                bio_sectors(bio), (uint64_t) bio->bi_sector);
 
-       /*
-        * Move b to the end of the lru, and keep track of what this bucket was
-        * last used for:
-        */
-       list_move_tail(&b->list, &c->data_buckets);
-       bkey_copy_key(&b->key, k);
-       b->last = s->task;
+       while (bio_sectors(bio)) {
+               unsigned sectors = min(bio_sectors(bio),
+                                      1U << (KEY_SIZE_BITS - 1));
 
-       b->sectors_free -= sectors;
+               if (bch_keylist_realloc(&op->insert_keys, 0, op->c))
+                       goto out;
 
-       for (i = 0; i < KEY_PTRS(&b->key); i++) {
-               SET_PTR_OFFSET(&b->key, i, PTR_OFFSET(&b->key, i) + sectors);
+               bio->bi_sector  += sectors;
+               bio->bi_size    -= sectors << 9;
 
-               atomic_long_add(sectors,
-                               &PTR_CACHE(c, &b->key, i)->sectors_written);
+               bch_keylist_add(&op->insert_keys,
+                               &KEY(op->inode, bio->bi_sector, sectors));
        }
 
-       if (b->sectors_free < c->sb.block_size)
-               b->sectors_free = 0;
-
-       /*
-        * k takes refcounts on the buckets it points to until it's inserted
-        * into the btree, but if we're done with this bucket we just transfer
-        * get_data_bucket()'s refcount.
-        */
-       if (b->sectors_free)
-               for (i = 0; i < KEY_PTRS(&b->key); i++)
-                       atomic_inc(&PTR_BUCKET(c, &b->key, i)->pin);
-
-       spin_unlock(&c->data_bucket_lock);
-       return true;
+       op->insert_data_done = true;
+       bio_put(bio);
+out:
+       continue_at(cl, bch_data_insert_keys, bcache_wq);
 }
 
-static void bch_insert_data_error(struct closure *cl)
+static void bch_data_insert_error(struct closure *cl)
 {
-       struct btree_op *op = container_of(cl, struct btree_op, cl);
+       struct data_insert_op *op = container_of(cl, struct data_insert_op, cl);
 
        /*
         * Our data write just errored, which means we've got a bunch of keys to
@@ -438,35 +296,34 @@ static void bch_insert_data_error(struct closure *cl)
         * from the keys we'll accomplish just that.
         */
 
-       struct bkey *src = op->keys.bottom, *dst = op->keys.bottom;
+       struct bkey *src = op->insert_keys.keys, *dst = op->insert_keys.keys;
 
-       while (src != op->keys.top) {
+       while (src != op->insert_keys.top) {
                struct bkey *n = bkey_next(src);
 
                SET_KEY_PTRS(src, 0);
-               bkey_copy(dst, src);
+               memmove(dst, src, bkey_bytes(src));
 
                dst = bkey_next(dst);
                src = n;
        }
 
-       op->keys.top = dst;
+       op->insert_keys.top = dst;
 
-       bch_journal(cl);
+       bch_data_insert_keys(cl);
 }
 
-static void bch_insert_data_endio(struct bio *bio, int error)
+static void bch_data_insert_endio(struct bio *bio, int error)
 {
        struct closure *cl = bio->bi_private;
-       struct btree_op *op = container_of(cl, struct btree_op, cl);
-       struct search *s = container_of(op, struct search, op);
+       struct data_insert_op *op = container_of(cl, struct data_insert_op, cl);
 
        if (error) {
                /* TODO: We could try to recover from this. */
-               if (s->writeback)
-                       s->error = error;
-               else if (s->write)
-                       set_closure_fn(cl, bch_insert_data_error, bcache_wq);
+               if (op->writeback)
+                       op->error = error;
+               else if (!op->replace)
+                       set_closure_fn(cl, bch_data_insert_error, bcache_wq);
                else
                        set_closure_fn(cl, NULL, NULL);
        }
@@ -474,18 +331,17 @@ static void bch_insert_data_endio(struct bio *bio, int error)
        bch_bbio_endio(op->c, bio, error, "writing data to cache");
 }
 
-static void bch_insert_data_loop(struct closure *cl)
+static void bch_data_insert_start(struct closure *cl)
 {
-       struct btree_op *op = container_of(cl, struct btree_op, cl);
-       struct search *s = container_of(op, struct search, op);
-       struct bio *bio = op->cache_bio, *n;
+       struct data_insert_op *op = container_of(cl, struct data_insert_op, cl);
+       struct bio *bio = op->bio, *n;
 
-       if (op->skip)
-               return bio_invalidate(cl);
+       if (op->bypass)
+               return bch_data_invalidate(cl);
 
        if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) {
                set_gc_sectors(op->c);
-               bch_queue_gc(op->c);
+               wake_up_gc(op->c);
        }
 
        /*
@@ -497,29 +353,30 @@ static void bch_insert_data_loop(struct closure *cl)
        do {
                unsigned i;
                struct bkey *k;
-               struct bio_set *split = s->d
-                       ? s->d->bio_split : op->c->bio_split;
+               struct bio_set *split = op->c->bio_split;
 
                /* 1 for the device pointer and 1 for the chksum */
-               if (bch_keylist_realloc(&op->keys,
+               if (bch_keylist_realloc(&op->insert_keys,
                                        1 + (op->csum ? 1 : 0),
                                        op->c))
-                       continue_at(cl, bch_journal, bcache_wq);
+                       continue_at(cl, bch_data_insert_keys, bcache_wq);
 
-               k = op->keys.top;
+               k = op->insert_keys.top;
                bkey_init(k);
                SET_KEY_INODE(k, op->inode);
                SET_KEY_OFFSET(k, bio->bi_sector);
 
-               if (!bch_alloc_sectors(k, bio_sectors(bio), s))
+               if (!bch_alloc_sectors(op->c, k, bio_sectors(bio),
+                                      op->write_point, op->write_prio,
+                                      op->writeback))
                        goto err;
 
                n = bch_bio_split(bio, KEY_SIZE(k), GFP_NOIO, split);
 
-               n->bi_end_io    = bch_insert_data_endio;
+               n->bi_end_io    = bch_data_insert_endio;
                n->bi_private   = cl;
 
-               if (s->writeback) {
+               if (op->writeback) {
                        SET_KEY_DIRTY(k, true);
 
                        for (i = 0; i < KEY_PTRS(k); i++)
@@ -532,17 +389,17 @@ static void bch_insert_data_loop(struct closure *cl)
                        bio_csum(n, k);
 
                trace_bcache_cache_insert(k);
-               bch_keylist_push(&op->keys);
+               bch_keylist_push(&op->insert_keys);
 
                n->bi_rw |= REQ_WRITE;
                bch_submit_bbio(n, op->c, k, 0);
        } while (n != bio);
 
        op->insert_data_done = true;
-       continue_at(cl, bch_journal, bcache_wq);
+       continue_at(cl, bch_data_insert_keys, bcache_wq);
 err:
        /* bch_alloc_sectors() blocks if s->writeback = true */
-       BUG_ON(s->writeback);
+       BUG_ON(op->writeback);
 
        /*
         * But if it's not a writeback write we'd rather just bail out if
@@ -550,15 +407,15 @@ err:
         * we might be starving btree writes for gc or something.
         */
 
-       if (s->write) {
+       if (!op->replace) {
                /*
                 * Writethrough write: We can't complete the write until we've
                 * updated the index. But we don't want to delay the write while
                 * we wait for buckets to be freed up, so just invalidate the
                 * rest of the write.
                 */
-               op->skip = true;
-               return bio_invalidate(cl);
+               op->bypass = true;
+               return bch_data_invalidate(cl);
        } else {
                /*
                 * From a cache miss, we can just insert the keys for the data
@@ -567,15 +424,15 @@ err:
                op->insert_data_done = true;
                bio_put(bio);
 
-               if (!bch_keylist_empty(&op->keys))
-                       continue_at(cl, bch_journal, bcache_wq);
+               if (!bch_keylist_empty(&op->insert_keys))
+                       continue_at(cl, bch_data_insert_keys, bcache_wq);
                else
                        closure_return(cl);
        }
 }
 
 /**
- * bch_insert_data - stick some data in the cache
+ * bch_data_insert - stick some data in the cache
  *
  * This is the starting point for any data to end up in a cache device; it could
  * be from a normal write, or a writeback write, or a write to a flash only
@@ -587,56 +444,179 @@ err:
  * data is written it calls bch_journal, and after the keys have been added to
  * the next journal write they're inserted into the btree.
  *
- * It inserts the data in op->cache_bio; bi_sector is used for the key offset,
+ * It inserts the data in s->cache_bio; bi_sector is used for the key offset,
  * and op->inode is used for the key inode.
  *
- * If op->skip is true, instead of inserting the data it invalidates the region
- * of the cache represented by op->cache_bio and op->inode.
+ * If s->bypass is true, instead of inserting the data it invalidates the
+ * region of the cache represented by s->cache_bio and op->inode.
  */
-void bch_insert_data(struct closure *cl)
+void bch_data_insert(struct closure *cl)
 {
-       struct btree_op *op = container_of(cl, struct btree_op, cl);
+       struct data_insert_op *op = container_of(cl, struct data_insert_op, cl);
+
+       trace_bcache_write(op->bio, op->writeback, op->bypass);
 
-       bch_keylist_init(&op->keys);
-       bio_get(op->cache_bio);
-       bch_insert_data_loop(cl);
+       bch_keylist_init(&op->insert_keys);
+       bio_get(op->bio);
+       bch_data_insert_start(cl);
 }
 
-void bch_btree_insert_async(struct closure *cl)
+/* Congested? */
+
+unsigned bch_get_congested(struct cache_set *c)
 {
-       struct btree_op *op = container_of(cl, struct btree_op, cl);
-       struct search *s = container_of(op, struct search, op);
+       int i;
+       long rand;
 
-       if (bch_btree_insert(op, op->c)) {
-               s->error                = -ENOMEM;
-               op->insert_data_done    = true;
-       }
+       if (!c->congested_read_threshold_us &&
+           !c->congested_write_threshold_us)
+               return 0;
+
+       i = (local_clock_us() - c->congested_last_us) / 1024;
+       if (i < 0)
+               return 0;
+
+       i += atomic_read(&c->congested);
+       if (i >= 0)
+               return 0;
 
-       if (op->insert_data_done) {
-               bch_keylist_free(&op->keys);
-               closure_return(cl);
-       } else
-               continue_at(cl, bch_insert_data_loop, bcache_wq);
+       i += CONGESTED_MAX;
+
+       if (i > 0)
+               i = fract_exp_two(i, 6);
+
+       rand = get_random_int();
+       i -= bitmap_weight(&rand, BITS_PER_LONG);
+
+       return i > 0 ? i : 1;
 }
 
-/* Common code for the make_request functions */
+static void add_sequential(struct task_struct *t)
+{
+       ewma_add(t->sequential_io_avg,
+                t->sequential_io, 8, 0);
 
-static void request_endio(struct bio *bio, int error)
+       t->sequential_io = 0;
+}
+
+static struct hlist_head *iohash(struct cached_dev *dc, uint64_t k)
 {
-       struct closure *cl = bio->bi_private;
+       return &dc->io_hash[hash_64(k, RECENT_IO_BITS)];
+}
 
-       if (error) {
-               struct search *s = container_of(cl, struct search, cl);
-               s->error = error;
-               /* Only cache read errors are recoverable */
-               s->recoverable = false;
+static bool check_should_bypass(struct cached_dev *dc, struct bio *bio)
+{
+       struct cache_set *c = dc->disk.c;
+       unsigned mode = cache_mode(dc, bio);
+       unsigned sectors, congested = bch_get_congested(c);
+       struct task_struct *task = current;
+       struct io *i;
+
+       if (test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) ||
+           c->gc_stats.in_use > CUTOFF_CACHE_ADD ||
+           (bio->bi_rw & REQ_DISCARD))
+               goto skip;
+
+       if (mode == CACHE_MODE_NONE ||
+           (mode == CACHE_MODE_WRITEAROUND &&
+            (bio->bi_rw & REQ_WRITE)))
+               goto skip;
+
+       if (bio->bi_sector & (c->sb.block_size - 1) ||
+           bio_sectors(bio) & (c->sb.block_size - 1)) {
+               pr_debug("skipping unaligned io");
+               goto skip;
        }
 
-       bio_put(bio);
-       closure_put(cl);
+       if (bypass_torture_test(dc)) {
+               if ((get_random_int() & 3) == 3)
+                       goto skip;
+               else
+                       goto rescale;
+       }
+
+       if (!congested && !dc->sequential_cutoff)
+               goto rescale;
+
+       if (!congested &&
+           mode == CACHE_MODE_WRITEBACK &&
+           (bio->bi_rw & REQ_WRITE) &&
+           (bio->bi_rw & REQ_SYNC))
+               goto rescale;
+
+       spin_lock(&dc->io_lock);
+
+       hlist_for_each_entry(i, iohash(dc, bio->bi_sector), hash)
+               if (i->last == bio->bi_sector &&
+                   time_before(jiffies, i->jiffies))
+                       goto found;
+
+       i = list_first_entry(&dc->io_lru, struct io, lru);
+
+       add_sequential(task);
+       i->sequential = 0;
+found:
+       if (i->sequential + bio->bi_size > i->sequential)
+               i->sequential   += bio->bi_size;
+
+       i->last                  = bio_end_sector(bio);
+       i->jiffies               = jiffies + msecs_to_jiffies(5000);
+       task->sequential_io      = i->sequential;
+
+       hlist_del(&i->hash);
+       hlist_add_head(&i->hash, iohash(dc, i->last));
+       list_move_tail(&i->lru, &dc->io_lru);
+
+       spin_unlock(&dc->io_lock);
+
+       sectors = max(task->sequential_io,
+                     task->sequential_io_avg) >> 9;
+
+       if (dc->sequential_cutoff &&
+           sectors >= dc->sequential_cutoff >> 9) {
+               trace_bcache_bypass_sequential(bio);
+               goto skip;
+       }
+
+       if (congested && sectors >= congested) {
+               trace_bcache_bypass_congested(bio);
+               goto skip;
+       }
+
+rescale:
+       bch_rescale_priorities(c, bio_sectors(bio));
+       return false;
+skip:
+       bch_mark_sectors_bypassed(c, dc, bio_sectors(bio));
+       return true;
 }
 
-void bch_cache_read_endio(struct bio *bio, int error)
+/* Cache lookup */
+
+struct search {
+       /* Stack frame for bio_complete */
+       struct closure          cl;
+
+       struct bcache_device    *d;
+
+       struct bbio             bio;
+       struct bio              *orig_bio;
+       struct bio              *cache_miss;
+
+       unsigned                insert_bio_sectors;
+
+       unsigned                recoverable:1;
+       unsigned                unaligned_bvec:1;
+       unsigned                write:1;
+       unsigned                read_dirty_data:1;
+
+       unsigned long           start_time;
+
+       struct btree_op         op;
+       struct data_insert_op   iop;
+};
+
+static void bch_cache_read_endio(struct bio *bio, int error)
 {
        struct bbio *b = container_of(bio, struct bbio, bio);
        struct closure *cl = bio->bi_private;
@@ -650,13 +630,113 @@ void bch_cache_read_endio(struct bio *bio, int error)
         */
 
        if (error)
-               s->error = error;
-       else if (ptr_stale(s->op.c, &b->key, 0)) {
-               atomic_long_inc(&s->op.c->cache_read_races);
-               s->error = -EINTR;
+               s->iop.error = error;
+       else if (ptr_stale(s->iop.c, &b->key, 0)) {
+               atomic_long_inc(&s->iop.c->cache_read_races);
+               s->iop.error = -EINTR;
        }
 
-       bch_bbio_endio(s->op.c, bio, error, "reading from cache");
+       bch_bbio_endio(s->iop.c, bio, error, "reading from cache");
+}
+
+/*
+ * Read from a single key, handling the initial cache miss if the key starts in
+ * the middle of the bio
+ */
+static int cache_lookup_fn(struct btree_op *op, struct btree *b, struct bkey *k)
+{
+       struct search *s = container_of(op, struct search, op);
+       struct bio *n, *bio = &s->bio.bio;
+       struct bkey *bio_key;
+       unsigned ptr;
+
+       if (bkey_cmp(k, &KEY(s->iop.inode, bio->bi_sector, 0)) <= 0)
+               return MAP_CONTINUE;
+
+       if (KEY_INODE(k) != s->iop.inode ||
+           KEY_START(k) > bio->bi_sector) {
+               unsigned bio_sectors = bio_sectors(bio);
+               unsigned sectors = KEY_INODE(k) == s->iop.inode
+                       ? min_t(uint64_t, INT_MAX,
+                               KEY_START(k) - bio->bi_sector)
+                       : INT_MAX;
+
+               int ret = s->d->cache_miss(b, s, bio, sectors);
+               if (ret != MAP_CONTINUE)
+                       return ret;
+
+               /* if this was a complete miss we shouldn't get here */
+               BUG_ON(bio_sectors <= sectors);
+       }
+
+       if (!KEY_SIZE(k))
+               return MAP_CONTINUE;
+
+       /* XXX: figure out best pointer - for multiple cache devices */
+       ptr = 0;
+
+       PTR_BUCKET(b->c, k, ptr)->prio = INITIAL_PRIO;
+
+       if (KEY_DIRTY(k))
+               s->read_dirty_data = true;
+
+       n = bch_bio_split(bio, min_t(uint64_t, INT_MAX,
+                                    KEY_OFFSET(k) - bio->bi_sector),
+                         GFP_NOIO, s->d->bio_split);
+
+       bio_key = &container_of(n, struct bbio, bio)->key;
+       bch_bkey_copy_single_ptr(bio_key, k, ptr);
+
+       bch_cut_front(&KEY(s->iop.inode, n->bi_sector, 0), bio_key);
+       bch_cut_back(&KEY(s->iop.inode, bio_end_sector(n), 0), bio_key);
+
+       n->bi_end_io    = bch_cache_read_endio;
+       n->bi_private   = &s->cl;
+
+       /*
+        * The bucket we're reading from might be reused while our bio
+        * is in flight, and we could then end up reading the wrong
+        * data.
+        *
+        * We guard against this by checking (in cache_read_endio()) if
+        * the pointer is stale again; if so, we treat it as an error
+        * and reread from the backing device (but we don't pass that
+        * error up anywhere).
+        */
+
+       __bch_submit_bbio(n, b->c);
+       return n == bio ? MAP_DONE : MAP_CONTINUE;
+}
+
+static void cache_lookup(struct closure *cl)
+{
+       struct search *s = container_of(cl, struct search, iop.cl);
+       struct bio *bio = &s->bio.bio;
+
+       int ret = bch_btree_map_keys(&s->op, s->iop.c,
+                                    &KEY(s->iop.inode, bio->bi_sector, 0),
+                                    cache_lookup_fn, MAP_END_KEY);
+       if (ret == -EAGAIN)
+               continue_at(cl, cache_lookup, bcache_wq);
+
+       closure_return(cl);
+}
+
+/* Common code for the make_request functions */
+
+static void request_endio(struct bio *bio, int error)
+{
+       struct closure *cl = bio->bi_private;
+
+       if (error) {
+               struct search *s = container_of(cl, struct search, cl);
+               s->iop.error = error;
+               /* Only cache read errors are recoverable */
+               s->recoverable = false;
+       }
+
+       bio_put(bio);
+       closure_put(cl);
 }
 
 static void bio_complete(struct search *s)
@@ -670,8 +750,8 @@ static void bio_complete(struct search *s)
                part_stat_add(cpu, &s->d->disk->part0, ticks[rw], duration);
                part_stat_unlock();
 
-               trace_bcache_request_end(s, s->orig_bio);
-               bio_endio(s->orig_bio, s->error);
+               trace_bcache_request_end(s->d, s->orig_bio);
+               bio_endio(s->orig_bio, s->iop.error);
                s->orig_bio = NULL;
        }
 }
@@ -691,8 +771,8 @@ static void search_free(struct closure *cl)
        struct search *s = container_of(cl, struct search, cl);
        bio_complete(s);
 
-       if (s->op.cache_bio)
-               bio_put(s->op.cache_bio);
+       if (s->iop.bio)
+               bio_put(s->iop.bio);
 
        if (s->unaligned_bvec)
                mempool_free(s->bio.bio.bi_io_vec, s->d->unaligned_bvec);
@@ -703,21 +783,22 @@ static void search_free(struct closure *cl)
 
 static struct search *search_alloc(struct bio *bio, struct bcache_device *d)
 {
+       struct search *s;
        struct bio_vec *bv;
-       struct search *s = mempool_alloc(d->c->search, GFP_NOIO);
-       memset(s, 0, offsetof(struct search, op.keys));
+
+       s = mempool_alloc(d->c->search, GFP_NOIO);
+       memset(s, 0, offsetof(struct search, iop.insert_keys));
 
        __closure_init(&s->cl, NULL);
 
-       s->op.inode             = d->id;
-       s->op.c                 = d->c;
+       s->iop.inode            = d->id;
+       s->iop.c                = d->c;
        s->d                    = d;
        s->op.lock              = -1;
-       s->task                 = current;
+       s->iop.write_point      = hash_long((unsigned long) current, 16);
        s->orig_bio             = bio;
        s->write                = (bio->bi_rw & REQ_WRITE) != 0;
-       s->op.flush_journal     = (bio->bi_rw & (REQ_FLUSH|REQ_FUA)) != 0;
-       s->op.skip              = (bio->bi_rw & REQ_DISCARD) != 0;
+       s->iop.flush_journal    = (bio->bi_rw & (REQ_FLUSH|REQ_FUA)) != 0;
        s->recoverable          = 1;
        s->start_time           = jiffies;
        do_bio_hook(s);
@@ -734,18 +815,6 @@ static struct search *search_alloc(struct bio *bio, struct bcache_device *d)
        return s;
 }
 
-static void btree_read_async(struct closure *cl)
-{
-       struct btree_op *op = container_of(cl, struct btree_op, cl);
-
-       int ret = btree_root(search_recurse, op->c, op);
-
-       if (ret == -EAGAIN)
-               continue_at(cl, btree_read_async, bcache_wq);
-
-       closure_return(cl);
-}
-
 /* Cached devices */
 
 static void cached_dev_bio_complete(struct closure *cl)
@@ -759,27 +828,28 @@ static void cached_dev_bio_complete(struct closure *cl)
 
 /* Process reads */
 
-static void cached_dev_read_complete(struct closure *cl)
+static void cached_dev_cache_miss_done(struct closure *cl)
 {
        struct search *s = container_of(cl, struct search, cl);
 
-       if (s->op.insert_collision)
-               bch_mark_cache_miss_collision(s);
+       if (s->iop.replace_collision)
+               bch_mark_cache_miss_collision(s->iop.c, s->d);
 
-       if (s->op.cache_bio) {
+       if (s->iop.bio) {
                int i;
                struct bio_vec *bv;
 
-               __bio_for_each_segment(bv, s->op.cache_bio, i, 0)
+               bio_for_each_segment_all(bv, s->iop.bio, i)
                        __free_page(bv->bv_page);
        }
 
        cached_dev_bio_complete(cl);
 }
 
-static void request_read_error(struct closure *cl)
+static void cached_dev_read_error(struct closure *cl)
 {
        struct search *s = container_of(cl, struct search, cl);
+       struct bio *bio = &s->bio.bio;
        struct bio_vec *bv;
        int i;
 
@@ -787,7 +857,7 @@ static void request_read_error(struct closure *cl)
                /* Retry from the backing device: */
                trace_bcache_read_retry(s->orig_bio);
 
-               s->error = 0;
+               s->iop.error = 0;
                bv = s->bio.bio.bi_io_vec;
                do_bio_hook(s);
                s->bio.bio.bi_io_vec = bv;
@@ -803,146 +873,148 @@ static void request_read_error(struct closure *cl)
 
                /* XXX: invalidate cache */
 
-               closure_bio_submit(&s->bio.bio, &s->cl, s->d);
+               closure_bio_submit(bio, cl, s->d);
        }
 
-       continue_at(cl, cached_dev_read_complete, NULL);
+       continue_at(cl, cached_dev_cache_miss_done, NULL);
 }
 
-static void request_read_done(struct closure *cl)
+static void cached_dev_read_done(struct closure *cl)
 {
        struct search *s = container_of(cl, struct search, cl);
        struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
 
        /*
-        * s->cache_bio != NULL implies that we had a cache miss; cache_bio now
-        * contains data ready to be inserted into the cache.
+        * We had a cache miss; cache_bio now contains data ready to be inserted
+        * into the cache.
         *
         * First, we copy the data we just read from cache_bio's bounce buffers
         * to the buffers the original bio pointed to:
         */
 
-       if (s->op.cache_bio) {
-               bio_reset(s->op.cache_bio);
-               s->op.cache_bio->bi_sector      = s->cache_miss->bi_sector;
-               s->op.cache_bio->bi_bdev        = s->cache_miss->bi_bdev;
-               s->op.cache_bio->bi_size        = s->cache_bio_sectors << 9;
-               bch_bio_map(s->op.cache_bio, NULL);
+       if (s->iop.bio) {
+               bio_reset(s->iop.bio);
+               s->iop.bio->bi_sector = s->cache_miss->bi_sector;
+               s->iop.bio->bi_bdev = s->cache_miss->bi_bdev;
+               s->iop.bio->bi_size = s->insert_bio_sectors << 9;
+               bch_bio_map(s->iop.bio, NULL);
 
-               bio_copy_data(s->cache_miss, s->op.cache_bio);
+               bio_copy_data(s->cache_miss, s->iop.bio);
 
                bio_put(s->cache_miss);
                s->cache_miss = NULL;
        }
 
-       if (verify(dc, &s->bio.bio) && s->recoverable)
-               bch_data_verify(s);
+       if (verify(dc, &s->bio.bio) && s->recoverable &&
+           !s->unaligned_bvec && !s->read_dirty_data)
+               bch_data_verify(dc, s->orig_bio);
 
        bio_complete(s);
 
-       if (s->op.cache_bio &&
-           !test_bit(CACHE_SET_STOPPING, &s->op.c->flags)) {
-               s->op.type = BTREE_REPLACE;
-               closure_call(&s->op.cl, bch_insert_data, NULL, cl);
+       if (s->iop.bio &&
+           !test_bit(CACHE_SET_STOPPING, &s->iop.c->flags)) {
+               BUG_ON(!s->iop.replace);
+               closure_call(&s->iop.cl, bch_data_insert, NULL, cl);
        }
 
-       continue_at(cl, cached_dev_read_complete, NULL);
+       continue_at(cl, cached_dev_cache_miss_done, NULL);
 }
 
-static void request_read_done_bh(struct closure *cl)
+static void cached_dev_read_done_bh(struct closure *cl)
 {
        struct search *s = container_of(cl, struct search, cl);
        struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
 
-       bch_mark_cache_accounting(s, !s->cache_miss, s->op.skip);
-       trace_bcache_read(s->orig_bio, !s->cache_miss, s->op.skip);
+       bch_mark_cache_accounting(s->iop.c, s->d,
+                                 !s->cache_miss, s->iop.bypass);
+       trace_bcache_read(s->orig_bio, !s->cache_miss, s->iop.bypass);
 
-       if (s->error)
-               continue_at_nobarrier(cl, request_read_error, bcache_wq);
-       else if (s->op.cache_bio || verify(dc, &s->bio.bio))
-               continue_at_nobarrier(cl, request_read_done, bcache_wq);
+       if (s->iop.error)
+               continue_at_nobarrier(cl, cached_dev_read_error, bcache_wq);
+       else if (s->iop.bio || verify(dc, &s->bio.bio))
+               continue_at_nobarrier(cl, cached_dev_read_done, bcache_wq);
        else
-               continue_at_nobarrier(cl, cached_dev_read_complete, NULL);
+               continue_at_nobarrier(cl, cached_dev_bio_complete, NULL);
 }
 
 static int cached_dev_cache_miss(struct btree *b, struct search *s,
                                 struct bio *bio, unsigned sectors)
 {
-       int ret = 0;
-       unsigned reada;
+       int ret = MAP_CONTINUE;
+       unsigned reada = 0;
        struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
-       struct bio *miss;
-
-       miss = bch_bio_split(bio, sectors, GFP_NOIO, s->d->bio_split);
-       if (miss == bio)
-               s->op.lookup_done = true;
+       struct bio *miss, *cache_bio;
 
-       miss->bi_end_io         = request_endio;
-       miss->bi_private        = &s->cl;
-
-       if (s->cache_miss || s->op.skip)
+       if (s->cache_miss || s->iop.bypass) {
+               miss = bch_bio_split(bio, sectors, GFP_NOIO, s->d->bio_split);
+               ret = miss == bio ? MAP_DONE : MAP_CONTINUE;
                goto out_submit;
-
-       if (miss != bio ||
-           (bio->bi_rw & REQ_RAHEAD) ||
-           (bio->bi_rw & REQ_META) ||
-           s->op.c->gc_stats.in_use >= CUTOFF_CACHE_READA)
-               reada = 0;
-       else {
-               reada = min(dc->readahead >> 9,
-                           sectors - bio_sectors(miss));
-
-               if (bio_end_sector(miss) + reada > bdev_sectors(miss->bi_bdev))
-                       reada = bdev_sectors(miss->bi_bdev) -
-                               bio_end_sector(miss);
        }
 
-       s->cache_bio_sectors = bio_sectors(miss) + reada;
-       s->op.cache_bio = bio_alloc_bioset(GFP_NOWAIT,
-                       DIV_ROUND_UP(s->cache_bio_sectors, PAGE_SECTORS),
-                       dc->disk.bio_split);
+       if (!(bio->bi_rw & REQ_RAHEAD) &&
+           !(bio->bi_rw & REQ_META) &&
+           s->iop.c->gc_stats.in_use < CUTOFF_CACHE_READA)
+               reada = min_t(sector_t, dc->readahead >> 9,
+                             bdev_sectors(bio->bi_bdev) - bio_end_sector(bio));
 
-       if (!s->op.cache_bio)
-               goto out_submit;
+       s->insert_bio_sectors = min(sectors, bio_sectors(bio) + reada);
 
-       s->op.cache_bio->bi_sector      = miss->bi_sector;
-       s->op.cache_bio->bi_bdev        = miss->bi_bdev;
-       s->op.cache_bio->bi_size        = s->cache_bio_sectors << 9;
+       s->iop.replace_key = KEY(s->iop.inode,
+                                bio->bi_sector + s->insert_bio_sectors,
+                                s->insert_bio_sectors);
 
-       s->op.cache_bio->bi_end_io      = request_endio;
-       s->op.cache_bio->bi_private     = &s->cl;
+       ret = bch_btree_insert_check_key(b, &s->op, &s->iop.replace_key);
+       if (ret)
+               return ret;
+
+       s->iop.replace = true;
+
+       miss = bch_bio_split(bio, sectors, GFP_NOIO, s->d->bio_split);
 
        /* btree_search_recurse()'s btree iterator is no good anymore */
-       ret = -EINTR;
-       if (!bch_btree_insert_check_key(b, &s->op, s->op.cache_bio))
-               goto out_put;
+       ret = miss == bio ? MAP_DONE : -EINTR;
+
+       cache_bio = bio_alloc_bioset(GFP_NOWAIT,
+                       DIV_ROUND_UP(s->insert_bio_sectors, PAGE_SECTORS),
+                       dc->disk.bio_split);
+       if (!cache_bio)
+               goto out_submit;
+
+       cache_bio->bi_sector    = miss->bi_sector;
+       cache_bio->bi_bdev      = miss->bi_bdev;
+       cache_bio->bi_size      = s->insert_bio_sectors << 9;
+
+       cache_bio->bi_end_io    = request_endio;
+       cache_bio->bi_private   = &s->cl;
 
-       bch_bio_map(s->op.cache_bio, NULL);
-       if (bio_alloc_pages(s->op.cache_bio, __GFP_NOWARN|GFP_NOIO))
+       bch_bio_map(cache_bio, NULL);
+       if (bio_alloc_pages(cache_bio, __GFP_NOWARN|GFP_NOIO))
                goto out_put;
 
-       s->cache_miss = miss;
-       bio_get(s->op.cache_bio);
+       if (reada)
+               bch_mark_cache_readahead(s->iop.c, s->d);
 
-       closure_bio_submit(s->op.cache_bio, &s->cl, s->d);
+       s->cache_miss   = miss;
+       s->iop.bio      = cache_bio;
+       bio_get(cache_bio);
+       closure_bio_submit(cache_bio, &s->cl, s->d);
 
        return ret;
 out_put:
-       bio_put(s->op.cache_bio);
-       s->op.cache_bio = NULL;
+       bio_put(cache_bio);
 out_submit:
+       miss->bi_end_io         = request_endio;
+       miss->bi_private        = &s->cl;
        closure_bio_submit(miss, &s->cl, s->d);
        return ret;
 }
 
-static void request_read(struct cached_dev *dc, struct search *s)
+static void cached_dev_read(struct cached_dev *dc, struct search *s)
 {
        struct closure *cl = &s->cl;
 
-       check_should_skip(dc, s);
-       closure_call(&s->op.cl, btree_read_async, NULL, cl);
-
-       continue_at(cl, request_read_done_bh, NULL);
+       closure_call(&s->iop.cl, cache_lookup, NULL, cl);
+       continue_at(cl, cached_dev_read_done_bh, NULL);
 }
 
 /* Process writes */
@@ -956,47 +1028,52 @@ static void cached_dev_write_complete(struct closure *cl)
        cached_dev_bio_complete(cl);
 }
 
-static void request_write(struct cached_dev *dc, struct search *s)
+static void cached_dev_write(struct cached_dev *dc, struct search *s)
 {
        struct closure *cl = &s->cl;
        struct bio *bio = &s->bio.bio;
-       struct bkey start, end;
-       start = KEY(dc->disk.id, bio->bi_sector, 0);
-       end = KEY(dc->disk.id, bio_end_sector(bio), 0);
+       struct bkey start = KEY(dc->disk.id, bio->bi_sector, 0);
+       struct bkey end = KEY(dc->disk.id, bio_end_sector(bio), 0);
 
-       bch_keybuf_check_overlapping(&s->op.c->moving_gc_keys, &start, &end);
+       bch_keybuf_check_overlapping(&s->iop.c->moving_gc_keys, &start, &end);
 
-       check_should_skip(dc, s);
        down_read_non_owner(&dc->writeback_lock);
-
        if (bch_keybuf_check_overlapping(&dc->writeback_keys, &start, &end)) {
-               s->op.skip      = false;
-               s->writeback    = true;
+               /*
+                * We overlap with some dirty data undergoing background
+                * writeback, force this write to writeback
+                */
+               s->iop.bypass = false;
+               s->iop.writeback = true;
        }
 
+       /*
+        * Discards aren't _required_ to do anything, so skipping if
+        * check_overlapping returned true is ok
+        *
+        * But check_overlapping drops dirty keys for which io hasn't started,
+        * so we still want to call it.
+        */
        if (bio->bi_rw & REQ_DISCARD)
-               goto skip;
+               s->iop.bypass = true;
 
        if (should_writeback(dc, s->orig_bio,
                             cache_mode(dc, bio),
-                            s->op.skip)) {
-               s->op.skip = false;
-               s->writeback = true;
+                            s->iop.bypass)) {
+               s->iop.bypass = false;
+               s->iop.writeback = true;
        }
 
-       if (s->op.skip)
-               goto skip;
-
-       trace_bcache_write(s->orig_bio, s->writeback, s->op.skip);
+       if (s->iop.bypass) {
+               s->iop.bio = s->orig_bio;
+               bio_get(s->iop.bio);
 
-       if (!s->writeback) {
-               s->op.cache_bio = bio_clone_bioset(bio, GFP_NOIO,
-                                                  dc->disk.bio_split);
-
-               closure_bio_submit(bio, cl, s->d);
-       } else {
+               if (!(bio->bi_rw & REQ_DISCARD) ||
+                   blk_queue_discard(bdev_get_queue(dc->bdev)))
+                       closure_bio_submit(bio, cl, s->d);
+       } else if (s->iop.writeback) {
                bch_writeback_add(dc);
-               s->op.cache_bio = bio;
+               s->iop.bio = bio;
 
                if (bio->bi_rw & REQ_FLUSH) {
                        /* Also need to send a flush to the backing device */
@@ -1010,36 +1087,26 @@ static void request_write(struct cached_dev *dc, struct search *s)
 
                        closure_bio_submit(flush, cl, s->d);
                }
-       }
-out:
-       closure_call(&s->op.cl, bch_insert_data, NULL, cl);
-       continue_at(cl, cached_dev_write_complete, NULL);
-skip:
-       s->op.skip = true;
-       s->op.cache_bio = s->orig_bio;
-       bio_get(s->op.cache_bio);
+       } else {
+               s->iop.bio = bio_clone_bioset(bio, GFP_NOIO,
+                                             dc->disk.bio_split);
 
-       if ((bio->bi_rw & REQ_DISCARD) &&
-           !blk_queue_discard(bdev_get_queue(dc->bdev)))
-               goto out;
+               closure_bio_submit(bio, cl, s->d);
+       }
 
-       closure_bio_submit(bio, cl, s->d);
-       goto out;
+       closure_call(&s->iop.cl, bch_data_insert, NULL, cl);
+       continue_at(cl, cached_dev_write_complete, NULL);
 }
 
-static void request_nodata(struct cached_dev *dc, struct search *s)
+static void cached_dev_nodata(struct closure *cl)
 {
-       struct closure *cl = &s->cl;
+       struct search *s = container_of(cl, struct search, cl);
        struct bio *bio = &s->bio.bio;
 
-       if (bio->bi_rw & REQ_DISCARD) {
-               request_write(dc, s);
-               return;
-       }
-
-       if (s->op.flush_journal)
-               bch_journal_meta(s->op.c, cl);
+       if (s->iop.flush_journal)
+               bch_journal_meta(s->iop.c, cl);
 
+       /* If it's a flush, we send the flush to the backing device too */
        closure_bio_submit(bio, cl, s->d);
 
        continue_at(cl, cached_dev_bio_complete, NULL);
@@ -1047,134 +1114,6 @@ static void request_nodata(struct cached_dev *dc, struct search *s)
 
 /* Cached devices - read & write stuff */
 
-unsigned bch_get_congested(struct cache_set *c)
-{
-       int i;
-       long rand;
-
-       if (!c->congested_read_threshold_us &&
-           !c->congested_write_threshold_us)
-               return 0;
-
-       i = (local_clock_us() - c->congested_last_us) / 1024;
-       if (i < 0)
-               return 0;
-
-       i += atomic_read(&c->congested);
-       if (i >= 0)
-               return 0;
-
-       i += CONGESTED_MAX;
-
-       if (i > 0)
-               i = fract_exp_two(i, 6);
-
-       rand = get_random_int();
-       i -= bitmap_weight(&rand, BITS_PER_LONG);
-
-       return i > 0 ? i : 1;
-}
-
-static void add_sequential(struct task_struct *t)
-{
-       ewma_add(t->sequential_io_avg,
-                t->sequential_io, 8, 0);
-
-       t->sequential_io = 0;
-}
-
-static struct hlist_head *iohash(struct cached_dev *dc, uint64_t k)
-{
-       return &dc->io_hash[hash_64(k, RECENT_IO_BITS)];
-}
-
-static void check_should_skip(struct cached_dev *dc, struct search *s)
-{
-       struct cache_set *c = s->op.c;
-       struct bio *bio = &s->bio.bio;
-       unsigned mode = cache_mode(dc, bio);
-       unsigned sectors, congested = bch_get_congested(c);
-
-       if (atomic_read(&dc->disk.detaching) ||
-           c->gc_stats.in_use > CUTOFF_CACHE_ADD ||
-           (bio->bi_rw & REQ_DISCARD))
-               goto skip;
-
-       if (mode == CACHE_MODE_NONE ||
-           (mode == CACHE_MODE_WRITEAROUND &&
-            (bio->bi_rw & REQ_WRITE)))
-               goto skip;
-
-       if (bio->bi_sector   & (c->sb.block_size - 1) ||
-           bio_sectors(bio) & (c->sb.block_size - 1)) {
-               pr_debug("skipping unaligned io");
-               goto skip;
-       }
-
-       if (!congested && !dc->sequential_cutoff)
-               goto rescale;
-
-       if (!congested &&
-           mode == CACHE_MODE_WRITEBACK &&
-           (bio->bi_rw & REQ_WRITE) &&
-           (bio->bi_rw & REQ_SYNC))
-               goto rescale;
-
-       if (dc->sequential_merge) {
-               struct io *i;
-
-               spin_lock(&dc->io_lock);
-
-               hlist_for_each_entry(i, iohash(dc, bio->bi_sector), hash)
-                       if (i->last == bio->bi_sector &&
-                           time_before(jiffies, i->jiffies))
-                               goto found;
-
-               i = list_first_entry(&dc->io_lru, struct io, lru);
-
-               add_sequential(s->task);
-               i->sequential = 0;
-found:
-               if (i->sequential + bio->bi_size > i->sequential)
-                       i->sequential   += bio->bi_size;
-
-               i->last                  = bio_end_sector(bio);
-               i->jiffies               = jiffies + msecs_to_jiffies(5000);
-               s->task->sequential_io   = i->sequential;
-
-               hlist_del(&i->hash);
-               hlist_add_head(&i->hash, iohash(dc, i->last));
-               list_move_tail(&i->lru, &dc->io_lru);
-
-               spin_unlock(&dc->io_lock);
-       } else {
-               s->task->sequential_io = bio->bi_size;
-
-               add_sequential(s->task);
-       }
-
-       sectors = max(s->task->sequential_io,
-                     s->task->sequential_io_avg) >> 9;
-
-       if (dc->sequential_cutoff &&
-           sectors >= dc->sequential_cutoff >> 9) {
-               trace_bcache_bypass_sequential(s->orig_bio);
-               goto skip;
-       }
-
-       if (congested && sectors >= congested) {
-               trace_bcache_bypass_congested(s->orig_bio);
-               goto skip;
-       }
-
-rescale:
-       bch_rescale_priorities(c, bio_sectors(bio));
-       return;
-skip:
-       bch_mark_sectors_bypassed(s, bio_sectors(bio));
-       s->op.skip = true;
-}
-
 static void cached_dev_make_request(struct request_queue *q, struct bio *bio)
 {
        struct search *s;
@@ -1192,14 +1131,24 @@ static void cached_dev_make_request(struct request_queue *q, struct bio *bio)
 
        if (cached_dev_get(dc)) {
                s = search_alloc(bio, d);
-               trace_bcache_request_start(s, bio);
-
-               if (!bio_has_data(bio))
-                       request_nodata(dc, s);
-               else if (rw)
-                       request_write(dc, s);
-               else
-                       request_read(dc, s);
+               trace_bcache_request_start(s->d, bio);
+
+               if (!bio->bi_size) {
+                       /*
+                        * can't call bch_journal_meta from under
+                        * generic_make_request
+                        */
+                       continue_at_nobarrier(&s->cl,
+                                             cached_dev_nodata,
+                                             bcache_wq);
+               } else {
+                       s->iop.bypass = check_should_bypass(dc, bio);
+
+                       if (rw)
+                               cached_dev_write(dc, s);
+                       else
+                               cached_dev_read(dc, s);
+               }
        } else {
                if ((bio->bi_rw & REQ_DISCARD) &&
                    !blk_queue_discard(bdev_get_queue(dc->bdev)))
@@ -1274,9 +1223,19 @@ static int flash_dev_cache_miss(struct btree *b, struct search *s,
        bio_advance(bio, min(sectors << 9, bio->bi_size));
 
        if (!bio->bi_size)
-               s->op.lookup_done = true;
+               return MAP_DONE;
 
-       return 0;
+       return MAP_CONTINUE;
+}
+
+static void flash_dev_nodata(struct closure *cl)
+{
+       struct search *s = container_of(cl, struct search, cl);
+
+       if (s->iop.flush_journal)
+               bch_journal_meta(s->iop.c, cl);
+
+       continue_at(cl, search_free, NULL);
 }
 
 static void flash_dev_make_request(struct request_queue *q, struct bio *bio)
@@ -1295,23 +1254,28 @@ static void flash_dev_make_request(struct request_queue *q, struct bio *bio)
        cl = &s->cl;
        bio = &s->bio.bio;
 
-       trace_bcache_request_start(s, bio);
+       trace_bcache_request_start(s->d, bio);
 
-       if (bio_has_data(bio) && !rw) {
-               closure_call(&s->op.cl, btree_read_async, NULL, cl);
-       } else if (bio_has_data(bio) || s->op.skip) {
-               bch_keybuf_check_overlapping(&s->op.c->moving_gc_keys,
+       if (!bio->bi_size) {
+               /*
+                * can't call bch_journal_meta from under
+                * generic_make_request
+                */
+               continue_at_nobarrier(&s->cl,
+                                     flash_dev_nodata,
+                                     bcache_wq);
+       } else if (rw) {
+               bch_keybuf_check_overlapping(&s->iop.c->moving_gc_keys,
                                        &KEY(d->id, bio->bi_sector, 0),
                                        &KEY(d->id, bio_end_sector(bio), 0));
 
-               s->writeback    = true;
-               s->op.cache_bio = bio;
+               s->iop.bypass           = (bio->bi_rw & REQ_DISCARD) != 0;
+               s->iop.writeback        = true;
+               s->iop.bio              = bio;
 
-               closure_call(&s->op.cl, bch_insert_data, NULL, cl);
+               closure_call(&s->iop.cl, bch_data_insert, NULL, cl);
        } else {
-               /* No data - probably a cache flush */
-               if (s->op.flush_journal)
-                       bch_journal_meta(s->op.c, cl);
+               closure_call(&s->iop.cl, cache_lookup, NULL, cl);
        }
 
        continue_at(cl, search_free, NULL);
index 57dc478..2cd65bf 100644 (file)
@@ -3,40 +3,33 @@
 
 #include <linux/cgroup.h>
 
-struct search {
-       /* Stack frame for bio_complete */
+struct data_insert_op {
        struct closure          cl;
+       struct cache_set        *c;
+       struct bio              *bio;
 
-       struct bcache_device    *d;
-       struct task_struct      *task;
-
-       struct bbio             bio;
-       struct bio              *orig_bio;
-       struct bio              *cache_miss;
-       unsigned                cache_bio_sectors;
-
-       unsigned                recoverable:1;
-       unsigned                unaligned_bvec:1;
+       unsigned                inode;
+       uint16_t                write_point;
+       uint16_t                write_prio;
+       short                   error;
 
-       unsigned                write:1;
+       unsigned                bypass:1;
        unsigned                writeback:1;
+       unsigned                flush_journal:1;
+       unsigned                csum:1;
 
-       /* IO error returned to s->bio */
-       short                   error;
-       unsigned long           start_time;
+       unsigned                replace:1;
+       unsigned                replace_collision:1;
+
+       unsigned                insert_data_done:1;
 
-       /* Anything past op->keys won't get zeroed in do_bio_hook */
-       struct btree_op         op;
+       /* Anything past this point won't get zeroed in search_alloc() */
+       struct keylist          insert_keys;
+       BKEY_PADDED(replace_key);
 };
 
-void bch_cache_read_endio(struct bio *, int);
 unsigned bch_get_congested(struct cache_set *);
-void bch_insert_data(struct closure *cl);
-void bch_btree_insert_async(struct closure *);
-void bch_cache_read_endio(struct bio *, int);
-
-void bch_open_buckets_free(struct cache_set *);
-int bch_open_buckets_alloc(struct cache_set *);
+void bch_data_insert(struct closure *cl);
 
 void bch_cached_dev_request_init(struct cached_dev *dc);
 void bch_flash_dev_request_init(struct bcache_device *d);
index b8730e7..84d0782 100644 (file)
@@ -7,7 +7,6 @@
 #include "bcache.h"
 #include "stats.h"
 #include "btree.h"
-#include "request.h"
 #include "sysfs.h"
 
 /*
@@ -196,35 +195,36 @@ static void mark_cache_stats(struct cache_stat_collector *stats,
                        atomic_inc(&stats->cache_bypass_misses);
 }
 
-void bch_mark_cache_accounting(struct search *s, bool hit, bool bypass)
+void bch_mark_cache_accounting(struct cache_set *c, struct bcache_device *d,
+                              bool hit, bool bypass)
 {
-       struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+       struct cached_dev *dc = container_of(d, struct cached_dev, disk);
        mark_cache_stats(&dc->accounting.collector, hit, bypass);
-       mark_cache_stats(&s->op.c->accounting.collector, hit, bypass);
+       mark_cache_stats(&c->accounting.collector, hit, bypass);
 #ifdef CONFIG_CGROUP_BCACHE
        mark_cache_stats(&(bch_bio_to_cgroup(s->orig_bio)->stats), hit, bypass);
 #endif
 }
 
-void bch_mark_cache_readahead(struct search *s)
+void bch_mark_cache_readahead(struct cache_set *c, struct bcache_device *d)
 {
-       struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+       struct cached_dev *dc = container_of(d, struct cached_dev, disk);
        atomic_inc(&dc->accounting.collector.cache_readaheads);
-       atomic_inc(&s->op.c->accounting.collector.cache_readaheads);
+       atomic_inc(&c->accounting.collector.cache_readaheads);
 }
 
-void bch_mark_cache_miss_collision(struct search *s)
+void bch_mark_cache_miss_collision(struct cache_set *c, struct bcache_device *d)
 {
-       struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+       struct cached_dev *dc = container_of(d, struct cached_dev, disk);
        atomic_inc(&dc->accounting.collector.cache_miss_collisions);
-       atomic_inc(&s->op.c->accounting.collector.cache_miss_collisions);
+       atomic_inc(&c->accounting.collector.cache_miss_collisions);
 }
 
-void bch_mark_sectors_bypassed(struct search *s, int sectors)
+void bch_mark_sectors_bypassed(struct cache_set *c, struct cached_dev *dc,
+                              int sectors)
 {
-       struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
        atomic_add(sectors, &dc->accounting.collector.sectors_bypassed);
-       atomic_add(sectors, &s->op.c->accounting.collector.sectors_bypassed);
+       atomic_add(sectors, &c->accounting.collector.sectors_bypassed);
 }
 
 void bch_cache_accounting_init(struct cache_accounting *acc,
index c7c7a8f..adbff14 100644 (file)
@@ -38,7 +38,9 @@ struct cache_accounting {
        struct cache_stats day;
 };
 
-struct search;
+struct cache_set;
+struct cached_dev;
+struct bcache_device;
 
 void bch_cache_accounting_init(struct cache_accounting *acc,
                               struct closure *parent);
@@ -50,9 +52,10 @@ void bch_cache_accounting_clear(struct cache_accounting *acc);
 
 void bch_cache_accounting_destroy(struct cache_accounting *acc);
 
-void bch_mark_cache_accounting(struct search *s, bool hit, bool bypass);
-void bch_mark_cache_readahead(struct search *s);
-void bch_mark_cache_miss_collision(struct search *s);
-void bch_mark_sectors_bypassed(struct search *s, int sectors);
+void bch_mark_cache_accounting(struct cache_set *, struct bcache_device *,
+                              bool, bool);
+void bch_mark_cache_readahead(struct cache_set *, struct bcache_device *);
+void bch_mark_cache_miss_collision(struct cache_set *, struct bcache_device *);
+void bch_mark_sectors_bypassed(struct cache_set *, struct cached_dev *, int);
 
 #endif /* _BCACHE_STATS_H_ */
index 547c4c5..dec15cd 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/buffer_head.h>
 #include <linux/debugfs.h>
 #include <linux/genhd.h>
+#include <linux/idr.h>
 #include <linux/kthread.h>
 #include <linux/module.h>
 #include <linux/random.h>
@@ -45,21 +46,13 @@ const char * const bch_cache_modes[] = {
        NULL
 };
 
-struct uuid_entry_v0 {
-       uint8_t         uuid[16];
-       uint8_t         label[32];
-       uint32_t        first_reg;
-       uint32_t        last_reg;
-       uint32_t        invalidated;
-       uint32_t        pad;
-};
-
 static struct kobject *bcache_kobj;
 struct mutex bch_register_lock;
 LIST_HEAD(bch_cache_sets);
 static LIST_HEAD(uncached_devices);
 
-static int bcache_major, bcache_minor;
+static int bcache_major;
+static DEFINE_IDA(bcache_minor);
 static wait_queue_head_t unregister_wait;
 struct workqueue_struct *bcache_wq;
 
@@ -382,7 +375,7 @@ static char *uuid_read(struct cache_set *c, struct jset *j, struct closure *cl)
 {
        struct bkey *k = &j->uuid_bucket;
 
-       if (__bch_ptr_invalid(c, 1, k))
+       if (bch_btree_ptr_invalid(c, k))
                return "bad uuid pointer";
 
        bkey_copy(&c->uuid_bucket, k);
@@ -427,7 +420,7 @@ static int __uuid_write(struct cache_set *c)
 
        lockdep_assert_held(&bch_register_lock);
 
-       if (bch_bucket_alloc_set(c, WATERMARK_METADATA, &k.key, 1, &cl))
+       if (bch_bucket_alloc_set(c, WATERMARK_METADATA, &k.key, 1, true))
                return 1;
 
        SET_KEY_SIZE(&k.key, c->sb.bucket_size);
@@ -435,7 +428,7 @@ static int __uuid_write(struct cache_set *c)
        closure_sync(&cl);
 
        bkey_copy(&c->uuid_bucket, &k.key);
-       __bkey_put(c, &k.key);
+       bkey_put(c, &k.key);
        return 0;
 }
 
@@ -562,10 +555,10 @@ void bch_prio_write(struct cache *ca)
                }
 
                p->next_bucket  = ca->prio_buckets[i + 1];
-               p->magic        = pset_magic(ca);
+               p->magic        = pset_magic(&ca->sb);
                p->csum         = bch_crc64(&p->magic, bucket_bytes(ca) - 8);
 
-               bucket = bch_bucket_alloc(ca, WATERMARK_PRIO, &cl);
+               bucket = bch_bucket_alloc(ca, WATERMARK_PRIO, true);
                BUG_ON(bucket == -1);
 
                mutex_unlock(&ca->set->bucket_lock);
@@ -613,7 +606,7 @@ static void prio_read(struct cache *ca, uint64_t bucket)
                        if (p->csum != bch_crc64(&p->magic, bucket_bytes(ca) - 8))
                                pr_warn("bad csum reading priorities");
 
-                       if (p->magic != pset_magic(ca))
+                       if (p->magic != pset_magic(&ca->sb))
                                pr_warn("bad magic reading priorities");
 
                        bucket = p->next_bucket;
@@ -630,7 +623,7 @@ static void prio_read(struct cache *ca, uint64_t bucket)
 static int open_dev(struct block_device *b, fmode_t mode)
 {
        struct bcache_device *d = b->bd_disk->private_data;
-       if (atomic_read(&d->closing))
+       if (test_bit(BCACHE_DEV_CLOSING, &d->flags))
                return -ENXIO;
 
        closure_get(&d->cl);
@@ -659,20 +652,24 @@ static const struct block_device_operations bcache_ops = {
 
 void bcache_device_stop(struct bcache_device *d)
 {
-       if (!atomic_xchg(&d->closing, 1))
+       if (!test_and_set_bit(BCACHE_DEV_CLOSING, &d->flags))
                closure_queue(&d->cl);
 }
 
 static void bcache_device_unlink(struct bcache_device *d)
 {
-       unsigned i;
-       struct cache *ca;
+       lockdep_assert_held(&bch_register_lock);
 
-       sysfs_remove_link(&d->c->kobj, d->name);
-       sysfs_remove_link(&d->kobj, "cache");
+       if (d->c && !test_and_set_bit(BCACHE_DEV_UNLINK_DONE, &d->flags)) {
+               unsigned i;
+               struct cache *ca;
 
-       for_each_cache(ca, d->c, i)
-               bd_unlink_disk_holder(ca->bdev, d->disk);
+               sysfs_remove_link(&d->c->kobj, d->name);
+               sysfs_remove_link(&d->kobj, "cache");
+
+               for_each_cache(ca, d->c, i)
+                       bd_unlink_disk_holder(ca->bdev, d->disk);
+       }
 }
 
 static void bcache_device_link(struct bcache_device *d, struct cache_set *c,
@@ -696,19 +693,16 @@ static void bcache_device_detach(struct bcache_device *d)
 {
        lockdep_assert_held(&bch_register_lock);
 
-       if (atomic_read(&d->detaching)) {
+       if (test_bit(BCACHE_DEV_DETACHING, &d->flags)) {
                struct uuid_entry *u = d->c->uuids + d->id;
 
                SET_UUID_FLASH_ONLY(u, 0);
                memcpy(u->uuid, invalid_uuid, 16);
                u->invalidated = cpu_to_le32(get_seconds());
                bch_uuid_write(d->c);
-
-               atomic_set(&d->detaching, 0);
        }
 
-       if (!d->flush_done)
-               bcache_device_unlink(d);
+       bcache_device_unlink(d);
 
        d->c->devices[d->id] = NULL;
        closure_put(&d->c->caching);
@@ -739,14 +733,20 @@ static void bcache_device_free(struct bcache_device *d)
                del_gendisk(d->disk);
        if (d->disk && d->disk->queue)
                blk_cleanup_queue(d->disk->queue);
-       if (d->disk)
+       if (d->disk) {
+               ida_simple_remove(&bcache_minor, d->disk->first_minor);
                put_disk(d->disk);
+       }
 
        bio_split_pool_free(&d->bio_split_hook);
        if (d->unaligned_bvec)
                mempool_destroy(d->unaligned_bvec);
        if (d->bio_split)
                bioset_free(d->bio_split);
+       if (is_vmalloc_addr(d->full_dirty_stripes))
+               vfree(d->full_dirty_stripes);
+       else
+               kfree(d->full_dirty_stripes);
        if (is_vmalloc_addr(d->stripe_sectors_dirty))
                vfree(d->stripe_sectors_dirty);
        else
@@ -760,15 +760,19 @@ static int bcache_device_init(struct bcache_device *d, unsigned block_size,
 {
        struct request_queue *q;
        size_t n;
+       int minor;
 
-       if (!d->stripe_size_bits)
-               d->stripe_size_bits = 31;
+       if (!d->stripe_size)
+               d->stripe_size = 1 << 31;
 
-       d->nr_stripes = round_up(sectors, 1 << d->stripe_size_bits) >>
-               d->stripe_size_bits;
+       d->nr_stripes = DIV_ROUND_UP_ULL(sectors, d->stripe_size);
 
-       if (!d->nr_stripes || d->nr_stripes > SIZE_MAX / sizeof(atomic_t))
+       if (!d->nr_stripes ||
+           d->nr_stripes > INT_MAX ||
+           d->nr_stripes > SIZE_MAX / sizeof(atomic_t)) {
+               pr_err("nr_stripes too large");
                return -ENOMEM;
+       }
 
        n = d->nr_stripes * sizeof(atomic_t);
        d->stripe_sectors_dirty = n < PAGE_SIZE << 6
@@ -777,22 +781,38 @@ static int bcache_device_init(struct bcache_device *d, unsigned block_size,
        if (!d->stripe_sectors_dirty)
                return -ENOMEM;
 
+       n = BITS_TO_LONGS(d->nr_stripes) * sizeof(unsigned long);
+       d->full_dirty_stripes = n < PAGE_SIZE << 6
+               ? kzalloc(n, GFP_KERNEL)
+               : vzalloc(n);
+       if (!d->full_dirty_stripes)
+               return -ENOMEM;
+
+       minor = ida_simple_get(&bcache_minor, 0, MINORMASK + 1, GFP_KERNEL);
+       if (minor < 0)
+               return minor;
+
        if (!(d->bio_split = bioset_create(4, offsetof(struct bbio, bio))) ||
            !(d->unaligned_bvec = mempool_create_kmalloc_pool(1,
                                sizeof(struct bio_vec) * BIO_MAX_PAGES)) ||
            bio_split_pool_init(&d->bio_split_hook) ||
-           !(d->disk = alloc_disk(1)) ||
-           !(q = blk_alloc_queue(GFP_KERNEL)))
+           !(d->disk = alloc_disk(1))) {
+               ida_simple_remove(&bcache_minor, minor);
                return -ENOMEM;
+       }
 
        set_capacity(d->disk, sectors);
-       snprintf(d->disk->disk_name, DISK_NAME_LEN, "bcache%i", bcache_minor);
+       snprintf(d->disk->disk_name, DISK_NAME_LEN, "bcache%i", minor);
 
        d->disk->major          = bcache_major;
-       d->disk->first_minor    = bcache_minor++;
+       d->disk->first_minor    = minor;
        d->disk->fops           = &bcache_ops;
        d->disk->private_data   = d;
 
+       q = blk_alloc_queue(GFP_KERNEL);
+       if (!q)
+               return -ENOMEM;
+
        blk_queue_make_request(q, NULL);
        d->disk->queue                  = q;
        q->queuedata                    = d;
@@ -874,7 +894,7 @@ static void cached_dev_detach_finish(struct work_struct *w)
        struct closure cl;
        closure_init_stack(&cl);
 
-       BUG_ON(!atomic_read(&dc->disk.detaching));
+       BUG_ON(!test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags));
        BUG_ON(atomic_read(&dc->count));
 
        mutex_lock(&bch_register_lock);
@@ -888,6 +908,8 @@ static void cached_dev_detach_finish(struct work_struct *w)
        bcache_device_detach(&dc->disk);
        list_move(&dc->list, &uncached_devices);
 
+       clear_bit(BCACHE_DEV_DETACHING, &dc->disk.flags);
+
        mutex_unlock(&bch_register_lock);
 
        pr_info("Caching disabled for %s", bdevname(dc->bdev, buf));
@@ -900,10 +922,10 @@ void bch_cached_dev_detach(struct cached_dev *dc)
 {
        lockdep_assert_held(&bch_register_lock);
 
-       if (atomic_read(&dc->disk.closing))
+       if (test_bit(BCACHE_DEV_CLOSING, &dc->disk.flags))
                return;
 
-       if (atomic_xchg(&dc->disk.detaching, 1))
+       if (test_and_set_bit(BCACHE_DEV_DETACHING, &dc->disk.flags))
                return;
 
        /*
@@ -1030,6 +1052,7 @@ static void cached_dev_free(struct closure *cl)
        struct cached_dev *dc = container_of(cl, struct cached_dev, disk.cl);
 
        cancel_delayed_work_sync(&dc->writeback_rate_update);
+       kthread_stop(dc->writeback_thread);
 
        mutex_lock(&bch_register_lock);
 
@@ -1058,11 +1081,7 @@ static void cached_dev_flush(struct closure *cl)
        struct bcache_device *d = &dc->disk;
 
        mutex_lock(&bch_register_lock);
-       d->flush_done = 1;
-
-       if (d->c)
-               bcache_device_unlink(d);
-
+       bcache_device_unlink(d);
        mutex_unlock(&bch_register_lock);
 
        bch_cache_accounting_destroy(&dc->accounting);
@@ -1088,7 +1107,6 @@ static int cached_dev_init(struct cached_dev *dc, unsigned block_size)
        spin_lock_init(&dc->io_lock);
        bch_cache_accounting_init(&dc->accounting, &dc->disk.cl);
 
-       dc->sequential_merge            = true;
        dc->sequential_cutoff           = 4 << 20;
 
        for (io = dc->io; io < dc->io + RECENT_IO; io++) {
@@ -1260,7 +1278,8 @@ bool bch_cache_set_error(struct cache_set *c, const char *fmt, ...)
 {
        va_list args;
 
-       if (test_bit(CACHE_SET_STOPPING, &c->flags))
+       if (c->on_error != ON_ERROR_PANIC &&
+           test_bit(CACHE_SET_STOPPING, &c->flags))
                return false;
 
        /* XXX: we can be called from atomic context
@@ -1275,6 +1294,9 @@ bool bch_cache_set_error(struct cache_set *c, const char *fmt, ...)
 
        printk(", disabling caching\n");
 
+       if (c->on_error == ON_ERROR_PANIC)
+               panic("panic forced after error\n");
+
        bch_cache_set_unregister(c);
        return true;
 }
@@ -1339,6 +1361,9 @@ static void cache_set_flush(struct closure *cl)
        kobject_put(&c->internal);
        kobject_del(&c->kobj);
 
+       if (c->gc_thread)
+               kthread_stop(c->gc_thread);
+
        if (!IS_ERR_OR_NULL(c->root))
                list_add(&c->root->list, &c->btree_cache);
 
@@ -1433,12 +1458,19 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
 
        c->sort_crit_factor = int_sqrt(c->btree_pages);
 
-       mutex_init(&c->bucket_lock);
-       mutex_init(&c->sort_lock);
-       spin_lock_init(&c->sort_time_lock);
        closure_init_unlocked(&c->sb_write);
+       mutex_init(&c->bucket_lock);
+       init_waitqueue_head(&c->try_wait);
+       init_waitqueue_head(&c->bucket_wait);
        closure_init_unlocked(&c->uuid_write);
-       spin_lock_init(&c->btree_read_time_lock);
+       mutex_init(&c->sort_lock);
+
+       spin_lock_init(&c->sort_time.lock);
+       spin_lock_init(&c->btree_gc_time.lock);
+       spin_lock_init(&c->btree_split_time.lock);
+       spin_lock_init(&c->btree_read_time.lock);
+       spin_lock_init(&c->try_harder_time.lock);
+
        bch_moving_init_cache_set(c);
 
        INIT_LIST_HEAD(&c->list);
@@ -1483,11 +1515,10 @@ static void run_cache_set(struct cache_set *c)
        const char *err = "cannot allocate memory";
        struct cached_dev *dc, *t;
        struct cache *ca;
+       struct closure cl;
        unsigned i;
 
-       struct btree_op op;
-       bch_btree_op_init_stack(&op);
-       op.lock = SHRT_MAX;
+       closure_init_stack(&cl);
 
        for_each_cache(ca, c, i)
                c->nbuckets += ca->sb.nbuckets;
@@ -1498,7 +1529,7 @@ static void run_cache_set(struct cache_set *c)
                struct jset *j;
 
                err = "cannot allocate memory for journal";
-               if (bch_journal_read(c, &journal, &op))
+               if (bch_journal_read(c, &journal))
                        goto err;
 
                pr_debug("btree_journal_read() done");
@@ -1522,23 +1553,23 @@ static void run_cache_set(struct cache_set *c)
                k = &j->btree_root;
 
                err = "bad btree root";
-               if (__bch_ptr_invalid(c, j->btree_level + 1, k))
+               if (bch_btree_ptr_invalid(c, k))
                        goto err;
 
                err = "error reading btree root";
-               c->root = bch_btree_node_get(c, k, j->btree_level, &op);
+               c->root = bch_btree_node_get(c, k, j->btree_level, true);
                if (IS_ERR_OR_NULL(c->root))
                        goto err;
 
                list_del_init(&c->root->list);
                rw_unlock(true, c->root);
 
-               err = uuid_read(c, j, &op.cl);
+               err = uuid_read(c, j, &cl);
                if (err)
                        goto err;
 
                err = "error in recovery";
-               if (bch_btree_check(c, &op))
+               if (bch_btree_check(c))
                        goto err;
 
                bch_journal_mark(c, &journal);
@@ -1570,11 +1601,9 @@ static void run_cache_set(struct cache_set *c)
                if (j->version < BCACHE_JSET_VERSION_UUID)
                        __uuid_write(c);
 
-               bch_journal_replay(c, &journal, &op);
+               bch_journal_replay(c, &journal);
        } else {
                pr_notice("invalidating existing data");
-               /* Don't want invalidate_buckets() to queue a gc yet */
-               closure_lock(&c->gc, NULL);
 
                for_each_cache(ca, c, i) {
                        unsigned j;
@@ -1600,15 +1629,15 @@ static void run_cache_set(struct cache_set *c)
 
                err = "cannot allocate new UUID bucket";
                if (__uuid_write(c))
-                       goto err_unlock_gc;
+                       goto err;
 
                err = "cannot allocate new btree root";
-               c->root = bch_btree_node_alloc(c, 0, &op.cl);
+               c->root = bch_btree_node_alloc(c, 0, true);
                if (IS_ERR_OR_NULL(c->root))
-                       goto err_unlock_gc;
+                       goto err;
 
                bkey_copy_key(&c->root->key, &MAX_KEY);
-               bch_btree_node_write(c->root, &op.cl);
+               bch_btree_node_write(c->root, &cl);
 
                bch_btree_set_root(c->root);
                rw_unlock(true, c->root);
@@ -1621,14 +1650,14 @@ static void run_cache_set(struct cache_set *c)
                SET_CACHE_SYNC(&c->sb, true);
 
                bch_journal_next(&c->journal);
-               bch_journal_meta(c, &op.cl);
-
-               /* Unlock */
-               closure_set_stopped(&c->gc.cl);
-               closure_put(&c->gc.cl);
+               bch_journal_meta(c, &cl);
        }
 
-       closure_sync(&op.cl);
+       err = "error starting gc thread";
+       if (bch_gc_thread_start(c))
+               goto err;
+
+       closure_sync(&cl);
        c->sb.last_mount = get_seconds();
        bcache_write_super(c);
 
@@ -1638,13 +1667,10 @@ static void run_cache_set(struct cache_set *c)
        flash_devs_run(c);
 
        return;
-err_unlock_gc:
-       closure_set_stopped(&c->gc.cl);
-       closure_put(&c->gc.cl);
 err:
-       closure_sync(&op.cl);
+       closure_sync(&cl);
        /* XXX: test this, it's broken */
-       bch_cache_set_error(c, err);
+       bch_cache_set_error(c, "%s", err);
 }
 
 static bool can_attach_cache(struct cache *ca, struct cache_set *c)
@@ -1725,8 +1751,6 @@ void bch_cache_release(struct kobject *kobj)
        if (ca->set)
                ca->set->cache[ca->sb.nr_this_dev] = NULL;
 
-       bch_cache_allocator_exit(ca);
-
        bio_split_pool_free(&ca->bio_split_hook);
 
        free_pages((unsigned long) ca->disk_buckets, ilog2(bucket_pages(ca)));
@@ -1758,8 +1782,6 @@ static int cache_alloc(struct cache_sb *sb, struct cache *ca)
        __module_get(THIS_MODULE);
        kobject_init(&ca->kobj, &bch_cache_ktype);
 
-       INIT_LIST_HEAD(&ca->discards);
-
        bio_init(&ca->journal.bio);
        ca->journal.bio.bi_max_vecs = 8;
        ca->journal.bio.bi_io_vec = ca->journal.bio.bi_inline_vecs;
@@ -2006,7 +2028,6 @@ static struct notifier_block reboot = {
 static void bcache_exit(void)
 {
        bch_debug_exit();
-       bch_writeback_exit();
        bch_request_exit();
        bch_btree_exit();
        if (bcache_kobj)
@@ -2039,7 +2060,6 @@ static int __init bcache_init(void)
            sysfs_create_files(bcache_kobj, files) ||
            bch_btree_init() ||
            bch_request_init() ||
-           bch_writeback_init() ||
            bch_debug_init(bcache_kobj))
                goto err;
 
index 924dcfd..80d4c2b 100644 (file)
@@ -21,6 +21,12 @@ static const char * const cache_replacement_policies[] = {
        NULL
 };
 
+static const char * const error_actions[] = {
+       "unregister",
+       "panic",
+       NULL
+};
+
 write_attribute(attach);
 write_attribute(detach);
 write_attribute(unregister);
@@ -66,7 +72,6 @@ rw_attribute(congested_read_threshold_us);
 rw_attribute(congested_write_threshold_us);
 
 rw_attribute(sequential_cutoff);
-rw_attribute(sequential_merge);
 rw_attribute(data_csum);
 rw_attribute(cache_mode);
 rw_attribute(writeback_metadata);
@@ -90,11 +95,14 @@ rw_attribute(discard);
 rw_attribute(running);
 rw_attribute(label);
 rw_attribute(readahead);
+rw_attribute(errors);
 rw_attribute(io_error_limit);
 rw_attribute(io_error_halflife);
 rw_attribute(verify);
+rw_attribute(bypass_torture_test);
 rw_attribute(key_merging_disabled);
 rw_attribute(gc_always_rewrite);
+rw_attribute(expensive_debug_checks);
 rw_attribute(freelist_percent);
 rw_attribute(cache_replacement_policy);
 rw_attribute(btree_shrinker_disabled);
@@ -116,6 +124,7 @@ SHOW(__bch_cached_dev)
 
        sysfs_printf(data_csum,         "%i", dc->disk.data_csum);
        var_printf(verify,              "%i");
+       var_printf(bypass_torture_test, "%i");
        var_printf(writeback_metadata,  "%i");
        var_printf(writeback_running,   "%i");
        var_print(writeback_delay);
@@ -150,10 +159,9 @@ SHOW(__bch_cached_dev)
        sysfs_hprint(dirty_data,
                     bcache_dev_sectors_dirty(&dc->disk) << 9);
 
-       sysfs_hprint(stripe_size,       (1 << dc->disk.stripe_size_bits) << 9);
+       sysfs_hprint(stripe_size,       dc->disk.stripe_size << 9);
        var_printf(partial_stripes_expensive,   "%u");
 
-       var_printf(sequential_merge,    "%i");
        var_hprint(sequential_cutoff);
        var_hprint(readahead);
 
@@ -185,6 +193,7 @@ STORE(__cached_dev)
 
        sysfs_strtoul(data_csum,        dc->disk.data_csum);
        d_strtoul(verify);
+       d_strtoul(bypass_torture_test);
        d_strtoul(writeback_metadata);
        d_strtoul(writeback_running);
        d_strtoul(writeback_delay);
@@ -199,7 +208,6 @@ STORE(__cached_dev)
                            dc->writeback_rate_p_term_inverse, 1, INT_MAX);
        d_strtoul(writeback_rate_d_smooth);
 
-       d_strtoul(sequential_merge);
        d_strtoi_h(sequential_cutoff);
        d_strtoi_h(readahead);
 
@@ -311,7 +319,6 @@ static struct attribute *bch_cached_dev_files[] = {
        &sysfs_stripe_size,
        &sysfs_partial_stripes_expensive,
        &sysfs_sequential_cutoff,
-       &sysfs_sequential_merge,
        &sysfs_clear_stats,
        &sysfs_running,
        &sysfs_state,
@@ -319,6 +326,7 @@ static struct attribute *bch_cached_dev_files[] = {
        &sysfs_readahead,
 #ifdef CONFIG_BCACHE_DEBUG
        &sysfs_verify,
+       &sysfs_bypass_torture_test,
 #endif
        NULL
 };
@@ -366,7 +374,7 @@ STORE(__bch_flash_dev)
        }
 
        if (attr == &sysfs_unregister) {
-               atomic_set(&d->detaching, 1);
+               set_bit(BCACHE_DEV_DETACHING, &d->flags);
                bcache_device_stop(d);
        }
 
@@ -481,7 +489,6 @@ lock_root:
 
        sysfs_print(btree_used_percent, btree_used(c));
        sysfs_print(btree_nodes,        c->gc_stats.nodes);
-       sysfs_hprint(dirty_data,        c->gc_stats.dirty);
        sysfs_hprint(average_key_size,  average_key_size(c));
 
        sysfs_print(cache_read_races,
@@ -492,6 +499,10 @@ lock_root:
        sysfs_print(writeback_keys_failed,
                    atomic_long_read(&c->writeback_keys_failed));
 
+       if (attr == &sysfs_errors)
+               return bch_snprint_string_list(buf, PAGE_SIZE, error_actions,
+                                              c->on_error);
+
        /* See count_io_errors for why 88 */
        sysfs_print(io_error_halflife,  c->error_decay * 88);
        sysfs_print(io_error_limit,     c->error_limit >> IO_ERROR_SHIFT);
@@ -506,6 +517,8 @@ lock_root:
        sysfs_print(active_journal_entries,     fifo_used(&c->journal.pin));
        sysfs_printf(verify,                    "%i", c->verify);
        sysfs_printf(key_merging_disabled,      "%i", c->key_merging_disabled);
+       sysfs_printf(expensive_debug_checks,
+                    "%i", c->expensive_debug_checks);
        sysfs_printf(gc_always_rewrite,         "%i", c->gc_always_rewrite);
        sysfs_printf(btree_shrinker_disabled,   "%i", c->shrinker_disabled);
        sysfs_printf(copy_gc_enabled,           "%i", c->copy_gc_enabled);
@@ -555,7 +568,7 @@ STORE(__bch_cache_set)
        }
 
        if (attr == &sysfs_trigger_gc)
-               bch_queue_gc(c);
+               wake_up_gc(c);
 
        if (attr == &sysfs_prune_cache) {
                struct shrink_control sc;
@@ -569,6 +582,15 @@ STORE(__bch_cache_set)
        sysfs_strtoul(congested_write_threshold_us,
                      c->congested_write_threshold_us);
 
+       if (attr == &sysfs_errors) {
+               ssize_t v = bch_read_string_list(buf, error_actions);
+
+               if (v < 0)
+                       return v;
+
+               c->on_error = v;
+       }
+
        if (attr == &sysfs_io_error_limit)
                c->error_limit = strtoul_or_return(buf) << IO_ERROR_SHIFT;
 
@@ -579,6 +601,7 @@ STORE(__bch_cache_set)
        sysfs_strtoul(journal_delay_ms,         c->journal_delay_ms);
        sysfs_strtoul(verify,                   c->verify);
        sysfs_strtoul(key_merging_disabled,     c->key_merging_disabled);
+       sysfs_strtoul(expensive_debug_checks,   c->expensive_debug_checks);
        sysfs_strtoul(gc_always_rewrite,        c->gc_always_rewrite);
        sysfs_strtoul(btree_shrinker_disabled,  c->shrinker_disabled);
        sysfs_strtoul(copy_gc_enabled,          c->copy_gc_enabled);
@@ -618,8 +641,8 @@ static struct attribute *bch_cache_set_files[] = {
        &sysfs_cache_available_percent,
 
        &sysfs_average_key_size,
-       &sysfs_dirty_data,
 
+       &sysfs_errors,
        &sysfs_io_error_limit,
        &sysfs_io_error_halflife,
        &sysfs_congested,
@@ -653,6 +676,7 @@ static struct attribute *bch_cache_set_internal_files[] = {
 #ifdef CONFIG_BCACHE_DEBUG
        &sysfs_verify,
        &sysfs_key_merging_disabled,
+       &sysfs_expensive_debug_checks,
 #endif
        &sysfs_gc_always_rewrite,
        &sysfs_btree_shrinker_disabled,
index f7b6c19..adbc3df 100644 (file)
@@ -1,6 +1,5 @@
 #include "bcache.h"
 #include "btree.h"
-#include "request.h"
 
 #include <linux/blktrace_api.h>
 #include <linux/module.h>
index 420dad5..462214e 100644 (file)
@@ -168,10 +168,14 @@ int bch_parse_uuid(const char *s, char *uuid)
 
 void bch_time_stats_update(struct time_stats *stats, uint64_t start_time)
 {
-       uint64_t now            = local_clock();
-       uint64_t duration       = time_after64(now, start_time)
+       uint64_t now, duration, last;
+
+       spin_lock(&stats->lock);
+
+       now             = local_clock();
+       duration        = time_after64(now, start_time)
                ? now - start_time : 0;
-       uint64_t last           = time_after64(now, stats->last)
+       last            = time_after64(now, stats->last)
                ? now - stats->last : 0;
 
        stats->max_duration = max(stats->max_duration, duration);
@@ -188,6 +192,8 @@ void bch_time_stats_update(struct time_stats *stats, uint64_t start_time)
        }
 
        stats->last = now ?: 1;
+
+       spin_unlock(&stats->lock);
 }
 
 /**
index ea345c6..362c4b3 100644 (file)
 
 struct closure;
 
-#ifdef CONFIG_BCACHE_EDEBUG
+#ifdef CONFIG_BCACHE_DEBUG
 
 #define atomic_dec_bug(v)      BUG_ON(atomic_dec_return(v) < 0)
 #define atomic_inc_bug(v, i)   BUG_ON(atomic_inc_return(v) <= i)
 
-#else /* EDEBUG */
+#else /* DEBUG */
 
 #define atomic_dec_bug(v)      atomic_dec(v)
 #define atomic_inc_bug(v, i)   atomic_inc(v)
 
 #endif
 
-#define BITMASK(name, type, field, offset, size)               \
-static inline uint64_t name(const type *k)                     \
-{ return (k->field >> offset) & ~(((uint64_t) ~0) << size); }  \
-                                                               \
-static inline void SET_##name(type *k, uint64_t v)             \
-{                                                              \
-       k->field &= ~(~((uint64_t) ~0 << size) << offset);      \
-       k->field |= v << offset;                                \
-}
-
 #define DECLARE_HEAP(type, name)                                       \
        struct {                                                        \
                size_t size, used;                                      \
@@ -388,6 +378,7 @@ ssize_t bch_snprint_string_list(char *buf, size_t size, const char * const list[
 ssize_t bch_read_string_list(const char *buf, const char * const list[]);
 
 struct time_stats {
+       spinlock_t      lock;
        /*
         * all fields are in nanoseconds, averages are ewmas stored left shifted
         * by 8
index ba3ee48..99053b1 100644 (file)
 #include "debug.h"
 #include "writeback.h"
 
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
 #include <trace/events/bcache.h>
 
-static struct workqueue_struct *dirty_wq;
-
-static void read_dirty(struct closure *);
-
-struct dirty_io {
-       struct closure          cl;
-       struct cached_dev       *dc;
-       struct bio              bio;
-};
-
 /* Rate limiting */
 
 static void __update_writeback_rate(struct cached_dev *dc)
@@ -72,9 +65,6 @@ out:
        dc->writeback_rate_derivative = derivative;
        dc->writeback_rate_change = change;
        dc->writeback_rate_target = target;
-
-       schedule_delayed_work(&dc->writeback_rate_update,
-                             dc->writeback_rate_update_seconds * HZ);
 }
 
 static void update_writeback_rate(struct work_struct *work)
@@ -90,13 +80,16 @@ static void update_writeback_rate(struct work_struct *work)
                __update_writeback_rate(dc);
 
        up_read(&dc->writeback_lock);
+
+       schedule_delayed_work(&dc->writeback_rate_update,
+                             dc->writeback_rate_update_seconds * HZ);
 }
 
 static unsigned writeback_delay(struct cached_dev *dc, unsigned sectors)
 {
        uint64_t ret;
 
-       if (atomic_read(&dc->disk.detaching) ||
+       if (test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) ||
            !dc->writeback_percent)
                return 0;
 
@@ -105,37 +98,11 @@ static unsigned writeback_delay(struct cached_dev *dc, unsigned sectors)
        return min_t(uint64_t, ret, HZ);
 }
 
-/* Background writeback */
-
-static bool dirty_pred(struct keybuf *buf, struct bkey *k)
-{
-       return KEY_DIRTY(k);
-}
-
-static bool dirty_full_stripe_pred(struct keybuf *buf, struct bkey *k)
-{
-       uint64_t stripe;
-       unsigned nr_sectors = KEY_SIZE(k);
-       struct cached_dev *dc = container_of(buf, struct cached_dev,
-                                            writeback_keys);
-       unsigned stripe_size = 1 << dc->disk.stripe_size_bits;
-
-       if (!KEY_DIRTY(k))
-               return false;
-
-       stripe = KEY_START(k) >> dc->disk.stripe_size_bits;
-       while (1) {
-               if (atomic_read(dc->disk.stripe_sectors_dirty + stripe) !=
-                   stripe_size)
-                       return false;
-
-               if (nr_sectors <= stripe_size)
-                       return true;
-
-               nr_sectors -= stripe_size;
-               stripe++;
-       }
-}
+struct dirty_io {
+       struct closure          cl;
+       struct cached_dev       *dc;
+       struct bio              bio;
+};
 
 static void dirty_init(struct keybuf_key *w)
 {
@@ -153,131 +120,6 @@ static void dirty_init(struct keybuf_key *w)
        bch_bio_map(bio, NULL);
 }
 
-static void refill_dirty(struct closure *cl)
-{
-       struct cached_dev *dc = container_of(cl, struct cached_dev,
-                                            writeback.cl);
-       struct keybuf *buf = &dc->writeback_keys;
-       bool searched_from_start = false;
-       struct bkey end = MAX_KEY;
-       SET_KEY_INODE(&end, dc->disk.id);
-
-       if (!atomic_read(&dc->disk.detaching) &&
-           !dc->writeback_running)
-               closure_return(cl);
-
-       down_write(&dc->writeback_lock);
-
-       if (!atomic_read(&dc->has_dirty)) {
-               SET_BDEV_STATE(&dc->sb, BDEV_STATE_CLEAN);
-               bch_write_bdev_super(dc, NULL);
-
-               up_write(&dc->writeback_lock);
-               closure_return(cl);
-       }
-
-       if (bkey_cmp(&buf->last_scanned, &end) >= 0) {
-               buf->last_scanned = KEY(dc->disk.id, 0, 0);
-               searched_from_start = true;
-       }
-
-       if (dc->partial_stripes_expensive) {
-               uint64_t i;
-
-               for (i = 0; i < dc->disk.nr_stripes; i++)
-                       if (atomic_read(dc->disk.stripe_sectors_dirty + i) ==
-                           1 << dc->disk.stripe_size_bits)
-                               goto full_stripes;
-
-               goto normal_refill;
-full_stripes:
-               bch_refill_keybuf(dc->disk.c, buf, &end,
-                                 dirty_full_stripe_pred);
-       } else {
-normal_refill:
-               bch_refill_keybuf(dc->disk.c, buf, &end, dirty_pred);
-       }
-
-       if (bkey_cmp(&buf->last_scanned, &end) >= 0 && searched_from_start) {
-               /* Searched the entire btree  - delay awhile */
-
-               if (RB_EMPTY_ROOT(&buf->keys)) {
-                       atomic_set(&dc->has_dirty, 0);
-                       cached_dev_put(dc);
-               }
-
-               if (!atomic_read(&dc->disk.detaching))
-                       closure_delay(&dc->writeback, dc->writeback_delay * HZ);
-       }
-
-       up_write(&dc->writeback_lock);
-
-       bch_ratelimit_reset(&dc->writeback_rate);
-
-       /* Punt to workqueue only so we don't recurse and blow the stack */
-       continue_at(cl, read_dirty, dirty_wq);
-}
-
-void bch_writeback_queue(struct cached_dev *dc)
-{
-       if (closure_trylock(&dc->writeback.cl, &dc->disk.cl)) {
-               if (!atomic_read(&dc->disk.detaching))
-                       closure_delay(&dc->writeback, dc->writeback_delay * HZ);
-
-               continue_at(&dc->writeback.cl, refill_dirty, dirty_wq);
-       }
-}
-
-void bch_writeback_add(struct cached_dev *dc)
-{
-       if (!atomic_read(&dc->has_dirty) &&
-           !atomic_xchg(&dc->has_dirty, 1)) {
-               atomic_inc(&dc->count);
-
-               if (BDEV_STATE(&dc->sb) != BDEV_STATE_DIRTY) {
-                       SET_BDEV_STATE(&dc->sb, BDEV_STATE_DIRTY);
-                       /* XXX: should do this synchronously */
-                       bch_write_bdev_super(dc, NULL);
-               }
-
-               bch_writeback_queue(dc);
-
-               if (dc->writeback_percent)
-                       schedule_delayed_work(&dc->writeback_rate_update,
-                                     dc->writeback_rate_update_seconds * HZ);
-       }
-}
-
-void bcache_dev_sectors_dirty_add(struct cache_set *c, unsigned inode,
-                                 uint64_t offset, int nr_sectors)
-{
-       struct bcache_device *d = c->devices[inode];
-       unsigned stripe_size, stripe_offset;
-       uint64_t stripe;
-
-       if (!d)
-               return;
-
-       stripe_size = 1 << d->stripe_size_bits;
-       stripe = offset >> d->stripe_size_bits;
-       stripe_offset = offset & (stripe_size - 1);
-
-       while (nr_sectors) {
-               int s = min_t(unsigned, abs(nr_sectors),
-                             stripe_size - stripe_offset);
-
-               if (nr_sectors < 0)
-                       s = -s;
-
-               atomic_add(s, d->stripe_sectors_dirty + stripe);
-               nr_sectors -= s;
-               stripe_offset = 0;
-               stripe++;
-       }
-}
-
-/* Background writeback - IO loop */
-
 static void dirty_io_destructor(struct closure *cl)
 {
        struct dirty_io *io = container_of(cl, struct dirty_io, cl);
@@ -297,26 +139,25 @@ static void write_dirty_finish(struct closure *cl)
 
        /* This is kind of a dumb way of signalling errors. */
        if (KEY_DIRTY(&w->key)) {
+               int ret;
                unsigned i;
-               struct btree_op op;
-               bch_btree_op_init_stack(&op);
+               struct keylist keys;
 
-               op.type = BTREE_REPLACE;
-               bkey_copy(&op.replace, &w->key);
+               bch_keylist_init(&keys);
 
-               SET_KEY_DIRTY(&w->key, false);
-               bch_keylist_add(&op.keys, &w->key);
+               bkey_copy(keys.top, &w->key);
+               SET_KEY_DIRTY(keys.top, false);
+               bch_keylist_push(&keys);
 
                for (i = 0; i < KEY_PTRS(&w->key); i++)
                        atomic_inc(&PTR_BUCKET(dc->disk.c, &w->key, i)->pin);
 
-               bch_btree_insert(&op, dc->disk.c);
-               closure_sync(&op.cl);
+               ret = bch_btree_insert(dc->disk.c, &keys, NULL, &w->key);
 
-               if (op.insert_collision)
+               if (ret)
                        trace_bcache_writeback_collision(&w->key);
 
-               atomic_long_inc(op.insert_collision
+               atomic_long_inc(ret
                                ? &dc->disk.c->writeback_keys_failed
                                : &dc->disk.c->writeback_keys_done);
        }
@@ -374,30 +215,33 @@ static void read_dirty_submit(struct closure *cl)
        continue_at(cl, write_dirty, system_wq);
 }
 
-static void read_dirty(struct closure *cl)
+static void read_dirty(struct cached_dev *dc)
 {
-       struct cached_dev *dc = container_of(cl, struct cached_dev,
-                                            writeback.cl);
-       unsigned delay = writeback_delay(dc, 0);
+       unsigned delay = 0;
        struct keybuf_key *w;
        struct dirty_io *io;
+       struct closure cl;
+
+       closure_init_stack(&cl);
 
        /*
         * XXX: if we error, background writeback just spins. Should use some
         * mempools.
         */
 
-       while (1) {
+       while (!kthread_should_stop()) {
+               try_to_freeze();
+
                w = bch_keybuf_next(&dc->writeback_keys);
                if (!w)
                        break;
 
                BUG_ON(ptr_stale(dc->disk.c, &w->key, 0));
 
-               if (delay > 0 &&
-                   (KEY_START(&w->key) != dc->last_read ||
-                    jiffies_to_msecs(delay) > 50))
-                       delay = schedule_timeout_uninterruptible(delay);
+               if (KEY_START(&w->key) != dc->last_read ||
+                   jiffies_to_msecs(delay) > 50)
+                       while (!kthread_should_stop() && delay)
+                               delay = schedule_timeout_interruptible(delay);
 
                dc->last_read   = KEY_OFFSET(&w->key);
 
@@ -423,7 +267,7 @@ static void read_dirty(struct closure *cl)
                trace_bcache_writeback(&w->key);
 
                down(&dc->in_flight);
-               closure_call(&io->cl, read_dirty_submit, NULL, cl);
+               closure_call(&io->cl, read_dirty_submit, NULL, &cl);
 
                delay = writeback_delay(dc, KEY_SIZE(&w->key));
        }
@@ -439,52 +283,205 @@ err:
         * Wait for outstanding writeback IOs to finish (and keybuf slots to be
         * freed) before refilling again
         */
-       continue_at(cl, refill_dirty, dirty_wq);
+       closure_sync(&cl);
 }
 
-/* Init */
+/* Scan for dirty data */
+
+void bcache_dev_sectors_dirty_add(struct cache_set *c, unsigned inode,
+                                 uint64_t offset, int nr_sectors)
+{
+       struct bcache_device *d = c->devices[inode];
+       unsigned stripe_offset, stripe, sectors_dirty;
+
+       if (!d)
+               return;
+
+       stripe = offset_to_stripe(d, offset);
+       stripe_offset = offset & (d->stripe_size - 1);
+
+       while (nr_sectors) {
+               int s = min_t(unsigned, abs(nr_sectors),
+                             d->stripe_size - stripe_offset);
+
+               if (nr_sectors < 0)
+                       s = -s;
+
+               if (stripe >= d->nr_stripes)
+                       return;
+
+               sectors_dirty = atomic_add_return(s,
+                                       d->stripe_sectors_dirty + stripe);
+               if (sectors_dirty == d->stripe_size)
+                       set_bit(stripe, d->full_dirty_stripes);
+               else
+                       clear_bit(stripe, d->full_dirty_stripes);
+
+               nr_sectors -= s;
+               stripe_offset = 0;
+               stripe++;
+       }
+}
 
-static int bch_btree_sectors_dirty_init(struct btree *b, struct btree_op *op,
-                                       struct cached_dev *dc)
+static bool dirty_pred(struct keybuf *buf, struct bkey *k)
 {
-       struct bkey *k;
-       struct btree_iter iter;
-
-       bch_btree_iter_init(b, &iter, &KEY(dc->disk.id, 0, 0));
-       while ((k = bch_btree_iter_next_filter(&iter, b, bch_ptr_bad)))
-               if (!b->level) {
-                       if (KEY_INODE(k) > dc->disk.id)
-                               break;
-
-                       if (KEY_DIRTY(k))
-                               bcache_dev_sectors_dirty_add(b->c, dc->disk.id,
-                                                            KEY_START(k),
-                                                            KEY_SIZE(k));
-               } else {
-                       btree(sectors_dirty_init, k, b, op, dc);
-                       if (KEY_INODE(k) > dc->disk.id)
-                               break;
-
-                       cond_resched();
+       return KEY_DIRTY(k);
+}
+
+static void refill_full_stripes(struct cached_dev *dc)
+{
+       struct keybuf *buf = &dc->writeback_keys;
+       unsigned start_stripe, stripe, next_stripe;
+       bool wrapped = false;
+
+       stripe = offset_to_stripe(&dc->disk, KEY_OFFSET(&buf->last_scanned));
+
+       if (stripe >= dc->disk.nr_stripes)
+               stripe = 0;
+
+       start_stripe = stripe;
+
+       while (1) {
+               stripe = find_next_bit(dc->disk.full_dirty_stripes,
+                                      dc->disk.nr_stripes, stripe);
+
+               if (stripe == dc->disk.nr_stripes)
+                       goto next;
+
+               next_stripe = find_next_zero_bit(dc->disk.full_dirty_stripes,
+                                                dc->disk.nr_stripes, stripe);
+
+               buf->last_scanned = KEY(dc->disk.id,
+                                       stripe * dc->disk.stripe_size, 0);
+
+               bch_refill_keybuf(dc->disk.c, buf,
+                                 &KEY(dc->disk.id,
+                                      next_stripe * dc->disk.stripe_size, 0),
+                                 dirty_pred);
+
+               if (array_freelist_empty(&buf->freelist))
+                       return;
+
+               stripe = next_stripe;
+next:
+               if (wrapped && stripe > start_stripe)
+                       return;
+
+               if (stripe == dc->disk.nr_stripes) {
+                       stripe = 0;
+                       wrapped = true;
                }
+       }
+}
+
+static bool refill_dirty(struct cached_dev *dc)
+{
+       struct keybuf *buf = &dc->writeback_keys;
+       struct bkey end = KEY(dc->disk.id, MAX_KEY_OFFSET, 0);
+       bool searched_from_start = false;
+
+       if (dc->partial_stripes_expensive) {
+               refill_full_stripes(dc);
+               if (array_freelist_empty(&buf->freelist))
+                       return false;
+       }
+
+       if (bkey_cmp(&buf->last_scanned, &end) >= 0) {
+               buf->last_scanned = KEY(dc->disk.id, 0, 0);
+               searched_from_start = true;
+       }
+
+       bch_refill_keybuf(dc->disk.c, buf, &end, dirty_pred);
+
+       return bkey_cmp(&buf->last_scanned, &end) >= 0 && searched_from_start;
+}
+
+static int bch_writeback_thread(void *arg)
+{
+       struct cached_dev *dc = arg;
+       bool searched_full_index;
+
+       while (!kthread_should_stop()) {
+               down_write(&dc->writeback_lock);
+               if (!atomic_read(&dc->has_dirty) ||
+                   (!test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) &&
+                    !dc->writeback_running)) {
+                       up_write(&dc->writeback_lock);
+                       set_current_state(TASK_INTERRUPTIBLE);
+
+                       if (kthread_should_stop())
+                               return 0;
+
+                       try_to_freeze();
+                       schedule();
+                       continue;
+               }
+
+               searched_full_index = refill_dirty(dc);
+
+               if (searched_full_index &&
+                   RB_EMPTY_ROOT(&dc->writeback_keys.keys)) {
+                       atomic_set(&dc->has_dirty, 0);
+                       cached_dev_put(dc);
+                       SET_BDEV_STATE(&dc->sb, BDEV_STATE_CLEAN);
+                       bch_write_bdev_super(dc, NULL);
+               }
+
+               up_write(&dc->writeback_lock);
+
+               bch_ratelimit_reset(&dc->writeback_rate);
+               read_dirty(dc);
+
+               if (searched_full_index) {
+                       unsigned delay = dc->writeback_delay * HZ;
+
+                       while (delay &&
+                              !kthread_should_stop() &&
+                              !test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags))
+                               delay = schedule_timeout_interruptible(delay);
+               }
+       }
 
        return 0;
 }
 
+/* Init */
+
+struct sectors_dirty_init {
+       struct btree_op op;
+       unsigned        inode;
+};
+
+static int sectors_dirty_init_fn(struct btree_op *_op, struct btree *b,
+                                struct bkey *k)
+{
+       struct sectors_dirty_init *op = container_of(_op,
+                                               struct sectors_dirty_init, op);
+       if (KEY_INODE(k) > op->inode)
+               return MAP_DONE;
+
+       if (KEY_DIRTY(k))
+               bcache_dev_sectors_dirty_add(b->c, KEY_INODE(k),
+                                            KEY_START(k), KEY_SIZE(k));
+
+       return MAP_CONTINUE;
+}
+
 void bch_sectors_dirty_init(struct cached_dev *dc)
 {
-       struct btree_op op;
+       struct sectors_dirty_init op;
+
+       bch_btree_op_init(&op.op, -1);
+       op.inode = dc->disk.id;
 
-       bch_btree_op_init_stack(&op);
-       btree_root(sectors_dirty_init, dc->disk.c, &op, dc);
+       bch_btree_map_keys(&op.op, dc->disk.c, &KEY(op.inode, 0, 0),
+                          sectors_dirty_init_fn, 0);
 }
 
-void bch_cached_dev_writeback_init(struct cached_dev *dc)
+int bch_cached_dev_writeback_init(struct cached_dev *dc)
 {
        sema_init(&dc->in_flight, 64);
-       closure_init_unlocked(&dc->writeback);
        init_rwsem(&dc->writeback_lock);
-
        bch_keybuf_init(&dc->writeback_keys);
 
        dc->writeback_metadata          = true;
@@ -498,22 +495,16 @@ void bch_cached_dev_writeback_init(struct cached_dev *dc)
        dc->writeback_rate_p_term_inverse = 64;
        dc->writeback_rate_d_smooth     = 8;
 
+       dc->writeback_thread = kthread_create(bch_writeback_thread, dc,
+                                             "bcache_writeback");
+       if (IS_ERR(dc->writeback_thread))
+               return PTR_ERR(dc->writeback_thread);
+
+       set_task_state(dc->writeback_thread, TASK_INTERRUPTIBLE);
+
        INIT_DELAYED_WORK(&dc->writeback_rate_update, update_writeback_rate);
        schedule_delayed_work(&dc->writeback_rate_update,
                              dc->writeback_rate_update_seconds * HZ);
-}
-
-void bch_writeback_exit(void)
-{
-       if (dirty_wq)
-               destroy_workqueue(dirty_wq);
-}
-
-int __init bch_writeback_init(void)
-{
-       dirty_wq = create_workqueue("bcache_writeback");
-       if (!dirty_wq)
-               return -ENOMEM;
 
        return 0;
 }
index c91f61b..c9ddcf4 100644 (file)
@@ -14,20 +14,27 @@ static inline uint64_t bcache_dev_sectors_dirty(struct bcache_device *d)
        return ret;
 }
 
-static inline bool bcache_dev_stripe_dirty(struct bcache_device *d,
+static inline unsigned offset_to_stripe(struct bcache_device *d,
+                                       uint64_t offset)
+{
+       do_div(offset, d->stripe_size);
+       return offset;
+}
+
+static inline bool bcache_dev_stripe_dirty(struct cached_dev *dc,
                                           uint64_t offset,
                                           unsigned nr_sectors)
 {
-       uint64_t stripe = offset >> d->stripe_size_bits;
+       unsigned stripe = offset_to_stripe(&dc->disk, offset);
 
        while (1) {
-               if (atomic_read(d->stripe_sectors_dirty + stripe))
+               if (atomic_read(dc->disk.stripe_sectors_dirty + stripe))
                        return true;
 
-               if (nr_sectors <= 1 << d->stripe_size_bits)
+               if (nr_sectors <= dc->disk.stripe_size)
                        return false;
 
-               nr_sectors -= 1 << d->stripe_size_bits;
+               nr_sectors -= dc->disk.stripe_size;
                stripe++;
        }
 }
@@ -38,12 +45,12 @@ static inline bool should_writeback(struct cached_dev *dc, struct bio *bio,
        unsigned in_use = dc->disk.c->gc_stats.in_use;
 
        if (cache_mode != CACHE_MODE_WRITEBACK ||
-           atomic_read(&dc->disk.detaching) ||
+           test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) ||
            in_use > CUTOFF_WRITEBACK_SYNC)
                return false;
 
        if (dc->partial_stripes_expensive &&
-           bcache_dev_stripe_dirty(&dc->disk, bio->bi_sector,
+           bcache_dev_stripe_dirty(dc, bio->bi_sector,
                                    bio_sectors(bio)))
                return true;
 
@@ -54,11 +61,30 @@ static inline bool should_writeback(struct cached_dev *dc, struct bio *bio,
                in_use <= CUTOFF_WRITEBACK;
 }
 
+static inline void bch_writeback_queue(struct cached_dev *dc)
+{
+       wake_up_process(dc->writeback_thread);
+}
+
+static inline void bch_writeback_add(struct cached_dev *dc)
+{
+       if (!atomic_read(&dc->has_dirty) &&
+           !atomic_xchg(&dc->has_dirty, 1)) {
+               atomic_inc(&dc->count);
+
+               if (BDEV_STATE(&dc->sb) != BDEV_STATE_DIRTY) {
+                       SET_BDEV_STATE(&dc->sb, BDEV_STATE_DIRTY);
+                       /* XXX: should do this synchronously */
+                       bch_write_bdev_super(dc, NULL);
+               }
+
+               bch_writeback_queue(dc);
+       }
+}
+
 void bcache_dev_sectors_dirty_add(struct cache_set *, unsigned, uint64_t, int);
-void bch_writeback_queue(struct cached_dev *);
-void bch_writeback_add(struct cached_dev *);
 
 void bch_sectors_dirty_init(struct cached_dev *dc);
-void bch_cached_dev_writeback_init(struct cached_dev *);
+int bch_cached_dev_writeback_init(struct cached_dev *);
 
 #endif
index 50ea7ed..81b0fa6 100644 (file)
@@ -950,7 +950,7 @@ static int crypt_convert(struct crypt_config *cc,
                /* async */
                case -EBUSY:
                        wait_for_completion(&ctx->restart);
-                       INIT_COMPLETION(ctx->restart);
+                       reinit_completion(&ctx->restart);
                        /* fall through*/
                case -EINPROGRESS:
                        this_cc->req = NULL;
index 8766eab..b6b7a28 100644 (file)
@@ -112,7 +112,7 @@ static inline int speed_max(struct mddev *mddev)
 
 static struct ctl_table_header *raid_table_header;
 
-static ctl_table raid_table[] = {
+static struct ctl_table raid_table[] = {
        {
                .procname       = "speed_limit_min",
                .data           = &sysctl_speed_limit_min,
@@ -130,7 +130,7 @@ static ctl_table raid_table[] = {
        { }
 };
 
-static ctl_table raid_dir_table[] = {
+static struct ctl_table raid_dir_table[] = {
        {
                .procname       = "raid",
                .maxlen         = 0,
@@ -140,7 +140,7 @@ static ctl_table raid_dir_table[] = {
        { }
 };
 
-static ctl_table raid_root_table[] = {
+static struct ctl_table raid_root_table[] = {
        {
                .procname       = "dev",
                .maxlen         = 0,
@@ -562,11 +562,19 @@ static struct mddev * mddev_find(dev_t unit)
        goto retry;
 }
 
-static inline int mddev_lock(struct mddev * mddev)
+static inline int __must_check mddev_lock(struct mddev * mddev)
 {
        return mutex_lock_interruptible(&mddev->reconfig_mutex);
 }
 
+/* Sometimes we need to take the lock in a situation where
+ * failure due to interrupts is not acceptable.
+ */
+static inline void mddev_lock_nointr(struct mddev * mddev)
+{
+       mutex_lock(&mddev->reconfig_mutex);
+}
+
 static inline int mddev_is_locked(struct mddev *mddev)
 {
        return mutex_is_locked(&mddev->reconfig_mutex);
@@ -2978,7 +2986,7 @@ rdev_size_store(struct md_rdev *rdev, const char *buf, size_t len)
                for_each_mddev(mddev, tmp) {
                        struct md_rdev *rdev2;
 
-                       mddev_lock(mddev);
+                       mddev_lock_nointr(mddev);
                        rdev_for_each(rdev2, mddev)
                                if (rdev->bdev == rdev2->bdev &&
                                    rdev != rdev2 &&
@@ -2994,7 +3002,7 @@ rdev_size_store(struct md_rdev *rdev, const char *buf, size_t len)
                                break;
                        }
                }
-               mddev_lock(my_mddev);
+               mddev_lock_nointr(my_mddev);
                if (overlap) {
                        /* Someone else could have slipped in a size
                         * change here, but doing so is just silly.
@@ -3580,6 +3588,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
                mddev->in_sync = 1;
                del_timer_sync(&mddev->safemode_timer);
        }
+       blk_set_stacking_limits(&mddev->queue->limits);
        pers->run(mddev);
        set_bit(MD_CHANGE_DEVS, &mddev->flags);
        mddev_resume(mddev);
@@ -5258,7 +5267,7 @@ static void __md_stop_writes(struct mddev *mddev)
 
 void md_stop_writes(struct mddev *mddev)
 {
-       mddev_lock(mddev);
+       mddev_lock_nointr(mddev);
        __md_stop_writes(mddev);
        mddev_unlock(mddev);
 }
@@ -5291,20 +5300,35 @@ EXPORT_SYMBOL_GPL(md_stop);
 static int md_set_readonly(struct mddev *mddev, struct block_device *bdev)
 {
        int err = 0;
+       int did_freeze = 0;
+
+       if (!test_bit(MD_RECOVERY_FROZEN, &mddev->recovery)) {
+               did_freeze = 1;
+               set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+               md_wakeup_thread(mddev->thread);
+       }
+       if (mddev->sync_thread) {
+               set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+               /* Thread might be blocked waiting for metadata update
+                * which will now never happen */
+               wake_up_process(mddev->sync_thread->tsk);
+       }
+       mddev_unlock(mddev);
+       wait_event(resync_wait, mddev->sync_thread == NULL);
+       mddev_lock_nointr(mddev);
+
        mutex_lock(&mddev->open_mutex);
-       if (atomic_read(&mddev->openers) > !!bdev) {
+       if (atomic_read(&mddev->openers) > !!bdev ||
+           mddev->sync_thread ||
+           (bdev && !test_bit(MD_STILL_CLOSED, &mddev->flags))) {
                printk("md: %s still in use.\n",mdname(mddev));
+               if (did_freeze) {
+                       clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+                       md_wakeup_thread(mddev->thread);
+               }
                err = -EBUSY;
                goto out;
        }
-       if (bdev && !test_bit(MD_STILL_CLOSED, &mddev->flags)) {
-               /* Someone opened the device since we flushed it
-                * so page cache could be dirty and it is too late
-                * to flush.  So abort
-                */
-               mutex_unlock(&mddev->open_mutex);
-               return -EBUSY;
-       }
        if (mddev->pers) {
                __md_stop_writes(mddev);
 
@@ -5315,7 +5339,7 @@ static int md_set_readonly(struct mddev *mddev, struct block_device *bdev)
                set_disk_ro(mddev->gendisk, 1);
                clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
                sysfs_notify_dirent_safe(mddev->sysfs_state);
-               err = 0;        
+               err = 0;
        }
 out:
        mutex_unlock(&mddev->open_mutex);
@@ -5331,20 +5355,34 @@ static int do_md_stop(struct mddev * mddev, int mode,
 {
        struct gendisk *disk = mddev->gendisk;
        struct md_rdev *rdev;
+       int did_freeze = 0;
+
+       if (!test_bit(MD_RECOVERY_FROZEN, &mddev->recovery)) {
+               did_freeze = 1;
+               set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+               md_wakeup_thread(mddev->thread);
+       }
+       if (mddev->sync_thread) {
+               set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+               /* Thread might be blocked waiting for metadata update
+                * which will now never happen */
+               wake_up_process(mddev->sync_thread->tsk);
+       }
+       mddev_unlock(mddev);
+       wait_event(resync_wait, mddev->sync_thread == NULL);
+       mddev_lock_nointr(mddev);
 
        mutex_lock(&mddev->open_mutex);
        if (atomic_read(&mddev->openers) > !!bdev ||
-           mddev->sysfs_active) {
+           mddev->sysfs_active ||
+           mddev->sync_thread ||
+           (bdev && !test_bit(MD_STILL_CLOSED, &mddev->flags))) {
                printk("md: %s still in use.\n",mdname(mddev));
                mutex_unlock(&mddev->open_mutex);
-               return -EBUSY;
-       }
-       if (bdev && !test_bit(MD_STILL_CLOSED, &mddev->flags)) {
-               /* Someone opened the device since we flushed it
-                * so page cache could be dirty and it is too late
-                * to flush.  So abort
-                */
-               mutex_unlock(&mddev->open_mutex);
+               if (did_freeze) {
+                       clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+                       md_wakeup_thread(mddev->thread);
+               }
                return -EBUSY;
        }
        if (mddev->pers) {
@@ -6551,7 +6589,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
                                wait_event(mddev->sb_wait,
                                           !test_bit(MD_CHANGE_DEVS, &mddev->flags) &&
                                           !test_bit(MD_CHANGE_PENDING, &mddev->flags));
-                               mddev_lock(mddev);
+                               mddev_lock_nointr(mddev);
                        }
                } else {
                        err = -EROFS;
@@ -7361,9 +7399,6 @@ void md_do_sync(struct md_thread *thread)
                mddev->curr_resync = 2;
 
        try_again:
-               if (kthread_should_stop())
-                       set_bit(MD_RECOVERY_INTR, &mddev->recovery);
-
                if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
                        goto skip;
                for_each_mddev(mddev2, tmp) {
@@ -7388,7 +7423,7 @@ void md_do_sync(struct md_thread *thread)
                                 * be caught by 'softlockup'
                                 */
                                prepare_to_wait(&resync_wait, &wq, TASK_INTERRUPTIBLE);
-                               if (!kthread_should_stop() &&
+                               if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
                                    mddev2->curr_resync >= mddev->curr_resync) {
                                        printk(KERN_INFO "md: delaying %s of %s"
                                               " until %s has finished (they"
@@ -7464,7 +7499,7 @@ void md_do_sync(struct md_thread *thread)
        last_check = 0;
 
        if (j>2) {
-               printk(KERN_INFO 
+               printk(KERN_INFO
                       "md: resuming %s of %s from checkpoint.\n",
                       desc, mdname(mddev));
                mddev->curr_resync = j;
@@ -7501,7 +7536,8 @@ void md_do_sync(struct md_thread *thread)
                        sysfs_notify(&mddev->kobj, NULL, "sync_completed");
                }
 
-               while (j >= mddev->resync_max && !kthread_should_stop()) {
+               while (j >= mddev->resync_max &&
+                      !test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
                        /* As this condition is controlled by user-space,
                         * we can block indefinitely, so use '_interruptible'
                         * to avoid triggering warnings.
@@ -7509,17 +7545,18 @@ void md_do_sync(struct md_thread *thread)
                        flush_signals(current); /* just in case */
                        wait_event_interruptible(mddev->recovery_wait,
                                                 mddev->resync_max > j
-                                                || kthread_should_stop());
+                                                || test_bit(MD_RECOVERY_INTR,
+                                                            &mddev->recovery));
                }
 
-               if (kthread_should_stop())
-                       goto interrupted;
+               if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
+                       break;
 
                sectors = mddev->pers->sync_request(mddev, j, &skipped,
                                                  currspeed < speed_min(mddev));
                if (sectors == 0) {
                        set_bit(MD_RECOVERY_INTR, &mddev->recovery);
-                       goto out;
+                       break;
                }
 
                if (!skipped) { /* actual IO requested */
@@ -7556,10 +7593,8 @@ void md_do_sync(struct md_thread *thread)
                        last_mark = next;
                }
 
-
-               if (kthread_should_stop())
-                       goto interrupted;
-
+               if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
+                       break;
 
                /*
                 * this loop exits only if either when we are slower than
@@ -7582,11 +7617,12 @@ void md_do_sync(struct md_thread *thread)
                        }
                }
        }
-       printk(KERN_INFO "md: %s: %s done.\n",mdname(mddev), desc);
+       printk(KERN_INFO "md: %s: %s %s.\n",mdname(mddev), desc,
+              test_bit(MD_RECOVERY_INTR, &mddev->recovery)
+              ? "interrupted" : "done");
        /*
         * this also signals 'finished resyncing' to md_stop
         */
- out:
        blk_finish_plug(&plug);
        wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active));
 
@@ -7640,16 +7676,6 @@ void md_do_sync(struct md_thread *thread)
        set_bit(MD_RECOVERY_DONE, &mddev->recovery);
        md_wakeup_thread(mddev->thread);
        return;
-
- interrupted:
-       /*
-        * got a signal, exit.
-        */
-       printk(KERN_INFO
-              "md: md_do_sync() got signal ... exiting\n");
-       set_bit(MD_RECOVERY_INTR, &mddev->recovery);
-       goto out;
-
 }
 EXPORT_SYMBOL_GPL(md_do_sync);
 
@@ -7894,6 +7920,7 @@ void md_reap_sync_thread(struct mddev *mddev)
 
        /* resync has finished, collect result */
        md_unregister_thread(&mddev->sync_thread);
+       wake_up(&resync_wait);
        if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
            !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
                /* success...*/
index af6681b..1e5a540 100644 (file)
@@ -66,7 +66,8 @@
  */
 static int max_queued_requests = 1024;
 
-static void allow_barrier(struct r1conf *conf);
+static void allow_barrier(struct r1conf *conf, sector_t start_next_window,
+                         sector_t bi_sector);
 static void lower_barrier(struct r1conf *conf);
 
 static void * r1bio_pool_alloc(gfp_t gfp_flags, void *data)
@@ -84,10 +85,12 @@ static void r1bio_pool_free(void *r1_bio, void *data)
 }
 
 #define RESYNC_BLOCK_SIZE (64*1024)
-//#define RESYNC_BLOCK_SIZE PAGE_SIZE
+#define RESYNC_DEPTH 32
 #define RESYNC_SECTORS (RESYNC_BLOCK_SIZE >> 9)
 #define RESYNC_PAGES ((RESYNC_BLOCK_SIZE + PAGE_SIZE-1) / PAGE_SIZE)
-#define RESYNC_WINDOW (2048*1024)
+#define RESYNC_WINDOW (RESYNC_BLOCK_SIZE * RESYNC_DEPTH)
+#define RESYNC_WINDOW_SECTORS (RESYNC_WINDOW >> 9)
+#define NEXT_NORMALIO_DISTANCE (3 * RESYNC_WINDOW_SECTORS)
 
 static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
 {
@@ -225,6 +228,8 @@ static void call_bio_endio(struct r1bio *r1_bio)
        struct bio *bio = r1_bio->master_bio;
        int done;
        struct r1conf *conf = r1_bio->mddev->private;
+       sector_t start_next_window = r1_bio->start_next_window;
+       sector_t bi_sector = bio->bi_sector;
 
        if (bio->bi_phys_segments) {
                unsigned long flags;
@@ -232,6 +237,11 @@ static void call_bio_endio(struct r1bio *r1_bio)
                bio->bi_phys_segments--;
                done = (bio->bi_phys_segments == 0);
                spin_unlock_irqrestore(&conf->device_lock, flags);
+               /*
+                * make_request() might be waiting for
+                * bi_phys_segments to decrease
+                */
+               wake_up(&conf->wait_barrier);
        } else
                done = 1;
 
@@ -243,7 +253,7 @@ static void call_bio_endio(struct r1bio *r1_bio)
                 * Wake up any possible resync thread that waits for the device
                 * to go idle.
                 */
-               allow_barrier(conf);
+               allow_barrier(conf, start_next_window, bi_sector);
        }
 }
 
@@ -814,8 +824,6 @@ static void flush_pending_writes(struct r1conf *conf)
  *    there is no normal IO happeing.  It must arrange to call
  *    lower_barrier when the particular background IO completes.
  */
-#define RESYNC_DEPTH 32
-
 static void raise_barrier(struct r1conf *conf)
 {
        spin_lock_irq(&conf->resync_lock);
@@ -827,9 +835,19 @@ static void raise_barrier(struct r1conf *conf)
        /* block any new IO from starting */
        conf->barrier++;
 
-       /* Now wait for all pending IO to complete */
+       /* For these conditions we must wait:
+        * A: while the array is in frozen state
+        * B: while barrier >= RESYNC_DEPTH, meaning resync reach
+        *    the max count which allowed.
+        * C: next_resync + RESYNC_SECTORS > start_next_window, meaning
+        *    next resync will reach to the window which normal bios are
+        *    handling.
+        */
        wait_event_lock_irq(conf->wait_barrier,
-                           !conf->nr_pending && conf->barrier < RESYNC_DEPTH,
+                           !conf->array_frozen &&
+                           conf->barrier < RESYNC_DEPTH &&
+                           (conf->start_next_window >=
+                            conf->next_resync + RESYNC_SECTORS),
                            conf->resync_lock);
 
        spin_unlock_irq(&conf->resync_lock);
@@ -845,10 +863,33 @@ static void lower_barrier(struct r1conf *conf)
        wake_up(&conf->wait_barrier);
 }
 
-static void wait_barrier(struct r1conf *conf)
+static bool need_to_wait_for_sync(struct r1conf *conf, struct bio *bio)
 {
+       bool wait = false;
+
+       if (conf->array_frozen || !bio)
+               wait = true;
+       else if (conf->barrier && bio_data_dir(bio) == WRITE) {
+               if (conf->next_resync < RESYNC_WINDOW_SECTORS)
+                       wait = true;
+               else if ((conf->next_resync - RESYNC_WINDOW_SECTORS
+                               >= bio_end_sector(bio)) ||
+                        (conf->next_resync + NEXT_NORMALIO_DISTANCE
+                               <= bio->bi_sector))
+                       wait = false;
+               else
+                       wait = true;
+       }
+
+       return wait;
+}
+
+static sector_t wait_barrier(struct r1conf *conf, struct bio *bio)
+{
+       sector_t sector = 0;
+
        spin_lock_irq(&conf->resync_lock);
-       if (conf->barrier) {
+       if (need_to_wait_for_sync(conf, bio)) {
                conf->nr_waiting++;
                /* Wait for the barrier to drop.
                 * However if there are already pending
@@ -860,22 +901,67 @@ static void wait_barrier(struct r1conf *conf)
                 * count down.
                 */
                wait_event_lock_irq(conf->wait_barrier,
-                                   !conf->barrier ||
-                                   (conf->nr_pending &&
+                                   !conf->array_frozen &&
+                                   (!conf->barrier ||
+                                   ((conf->start_next_window <
+                                     conf->next_resync + RESYNC_SECTORS) &&
                                     current->bio_list &&
-                                    !bio_list_empty(current->bio_list)),
+                                    !bio_list_empty(current->bio_list))),
                                    conf->resync_lock);
                conf->nr_waiting--;
        }
+
+       if (bio && bio_data_dir(bio) == WRITE) {
+               if (conf->next_resync + NEXT_NORMALIO_DISTANCE
+                   <= bio->bi_sector) {
+                       if (conf->start_next_window == MaxSector)
+                               conf->start_next_window =
+                                       conf->next_resync +
+                                       NEXT_NORMALIO_DISTANCE;
+
+                       if ((conf->start_next_window + NEXT_NORMALIO_DISTANCE)
+                           <= bio->bi_sector)
+                               conf->next_window_requests++;
+                       else
+                               conf->current_window_requests++;
+               }
+               if (bio->bi_sector >= conf->start_next_window)
+                       sector = conf->start_next_window;
+       }
+
        conf->nr_pending++;
        spin_unlock_irq(&conf->resync_lock);
+       return sector;
 }
 
-static void allow_barrier(struct r1conf *conf)
+static void allow_barrier(struct r1conf *conf, sector_t start_next_window,
+                         sector_t bi_sector)
 {
        unsigned long flags;
+
        spin_lock_irqsave(&conf->resync_lock, flags);
        conf->nr_pending--;
+       if (start_next_window) {
+               if (start_next_window == conf->start_next_window) {
+                       if (conf->start_next_window + NEXT_NORMALIO_DISTANCE
+                           <= bi_sector)
+                               conf->next_window_requests--;
+                       else
+                               conf->current_window_requests--;
+               } else
+                       conf->current_window_requests--;
+
+               if (!conf->current_window_requests) {
+                       if (conf->next_window_requests) {
+                               conf->current_window_requests =
+                                       conf->next_window_requests;
+                               conf->next_window_requests = 0;
+                               conf->start_next_window +=
+                                       NEXT_NORMALIO_DISTANCE;
+                       } else
+                               conf->start_next_window = MaxSector;
+               }
+       }
        spin_unlock_irqrestore(&conf->resync_lock, flags);
        wake_up(&conf->wait_barrier);
 }
@@ -884,8 +970,7 @@ static void freeze_array(struct r1conf *conf, int extra)
 {
        /* stop syncio and normal IO and wait for everything to
         * go quite.
-        * We increment barrier and nr_waiting, and then
-        * wait until nr_pending match nr_queued+extra
+        * We wait until nr_pending match nr_queued+extra
         * This is called in the context of one normal IO request
         * that has failed. Thus any sync request that might be pending
         * will be blocked by nr_pending, and we need to wait for
@@ -895,8 +980,7 @@ static void freeze_array(struct r1conf *conf, int extra)
         * we continue.
         */
        spin_lock_irq(&conf->resync_lock);
-       conf->barrier++;
-       conf->nr_waiting++;
+       conf->array_frozen = 1;
        wait_event_lock_irq_cmd(conf->wait_barrier,
                                conf->nr_pending == conf->nr_queued+extra,
                                conf->resync_lock,
@@ -907,8 +991,7 @@ static void unfreeze_array(struct r1conf *conf)
 {
        /* reverse the effect of the freeze */
        spin_lock_irq(&conf->resync_lock);
-       conf->barrier--;
-       conf->nr_waiting--;
+       conf->array_frozen = 0;
        wake_up(&conf->wait_barrier);
        spin_unlock_irq(&conf->resync_lock);
 }
@@ -1013,6 +1096,7 @@ static void make_request(struct mddev *mddev, struct bio * bio)
        int first_clone;
        int sectors_handled;
        int max_sectors;
+       sector_t start_next_window;
 
        /*
         * Register the new request and wait if the reconstruction
@@ -1042,7 +1126,7 @@ static void make_request(struct mddev *mddev, struct bio * bio)
                finish_wait(&conf->wait_barrier, &w);
        }
 
-       wait_barrier(conf);
+       start_next_window = wait_barrier(conf, bio);
 
        bitmap = mddev->bitmap;
 
@@ -1163,6 +1247,7 @@ read_again:
 
        disks = conf->raid_disks * 2;
  retry_write:
+       r1_bio->start_next_window = start_next_window;
        blocked_rdev = NULL;
        rcu_read_lock();
        max_sectors = r1_bio->sectors;
@@ -1231,14 +1316,24 @@ read_again:
        if (unlikely(blocked_rdev)) {
                /* Wait for this device to become unblocked */
                int j;
+               sector_t old = start_next_window;
 
                for (j = 0; j < i; j++)
                        if (r1_bio->bios[j])
                                rdev_dec_pending(conf->mirrors[j].rdev, mddev);
                r1_bio->state = 0;
-               allow_barrier(conf);
+               allow_barrier(conf, start_next_window, bio->bi_sector);
                md_wait_for_blocked_rdev(blocked_rdev, mddev);
-               wait_barrier(conf);
+               start_next_window = wait_barrier(conf, bio);
+               /*
+                * We must make sure the multi r1bios of bio have
+                * the same value of bi_phys_segments
+                */
+               if (bio->bi_phys_segments && old &&
+                   old != start_next_window)
+                       /* Wait for the former r1bio(s) to complete */
+                       wait_event(conf->wait_barrier,
+                                  bio->bi_phys_segments == 1);
                goto retry_write;
        }
 
@@ -1438,11 +1533,14 @@ static void print_conf(struct r1conf *conf)
 
 static void close_sync(struct r1conf *conf)
 {
-       wait_barrier(conf);
-       allow_barrier(conf);
+       wait_barrier(conf, NULL);
+       allow_barrier(conf, 0, 0);
 
        mempool_destroy(conf->r1buf_pool);
        conf->r1buf_pool = NULL;
+
+       conf->next_resync = 0;
+       conf->start_next_window = MaxSector;
 }
 
 static int raid1_spare_active(struct mddev *mddev)
@@ -2714,6 +2812,9 @@ static struct r1conf *setup_conf(struct mddev *mddev)
        conf->pending_count = 0;
        conf->recovery_disabled = mddev->recovery_disabled - 1;
 
+       conf->start_next_window = MaxSector;
+       conf->current_window_requests = conf->next_window_requests = 0;
+
        err = -EIO;
        for (i = 0; i < conf->raid_disks * 2; i++) {
 
@@ -2871,8 +2972,8 @@ static int stop(struct mddev *mddev)
                           atomic_read(&bitmap->behind_writes) == 0);
        }
 
-       raise_barrier(conf);
-       lower_barrier(conf);
+       freeze_array(conf, 0);
+       unfreeze_array(conf);
 
        md_unregister_thread(&mddev->thread);
        if (conf->r1bio_pool)
@@ -3031,10 +3132,10 @@ static void raid1_quiesce(struct mddev *mddev, int state)
                wake_up(&conf->wait_barrier);
                break;
        case 1:
-               raise_barrier(conf);
+               freeze_array(conf, 0);
                break;
        case 0:
-               lower_barrier(conf);
+               unfreeze_array(conf);
                break;
        }
 }
@@ -3051,7 +3152,8 @@ static void *raid1_takeover(struct mddev *mddev)
                mddev->new_chunk_sectors = 0;
                conf = setup_conf(mddev);
                if (!IS_ERR(conf))
-                       conf->barrier = 1;
+                       /* Array must appear to be quiesced */
+                       conf->array_frozen = 1;
                return conf;
        }
        return ERR_PTR(-EINVAL);
index 0ff3715..9bebca7 100644 (file)
@@ -41,6 +41,19 @@ struct r1conf {
         */
        sector_t                next_resync;
 
+       /* When raid1 starts resync, we divide array into four partitions
+        * |---------|--------------|---------------------|-------------|
+        *        next_resync   start_next_window       end_window
+        * start_next_window = next_resync + NEXT_NORMALIO_DISTANCE
+        * end_window = start_next_window + NEXT_NORMALIO_DISTANCE
+        * current_window_requests means the count of normalIO between
+        *   start_next_window and end_window.
+        * next_window_requests means the count of normalIO after end_window.
+        * */
+       sector_t                start_next_window;
+       int                     current_window_requests;
+       int                     next_window_requests;
+
        spinlock_t              device_lock;
 
        /* list of 'struct r1bio' that need to be processed by raid1d,
@@ -65,6 +78,7 @@ struct r1conf {
        int                     nr_waiting;
        int                     nr_queued;
        int                     barrier;
+       int                     array_frozen;
 
        /* Set to 1 if a full sync is needed, (fresh device added).
         * Cleared when a sync completes.
@@ -111,6 +125,7 @@ struct r1bio {
                                                 * in this BehindIO request
                                                 */
        sector_t                sector;
+       sector_t                start_next_window;
        int                     sectors;
        unsigned long           state;
        struct mddev            *mddev;
index 7c3508a..c504e83 100644 (file)
@@ -4384,7 +4384,11 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
                set_bit(MD_CHANGE_DEVS, &mddev->flags);
                md_wakeup_thread(mddev->thread);
                wait_event(mddev->sb_wait, mddev->flags == 0 ||
-                          kthread_should_stop());
+                          test_bit(MD_RECOVERY_INTR, &mddev->recovery));
+               if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
+                       allow_barrier(conf);
+                       return sectors_done;
+               }
                conf->reshape_safe = mddev->reshape_position;
                allow_barrier(conf);
        }
index f8b9068..47da0af 100644 (file)
@@ -85,6 +85,42 @@ static inline struct hlist_head *stripe_hash(struct r5conf *conf, sector_t sect)
        return &conf->stripe_hashtbl[hash];
 }
 
+static inline int stripe_hash_locks_hash(sector_t sect)
+{
+       return (sect >> STRIPE_SHIFT) & STRIPE_HASH_LOCKS_MASK;
+}
+
+static inline void lock_device_hash_lock(struct r5conf *conf, int hash)
+{
+       spin_lock_irq(conf->hash_locks + hash);
+       spin_lock(&conf->device_lock);
+}
+
+static inline void unlock_device_hash_lock(struct r5conf *conf, int hash)
+{
+       spin_unlock(&conf->device_lock);
+       spin_unlock_irq(conf->hash_locks + hash);
+}
+
+static inline void lock_all_device_hash_locks_irq(struct r5conf *conf)
+{
+       int i;
+       local_irq_disable();
+       spin_lock(conf->hash_locks);
+       for (i = 1; i < NR_STRIPE_HASH_LOCKS; i++)
+               spin_lock_nest_lock(conf->hash_locks + i, conf->hash_locks);
+       spin_lock(&conf->device_lock);
+}
+
+static inline void unlock_all_device_hash_locks_irq(struct r5conf *conf)
+{
+       int i;
+       spin_unlock(&conf->device_lock);
+       for (i = NR_STRIPE_HASH_LOCKS; i; i--)
+               spin_unlock(conf->hash_locks + i - 1);
+       local_irq_enable();
+}
+
 /* bio's attached to a stripe+device for I/O are linked together in bi_sector
  * order without overlap.  There may be several bio's per stripe+device, and
  * a bio could span several devices.
@@ -249,7 +285,8 @@ static void raid5_wakeup_stripe_thread(struct stripe_head *sh)
        }
 }
 
-static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh)
+static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh,
+                             struct list_head *temp_inactive_list)
 {
        BUG_ON(!list_empty(&sh->lru));
        BUG_ON(atomic_read(&conf->active_stripes)==0);
@@ -278,37 +315,68 @@ static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh)
                            < 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);
-               }
+               if (!test_bit(STRIPE_EXPANDING, &sh->state))
+                       list_add_tail(&sh->lru, temp_inactive_list);
        }
 }
 
-static void __release_stripe(struct r5conf *conf, struct stripe_head *sh)
+static void __release_stripe(struct r5conf *conf, struct stripe_head *sh,
+                            struct list_head *temp_inactive_list)
 {
        if (atomic_dec_and_test(&sh->count))
-               do_release_stripe(conf, sh);
+               do_release_stripe(conf, sh, temp_inactive_list);
 }
 
-static struct llist_node *llist_reverse_order(struct llist_node *head)
+/*
+ * @hash could be NR_STRIPE_HASH_LOCKS, then we have a list of inactive_list
+ *
+ * Be careful: Only one task can add/delete stripes from temp_inactive_list at
+ * given time. Adding stripes only takes device lock, while deleting stripes
+ * only takes hash lock.
+ */
+static void release_inactive_stripe_list(struct r5conf *conf,
+                                        struct list_head *temp_inactive_list,
+                                        int hash)
 {
-       struct llist_node *new_head = NULL;
+       int size;
+       bool do_wakeup = false;
+       unsigned long flags;
 
-       while (head) {
-               struct llist_node *tmp = head;
-               head = head->next;
-               tmp->next = new_head;
-               new_head = tmp;
+       if (hash == NR_STRIPE_HASH_LOCKS) {
+               size = NR_STRIPE_HASH_LOCKS;
+               hash = NR_STRIPE_HASH_LOCKS - 1;
+       } else
+               size = 1;
+       while (size) {
+               struct list_head *list = &temp_inactive_list[size - 1];
+
+               /*
+                * We don't hold any lock here yet, get_active_stripe() might
+                * remove stripes from the list
+                */
+               if (!list_empty_careful(list)) {
+                       spin_lock_irqsave(conf->hash_locks + hash, flags);
+                       if (list_empty(conf->inactive_list + hash) &&
+                           !list_empty(list))
+                               atomic_dec(&conf->empty_inactive_list_nr);
+                       list_splice_tail_init(list, conf->inactive_list + hash);
+                       do_wakeup = true;
+                       spin_unlock_irqrestore(conf->hash_locks + hash, flags);
+               }
+               size--;
+               hash--;
        }
 
-       return new_head;
+       if (do_wakeup) {
+               wake_up(&conf->wait_for_stripe);
+               if (conf->retry_read_aligned)
+                       md_wakeup_thread(conf->mddev->thread);
+       }
 }
 
 /* should hold conf->device_lock already */
-static int release_stripe_list(struct r5conf *conf)
+static int release_stripe_list(struct r5conf *conf,
+                              struct list_head *temp_inactive_list)
 {
        struct stripe_head *sh;
        int count = 0;
@@ -317,6 +385,8 @@ static int release_stripe_list(struct r5conf *conf)
        head = llist_del_all(&conf->released_stripes);
        head = llist_reverse_order(head);
        while (head) {
+               int hash;
+
                sh = llist_entry(head, struct stripe_head, release_list);
                head = llist_next(head);
                /* sh could be readded after STRIPE_ON_RELEASE_LIST is cleard */
@@ -327,7 +397,8 @@ static int release_stripe_list(struct r5conf *conf)
                 * again, the count is always > 1. This is true for
                 * STRIPE_ON_UNPLUG_LIST bit too.
                 */
-               __release_stripe(conf, sh);
+               hash = sh->hash_lock_index;
+               __release_stripe(conf, sh, &temp_inactive_list[hash]);
                count++;
        }
 
@@ -338,9 +409,12 @@ static void release_stripe(struct stripe_head *sh)
 {
        struct r5conf *conf = sh->raid_conf;
        unsigned long flags;
+       struct list_head list;
+       int hash;
        bool wakeup;
 
-       if (test_and_set_bit(STRIPE_ON_RELEASE_LIST, &sh->state))
+       if (unlikely(!conf->mddev->thread) ||
+               test_and_set_bit(STRIPE_ON_RELEASE_LIST, &sh->state))
                goto slow_path;
        wakeup = llist_add(&sh->release_list, &conf->released_stripes);
        if (wakeup)
@@ -350,8 +424,11 @@ slow_path:
        local_irq_save(flags);
        /* we are ok here if STRIPE_ON_RELEASE_LIST is set or not */
        if (atomic_dec_and_lock(&sh->count, &conf->device_lock)) {
-               do_release_stripe(conf, sh);
+               INIT_LIST_HEAD(&list);
+               hash = sh->hash_lock_index;
+               do_release_stripe(conf, sh, &list);
                spin_unlock(&conf->device_lock);
+               release_inactive_stripe_list(conf, &list, hash);
        }
        local_irq_restore(flags);
 }
@@ -376,18 +453,21 @@ static inline void insert_hash(struct r5conf *conf, struct stripe_head *sh)
 
 
 /* find an idle stripe, make sure it is unhashed, and return it. */
-static struct stripe_head *get_free_stripe(struct r5conf *conf)
+static struct stripe_head *get_free_stripe(struct r5conf *conf, int hash)
 {
        struct stripe_head *sh = NULL;
        struct list_head *first;
 
-       if (list_empty(&conf->inactive_list))
+       if (list_empty(conf->inactive_list + hash))
                goto out;
-       first = conf->inactive_list.next;
+       first = (conf->inactive_list + hash)->next;
        sh = list_entry(first, struct stripe_head, lru);
        list_del_init(first);
        remove_hash(sh);
        atomic_inc(&conf->active_stripes);
+       BUG_ON(hash != sh->hash_lock_index);
+       if (list_empty(conf->inactive_list + hash))
+               atomic_inc(&conf->empty_inactive_list_nr);
 out:
        return sh;
 }
@@ -430,7 +510,7 @@ static void stripe_set_idx(sector_t stripe, struct r5conf *conf, int previous,
 static void init_stripe(struct stripe_head *sh, sector_t sector, int previous)
 {
        struct r5conf *conf = sh->raid_conf;
-       int i;
+       int i, seq;
 
        BUG_ON(atomic_read(&sh->count) != 0);
        BUG_ON(test_bit(STRIPE_HANDLE, &sh->state));
@@ -440,7 +520,8 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int previous)
                (unsigned long long)sh->sector);
 
        remove_hash(sh);
-
+retry:
+       seq = read_seqcount_begin(&conf->gen_lock);
        sh->generation = conf->generation - previous;
        sh->disks = previous ? conf->previous_raid_disks : conf->raid_disks;
        sh->sector = sector;
@@ -462,6 +543,8 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int previous)
                dev->flags = 0;
                raid5_build_block(sh, i, previous);
        }
+       if (read_seqcount_retry(&conf->gen_lock, seq))
+               goto retry;
        insert_hash(conf, sh);
        sh->cpu = smp_processor_id();
 }
@@ -566,29 +649,31 @@ get_active_stripe(struct r5conf *conf, sector_t sector,
                  int previous, int noblock, int noquiesce)
 {
        struct stripe_head *sh;
+       int hash = stripe_hash_locks_hash(sector);
 
        pr_debug("get_stripe, sector %llu\n", (unsigned long long)sector);
 
-       spin_lock_irq(&conf->device_lock);
+       spin_lock_irq(conf->hash_locks + hash);
 
        do {
                wait_event_lock_irq(conf->wait_for_stripe,
                                    conf->quiesce == 0 || noquiesce,
-                                   conf->device_lock);
+                                   *(conf->hash_locks + hash));
                sh = __find_stripe(conf, sector, conf->generation - previous);
                if (!sh) {
                        if (!conf->inactive_blocked)
-                               sh = get_free_stripe(conf);
+                               sh = get_free_stripe(conf, hash);
                        if (noblock && sh == NULL)
                                break;
                        if (!sh) {
                                conf->inactive_blocked = 1;
-                               wait_event_lock_irq(conf->wait_for_stripe,
-                                                   !list_empty(&conf->inactive_list) &&
-                                                   (atomic_read(&conf->active_stripes)
-                                                    < (conf->max_nr_stripes *3/4)
-                                                    || !conf->inactive_blocked),
-                                                   conf->device_lock);
+                               wait_event_lock_irq(
+                                       conf->wait_for_stripe,
+                                       !list_empty(conf->inactive_list + hash) &&
+                                       (atomic_read(&conf->active_stripes)
+                                        < (conf->max_nr_stripes * 3 / 4)
+                                        || !conf->inactive_blocked),
+                                       *(conf->hash_locks + hash));
                                conf->inactive_blocked = 0;
                        } else
                                init_stripe(sh, sector, previous);
@@ -599,9 +684,11 @@ get_active_stripe(struct r5conf *conf, sector_t sector,
                                    && !test_bit(STRIPE_ON_UNPLUG_LIST, &sh->state)
                                    && !test_bit(STRIPE_ON_RELEASE_LIST, &sh->state));
                        } else {
+                               spin_lock(&conf->device_lock);
                                if (!test_bit(STRIPE_HANDLE, &sh->state))
                                        atomic_inc(&conf->active_stripes);
                                if (list_empty(&sh->lru) &&
+                                   !test_bit(STRIPE_ON_RELEASE_LIST, &sh->state) &&
                                    !test_bit(STRIPE_EXPANDING, &sh->state))
                                        BUG();
                                list_del_init(&sh->lru);
@@ -609,6 +696,7 @@ get_active_stripe(struct r5conf *conf, sector_t sector,
                                        sh->group->stripes_cnt--;
                                        sh->group = NULL;
                                }
+                               spin_unlock(&conf->device_lock);
                        }
                }
        } while (sh == NULL);
@@ -616,7 +704,7 @@ get_active_stripe(struct r5conf *conf, sector_t sector,
        if (sh)
                atomic_inc(&sh->count);
 
-       spin_unlock_irq(&conf->device_lock);
+       spin_unlock_irq(conf->hash_locks + hash);
        return sh;
 }
 
@@ -772,7 +860,7 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
                                bi->bi_sector = (sh->sector
                                                 + rdev->data_offset);
                        if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags))
-                               bi->bi_rw |= REQ_FLUSH;
+                               bi->bi_rw |= REQ_NOMERGE;
 
                        bi->bi_vcnt = 1;
                        bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
@@ -1596,7 +1684,7 @@ static void raid_run_ops(struct stripe_head *sh, unsigned long ops_request)
        put_cpu();
 }
 
-static int grow_one_stripe(struct r5conf *conf)
+static int grow_one_stripe(struct r5conf *conf, int hash)
 {
        struct stripe_head *sh;
        sh = kmem_cache_zalloc(conf->slab_cache, GFP_KERNEL);
@@ -1612,6 +1700,7 @@ static int grow_one_stripe(struct r5conf *conf)
                kmem_cache_free(conf->slab_cache, sh);
                return 0;
        }
+       sh->hash_lock_index = hash;
        /* we just created an active stripe so... */
        atomic_set(&sh->count, 1);
        atomic_inc(&conf->active_stripes);
@@ -1624,6 +1713,7 @@ static int grow_stripes(struct r5conf *conf, int num)
 {
        struct kmem_cache *sc;
        int devs = max(conf->raid_disks, conf->previous_raid_disks);
+       int hash;
 
        if (conf->mddev->gendisk)
                sprintf(conf->cache_name[0],
@@ -1641,9 +1731,13 @@ static int grow_stripes(struct r5conf *conf, int num)
                return 1;
        conf->slab_cache = sc;
        conf->pool_size = devs;
-       while (num--)
-               if (!grow_one_stripe(conf))
+       hash = conf->max_nr_stripes % NR_STRIPE_HASH_LOCKS;
+       while (num--) {
+               if (!grow_one_stripe(conf, hash))
                        return 1;
+               conf->max_nr_stripes++;
+               hash = (hash + 1) % NR_STRIPE_HASH_LOCKS;
+       }
        return 0;
 }
 
@@ -1701,6 +1795,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
        int err;
        struct kmem_cache *sc;
        int i;
+       int hash, cnt;
 
        if (newsize <= conf->pool_size)
                return 0; /* never bother to shrink */
@@ -1740,19 +1835,29 @@ static int resize_stripes(struct r5conf *conf, int newsize)
         * OK, we have enough stripes, start collecting inactive
         * stripes and copying them over
         */
+       hash = 0;
+       cnt = 0;
        list_for_each_entry(nsh, &newstripes, lru) {
-               spin_lock_irq(&conf->device_lock);
-               wait_event_lock_irq(conf->wait_for_stripe,
-                                   !list_empty(&conf->inactive_list),
-                                   conf->device_lock);
-               osh = get_free_stripe(conf);
-               spin_unlock_irq(&conf->device_lock);
+               lock_device_hash_lock(conf, hash);
+               wait_event_cmd(conf->wait_for_stripe,
+                                   !list_empty(conf->inactive_list + hash),
+                                   unlock_device_hash_lock(conf, hash),
+                                   lock_device_hash_lock(conf, hash));
+               osh = get_free_stripe(conf, hash);
+               unlock_device_hash_lock(conf, hash);
                atomic_set(&nsh->count, 1);
                for(i=0; i<conf->pool_size; i++)
                        nsh->dev[i].page = osh->dev[i].page;
                for( ; i<newsize; i++)
                        nsh->dev[i].page = NULL;
+               nsh->hash_lock_index = hash;
                kmem_cache_free(conf->slab_cache, osh);
+               cnt++;
+               if (cnt >= conf->max_nr_stripes / NR_STRIPE_HASH_LOCKS +
+                   !!((conf->max_nr_stripes % NR_STRIPE_HASH_LOCKS) > hash)) {
+                       hash++;
+                       cnt = 0;
+               }
        }
        kmem_cache_destroy(conf->slab_cache);
 
@@ -1811,13 +1916,13 @@ static int resize_stripes(struct r5conf *conf, int newsize)
        return err;
 }
 
-static int drop_one_stripe(struct r5conf *conf)
+static int drop_one_stripe(struct r5conf *conf, int hash)
 {
        struct stripe_head *sh;
 
-       spin_lock_irq(&conf->device_lock);
-       sh = get_free_stripe(conf);
-       spin_unlock_irq(&conf->device_lock);
+       spin_lock_irq(conf->hash_locks + hash);
+       sh = get_free_stripe(conf, hash);
+       spin_unlock_irq(conf->hash_locks + hash);
        if (!sh)
                return 0;
        BUG_ON(atomic_read(&sh->count));
@@ -1829,8 +1934,10 @@ static int drop_one_stripe(struct r5conf *conf)
 
 static void shrink_stripes(struct r5conf *conf)
 {
-       while (drop_one_stripe(conf))
-               ;
+       int hash;
+       for (hash = 0; hash < NR_STRIPE_HASH_LOCKS; hash++)
+               while (drop_one_stripe(conf, hash))
+                       ;
 
        if (conf->slab_cache)
                kmem_cache_destroy(conf->slab_cache);
@@ -1935,6 +2042,9 @@ static void raid5_end_read_request(struct bio * bi, int error)
                               mdname(conf->mddev), bdn);
                else
                        retry = 1;
+               if (set_bad && test_bit(In_sync, &rdev->flags)
+                   && !test_bit(R5_ReadNoMerge, &sh->dev[i].flags))
+                       retry = 1;
                if (retry)
                        if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags)) {
                                set_bit(R5_ReadError, &sh->dev[i].flags);
@@ -3914,7 +4024,8 @@ static void raid5_activate_delayed(struct r5conf *conf)
        }
 }
 
-static void activate_bit_delay(struct r5conf *conf)
+static void activate_bit_delay(struct r5conf *conf,
+       struct list_head *temp_inactive_list)
 {
        /* device_lock is held */
        struct list_head head;
@@ -3922,9 +4033,11 @@ static void activate_bit_delay(struct r5conf *conf)
        list_del_init(&conf->bitmap_list);
        while (!list_empty(&head)) {
                struct stripe_head *sh = list_entry(head.next, struct stripe_head, lru);
+               int hash;
                list_del_init(&sh->lru);
                atomic_inc(&sh->count);
-               __release_stripe(conf, sh);
+               hash = sh->hash_lock_index;
+               __release_stripe(conf, sh, &temp_inactive_list[hash]);
        }
 }
 
@@ -3940,7 +4053,7 @@ int md_raid5_congested(struct mddev *mddev, int bits)
                return 1;
        if (conf->quiesce)
                return 1;
-       if (list_empty_careful(&conf->inactive_list))
+       if (atomic_read(&conf->empty_inactive_list_nr))
                return 1;
 
        return 0;
@@ -4270,6 +4383,7 @@ static struct stripe_head *__get_priority_stripe(struct r5conf *conf, int group)
 struct raid5_plug_cb {
        struct blk_plug_cb      cb;
        struct list_head        list;
+       struct list_head        temp_inactive_list[NR_STRIPE_HASH_LOCKS];
 };
 
 static void raid5_unplug(struct blk_plug_cb *blk_cb, bool from_schedule)
@@ -4280,6 +4394,7 @@ static void raid5_unplug(struct blk_plug_cb *blk_cb, bool from_schedule)
        struct mddev *mddev = cb->cb.data;
        struct r5conf *conf = mddev->private;
        int cnt = 0;
+       int hash;
 
        if (cb->list.next && !list_empty(&cb->list)) {
                spin_lock_irq(&conf->device_lock);
@@ -4297,11 +4412,14 @@ static void raid5_unplug(struct blk_plug_cb *blk_cb, bool from_schedule)
                         * STRIPE_ON_RELEASE_LIST could be set here. In that
                         * case, the count is always > 1 here
                         */
-                       __release_stripe(conf, sh);
+                       hash = sh->hash_lock_index;
+                       __release_stripe(conf, sh, &cb->temp_inactive_list[hash]);
                        cnt++;
                }
                spin_unlock_irq(&conf->device_lock);
        }
+       release_inactive_stripe_list(conf, cb->temp_inactive_list,
+                                    NR_STRIPE_HASH_LOCKS);
        if (mddev->queue)
                trace_block_unplug(mddev->queue, cnt, !from_schedule);
        kfree(cb);
@@ -4322,8 +4440,12 @@ static void release_stripe_plug(struct mddev *mddev,
 
        cb = container_of(blk_cb, struct raid5_plug_cb, cb);
 
-       if (cb->list.next == NULL)
+       if (cb->list.next == NULL) {
+               int i;
                INIT_LIST_HEAD(&cb->list);
+               for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++)
+                       INIT_LIST_HEAD(cb->temp_inactive_list + i);
+       }
 
        if (!test_and_set_bit(STRIPE_ON_UNPLUG_LIST, &sh->state))
                list_add_tail(&sh->lru, &cb->list);
@@ -4706,14 +4828,19 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr, int *sk
            time_after(jiffies, conf->reshape_checkpoint + 10*HZ)) {
                /* Cannot proceed until we've updated the superblock... */
                wait_event(conf->wait_for_overlap,
-                          atomic_read(&conf->reshape_stripes)==0);
+                          atomic_read(&conf->reshape_stripes)==0
+                          || test_bit(MD_RECOVERY_INTR, &mddev->recovery));
+               if (atomic_read(&conf->reshape_stripes) != 0)
+                       return 0;
                mddev->reshape_position = conf->reshape_progress;
                mddev->curr_resync_completed = sector_nr;
                conf->reshape_checkpoint = jiffies;
                set_bit(MD_CHANGE_DEVS, &mddev->flags);
                md_wakeup_thread(mddev->thread);
                wait_event(mddev->sb_wait, mddev->flags == 0 ||
-                          kthread_should_stop());
+                          test_bit(MD_RECOVERY_INTR, &mddev->recovery));
+               if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
+                       return 0;
                spin_lock_irq(&conf->device_lock);
                conf->reshape_safe = mddev->reshape_position;
                spin_unlock_irq(&conf->device_lock);
@@ -4796,7 +4923,10 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr, int *sk
            >= mddev->resync_max - mddev->curr_resync_completed) {
                /* Cannot proceed until we've updated the superblock... */
                wait_event(conf->wait_for_overlap,
-                          atomic_read(&conf->reshape_stripes) == 0);
+                          atomic_read(&conf->reshape_stripes) == 0
+                          || test_bit(MD_RECOVERY_INTR, &mddev->recovery));
+               if (atomic_read(&conf->reshape_stripes) != 0)
+                       goto ret;
                mddev->reshape_position = conf->reshape_progress;
                mddev->curr_resync_completed = sector_nr;
                conf->reshape_checkpoint = jiffies;
@@ -4804,13 +4934,16 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr, int *sk
                md_wakeup_thread(mddev->thread);
                wait_event(mddev->sb_wait,
                           !test_bit(MD_CHANGE_DEVS, &mddev->flags)
-                          || kthread_should_stop());
+                          || test_bit(MD_RECOVERY_INTR, &mddev->recovery));
+               if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
+                       goto ret;
                spin_lock_irq(&conf->device_lock);
                conf->reshape_safe = mddev->reshape_position;
                spin_unlock_irq(&conf->device_lock);
                wake_up(&conf->wait_for_overlap);
                sysfs_notify(&mddev->kobj, NULL, "sync_completed");
        }
+ret:
        return reshape_sectors;
 }
 
@@ -4968,27 +5101,45 @@ static int  retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
 }
 
 static int handle_active_stripes(struct r5conf *conf, int group,
-                                struct r5worker *worker)
+                                struct r5worker *worker,
+                                struct list_head *temp_inactive_list)
 {
        struct stripe_head *batch[MAX_STRIPE_BATCH], *sh;
-       int i, batch_size = 0;
+       int i, batch_size = 0, hash;
+       bool release_inactive = false;
 
        while (batch_size < MAX_STRIPE_BATCH &&
                        (sh = __get_priority_stripe(conf, group)) != NULL)
                batch[batch_size++] = sh;
 
-       if (batch_size == 0)
-               return batch_size;
+       if (batch_size == 0) {
+               for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++)
+                       if (!list_empty(temp_inactive_list + i))
+                               break;
+               if (i == NR_STRIPE_HASH_LOCKS)
+                       return batch_size;
+               release_inactive = true;
+       }
        spin_unlock_irq(&conf->device_lock);
 
+       release_inactive_stripe_list(conf, temp_inactive_list,
+                                    NR_STRIPE_HASH_LOCKS);
+
+       if (release_inactive) {
+               spin_lock_irq(&conf->device_lock);
+               return 0;
+       }
+
        for (i = 0; i < batch_size; i++)
                handle_stripe(batch[i]);
 
        cond_resched();
 
        spin_lock_irq(&conf->device_lock);
-       for (i = 0; i < batch_size; i++)
-               __release_stripe(conf, batch[i]);
+       for (i = 0; i < batch_size; i++) {
+               hash = batch[i]->hash_lock_index;
+               __release_stripe(conf, batch[i], &temp_inactive_list[hash]);
+       }
        return batch_size;
 }
 
@@ -5009,9 +5160,10 @@ static void raid5_do_work(struct work_struct *work)
        while (1) {
                int batch_size, released;
 
-               released = release_stripe_list(conf);
+               released = release_stripe_list(conf, worker->temp_inactive_list);
 
-               batch_size = handle_active_stripes(conf, group_id, worker);
+               batch_size = handle_active_stripes(conf, group_id, worker,
+                                                  worker->temp_inactive_list);
                worker->working = false;
                if (!batch_size && !released)
                        break;
@@ -5050,7 +5202,7 @@ static void raid5d(struct md_thread *thread)
                struct bio *bio;
                int batch_size, released;
 
-               released = release_stripe_list(conf);
+               released = release_stripe_list(conf, conf->temp_inactive_list);
 
                if (
                    !list_empty(&conf->bitmap_list)) {
@@ -5060,7 +5212,7 @@ static void raid5d(struct md_thread *thread)
                        bitmap_unplug(mddev->bitmap);
                        spin_lock_irq(&conf->device_lock);
                        conf->seq_write = conf->seq_flush;
-                       activate_bit_delay(conf);
+                       activate_bit_delay(conf, conf->temp_inactive_list);
                }
                raid5_activate_delayed(conf);
 
@@ -5074,7 +5226,8 @@ static void raid5d(struct md_thread *thread)
                        handled++;
                }
 
-               batch_size = handle_active_stripes(conf, ANY_GROUP, NULL);
+               batch_size = handle_active_stripes(conf, ANY_GROUP, NULL,
+                                                  conf->temp_inactive_list);
                if (!batch_size && !released)
                        break;
                handled += batch_size;
@@ -5110,22 +5263,29 @@ raid5_set_cache_size(struct mddev *mddev, int size)
 {
        struct r5conf *conf = mddev->private;
        int err;
+       int hash;
 
        if (size <= 16 || size > 32768)
                return -EINVAL;
+       hash = (conf->max_nr_stripes - 1) % NR_STRIPE_HASH_LOCKS;
        while (size < conf->max_nr_stripes) {
-               if (drop_one_stripe(conf))
+               if (drop_one_stripe(conf, hash))
                        conf->max_nr_stripes--;
                else
                        break;
+               hash--;
+               if (hash < 0)
+                       hash = NR_STRIPE_HASH_LOCKS - 1;
        }
        err = md_allow_write(mddev);
        if (err)
                return err;
+       hash = conf->max_nr_stripes % NR_STRIPE_HASH_LOCKS;
        while (size > conf->max_nr_stripes) {
-               if (grow_one_stripe(conf))
+               if (grow_one_stripe(conf, hash))
                        conf->max_nr_stripes++;
                else break;
+               hash = (hash + 1) % NR_STRIPE_HASH_LOCKS;
        }
        return 0;
 }
@@ -5213,15 +5373,18 @@ raid5_show_group_thread_cnt(struct mddev *mddev, char *page)
                return 0;
 }
 
-static int alloc_thread_groups(struct r5conf *conf, int cnt);
+static int alloc_thread_groups(struct r5conf *conf, int cnt,
+                              int *group_cnt,
+                              int *worker_cnt_per_group,
+                              struct r5worker_group **worker_groups);
 static ssize_t
 raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len)
 {
        struct r5conf *conf = mddev->private;
        unsigned long new;
        int err;
-       struct r5worker_group *old_groups;
-       int old_group_cnt;
+       struct r5worker_group *new_groups, *old_groups;
+       int group_cnt, worker_cnt_per_group;
 
        if (len >= PAGE_SIZE)
                return -EINVAL;
@@ -5237,14 +5400,19 @@ raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len)
        mddev_suspend(mddev);
 
        old_groups = conf->worker_groups;
-       old_group_cnt = conf->worker_cnt_per_group;
+       if (old_groups)
+               flush_workqueue(raid5_wq);
+
+       err = alloc_thread_groups(conf, new,
+                                 &group_cnt, &worker_cnt_per_group,
+                                 &new_groups);
+       if (!err) {
+               spin_lock_irq(&conf->device_lock);
+               conf->group_cnt = group_cnt;
+               conf->worker_cnt_per_group = worker_cnt_per_group;
+               conf->worker_groups = new_groups;
+               spin_unlock_irq(&conf->device_lock);
 
-       conf->worker_groups = NULL;
-       err = alloc_thread_groups(conf, new);
-       if (err) {
-               conf->worker_groups = old_groups;
-               conf->worker_cnt_per_group = old_group_cnt;
-       } else {
                if (old_groups)
                        kfree(old_groups[0].workers);
                kfree(old_groups);
@@ -5274,40 +5442,47 @@ static struct attribute_group raid5_attrs_group = {
        .attrs = raid5_attrs,
 };
 
-static int alloc_thread_groups(struct r5conf *conf, int cnt)
+static int alloc_thread_groups(struct r5conf *conf, int cnt,
+                              int *group_cnt,
+                              int *worker_cnt_per_group,
+                              struct r5worker_group **worker_groups)
 {
-       int i, j;
+       int i, j, k;
        ssize_t size;
        struct r5worker *workers;
 
-       conf->worker_cnt_per_group = cnt;
+       *worker_cnt_per_group = cnt;
        if (cnt == 0) {
-               conf->worker_groups = NULL;
+               *group_cnt = 0;
+               *worker_groups = NULL;
                return 0;
        }
-       conf->group_cnt = num_possible_nodes();
+       *group_cnt = num_possible_nodes();
        size = sizeof(struct r5worker) * cnt;
-       workers = kzalloc(size * conf->group_cnt, GFP_NOIO);
-       conf->worker_groups = kzalloc(sizeof(struct r5worker_group) *
-                               conf->group_cnt, GFP_NOIO);
-       if (!conf->worker_groups || !workers) {
+       workers = kzalloc(size * *group_cnt, GFP_NOIO);
+       *worker_groups = kzalloc(sizeof(struct r5worker_group) *
+                               *group_cnt, GFP_NOIO);
+       if (!*worker_groups || !workers) {
                kfree(workers);
-               kfree(conf->worker_groups);
-               conf->worker_groups = NULL;
+               kfree(*worker_groups);
                return -ENOMEM;
        }
 
-       for (i = 0; i < conf->group_cnt; i++) {
+       for (i = 0; i < *group_cnt; i++) {
                struct r5worker_group *group;
 
-               group = &conf->worker_groups[i];
+               group = worker_groups[i];
                INIT_LIST_HEAD(&group->handle_list);
                group->conf = conf;
                group->workers = workers + i * cnt;
 
                for (j = 0; j < cnt; j++) {
-                       group->workers[j].group = group;
-                       INIT_WORK(&group->workers[j].work, raid5_do_work);
+                       struct r5worker *worker = group->workers + j;
+                       worker->group = group;
+                       INIT_WORK(&worker->work, raid5_do_work);
+
+                       for (k = 0; k < NR_STRIPE_HASH_LOCKS; k++)
+                               INIT_LIST_HEAD(worker->temp_inactive_list + k);
                }
        }
 
@@ -5458,6 +5633,9 @@ static struct r5conf *setup_conf(struct mddev *mddev)
        struct md_rdev *rdev;
        struct disk_info *disk;
        char pers_name[6];
+       int i;
+       int group_cnt, worker_cnt_per_group;
+       struct r5worker_group *new_group;
 
        if (mddev->new_level != 5
            && mddev->new_level != 4
@@ -5492,7 +5670,12 @@ static struct r5conf *setup_conf(struct mddev *mddev)
        if (conf == NULL)
                goto abort;
        /* Don't enable multi-threading by default*/
-       if (alloc_thread_groups(conf, 0))
+       if (!alloc_thread_groups(conf, 0, &group_cnt, &worker_cnt_per_group,
+                                &new_group)) {
+               conf->group_cnt = group_cnt;
+               conf->worker_cnt_per_group = worker_cnt_per_group;
+               conf->worker_groups = new_group;
+       } else
                goto abort;
        spin_lock_init(&conf->device_lock);
        seqcount_init(&conf->gen_lock);
@@ -5502,7 +5685,6 @@ static struct r5conf *setup_conf(struct mddev *mddev)
        INIT_LIST_HEAD(&conf->hold_list);
        INIT_LIST_HEAD(&conf->delayed_list);
        INIT_LIST_HEAD(&conf->bitmap_list);
-       INIT_LIST_HEAD(&conf->inactive_list);
        init_llist_head(&conf->released_stripes);
        atomic_set(&conf->active_stripes, 0);
        atomic_set(&conf->preread_active_stripes, 0);
@@ -5528,6 +5710,21 @@ static struct r5conf *setup_conf(struct mddev *mddev)
        if ((conf->stripe_hashtbl = kzalloc(PAGE_SIZE, GFP_KERNEL)) == NULL)
                goto abort;
 
+       /* We init hash_locks[0] separately to that it can be used
+        * as the reference lock in the spin_lock_nest_lock() call
+        * in lock_all_device_hash_locks_irq in order to convince
+        * lockdep that we know what we are doing.
+        */
+       spin_lock_init(conf->hash_locks);
+       for (i = 1; i < NR_STRIPE_HASH_LOCKS; i++)
+               spin_lock_init(conf->hash_locks + i);
+
+       for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++)
+               INIT_LIST_HEAD(conf->inactive_list + i);
+
+       for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++)
+               INIT_LIST_HEAD(conf->temp_inactive_list + i);
+
        conf->level = mddev->new_level;
        if (raid5_alloc_percpu(conf) != 0)
                goto abort;
@@ -5568,7 +5765,6 @@ static struct r5conf *setup_conf(struct mddev *mddev)
        else
                conf->max_degraded = 1;
        conf->algorithm = mddev->new_layout;
-       conf->max_nr_stripes = NR_STRIPES;
        conf->reshape_progress = mddev->reshape_position;
        if (conf->reshape_progress != MaxSector) {
                conf->prev_chunk_sectors = mddev->chunk_sectors;
@@ -5577,7 +5773,8 @@ static struct r5conf *setup_conf(struct mddev *mddev)
 
        memory = conf->max_nr_stripes * (sizeof(struct stripe_head) +
                 max_disks * ((sizeof(struct bio) + PAGE_SIZE))) / 1024;
-       if (grow_stripes(conf, conf->max_nr_stripes)) {
+       atomic_set(&conf->empty_inactive_list_nr, NR_STRIPE_HASH_LOCKS);
+       if (grow_stripes(conf, NR_STRIPES)) {
                printk(KERN_ERR
                       "md/raid:%s: couldn't allocate %dkB for buffers\n",
                       mdname(mddev), memory);
@@ -6383,12 +6580,18 @@ static int raid5_start_reshape(struct mddev *mddev)
        if (!mddev->sync_thread) {
                mddev->recovery = 0;
                spin_lock_irq(&conf->device_lock);
+               write_seqcount_begin(&conf->gen_lock);
                mddev->raid_disks = conf->raid_disks = conf->previous_raid_disks;
+               mddev->new_chunk_sectors =
+                       conf->chunk_sectors = conf->prev_chunk_sectors;
+               mddev->new_layout = conf->algorithm = conf->prev_algo;
                rdev_for_each(rdev, mddev)
                        rdev->new_data_offset = rdev->data_offset;
                smp_wmb();
+               conf->generation --;
                conf->reshape_progress = MaxSector;
                mddev->reshape_position = MaxSector;
+               write_seqcount_end(&conf->gen_lock);
                spin_unlock_irq(&conf->device_lock);
                return -EAGAIN;
        }
@@ -6476,27 +6679,28 @@ static void raid5_quiesce(struct mddev *mddev, int state)
                break;
 
        case 1: /* stop all writes */
-               spin_lock_irq(&conf->device_lock);
+               lock_all_device_hash_locks_irq(conf);
                /* '2' tells resync/reshape to pause so that all
                 * active stripes can drain
                 */
                conf->quiesce = 2;
-               wait_event_lock_irq(conf->wait_for_stripe,
+               wait_event_cmd(conf->wait_for_stripe,
                                    atomic_read(&conf->active_stripes) == 0 &&
                                    atomic_read(&conf->active_aligned_reads) == 0,
-                                   conf->device_lock);
+                                   unlock_all_device_hash_locks_irq(conf),
+                                   lock_all_device_hash_locks_irq(conf));
                conf->quiesce = 1;
-               spin_unlock_irq(&conf->device_lock);
+               unlock_all_device_hash_locks_irq(conf);
                /* allow reshape to continue */
                wake_up(&conf->wait_for_overlap);
                break;
 
        case 0: /* re-enable writes */
-               spin_lock_irq(&conf->device_lock);
+               lock_all_device_hash_locks_irq(conf);
                conf->quiesce = 0;
                wake_up(&conf->wait_for_stripe);
                wake_up(&conf->wait_for_overlap);
-               spin_unlock_irq(&conf->device_lock);
+               unlock_all_device_hash_locks_irq(conf);
                break;
        }
 }
index 2113ffa..01ad8ae 100644 (file)
@@ -49,7 +49,7 @@
  * can't distinguish between a clean block that has been generated
  * from parity calculations, and a clean block that has been
  * successfully written to the spare ( or to parity when resyncing).
- * To distingush these states we have a stripe bit STRIPE_INSYNC that
+ * To distinguish these states we have a stripe bit STRIPE_INSYNC that
  * is set whenever a write is scheduled to the spare, or to the parity
  * disc if there is no spare.  A sync request clears this bit, and
  * when we find it set with no buffers locked, we know the sync is
@@ -205,6 +205,7 @@ struct stripe_head {
        short                   pd_idx;         /* parity disk index */
        short                   qd_idx;         /* 'Q' disk index for raid6 */
        short                   ddf_layout;/* use DDF ordering to calculate Q */
+       short                   hash_lock_index;
        unsigned long           state;          /* state flags */
        atomic_t                count;        /* nr of active thread/requests */
        int                     bm_seq; /* sequence number for bitmap flushes */
@@ -367,9 +368,18 @@ struct disk_info {
        struct md_rdev  *rdev, *replacement;
 };
 
+/* NOTE NR_STRIPE_HASH_LOCKS must remain below 64.
+ * This is because we sometimes take all the spinlocks
+ * and creating that much locking depth can cause
+ * problems.
+ */
+#define NR_STRIPE_HASH_LOCKS 8
+#define STRIPE_HASH_LOCKS_MASK (NR_STRIPE_HASH_LOCKS - 1)
+
 struct r5worker {
        struct work_struct work;
        struct r5worker_group *group;
+       struct list_head temp_inactive_list[NR_STRIPE_HASH_LOCKS];
        bool working;
 };
 
@@ -382,6 +392,8 @@ struct r5worker_group {
 
 struct r5conf {
        struct hlist_head       *stripe_hashtbl;
+       /* only protect corresponding hash list and inactive_list */
+       spinlock_t              hash_locks[NR_STRIPE_HASH_LOCKS];
        struct mddev            *mddev;
        int                     chunk_sectors;
        int                     level, algorithm;
@@ -462,7 +474,8 @@ struct r5conf {
         * Free stripes pool
         */
        atomic_t                active_stripes;
-       struct list_head        inactive_list;
+       struct list_head        inactive_list[NR_STRIPE_HASH_LOCKS];
+       atomic_t                empty_inactive_list_nr;
        struct llist_head       released_stripes;
        wait_queue_head_t       wait_for_stripe;
        wait_queue_head_t       wait_for_overlap;
@@ -477,6 +490,7 @@ struct r5conf {
         * the new thread here until we fully activate the array.
         */
        struct md_thread        *thread;
+       struct list_head        temp_inactive_list[NR_STRIPE_HASH_LOCKS];
        struct r5worker_group   *worker_groups;
        int                     group_cnt;
        int                     worker_cnt_per_group;
index f2199e4..185c285 100644 (file)
@@ -85,7 +85,7 @@ static void flexcop_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *
                while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
                        mdelay(1);
                        retries--;
-               };
+               }
 
                if (retries == 0)
                        printk("%s: SRAM timeout\n", __func__);
@@ -110,7 +110,7 @@ static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf,
                while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
                        mdelay(1);
                        retries--;
-               };
+               }
 
                if (retries == 0)
                        printk("%s: SRAM timeout\n", __func__);
@@ -122,7 +122,7 @@ static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf,
                while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
                        mdelay(1);
                        retries--;
-               };
+               }
 
                if (retries == 0)
                        printk("%s: SRAM timeout\n", __func__);
index bb6ee51..34b0d0d 100644 (file)
@@ -411,7 +411,7 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
        saa7146_write(dev, MC2, 0xf8000000);
 
        /* request an interrupt for the saa7146 */
-       err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED | IRQF_DISABLED,
+       err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED,
                          dev->name, dev);
        if (err < 0) {
                ERR("request_irq() failed\n");
@@ -524,8 +524,6 @@ static void saa7146_remove_one(struct pci_dev *pdev)
        DEB_EE("dev:%p\n", dev);
 
        dev->ext->detach(dev);
-       /* Zero the PCI drvdata after use. */
-       pci_set_drvdata(pdev, NULL);
 
        /* shut down all video dma transfers */
        saa7146_write(dev, MC1, 0x00ff0000);
index a142f79..050984c 100644 (file)
@@ -922,8 +922,8 @@ static int smscore_load_firmware_family2(struct smscore_device_t *coredev,
        u32 i, *ptr;
        u8 *payload = firmware->payload;
        int rc = 0;
-       firmware->start_address = le32_to_cpu(firmware->start_address);
-       firmware->length = le32_to_cpu(firmware->length);
+       firmware->start_address = le32_to_cpup((__le32 *)&firmware->start_address);
+       firmware->length = le32_to_cpup((__le32 *)&firmware->length);
 
        mem_address = firmware->start_address;
 
@@ -982,7 +982,7 @@ static int smscore_load_firmware_family2(struct smscore_device_t *coredev,
        if (rc < 0)
                goto exit_fw_download;
 
-       sms_err("sending MSG_SMS_DATA_VALIDITY_REQ expecting 0x%x",
+       sms_debug("sending MSG_SMS_DATA_VALIDITY_REQ expecting 0x%x",
                calc_checksum);
        SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_DATA_VALIDITY_REQ,
                        sizeof(msg->x_msg_header) +
@@ -1562,7 +1562,7 @@ void smscore_onresponse(struct smscore_device_t *coredev,
                {
                        struct sms_msg_data *validity = (struct sms_msg_data *) phdr;
 
-                       sms_err("MSG_SMS_DATA_VALIDITY_RES, checksum = 0x%x",
+                       sms_debug("MSG_SMS_DATA_VALIDITY_RES, checksum = 0x%x",
                                validity->msg_data[0]);
                        complete(&coredev->data_validity_done);
                        break;
index 63676a8..85151ef 100644 (file)
@@ -44,14 +44,14 @@ module_param_named(debug, sms_dbg, int, 0644);
 MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
 
 
-u32 sms_to_guard_interval_table[] = {
+static u32 sms_to_guard_interval_table[] = {
        [0] = GUARD_INTERVAL_1_32,
        [1] = GUARD_INTERVAL_1_16,
        [2] = GUARD_INTERVAL_1_8,
        [3] = GUARD_INTERVAL_1_4,
 };
 
-u32 sms_to_code_rate_table[] = {
+static u32 sms_to_code_rate_table[] = {
        [0] = FEC_1_2,
        [1] = FEC_2_3,
        [2] = FEC_3_4,
@@ -60,14 +60,14 @@ u32 sms_to_code_rate_table[] = {
 };
 
 
-u32 sms_to_hierarchy_table[] = {
+static u32 sms_to_hierarchy_table[] = {
        [0] = HIERARCHY_NONE,
        [1] = HIERARCHY_1,
        [2] = HIERARCHY_2,
        [3] = HIERARCHY_4,
 };
 
-u32 sms_to_modulation_table[] = {
+static u32 sms_to_modulation_table[] = {
        [0] = QPSK,
        [1] = QAM_16,
        [2] = QAM_64,
index 3485655..58de441 100644 (file)
@@ -476,7 +476,9 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
 void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
                              size_t count)
 {
-       spin_lock(&demux->lock);
+       unsigned long flags;
+
+       spin_lock_irqsave(&demux->lock, flags);
 
        while (count--) {
                if (buf[0] == 0x47)
@@ -484,7 +486,7 @@ void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
                buf += 188;
        }
 
-       spin_unlock(&demux->lock);
+       spin_unlock_irqrestore(&demux->lock, flags);
 }
 
 EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
@@ -519,8 +521,9 @@ static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
 {
        int p = 0, i, j;
        const u8 *q;
+       unsigned long flags;
 
-       spin_lock(&demux->lock);
+       spin_lock_irqsave(&demux->lock, flags);
 
        if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
                i = demux->tsbufp;
@@ -564,7 +567,7 @@ static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
        }
 
 bailout:
-       spin_unlock(&demux->lock);
+       spin_unlock_irqrestore(&demux->lock, flags);
 }
 
 void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
@@ -581,11 +584,13 @@ EXPORT_SYMBOL(dvb_dmx_swfilter_204);
 
 void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
 {
-       spin_lock(&demux->lock);
+       unsigned long flags;
+
+       spin_lock_irqsave(&demux->lock, flags);
 
        demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK);
 
-       spin_unlock(&demux->lock);
+       spin_unlock_irqrestore(&demux->lock, flags);
 }
 EXPORT_SYMBOL(dvb_dmx_swfilter_raw);
 
index 0e2ec6f..bddbab4 100644 (file)
@@ -200,6 +200,13 @@ config DVB_CX24116
        help
          A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
 
+config DVB_CX24117
+       tristate "Conexant CX24117 based"
+       depends on DVB_CORE && I2C
+       default m if !MEDIA_SUBDRV_AUTOSELECT
+       help
+         A Dual DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
 config DVB_SI21XX
        tristate "Silicon Labs SI21XX based"
        depends on DVB_CORE && I2C
index cebc0fa..f9cb43d 100644 (file)
@@ -76,6 +76,7 @@ obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o
 obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
 obj-$(CONFIG_DVB_AF9013) += af9013.o
 obj-$(CONFIG_DVB_CX24116) += cx24116.o
+obj-$(CONFIG_DVB_CX24117) += cx24117.o
 obj-$(CONFIG_DVB_SI21XX) += si21xx.o
 obj-$(CONFIG_DVB_STV0288) += stv0288.o
 obj-$(CONFIG_DVB_STB6000) += stb6000.o
index a204f28..fb504f1 100644 (file)
@@ -24,6 +24,9 @@
 
 #include "af9013_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 struct af9013_state {
        struct i2c_adapter *i2c;
        struct dvb_frontend fe;
@@ -50,16 +53,23 @@ static int af9013_wr_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
        const u8 *val, int len)
 {
        int ret;
-       u8 buf[3+len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
                        .addr = priv->config.i2c_addr,
                        .flags = 0,
-                       .len = sizeof(buf),
+                       .len = 3 + len,
                        .buf = buf,
                }
        };
 
+       if (3 + len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        buf[0] = (reg >> 8) & 0xff;
        buf[1] = (reg >> 0) & 0xff;
        buf[2] = mbox;
index a777b4b..30ee590 100644 (file)
@@ -21,6 +21,9 @@
 
 #include "af9033_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 struct af9033_state {
        struct i2c_adapter *i2c;
        struct dvb_frontend fe;
@@ -40,16 +43,23 @@ static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val,
                int len)
 {
        int ret;
-       u8 buf[3 + len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
                        .addr = state->cfg.i2c_addr,
                        .flags = 0,
-                       .len = sizeof(buf),
+                       .len = 3 + len,
                        .buf = buf,
                }
        };
 
+       if (3 + len > sizeof(buf)) {
+               dev_warn(&state->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        buf[0] = (reg >> 16) & 0xff;
        buf[1] = (reg >>  8) & 0xff;
        buf[2] = (reg >>  0) & 0xff;
@@ -161,7 +171,14 @@ static int af9033_wr_reg_val_tab(struct af9033_state *state,
                const struct reg_val *tab, int tab_len)
 {
        int ret, i, j;
-       u8 buf[tab_len];
+       u8 buf[MAX_XFER_SIZE];
+
+       if (tab_len > sizeof(buf)) {
+               dev_warn(&state->i2c->dev,
+                        "%s: i2c wr len=%d is too big!\n",
+                        KBUILD_MODNAME, tab_len);
+               return -EINVAL;
+       }
 
        dev_dbg(&state->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len);
 
index 1b77909..39a29dd 100644 (file)
@@ -44,6 +44,9 @@
 #include "bcm3510.h"
 #include "bcm3510_priv.h"
 
+/* Max transfer size done by bcm3510_do_hab_cmd() function */
+#define MAX_XFER_SIZE  128
+
 struct bcm3510_state {
 
        struct i2c_adapter* i2c;
@@ -201,9 +204,19 @@ static int bcm3510_hab_send_request(struct bcm3510_state *st, u8 *buf, int len)
 
 static int bcm3510_do_hab_cmd(struct bcm3510_state *st, u8 cmd, u8 msgid, u8 *obuf, u8 olen, u8 *ibuf, u8 ilen)
 {
-       u8 ob[olen+2],ib[ilen+2];
+       u8 ob[MAX_XFER_SIZE], ib[MAX_XFER_SIZE];
        int ret = 0;
 
+       if (ilen + 2 > sizeof(ib)) {
+               deb_hab("do_hab_cmd: ilen=%d is too big!\n", ilen);
+               return -EINVAL;
+       }
+
+       if (olen + 2 > sizeof(ob)) {
+               deb_hab("do_hab_cmd: olen=%d is too big!\n", olen);
+               return -EINVAL;
+       }
+
        ob[0] = cmd;
        ob[1] = msgid;
        memcpy(&ob[2],obuf,olen);
index 0cd6927..95b981c 100644 (file)
@@ -378,7 +378,7 @@ static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltag
                return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40);
        default:
                return -EINVAL;
-       };
+       }
 }
 
 static int cx24110_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c
new file mode 100644 (file)
index 0000000..476b422
--- /dev/null
@@ -0,0 +1,1650 @@
+/*
+    Conexant cx24117/cx24132 - Dual DVBS/S2 Satellite demod/tuner driver
+
+    Copyright (C) 2013 Luis Alves <ljalvs@gmail.com>
+       July, 6th 2013
+           First release based on cx24116 driver by:
+           Steven Toth and Georg Acher, Darron Broad, Igor Liplianin
+           Cards currently supported:
+               TBS6980 - Dual DVBS/S2 PCIe card
+               TBS6981 - Dual DVBS/S2 PCIe card
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "tuner-i2c.h"
+#include "dvb_frontend.h"
+#include "cx24117.h"
+
+
+#define CX24117_DEFAULT_FIRMWARE "dvb-fe-cx24117.fw"
+#define CX24117_SEARCH_RANGE_KHZ 5000
+
+/* known registers */
+#define CX24117_REG_COMMAND      (0x00)      /* command buffer */
+#define CX24117_REG_EXECUTE      (0x1f)      /* execute command */
+
+#define CX24117_REG_FREQ3_0      (0x34)      /* frequency */
+#define CX24117_REG_FREQ2_0      (0x35)
+#define CX24117_REG_FREQ1_0      (0x36)
+#define CX24117_REG_STATE0       (0x39)
+#define CX24117_REG_SSTATUS0     (0x3a)      /* demod0 signal high / status */
+#define CX24117_REG_SIGNAL0      (0x3b)
+#define CX24117_REG_FREQ5_0      (0x3c)      /* +-freq */
+#define CX24117_REG_FREQ6_0      (0x3d)
+#define CX24117_REG_SRATE2_0     (0x3e)      /* +- 1000 * srate */
+#define CX24117_REG_SRATE1_0     (0x3f)
+#define CX24117_REG_QUALITY2_0   (0x40)
+#define CX24117_REG_QUALITY1_0   (0x41)
+
+#define CX24117_REG_BER4_0       (0x47)
+#define CX24117_REG_BER3_0       (0x48)
+#define CX24117_REG_BER2_0       (0x49)
+#define CX24117_REG_BER1_0       (0x4a)
+#define CX24117_REG_DVBS_UCB2_0  (0x4b)
+#define CX24117_REG_DVBS_UCB1_0  (0x4c)
+#define CX24117_REG_DVBS2_UCB2_0 (0x50)
+#define CX24117_REG_DVBS2_UCB1_0 (0x51)
+#define CX24117_REG_QSTATUS0     (0x93)
+#define CX24117_REG_CLKDIV0      (0xe6)
+#define CX24117_REG_RATEDIV0     (0xf0)
+
+
+#define CX24117_REG_FREQ3_1      (0x55)      /* frequency */
+#define CX24117_REG_FREQ2_1      (0x56)
+#define CX24117_REG_FREQ1_1      (0x57)
+#define CX24117_REG_STATE1       (0x5a)
+#define CX24117_REG_SSTATUS1     (0x5b)      /* demod1 signal high / status */
+#define CX24117_REG_SIGNAL1      (0x5c)
+#define CX24117_REG_FREQ5_1      (0x5d)      /* +- freq */
+#define CX24117_REG_FREQ4_1      (0x5e)
+#define CX24117_REG_SRATE2_1     (0x5f)
+#define CX24117_REG_SRATE1_1     (0x60)
+#define CX24117_REG_QUALITY2_1   (0x61)
+#define CX24117_REG_QUALITY1_1   (0x62)
+#define CX24117_REG_BER4_1       (0x68)
+#define CX24117_REG_BER3_1       (0x69)
+#define CX24117_REG_BER2_1       (0x6a)
+#define CX24117_REG_BER1_1       (0x6b)
+#define CX24117_REG_DVBS_UCB2_1  (0x6c)
+#define CX24117_REG_DVBS_UCB1_1  (0x6d)
+#define CX24117_REG_DVBS2_UCB2_1 (0x71)
+#define CX24117_REG_DVBS2_UCB1_1 (0x72)
+#define CX24117_REG_QSTATUS1     (0x9f)
+#define CX24117_REG_CLKDIV1      (0xe7)
+#define CX24117_REG_RATEDIV1     (0xf1)
+
+
+/* arg buffer size */
+#define CX24117_ARGLEN       (0x1e)
+
+/* rolloff */
+#define CX24117_ROLLOFF_020  (0x00)
+#define CX24117_ROLLOFF_025  (0x01)
+#define CX24117_ROLLOFF_035  (0x02)
+
+/* pilot bit */
+#define CX24117_PILOT_OFF    (0x00)
+#define CX24117_PILOT_ON     (0x40)
+#define CX24117_PILOT_AUTO   (0x80)
+
+/* signal status */
+#define CX24117_HAS_SIGNAL   (0x01)
+#define CX24117_HAS_CARRIER  (0x02)
+#define CX24117_HAS_VITERBI  (0x04)
+#define CX24117_HAS_SYNCLOCK (0x08)
+#define CX24117_STATUS_MASK  (0x0f)
+#define CX24117_SIGNAL_MASK  (0xc0)
+
+
+/* arg offset for DiSEqC */
+#define CX24117_DISEQC_DEMOD  (1)
+#define CX24117_DISEQC_BURST  (2)
+#define CX24117_DISEQC_ARG3_2 (3)   /* unknown value=2 */
+#define CX24117_DISEQC_ARG4_0 (4)   /* unknown value=0 */
+#define CX24117_DISEQC_ARG5_0 (5)   /* unknown value=0 */
+#define CX24117_DISEQC_MSGLEN (6)
+#define CX24117_DISEQC_MSGOFS (7)
+
+/* DiSEqC burst */
+#define CX24117_DISEQC_MINI_A (0)
+#define CX24117_DISEQC_MINI_B (1)
+
+
+#define CX24117_PNE    (0) /* 0 disabled / 2 enabled */
+#define CX24117_OCC    (1) /* 0 disabled / 1 enabled */
+
+
+enum cmds {
+       CMD_SET_VCO     = 0x10,
+       CMD_TUNEREQUEST = 0x11,
+       CMD_MPEGCONFIG  = 0x13,
+       CMD_TUNERINIT   = 0x14,
+       CMD_LNBSEND     = 0x21, /* Formerly CMD_SEND_DISEQC */
+       CMD_LNBDCLEVEL  = 0x22,
+       CMD_SET_TONE    = 0x23,
+       CMD_UPDFWVERS   = 0x35,
+       CMD_TUNERSLEEP  = 0x36,
+};
+
+static LIST_HEAD(hybrid_tuner_instance_list);
+static DEFINE_MUTEX(cx24117_list_mutex);
+
+/* The Demod/Tuner can't easily provide these, we cache them */
+struct cx24117_tuning {
+       u32 frequency;
+       u32 symbol_rate;
+       fe_spectral_inversion_t inversion;
+       fe_code_rate_t fec;
+
+       fe_delivery_system_t delsys;
+       fe_modulation_t modulation;
+       fe_pilot_t pilot;
+       fe_rolloff_t rolloff;
+
+       /* Demod values */
+       u8 fec_val;
+       u8 fec_mask;
+       u8 inversion_val;
+       u8 pilot_val;
+       u8 rolloff_val;
+};
+
+/* Basic commands that are sent to the firmware */
+struct cx24117_cmd {
+       u8 len;
+       u8 args[CX24117_ARGLEN];
+};
+
+/* common to both fe's */
+struct cx24117_priv {
+       u8 demod_address;
+       struct i2c_adapter *i2c;
+       u8 skip_fw_load;
+       struct mutex fe_lock;
+
+       /* Used for sharing this struct between demods */
+       struct tuner_i2c_props i2c_props;
+       struct list_head hybrid_tuner_instance_list;
+};
+
+/* one per each fe */
+struct cx24117_state {
+       struct cx24117_priv *priv;
+       struct dvb_frontend frontend;
+
+       struct cx24117_tuning dcur;
+       struct cx24117_tuning dnxt;
+       struct cx24117_cmd dsec_cmd;
+
+       int demod;
+};
+
+/* modfec (modulation and FEC) lookup table */
+/* Check cx24116.c for a detailed description of each field */
+static struct cx24117_modfec {
+       fe_delivery_system_t delivery_system;
+       fe_modulation_t modulation;
+       fe_code_rate_t fec;
+       u8 mask;        /* In DVBS mode this is used to autodetect */
+       u8 val;         /* Passed to the firmware to indicate mode selection */
+} cx24117_modfec_modes[] = {
+       /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
+
+       /*mod   fec       mask  val */
+       { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 },
+       { SYS_DVBS, QPSK, FEC_1_2,  0x02, 0x2e }, /* 00000010 00101110 */
+       { SYS_DVBS, QPSK, FEC_2_3,  0x04, 0x2f }, /* 00000100 00101111 */
+       { SYS_DVBS, QPSK, FEC_3_4,  0x08, 0x30 }, /* 00001000 00110000 */
+       { SYS_DVBS, QPSK, FEC_4_5,  0xfe, 0x30 }, /* 000?0000 ?        */
+       { SYS_DVBS, QPSK, FEC_5_6,  0x20, 0x31 }, /* 00100000 00110001 */
+       { SYS_DVBS, QPSK, FEC_6_7,  0xfe, 0x30 }, /* 0?000000 ?        */
+       { SYS_DVBS, QPSK, FEC_7_8,  0x80, 0x32 }, /* 10000000 00110010 */
+       { SYS_DVBS, QPSK, FEC_8_9,  0xfe, 0x30 }, /* 0000000? ?        */
+       { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 },
+       /* NBC-QPSK */
+       { SYS_DVBS2, QPSK, FEC_NONE, 0x00, 0x00 },
+       { SYS_DVBS2, QPSK, FEC_1_2,  0x00, 0x04 },
+       { SYS_DVBS2, QPSK, FEC_3_5,  0x00, 0x05 },
+       { SYS_DVBS2, QPSK, FEC_2_3,  0x00, 0x06 },
+       { SYS_DVBS2, QPSK, FEC_3_4,  0x00, 0x07 },
+       { SYS_DVBS2, QPSK, FEC_4_5,  0x00, 0x08 },
+       { SYS_DVBS2, QPSK, FEC_5_6,  0x00, 0x09 },
+       { SYS_DVBS2, QPSK, FEC_8_9,  0x00, 0x0a },
+       { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b },
+       { SYS_DVBS2, QPSK, FEC_AUTO, 0x00, 0x00 },
+       /* 8PSK */
+       { SYS_DVBS2, PSK_8, FEC_NONE, 0x00, 0x00 },
+       { SYS_DVBS2, PSK_8, FEC_3_5,  0x00, 0x0c },
+       { SYS_DVBS2, PSK_8, FEC_2_3,  0x00, 0x0d },
+       { SYS_DVBS2, PSK_8, FEC_3_4,  0x00, 0x0e },
+       { SYS_DVBS2, PSK_8, FEC_5_6,  0x00, 0x0f },
+       { SYS_DVBS2, PSK_8, FEC_8_9,  0x00, 0x10 },
+       { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 },
+       { SYS_DVBS2, PSK_8, FEC_AUTO, 0x00, 0x00 },
+       /*
+        * 'val' can be found in the FECSTATUS register when tuning.
+        * FECSTATUS will give the actual FEC in use if tuning was successful.
+        */
+};
+
+
+static int cx24117_writereg(struct cx24117_state *state, u8 reg, u8 data)
+{
+       u8 buf[] = { reg, data };
+       struct i2c_msg msg = { .addr = state->priv->demod_address,
+               .flags = 0, .buf = buf, .len = 2 };
+       int ret;
+
+       dev_dbg(&state->priv->i2c->dev,
+                       "%s() demod%d i2c wr @0x%02x=0x%02x\n",
+                       __func__, state->demod, reg, data);
+
+       ret = i2c_transfer(state->priv->i2c, &msg, 1);
+       if (ret < 0) {
+               dev_warn(&state->priv->i2c->dev,
+                       "%s: demod%d i2c wr err(%i) @0x%02x=0x%02x\n",
+                       KBUILD_MODNAME, state->demod, ret, reg, data);
+               return ret;
+       }
+       return 0;
+}
+
+static int cx24117_writecmd(struct cx24117_state *state,
+       struct cx24117_cmd *cmd)
+{
+       struct i2c_msg msg;
+       u8 buf[CX24117_ARGLEN+1];
+       int ret;
+
+       dev_dbg(&state->priv->i2c->dev,
+                       "%s() demod%d i2c wr cmd len=%d\n",
+                       __func__, state->demod, cmd->len);
+
+       buf[0] = CX24117_REG_COMMAND;
+       memcpy(&buf[1], cmd->args, cmd->len);
+
+       msg.addr = state->priv->demod_address;
+       msg.flags = 0;
+       msg.len = cmd->len+1;
+       msg.buf = buf;
+       ret = i2c_transfer(state->priv->i2c, &msg, 1);
+       if (ret < 0) {
+               dev_warn(&state->priv->i2c->dev,
+                       "%s: demod%d i2c wr cmd err(%i) len=%d\n",
+                       KBUILD_MODNAME, state->demod, ret, cmd->len);
+               return ret;
+       }
+       return 0;
+}
+
+static int cx24117_readreg(struct cx24117_state *state, u8 reg)
+{
+       int ret;
+       u8 recv = 0;
+       struct i2c_msg msg[] = {
+               { .addr = state->priv->demod_address, .flags = 0,
+                       .buf = &reg, .len = 1 },
+               { .addr = state->priv->demod_address, .flags = I2C_M_RD,
+                       .buf = &recv, .len = 1 }
+       };
+
+       ret = i2c_transfer(state->priv->i2c, msg, 2);
+       if (ret < 0) {
+               dev_warn(&state->priv->i2c->dev,
+                       "%s: demod%d i2c rd err(%d) @0x%x\n",
+                       KBUILD_MODNAME, state->demod, ret, reg);
+               return ret;
+       }
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d i2c rd @0x%02x=0x%02x\n",
+               __func__, state->demod, reg, recv);
+
+       return recv;
+}
+
+static int cx24117_readregN(struct cx24117_state *state,
+       u8 reg, u8 *buf, int len)
+{
+       int ret;
+       struct i2c_msg msg[] = {
+               { .addr = state->priv->demod_address, .flags = 0,
+                       .buf = &reg, .len = 1 },
+               { .addr = state->priv->demod_address, .flags = I2C_M_RD,
+                       .buf = buf, .len = len }
+       };
+
+       ret = i2c_transfer(state->priv->i2c, msg, 2);
+       if (ret < 0) {
+               dev_warn(&state->priv->i2c->dev,
+                       "%s: demod%d i2c rd err(%d) @0x%x\n",
+                       KBUILD_MODNAME, state->demod, ret, reg);
+               return ret;
+       }
+       return 0;
+}
+
+static int cx24117_set_inversion(struct cx24117_state *state,
+       fe_spectral_inversion_t inversion)
+{
+       dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n",
+               __func__, inversion, state->demod);
+
+       switch (inversion) {
+       case INVERSION_OFF:
+               state->dnxt.inversion_val = 0x00;
+               break;
+       case INVERSION_ON:
+               state->dnxt.inversion_val = 0x04;
+               break;
+       case INVERSION_AUTO:
+               state->dnxt.inversion_val = 0x0C;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       state->dnxt.inversion = inversion;
+
+       return 0;
+}
+
+static int cx24117_lookup_fecmod(struct cx24117_state *state,
+       fe_delivery_system_t d, fe_modulation_t m, fe_code_rate_t f)
+{
+       int i, ret = -EINVAL;
+
+       dev_dbg(&state->priv->i2c->dev,
+               "%s(demod(0x%02x,0x%02x) demod%d\n",
+               __func__, m, f, state->demod);
+
+       for (i = 0; i < ARRAY_SIZE(cx24117_modfec_modes); i++) {
+               if ((d == cx24117_modfec_modes[i].delivery_system) &&
+                       (m == cx24117_modfec_modes[i].modulation) &&
+                       (f == cx24117_modfec_modes[i].fec)) {
+                               ret = i;
+                               break;
+                       }
+       }
+
+       return ret;
+}
+
+static int cx24117_set_fec(struct cx24117_state *state,
+       fe_delivery_system_t delsys, fe_modulation_t mod, fe_code_rate_t fec)
+{
+       int ret;
+
+       dev_dbg(&state->priv->i2c->dev,
+               "%s(0x%02x,0x%02x) demod%d\n",
+               __func__, mod, fec, state->demod);
+
+       ret = cx24117_lookup_fecmod(state, delsys, mod, fec);
+       if (ret < 0)
+               return ret;
+
+       state->dnxt.fec = fec;
+       state->dnxt.fec_val = cx24117_modfec_modes[ret].val;
+       state->dnxt.fec_mask = cx24117_modfec_modes[ret].mask;
+       dev_dbg(&state->priv->i2c->dev,
+               "%s() demod%d mask/val = 0x%02x/0x%02x\n", __func__,
+               state->demod, state->dnxt.fec_mask, state->dnxt.fec_val);
+
+       return 0;
+}
+
+static int cx24117_set_symbolrate(struct cx24117_state *state, u32 rate)
+{
+       dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n",
+               __func__, rate, state->demod);
+
+       state->dnxt.symbol_rate = rate;
+
+       dev_dbg(&state->priv->i2c->dev,
+               "%s() demod%d symbol_rate = %d\n",
+               __func__, state->demod, rate);
+
+       return 0;
+}
+
+static int cx24117_load_firmware(struct dvb_frontend *fe,
+       const struct firmware *fw);
+
+static int cx24117_firmware_ondemand(struct dvb_frontend *fe)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       const struct firmware *fw;
+       int ret = 0;
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d skip_fw_load=%d\n",
+               __func__, state->demod, state->priv->skip_fw_load);
+
+       if (state->priv->skip_fw_load)
+               return 0;
+
+       /* check if firmware if already running */
+       if (cx24117_readreg(state, 0xeb) != 0xa) {
+               /* Load firmware */
+               /* request the firmware, this will block until loaded */
+               dev_dbg(&state->priv->i2c->dev,
+                       "%s: Waiting for firmware upload (%s)...\n",
+                       __func__, CX24117_DEFAULT_FIRMWARE);
+               ret = request_firmware(&fw, CX24117_DEFAULT_FIRMWARE,
+                       state->priv->i2c->dev.parent);
+               dev_dbg(&state->priv->i2c->dev,
+                       "%s: Waiting for firmware upload(2)...\n", __func__);
+               if (ret) {
+                       dev_err(&state->priv->i2c->dev,
+                               "%s: No firmware uploaded "
+                               "(timeout or file not found?)\n", __func__);
+                       return ret;
+               }
+
+               /* Make sure we don't recurse back through here
+                * during loading */
+               state->priv->skip_fw_load = 1;
+
+               ret = cx24117_load_firmware(fe, fw);
+               if (ret)
+                       dev_err(&state->priv->i2c->dev,
+                               "%s: Writing firmware failed\n", __func__);
+               release_firmware(fw);
+
+               dev_info(&state->priv->i2c->dev,
+                       "%s: Firmware upload %s\n", __func__,
+                       ret == 0 ? "complete" : "failed");
+
+               /* Ensure firmware is always loaded if required */
+               state->priv->skip_fw_load = 0;
+       }
+
+       return ret;
+}
+
+/* Take a basic firmware command structure, format it
+ * and forward it for processing
+ */
+static int cx24117_cmd_execute_nolock(struct dvb_frontend *fe,
+       struct cx24117_cmd *cmd)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       int i, ret;
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
+               __func__, state->demod);
+
+       /* Load the firmware if required */
+       ret = cx24117_firmware_ondemand(fe);
+       if (ret != 0)
+               return ret;
+
+       /* Write the command */
+       cx24117_writecmd(state, cmd);
+
+       /* Start execution and wait for cmd to terminate */
+       cx24117_writereg(state, CX24117_REG_EXECUTE, 0x01);
+       i = 0;
+       while (cx24117_readreg(state, CX24117_REG_EXECUTE)) {
+               msleep(20);
+               if (i++ > 40) {
+                       /* Avoid looping forever if the firmware does
+                               not respond */
+                       dev_warn(&state->priv->i2c->dev,
+                               "%s() Firmware not responding\n", __func__);
+                       return -EIO;
+               }
+       }
+       return 0;
+}
+
+static int cx24117_cmd_execute(struct dvb_frontend *fe, struct cx24117_cmd *cmd)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       int ret;
+
+       mutex_lock(&state->priv->fe_lock);
+       ret = cx24117_cmd_execute_nolock(fe, cmd);
+       mutex_unlock(&state->priv->fe_lock);
+
+       return ret;
+}
+
+static int cx24117_load_firmware(struct dvb_frontend *fe,
+       const struct firmware *fw)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       struct cx24117_cmd cmd;
+       int i, ret;
+       unsigned char vers[4];
+
+       struct i2c_msg msg;
+       u8 *buf;
+
+       dev_dbg(&state->priv->i2c->dev,
+               "%s() demod%d FW is %zu bytes (%02x %02x .. %02x %02x)\n",
+               __func__, state->demod, fw->size, fw->data[0], fw->data[1],
+               fw->data[fw->size - 2], fw->data[fw->size - 1]);
+
+       cx24117_writereg(state, 0xea, 0x00);
+       cx24117_writereg(state, 0xea, 0x01);
+       cx24117_writereg(state, 0xea, 0x00);
+
+       cx24117_writereg(state, 0xce, 0x92);
+
+       cx24117_writereg(state, 0xfb, 0x00);
+       cx24117_writereg(state, 0xfc, 0x00);
+
+       cx24117_writereg(state, 0xc3, 0x04);
+       cx24117_writereg(state, 0xc4, 0x04);
+
+       cx24117_writereg(state, 0xce, 0x00);
+       cx24117_writereg(state, 0xcf, 0x00);
+
+       cx24117_writereg(state, 0xea, 0x00);
+       cx24117_writereg(state, 0xeb, 0x0c);
+       cx24117_writereg(state, 0xec, 0x06);
+       cx24117_writereg(state, 0xed, 0x05);
+       cx24117_writereg(state, 0xee, 0x03);
+       cx24117_writereg(state, 0xef, 0x05);
+
+       cx24117_writereg(state, 0xf3, 0x03);
+       cx24117_writereg(state, 0xf4, 0x44);
+
+       cx24117_writereg(state, CX24117_REG_RATEDIV0, 0x04);
+       cx24117_writereg(state, CX24117_REG_CLKDIV0, 0x02);
+
+       cx24117_writereg(state, CX24117_REG_RATEDIV1, 0x04);
+       cx24117_writereg(state, CX24117_REG_CLKDIV1, 0x02);
+
+       cx24117_writereg(state, 0xf2, 0x04);
+       cx24117_writereg(state, 0xe8, 0x02);
+       cx24117_writereg(state, 0xea, 0x01);
+       cx24117_writereg(state, 0xc8, 0x00);
+       cx24117_writereg(state, 0xc9, 0x00);
+       cx24117_writereg(state, 0xca, 0x00);
+       cx24117_writereg(state, 0xcb, 0x00);
+       cx24117_writereg(state, 0xcc, 0x00);
+       cx24117_writereg(state, 0xcd, 0x00);
+       cx24117_writereg(state, 0xe4, 0x03);
+       cx24117_writereg(state, 0xeb, 0x0a);
+
+       cx24117_writereg(state, 0xfb, 0x00);
+       cx24117_writereg(state, 0xe0, 0x76);
+       cx24117_writereg(state, 0xf7, 0x81);
+       cx24117_writereg(state, 0xf8, 0x00);
+       cx24117_writereg(state, 0xf9, 0x00);
+
+       buf = kmalloc(fw->size + 1, GFP_KERNEL);
+       if (buf == NULL) {
+               state->priv->skip_fw_load = 0;
+               return -ENOMEM;
+       }
+
+       /* fw upload reg */
+       buf[0] = 0xfa;
+       memcpy(&buf[1], fw->data, fw->size);
+
+       /* prepare i2c message to send */
+       msg.addr = state->priv->demod_address;
+       msg.flags = 0;
+       msg.len = fw->size + 1;
+       msg.buf = buf;
+
+       /* send fw */
+       ret = i2c_transfer(state->priv->i2c, &msg, 1);
+       if (ret < 0)
+               return ret;
+
+       kfree(buf);
+
+       cx24117_writereg(state, 0xf7, 0x0c);
+       cx24117_writereg(state, 0xe0, 0x00);
+
+       /* CMD 1B */
+       cmd.args[0] = 0x1b;
+       cmd.args[1] = 0x00;
+       cmd.args[2] = 0x01;
+       cmd.args[3] = 0x00;
+       cmd.len = 4;
+       ret = cx24117_cmd_execute_nolock(fe, &cmd);
+       if (ret != 0)
+               goto error;
+
+       /* CMD 10 */
+       cmd.args[0] = CMD_SET_VCO;
+       cmd.args[1] = 0x06;
+       cmd.args[2] = 0x2b;
+       cmd.args[3] = 0xd8;
+       cmd.args[4] = 0xa5;
+       cmd.args[5] = 0xee;
+       cmd.args[6] = 0x03;
+       cmd.args[7] = 0x9d;
+       cmd.args[8] = 0xfc;
+       cmd.args[9] = 0x06;
+       cmd.args[10] = 0x02;
+       cmd.args[11] = 0x9d;
+       cmd.args[12] = 0xfc;
+       cmd.len = 13;
+       ret = cx24117_cmd_execute_nolock(fe, &cmd);
+       if (ret != 0)
+               goto error;
+
+       /* CMD 15 */
+       cmd.args[0] = 0x15;
+       cmd.args[1] = 0x00;
+       cmd.args[2] = 0x01;
+       cmd.args[3] = 0x00;
+       cmd.args[4] = 0x00;
+       cmd.args[5] = 0x01;
+       cmd.args[6] = 0x01;
+       cmd.args[7] = 0x01;
+       cmd.args[8] = 0x00;
+       cmd.args[9] = 0x05;
+       cmd.args[10] = 0x02;
+       cmd.args[11] = 0x02;
+       cmd.args[12] = 0x00;
+       cmd.len = 13;
+       ret = cx24117_cmd_execute_nolock(fe, &cmd);
+       if (ret != 0)
+               goto error;
+
+       /* CMD 13 */
+       cmd.args[0] = CMD_MPEGCONFIG;
+       cmd.args[1] = 0x00;
+       cmd.args[2] = 0x00;
+       cmd.args[3] = 0x00;
+       cmd.args[4] = 0x01;
+       cmd.args[5] = 0x00;
+       cmd.len = 6;
+       ret = cx24117_cmd_execute_nolock(fe, &cmd);
+       if (ret != 0)
+               goto error;
+
+       /* CMD 14 */
+       for (i = 0; i < 2; i++) {
+               cmd.args[0] = CMD_TUNERINIT;
+               cmd.args[1] = (u8) i;
+               cmd.args[2] = 0x00;
+               cmd.args[3] = 0x05;
+               cmd.args[4] = 0x00;
+               cmd.args[5] = 0x00;
+               cmd.args[6] = 0x55;
+               cmd.args[7] = 0x00;
+               cmd.len = 8;
+               ret = cx24117_cmd_execute_nolock(fe, &cmd);
+               if (ret != 0)
+                       goto error;
+       }
+
+       cx24117_writereg(state, 0xce, 0xc0);
+       cx24117_writereg(state, 0xcf, 0x00);
+       cx24117_writereg(state, 0xe5, 0x04);
+
+       /* Firmware CMD 35: Get firmware version */
+       cmd.args[0] = CMD_UPDFWVERS;
+       cmd.len = 2;
+       for (i = 0; i < 4; i++) {
+               cmd.args[1] = i;
+               ret = cx24117_cmd_execute_nolock(fe, &cmd);
+               if (ret != 0)
+                       goto error;
+               vers[i] = cx24117_readreg(state, 0x33);
+       }
+       dev_info(&state->priv->i2c->dev,
+               "%s: FW version %i.%i.%i.%i\n", __func__,
+               vers[0], vers[1], vers[2], vers[3]);
+       return 0;
+error:
+       state->priv->skip_fw_load = 0;
+       dev_err(&state->priv->i2c->dev, "%s() Error running FW.\n", __func__);
+       return ret;
+}
+
+static int cx24117_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       int lock;
+
+       lock = cx24117_readreg(state,
+               (state->demod == 0) ? CX24117_REG_SSTATUS0 :
+                                     CX24117_REG_SSTATUS1) &
+               CX24117_STATUS_MASK;
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d status = 0x%02x\n",
+               __func__, state->demod, lock);
+
+       *status = 0;
+
+       if (lock & CX24117_HAS_SIGNAL)
+               *status |= FE_HAS_SIGNAL;
+       if (lock & CX24117_HAS_CARRIER)
+               *status |= FE_HAS_CARRIER;
+       if (lock & CX24117_HAS_VITERBI)
+               *status |= FE_HAS_VITERBI;
+       if (lock & CX24117_HAS_SYNCLOCK)
+               *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+       return 0;
+}
+
+static int cx24117_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       int ret;
+       u8 buf[4];
+       u8 base_reg = (state->demod == 0) ?
+                       CX24117_REG_BER4_0 :
+                       CX24117_REG_BER4_1;
+
+       ret = cx24117_readregN(state, base_reg, buf, 4);
+       if (ret != 0)
+               return ret;
+
+       *ber = (buf[0] << 24) | (buf[1] << 16) |
+               (buf[1] << 8) | buf[0];
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d ber=0x%04x\n",
+               __func__, state->demod, *ber);
+
+       return 0;
+}
+
+static int cx24117_read_signal_strength(struct dvb_frontend *fe,
+       u16 *signal_strength)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       struct cx24117_cmd cmd;
+       int ret;
+       u16 sig_reading;
+       u8 buf[2];
+       u8 reg = (state->demod == 0) ?
+               CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1;
+
+       /* Firmware CMD 1A */
+       cmd.args[0] = 0x1a;
+       cmd.args[1] = (u8) state->demod;
+       cmd.len = 2;
+       ret = cx24117_cmd_execute(fe, &cmd);
+       if (ret != 0)
+               return ret;
+
+       ret = cx24117_readregN(state, reg, buf, 2);
+       if (ret != 0)
+               return ret;
+       sig_reading = ((buf[0] & CX24117_SIGNAL_MASK) << 2) | buf[1];
+
+       *signal_strength = -100 * sig_reading + 94324;
+
+       dev_dbg(&state->priv->i2c->dev,
+               "%s() demod%d raw / cooked = 0x%04x / 0x%04x\n",
+               __func__, state->demod, sig_reading, *signal_strength);
+
+       return 0;
+}
+
+static int cx24117_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       int ret;
+       u8 buf[2];
+       u8 reg = (state->demod == 0) ?
+               CX24117_REG_QUALITY2_0 : CX24117_REG_QUALITY2_1;
+
+       ret = cx24117_readregN(state, reg, buf, 2);
+       if (ret != 0)
+               return ret;
+
+       *snr = (buf[0] << 8) | buf[1];
+
+       dev_dbg(&state->priv->i2c->dev,
+               "%s() demod%d snr = 0x%04x\n",
+               __func__, state->demod, *snr);
+
+       return ret;
+}
+
+static int cx24117_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       fe_delivery_system_t delsys = fe->dtv_property_cache.delivery_system;
+       int ret;
+       u8 buf[2];
+       u8 reg = (state->demod == 0) ?
+               CX24117_REG_DVBS_UCB2_0 :
+               CX24117_REG_DVBS_UCB2_1;
+
+       switch (delsys) {
+       case SYS_DVBS:
+               break;
+       case SYS_DVBS2:
+               reg += (CX24117_REG_DVBS2_UCB2_0 - CX24117_REG_DVBS_UCB2_0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = cx24117_readregN(state, reg, buf, 2);
+       if (ret != 0)
+               return ret;
+       *ucblocks = (buf[0] << 8) | buf[1];
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d ucb=0x%04x\n",
+               __func__, state->demod, *ucblocks);
+
+       return 0;
+}
+
+/* Overwrite the current tuning params, we are about to tune */
+static void cx24117_clone_params(struct dvb_frontend *fe)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       state->dcur = state->dnxt;
+}
+
+/* Wait for LNB */
+static int cx24117_wait_for_lnb(struct dvb_frontend *fe)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       int i;
+       u8 val, reg = (state->demod == 0) ? CX24117_REG_QSTATUS0 :
+                                           CX24117_REG_QSTATUS1;
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d qstatus = 0x%02x\n",
+               __func__, state->demod, cx24117_readreg(state, reg));
+
+       /* Wait for up to 300 ms */
+       for (i = 0; i < 10; i++) {
+               val = cx24117_readreg(state, reg) & 0x01;
+               if (val != 0)
+                       return 0;
+               msleep(30);
+       }
+
+       dev_warn(&state->priv->i2c->dev, "%s: demod%d LNB not ready\n",
+               KBUILD_MODNAME, state->demod);
+
+       return -ETIMEDOUT; /* -EBUSY ? */
+}
+
+static int cx24117_set_voltage(struct dvb_frontend *fe,
+       fe_sec_voltage_t voltage)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       struct cx24117_cmd cmd;
+       int ret;
+       u8 reg = (state->demod == 0) ? 0x10 : 0x20;
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d %s\n",
+               __func__, state->demod,
+               voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+               voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" :
+               "SEC_VOLTAGE_OFF");
+
+       /* CMD 32 */
+       cmd.args[0] = 0x32;
+       cmd.args[1] = reg;
+       cmd.args[2] = reg;
+       cmd.len = 3;
+       ret = cx24117_cmd_execute(fe, &cmd);
+       if (ret)
+               return ret;
+
+       if ((voltage == SEC_VOLTAGE_13) ||
+           (voltage == SEC_VOLTAGE_18)) {
+               /* CMD 33 */
+               cmd.args[0] = 0x33;
+               cmd.args[1] = reg;
+               cmd.args[2] = reg;
+               cmd.len = 3;
+               ret = cx24117_cmd_execute(fe, &cmd);
+               if (ret != 0)
+                       return ret;
+
+               ret = cx24117_wait_for_lnb(fe);
+               if (ret != 0)
+                       return ret;
+
+               /* Wait for voltage/min repeat delay */
+               msleep(100);
+
+               /* CMD 22 - CMD_LNBDCLEVEL */
+               cmd.args[0] = CMD_LNBDCLEVEL;
+               cmd.args[1] = state->demod ? 0 : 1;
+               cmd.args[2] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00);
+               cmd.len = 3;
+
+               /* Min delay time before DiSEqC send */
+               msleep(20);
+       } else {
+               cmd.args[0] = 0x33;
+               cmd.args[1] = 0x00;
+               cmd.args[2] = reg;
+               cmd.len = 3;
+       }
+
+       return cx24117_cmd_execute(fe, &cmd);
+}
+
+static int cx24117_set_tone(struct dvb_frontend *fe,
+       fe_sec_tone_mode_t tone)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       struct cx24117_cmd cmd;
+       int ret;
+
+       dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n",
+               __func__, state->demod, tone);
+       if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
+               dev_warn(&state->priv->i2c->dev, "%s: demod%d invalid tone=%d\n",
+                       KBUILD_MODNAME, state->demod, tone);
+               return -EINVAL;
+       }
+
+       /* Wait for LNB ready */
+       ret = cx24117_wait_for_lnb(fe);
+       if (ret != 0)
+               return ret;
+
+       /* Min delay time after DiSEqC send */
+       msleep(20);
+
+       /* Set the tone */
+       /* CMD 23 - CMD_SET_TONE */
+       cmd.args[0] = CMD_SET_TONE;
+       cmd.args[1] = (state->demod ? 0 : 1);
+       cmd.args[2] = 0x00;
+       cmd.args[3] = 0x00;
+       cmd.len = 5;
+       switch (tone) {
+       case SEC_TONE_ON:
+               cmd.args[4] = 0x01;
+               break;
+       case SEC_TONE_OFF:
+               cmd.args[4] = 0x00;
+               break;
+       }
+
+       msleep(20);
+
+       return cx24117_cmd_execute(fe, &cmd);
+}
+
+/* Initialise DiSEqC */
+static int cx24117_diseqc_init(struct dvb_frontend *fe)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+
+       /* Prepare a DiSEqC command */
+       state->dsec_cmd.args[0] = CMD_LNBSEND;
+
+       /* demod */
+       state->dsec_cmd.args[CX24117_DISEQC_DEMOD] = state->demod ? 0 : 1;
+
+       /* DiSEqC burst */
+       state->dsec_cmd.args[CX24117_DISEQC_BURST] = CX24117_DISEQC_MINI_A;
+
+       /* Unknown */
+       state->dsec_cmd.args[CX24117_DISEQC_ARG3_2] = 0x02;
+       state->dsec_cmd.args[CX24117_DISEQC_ARG4_0] = 0x00;
+
+       /* Continuation flag? */
+       state->dsec_cmd.args[CX24117_DISEQC_ARG5_0] = 0x00;
+
+       /* DiSEqC message length */
+       state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] = 0x00;
+
+       /* Command length */
+       state->dsec_cmd.len = 7;
+
+       return 0;
+}
+
+/* Send DiSEqC message */
+static int cx24117_send_diseqc_msg(struct dvb_frontend *fe,
+       struct dvb_diseqc_master_cmd *d)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       int i, ret;
+
+       /* Dump DiSEqC message */
+       dev_dbg(&state->priv->i2c->dev, "%s: demod %d (",
+               __func__, state->demod);
+       for (i = 0; i < d->msg_len; i++)
+               dev_dbg(&state->priv->i2c->dev, "0x%02x ", d->msg[i]);
+       dev_dbg(&state->priv->i2c->dev, ")\n");
+
+       /* Validate length */
+       if (d->msg_len > 15)
+               return -EINVAL;
+
+       /* DiSEqC message */
+       for (i = 0; i < d->msg_len; i++)
+               state->dsec_cmd.args[CX24117_DISEQC_MSGOFS + i] = d->msg[i];
+
+       /* DiSEqC message length */
+       state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] = d->msg_len;
+
+       /* Command length */
+       state->dsec_cmd.len = CX24117_DISEQC_MSGOFS +
+               state->dsec_cmd.args[CX24117_DISEQC_MSGLEN];
+
+       /*
+        * Message is sent with derived else cached burst
+        *
+        * WRITE PORT GROUP COMMAND 38
+        *
+        * 0/A/A: E0 10 38 F0..F3
+        * 1/B/B: E0 10 38 F4..F7
+        * 2/C/A: E0 10 38 F8..FB
+        * 3/D/B: E0 10 38 FC..FF
+        *
+        * databyte[3]= 8421:8421
+        *              ABCD:WXYZ
+        *              CLR :SET
+        *
+        *              WX= PORT SELECT 0..3    (X=TONEBURST)
+        *              Y = VOLTAGE             (0=13V, 1=18V)
+        *              Z = BAND                (0=LOW, 1=HIGH(22K))
+        */
+       if (d->msg_len >= 4 && d->msg[2] == 0x38)
+               state->dsec_cmd.args[CX24117_DISEQC_BURST] =
+                       ((d->msg[3] & 4) >> 2);
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d burst=%d\n",
+               __func__, state->demod,
+               state->dsec_cmd.args[CX24117_DISEQC_BURST]);
+
+       /* Wait for LNB ready */
+       ret = cx24117_wait_for_lnb(fe);
+       if (ret != 0)
+               return ret;
+
+       /* Wait for voltage/min repeat delay */
+       msleep(100);
+
+       /* Command */
+       ret = cx24117_cmd_execute(fe, &state->dsec_cmd);
+       if (ret != 0)
+               return ret;
+       /*
+        * Wait for send
+        *
+        * Eutelsat spec:
+        * >15ms delay          + (XXX determine if FW does this, see set_tone)
+        *  13.5ms per byte     +
+        * >15ms delay          +
+        *  12.5ms burst        +
+        * >15ms delay            (XXX determine if FW does this, see set_tone)
+        */
+       msleep((state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] << 4) + 60);
+
+       return 0;
+}
+
+/* Send DiSEqC burst */
+static int cx24117_diseqc_send_burst(struct dvb_frontend *fe,
+       fe_sec_mini_cmd_t burst)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+
+       dev_dbg(&state->priv->i2c->dev, "%s(%d) demod=%d\n",
+               __func__, burst, state->demod);
+
+       /* DiSEqC burst */
+       if (burst == SEC_MINI_A)
+               state->dsec_cmd.args[CX24117_DISEQC_BURST] =
+                       CX24117_DISEQC_MINI_A;
+       else if (burst == SEC_MINI_B)
+               state->dsec_cmd.args[CX24117_DISEQC_BURST] =
+                       CX24117_DISEQC_MINI_B;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int cx24117_get_priv(struct cx24117_priv **priv,
+       struct i2c_adapter *i2c, u8 client_address)
+{
+       int ret;
+
+       mutex_lock(&cx24117_list_mutex);
+       ret = hybrid_tuner_request_state(struct cx24117_priv, (*priv),
+               hybrid_tuner_instance_list, i2c, client_address, "cx24117");
+       mutex_unlock(&cx24117_list_mutex);
+
+       return ret;
+}
+
+static void cx24117_release_priv(struct cx24117_priv *priv)
+{
+       mutex_lock(&cx24117_list_mutex);
+       if (priv != NULL)
+               hybrid_tuner_release_state(priv);
+       mutex_unlock(&cx24117_list_mutex);
+}
+
+static void cx24117_release(struct dvb_frontend *fe)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       dev_dbg(&state->priv->i2c->dev, "%s demod%d\n",
+               __func__, state->demod);
+       cx24117_release_priv(state->priv);
+       kfree(state);
+}
+
+static struct dvb_frontend_ops cx24117_ops;
+
+struct dvb_frontend *cx24117_attach(const struct cx24117_config *config,
+       struct i2c_adapter *i2c)
+{
+       struct cx24117_state *state = NULL;
+       struct cx24117_priv *priv = NULL;
+       int demod = 0;
+
+       /* get the common data struct for both demods */
+       demod = cx24117_get_priv(&priv, i2c, config->demod_address);
+
+       switch (demod) {
+       case 0:
+               dev_err(&state->priv->i2c->dev,
+                       "%s: Error attaching frontend %d\n",
+                       KBUILD_MODNAME, demod);
+               goto error1;
+               break;
+       case 1:
+               /* new priv instance */
+               priv->i2c = i2c;
+               priv->demod_address = config->demod_address;
+               mutex_init(&priv->fe_lock);
+               break;
+       default:
+               /* existing priv instance */
+               break;
+       }
+
+       /* allocate memory for the internal state */
+       state = kzalloc(sizeof(struct cx24117_state), GFP_KERNEL);
+       if (state == NULL)
+               goto error2;
+
+       state->demod = demod - 1;
+       state->priv = priv;
+
+       /* test i2c bus for ack */
+       if (demod == 0) {
+               if (cx24117_readreg(state, 0x00) < 0)
+                       goto error3;
+       }
+
+       dev_info(&state->priv->i2c->dev,
+               "%s: Attaching frontend %d\n",
+               KBUILD_MODNAME, state->demod);
+
+       /* create dvb_frontend */
+       memcpy(&state->frontend.ops, &cx24117_ops,
+               sizeof(struct dvb_frontend_ops));
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error3:
+       kfree(state);
+error2:
+       cx24117_release_priv(priv);
+error1:
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(cx24117_attach);
+
+/*
+ * Initialise or wake up device
+ *
+ * Power config will reset and load initial firmware if required
+ */
+static int cx24117_initfe(struct dvb_frontend *fe)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       struct cx24117_cmd cmd;
+       int ret;
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
+               __func__, state->demod);
+
+       mutex_lock(&state->priv->fe_lock);
+
+       /* Firmware CMD 36: Power config */
+       cmd.args[0] = CMD_TUNERSLEEP;
+       cmd.args[1] = (state->demod ? 1 : 0);
+       cmd.args[2] = 0;
+       cmd.len = 3;
+       ret = cx24117_cmd_execute_nolock(fe, &cmd);
+       if (ret != 0)
+               goto exit;
+
+       ret = cx24117_diseqc_init(fe);
+       if (ret != 0)
+               goto exit;
+
+       /* CMD 3C */
+       cmd.args[0] = 0x3c;
+       cmd.args[1] = (state->demod ? 1 : 0);
+       cmd.args[2] = 0x10;
+       cmd.args[3] = 0x10;
+       cmd.len = 4;
+       ret = cx24117_cmd_execute_nolock(fe, &cmd);
+       if (ret != 0)
+               goto exit;
+
+       /* CMD 34 */
+       cmd.args[0] = 0x34;
+       cmd.args[1] = (state->demod ? 1 : 0);
+       cmd.args[2] = CX24117_OCC;
+       cmd.len = 3;
+       ret = cx24117_cmd_execute_nolock(fe, &cmd);
+
+exit:
+       mutex_unlock(&state->priv->fe_lock);
+
+       return ret;
+}
+
+/*
+ * Put device to sleep
+ */
+static int cx24117_sleep(struct dvb_frontend *fe)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       struct cx24117_cmd cmd;
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
+               __func__, state->demod);
+
+       /* Firmware CMD 36: Power config */
+       cmd.args[0] = CMD_TUNERSLEEP;
+       cmd.args[1] = (state->demod ? 1 : 0);
+       cmd.args[2] = 1;
+       cmd.len = 3;
+       return cx24117_cmd_execute(fe, &cmd);
+}
+
+/* dvb-core told us to tune, the tv property cache will be complete,
+ * it's safe for is to pull values and use them for tuning purposes.
+ */
+static int cx24117_set_frontend(struct dvb_frontend *fe)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       struct cx24117_cmd cmd;
+       fe_status_t tunerstat;
+       int i, status, ret, retune = 1;
+       u8 reg_clkdiv, reg_ratediv;
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
+               __func__, state->demod);
+
+       switch (c->delivery_system) {
+       case SYS_DVBS:
+               dev_dbg(&state->priv->i2c->dev, "%s() demod%d DVB-S\n",
+                       __func__, state->demod);
+
+               /* Only QPSK is supported for DVB-S */
+               if (c->modulation != QPSK) {
+                       dev_dbg(&state->priv->i2c->dev,
+                               "%s() demod%d unsupported modulation (%d)\n",
+                               __func__, state->demod, c->modulation);
+                       return -EINVAL;
+               }
+
+               /* Pilot doesn't exist in DVB-S, turn bit off */
+               state->dnxt.pilot_val = CX24117_PILOT_OFF;
+
+               /* DVB-S only supports 0.35 */
+               state->dnxt.rolloff_val = CX24117_ROLLOFF_035;
+               break;
+
+       case SYS_DVBS2:
+               dev_dbg(&state->priv->i2c->dev, "%s() demod%d DVB-S2\n",
+                       __func__, state->demod);
+
+               /*
+                * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2,
+                * but not hardware auto detection
+                */
+               if (c->modulation != PSK_8 && c->modulation != QPSK) {
+                       dev_dbg(&state->priv->i2c->dev,
+                               "%s() demod%d unsupported modulation (%d)\n",
+                               __func__, state->demod, c->modulation);
+                       return -EOPNOTSUPP;
+               }
+
+               switch (c->pilot) {
+               case PILOT_AUTO:
+                       state->dnxt.pilot_val = CX24117_PILOT_AUTO;
+                       break;
+               case PILOT_OFF:
+                       state->dnxt.pilot_val = CX24117_PILOT_OFF;
+                       break;
+               case PILOT_ON:
+                       state->dnxt.pilot_val = CX24117_PILOT_ON;
+                       break;
+               default:
+                       dev_dbg(&state->priv->i2c->dev,
+                               "%s() demod%d unsupported pilot mode (%d)\n",
+                               __func__, state->demod, c->pilot);
+                       return -EOPNOTSUPP;
+               }
+
+               switch (c->rolloff) {
+               case ROLLOFF_20:
+                       state->dnxt.rolloff_val = CX24117_ROLLOFF_020;
+                       break;
+               case ROLLOFF_25:
+                       state->dnxt.rolloff_val = CX24117_ROLLOFF_025;
+                       break;
+               case ROLLOFF_35:
+                       state->dnxt.rolloff_val = CX24117_ROLLOFF_035;
+                       break;
+               case ROLLOFF_AUTO:
+                       state->dnxt.rolloff_val = CX24117_ROLLOFF_035;
+                       /* soft-auto rolloff */
+                       retune = 3;
+                       break;
+               default:
+                       dev_warn(&state->priv->i2c->dev,
+                               "%s: demod%d unsupported rolloff (%d)\n",
+                               KBUILD_MODNAME, state->demod, c->rolloff);
+                       return -EOPNOTSUPP;
+               }
+               break;
+
+       default:
+               dev_warn(&state->priv->i2c->dev,
+                       "%s: demod %d unsupported delivery system (%d)\n",
+                       KBUILD_MODNAME, state->demod, c->delivery_system);
+               return -EINVAL;
+       }
+
+       state->dnxt.delsys = c->delivery_system;
+       state->dnxt.modulation = c->modulation;
+       state->dnxt.frequency = c->frequency;
+       state->dnxt.pilot = c->pilot;
+       state->dnxt.rolloff = c->rolloff;
+
+       ret = cx24117_set_inversion(state, c->inversion);
+       if (ret !=  0)
+               return ret;
+
+       ret = cx24117_set_fec(state,
+               c->delivery_system, c->modulation, c->fec_inner);
+       if (ret !=  0)
+               return ret;
+
+       ret = cx24117_set_symbolrate(state, c->symbol_rate);
+       if (ret !=  0)
+               return ret;
+
+       /* discard the 'current' tuning parameters and prepare to tune */
+       cx24117_clone_params(fe);
+
+       dev_dbg(&state->priv->i2c->dev,
+               "%s: delsys      = %d\n", __func__, state->dcur.delsys);
+       dev_dbg(&state->priv->i2c->dev,
+               "%s: modulation  = %d\n", __func__, state->dcur.modulation);
+       dev_dbg(&state->priv->i2c->dev,
+               "%s: frequency   = %d\n", __func__, state->dcur.frequency);
+       dev_dbg(&state->priv->i2c->dev,
+               "%s: pilot       = %d (val = 0x%02x)\n", __func__,
+               state->dcur.pilot, state->dcur.pilot_val);
+       dev_dbg(&state->priv->i2c->dev,
+               "%s: retune      = %d\n", __func__, retune);
+       dev_dbg(&state->priv->i2c->dev,
+               "%s: rolloff     = %d (val = 0x%02x)\n", __func__,
+               state->dcur.rolloff, state->dcur.rolloff_val);
+       dev_dbg(&state->priv->i2c->dev,
+               "%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
+       dev_dbg(&state->priv->i2c->dev,
+               "%s: FEC         = %d (mask/val = 0x%02x/0x%02x)\n", __func__,
+               state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
+       dev_dbg(&state->priv->i2c->dev,
+               "%s: Inversion   = %d (val = 0x%02x)\n", __func__,
+               state->dcur.inversion, state->dcur.inversion_val);
+
+       /* Prepare a tune request */
+       cmd.args[0] = CMD_TUNEREQUEST;
+
+       /* demod */
+       cmd.args[1] = state->demod;
+
+       /* Frequency */
+       cmd.args[2] = (state->dcur.frequency & 0xff0000) >> 16;
+       cmd.args[3] = (state->dcur.frequency & 0x00ff00) >> 8;
+       cmd.args[4] = (state->dcur.frequency & 0x0000ff);
+
+       /* Symbol Rate */
+       cmd.args[5] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
+       cmd.args[6] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
+
+       /* Automatic Inversion */
+       cmd.args[7] = state->dcur.inversion_val;
+
+       /* Modulation / FEC / Pilot */
+       cmd.args[8] = state->dcur.fec_val | state->dcur.pilot_val;
+
+       cmd.args[9] = CX24117_SEARCH_RANGE_KHZ >> 8;
+       cmd.args[10] = CX24117_SEARCH_RANGE_KHZ & 0xff;
+
+       cmd.args[11] = state->dcur.rolloff_val;
+       cmd.args[12] = state->dcur.fec_mask;
+
+       if (state->dcur.symbol_rate > 30000000) {
+               reg_ratediv = 0x04;
+               reg_clkdiv = 0x02;
+       } else if (state->dcur.symbol_rate > 10000000) {
+               reg_ratediv = 0x06;
+               reg_clkdiv = 0x03;
+       } else {
+               reg_ratediv = 0x0a;
+               reg_clkdiv = 0x05;
+       }
+
+       cmd.args[13] = reg_ratediv;
+       cmd.args[14] = reg_clkdiv;
+
+       cx24117_writereg(state, (state->demod == 0) ?
+               CX24117_REG_CLKDIV0 : CX24117_REG_CLKDIV1, reg_clkdiv);
+       cx24117_writereg(state, (state->demod == 0) ?
+               CX24117_REG_RATEDIV0 : CX24117_REG_RATEDIV1, reg_ratediv);
+
+       cmd.args[15] = CX24117_PNE;
+       cmd.len = 16;
+
+       do {
+               /* Reset status register */
+               status = cx24117_readreg(state, (state->demod == 0) ?
+                       CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1) &
+                       CX24117_SIGNAL_MASK;
+
+               dev_dbg(&state->priv->i2c->dev,
+                       "%s() demod%d status_setfe = %02x\n",
+                       __func__, state->demod, status);
+
+               cx24117_writereg(state, (state->demod == 0) ?
+                       CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1, status);
+
+               /* Tune */
+               ret = cx24117_cmd_execute(fe, &cmd);
+               if (ret != 0)
+                       break;
+
+               /*
+                * Wait for up to 500 ms before retrying
+                *
+                * If we are able to tune then generally it occurs within 100ms.
+                * If it takes longer, try a different rolloff setting.
+                */
+               for (i = 0; i < 50; i++) {
+                       cx24117_read_status(fe, &tunerstat);
+                       status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);
+                       if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {
+                               dev_dbg(&state->priv->i2c->dev,
+                                       "%s() demod%d tuned\n",
+                                       __func__, state->demod);
+                               return 0;
+                       }
+                       msleep(20);
+               }
+
+               dev_dbg(&state->priv->i2c->dev, "%s() demod%d not tuned\n",
+                       __func__, state->demod);
+
+               /* try next rolloff value */
+               if (state->dcur.rolloff == 3)
+                       cmd.args[11]--;
+
+       } while (--retune);
+       return -EINVAL;
+}
+
+static int cx24117_tune(struct dvb_frontend *fe, bool re_tune,
+       unsigned int mode_flags, unsigned int *delay, fe_status_t *status)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+
+       dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
+               __func__, state->demod);
+
+       *delay = HZ / 5;
+       if (re_tune) {
+               int ret = cx24117_set_frontend(fe);
+               if (ret)
+                       return ret;
+       }
+       return cx24117_read_status(fe, status);
+}
+
+static int cx24117_get_algo(struct dvb_frontend *fe)
+{
+       return DVBFE_ALGO_HW;
+}
+
+static int cx24117_get_frontend(struct dvb_frontend *fe)
+{
+       struct cx24117_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       struct cx24117_cmd cmd;
+       u8 reg, st, inv;
+       int ret, idx;
+       unsigned int freq;
+       short srate_os, freq_os;
+
+       u8 buf[0x1f-4];
+
+       cmd.args[0] = 0x1c;
+       cmd.args[1] = (u8) state->demod;
+       cmd.len = 2;
+       ret = cx24117_cmd_execute(fe, &cmd);
+       if (ret != 0)
+               return ret;
+
+       /* read all required regs at once */
+       reg = (state->demod == 0) ? CX24117_REG_FREQ3_0 : CX24117_REG_FREQ3_1;
+       ret = cx24117_readregN(state, reg, buf, 0x1f-4);
+       if (ret != 0)
+               return ret;
+
+       st = buf[5];
+
+       /* get spectral inversion */
+       inv = (((state->demod == 0) ? ~st : st) >> 6) & 1;
+       if (inv == 0)
+               c->inversion = INVERSION_OFF;
+       else
+               c->inversion = INVERSION_ON;
+
+       /* modulation and fec */
+       idx = st & 0x3f;
+       if (c->delivery_system == SYS_DVBS2) {
+               if (idx > 11)
+                       idx += 9;
+               else
+                       idx += 7;
+       }
+
+       c->modulation = cx24117_modfec_modes[idx].modulation;
+       c->fec_inner = cx24117_modfec_modes[idx].fec;
+
+       /* frequency */
+       freq = (buf[0] << 16) | (buf[1] << 8) | buf[2];
+       freq_os = (buf[8] << 8) | buf[9];
+       c->frequency = freq + freq_os;
+
+       /* symbol rate */
+       srate_os = (buf[10] << 8) | buf[11];
+       c->symbol_rate = -1000 * srate_os + state->dcur.symbol_rate;
+       return 0;
+}
+
+static struct dvb_frontend_ops cx24117_ops = {
+       .delsys = { SYS_DVBS, SYS_DVBS2 },
+       .info = {
+               .name = "Conexant CX24117/CX24132",
+               .frequency_min = 950000,
+               .frequency_max = 2150000,
+               .frequency_stepsize = 1011, /* kHz for QPSK frontends */
+               .frequency_tolerance = 5000,
+               .symbol_rate_min = 1000000,
+               .symbol_rate_max = 45000000,
+               .caps = FE_CAN_INVERSION_AUTO |
+                       FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+                       FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                       FE_CAN_2G_MODULATION |
+                       FE_CAN_QPSK | FE_CAN_RECOVER
+       },
+
+       .release = cx24117_release,
+
+       .init = cx24117_initfe,
+       .sleep = cx24117_sleep,
+       .read_status = cx24117_read_status,
+       .read_ber = cx24117_read_ber,
+       .read_signal_strength = cx24117_read_signal_strength,
+       .read_snr = cx24117_read_snr,
+       .read_ucblocks = cx24117_read_ucblocks,
+       .set_tone = cx24117_set_tone,
+       .set_voltage = cx24117_set_voltage,
+       .diseqc_send_master_cmd = cx24117_send_diseqc_msg,
+       .diseqc_send_burst = cx24117_diseqc_send_burst,
+       .get_frontend_algo = cx24117_get_algo,
+       .tune = cx24117_tune,
+
+       .set_frontend = cx24117_set_frontend,
+       .get_frontend = cx24117_get_frontend,
+};
+
+
+MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24117/cx24132 hardware");
+MODULE_AUTHOR("Luis Alves (ljalvs@gmail.com)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.1");
+MODULE_FIRMWARE(CX24117_DEFAULT_FIRMWARE);
+
diff --git a/drivers/media/dvb-frontends/cx24117.h b/drivers/media/dvb-frontends/cx24117.h
new file mode 100644 (file)
index 0000000..4e59e95
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+    Conexant cx24117/cx24132 - Dual DVBS/S2 Satellite demod/tuner driver
+
+    Copyright (C) 2013 Luis Alves <ljalvs@gmail.com>
+       (based on cx24116.h by Steven Toth)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the 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 CX24117_H
+#define CX24117_H
+
+#include <linux/kconfig.h>
+#include <linux/dvb/frontend.h>
+
+struct cx24117_config {
+       /* the demodulator's i2c address */
+       u8 demod_address;
+};
+
+#if IS_ENABLED(CONFIG_DVB_CX24117)
+extern struct dvb_frontend *cx24117_attach(
+       const struct cx24117_config *config,
+       struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *cx24117_attach(
+       const struct cx24117_config *config,
+       struct i2c_adapter *i2c)
+{
+       dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+#endif
+
+#endif /* CX24117_H */
index a771da3..72fb583 100644 (file)
@@ -739,7 +739,7 @@ static int cx24123_set_voltage(struct dvb_frontend *fe,
                return 0;
        default:
                return -EINVAL;
-       };
+       }
 
        return 0;
 }
index 7ca5c69..03930d5 100644 (file)
 
 #include "cxd2820r_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 /* write multiple registers */
 static int cxd2820r_wr_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg,
        u8 *val, int len)
 {
        int ret;
-       u8 buf[len+1];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
                        .addr = i2c,
                        .flags = 0,
-                       .len = sizeof(buf),
+                       .len = len + 1,
                        .buf = buf,
                }
        };
 
+       if (1 + len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        buf[0] = reg;
        memcpy(&buf[1], val, len);
 
@@ -55,7 +65,7 @@ static int cxd2820r_rd_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg,
        u8 *val, int len)
 {
        int ret;
-       u8 buf[len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[2] = {
                {
                        .addr = i2c,
@@ -65,11 +75,18 @@ static int cxd2820r_rd_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg,
                }, {
                        .addr = i2c,
                        .flags = I2C_M_RD,
-                       .len = sizeof(buf),
+                       .len = len,
                        .buf = buf,
                }
        };
 
+       if (len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        ret = i2c_transfer(priv->i2c, msg, 2);
        if (ret == 2) {
                memcpy(val, buf, len);
index 6201c59..e540cfb 100644 (file)
@@ -649,9 +649,9 @@ static int dib9000_risc_debug_buf(struct dib9000_state *state, u16 * data, u8 si
        b[2 * (size - 2) - 1] = '\0';   /* Bullet proof the buffer */
        if (*b == '~') {
                b++;
-               dprintk(b);
+               dprintk("%s", b);
        } else
-               dprintk("RISC%d: %d.%04d %s", state->fe_id, ts / 10000, ts % 10000, *b ? b : "<emtpy>");
+               dprintk("RISC%d: %d.%04d %s", state->fe_id, ts / 10000, ts % 10000, *b ? b : "<empty>");
        return 1;
 }
 
index 9a21347..959ae36 100644 (file)
 #define DRX_I2C_MODEFLAGS     0xC0
 #define DRX_I2C_FLAGS         0xF0
 
-#ifndef SIZEOF_ARRAY
-#define SIZEOF_ARRAY(array) (sizeof((array))/sizeof((array)[0]))
-#endif
-
 #define DEFAULT_LOCK_TIMEOUT    1100
 
 #define DRX_CHANNEL_AUTO 0
@@ -1018,7 +1014,7 @@ static int HI_CfgCommand(struct drxd_state *state)
                status = Write16(state, HI_RA_RAM_SRV_CMD__A,
                                 HI_RA_RAM_SRV_CMD_CONFIG, 0);
        else
-               status = HI_Command(state, HI_RA_RAM_SRV_CMD_CONFIG, 0);
+               status = HI_Command(state, HI_RA_RAM_SRV_CMD_CONFIG, NULL);
        mutex_unlock(&state->mutex);
        return status;
 }
@@ -1039,7 +1035,7 @@ static int HI_ResetCommand(struct drxd_state *state)
        status = Write16(state, HI_RA_RAM_SRV_RST_KEY__A,
                         HI_RA_RAM_SRV_RST_KEY_ACT, 0);
        if (status == 0)
-               status = HI_Command(state, HI_RA_RAM_SRV_CMD_RESET, 0);
+               status = HI_Command(state, HI_RA_RAM_SRV_CMD_RESET, NULL);
        mutex_unlock(&state->mutex);
        msleep(1);
        return status;
@@ -2837,7 +2833,7 @@ static int drxd_init(struct dvb_frontend *fe)
        int err = 0;
 
 /*     if (request_firmware(&state->fw, "drxd.fw", state->dev)<0) */
-       return DRXD_init(state, 0, 0);
+       return DRXD_init(state, NULL, 0);
 
        err = DRXD_init(state, state->fw->data, state->fw->size);
        release_firmware(state->fw);
@@ -2973,7 +2969,7 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config,
 
        mutex_init(&state->mutex);
 
-       if (Read16(state, 0, 0, 0) < 0)
+       if (Read16(state, 0, NULL, 0) < 0)
                goto error;
 
        state->frontend.ops = drxd_ops;
index 082014d..d416c15 100644 (file)
@@ -1083,7 +1083,7 @@ static int hi_cfg_command(struct drxk_state *state)
                         SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
        if (status < 0)
                goto error;
-       status = hi_command(state, SIO_HI_RA_RAM_CMD_CONFIG, 0);
+       status = hi_command(state, SIO_HI_RA_RAM_CMD_CONFIG, NULL);
        if (status < 0)
                goto error;
 
@@ -2781,7 +2781,7 @@ static int ConfigureI2CBridge(struct drxk_state *state, bool b_enable_bridge)
                        goto error;
        }
 
-       status = hi_command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, 0);
+       status = hi_command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, NULL);
 
 error:
        if (status < 0)
index c1c3400..cadcae4 100644 (file)
@@ -31,6 +31,9 @@
 #include "itd1000.h"
 #include "itd1000_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 static int debug;
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
@@ -52,10 +55,18 @@ MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
 /* don't write more than one byte with flexcop behind */
 static int itd1000_write_regs(struct itd1000_state *state, u8 reg, u8 v[], u8 len)
 {
-       u8 buf[1+len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg = {
                .addr = state->cfg->i2c_address, .flags = 0, .buf = buf, .len = len+1
        };
+
+       if (1 + len > sizeof(buf)) {
+               printk(KERN_WARNING
+                      "itd1000: i2c wr reg=%04x: len=%d is too big!\n",
+                      reg, len);
+               return -EINVAL;
+       }
+
        buf[0] = reg;
        memcpy(&buf[1], v, len);
 
index ec388c1..a74ac0d 100644 (file)
@@ -36,6 +36,8 @@
 #include "mt312_priv.h"
 #include "mt312.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
 
 struct mt312_state {
        struct i2c_adapter *i2c;
@@ -96,9 +98,15 @@ static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg,
                       const u8 *src, const size_t count)
 {
        int ret;
-       u8 buf[count + 1];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg;
 
+       if (1 + count > sizeof(buf)) {
+               printk(KERN_WARNING
+                      "mt312: write: len=%zd is too big!\n", count);
+               return -EINVAL;
+       }
+
        if (debug) {
                int i;
                dprintk("W(%d):", reg & 0x7f);
index 8e28894..fbca985 100644 (file)
@@ -39,6 +39,9 @@
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 #define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw"
 #define NXT2004_DEFAULT_FIRMWARE "dvb-fe-nxt2004.fw"
 #define CRC_CCIT_MASK 0x1021
@@ -95,10 +98,16 @@ static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len)
 static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg,
                               const u8 *buf, u8 len)
 {
-       u8 buf2 [len+1];
+       u8 buf2[MAX_XFER_SIZE];
        int err;
        struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf2, .len = len + 1 };
 
+       if (1 + len > sizeof(buf2)) {
+               pr_warn("%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        __func__, reg, len);
+               return -EINVAL;
+       }
+
        buf2[0] = reg;
        memcpy(&buf2[1], buf, len);
 
index 362d26d..7efb796 100644 (file)
 
 #include "rtl2830_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 /* write multiple hardware registers */
 static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, const u8 *val, int len)
 {
        int ret;
-       u8 buf[1+len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
                        .addr = priv->cfg.i2c_addr,
                        .flags = 0,
-                       .len = 1+len,
+                       .len = 1 + len,
                        .buf = buf,
                }
        };
 
+       if (1 + len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        buf[0] = reg;
        memcpy(&buf[1], val, len);
 
index facb848..ff73da9 100644 (file)
@@ -22,6 +22,9 @@
 #include "dvb_math.h"
 #include <linux/bitops.h>
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 int rtl2832_debug;
 module_param_named(debug, rtl2832_debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
@@ -162,16 +165,23 @@ static const struct rtl2832_reg_entry registers[] = {
 static int rtl2832_wr(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
 {
        int ret;
-       u8 buf[1+len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
                        .addr = priv->cfg.i2c_addr,
                        .flags = 0,
-                       .len = 1+len,
+                       .len = 1 + len,
                        .buf = buf,
                }
        };
 
+       if (1 + len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        buf[0] = reg;
        memcpy(&buf[1], val, len);
 
@@ -489,6 +499,7 @@ static int rtl2832_init(struct dvb_frontend *fe)
                init = rtl2832_tuner_init_e4000;
                break;
        case RTL2832_TUNER_R820T:
+       case RTL2832_TUNER_R828D:
                len = ARRAY_SIZE(rtl2832_tuner_init_r820t);
                init = rtl2832_tuner_init_r820t;
                break;
index 91b2dcf..2cfbb6a 100644 (file)
@@ -53,6 +53,7 @@ struct rtl2832_config {
 #define RTL2832_TUNER_E4000     0x27
 #define RTL2832_TUNER_FC0013    0x29
 #define RTL2832_TUNER_R820T    0x2a
+#define RTL2832_TUNER_R828D    0x2b
        u8 tuner;
 };
 
index e2fec9e..93eeaf7 100644 (file)
@@ -836,9 +836,16 @@ static u32 s5h1420_tuner_i2c_func(struct i2c_adapter *adapter)
 static int s5h1420_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
 {
        struct s5h1420_state *state = i2c_get_adapdata(i2c_adap);
-       struct i2c_msg m[1 + num];
+       struct i2c_msg m[3];
        u8 tx_open[2] = { CON_1, state->CON_1_val | 1 }; /* repeater stops once there was a stop condition */
 
+       if (1 + num > ARRAY_SIZE(m)) {
+               printk(KERN_WARNING
+                      "%s: i2c xfer: num=%d is too big!\n",
+                      KBUILD_MODNAME, num);
+               return  -EOPNOTSUPP;
+       }
+
        memset(m, 0, sizeof(struct i2c_msg) * (1 + num));
 
        m[0].addr = state->config->demod_address;
@@ -847,7 +854,7 @@ static int s5h1420_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c
 
        memcpy(&m[1], msg, sizeof(struct i2c_msg) * num);
 
-       return i2c_transfer(state->i2c, m, 1+num) == 1 + num ? num : -EIO;
+       return i2c_transfer(state->i2c, m, 1 + num) == 1 + num ? num : -EIO;
 }
 
 static struct i2c_algorithm s5h1420_tuner_i2c_algo = {
index 3dd5714..07cd5ea 100644 (file)
@@ -32,6 +32,9 @@
 #include "stb0899_priv.h"
 #include "stb0899_reg.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 static unsigned int verbose = 0;//1;
 module_param(verbose, int, 0644);
 
@@ -499,7 +502,7 @@ err:
 int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data, u32 count)
 {
        int ret;
-       u8 buf[2 + count];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg i2c_msg = {
                .addr   = state->config->demod_address,
                .flags  = 0,
@@ -507,6 +510,13 @@ int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data,
                .len    = 2 + count
        };
 
+       if (2 + count > sizeof(buf)) {
+               printk(KERN_WARNING
+                      "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                      KBUILD_MODNAME, reg, count);
+               return -EINVAL;
+       }
+
        buf[0] = reg >> 8;
        buf[1] = reg & 0xff;
        memcpy(&buf[2], data, count);
index 45f9523..cea175d 100644 (file)
@@ -31,6 +31,8 @@
 static unsigned int verbose;
 module_param(verbose, int, 0644);
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
 
 #define FE_ERROR               0
 #define FE_NOTICE              1
@@ -183,7 +185,7 @@ static int stb6100_read_reg(struct stb6100_state *state, u8 reg)
 static int stb6100_write_reg_range(struct stb6100_state *state, u8 buf[], int start, int len)
 {
        int rc;
-       u8 cmdbuf[len + 1];
+       u8 cmdbuf[MAX_XFER_SIZE];
        struct i2c_msg msg = {
                .addr   = state->config->tuner_address,
                .flags  = 0,
@@ -191,6 +193,13 @@ static int stb6100_write_reg_range(struct stb6100_state *state, u8 buf[], int st
                .len    = len + 1
        };
 
+       if (1 + len > sizeof(buf)) {
+               printk(KERN_WARNING
+                      "%s: i2c wr: len=%d is too big!\n",
+                      KBUILD_MODNAME, len);
+               return -EINVAL;
+       }
+
        if (unlikely(start < 1 || start + len > STB6100_NUMREGS)) {
                dprintk(verbose, FE_ERROR, 1, "Invalid register range %d:%d",
                        start, len);
index 7b6dba3..4587727 100644 (file)
@@ -33,6 +33,9 @@
 #include "stv0367_regs.h"
 #include "stv0367_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 static int stvdebug;
 module_param_named(debug, stvdebug, int, 0644);
 
@@ -767,7 +770,7 @@ static struct st_register def0367cab[STV0367CAB_NBREGS] = {
 static
 int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len)
 {
-       u8 buf[len + 2];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg = {
                .addr = state->config->demod_address,
                .flags = 0,
@@ -776,6 +779,14 @@ int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len)
        };
        int ret;
 
+       if (2 + len > sizeof(buf)) {
+               printk(KERN_WARNING
+                      "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                      KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
+
        buf[0] = MSB(reg);
        buf[1] = LSB(reg);
        memcpy(buf + 2, data, len);
index 56d470a..23e872f 100644 (file)
@@ -35,6 +35,9 @@
 #include "stv090x.h"
 #include "stv090x_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 static unsigned int verbose;
 module_param(verbose, int, 0644);
 
@@ -722,9 +725,16 @@ static int stv090x_write_regs(struct stv090x_state *state, unsigned int reg, u8
 {
        const struct stv090x_config *config = state->config;
        int ret;
-       u8 buf[2 + count];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg i2c_msg = { .addr = config->address, .flags = 0, .buf = buf, .len = 2 + count };
 
+       if (2 + count > sizeof(buf)) {
+               printk(KERN_WARNING
+                      "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                      KBUILD_MODNAME, reg, count);
+               return -EINVAL;
+       }
+
        buf[0] = reg >> 8;
        buf[1] = reg & 0xff;
        memcpy(&buf[2], data, count);
index 20b5fa9..b142583 100644 (file)
@@ -30,6 +30,9 @@
 
 #include "stv6110.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 static int debug;
 
 struct stv6110_priv {
@@ -68,7 +71,7 @@ static int stv6110_write_regs(struct dvb_frontend *fe, u8 buf[],
 {
        struct stv6110_priv *priv = fe->tuner_priv;
        int rc;
-       u8 cmdbuf[len + 1];
+       u8 cmdbuf[MAX_XFER_SIZE];
        struct i2c_msg msg = {
                .addr   = priv->i2c_address,
                .flags  = 0,
@@ -78,6 +81,13 @@ static int stv6110_write_regs(struct dvb_frontend *fe, u8 buf[],
 
        dprintk("%s\n", __func__);
 
+       if (1 + len > sizeof(cmdbuf)) {
+               printk(KERN_WARNING
+                      "%s: i2c wr: len=%d is too big!\n",
+                      KBUILD_MODNAME, len);
+               return -EINVAL;
+       }
+
        if (start + len > 8)
                return -EINVAL;
 
index f36cab1..e66154e 100644 (file)
@@ -32,6 +32,9 @@
 #include "stv6110x.h"
 #include "stv6110x_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 static unsigned int verbose;
 module_param(verbose, int, 0644);
 MODULE_PARM_DESC(verbose, "Set Verbosity level");
@@ -61,7 +64,8 @@ static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 da
 {
        int ret;
        const struct stv6110x_config *config = stv6110x->config;
-       u8 buf[len + 1];
+       u8 buf[MAX_XFER_SIZE];
+
        struct i2c_msg msg = {
                .addr = config->addr,
                .flags = 0,
@@ -69,6 +73,13 @@ static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 da
                .len = len + 1
        };
 
+       if (1 + len > sizeof(buf)) {
+               printk(KERN_WARNING
+                      "%s: i2c wr: len=%d is too big!\n",
+                      KBUILD_MODNAME, len);
+               return -EINVAL;
+       }
+
        if (start + len > 8)
                return -EINVAL;
 
index e79749c..8ad3a57 100644 (file)
@@ -20,6 +20,9 @@
 
 #include "tda10071_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 static struct dvb_frontend_ops tda10071_ops;
 
 /* write multiple registers */
@@ -27,16 +30,23 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
        int len)
 {
        int ret;
-       u8 buf[len+1];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
                        .addr = priv->cfg.demod_i2c_addr,
                        .flags = 0,
-                       .len = sizeof(buf),
+                       .len = 1 + len,
                        .buf = buf,
                }
        };
 
+       if (1 + len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        buf[0] = reg;
        memcpy(&buf[1], val, len);
 
@@ -56,7 +66,7 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
        int len)
 {
        int ret;
-       u8 buf[len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[2] = {
                {
                        .addr = priv->cfg.demod_i2c_addr,
@@ -66,11 +76,18 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
                }, {
                        .addr = priv->cfg.demod_i2c_addr,
                        .flags = I2C_M_RD,
-                       .len = sizeof(buf),
+                       .len = len,
                        .buf = buf,
                }
        };
 
+       if (len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        ret = i2c_transfer(priv->i2c, msg, 2);
        if (ret == 2) {
                memcpy(val, buf, len);
index d281f77..2c54586 100644 (file)
@@ -34,6 +34,9 @@
 #include "dvb_frontend.h"
 #include "tda18271c2dd.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 struct SStandardParam {
        s32   m_IFFrequency;
        u32   m_BandWidth;
@@ -139,11 +142,18 @@ static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
 static int WriteRegs(struct tda_state *state,
                     u8 SubAddr, u8 *Regs, u16 nRegs)
 {
-       u8 data[nRegs+1];
+       u8 data[MAX_XFER_SIZE];
+
+       if (1 + nRegs > sizeof(data)) {
+               printk(KERN_WARNING
+                      "%s: i2c wr: len=%d is too big!\n",
+                      KBUILD_MODNAME, nRegs);
+               return -EINVAL;
+       }
 
        data[0] = SubAddr;
        memcpy(data + 1, Regs, nRegs);
-       return i2c_write(state->i2c, state->adr, data, nRegs+1);
+       return i2c_write(state->i2c, state->adr, data, nRegs + 1);
 }
 
 static int WriteReg(struct tda_state *state, u8 SubAddr, u8 Reg)
index 9d08350..69e62f4 100644 (file)
@@ -189,7 +189,7 @@ static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t ton
                return tda8083_writereg (state, 0x29, 0x80);
        default:
                return -EINVAL;
-       };
+       }
 }
 
 static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t voltage)
@@ -201,7 +201,7 @@ static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t vo
                return tda8083_writereg (state, 0x20, 0x11);
        default:
                return -EINVAL;
-       };
+       }
 }
 
 static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_cmd_t burst)
index ad7ad85..9aba044 100644 (file)
@@ -31,6 +31,7 @@ struct ts2020_priv {
        struct i2c_adapter *i2c;
        u8 clk_out_div;
        u32 frequency;
+       u32 frequency_div;
 };
 
 static int ts2020_release(struct dvb_frontend *fe)
@@ -193,7 +194,7 @@ static int ts2020_set_params(struct dvb_frontend *fe)
        u8 lo = 0x01, div4 = 0x0;
 
        /* Calculate frequency divider */
-       if (frequency < 1060000) {
+       if (frequency < priv->frequency_div) {
                lo |= 0x10;
                div4 = 0x1;
                ndiv = (frequency * 14 * 4) / TS2020_XTAL_FREQ;
@@ -340,8 +341,12 @@ struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe,
        priv->i2c_address = config->tuner_address;
        priv->i2c = i2c;
        priv->clk_out_div = config->clk_out_div;
+       priv->frequency_div = config->frequency_div;
        fe->tuner_priv = priv;
 
+       if (!priv->frequency_div)
+               priv->frequency_div = 1060000;
+
        /* Wake Up the tuner */
        if ((0x03 & ts2020_readreg(fe, 0x00)) == 0x00) {
                ts2020_writereg(fe, 0x00, 0x01);
index 5bcb9a7..b2fe6bb 100644 (file)
@@ -28,6 +28,7 @@
 struct ts2020_config {
        u8 tuner_address;
        u8 clk_out_div;
+       u32 frequency_div;
 };
 
 #if IS_ENABLED(CONFIG_DVB_TS2020)
index eff9c5f..91b6b2e 100644 (file)
@@ -30,6 +30,9 @@
 
 static int debug;
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 #define dprintk(args...) \
        do { \
                if (debug) \
@@ -98,7 +101,7 @@ static int zl10039_write(struct zl10039_state *state,
                        const enum zl10039_reg_addr reg, const u8 *src,
                        const size_t count)
 {
-       u8 buf[count + 1];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg = {
                .addr = state->i2c_addr,
                .flags = 0,
@@ -106,6 +109,13 @@ static int zl10039_write(struct zl10039_state *state,
                .len = count + 1,
        };
 
+       if (1 + count > sizeof(buf)) {
+               printk(KERN_WARNING
+                      "%s: i2c wr reg=%04x: len=%zd is too big!\n",
+                      KBUILD_MODNAME, reg, count);
+               return -EINVAL;
+       }
+
        dprintk("%s\n", __func__);
        /* Write register address and data in one go */
        buf[0] = reg;
index d18be19..842654d 100644 (file)
@@ -621,6 +621,15 @@ config VIDEO_AS3645A
          This is a driver for the AS3645A and LM3555 flash controllers. It has
          build in control for flash, torch and indicator LEDs.
 
+config VIDEO_LM3560
+       tristate "LM3560 dual flash driver support"
+       depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+       depends on MEDIA_CAMERA_SUPPORT
+       select REGMAP_I2C
+       ---help---
+         This is a driver for the lm3560 dual flash controllers. It controls
+         flash, torch LEDs.
+
 comment "Video improvement chips"
 
 config VIDEO_UPD64031A
@@ -646,7 +655,7 @@ config VIDEO_UPD64083
          To compile this driver as a module, choose M here: the
          module will be called upd64083.
 
-comment "Miscelaneous helper chips"
+comment "Miscellaneous helper chips"
 
 config VIDEO_THS7303
        tristate "THS7303/53 Video Amplifier"
index 9f462df..e03f177 100644 (file)
@@ -70,6 +70,7 @@ obj-$(CONFIG_VIDEO_S5K4ECGX)  += s5k4ecgx.o
 obj-$(CONFIG_VIDEO_S5C73M3)    += s5c73m3/
 obj-$(CONFIG_VIDEO_ADP1653)    += adp1653.o
 obj-$(CONFIG_VIDEO_AS3645A)    += as3645a.o
+obj-$(CONFIG_VIDEO_LM3560)     += lm3560.o
 obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
 obj-$(CONFIG_VIDEO_AK881X)             += ak881x.o
 obj-$(CONFIG_VIDEO_IR_I2C)  += ir-kbd-i2c.o
index 6f738d8..d45e0e3 100644 (file)
@@ -178,7 +178,7 @@ static int adv7183_log_status(struct v4l2_subdev *sd)
                        adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1),
                        adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2),
                        adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3));
-       v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+       v4l2_info(sd, "adv7183: Hsync position control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
                        adv7183_read(sd, ADV7183_HS_POS_CTRL_1),
                        adv7183_read(sd, ADV7183_HS_POS_CTRL_2),
                        adv7183_read(sd, ADV7183_HS_POS_CTRL_3));
index aeb56c5..d4e15a6 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/videodev2.h>
 #include <linux/uaccess.h>
+#include <linux/of.h>
 
 #include <media/adv7343.h>
 #include <media/v4l2-async.h>
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
new file mode 100644 (file)
index 0000000..3317a9a
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ * drivers/media/i2c/lm3560.c
+ * General device driver for TI lm3560, FLASH LED Driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * Contact: Daniel Jeong <gshark.jeong@gmail.com>
+ *                     Ldd-Mlp <ldd-mlp@list.ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/videodev2.h>
+#include <media/lm3560.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+/* registers definitions */
+#define REG_ENABLE             0x10
+#define REG_TORCH_BR   0xa0
+#define REG_FLASH_BR   0xb0
+#define REG_FLASH_TOUT 0xc0
+#define REG_FLAG               0xd0
+#define REG_CONFIG1            0xe0
+
+/* Fault Mask */
+#define FAULT_TIMEOUT  (1<<0)
+#define FAULT_OVERTEMP (1<<1)
+#define FAULT_SHORT_CIRCUIT    (1<<2)
+
+enum led_enable {
+       MODE_SHDN = 0x0,
+       MODE_TORCH = 0x2,
+       MODE_FLASH = 0x3,
+};
+
+/* struct lm3560_flash
+ *
+ * @pdata: platform data
+ * @regmap: reg. map for i2c
+ * @lock: muxtex for serial access.
+ * @led_mode: V4L2 LED mode
+ * @ctrls_led: V4L2 contols
+ * @subdev_led: V4L2 subdev
+ */
+struct lm3560_flash {
+       struct device *dev;
+       struct lm3560_platform_data *pdata;
+       struct regmap *regmap;
+       struct mutex lock;
+
+       enum v4l2_flash_led_mode led_mode;
+       struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX];
+       struct v4l2_subdev subdev_led[LM3560_LED_MAX];
+};
+
+#define to_lm3560_flash(_ctrl, _no)    \
+       container_of(_ctrl->handler, struct lm3560_flash, ctrls_led[_no])
+
+/* enable mode control */
+static int lm3560_mode_ctrl(struct lm3560_flash *flash)
+{
+       int rval = -EINVAL;
+
+       switch (flash->led_mode) {
+       case V4L2_FLASH_LED_MODE_NONE:
+               rval = regmap_update_bits(flash->regmap,
+                                         REG_ENABLE, 0x03, MODE_SHDN);
+               break;
+       case V4L2_FLASH_LED_MODE_TORCH:
+               rval = regmap_update_bits(flash->regmap,
+                                         REG_ENABLE, 0x03, MODE_TORCH);
+               break;
+       case V4L2_FLASH_LED_MODE_FLASH:
+               rval = regmap_update_bits(flash->regmap,
+                                         REG_ENABLE, 0x03, MODE_FLASH);
+               break;
+       }
+       return rval;
+}
+
+/* led1/2  enable/disable */
+static int lm3560_enable_ctrl(struct lm3560_flash *flash,
+                             enum lm3560_led_id led_no, bool on)
+{
+       int rval;
+
+       if (led_no == LM3560_LED0) {
+               if (on == true)
+                       rval = regmap_update_bits(flash->regmap,
+                                                 REG_ENABLE, 0x08, 0x08);
+               else
+                       rval = regmap_update_bits(flash->regmap,
+                                                 REG_ENABLE, 0x08, 0x00);
+       } else {
+               if (on == true)
+                       rval = regmap_update_bits(flash->regmap,
+                                                 REG_ENABLE, 0x10, 0x10);
+               else
+                       rval = regmap_update_bits(flash->regmap,
+                                                 REG_ENABLE, 0x10, 0x00);
+       }
+       return rval;
+}
+
+/* torch1/2 brightness control */
+static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash,
+                                enum lm3560_led_id led_no, unsigned int brt)
+{
+       int rval;
+       u8 br_bits;
+
+       if (brt < LM3560_TORCH_BRT_MIN)
+               return lm3560_enable_ctrl(flash, led_no, false);
+       else
+               rval = lm3560_enable_ctrl(flash, led_no, true);
+
+       br_bits = LM3560_TORCH_BRT_uA_TO_REG(brt);
+       if (led_no == LM3560_LED0)
+               rval = regmap_update_bits(flash->regmap,
+                                         REG_TORCH_BR, 0x07, br_bits);
+       else
+               rval = regmap_update_bits(flash->regmap,
+                                         REG_TORCH_BR, 0x38, br_bits << 3);
+
+       return rval;
+}
+
+/* flash1/2 brightness control */
+static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash,
+                                enum lm3560_led_id led_no, unsigned int brt)
+{
+       int rval;
+       u8 br_bits;
+
+       if (brt < LM3560_FLASH_BRT_MIN)
+               return lm3560_enable_ctrl(flash, led_no, false);
+       else
+               rval = lm3560_enable_ctrl(flash, led_no, true);
+
+       br_bits = LM3560_FLASH_BRT_uA_TO_REG(brt);
+       if (led_no == LM3560_LED0)
+               rval = regmap_update_bits(flash->regmap,
+                                         REG_FLASH_BR, 0x0f, br_bits);
+       else
+               rval = regmap_update_bits(flash->regmap,
+                                         REG_FLASH_BR, 0xf0, br_bits << 4);
+
+       return rval;
+}
+
+/* V4L2 controls  */
+static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
+{
+       struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no);
+
+       mutex_lock(&flash->lock);
+
+       if (ctrl->id == V4L2_CID_FLASH_FAULT) {
+               int rval;
+               s32 fault = 0;
+               unsigned int reg_val;
+               rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
+               if (rval < 0)
+                       return rval;
+               if (rval & FAULT_SHORT_CIRCUIT)
+                       fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
+               if (rval & FAULT_OVERTEMP)
+                       fault |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
+               if (rval & FAULT_TIMEOUT)
+                       fault |= V4L2_FLASH_FAULT_TIMEOUT;
+               ctrl->cur.val = fault;
+               return 0;
+       }
+
+       mutex_unlock(&flash->lock);
+       return -EINVAL;
+}
+
+static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
+{
+       struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no);
+       u8 tout_bits;
+       int rval = -EINVAL;
+
+       mutex_lock(&flash->lock);
+
+       switch (ctrl->id) {
+       case V4L2_CID_FLASH_LED_MODE:
+               flash->led_mode = ctrl->val;
+               if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+                       rval = lm3560_mode_ctrl(flash);
+               break;
+
+       case V4L2_CID_FLASH_STROBE_SOURCE:
+               rval = regmap_update_bits(flash->regmap,
+                                         REG_CONFIG1, 0x04, (ctrl->val) << 2);
+               if (rval < 0)
+                       goto err_out;
+               break;
+
+       case V4L2_CID_FLASH_STROBE:
+               if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+                       return -EBUSY;
+               flash->led_mode = V4L2_FLASH_LED_MODE_FLASH;
+               rval = lm3560_mode_ctrl(flash);
+               break;
+
+       case V4L2_CID_FLASH_STROBE_STOP:
+               if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+                       return -EBUSY;
+               flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
+               rval = lm3560_mode_ctrl(flash);
+               break;
+
+       case V4L2_CID_FLASH_TIMEOUT:
+               tout_bits = LM3560_FLASH_TOUT_ms_TO_REG(ctrl->val);
+               rval = regmap_update_bits(flash->regmap,
+                                         REG_FLASH_TOUT, 0x1f, tout_bits);
+               break;
+
+       case V4L2_CID_FLASH_INTENSITY:
+               rval = lm3560_flash_brt_ctrl(flash, led_no, ctrl->val);
+               break;
+
+       case V4L2_CID_FLASH_TORCH_INTENSITY:
+               rval = lm3560_torch_brt_ctrl(flash, led_no, ctrl->val);
+               break;
+       }
+
+       mutex_unlock(&flash->lock);
+err_out:
+       return rval;
+}
+
+static int lm3560_led1_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+       return lm3560_get_ctrl(ctrl, LM3560_LED1);
+}
+
+static int lm3560_led1_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+       return lm3560_set_ctrl(ctrl, LM3560_LED1);
+}
+
+static int lm3560_led0_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+       return lm3560_get_ctrl(ctrl, LM3560_LED0);
+}
+
+static int lm3560_led0_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+       return lm3560_set_ctrl(ctrl, LM3560_LED0);
+}
+
+static const struct v4l2_ctrl_ops lm3560_led_ctrl_ops[LM3560_LED_MAX] = {
+       [LM3560_LED0] = {
+                        .g_volatile_ctrl = lm3560_led0_get_ctrl,
+                        .s_ctrl = lm3560_led0_set_ctrl,
+                        },
+       [LM3560_LED1] = {
+                        .g_volatile_ctrl = lm3560_led1_get_ctrl,
+                        .s_ctrl = lm3560_led1_set_ctrl,
+                        }
+};
+
+static int lm3560_init_controls(struct lm3560_flash *flash,
+                               enum lm3560_led_id led_no)
+{
+       struct v4l2_ctrl *fault;
+       u32 max_flash_brt = flash->pdata->max_flash_brt[led_no];
+       u32 max_torch_brt = flash->pdata->max_torch_brt[led_no];
+       struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no];
+       const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no];
+
+       v4l2_ctrl_handler_init(hdl, 8);
+       /* flash mode */
+       v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
+                              V4L2_FLASH_LED_MODE_TORCH, ~0x7,
+                              V4L2_FLASH_LED_MODE_NONE);
+       flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
+
+       /* flash source */
+       v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE,
+                              0x1, ~0x3, V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
+
+       /* flash strobe */
+       v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+       /* flash strobe stop */
+       v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
+
+       /* flash strobe timeout */
+       v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT,
+                         LM3560_FLASH_TOUT_MIN,
+                         flash->pdata->max_flash_timeout,
+                         LM3560_FLASH_TOUT_STEP,
+                         flash->pdata->max_flash_timeout);
+
+       /* flash brt */
+       v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY,
+                         LM3560_FLASH_BRT_MIN, max_flash_brt,
+                         LM3560_FLASH_BRT_STEP, max_flash_brt);
+
+       /* torch brt */
+       v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY,
+                         LM3560_TORCH_BRT_MIN, max_torch_brt,
+                         LM3560_TORCH_BRT_STEP, max_torch_brt);
+
+       /* fault */
+       fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0,
+                                 V4L2_FLASH_FAULT_OVER_VOLTAGE
+                                 | V4L2_FLASH_FAULT_OVER_TEMPERATURE
+                                 | V4L2_FLASH_FAULT_SHORT_CIRCUIT
+                                 | V4L2_FLASH_FAULT_TIMEOUT, 0, 0);
+       if (fault != NULL)
+               fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+       if (hdl->error)
+               return hdl->error;
+
+       flash->subdev_led[led_no].ctrl_handler = hdl;
+       return 0;
+}
+
+/* initialize device */
+static const struct v4l2_subdev_ops lm3560_ops = {
+       .core = NULL,
+};
+
+static const struct regmap_config lm3560_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = 0xFF,
+};
+
+static int lm3560_subdev_init(struct lm3560_flash *flash,
+                             enum lm3560_led_id led_no, char *led_name)
+{
+       struct i2c_client *client = to_i2c_client(flash->dev);
+       int rval;
+
+       v4l2_i2c_subdev_init(&flash->subdev_led[led_no], client, &lm3560_ops);
+       flash->subdev_led[led_no].flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+       strcpy(flash->subdev_led[led_no].name, led_name);
+       rval = lm3560_init_controls(flash, led_no);
+       if (rval)
+               goto err_out;
+       rval = media_entity_init(&flash->subdev_led[led_no].entity, 0, NULL, 0);
+       if (rval < 0)
+               goto err_out;
+       flash->subdev_led[led_no].entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+
+       return rval;
+
+err_out:
+       v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]);
+       return rval;
+}
+
+static int lm3560_init_device(struct lm3560_flash *flash)
+{
+       int rval;
+       unsigned int reg_val;
+
+       /* set peak current */
+       rval = regmap_update_bits(flash->regmap,
+                                 REG_FLASH_TOUT, 0x60, flash->pdata->peak);
+       if (rval < 0)
+               return rval;
+       /* output disable */
+       flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
+       rval = lm3560_mode_ctrl(flash);
+       if (rval < 0)
+               return rval;
+       /* Reset faults */
+       rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
+       return rval;
+}
+
+static int lm3560_probe(struct i2c_client *client,
+                       const struct i2c_device_id *devid)
+{
+       struct lm3560_flash *flash;
+       struct lm3560_platform_data *pdata = dev_get_platdata(&client->dev);
+       int rval;
+
+       flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
+       if (flash == NULL)
+               return -ENOMEM;
+
+       flash->regmap = devm_regmap_init_i2c(client, &lm3560_regmap);
+       if (IS_ERR(flash->regmap)) {
+               rval = PTR_ERR(flash->regmap);
+               return rval;
+       }
+
+       /* if there is no platform data, use chip default value */
+       if (pdata == NULL) {
+               pdata =
+                   kzalloc(sizeof(struct lm3560_platform_data), GFP_KERNEL);
+               if (pdata == NULL)
+                       return -ENODEV;
+               pdata->peak = LM3560_PEAK_3600mA;
+               pdata->max_flash_timeout = LM3560_FLASH_TOUT_MAX;
+               /* led 1 */
+               pdata->max_flash_brt[LM3560_LED0] = LM3560_FLASH_BRT_MAX;
+               pdata->max_torch_brt[LM3560_LED0] = LM3560_TORCH_BRT_MAX;
+               /* led 2 */
+               pdata->max_flash_brt[LM3560_LED1] = LM3560_FLASH_BRT_MAX;
+               pdata->max_torch_brt[LM3560_LED1] = LM3560_TORCH_BRT_MAX;
+       }
+       flash->pdata = pdata;
+       flash->dev = &client->dev;
+       mutex_init(&flash->lock);
+
+       rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0");
+       if (rval < 0)
+               return rval;
+
+       rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1");
+       if (rval < 0)
+               return rval;
+
+       rval = lm3560_init_device(flash);
+       if (rval < 0)
+               return rval;
+
+       return 0;
+}
+
+static int lm3560_remove(struct i2c_client *client)
+{
+       struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+       struct lm3560_flash *flash = container_of(subdev, struct lm3560_flash,
+                                                 subdev_led[LM3560_LED_MAX]);
+       unsigned int i;
+
+       for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) {
+               v4l2_device_unregister_subdev(&flash->subdev_led[i]);
+               v4l2_ctrl_handler_free(&flash->ctrls_led[i]);
+               media_entity_cleanup(&flash->subdev_led[i].entity);
+       }
+
+       return 0;
+}
+
+static const struct i2c_device_id lm3560_id_table[] = {
+       {LM3560_NAME, 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lm3560_id_table);
+
+static struct i2c_driver lm3560_i2c_driver = {
+       .driver = {
+                  .name = LM3560_NAME,
+                  .pm = NULL,
+                  },
+       .probe = lm3560_probe,
+       .remove = lm3560_remove,
+       .id_table = lm3560_id_table,
+};
+
+module_i2c_driver(lm3560_i2c_driver);
+
+MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
+MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
+MODULE_DESCRIPTION("Texas Instruments LM3560 LED flash driver");
+MODULE_LICENSE("GPL");
index b76ec0e..6fec938 100644 (file)
@@ -1581,7 +1581,7 @@ static int s5c73m3_probe(struct i2c_client *client,
        oif_sd = &state->oif_sd;
 
        v4l2_subdev_init(sd, &s5c73m3_subdev_ops);
-       sd->owner = client->driver->driver.owner;
+       sd->owner = client->dev.driver->owner;
        v4l2_set_subdevdata(sd, state);
        strlcpy(sd->name, "S5C73M3", sizeof(sd->name));
 
@@ -1651,7 +1651,7 @@ static int s5c73m3_probe(struct i2c_client *client,
        if (ret < 0)
                goto out_err;
 
-       v4l2_info(sd, "%s: completed succesfully\n", __func__);
+       v4l2_info(sd, "%s: completed successfully\n", __func__);
        return 0;
 
 out_err:
index 1d384a3..5b91593 100644 (file)
@@ -451,7 +451,9 @@ static int imx074_probe(struct i2c_client *client,
        if (ret < 0)
                goto eprobe;
 
-       return v4l2_async_register_subdev(&priv->subdev);
+       ret = v4l2_async_register_subdev(&priv->subdev);
+       if (!ret)
+               return 0;
 
 epwrinit:
 eprobe:
index e968c3f..bc74224 100644 (file)
@@ -371,7 +371,7 @@ static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code,
                alt->com13      = OV9640_COM13_RGB_AVG;
                alt->com15      = OV9640_COM15_RGB_565;
                break;
-       };
+       }
 }
 
 /* Setup registers according to resolution and color encoding */
index d9f65d7..04139ee 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/v4l2-dv-timings.h>
 
 #include <media/v4l2-dv-timings.h>
index 91f3dd4..83d85df 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/videodev2.h>
 #include <linux/module.h>
 #include <linux/v4l2-mediabus.h>
+#include <linux/of.h>
 
 #include <media/v4l2-async.h>
 #include <media/v4l2-device.h>
index 24a08fa..912e1cc 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/v4l2-dv-timings.h>
 #include <media/tvp7002.h>
 #include <media/v4l2-async.h>
index 447afbd..8b5e0b3 100644 (file)
@@ -319,7 +319,6 @@ static int flexcop_pci_init(struct flexcop_pci *fc_pci)
 
 err_pci_iounmap:
        pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
-       pci_set_drvdata(fc_pci->pdev, NULL);
 err_pci_release_regions:
        pci_release_regions(fc_pci->pdev);
 err_pci_disable_device:
@@ -332,7 +331,6 @@ static void flexcop_pci_exit(struct flexcop_pci *fc_pci)
        if (fc_pci->init_state & FC_PCI_INIT) {
                free_irq(fc_pci->pdev->irq, fc_pci);
                pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
-               pci_set_drvdata(fc_pci->pdev, NULL);
                pci_release_regions(fc_pci->pdev);
                pci_disable_device(fc_pci->pdev);
        }
index 66eb0ba..d0c281f 100644 (file)
@@ -488,8 +488,7 @@ static int bt878_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
        btwrite(0, BT848_INT_MASK);
 
        result = request_irq(bt->irq, bt878_irq,
-                            IRQF_SHARED | IRQF_DISABLED, "bt878",
-                            (void *) bt);
+                            IRQF_SHARED, "bt878", (void *) bt);
        if (result == -EINVAL) {
                printk(KERN_ERR "bt878(%d): Bad irq number or handler\n",
                       bt878_num);
@@ -563,7 +562,6 @@ static void bt878_remove(struct pci_dev *pci_dev)
        bt->shutdown = 1;
        bt878_mem_free(bt);
 
-       pci_set_drvdata(pci_dev, NULL);
        pci_disable_device(pci_dev);
        return;
 }
index c6532de..a3b1ee9 100644 (file)
@@ -4086,7 +4086,7 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
        /* disable irqs, register irq handler */
        btwrite(0, BT848_INT_MASK);
        result = request_irq(btv->c.pci->irq, bttv_irq,
-           IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
+           IRQF_SHARED, btv->c.v4l2_dev.name, (void *)btv);
        if (result < 0) {
                pr_err("%d: can't get IRQ %d\n",
                       bttv_num, btv->c.pci->irq);
index 004d8ac..c1f8cc6 100644 (file)
@@ -324,23 +324,24 @@ static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len)
 /* Hauppauge card? get values from tveeprom */
 void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
 {
-       struct i2c_client c;
+       struct i2c_client *c;
        u8 eedata[256];
 
-       memset(&c, 0, sizeof(c));
-       strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
-       c.adapter = &cx->i2c_adap[0];
-       c.addr = 0xA0 >> 1;
+       c = kzalloc(sizeof(*c), GFP_KERNEL);
+
+       strlcpy(c->name, "cx18 tveeprom tmp", sizeof(c->name));
+       c->adapter = &cx->i2c_adap[0];
+       c->addr = 0xa0 >> 1;
 
        memset(tv, 0, sizeof(*tv));
-       if (tveeprom_read(&c, eedata, sizeof(eedata)))
-               return;
+       if (tveeprom_read(c, eedata, sizeof(eedata)))
+               goto ret;
 
        switch (cx->card->type) {
        case CX18_CARD_HVR_1600_ESMT:
        case CX18_CARD_HVR_1600_SAMSUNG:
        case CX18_CARD_HVR_1600_S5H1411:
-               tveeprom_hauppauge_analog(&c, tv, eedata);
+               tveeprom_hauppauge_analog(c, tv, eedata);
                break;
        case CX18_CARD_YUAN_MPC718:
        case CX18_CARD_GOTVIEW_PCI_DVD3:
@@ -354,6 +355,9 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
                cx18_eeprom_dump(cx, eedata, sizeof(eedata));
                break;
        }
+
+ret:
+       kfree(c);
 }
 
 static void cx18_process_eeprom(struct cx18 *cx)
@@ -1031,8 +1035,7 @@ static int cx18_probe(struct pci_dev *pci_dev,
 
        /* Register IRQ */
        retval = request_irq(cx->pci_dev->irq, cx18_irq_handler,
-                            IRQF_SHARED | IRQF_DISABLED,
-                            cx->v4l2_dev.name, (void *)cx);
+                            IRQF_SHARED, cx->v4l2_dev.name, (void *)cx);
        if (retval) {
                CX18_ERR("Failed to register irq %d\n", retval);
                goto free_i2c;
index 5104c80..d1dcb1d 100644 (file)
@@ -23,6 +23,7 @@ config VIDEO_CX23885
        select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+       select DVB_CX24117 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT
index 7344849..16fa7ea 100644 (file)
 #include "cx23885.h"
 #include "cimax2.h"
 #include "dvb_ca_en50221.h"
+
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 /**** Bit definitions for MC417_RWD and MC417_OEN registers  ***
   bits 31-16
 +-----------+
@@ -125,7 +129,7 @@ static int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
                                                u8 *buf, int len)
 {
        int ret;
-       u8 buffer[len + 1];
+       u8 buffer[MAX_XFER_SIZE];
 
        struct i2c_msg msg = {
                .addr   = addr,
@@ -134,6 +138,13 @@ static int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
                .len    = len + 1
        };
 
+       if (1 + len > sizeof(buffer)) {
+               printk(KERN_WARNING
+                      "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                      KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        buffer[0] = reg;
        memcpy(&buffer[1], buf, len);
 
index 6a71a96..79f20c8 100644 (file)
@@ -223,6 +223,39 @@ struct cx23885_board cx23885_boards[] = {
                .name           = "Leadtek Winfast PxDVR3200 H",
                .portc          = CX23885_MPEG_DVB,
        },
+       [CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200] = {
+               .name           = "Leadtek Winfast PxPVR2200",
+               .porta          = CX23885_ANALOG_VIDEO,
+               .tuner_type     = TUNER_XC2028,
+               .tuner_addr     = 0x61,
+               .tuner_bus      = 1,
+               .input          = {{
+                       .type   = CX23885_VMUX_TELEVISION,
+                       .vmux   = CX25840_VIN2_CH1 |
+                                 CX25840_VIN5_CH2,
+                       .amux   = CX25840_AUDIO8,
+                       .gpio0  = 0x704040,
+               }, {
+                       .type   = CX23885_VMUX_COMPOSITE1,
+                       .vmux   = CX25840_COMPOSITE1,
+                       .amux   = CX25840_AUDIO7,
+                       .gpio0  = 0x704040,
+               }, {
+                       .type   = CX23885_VMUX_SVIDEO,
+                       .vmux   = CX25840_SVIDEO_LUMA3 |
+                                 CX25840_SVIDEO_CHROMA4,
+                       .amux   = CX25840_AUDIO7,
+                       .gpio0  = 0x704040,
+               }, {
+                       .type   = CX23885_VMUX_COMPONENT,
+                       .vmux   = CX25840_VIN7_CH1 |
+                                 CX25840_VIN6_CH2 |
+                                 CX25840_VIN8_CH3 |
+                                 CX25840_COMPONENT_ON,
+                       .amux   = CX25840_AUDIO7,
+                       .gpio0  = 0x704040,
+               } },
+       },
        [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000] = {
                .name           = "Leadtek Winfast PxDVR3200 H XC4000",
                .porta          = CX23885_ANALOG_VIDEO,
@@ -259,6 +292,16 @@ struct cx23885_board cx23885_boards[] = {
                .name           = "TurboSight TBS 6920",
                .portb          = CX23885_MPEG_DVB,
        },
+       [CX23885_BOARD_TBS_6980] = {
+               .name           = "TurboSight TBS 6980",
+               .portb          = CX23885_MPEG_DVB,
+               .portc          = CX23885_MPEG_DVB,
+       },
+       [CX23885_BOARD_TBS_6981] = {
+               .name           = "TurboSight TBS 6981",
+               .portb          = CX23885_MPEG_DVB,
+               .portc          = CX23885_MPEG_DVB,
+       },
        [CX23885_BOARD_TEVII_S470] = {
                .name           = "TeVii S470",
                .portb          = CX23885_MPEG_DVB,
@@ -688,6 +731,10 @@ struct cx23885_subid cx23885_subids[] = {
                .card      = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
        }, {
                .subvendor = 0x107d,
+               .subdevice = 0x6f21,
+               .card      = CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200,
+       }, {
+               .subvendor = 0x107d,
                .subdevice = 0x6f39,
                .card      = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000,
        }, {
@@ -699,6 +746,14 @@ struct cx23885_subid cx23885_subids[] = {
                .subdevice = 0x8888,
                .card      = CX23885_BOARD_TBS_6920,
        }, {
+               .subvendor = 0x6980,
+               .subdevice = 0x8888,
+               .card      = CX23885_BOARD_TBS_6980,
+       }, {
+               .subvendor = 0x6981,
+               .subdevice = 0x8888,
+               .card      = CX23885_BOARD_TBS_6981,
+       }, {
                .subvendor = 0xd470,
                .subdevice = 0x9022,
                .card      = CX23885_BOARD_TEVII_S470,
@@ -1023,6 +1078,35 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
                        dev->name, tv.model);
 }
 
+/* Some TBS cards require initing a chip using a bitbanged SPI attached
+   to the cx23885 gpio's. If this chip doesn't get init'ed the demod
+   doesn't respond to any command. */
+static void tbs_card_init(struct cx23885_dev *dev)
+{
+       int i;
+       const u8 buf[] = {
+               0xe0, 0x06, 0x66, 0x33, 0x65,
+               0x01, 0x17, 0x06, 0xde};
+
+       switch (dev->board) {
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
+               cx_set(GP0_IO, 0x00070007);
+               usleep_range(1000, 10000);
+               cx_clear(GP0_IO, 2);
+               usleep_range(1000, 10000);
+               for (i = 0; i < 9 * 8; i++) {
+                       cx_clear(GP0_IO, 7);
+                       usleep_range(1000, 10000);
+                       cx_set(GP0_IO,
+                               ((buf[i >> 3] >> (7 - (i & 7))) & 1) | 4);
+                       usleep_range(1000, 10000);
+               }
+               cx_set(GP0_IO, 7);
+               break;
+       }
+}
+
 int cx23885_tuner_callback(void *priv, int component, int command, int arg)
 {
        struct cx23885_tsport *port = priv;
@@ -1043,6 +1127,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg)
        case CX23885_BOARD_HAUPPAUGE_HVR1500:
        case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
        case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+       case CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200:
        case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
        case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
        case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
@@ -1208,6 +1293,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
                cx_set(GP0_IO, 0x000f000f);
                break;
        case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+       case CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200:
        case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
        case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
        case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
@@ -1225,6 +1311,8 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
                cx_set(GP0_IO, 0x00040004);
                break;
        case CX23885_BOARD_TBS_6920:
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
        case CX23885_BOARD_PROF_8000:
                cx_write(MC417_CTL, 0x00000036);
                cx_write(MC417_OEN, 0x00001000);
@@ -1473,6 +1561,8 @@ int cx23885_ir_init(struct cx23885_dev *dev)
        case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
        case CX23885_BOARD_TEVII_S470:
        case CX23885_BOARD_MYGICA_X8507:
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
                if (!enable_885_ir)
                        break;
                dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
@@ -1516,6 +1606,8 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
        case CX23885_BOARD_TEVII_S470:
        case CX23885_BOARD_HAUPPAUGE_HVR1250:
        case CX23885_BOARD_MYGICA_X8507:
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
                cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
                /* sd_ir is a duplicate pointer to the AV Core, just clear it */
                dev->sd_ir = NULL;
@@ -1561,6 +1653,8 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
        case CX23885_BOARD_TEVII_S470:
        case CX23885_BOARD_HAUPPAUGE_HVR1250:
        case CX23885_BOARD_MYGICA_X8507:
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
                if (dev->sd_ir)
                        cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
                break;
@@ -1676,6 +1770,16 @@ void cx23885_card_setup(struct cx23885_dev *dev)
                ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
                ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
                break;
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
+               ts1->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+               ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+               ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+               ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+               ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+               ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+               tbs_card_init(dev);
+               break;
        case CX23885_BOARD_MYGICA_X8506:
        case CX23885_BOARD_MAGICPRO_PROHDTVE2:
        case CX23885_BOARD_MYGICA_X8507:
@@ -1704,6 +1808,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        case CX23885_BOARD_HAUPPAUGE_HVR1700:
        case CX23885_BOARD_HAUPPAUGE_HVR1400:
        case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+       case CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200:
        case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
        case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
        case CX23885_BOARD_HAUPPAUGE_HVR1270:
@@ -1733,6 +1838,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
        case CX23885_BOARD_HAUPPAUGE_HVR1700:
        case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+       case CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200:
        case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
        case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
        case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
@@ -1752,6 +1858,8 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        case CX23885_BOARD_MYGICA_X8507:
        case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
        case CX23885_BOARD_AVERMEDIA_HC81R:
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
                dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
                                &dev->i2c_bus[2].i2c_adap,
                                "cx25840", 0x88 >> 1, NULL);
index 9f63d93..edcd79d 100644 (file)
@@ -2129,7 +2129,7 @@ static int cx23885_initdev(struct pci_dev *pci_dev,
        }
 
        err = request_irq(pci_dev->irq, cx23885_irq,
-                         IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+                         IRQF_SHARED, dev->name, dev);
        if (err < 0) {
                printk(KERN_ERR "%s: can't get IRQ %d\n",
                       dev->name, pci_dev->irq);
index 971e4ff..0549205 100644 (file)
@@ -51,6 +51,7 @@
 #include "stv6110.h"
 #include "lnbh24.h"
 #include "cx24116.h"
+#include "cx24117.h"
 #include "cimax2.h"
 #include "lgs8gxx.h"
 #include "netup-eeprom.h"
@@ -461,6 +462,10 @@ static struct cx24116_config tbs_cx24116_config = {
        .demod_address = 0x55,
 };
 
+static struct cx24117_config tbs_cx24117_config = {
+       .demod_address = 0x55,
+};
+
 static struct ds3000_config tevii_ds3000_config = {
        .demod_address = 0x68,
 };
@@ -1044,6 +1049,25 @@ static int dvb_register(struct cx23885_tsport *port)
                        fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
 
                break;
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
+               i2c_bus = &dev->i2c_bus[1];
+
+               switch (port->nr) {
+               /* PORT B */
+               case 1:
+                       fe0->dvb.frontend = dvb_attach(cx24117_attach,
+                                       &tbs_cx24117_config,
+                                       &i2c_bus->i2c_adap);
+                       break;
+               /* PORT C */
+               case 2:
+                       fe0->dvb.frontend = dvb_attach(cx24117_attach,
+                                       &tbs_cx24117_config,
+                                       &i2c_bus->i2c_adap);
+                       break;
+               }
+               break;
        case CX23885_BOARD_TEVII_S470:
                i2c_bus = &dev->i2c_bus[1];
 
index 7875dfb..8a49e7c 100644 (file)
@@ -90,6 +90,8 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
        case CX23885_BOARD_TEVII_S470:
        case CX23885_BOARD_HAUPPAUGE_HVR1250:
        case CX23885_BOARD_MYGICA_X8507:
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
                /*
                 * The only boards we handle right now.  However other boards
                 * using the CX2388x integrated IR controller should be similar
@@ -168,6 +170,8 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
                break;
        case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
        case CX23885_BOARD_TEVII_S470:
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
                /*
                 * The IR controller on this board only returns pulse widths.
                 * Any other mode setting will fail to set up the device.
@@ -298,6 +302,14 @@ int cx23885_input_init(struct cx23885_dev *dev)
                /* A guess at the remote */
                rc_map = RC_MAP_TOTAL_MEDIA_IN_HAND_02;
                break;
+       case CX23885_BOARD_TBS_6980:
+       case CX23885_BOARD_TBS_6981:
+               /* Integrated CX23885 IR controller */
+               driver_type = RC_DRIVER_IR_RAW;
+               allowed_protos = RC_BIT_ALL;
+               /* A guess at the remote */
+               rc_map = RC_MAP_TBS_NEC;
+               break;
        default:
                return -ENODEV;
        }
index 1616868..7891f34 100644 (file)
@@ -1865,7 +1865,8 @@ int cx23885_video_register(struct cx23885_dev *dev)
 
                        v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup);
 
-                       if (dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXTV1200) {
+                       if ((dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXTV1200) ||
+                           (dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200)) {
                                struct xc2028_ctrl ctrl = {
                                        .fname = XC2028_DEFAULT_FIRMWARE,
                                        .max_len = 64
index 038caf5..0fa4048 100644 (file)
@@ -93,6 +93,9 @@
 #define CX23885_BOARD_PROF_8000                37
 #define CX23885_BOARD_HAUPPAUGE_HVR4400        38
 #define CX23885_BOARD_AVERMEDIA_HC81R          39
+#define CX23885_BOARD_TBS_6981                 40
+#define CX23885_BOARD_TBS_6980                 41
+#define CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200 42
 
 #define GPIO_0 0x00000001
 #define GPIO_1 0x00000002
index 3b409fe..f2ebc98 100644 (file)
@@ -45,5 +45,3 @@ struct cx25821_board cx25821_boards[] = {
        },
 
 };
-
-const unsigned int cx25821_bcount = ARRAY_SIZE(cx25821_boards);
index 22fa044..43bdfa4 100644 (file)
@@ -438,7 +438,7 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width,
                decoder_count = decoder_select + 1;
        } else {
                decoder = 0;
-               decoder_count = _num_decoders;
+               decoder_count = dev->_max_num_decoders;
        }
 
        switch (width) {
@@ -506,8 +506,6 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder,
                break;
        }
 
-       _display_field_cnt[decoder] = duration;
-
        /* update hardware */
        fld_cnt = cx25821_i2c_read(&dev->i2c_bus[0], disp_cnt_reg, &tmp);
 
@@ -667,8 +665,6 @@ int medusa_video_init(struct cx25821_dev *dev)
        int ret_val = 0;
        int i = 0;
 
-       _num_decoders = dev->_max_num_decoders;
-
        /* disable Auto source selection on all video decoders */
        value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp);
        value &= 0xFFFFF0FF;
@@ -685,8 +681,14 @@ int medusa_video_init(struct cx25821_dev *dev)
        if (ret_val < 0)
                goto error;
 
-       for (i = 0; i < _num_decoders; i++)
-               medusa_set_decoderduration(dev, i, _display_field_cnt[i]);
+       /*
+        * FIXME: due to a coding bug the duration was always 0. It's
+        * likely that it really should be something else, but due to the
+        * lack of documentation I have no idea what it should be. For
+        * now just fill in 0 as the duration.
+        */
+       for (i = 0; i < dev->_max_num_decoders; i++)
+               medusa_set_decoderduration(dev, i, 0);
 
        /* Select monitor as DENC A input, power up the DAC */
        value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_AB_CTRL, &tmp);
@@ -717,7 +719,7 @@ int medusa_video_init(struct cx25821_dev *dev)
        /* Turn on all of the data out and control output pins. */
        value = cx25821_i2c_read(&dev->i2c_bus[0], PIN_OE_CTRL, &tmp);
        value &= 0xFEF0FE00;
-       if (_num_decoders == MAX_DECODERS) {
+       if (dev->_max_num_decoders == MAX_DECODERS) {
                /*
                 * Note: The octal board does not support control pins(bit16-19)
                 * These bits are ignored in the octal board.
index 6175e09..8bf602f 100644 (file)
 #define CONTRAST_DEFAULT                5000
 #define HUE_DEFAULT                     5000
 
-unsigned short _num_decoders;
-unsigned short _num_cameras;
-
-unsigned int _video_standard;
-int _display_field_cnt[MAX_DECODERS];
-
 #endif
index 88ffef4..1f43be0 100644 (file)
@@ -159,10 +159,10 @@ static __le32 *cx25821_risc_field_upstream(struct cx25821_channel *chan, __le32
                 * For the upstream video channel, the risc engine will enable
                 * the FIFO. */
                if (fifo_enable && line == 3) {
-                       *(rp++) = RISC_WRITECR;
-                       *(rp++) = sram_ch->dma_ctl;
-                       *(rp++) = FLD_VID_FIFO_EN;
-                       *(rp++) = 0x00000001;
+                       *(rp++) = cpu_to_le32(RISC_WRITECR);
+                       *(rp++) = cpu_to_le32(sram_ch->dma_ctl);
+                       *(rp++) = cpu_to_le32(FLD_VID_FIFO_EN);
+                       *(rp++) = cpu_to_le32(0x00000001);
                }
        }
 
index aba5b1c..400eb1c 100644 (file)
@@ -834,7 +834,7 @@ static int snd_cx88_create(struct snd_card *card, struct pci_dev *pci,
 
        /* get irq */
        err = request_irq(chip->pci->irq, cx8801_irq,
-                         IRQF_SHARED | IRQF_DISABLED, chip->core->name, chip);
+                         IRQF_SHARED, chip->core->name, chip);
        if (err < 0) {
                dprintk(0, "%s: can't get IRQ %d\n",
                       chip->core->name, chip->pci->irq);
@@ -935,8 +935,6 @@ static void cx88_audio_finidev(struct pci_dev *pci)
 
        snd_card_free((void *)card);
 
-       pci_set_drvdata(pci, NULL);
-
        devno--;
 }
 
@@ -951,27 +949,4 @@ static struct pci_driver cx88_audio_pci_driver = {
        .remove   = cx88_audio_finidev,
 };
 
-/****************************************************************************
-                               LINUX MODULE INIT
- ****************************************************************************/
-
-/*
- * module init
- */
-static int __init cx88_audio_init(void)
-{
-       printk(KERN_INFO "cx2388x alsa driver version %s loaded\n",
-              CX88_VERSION);
-       return pci_register_driver(&cx88_audio_pci_driver);
-}
-
-/*
- * module remove
- */
-static void __exit cx88_audio_fini(void)
-{
-       pci_unregister_driver(&cx88_audio_pci_driver);
-}
-
-module_init(cx88_audio_init);
-module_exit(cx88_audio_fini);
+module_pci_driver(cx88_audio_pci_driver);
index 2d3507e..74b7b86 100644 (file)
@@ -499,7 +499,7 @@ static int cx8802_init_common(struct cx8802_dev *dev)
 
        /* get irq */
        err = request_irq(dev->pci->irq, cx8802_irq,
-                         IRQF_SHARED | IRQF_DISABLED, dev->core->name, dev);
+                         IRQF_SHARED, dev->core->name, dev);
        if (err < 0) {
                printk(KERN_ERR "%s: can't get IRQ %d\n",
                       dev->core->name, dev->pci->irq);
@@ -520,7 +520,6 @@ static void cx8802_fini_common(struct cx8802_dev *dev)
 
        /* unregister stuff */
        free_irq(dev->pci->irq, dev);
-       pci_set_drvdata(dev->pci, NULL);
 
        /* free memory */
        btcx_riscmem_free(dev->pci,&dev->mpegq.stopper);
@@ -903,20 +902,8 @@ static struct pci_driver cx8802_pci_driver = {
        .remove   = cx8802_remove,
 };
 
-static int __init cx8802_init(void)
-{
-       printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %s loaded\n",
-              CX88_VERSION);
-       return pci_register_driver(&cx8802_pci_driver);
-}
-
-static void __exit cx8802_fini(void)
-{
-       pci_unregister_driver(&cx8802_pci_driver);
-}
+module_pci_driver(cx8802_pci_driver);
 
-module_init(cx8802_init);
-module_exit(cx8802_fini);
 EXPORT_SYMBOL(cx8802_buf_prepare);
 EXPORT_SYMBOL(cx8802_buf_queue);
 EXPORT_SYMBOL(cx8802_cancel_buffers);
index ecf21d9..ed8cb90 100644 (file)
@@ -1738,7 +1738,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
 
        /* get irq */
        err = request_irq(pci_dev->irq, cx8800_irq,
-                         IRQF_SHARED | IRQF_DISABLED, core->name, dev);
+                         IRQF_SHARED, core->name, dev);
        if (err < 0) {
                printk(KERN_ERR "%s/0: can't get IRQ %d\n",
                       core->name,pci_dev->irq);
@@ -1922,7 +1922,6 @@ static void cx8800_finidev(struct pci_dev *pci_dev)
 
        free_irq(pci_dev->irq, dev);
        cx8800_unregister_video(dev);
-       pci_set_drvdata(pci_dev, NULL);
 
        /* free memory */
        btcx_riscmem_free(dev->pci,&dev->vidq.stopper);
@@ -2039,17 +2038,4 @@ static struct pci_driver cx8800_pci_driver = {
 #endif
 };
 
-static int __init cx8800_init(void)
-{
-       printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %s loaded\n",
-              CX88_VERSION);
-       return pci_register_driver(&cx8800_pci_driver);
-}
-
-static void __exit cx8800_fini(void)
-{
-       pci_unregister_driver(&cx8800_pci_driver);
-}
-
-module_init(cx8800_init);
-module_exit(cx8800_fini);
+module_pci_driver(cx8800_pci_driver);
index 36e3452..9375f30 100644 (file)
@@ -1544,7 +1544,7 @@ static void ddb_unmap(struct ddb *dev)
 
 static void ddb_remove(struct pci_dev *pdev)
 {
-       struct ddb *dev = (struct ddb *) pci_get_drvdata(pdev);
+       struct ddb *dev = pci_get_drvdata(pdev);
 
        ddb_ports_detach(dev);
        ddb_i2c_release(dev);
index ab797fe..e60ac35 100644 (file)
@@ -1178,7 +1178,6 @@ err_pci_release_regions:
 err_pci_disable_device:
        pci_disable_device(pdev);
 err_kfree:
-       pci_set_drvdata(pdev, NULL);
        kfree(dev);
        return ret;
 }
@@ -1202,8 +1201,7 @@ static void dm1105_remove(struct pci_dev *pdev)
        dvb_dmxdev_release(&dev->dmxdev);
        dvb_dmx_release(dvbdemux);
        dvb_unregister_adapter(dvb_adapter);
-       if (&dev->i2c_adap)
-               i2c_del_adapter(&dev->i2c_adap);
+       i2c_del_adapter(&dev->i2c_adap);
 
        dm1105_hw_exit(dev);
        synchronize_irq(pdev->irq);
@@ -1211,7 +1209,6 @@ static void dm1105_remove(struct pci_dev *pdev)
        pci_iounmap(pdev, dev->io_mem);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        dm1105_devcount--;
        kfree(dev);
 }
index c08ae3e..802642d 100644 (file)
@@ -1261,7 +1261,7 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
 
        /* Register IRQ */
        retval = request_irq(itv->pdev->irq, ivtv_irq_handler,
-            IRQF_SHARED | IRQF_DISABLED, itv->v4l2_dev.name, (void *)itv);
+            IRQF_SHARED, itv->v4l2_dev.name, (void *)itv);
        if (retval) {
                IVTV_ERR("Failed to register irq %d\n", retval);
                goto free_i2c;
index a846036..9e89e04 100644 (file)
@@ -143,7 +143,6 @@ fail1:
 
 fail0:
        dprintk(MANTIS_ERROR, 1, "ERROR: <%d> exiting", ret);
-       pci_set_drvdata(pdev, NULL);
        return ret;
 }
 EXPORT_SYMBOL_GPL(mantis_pci_init);
@@ -161,7 +160,6 @@ void mantis_pci_exit(struct mantis_pci *mantis)
        }
 
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 EXPORT_SYMBOL_GPL(mantis_pci_exit);
 
index 2381b05..54d5c82 100644 (file)
@@ -1698,7 +1698,7 @@ static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
 
        meye.mchip_irq = pcidev->irq;
        if (request_irq(meye.mchip_irq, meye_irq,
-                       IRQF_DISABLED | IRQF_SHARED, "meye", meye_irq)) {
+                       IRQF_SHARED, "meye", meye_irq)) {
                v4l2_err(v4l2_dev, "request_irq failed\n");
                goto outreqirq;
        }
index 37ebc42..970e833 100644 (file)
@@ -1622,7 +1622,7 @@ static void ngene_unlink(struct ngene *dev)
 
 void ngene_shutdown(struct pci_dev *pdev)
 {
-       struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev);
+       struct ngene *dev = pci_get_drvdata(pdev);
 
        if (!dev || !shutdown_workaround)
                return;
@@ -1648,7 +1648,6 @@ void ngene_remove(struct pci_dev *pdev)
                cxd_detach(dev);
        ngene_stop(dev);
        ngene_release_buffers(dev);
-       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
 }
 
@@ -1702,6 +1701,5 @@ fail1:
        ngene_release_buffers(dev);
 fail0:
        pci_disable_device(pci_dev);
-       pci_set_drvdata(pci_dev, NULL);
        return stat;
 }
index 4938285..8164d74 100644 (file)
@@ -736,7 +736,6 @@ err_pci_release_regions:
 err_pci_disable_device:
        pci_disable_device(pdev);
 err_kfree:
-       pci_set_drvdata(pdev, NULL);
        kfree(pluto);
        goto out;
 }
@@ -765,7 +764,6 @@ static void pluto2_remove(struct pci_dev *pdev)
        pci_iounmap(pdev, pluto->io_mem);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        kfree(pluto);
 }
 
index 75ce142..db887b0 100644 (file)
@@ -1076,7 +1076,6 @@ static void pt1_remove(struct pci_dev *pdev)
        pt1_update_power(pt1);
        pt1_cleanup_adapters(pt1);
        i2c_del_adapter(&pt1->i2c_adap);
-       pci_set_drvdata(pdev, NULL);
        kfree(pt1);
        pci_iounmap(pdev, regs);
        pci_release_regions(pdev);
@@ -1198,7 +1197,6 @@ err_i2c_del_adapter:
 err_pt1_cleanup_adapters:
        pt1_cleanup_adapters(pt1);
 err_kfree:
-       pci_set_drvdata(pdev, NULL);
        kfree(pt1);
 err_pci_iounmap:
        pci_iounmap(pdev, regs);
index dbcdfbf..dd67c8a 100644 (file)
@@ -1096,7 +1096,7 @@ static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum)
 
 
        err = request_irq(dev->pci->irq, saa7134_alsa_irq,
-                               IRQF_SHARED | IRQF_DISABLED, dev->name,
+                               IRQF_SHARED, dev->name,
                                (void*) &dev->dmasound);
 
        if (err < 0) {
index 45f0aca..27d7ee7 100644 (file)
@@ -992,7 +992,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
 
        /* get irq */
        err = request_irq(pci_dev->irq, saa7134_irq,
-                         IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+                         IRQF_SHARED, dev->name, dev);
        if (err < 0) {
                printk(KERN_ERR "%s: can't get IRQ %d\n",
                       dev->name,pci_dev->irq);
index d37ee37..57ef545 100644 (file)
@@ -1232,7 +1232,7 @@ static int saa7164_initdev(struct pci_dev *pci_dev,
        }
 
        err = request_irq(pci_dev->irq, saa7164_irq,
-               IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+               IRQF_SHARED, dev->name, dev);
        if (err < 0) {
                printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name,
                        pci_dev->irq);
@@ -1439,7 +1439,6 @@ static void saa7164_finidev(struct pci_dev *pci_dev)
 
        /* unregister stuff */
        free_irq(pci_dev->irq, dev);
-       pci_set_drvdata(pci_dev, NULL);
 
        mutex_lock(&devlist);
        list_del(&dev->devlist);
index f1cbfe5..6299d5d 100644 (file)
@@ -22,7 +22,7 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
  *
- * the project's page is at http://www.linuxtv.org/ 
+ * the project's page is at http://www.linuxtv.org/
  */
 
 /* for debugging ARM communication: */
 
 #define _NOHANDSHAKE
 
+/*
+ * Max transfer size done by av7110_fw_cmd()
+ *
+ * The maximum size passed to this function is 6 bytes. The buffer also
+ * uses two additional ones for type and size. So, 8 bytes is enough.
+ */
+#define MAX_XFER_SIZE  8
+
 /****************************************************************************
  * DEBI functions
  ****************************************************************************/
@@ -488,11 +496,18 @@ static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
 int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)
 {
        va_list args;
-       u16 buf[num + 2];
+       u16 buf[MAX_XFER_SIZE];
        int i, ret;
 
 //     dprintk(4, "%p\n", av7110);
 
+       if (2 + num > sizeof(buf)) {
+               printk(KERN_WARNING
+                      "%s: %s len=%d is too big!\n",
+                      KBUILD_MODNAME, __func__, num);
+               return -EINVAL;
+       }
+
        buf[0] = ((type << 8) | com);
        buf[1] = num;
 
index 26ca870..39ec35b 100644 (file)
@@ -1,6 +1,7 @@
 config VIDEO_ZORAN
        tristate "Zoran ZR36057/36067 Video For Linux"
        depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 && VIRT_TO_BUS
+       depends on !ALPHA
        help
          Say Y for support for MJPEG capture cards based on the Zoran
          36057/36067 PCI controller chipset. This includes the Iomega
index 923d59a..cec5b75 100644 (file)
@@ -1293,7 +1293,7 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        result = request_irq(zr->pci_dev->irq, zoran_irq,
-                            IRQF_SHARED | IRQF_DISABLED, ZR_DEVNAME(zr), zr);
+                            IRQF_SHARED, ZR_DEVNAME(zr), zr);
        if (result < 0) {
                if (result == -EINVAL) {
                        dprintk(1,
index eb70dda..d7f0249 100644 (file)
@@ -143,6 +143,7 @@ if V4L_MEM2MEM_DRIVERS
 config VIDEO_CODA
        tristate "Chips&Media Coda multi-standard codec IP"
        depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXC
+       select SRAM
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
        ---help---
@@ -212,7 +213,7 @@ config VIDEO_SH_VEU
 
 config VIDEO_RENESAS_VSP1
        tristate "Renesas VSP1 Video Processing Engine"
-       depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+       depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        ---help---
          This is a V4L2 driver for the Renesas VSP1 video processing engine.
@@ -220,6 +221,22 @@ config VIDEO_RENESAS_VSP1
          To compile this driver as a module, choose M here: the module
          will be called vsp1.
 
+config VIDEO_TI_VPE
+       tristate "TI VPE (Video Processing Engine) driver"
+       depends on VIDEO_DEV && VIDEO_V4L2 && SOC_DRA7XX
+       select VIDEOBUF2_DMA_CONTIG
+       select V4L2_MEM2MEM_DEV
+       default n
+       ---help---
+         Support for the TI VPE(Video Processing Engine) block
+         found on DRA7XX SoC.
+
+config VIDEO_TI_VPE_DEBUG
+       bool "VPE debug messages"
+       depends on VIDEO_TI_VPE
+       ---help---
+         Enable debug messages on VPE driver.
+
 endif # V4L_MEM2MEM_DRIVERS
 
 menuconfig V4L_TEST_DRIVERS
index 4e4da48..1348ba1 100644 (file)
@@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o
 
 obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o
 
+obj-$(CONFIG_VIDEO_TI_VPE)             += ti-vpe/
+
 obj-$(CONFIG_VIDEO_MX2_EMMAPRP)                += mx2_emmaprp.o
 obj-$(CONFIG_VIDEO_CODA)               += coda.o
 
index 4c11059..2819165 100644 (file)
@@ -422,7 +422,7 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
                return ret;
        }
 
-       INIT_COMPLETION(bcap_dev->comp);
+       reinit_completion(&bcap_dev->comp);
        bcap_dev->stop = false;
        return 0;
 }
index 4993610..bd72fb9 100644 (file)
@@ -39,7 +39,7 @@
 
 #define CODA_NAME              "coda"
 
-#define CODA_MAX_INSTANCES     4
+#define CODADX6_MAX_INSTANCES  4
 
 #define CODA_FMO_BUF_SIZE      32
 #define CODADX6_WORK_BUF_SIZE  (288 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024)
@@ -54,8 +54,6 @@
 
 #define CODA_MAX_FRAMEBUFFERS  8
 
-#define MAX_W          8192
-#define MAX_H          8192
 #define CODA_MAX_FRAME_SIZE    0x100000
 #define FMO_SLICE_SAVE_BUF_SIZE         (32)
 #define CODA_DEFAULT_GAMMA             4096
@@ -394,14 +392,57 @@ static struct coda_codec *coda_find_codec(struct coda_dev *dev, int src_fourcc,
        return &codecs[k];
 }
 
+static void coda_get_max_dimensions(struct coda_dev *dev,
+                                   struct coda_codec *codec,
+                                   int *max_w, int *max_h)
+{
+       struct coda_codec *codecs = dev->devtype->codecs;
+       int num_codecs = dev->devtype->num_codecs;
+       unsigned int w, h;
+       int k;
+
+       if (codec) {
+               w = codec->max_w;
+               h = codec->max_h;
+       } else {
+               for (k = 0, w = 0, h = 0; k < num_codecs; k++) {
+                       w = max(w, codecs[k].max_w);
+                       h = max(h, codecs[k].max_h);
+               }
+       }
+
+       if (max_w)
+               *max_w = w;
+       if (max_h)
+               *max_h = h;
+}
+
+static char *coda_product_name(int product)
+{
+       static char buf[9];
+
+       switch (product) {
+       case CODA_DX6:
+               return "CodaDx6";
+       case CODA_7541:
+               return "CODA7541";
+       default:
+               snprintf(buf, sizeof(buf), "(0x%04x)", product);
+               return buf;
+       }
+}
+
 /*
  * V4L2 ioctl() operations.
  */
-static int vidioc_querycap(struct file *file, void *priv,
-                          struct v4l2_capability *cap)
+static int coda_querycap(struct file *file, void *priv,
+                        struct v4l2_capability *cap)
 {
+       struct coda_ctx *ctx = fh_to_ctx(priv);
+
        strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver));
-       strlcpy(cap->card, CODA_NAME, sizeof(cap->card));
+       strlcpy(cap->card, coda_product_name(ctx->dev->devtype->product),
+               sizeof(cap->card));
        strlcpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info));
        /*
         * This is only a mem-to-mem video device. The capture and output
@@ -457,6 +498,8 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
                fmt = &formats[i];
                strlcpy(f->description, fmt->name, sizeof(f->description));
                f->pixelformat = fmt->fourcc;
+               if (!coda_format_is_yuv(fmt->fourcc))
+                       f->flags |= V4L2_FMT_FLAG_COMPRESSED;
                return 0;
        }
 
@@ -464,8 +507,8 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
        return -EINVAL;
 }
 
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
-                                  struct v4l2_fmtdesc *f)
+static int coda_enum_fmt_vid_cap(struct file *file, void *priv,
+                                struct v4l2_fmtdesc *f)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
        struct vb2_queue *src_vq;
@@ -483,13 +526,14 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
        return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
 }
 
-static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
-                                  struct v4l2_fmtdesc *f)
+static int coda_enum_fmt_vid_out(struct file *file, void *priv,
+                                struct v4l2_fmtdesc *f)
 {
        return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);
 }
 
-static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+static int coda_g_fmt(struct file *file, void *priv,
+                     struct v4l2_format *f)
 {
        struct vb2_queue *vq;
        struct coda_q_data *q_data;
@@ -516,8 +560,11 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
        return 0;
 }
 
-static int vidioc_try_fmt(struct coda_codec *codec, struct v4l2_format *f)
+static int coda_try_fmt(struct coda_ctx *ctx, struct coda_codec *codec,
+                       struct v4l2_format *f)
 {
+       struct coda_dev *dev = ctx->dev;
+       struct coda_q_data *q_data;
        unsigned int max_w, max_h;
        enum v4l2_field field;
 
@@ -531,32 +578,48 @@ static int vidioc_try_fmt(struct coda_codec *codec, struct v4l2_format *f)
         * if any of the dimensions is unsupported */
        f->fmt.pix.field = field;
 
-       if (codec) {
-               max_w = codec->max_w;
-               max_h = codec->max_h;
-       } else {
-               max_w = MAX_W;
-               max_h = MAX_H;
+       coda_get_max_dimensions(dev, codec, &max_w, &max_h);
+       v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN,
+                             &f->fmt.pix.height, MIN_H, max_h, H_ALIGN,
+                             S_ALIGN);
+
+       switch (f->fmt.pix.pixelformat) {
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_YVU420:
+       case V4L2_PIX_FMT_H264:
+       case V4L2_PIX_FMT_MPEG4:
+       case V4L2_PIX_FMT_JPEG:
+               break;
+       default:
+               q_data = get_q_data(ctx, f->type);
+               f->fmt.pix.pixelformat = q_data->fourcc;
        }
-       v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w,
-                             W_ALIGN, &f->fmt.pix.height,
-                             MIN_H, max_h, H_ALIGN, S_ALIGN);
 
-       if (coda_format_is_yuv(f->fmt.pix.pixelformat)) {
+       switch (f->fmt.pix.pixelformat) {
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_YVU420:
                /* Frame stride must be multiple of 8 */
                f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 8);
                f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
                                        f->fmt.pix.height * 3 / 2;
-       } else { /*encoded formats h.264/mpeg4 */
+               break;
+       case V4L2_PIX_FMT_H264:
+       case V4L2_PIX_FMT_MPEG4:
+       case V4L2_PIX_FMT_JPEG:
                f->fmt.pix.bytesperline = 0;
                f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE;
+               break;
+       default:
+               BUG();
        }
 
+       f->fmt.pix.priv = 0;
+
        return 0;
 }
 
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
-                                 struct v4l2_format *f)
+static int coda_try_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
        struct coda_codec *codec;
@@ -584,7 +647,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
 
        f->fmt.pix.colorspace = ctx->colorspace;
 
-       ret = vidioc_try_fmt(codec, f);
+       ret = coda_try_fmt(ctx, codec, f);
        if (ret < 0)
                return ret;
 
@@ -600,8 +663,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
-                                 struct v4l2_format *f)
+static int coda_try_fmt_vid_out(struct file *file, void *priv,
+                               struct v4l2_format *f)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
        struct coda_codec *codec;
@@ -613,10 +676,10 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
        if (!f->fmt.pix.colorspace)
                f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
 
-       return vidioc_try_fmt(codec, f);
+       return coda_try_fmt(ctx, codec, f);
 }
 
-static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
+static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
 {
        struct coda_q_data *q_data;
        struct vb2_queue *vq;
@@ -646,61 +709,62 @@ static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
        return 0;
 }
 
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
-                               struct v4l2_format *f)
+static int coda_s_fmt_vid_cap(struct file *file, void *priv,
+                             struct v4l2_format *f)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
        int ret;
 
-       ret = vidioc_try_fmt_vid_cap(file, priv, f);
+       ret = coda_try_fmt_vid_cap(file, priv, f);
        if (ret)
                return ret;
 
-       return vidioc_s_fmt(ctx, f);
+       return coda_s_fmt(ctx, f);
 }
 
-static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
-                               struct v4l2_format *f)
+static int coda_s_fmt_vid_out(struct file *file, void *priv,
+                             struct v4l2_format *f)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
        int ret;
 
-       ret = vidioc_try_fmt_vid_out(file, priv, f);
+       ret = coda_try_fmt_vid_out(file, priv, f);
        if (ret)
                return ret;
 
-       ret = vidioc_s_fmt(ctx, f);
+       ret = coda_s_fmt(ctx, f);
        if (ret)
                ctx->colorspace = f->fmt.pix.colorspace;
 
        return ret;
 }
 
-static int vidioc_reqbufs(struct file *file, void *priv,
-                         struct v4l2_requestbuffers *reqbufs)
+static int coda_reqbufs(struct file *file, void *priv,
+                       struct v4l2_requestbuffers *reqbufs)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
 
        return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
 }
 
-static int vidioc_querybuf(struct file *file, void *priv,
-                          struct v4l2_buffer *buf)
+static int coda_querybuf(struct file *file, void *priv,
+                        struct v4l2_buffer *buf)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
 
        return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
 }
 
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+static int coda_qbuf(struct file *file, void *priv,
+                    struct v4l2_buffer *buf)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
 
        return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
 }
 
-static int vidioc_expbuf(struct file *file, void *priv,
-                        struct v4l2_exportbuffer *eb)
+static int coda_expbuf(struct file *file, void *priv,
+                      struct v4l2_exportbuffer *eb)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
 
@@ -718,7 +782,8 @@ static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx,
                (buf->sequence == (ctx->qsequence - 1)));
 }
 
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+static int coda_dqbuf(struct file *file, void *priv,
+                     struct v4l2_buffer *buf)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
        int ret;
@@ -738,24 +803,24 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
        return ret;
 }
 
-static int vidioc_create_bufs(struct file *file, void *priv,
-                             struct v4l2_create_buffers *create)
+static int coda_create_bufs(struct file *file, void *priv,
+                           struct v4l2_create_buffers *create)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
 
        return v4l2_m2m_create_bufs(file, ctx->m2m_ctx, create);
 }
 
-static int vidioc_streamon(struct file *file, void *priv,
-                          enum v4l2_buf_type type)
+static int coda_streamon(struct file *file, void *priv,
+                        enum v4l2_buf_type type)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
 
        return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
 }
 
-static int vidioc_streamoff(struct file *file, void *priv,
-                           enum v4l2_buf_type type)
+static int coda_streamoff(struct file *file, void *priv,
+                         enum v4l2_buf_type type)
 {
        struct coda_ctx *ctx = fh_to_ctx(priv);
        int ret;
@@ -772,23 +837,34 @@ static int vidioc_streamoff(struct file *file, void *priv,
        return ret;
 }
 
-static int vidioc_decoder_cmd(struct file *file, void *fh,
-                             struct v4l2_decoder_cmd *dc)
+static int coda_try_decoder_cmd(struct file *file, void *fh,
+                               struct v4l2_decoder_cmd *dc)
 {
-       struct coda_ctx *ctx = fh_to_ctx(fh);
-
        if (dc->cmd != V4L2_DEC_CMD_STOP)
                return -EINVAL;
 
-       if ((dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) ||
-           (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY))
+       if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK)
                return -EINVAL;
 
-       if (dc->stop.pts != 0)
+       if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0))
                return -EINVAL;
 
+       return 0;
+}
+
+static int coda_decoder_cmd(struct file *file, void *fh,
+                           struct v4l2_decoder_cmd *dc)
+{
+       struct coda_ctx *ctx = fh_to_ctx(fh);
+       int ret;
+
+       ret = coda_try_decoder_cmd(file, fh, dc);
+       if (ret < 0)
+               return ret;
+
+       /* Ignore decoder stop command silently in encoder context */
        if (ctx->inst_type != CODA_INST_DECODER)
-               return -EINVAL;
+               return 0;
 
        /* Set the strem-end flag on this context */
        ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
@@ -796,8 +872,8 @@ static int vidioc_decoder_cmd(struct file *file, void *fh,
        return 0;
 }
 
-static int vidioc_subscribe_event(struct v4l2_fh *fh,
-                                 const struct v4l2_event_subscription *sub)
+static int coda_subscribe_event(struct v4l2_fh *fh,
+                               const struct v4l2_event_subscription *sub)
 {
        switch (sub->type) {
        case V4L2_EVENT_EOS:
@@ -808,32 +884,33 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh,
 }
 
 static const struct v4l2_ioctl_ops coda_ioctl_ops = {
-       .vidioc_querycap        = vidioc_querycap,
+       .vidioc_querycap        = coda_querycap,
 
-       .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
-       .vidioc_g_fmt_vid_cap   = vidioc_g_fmt,
-       .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
-       .vidioc_s_fmt_vid_cap   = vidioc_s_fmt_vid_cap,
+       .vidioc_enum_fmt_vid_cap = coda_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap   = coda_g_fmt,
+       .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap   = coda_s_fmt_vid_cap,
 
-       .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
-       .vidioc_g_fmt_vid_out   = vidioc_g_fmt,
-       .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
-       .vidioc_s_fmt_vid_out   = vidioc_s_fmt_vid_out,
+       .vidioc_enum_fmt_vid_out = coda_enum_fmt_vid_out,
+       .vidioc_g_fmt_vid_out   = coda_g_fmt,
+       .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out,
+       .vidioc_s_fmt_vid_out   = coda_s_fmt_vid_out,
 
-       .vidioc_reqbufs         = vidioc_reqbufs,
-       .vidioc_querybuf        = vidioc_querybuf,
+       .vidioc_reqbufs         = coda_reqbufs,
+       .vidioc_querybuf        = coda_querybuf,
 
-       .vidioc_qbuf            = vidioc_qbuf,
-       .vidioc_expbuf          = vidioc_expbuf,
-       .vidioc_dqbuf           = vidioc_dqbuf,
-       .vidioc_create_bufs     = vidioc_create_bufs,
+       .vidioc_qbuf            = coda_qbuf,
+       .vidioc_expbuf          = coda_expbuf,
+       .vidioc_dqbuf           = coda_dqbuf,
+       .vidioc_create_bufs     = coda_create_bufs,
 
-       .vidioc_streamon        = vidioc_streamon,
-       .vidioc_streamoff       = vidioc_streamoff,
+       .vidioc_streamon        = coda_streamon,
+       .vidioc_streamoff       = coda_streamoff,
 
-       .vidioc_decoder_cmd     = vidioc_decoder_cmd,
+       .vidioc_try_decoder_cmd = coda_try_decoder_cmd,
+       .vidioc_decoder_cmd     = coda_decoder_cmd,
 
-       .vidioc_subscribe_event = vidioc_subscribe_event,
+       .vidioc_subscribe_event = coda_subscribe_event,
        .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 };
 
@@ -1928,8 +2005,9 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
        if (!(ctx->streamon_out & ctx->streamon_cap))
                return 0;
 
-       /* Allow device_run with no buffers queued and after streamoff */
-       v4l2_m2m_set_src_buffered(ctx->m2m_ctx, true);
+       /* Allow decoder device_run with no new buffers queued */
+       if (ctx->inst_type == CODA_INST_DECODER)
+               v4l2_m2m_set_src_buffered(ctx->m2m_ctx, true);
 
        ctx->gopcounter = ctx->params.gop_size - 1;
        buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
@@ -2071,10 +2149,8 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
        coda_setup_iram(ctx);
 
        if (dst_fourcc == V4L2_PIX_FMT_H264) {
-               value  = (FMO_SLICE_SAVE_BUF_SIZE << 7);
-               value |= (0 & CODA_FMOPARAM_TYPE_MASK) << CODA_FMOPARAM_TYPE_OFFSET;
-               value |=  0 & CODA_FMOPARAM_SLICENUM_MASK;
                if (dev->devtype->product == CODA_DX6) {
+                       value = FMO_SLICE_SAVE_BUF_SIZE << 7;
                        coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO);
                } else {
                        coda_write(dev, ctx->iram_info.search_ram_paddr,
@@ -2371,7 +2447,13 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq,
 
 static int coda_next_free_instance(struct coda_dev *dev)
 {
-       return ffz(dev->instance_mask);
+       int idx = ffz(dev->instance_mask);
+
+       if ((idx < 0) ||
+           (dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES))
+               return -EBUSY;
+
+       return idx;
 }
 
 static int coda_open(struct file *file)
@@ -2386,8 +2468,8 @@ static int coda_open(struct file *file)
                return -ENOMEM;
 
        idx = coda_next_free_instance(dev);
-       if (idx >= CODA_MAX_INSTANCES) {
-               ret = -EBUSY;
+       if (idx < 0) {
+               ret = idx;
                goto err_coda_max;
        }
        set_bit(idx, &dev->instance_mask);
@@ -2719,7 +2801,6 @@ static void coda_finish_encode(struct coda_ctx *ctx)
        dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
 
        /* Get results from the coda */
-       coda_read(dev, CODA_RET_ENC_PIC_TYPE);
        start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START);
        wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
 
@@ -2739,7 +2820,7 @@ static void coda_finish_encode(struct coda_ctx *ctx)
        coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM);
        coda_read(dev, CODA_RET_ENC_PIC_FLAG);
 
-       if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) {
+       if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) {
                dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
                dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
        } else {
@@ -2861,21 +2942,6 @@ static bool coda_firmware_supported(u32 vernum)
        return false;
 }
 
-static char *coda_product_name(int product)
-{
-       static char buf[9];
-
-       switch (product) {
-       case CODA_DX6:
-               return "CodaDx6";
-       case CODA_7541:
-               return "CODA7541";
-       default:
-               snprintf(buf, sizeof(buf), "(0x%04x)", product);
-               return buf;
-       }
-}
-
 static int coda_hw_init(struct coda_dev *dev)
 {
        u16 product, major, minor, release;
index 04609cc..eac472b 100644 (file)
@@ -1785,7 +1785,7 @@ static int vpbe_display_probe(struct platform_device *pdev)
        }
 
        irq = res->start;
-       err = devm_request_irq(&pdev->dev, irq, venc_isr, IRQF_DISABLED,
+       err = devm_request_irq(&pdev->dev, irq, venc_isr, 0,
                               VPBE_DISPLAY_DRIVER, disp_dev);
        if (err) {
                v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
index 9360909..d762246 100644 (file)
@@ -688,7 +688,7 @@ static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
        frame_format = ccdc_dev->hw_ops.get_frame_format();
        if (frame_format == CCDC_FRMFMT_PROGRESSIVE) {
                return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr,
-                                   IRQF_DISABLED, "vpfe_capture1",
+                                   0, "vpfe_capture1",
                                    vpfe_dev);
        }
        return 0;
@@ -1863,7 +1863,7 @@ static int vpfe_probe(struct platform_device *pdev)
        }
        vpfe_dev->ccdc_irq1 = res1->start;
 
-       ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED,
+       ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, 0,
                          "vpfe_capture0", vpfe_dev);
 
        if (0 != ret) {
index 1089834..52ac5e6 100644 (file)
@@ -2154,7 +2154,7 @@ static __init int vpif_probe(struct platform_device *pdev)
 
                        if (!vpif_obj.sd[i]) {
                                vpif_err("Error registering v4l2 subdevice\n");
-                               err = -ENOMEM;
+                               err = -ENODEV;
                                goto probe_subdev_out;
                        }
                        v4l2_info(&vpif_obj.v4l2_dev,
index 76435d3..ef0a656 100644 (file)
@@ -45,6 +45,7 @@
 #define GSC_DST_FMT                    (1 << 2)
 #define GSC_CTX_M2M                    (1 << 3)
 #define GSC_CTX_STOP_REQ               (1 << 6)
+#define        GSC_CTX_ABORT                   (1 << 7)
 
 enum gsc_dev_flags {
        /* for global */
index e576ff2..810c3e1 100644 (file)
@@ -46,6 +46,17 @@ static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)
        return ret == 0 ? -ETIMEDOUT : ret;
 }
 
+static void __gsc_m2m_job_abort(struct gsc_ctx *ctx)
+{
+       int ret;
+
+       ret = gsc_m2m_ctx_stop_req(ctx);
+       if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) {
+               gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx);
+               gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+       }
+}
+
 static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
 {
        struct gsc_ctx *ctx = q->drv_priv;
@@ -58,11 +69,8 @@ static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
 static int gsc_m2m_stop_streaming(struct vb2_queue *q)
 {
        struct gsc_ctx *ctx = q->drv_priv;
-       int ret;
 
-       ret = gsc_m2m_ctx_stop_req(ctx);
-       if (ret == -ETIMEDOUT)
-               gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+       __gsc_m2m_job_abort(ctx);
 
        pm_runtime_put(&ctx->gsc_dev->pdev->dev);
 
@@ -91,15 +99,9 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
        }
 }
 
-
 static void gsc_m2m_job_abort(void *priv)
 {
-       struct gsc_ctx *ctx = priv;
-       int ret;
-
-       ret = gsc_m2m_ctx_stop_req(ctx);
-       if (ret == -ETIMEDOUT)
-               gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+       __gsc_m2m_job_abort((struct gsc_ctx *)priv);
 }
 
 static int gsc_get_bufs(struct gsc_ctx *ctx)
@@ -150,9 +152,10 @@ static void gsc_m2m_device_run(void *priv)
                gsc->m2m.ctx = ctx;
        }
 
-       is_set = (ctx->state & GSC_CTX_STOP_REQ) ? 1 : 0;
-       ctx->state &= ~GSC_CTX_STOP_REQ;
+       is_set = ctx->state & GSC_CTX_STOP_REQ;
        if (is_set) {
+               ctx->state &= ~GSC_CTX_STOP_REQ;
+               ctx->state |= GSC_CTX_ABORT;
                wake_up(&gsc->irq_queue);
                goto put_device;
        }
index d2e6cba..f3c6136 100644 (file)
@@ -511,7 +511,7 @@ static int __ctrl_set_metering(struct fimc_is *is, unsigned int value)
                break;
        default:
                return -EINVAL;
-       };
+       }
 
        __is_set_isp_metering(is, IS_METERING_CONFIG_CMD, val);
        return 0;
index a835112..7a4ee4c 100644 (file)
@@ -411,8 +411,8 @@ static int fimc_md_of_add_sensor(struct fimc_md *fmd,
 
        device_lock(&client->dev);
 
-       if (!client->driver ||
-           !try_module_get(client->driver->driver.owner)) {
+       if (!client->dev.driver ||
+           !try_module_get(client->dev.driver->owner)) {
                ret = -EPROBE_DEFER;
                v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n",
                                                node->full_name);
@@ -442,7 +442,7 @@ static int fimc_md_of_add_sensor(struct fimc_md *fmd,
        fmd->num_sensors++;
 
 mod_put:
-       module_put(client->driver->driver.owner);
+       module_put(client->dev.driver->owner);
 dev_put:
        device_unlock(&client->dev);
        put_device(&client->dev);
index 540516c..65cab70 100644 (file)
@@ -341,8 +341,7 @@ static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op,
        ctx->xt->dir = DMA_MEM_TO_MEM;
        ctx->xt->src_sgl = false;
        ctx->xt->dst_sgl = true;
-       flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT |
-               DMA_COMPL_SKIP_DEST_UNMAP | DMA_COMPL_SKIP_SRC_UNMAP;
+       flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
 
        tx = dmadev->device_prep_interleaved_dma(chan, ctx->xt, flags);
        if (tx == NULL) {
@@ -1084,8 +1083,7 @@ free_dev:
 
 static int deinterlace_remove(struct platform_device *pdev)
 {
-       struct deinterlace_dev *pcdev =
-               (struct deinterlace_dev *)platform_get_drvdata(pdev);
+       struct deinterlace_dev *pcdev = platform_get_drvdata(pdev);
 
        v4l2_info(&pcdev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
        v4l2_m2m_release(pcdev->m2m_dev);
index 5184887..32fab30 100644 (file)
@@ -1221,16 +1221,16 @@ static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
 {
        struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
        struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
-       struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0);
+       struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0);
        struct mcam_dma_desc *desc = mvb->dma_desc;
        struct scatterlist *sg;
        int i;
 
-       mvb->dma_desc_nent = dma_map_sg(cam->dev, sgd->sglist, sgd->num_pages,
-                       DMA_FROM_DEVICE);
+       mvb->dma_desc_nent = dma_map_sg(cam->dev, sg_table->sgl,
+                       sg_table->nents, DMA_FROM_DEVICE);
        if (mvb->dma_desc_nent <= 0)
                return -EIO;  /* Not sure what's right here */
-       for_each_sg(sgd->sglist, sg, mvb->dma_desc_nent, i) {
+       for_each_sg(sg_table->sgl, sg, mvb->dma_desc_nent, i) {
                desc->dma_addr = sg_dma_address(sg);
                desc->segment_len = sg_dma_len(sg);
                desc++;
@@ -1241,9 +1241,11 @@ static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
 static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
 {
        struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
-       struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0);
+       struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0);
 
-       dma_unmap_sg(cam->dev, sgd->sglist, sgd->num_pages, DMA_FROM_DEVICE);
+       if (sg_table)
+               dma_unmap_sg(cam->dev, sg_table->sgl,
+                               sg_table->nents, DMA_FROM_DEVICE);
        return 0;
 }
 
index b5a19af..3458fa0 100644 (file)
@@ -481,7 +481,6 @@ static int mmpcam_remove(struct mmp_camera *cam)
        struct mmp_camera_platform_data *pdata;
 
        mmpcam_remove_device(cam);
-       free_irq(cam->irq, mcam);
        mccic_shutdown(mcam);
        mmpcam_power_down(mcam);
        pdata = cam->pdev->dev.platform_data;
index 6a17676..8df5975 100644 (file)
@@ -1090,8 +1090,7 @@ unreg_dev:
 
 static int m2mtest_remove(struct platform_device *pdev)
 {
-       struct m2mtest_dev *dev =
-               (struct m2mtest_dev *)platform_get_drvdata(pdev);
+       struct m2mtest_dev *dev = platform_get_drvdata(pdev);
 
        v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
        v4l2_m2m_release(dev->m2m_dev);
index fd6289d..0b29483 100644 (file)
@@ -840,7 +840,7 @@ put_clk:
 
 static int g2d_remove(struct platform_device *pdev)
 {
-       struct g2d_dev *dev = (struct g2d_dev *)platform_get_drvdata(pdev);
+       struct g2d_dev *dev = platform_get_drvdata(pdev);
 
        v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME);
        v4l2_m2m_release(dev->m2m_dev);
index 084263d..5f2c4ad 100644 (file)
@@ -404,7 +404,11 @@ leave_handle_frame:
        if (test_and_clear_bit(0, &dev->hw_lock) == 0)
                BUG();
        s5p_mfc_clock_off();
-       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       /* if suspending, wake up device and do not try_run again*/
+       if (test_bit(0, &dev->enter_suspend))
+               wake_up_dev(dev, reason, err);
+       else
+               s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
 }
 
 /* Error handling for interrupt */
@@ -1101,7 +1105,7 @@ static int s5p_mfc_probe(struct platform_device *pdev)
        }
        dev->irq = res->start;
        ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq,
-                                       IRQF_DISABLED, pdev->name, dev);
+                                       0, pdev->name, dev);
        if (ret) {
                dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
                goto err_res;
@@ -1286,9 +1290,7 @@ static int s5p_mfc_suspend(struct device *dev)
                /* Try and lock the HW */
                /* Wait on the interrupt waitqueue */
                ret = wait_event_interruptible_timeout(m_dev->queue,
-                       m_dev->int_cond || m_dev->ctx[m_dev->curr_ctx]->int_cond,
-                       msecs_to_jiffies(MFC_INT_TIMEOUT));
-
+                       m_dev->int_cond, msecs_to_jiffies(MFC_INT_TIMEOUT));
                if (ret == 0) {
                        mfc_err("Waiting for hardware to finish timed out\n");
                        return -EIO;
index ad4f1df..9a6efd6 100644 (file)
@@ -111,7 +111,7 @@ static int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx)
                break;
        default:
                h2r_args.arg[0] = S5P_FIMV_CODEC_NONE;
-       };
+       }
        h2r_args.arg[1] = 0; /* no crc & no pixelcache */
        h2r_args.arg[2] = ctx->ctx.ofs;
        h2r_args.arg[3] = ctx->ctx.size;
index db796c8..ec1a594 100644 (file)
@@ -113,7 +113,7 @@ static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
                break;
        default:
                codec_type = S5P_FIMV_CODEC_NONE_V6;
-       };
+       }
        mfc_write(dev, codec_type, S5P_FIMV_CODEC_TYPE_V6);
        mfc_write(dev, ctx->ctx.dma, S5P_FIMV_CONTEXT_MEM_ADDR_V6);
        mfc_write(dev, ctx->ctx.size, S5P_FIMV_CONTEXT_MEM_SIZE_V6);
index 41f5a3c..4ff3b6c 100644 (file)
@@ -113,7 +113,7 @@ static struct mfc_control controls[] = {
                .minimum = 0,
                .maximum = (1 << 16) - 1,
                .step = 1,
-               .default_value = 0,
+               .default_value = 12,
        },
        {
                .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
@@ -356,7 +356,7 @@ static struct mfc_control controls[] = {
                .minimum = 0,
                .maximum = 51,
                .step = 1,
-               .default_value = 1,
+               .default_value = 51,
        },
        {
                .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
@@ -399,7 +399,7 @@ static struct mfc_control controls[] = {
                .minimum = 1,
                .maximum = 31,
                .step = 1,
-               .default_value = 1,
+               .default_value = 31,
        },
        {
                .id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP,
@@ -444,7 +444,7 @@ static struct mfc_control controls[] = {
                .minimum = 0,
                .maximum = 51,
                .step = 1,
-               .default_value = 1,
+               .default_value = 51,
        },
        {
                .id = V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP,
index 368582b..58ec7bb 100644 (file)
@@ -1582,7 +1582,7 @@ static int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev)
                break;
        default:
                reason = S5P_MFC_R2H_CMD_EMPTY;
-       };
+       }
        return reason;
 }
 
index b93a21f..74344c7 100644 (file)
@@ -226,7 +226,7 @@ static void mxr_graph_fix_geometry(struct mxr_layer *layer,
                        src->width + src->x_offset, 32767);
                src->full_height = clamp_val(src->full_height,
                        src->height + src->y_offset, 2047);
-       };
+       }
 }
 
 /* PUBLIC API */
index 3d13a63..c9388c4 100644 (file)
@@ -197,7 +197,7 @@ static void mxr_vp_fix_geometry(struct mxr_layer *layer,
                        ALIGN(src->width + src->x_offset, 8), 8192U);
                src->full_height = clamp(src->full_height,
                        src->height + src->y_offset, 8192U);
-       };
+       }
 }
 
 /* PUBLIC API */
index d02a7e0..6866bb4 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_data/camera-rcar.h>
 #define VIN_MAX_HEIGHT         2048
 
 enum chip_id {
+       RCAR_H2,
        RCAR_H1,
        RCAR_M1,
        RCAR_E1,
@@ -300,7 +302,8 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
                dmr = 0;
                break;
        case V4L2_PIX_FMT_RGB32:
-               if (priv->chip == RCAR_H1 || priv->chip == RCAR_E1) {
+               if (priv->chip == RCAR_H2 || priv->chip == RCAR_H1 ||
+                   priv->chip == RCAR_E1) {
                        dmr = VNDMR_EXRGB;
                        break;
                }
@@ -1381,6 +1384,7 @@ static struct soc_camera_host_ops rcar_vin_host_ops = {
 };
 
 static struct platform_device_id rcar_vin_id_table[] = {
+       { "r8a7790-vin",  RCAR_H2 },
        { "r8a7779-vin",  RCAR_H1 },
        { "r8a7778-vin",  RCAR_M1 },
        { "uPD35004-vin", RCAR_E1 },
index 8df22f7..150bd4d 100644 (file)
@@ -1800,7 +1800,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 
        /* request irq */
        err = devm_request_irq(&pdev->dev, pcdev->irq, sh_mobile_ceu_irq,
-                              IRQF_DISABLED, dev_name(&pdev->dev), pcdev);
+                              0, dev_name(&pdev->dev), pcdev);
        if (err) {
                dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");
                goto exit_release_mem;
index 387a232..4b8c024 100644 (file)
@@ -71,13 +71,23 @@ static int video_dev_create(struct soc_camera_device *icd);
 int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
                        struct v4l2_clk *clk)
 {
-       int ret = clk ? v4l2_clk_enable(clk) : 0;
-       if (ret < 0) {
-               dev_err(dev, "Cannot enable clock: %d\n", ret);
-               return ret;
+       int ret;
+       bool clock_toggle;
+
+       if (clk && (!ssdd->unbalanced_power ||
+                   !test_and_set_bit(0, &ssdd->clock_state))) {
+               ret = v4l2_clk_enable(clk);
+               if (ret < 0) {
+                       dev_err(dev, "Cannot enable clock: %d\n", ret);
+                       return ret;
+               }
+               clock_toggle = true;
+       } else {
+               clock_toggle = false;
        }
-       ret = regulator_bulk_enable(ssdd->num_regulators,
-                                       ssdd->regulators);
+
+       ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators,
+                                   ssdd->sd_pdata.regulators);
        if (ret < 0) {
                dev_err(dev, "Cannot enable regulators\n");
                goto eregenable;
@@ -95,10 +105,10 @@ int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
        return 0;
 
 epwron:
-       regulator_bulk_disable(ssdd->num_regulators,
-                              ssdd->regulators);
+       regulator_bulk_disable(ssdd->sd_pdata.num_regulators,
+                              ssdd->sd_pdata.regulators);
 eregenable:
-       if (clk)
+       if (clock_toggle)
                v4l2_clk_disable(clk);
 
        return ret;
@@ -120,14 +130,14 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd
                }
        }
 
-       err = regulator_bulk_disable(ssdd->num_regulators,
-                                    ssdd->regulators);
+       err = regulator_bulk_disable(ssdd->sd_pdata.num_regulators,
+                                    ssdd->sd_pdata.regulators);
        if (err < 0) {
                dev_err(dev, "Cannot disable regulators\n");
                ret = ret ? : err;
        }
 
-       if (clk)
+       if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state)))
                v4l2_clk_disable(clk);
 
        return ret;
@@ -137,8 +147,8 @@ EXPORT_SYMBOL(soc_camera_power_off);
 int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd)
 {
        /* Should not have any effect in synchronous case */
-       return devm_regulator_bulk_get(dev, ssdd->num_regulators,
-                                      ssdd->regulators);
+       return devm_regulator_bulk_get(dev, ssdd->sd_pdata.num_regulators,
+                                      ssdd->sd_pdata.regulators);
 }
 EXPORT_SYMBOL(soc_camera_power_init);
 
@@ -1346,8 +1356,8 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd,
         * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try
         * to allocate them again.
         */
-       ssdd->num_regulators = 0;
-       ssdd->regulators = NULL;
+       ssdd->sd_pdata.num_regulators = 0;
+       ssdd->sd_pdata.regulators = NULL;
        shd->board_info->platform_data = ssdd;
 
        snprintf(clk_name, sizeof(clk_name), "%d-%04x",
@@ -2020,8 +2030,8 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev)
         * that case regulators are attached to the I2C device and not to the
         * camera platform device.
         */
-       ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators,
-                                     ssdd->regulators);
+       ret = devm_regulator_bulk_get(&pdev->dev, ssdd->sd_pdata.num_regulators,
+                                     ssdd->sd_pdata.regulators);
        if (ret < 0)
                return ret;
 
diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile
new file mode 100644 (file)
index 0000000..cbf0a80
--- /dev/null
@@ -0,0 +1,5 @@
+obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o
+
+ti-vpe-y := vpe.o vpdma.o
+
+ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG
diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c
new file mode 100644 (file)
index 0000000..af0a5ff
--- /dev/null
@@ -0,0 +1,846 @@
+/*
+ * VPDMA helper library
+ *
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include "vpdma.h"
+#include "vpdma_priv.h"
+
+#define VPDMA_FIRMWARE "vpdma-1b8.bin"
+
+const struct vpdma_data_format vpdma_yuv_fmts[] = {
+       [VPDMA_DATA_FMT_Y444] = {
+               .data_type      = DATA_TYPE_Y444,
+               .depth          = 8,
+       },
+       [VPDMA_DATA_FMT_Y422] = {
+               .data_type      = DATA_TYPE_Y422,
+               .depth          = 8,
+       },
+       [VPDMA_DATA_FMT_Y420] = {
+               .data_type      = DATA_TYPE_Y420,
+               .depth          = 8,
+       },
+       [VPDMA_DATA_FMT_C444] = {
+               .data_type      = DATA_TYPE_C444,
+               .depth          = 8,
+       },
+       [VPDMA_DATA_FMT_C422] = {
+               .data_type      = DATA_TYPE_C422,
+               .depth          = 8,
+       },
+       [VPDMA_DATA_FMT_C420] = {
+               .data_type      = DATA_TYPE_C420,
+               .depth          = 4,
+       },
+       [VPDMA_DATA_FMT_YC422] = {
+               .data_type      = DATA_TYPE_YC422,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_YC444] = {
+               .data_type      = DATA_TYPE_YC444,
+               .depth          = 24,
+       },
+       [VPDMA_DATA_FMT_CY422] = {
+               .data_type      = DATA_TYPE_CY422,
+               .depth          = 16,
+       },
+};
+
+const struct vpdma_data_format vpdma_rgb_fmts[] = {
+       [VPDMA_DATA_FMT_RGB565] = {
+               .data_type      = DATA_TYPE_RGB16_565,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_ARGB16_1555] = {
+               .data_type      = DATA_TYPE_ARGB_1555,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_ARGB16] = {
+               .data_type      = DATA_TYPE_ARGB_4444,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_RGBA16_5551] = {
+               .data_type      = DATA_TYPE_RGBA_5551,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_RGBA16] = {
+               .data_type      = DATA_TYPE_RGBA_4444,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_ARGB24] = {
+               .data_type      = DATA_TYPE_ARGB24_6666,
+               .depth          = 24,
+       },
+       [VPDMA_DATA_FMT_RGB24] = {
+               .data_type      = DATA_TYPE_RGB24_888,
+               .depth          = 24,
+       },
+       [VPDMA_DATA_FMT_ARGB32] = {
+               .data_type      = DATA_TYPE_ARGB32_8888,
+               .depth          = 32,
+       },
+       [VPDMA_DATA_FMT_RGBA24] = {
+               .data_type      = DATA_TYPE_RGBA24_6666,
+               .depth          = 24,
+       },
+       [VPDMA_DATA_FMT_RGBA32] = {
+               .data_type      = DATA_TYPE_RGBA32_8888,
+               .depth          = 32,
+       },
+       [VPDMA_DATA_FMT_BGR565] = {
+               .data_type      = DATA_TYPE_BGR16_565,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_ABGR16_1555] = {
+               .data_type      = DATA_TYPE_ABGR_1555,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_ABGR16] = {
+               .data_type      = DATA_TYPE_ABGR_4444,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_BGRA16_5551] = {
+               .data_type      = DATA_TYPE_BGRA_5551,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_BGRA16] = {
+               .data_type      = DATA_TYPE_BGRA_4444,
+               .depth          = 16,
+       },
+       [VPDMA_DATA_FMT_ABGR24] = {
+               .data_type      = DATA_TYPE_ABGR24_6666,
+               .depth          = 24,
+       },
+       [VPDMA_DATA_FMT_BGR24] = {
+               .data_type      = DATA_TYPE_BGR24_888,
+               .depth          = 24,
+       },
+       [VPDMA_DATA_FMT_ABGR32] = {
+               .data_type      = DATA_TYPE_ABGR32_8888,
+               .depth          = 32,
+       },
+       [VPDMA_DATA_FMT_BGRA24] = {
+               .data_type      = DATA_TYPE_BGRA24_6666,
+               .depth          = 24,
+       },
+       [VPDMA_DATA_FMT_BGRA32] = {
+               .data_type      = DATA_TYPE_BGRA32_8888,
+               .depth          = 32,
+       },
+};
+
+const struct vpdma_data_format vpdma_misc_fmts[] = {
+       [VPDMA_DATA_FMT_MV] = {
+               .data_type      = DATA_TYPE_MV,
+               .depth          = 4,
+       },
+};
+
+struct vpdma_channel_info {
+       int num;                /* VPDMA channel number */
+       int cstat_offset;       /* client CSTAT register offset */
+};
+
+static const struct vpdma_channel_info chan_info[] = {
+       [VPE_CHAN_LUMA1_IN] = {
+               .num            = VPE_CHAN_NUM_LUMA1_IN,
+               .cstat_offset   = VPDMA_DEI_LUMA1_CSTAT,
+       },
+       [VPE_CHAN_CHROMA1_IN] = {
+               .num            = VPE_CHAN_NUM_CHROMA1_IN,
+               .cstat_offset   = VPDMA_DEI_CHROMA1_CSTAT,
+       },
+       [VPE_CHAN_LUMA2_IN] = {
+               .num            = VPE_CHAN_NUM_LUMA2_IN,
+               .cstat_offset   = VPDMA_DEI_LUMA2_CSTAT,
+       },
+       [VPE_CHAN_CHROMA2_IN] = {
+               .num            = VPE_CHAN_NUM_CHROMA2_IN,
+               .cstat_offset   = VPDMA_DEI_CHROMA2_CSTAT,
+       },
+       [VPE_CHAN_LUMA3_IN] = {
+               .num            = VPE_CHAN_NUM_LUMA3_IN,
+               .cstat_offset   = VPDMA_DEI_LUMA3_CSTAT,
+       },
+       [VPE_CHAN_CHROMA3_IN] = {
+               .num            = VPE_CHAN_NUM_CHROMA3_IN,
+               .cstat_offset   = VPDMA_DEI_CHROMA3_CSTAT,
+       },
+       [VPE_CHAN_MV_IN] = {
+               .num            = VPE_CHAN_NUM_MV_IN,
+               .cstat_offset   = VPDMA_DEI_MV_IN_CSTAT,
+       },
+       [VPE_CHAN_MV_OUT] = {
+               .num            = VPE_CHAN_NUM_MV_OUT,
+               .cstat_offset   = VPDMA_DEI_MV_OUT_CSTAT,
+       },
+       [VPE_CHAN_LUMA_OUT] = {
+               .num            = VPE_CHAN_NUM_LUMA_OUT,
+               .cstat_offset   = VPDMA_VIP_UP_Y_CSTAT,
+       },
+       [VPE_CHAN_CHROMA_OUT] = {
+               .num            = VPE_CHAN_NUM_CHROMA_OUT,
+               .cstat_offset   = VPDMA_VIP_UP_UV_CSTAT,
+       },
+       [VPE_CHAN_RGB_OUT] = {
+               .num            = VPE_CHAN_NUM_RGB_OUT,
+               .cstat_offset   = VPDMA_VIP_UP_Y_CSTAT,
+       },
+};
+
+static u32 read_reg(struct vpdma_data *vpdma, int offset)
+{
+       return ioread32(vpdma->base + offset);
+}
+
+static void write_reg(struct vpdma_data *vpdma, int offset, u32 value)
+{
+       iowrite32(value, vpdma->base + offset);
+}
+
+static int read_field_reg(struct vpdma_data *vpdma, int offset,
+               u32 mask, int shift)
+{
+       return (read_reg(vpdma, offset) & (mask << shift)) >> shift;
+}
+
+static void write_field_reg(struct vpdma_data *vpdma, int offset, u32 field,
+               u32 mask, int shift)
+{
+       u32 val = read_reg(vpdma, offset);
+
+       val &= ~(mask << shift);
+       val |= (field & mask) << shift;
+
+       write_reg(vpdma, offset, val);
+}
+
+void vpdma_dump_regs(struct vpdma_data *vpdma)
+{
+       struct device *dev = &vpdma->pdev->dev;
+
+#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(vpdma, VPDMA_##r))
+
+       dev_dbg(dev, "VPDMA Registers:\n");
+
+       DUMPREG(PID);
+       DUMPREG(LIST_ADDR);
+       DUMPREG(LIST_ATTR);
+       DUMPREG(LIST_STAT_SYNC);
+       DUMPREG(BG_RGB);
+       DUMPREG(BG_YUV);
+       DUMPREG(SETUP);
+       DUMPREG(MAX_SIZE1);
+       DUMPREG(MAX_SIZE2);
+       DUMPREG(MAX_SIZE3);
+
+       /*
+        * dumping registers of only group0 and group3, because VPE channels
+        * lie within group0 and group3 registers
+        */
+       DUMPREG(INT_CHAN_STAT(0));
+       DUMPREG(INT_CHAN_MASK(0));
+       DUMPREG(INT_CHAN_STAT(3));
+       DUMPREG(INT_CHAN_MASK(3));
+       DUMPREG(INT_CLIENT0_STAT);
+       DUMPREG(INT_CLIENT0_MASK);
+       DUMPREG(INT_CLIENT1_STAT);
+       DUMPREG(INT_CLIENT1_MASK);
+       DUMPREG(INT_LIST0_STAT);
+       DUMPREG(INT_LIST0_MASK);
+
+       /*
+        * these are registers specific to VPE clients, we can make this
+        * function dump client registers specific to VPE or VIP based on
+        * who is using it
+        */
+       DUMPREG(DEI_CHROMA1_CSTAT);
+       DUMPREG(DEI_LUMA1_CSTAT);
+       DUMPREG(DEI_CHROMA2_CSTAT);
+       DUMPREG(DEI_LUMA2_CSTAT);
+       DUMPREG(DEI_CHROMA3_CSTAT);
+       DUMPREG(DEI_LUMA3_CSTAT);
+       DUMPREG(DEI_MV_IN_CSTAT);
+       DUMPREG(DEI_MV_OUT_CSTAT);
+       DUMPREG(VIP_UP_Y_CSTAT);
+       DUMPREG(VIP_UP_UV_CSTAT);
+       DUMPREG(VPI_CTL_CSTAT);
+}
+
+/*
+ * Allocate a DMA buffer
+ */
+int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size)
+{
+       buf->size = size;
+       buf->mapped = false;
+       buf->addr = kzalloc(size, GFP_KERNEL);
+       if (!buf->addr)
+               return -ENOMEM;
+
+       WARN_ON((u32) buf->addr & VPDMA_DESC_ALIGN);
+
+       return 0;
+}
+
+void vpdma_free_desc_buf(struct vpdma_buf *buf)
+{
+       WARN_ON(buf->mapped);
+       kfree(buf->addr);
+       buf->addr = NULL;
+       buf->size = 0;
+}
+
+/*
+ * map descriptor/payload DMA buffer, enabling DMA access
+ */
+int vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf)
+{
+       struct device *dev = &vpdma->pdev->dev;
+
+       WARN_ON(buf->mapped);
+       buf->dma_addr = dma_map_single(dev, buf->addr, buf->size,
+                               DMA_TO_DEVICE);
+       if (dma_mapping_error(dev, buf->dma_addr)) {
+               dev_err(dev, "failed to map buffer\n");
+               return -EINVAL;
+       }
+
+       buf->mapped = true;
+
+       return 0;
+}
+
+/*
+ * unmap descriptor/payload DMA buffer, disabling DMA access and
+ * allowing the main processor to acces the data
+ */
+void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf)
+{
+       struct device *dev = &vpdma->pdev->dev;
+
+       if (buf->mapped)
+               dma_unmap_single(dev, buf->dma_addr, buf->size, DMA_TO_DEVICE);
+
+       buf->mapped = false;
+}
+
+/*
+ * create a descriptor list, the user of this list will append configuration,
+ * control and data descriptors to this list, this list will be submitted to
+ * VPDMA. VPDMA's list parser will go through each descriptor and perform the
+ * required DMA operations
+ */
+int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type)
+{
+       int r;
+
+       r = vpdma_alloc_desc_buf(&list->buf, size);
+       if (r)
+               return r;
+
+       list->next = list->buf.addr;
+
+       list->type = type;
+
+       return 0;
+}
+
+/*
+ * once a descriptor list is parsed by VPDMA, we reset the list by emptying it,
+ * to allow new descriptors to be added to the list.
+ */
+void vpdma_reset_desc_list(struct vpdma_desc_list *list)
+{
+       list->next = list->buf.addr;
+}
+
+/*
+ * free the buffer allocated fot the VPDMA descriptor list, this should be
+ * called when the user doesn't want to use VPDMA any more.
+ */
+void vpdma_free_desc_list(struct vpdma_desc_list *list)
+{
+       vpdma_free_desc_buf(&list->buf);
+
+       list->next = NULL;
+}
+
+static bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num)
+{
+       return read_reg(vpdma, VPDMA_LIST_STAT_SYNC) & BIT(list_num + 16);
+}
+
+/*
+ * submit a list of DMA descriptors to the VPE VPDMA, do not wait for completion
+ */
+int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list)
+{
+       /* we always use the first list */
+       int list_num = 0;
+       int list_size;
+
+       if (vpdma_list_busy(vpdma, list_num))
+               return -EBUSY;
+
+       /* 16-byte granularity */
+       list_size = (list->next - list->buf.addr) >> 4;
+
+       write_reg(vpdma, VPDMA_LIST_ADDR, (u32) list->buf.dma_addr);
+
+       write_reg(vpdma, VPDMA_LIST_ATTR,
+                       (list_num << VPDMA_LIST_NUM_SHFT) |
+                       (list->type << VPDMA_LIST_TYPE_SHFT) |
+                       list_size);
+
+       return 0;
+}
+
+static void dump_cfd(struct vpdma_cfd *cfd)
+{
+       int class;
+
+       class = cfd_get_class(cfd);
+
+       pr_debug("config descriptor of payload class: %s\n",
+               class == CFD_CLS_BLOCK ? "simple block" :
+               "address data block");
+
+       if (class == CFD_CLS_BLOCK)
+               pr_debug("word0: dst_addr_offset = 0x%08x\n",
+                       cfd->dest_addr_offset);
+
+       if (class == CFD_CLS_BLOCK)
+               pr_debug("word1: num_data_wrds = %d\n", cfd->block_len);
+
+       pr_debug("word2: payload_addr = 0x%08x\n", cfd->payload_addr);
+
+       pr_debug("word3: pkt_type = %d, direct = %d, class = %d, dest = %d, "
+               "payload_len = %d\n", cfd_get_pkt_type(cfd),
+               cfd_get_direct(cfd), class, cfd_get_dest(cfd),
+               cfd_get_payload_len(cfd));
+}
+
+/*
+ * append a configuration descriptor to the given descriptor list, where the
+ * payload is in the form of a simple data block specified in the descriptor
+ * header, this is used to upload scaler coefficients to the scaler module
+ */
+void vpdma_add_cfd_block(struct vpdma_desc_list *list, int client,
+               struct vpdma_buf *blk, u32 dest_offset)
+{
+       struct vpdma_cfd *cfd;
+       int len = blk->size;
+
+       WARN_ON(blk->dma_addr & VPDMA_DESC_ALIGN);
+
+       cfd = list->next;
+       WARN_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size));
+
+       cfd->dest_addr_offset = dest_offset;
+       cfd->block_len = len;
+       cfd->payload_addr = (u32) blk->dma_addr;
+       cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_BLOCK,
+                               client, len >> 4);
+
+       list->next = cfd + 1;
+
+       dump_cfd(cfd);
+}
+
+/*
+ * append a configuration descriptor to the given descriptor list, where the
+ * payload is in the address data block format, this is used to a configure a
+ * discontiguous set of MMRs
+ */
+void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client,
+               struct vpdma_buf *adb)
+{
+       struct vpdma_cfd *cfd;
+       unsigned int len = adb->size;
+
+       WARN_ON(len & VPDMA_ADB_SIZE_ALIGN);
+       WARN_ON(adb->dma_addr & VPDMA_DESC_ALIGN);
+
+       cfd = list->next;
+       BUG_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size));
+
+       cfd->w0 = 0;
+       cfd->w1 = 0;
+       cfd->payload_addr = (u32) adb->dma_addr;
+       cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_ADB,
+                               client, len >> 4);
+
+       list->next = cfd + 1;
+
+       dump_cfd(cfd);
+};
+
+/*
+ * control descriptor format change based on what type of control descriptor it
+ * is, we only use 'sync on channel' control descriptors for now, so assume it's
+ * that
+ */
+static void dump_ctd(struct vpdma_ctd *ctd)
+{
+       pr_debug("control descriptor\n");
+
+       pr_debug("word3: pkt_type = %d, source = %d, ctl_type = %d\n",
+               ctd_get_pkt_type(ctd), ctd_get_source(ctd), ctd_get_ctl(ctd));
+}
+
+/*
+ * append a 'sync on channel' type control descriptor to the given descriptor
+ * list, this descriptor stalls the VPDMA list till the time DMA is completed
+ * on the specified channel
+ */
+void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list,
+               enum vpdma_channel chan)
+{
+       struct vpdma_ctd *ctd;
+
+       ctd = list->next;
+       WARN_ON((void *)(ctd + 1) > (list->buf.addr + list->buf.size));
+
+       ctd->w0 = 0;
+       ctd->w1 = 0;
+       ctd->w2 = 0;
+       ctd->type_source_ctl = ctd_type_source_ctl(chan_info[chan].num,
+                               CTD_TYPE_SYNC_ON_CHANNEL);
+
+       list->next = ctd + 1;
+
+       dump_ctd(ctd);
+}
+
+static void dump_dtd(struct vpdma_dtd *dtd)
+{
+       int dir, chan;
+
+       dir = dtd_get_dir(dtd);
+       chan = dtd_get_chan(dtd);
+
+       pr_debug("%s data transfer descriptor for channel %d\n",
+               dir == DTD_DIR_OUT ? "outbound" : "inbound", chan);
+
+       pr_debug("word0: data_type = %d, notify = %d, field = %d, 1D = %d, "
+               "even_ln_skp = %d, odd_ln_skp = %d, line_stride = %d\n",
+               dtd_get_data_type(dtd), dtd_get_notify(dtd), dtd_get_field(dtd),
+               dtd_get_1d(dtd), dtd_get_even_line_skip(dtd),
+               dtd_get_odd_line_skip(dtd), dtd_get_line_stride(dtd));
+
+       if (dir == DTD_DIR_IN)
+               pr_debug("word1: line_length = %d, xfer_height = %d\n",
+                       dtd_get_line_length(dtd), dtd_get_xfer_height(dtd));
+
+       pr_debug("word2: start_addr = 0x%08x\n", dtd->start_addr);
+
+       pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, "
+               "pri = %d, next_chan = %d\n", dtd_get_pkt_type(dtd),
+               dtd_get_mode(dtd), dir, chan, dtd_get_priority(dtd),
+               dtd_get_next_chan(dtd));
+
+       if (dir == DTD_DIR_IN)
+               pr_debug("word4: frame_width = %d, frame_height = %d\n",
+                       dtd_get_frame_width(dtd), dtd_get_frame_height(dtd));
+       else
+               pr_debug("word4: desc_write_addr = 0x%08x, write_desc = %d, "
+                       "drp_data = %d, use_desc_reg = %d\n",
+                       dtd_get_desc_write_addr(dtd), dtd_get_write_desc(dtd),
+                       dtd_get_drop_data(dtd), dtd_get_use_desc(dtd));
+
+       if (dir == DTD_DIR_IN)
+               pr_debug("word5: hor_start = %d, ver_start = %d\n",
+                       dtd_get_h_start(dtd), dtd_get_v_start(dtd));
+       else
+               pr_debug("word5: max_width %d, max_height %d\n",
+                       dtd_get_max_width(dtd), dtd_get_max_height(dtd));
+
+       pr_debug("word6: client specfic attr0 = 0x%08x\n", dtd->client_attr0);
+       pr_debug("word7: client specfic attr1 = 0x%08x\n", dtd->client_attr1);
+}
+
+/*
+ * append an outbound data transfer descriptor to the given descriptor list,
+ * this sets up a 'client to memory' VPDMA transfer for the given VPDMA channel
+ */
+void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect,
+               const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
+               enum vpdma_channel chan, u32 flags)
+{
+       int priority = 0;
+       int field = 0;
+       int notify = 1;
+       int channel, next_chan;
+       int depth = fmt->depth;
+       int stride;
+       struct vpdma_dtd *dtd;
+
+       channel = next_chan = chan_info[chan].num;
+
+       if (fmt->data_type == DATA_TYPE_C420)
+               depth = 8;
+
+       stride = (depth * c_rect->width) >> 3;
+       dma_addr += (c_rect->left * depth) >> 3;
+
+       dtd = list->next;
+       WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size));
+
+       dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type,
+                                       notify,
+                                       field,
+                                       !!(flags & VPDMA_DATA_FRAME_1D),
+                                       !!(flags & VPDMA_DATA_EVEN_LINE_SKIP),
+                                       !!(flags & VPDMA_DATA_ODD_LINE_SKIP),
+                                       stride);
+       dtd->w1 = 0;
+       dtd->start_addr = (u32) dma_addr;
+       dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED),
+                               DTD_DIR_OUT, channel, priority, next_chan);
+       dtd->desc_write_addr = dtd_desc_write_addr(0, 0, 0, 0);
+       dtd->max_width_height = dtd_max_width_height(MAX_OUT_WIDTH_1920,
+                                       MAX_OUT_HEIGHT_1080);
+       dtd->client_attr0 = 0;
+       dtd->client_attr1 = 0;
+
+       list->next = dtd + 1;
+
+       dump_dtd(dtd);
+}
+
+/*
+ * append an inbound data transfer descriptor to the given descriptor list,
+ * this sets up a 'memory to client' VPDMA transfer for the given VPDMA channel
+ */
+void vpdma_add_in_dtd(struct vpdma_desc_list *list, int frame_width,
+               int frame_height, struct v4l2_rect *c_rect,
+               const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
+               enum vpdma_channel chan, int field, u32 flags)
+{
+       int priority = 0;
+       int notify = 1;
+       int depth = fmt->depth;
+       int channel, next_chan;
+       int stride;
+       int height = c_rect->height;
+       struct vpdma_dtd *dtd;
+
+       channel = next_chan = chan_info[chan].num;
+
+       if (fmt->data_type == DATA_TYPE_C420) {
+               height >>= 1;
+               frame_height >>= 1;
+               depth = 8;
+       }
+
+       stride = (depth * c_rect->width) >> 3;
+       dma_addr += (c_rect->left * depth) >> 3;
+
+       dtd = list->next;
+       WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size));
+
+       dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type,
+                                       notify,
+                                       field,
+                                       !!(flags & VPDMA_DATA_FRAME_1D),
+                                       !!(flags & VPDMA_DATA_EVEN_LINE_SKIP),
+                                       !!(flags & VPDMA_DATA_ODD_LINE_SKIP),
+                                       stride);
+
+       dtd->xfer_length_height = dtd_xfer_length_height(c_rect->width, height);
+       dtd->start_addr = (u32) dma_addr;
+       dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED),
+                               DTD_DIR_IN, channel, priority, next_chan);
+       dtd->frame_width_height = dtd_frame_width_height(frame_width,
+                                       frame_height);
+       dtd->start_h_v = dtd_start_h_v(c_rect->left, c_rect->top);
+       dtd->client_attr0 = 0;
+       dtd->client_attr1 = 0;
+
+       list->next = dtd + 1;
+
+       dump_dtd(dtd);
+}
+
+/* set or clear the mask for list complete interrupt */
+void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int list_num,
+               bool enable)
+{
+       u32 val;
+
+       val = read_reg(vpdma, VPDMA_INT_LIST0_MASK);
+       if (enable)
+               val |= (1 << (list_num * 2));
+       else
+               val &= ~(1 << (list_num * 2));
+       write_reg(vpdma, VPDMA_INT_LIST0_MASK, val);
+}
+
+/* clear previosuly occured list intterupts in the LIST_STAT register */
+void vpdma_clear_list_stat(struct vpdma_data *vpdma)
+{
+       write_reg(vpdma, VPDMA_INT_LIST0_STAT,
+               read_reg(vpdma, VPDMA_INT_LIST0_STAT));
+}
+
+/*
+ * configures the output mode of the line buffer for the given client, the
+ * line buffer content can either be mirrored(each line repeated twice) or
+ * passed to the client as is
+ */
+void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode,
+               enum vpdma_channel chan)
+{
+       int client_cstat = chan_info[chan].cstat_offset;
+
+       write_field_reg(vpdma, client_cstat, line_mode,
+               VPDMA_CSTAT_LINE_MODE_MASK, VPDMA_CSTAT_LINE_MODE_SHIFT);
+}
+
+/*
+ * configures the event which should trigger VPDMA transfer for the given
+ * client
+ */
+void vpdma_set_frame_start_event(struct vpdma_data *vpdma,
+               enum vpdma_frame_start_event fs_event,
+               enum vpdma_channel chan)
+{
+       int client_cstat = chan_info[chan].cstat_offset;
+
+       write_field_reg(vpdma, client_cstat, fs_event,
+               VPDMA_CSTAT_FRAME_START_MASK, VPDMA_CSTAT_FRAME_START_SHIFT);
+}
+
+static void vpdma_firmware_cb(const struct firmware *f, void *context)
+{
+       struct vpdma_data *vpdma = context;
+       struct vpdma_buf fw_dma_buf;
+       int i, r;
+
+       dev_dbg(&vpdma->pdev->dev, "firmware callback\n");
+
+       if (!f || !f->data) {
+               dev_err(&vpdma->pdev->dev, "couldn't get firmware\n");
+               return;
+       }
+
+       /* already initialized */
+       if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
+                       VPDMA_LIST_RDY_SHFT)) {
+               vpdma->ready = true;
+               return;
+       }
+
+       r = vpdma_alloc_desc_buf(&fw_dma_buf, f->size);
+       if (r) {
+               dev_err(&vpdma->pdev->dev,
+                       "failed to allocate dma buffer for firmware\n");
+               goto rel_fw;
+       }
+
+       memcpy(fw_dma_buf.addr, f->data, f->size);
+
+       vpdma_map_desc_buf(vpdma, &fw_dma_buf);
+
+       write_reg(vpdma, VPDMA_LIST_ADDR, (u32) fw_dma_buf.dma_addr);
+
+       for (i = 0; i < 100; i++) {             /* max 1 second */
+               msleep_interruptible(10);
+
+               if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
+                               VPDMA_LIST_RDY_SHFT))
+                       break;
+       }
+
+       if (i == 100) {
+               dev_err(&vpdma->pdev->dev, "firmware upload failed\n");
+               goto free_buf;
+       }
+
+       vpdma->ready = true;
+
+free_buf:
+       vpdma_unmap_desc_buf(vpdma, &fw_dma_buf);
+
+       vpdma_free_desc_buf(&fw_dma_buf);
+rel_fw:
+       release_firmware(f);
+}
+
+static int vpdma_load_firmware(struct vpdma_data *vpdma)
+{
+       int r;
+       struct device *dev = &vpdma->pdev->dev;
+
+       r = request_firmware_nowait(THIS_MODULE, 1,
+               (const char *) VPDMA_FIRMWARE, dev, GFP_KERNEL, vpdma,
+               vpdma_firmware_cb);
+       if (r) {
+               dev_err(dev, "firmware not available %s\n", VPDMA_FIRMWARE);
+               return r;
+       } else {
+               dev_info(dev, "loading firmware %s\n", VPDMA_FIRMWARE);
+       }
+
+       return 0;
+}
+
+struct vpdma_data *vpdma_create(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct vpdma_data *vpdma;
+       int r;
+
+       dev_dbg(&pdev->dev, "vpdma_create\n");
+
+       vpdma = devm_kzalloc(&pdev->dev, sizeof(*vpdma), GFP_KERNEL);
+       if (!vpdma) {
+               dev_err(&pdev->dev, "couldn't alloc vpdma_dev\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       vpdma->pdev = pdev;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma");
+       if (res == NULL) {
+               dev_err(&pdev->dev, "missing platform resources data\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       vpdma->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (!vpdma->base) {
+               dev_err(&pdev->dev, "failed to ioremap\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       r = vpdma_load_firmware(vpdma);
+       if (r) {
+               pr_err("failed to load firmware %s\n", VPDMA_FIRMWARE);
+               return ERR_PTR(r);
+       }
+
+       return vpdma;
+}
+MODULE_FIRMWARE(VPDMA_FIRMWARE);
diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h
new file mode 100644 (file)
index 0000000..eaa2a71
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __TI_VPDMA_H_
+#define __TI_VPDMA_H_
+
+/*
+ * A vpdma_buf tracks the size, DMA address and mapping status of each
+ * driver DMA area.
+ */
+struct vpdma_buf {
+       void                    *addr;
+       dma_addr_t              dma_addr;
+       size_t                  size;
+       bool                    mapped;
+};
+
+struct vpdma_desc_list {
+       struct vpdma_buf buf;
+       void *next;
+       int type;
+};
+
+struct vpdma_data {
+       void __iomem            *base;
+
+       struct platform_device  *pdev;
+
+       /* tells whether vpdma firmware is loaded or not */
+       bool ready;
+};
+
+struct vpdma_data_format {
+       int data_type;
+       u8 depth;
+};
+
+#define VPDMA_DESC_ALIGN               16      /* 16-byte descriptor alignment */
+
+#define VPDMA_DTD_DESC_SIZE            32      /* 8 words */
+#define VPDMA_CFD_CTD_DESC_SIZE                16      /* 4 words */
+
+#define VPDMA_LIST_TYPE_NORMAL         0
+#define VPDMA_LIST_TYPE_SELF_MODIFYING 1
+#define VPDMA_LIST_TYPE_DOORBELL       2
+
+enum vpdma_yuv_formats {
+       VPDMA_DATA_FMT_Y444 = 0,
+       VPDMA_DATA_FMT_Y422,
+       VPDMA_DATA_FMT_Y420,
+       VPDMA_DATA_FMT_C444,
+       VPDMA_DATA_FMT_C422,
+       VPDMA_DATA_FMT_C420,
+       VPDMA_DATA_FMT_YC422,
+       VPDMA_DATA_FMT_YC444,
+       VPDMA_DATA_FMT_CY422,
+};
+
+enum vpdma_rgb_formats {
+       VPDMA_DATA_FMT_RGB565 = 0,
+       VPDMA_DATA_FMT_ARGB16_1555,
+       VPDMA_DATA_FMT_ARGB16,
+       VPDMA_DATA_FMT_RGBA16_5551,
+       VPDMA_DATA_FMT_RGBA16,
+       VPDMA_DATA_FMT_ARGB24,
+       VPDMA_DATA_FMT_RGB24,
+       VPDMA_DATA_FMT_ARGB32,
+       VPDMA_DATA_FMT_RGBA24,
+       VPDMA_DATA_FMT_RGBA32,
+       VPDMA_DATA_FMT_BGR565,
+       VPDMA_DATA_FMT_ABGR16_1555,
+       VPDMA_DATA_FMT_ABGR16,
+       VPDMA_DATA_FMT_BGRA16_5551,
+       VPDMA_DATA_FMT_BGRA16,
+       VPDMA_DATA_FMT_ABGR24,
+       VPDMA_DATA_FMT_BGR24,
+       VPDMA_DATA_FMT_ABGR32,
+       VPDMA_DATA_FMT_BGRA24,
+       VPDMA_DATA_FMT_BGRA32,
+};
+
+enum vpdma_misc_formats {
+       VPDMA_DATA_FMT_MV = 0,
+};
+
+extern const struct vpdma_data_format vpdma_yuv_fmts[];
+extern const struct vpdma_data_format vpdma_rgb_fmts[];
+extern const struct vpdma_data_format vpdma_misc_fmts[];
+
+enum vpdma_frame_start_event {
+       VPDMA_FSEVENT_HDMI_FID = 0,
+       VPDMA_FSEVENT_DVO2_FID,
+       VPDMA_FSEVENT_HDCOMP_FID,
+       VPDMA_FSEVENT_SD_FID,
+       VPDMA_FSEVENT_LM_FID0,
+       VPDMA_FSEVENT_LM_FID1,
+       VPDMA_FSEVENT_LM_FID2,
+       VPDMA_FSEVENT_CHANNEL_ACTIVE,
+};
+
+/*
+ * VPDMA channel numbers
+ */
+enum vpdma_channel {
+       VPE_CHAN_LUMA1_IN,
+       VPE_CHAN_CHROMA1_IN,
+       VPE_CHAN_LUMA2_IN,
+       VPE_CHAN_CHROMA2_IN,
+       VPE_CHAN_LUMA3_IN,
+       VPE_CHAN_CHROMA3_IN,
+       VPE_CHAN_MV_IN,
+       VPE_CHAN_MV_OUT,
+       VPE_CHAN_LUMA_OUT,
+       VPE_CHAN_CHROMA_OUT,
+       VPE_CHAN_RGB_OUT,
+};
+
+/* flags for VPDMA data descriptors */
+#define VPDMA_DATA_ODD_LINE_SKIP       (1 << 0)
+#define VPDMA_DATA_EVEN_LINE_SKIP      (1 << 1)
+#define VPDMA_DATA_FRAME_1D            (1 << 2)
+#define VPDMA_DATA_MODE_TILED          (1 << 3)
+
+/*
+ * client identifiers used for configuration descriptors
+ */
+#define CFD_MMR_CLIENT         0
+#define CFD_SC_CLIENT          4
+
+/* Address data block header format */
+struct vpdma_adb_hdr {
+       u32                     offset;
+       u32                     nwords;
+       u32                     reserved0;
+       u32                     reserved1;
+};
+
+/* helpers for creating ADB headers for config descriptors MMRs as client */
+#define ADB_ADDR(dma_buf, str, fld)    ((dma_buf)->addr + offsetof(str, fld))
+#define MMR_ADB_ADDR(buf, str, fld)    ADB_ADDR(&(buf), struct str, fld)
+
+#define VPDMA_SET_MMR_ADB_HDR(buf, str, hdr, regs, offset_a)   \
+       do {                                                    \
+               struct vpdma_adb_hdr *h;                        \
+               struct str *adb = NULL;                         \
+               h = MMR_ADB_ADDR(buf, str, hdr);                \
+               h->offset = (offset_a);                         \
+               h->nwords = sizeof(adb->regs) >> 2;             \
+       } while (0)
+
+/* vpdma descriptor buffer allocation and management */
+int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size);
+void vpdma_free_desc_buf(struct vpdma_buf *buf);
+int vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf);
+void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf);
+
+/* vpdma descriptor list funcs */
+int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type);
+void vpdma_reset_desc_list(struct vpdma_desc_list *list);
+void vpdma_free_desc_list(struct vpdma_desc_list *list);
+int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list);
+
+/* helpers for creating vpdma descriptors */
+void vpdma_add_cfd_block(struct vpdma_desc_list *list, int client,
+               struct vpdma_buf *blk, u32 dest_offset);
+void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client,
+               struct vpdma_buf *adb);
+void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list,
+               enum vpdma_channel chan);
+void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect,
+               const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
+               enum vpdma_channel chan, u32 flags);
+void vpdma_add_in_dtd(struct vpdma_desc_list *list, int frame_width,
+               int frame_height, struct v4l2_rect *c_rect,
+               const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
+               enum vpdma_channel chan, int field, u32 flags);
+
+/* vpdma list interrupt management */
+void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int list_num,
+               bool enable);
+void vpdma_clear_list_stat(struct vpdma_data *vpdma);
+
+/* vpdma client configuration */
+void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode,
+               enum vpdma_channel chan);
+void vpdma_set_frame_start_event(struct vpdma_data *vpdma,
+               enum vpdma_frame_start_event fs_event, enum vpdma_channel chan);
+
+void vpdma_dump_regs(struct vpdma_data *vpdma);
+
+/* initialize vpdma, passed with VPE's platform device pointer */
+struct vpdma_data *vpdma_create(struct platform_device *pdev);
+
+#endif
diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti-vpe/vpdma_priv.h
new file mode 100644 (file)
index 0000000..f0e9a80
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _TI_VPDMA_PRIV_H_
+#define _TI_VPDMA_PRIV_H_
+
+/*
+ * VPDMA Register offsets
+ */
+
+/* Top level */
+#define VPDMA_PID              0x00
+#define VPDMA_LIST_ADDR                0x04
+#define VPDMA_LIST_ATTR                0x08
+#define VPDMA_LIST_STAT_SYNC   0x0c
+#define VPDMA_BG_RGB           0x18
+#define VPDMA_BG_YUV           0x1c
+#define VPDMA_SETUP            0x30
+#define VPDMA_MAX_SIZE1                0x34
+#define VPDMA_MAX_SIZE2                0x38
+#define VPDMA_MAX_SIZE3                0x3c
+
+/* Interrupts */
+#define VPDMA_INT_CHAN_STAT(grp)       (0x40 + grp * 8)
+#define VPDMA_INT_CHAN_MASK(grp)       (VPDMA_INT_CHAN_STAT(grp) + 4)
+#define VPDMA_INT_CLIENT0_STAT         0x78
+#define VPDMA_INT_CLIENT0_MASK         0x7c
+#define VPDMA_INT_CLIENT1_STAT         0x80
+#define VPDMA_INT_CLIENT1_MASK         0x84
+#define VPDMA_INT_LIST0_STAT           0x88
+#define VPDMA_INT_LIST0_MASK           0x8c
+
+#define VPDMA_PERFMON(i)               (0x200 + i * 4)
+
+/* VPE specific client registers */
+#define VPDMA_DEI_CHROMA1_CSTAT                0x0300
+#define VPDMA_DEI_LUMA1_CSTAT          0x0304
+#define VPDMA_DEI_LUMA2_CSTAT          0x0308
+#define VPDMA_DEI_CHROMA2_CSTAT                0x030c
+#define VPDMA_DEI_LUMA3_CSTAT          0x0310
+#define VPDMA_DEI_CHROMA3_CSTAT                0x0314
+#define VPDMA_DEI_MV_IN_CSTAT          0x0330
+#define VPDMA_DEI_MV_OUT_CSTAT         0x033c
+#define VPDMA_VIP_UP_Y_CSTAT           0x0390
+#define VPDMA_VIP_UP_UV_CSTAT          0x0394
+#define VPDMA_VPI_CTL_CSTAT            0x03d0
+
+/* Reg field info for VPDMA_CLIENT_CSTAT registers */
+#define VPDMA_CSTAT_LINE_MODE_MASK     0x03
+#define VPDMA_CSTAT_LINE_MODE_SHIFT    8
+#define VPDMA_CSTAT_FRAME_START_MASK   0xf
+#define VPDMA_CSTAT_FRAME_START_SHIFT  10
+
+#define VPDMA_LIST_NUM_MASK            0x07
+#define VPDMA_LIST_NUM_SHFT            24
+#define VPDMA_LIST_STOP_SHFT           20
+#define VPDMA_LIST_RDY_MASK            0x01
+#define VPDMA_LIST_RDY_SHFT            19
+#define VPDMA_LIST_TYPE_MASK           0x03
+#define VPDMA_LIST_TYPE_SHFT           16
+#define VPDMA_LIST_SIZE_MASK           0xffff
+
+/* VPDMA data type values for data formats */
+#define DATA_TYPE_Y444                         0x0
+#define DATA_TYPE_Y422                         0x1
+#define DATA_TYPE_Y420                         0x2
+#define DATA_TYPE_C444                         0x4
+#define DATA_TYPE_C422                         0x5
+#define DATA_TYPE_C420                         0x6
+#define DATA_TYPE_YC422                                0x7
+#define DATA_TYPE_YC444                                0x8
+#define DATA_TYPE_CY422                                0x23
+
+#define DATA_TYPE_RGB16_565                    0x0
+#define DATA_TYPE_ARGB_1555                    0x1
+#define DATA_TYPE_ARGB_4444                    0x2
+#define DATA_TYPE_RGBA_5551                    0x3
+#define DATA_TYPE_RGBA_4444                    0x4
+#define DATA_TYPE_ARGB24_6666                  0x5
+#define DATA_TYPE_RGB24_888                    0x6
+#define DATA_TYPE_ARGB32_8888                  0x7
+#define DATA_TYPE_RGBA24_6666                  0x8
+#define DATA_TYPE_RGBA32_8888                  0x9
+#define DATA_TYPE_BGR16_565                    0x10
+#define DATA_TYPE_ABGR_1555                    0x11
+#define DATA_TYPE_ABGR_4444                    0x12
+#define DATA_TYPE_BGRA_5551                    0x13
+#define DATA_TYPE_BGRA_4444                    0x14
+#define DATA_TYPE_ABGR24_6666                  0x15
+#define DATA_TYPE_BGR24_888                    0x16
+#define DATA_TYPE_ABGR32_8888                  0x17
+#define DATA_TYPE_BGRA24_6666                  0x18
+#define DATA_TYPE_BGRA32_8888                  0x19
+
+#define DATA_TYPE_MV                           0x3
+
+/* VPDMA channel numbers(only VPE channels for now) */
+#define        VPE_CHAN_NUM_LUMA1_IN           0
+#define        VPE_CHAN_NUM_CHROMA1_IN         1
+#define        VPE_CHAN_NUM_LUMA2_IN           2
+#define        VPE_CHAN_NUM_CHROMA2_IN         3
+#define        VPE_CHAN_NUM_LUMA3_IN           4
+#define        VPE_CHAN_NUM_CHROMA3_IN         5
+#define        VPE_CHAN_NUM_MV_IN              12
+#define        VPE_CHAN_NUM_MV_OUT             15
+#define        VPE_CHAN_NUM_LUMA_OUT           102
+#define        VPE_CHAN_NUM_CHROMA_OUT         103
+#define        VPE_CHAN_NUM_RGB_OUT            106
+
+/*
+ * a VPDMA address data block payload for a configuration descriptor needs to
+ * have each sub block length as a multiple of 16 bytes. Therefore, the overall
+ * size of the payload also needs to be a multiple of 16 bytes. The sub block
+ * lengths should be ensured to be aligned by the VPDMA user.
+ */
+#define VPDMA_ADB_SIZE_ALIGN           0x0f
+
+/*
+ * data transfer descriptor
+ */
+struct vpdma_dtd {
+       u32                     type_ctl_stride;
+       union {
+               u32             xfer_length_height;
+               u32             w1;
+       };
+       dma_addr_t              start_addr;
+       u32                     pkt_ctl;
+       union {
+               u32             frame_width_height;     /* inbound */
+               dma_addr_t      desc_write_addr;        /* outbound */
+       };
+       union {
+               u32             start_h_v;              /* inbound */
+               u32             max_width_height;       /* outbound */
+       };
+       u32                     client_attr0;
+       u32                     client_attr1;
+};
+
+/* Data Transfer Descriptor specifics */
+#define DTD_NO_NOTIFY          0
+#define DTD_NOTIFY             1
+
+#define DTD_PKT_TYPE           0xa
+#define DTD_DIR_IN             0
+#define DTD_DIR_OUT            1
+
+/* type_ctl_stride */
+#define DTD_DATA_TYPE_MASK     0x3f
+#define DTD_DATA_TYPE_SHFT     26
+#define DTD_NOTIFY_MASK                0x01
+#define DTD_NOTIFY_SHFT                25
+#define DTD_FIELD_MASK         0x01
+#define DTD_FIELD_SHFT         24
+#define DTD_1D_MASK            0x01
+#define DTD_1D_SHFT            23
+#define DTD_EVEN_LINE_SKIP_MASK        0x01
+#define DTD_EVEN_LINE_SKIP_SHFT        20
+#define DTD_ODD_LINE_SKIP_MASK 0x01
+#define DTD_ODD_LINE_SKIP_SHFT 16
+#define DTD_LINE_STRIDE_MASK   0xffff
+#define DTD_LINE_STRIDE_SHFT   0
+
+/* xfer_length_height */
+#define DTD_LINE_LENGTH_MASK   0xffff
+#define DTD_LINE_LENGTH_SHFT   16
+#define DTD_XFER_HEIGHT_MASK   0xffff
+#define DTD_XFER_HEIGHT_SHFT   0
+
+/* pkt_ctl */
+#define DTD_PKT_TYPE_MASK      0x1f
+#define DTD_PKT_TYPE_SHFT      27
+#define DTD_MODE_MASK          0x01
+#define DTD_MODE_SHFT          26
+#define DTD_DIR_MASK           0x01
+#define DTD_DIR_SHFT           25
+#define DTD_CHAN_MASK          0x01ff
+#define DTD_CHAN_SHFT          16
+#define DTD_PRI_MASK           0x0f
+#define DTD_PRI_SHFT           9
+#define DTD_NEXT_CHAN_MASK     0x01ff
+#define DTD_NEXT_CHAN_SHFT     0
+
+/* frame_width_height */
+#define DTD_FRAME_WIDTH_MASK   0xffff
+#define DTD_FRAME_WIDTH_SHFT   16
+#define DTD_FRAME_HEIGHT_MASK  0xffff
+#define DTD_FRAME_HEIGHT_SHFT  0
+
+/* start_h_v */
+#define DTD_H_START_MASK       0xffff
+#define DTD_H_START_SHFT       16
+#define DTD_V_START_MASK       0xffff
+#define DTD_V_START_SHFT       0
+
+#define DTD_DESC_START_SHIFT   5
+#define DTD_WRITE_DESC_MASK    0x01
+#define DTD_WRITE_DESC_SHIFT   2
+#define DTD_DROP_DATA_MASK     0x01
+#define DTD_DROP_DATA_SHIFT    1
+#define DTD_USE_DESC_MASK      0x01
+#define DTD_USE_DESC_SHIFT     0
+
+/* max_width_height */
+#define DTD_MAX_WIDTH_MASK     0x07
+#define DTD_MAX_WIDTH_SHFT     4
+#define DTD_MAX_HEIGHT_MASK    0x07
+#define DTD_MAX_HEIGHT_SHFT    0
+
+/* max width configurations */
+ /* unlimited width */
+#define        MAX_OUT_WIDTH_UNLIMITED         0
+/* as specified in max_size1 reg */
+#define MAX_OUT_WIDTH_REG1             1
+/* as specified in max_size2 reg */
+#define MAX_OUT_WIDTH_REG2             2
+/* as specified in max_size3 reg */
+#define        MAX_OUT_WIDTH_REG3              3
+/* maximum of 352 pixels as width */
+#define MAX_OUT_WIDTH_352              4
+/* maximum of 768 pixels as width */
+#define        MAX_OUT_WIDTH_768               5
+/* maximum of 1280 pixels width */
+#define        MAX_OUT_WIDTH_1280              6
+/* maximum of 1920 pixels as width */
+#define        MAX_OUT_WIDTH_1920              7
+
+/* max height configurations */
+ /* unlimited height */
+#define        MAX_OUT_HEIGHT_UNLIMITED        0
+/* as specified in max_size1 reg */
+#define MAX_OUT_HEIGHT_REG1            1
+/* as specified in max_size2 reg */
+#define MAX_OUT_HEIGHT_REG2            2
+/* as specified in max_size3 reg */
+#define        MAX_OUT_HEIGHT_REG3             3
+/* maximum of 288 lines as height */
+#define MAX_OUT_HEIGHT_288             4
+/* maximum of 576 lines as height */
+#define        MAX_OUT_HEIGHT_576              5
+/* maximum of 720 lines as height */
+#define        MAX_OUT_HEIGHT_720              6
+/* maximum of 1080 lines as height */
+#define        MAX_OUT_HEIGHT_1080             7
+
+static inline u32 dtd_type_ctl_stride(int type, bool notify, int field,
+                       bool one_d, bool even_line_skip, bool odd_line_skip,
+                       int line_stride)
+{
+       return (type << DTD_DATA_TYPE_SHFT) | (notify << DTD_NOTIFY_SHFT) |
+               (field << DTD_FIELD_SHFT) | (one_d << DTD_1D_SHFT) |
+               (even_line_skip << DTD_EVEN_LINE_SKIP_SHFT) |
+               (odd_line_skip << DTD_ODD_LINE_SKIP_SHFT) |
+               line_stride;
+}
+
+static inline u32 dtd_xfer_length_height(int line_length, int xfer_height)
+{
+       return (line_length << DTD_LINE_LENGTH_SHFT) | xfer_height;
+}
+
+static inline u32 dtd_pkt_ctl(bool mode, bool dir, int chan, int pri,
+                       int next_chan)
+{
+       return (DTD_PKT_TYPE << DTD_PKT_TYPE_SHFT) | (mode << DTD_MODE_SHFT) |
+               (dir << DTD_DIR_SHFT) | (chan << DTD_CHAN_SHFT) |
+               (pri << DTD_PRI_SHFT) | next_chan;
+}
+
+static inline u32 dtd_frame_width_height(int width, int height)
+{
+       return (width << DTD_FRAME_WIDTH_SHFT) | height;
+}
+
+static inline u32 dtd_desc_write_addr(unsigned int addr, bool write_desc,
+                       bool drop_data, bool use_desc)
+{
+       return (addr << DTD_DESC_START_SHIFT) |
+               (write_desc << DTD_WRITE_DESC_SHIFT) |
+               (drop_data << DTD_DROP_DATA_SHIFT) |
+               use_desc;
+}
+
+static inline u32 dtd_start_h_v(int h_start, int v_start)
+{
+       return (h_start << DTD_H_START_SHFT) | v_start;
+}
+
+static inline u32 dtd_max_width_height(int max_width, int max_height)
+{
+       return (max_width << DTD_MAX_WIDTH_SHFT) | max_height;
+}
+
+static inline int dtd_get_data_type(struct vpdma_dtd *dtd)
+{
+       return dtd->type_ctl_stride >> DTD_DATA_TYPE_SHFT;
+}
+
+static inline bool dtd_get_notify(struct vpdma_dtd *dtd)
+{
+       return (dtd->type_ctl_stride >> DTD_NOTIFY_SHFT) & DTD_NOTIFY_MASK;
+}
+
+static inline int dtd_get_field(struct vpdma_dtd *dtd)
+{
+       return (dtd->type_ctl_stride >> DTD_FIELD_SHFT) & DTD_FIELD_MASK;
+}
+
+static inline bool dtd_get_1d(struct vpdma_dtd *dtd)
+{
+       return (dtd->type_ctl_stride >> DTD_1D_SHFT) & DTD_1D_MASK;
+}
+
+static inline bool dtd_get_even_line_skip(struct vpdma_dtd *dtd)
+{
+       return (dtd->type_ctl_stride >> DTD_EVEN_LINE_SKIP_SHFT)
+               & DTD_EVEN_LINE_SKIP_MASK;
+}
+
+static inline bool dtd_get_odd_line_skip(struct vpdma_dtd *dtd)
+{
+       return (dtd->type_ctl_stride >> DTD_ODD_LINE_SKIP_SHFT)
+               & DTD_ODD_LINE_SKIP_MASK;
+}
+
+static inline int dtd_get_line_stride(struct vpdma_dtd *dtd)
+{
+       return dtd->type_ctl_stride & DTD_LINE_STRIDE_MASK;
+}
+
+static inline int dtd_get_line_length(struct vpdma_dtd *dtd)
+{
+       return dtd->xfer_length_height >> DTD_LINE_LENGTH_SHFT;
+}
+
+static inline int dtd_get_xfer_height(struct vpdma_dtd *dtd)
+{
+       return dtd->xfer_length_height & DTD_XFER_HEIGHT_MASK;
+}
+
+static inline int dtd_get_pkt_type(struct vpdma_dtd *dtd)
+{
+       return dtd->pkt_ctl >> DTD_PKT_TYPE_SHFT;
+}
+
+static inline bool dtd_get_mode(struct vpdma_dtd *dtd)
+{
+       return (dtd->pkt_ctl >> DTD_MODE_SHFT) & DTD_MODE_MASK;
+}
+
+static inline bool dtd_get_dir(struct vpdma_dtd *dtd)
+{
+       return (dtd->pkt_ctl >> DTD_DIR_SHFT) & DTD_DIR_MASK;
+}
+
+static inline int dtd_get_chan(struct vpdma_dtd *dtd)
+{
+       return (dtd->pkt_ctl >> DTD_CHAN_SHFT) & DTD_CHAN_MASK;
+}
+
+static inline int dtd_get_priority(struct vpdma_dtd *dtd)
+{
+       return (dtd->pkt_ctl >> DTD_PRI_SHFT) & DTD_PRI_MASK;
+}
+
+static inline int dtd_get_next_chan(struct vpdma_dtd *dtd)
+{
+       return (dtd->pkt_ctl >> DTD_NEXT_CHAN_SHFT) & DTD_NEXT_CHAN_MASK;
+}
+
+static inline int dtd_get_frame_width(struct vpdma_dtd *dtd)
+{
+       return dtd->frame_width_height >> DTD_FRAME_WIDTH_SHFT;
+}
+
+static inline int dtd_get_frame_height(struct vpdma_dtd *dtd)
+{
+       return dtd->frame_width_height & DTD_FRAME_HEIGHT_MASK;
+}
+
+static inline int dtd_get_desc_write_addr(struct vpdma_dtd *dtd)
+{
+       return dtd->desc_write_addr >> DTD_DESC_START_SHIFT;
+}
+
+static inline bool dtd_get_write_desc(struct vpdma_dtd *dtd)
+{
+       return (dtd->desc_write_addr >> DTD_WRITE_DESC_SHIFT) &
+                                                       DTD_WRITE_DESC_MASK;
+}
+
+static inline bool dtd_get_drop_data(struct vpdma_dtd *dtd)
+{
+       return (dtd->desc_write_addr >> DTD_DROP_DATA_SHIFT) &
+                                                       DTD_DROP_DATA_MASK;
+}
+
+static inline bool dtd_get_use_desc(struct vpdma_dtd *dtd)
+{
+       return dtd->desc_write_addr & DTD_USE_DESC_MASK;
+}
+
+static inline int dtd_get_h_start(struct vpdma_dtd *dtd)
+{
+       return dtd->start_h_v >> DTD_H_START_SHFT;
+}
+
+static inline int dtd_get_v_start(struct vpdma_dtd *dtd)
+{
+       return dtd->start_h_v & DTD_V_START_MASK;
+}
+
+static inline int dtd_get_max_width(struct vpdma_dtd *dtd)
+{
+       return (dtd->max_width_height >> DTD_MAX_WIDTH_SHFT) &
+                                                       DTD_MAX_WIDTH_MASK;
+}
+
+static inline int dtd_get_max_height(struct vpdma_dtd *dtd)
+{
+       return (dtd->max_width_height >> DTD_MAX_HEIGHT_SHFT) &
+                                                       DTD_MAX_HEIGHT_MASK;
+}
+
+/*
+ * configuration descriptor
+ */
+struct vpdma_cfd {
+       union {
+               u32     dest_addr_offset;
+               u32     w0;
+       };
+       union {
+               u32     block_len;              /* in words */
+               u32     w1;
+       };
+       u32             payload_addr;
+       u32             ctl_payload_len;        /* in words */
+};
+
+/* Configuration descriptor specifics */
+
+#define CFD_PKT_TYPE           0xb
+
+#define CFD_DIRECT             1
+#define CFD_INDIRECT           0
+#define CFD_CLS_ADB            0
+#define CFD_CLS_BLOCK          1
+
+/* block_len */
+#define CFD__BLOCK_LEN_MASK    0xffff
+#define CFD__BLOCK_LEN_SHFT    0
+
+/* ctl_payload_len */
+#define CFD_PKT_TYPE_MASK      0x1f
+#define CFD_PKT_TYPE_SHFT      27
+#define CFD_DIRECT_MASK                0x01
+#define CFD_DIRECT_SHFT                26
+#define CFD_CLASS_MASK         0x03
+#define CFD_CLASS_SHFT         24
+#define CFD_DEST_MASK          0xff
+#define CFD_DEST_SHFT          16
+#define CFD_PAYLOAD_LEN_MASK   0xffff
+#define CFD_PAYLOAD_LEN_SHFT   0
+
+static inline u32 cfd_pkt_payload_len(bool direct, int cls, int dest,
+               int payload_len)
+{
+       return (CFD_PKT_TYPE << CFD_PKT_TYPE_SHFT) |
+               (direct << CFD_DIRECT_SHFT) |
+               (cls << CFD_CLASS_SHFT) |
+               (dest << CFD_DEST_SHFT) |
+               payload_len;
+}
+
+static inline int cfd_get_pkt_type(struct vpdma_cfd *cfd)
+{
+       return cfd->ctl_payload_len >> CFD_PKT_TYPE_SHFT;
+}
+
+static inline bool cfd_get_direct(struct vpdma_cfd *cfd)
+{
+       return (cfd->ctl_payload_len >> CFD_DIRECT_SHFT) & CFD_DIRECT_MASK;
+}
+
+static inline bool cfd_get_class(struct vpdma_cfd *cfd)
+{
+       return (cfd->ctl_payload_len >> CFD_CLASS_SHFT) & CFD_CLASS_MASK;
+}
+
+static inline int cfd_get_dest(struct vpdma_cfd *cfd)
+{
+       return (cfd->ctl_payload_len >> CFD_DEST_SHFT) & CFD_DEST_MASK;
+}
+
+static inline int cfd_get_payload_len(struct vpdma_cfd *cfd)
+{
+       return cfd->ctl_payload_len & CFD_PAYLOAD_LEN_MASK;
+}
+
+/*
+ * control descriptor
+ */
+struct vpdma_ctd {
+       union {
+               u32     timer_value;
+               u32     list_addr;
+               u32     w0;
+       };
+       union {
+               u32     pixel_line_count;
+               u32     list_size;
+               u32     w1;
+       };
+       union {
+               u32     event;
+               u32     fid_ctl;
+               u32     w2;
+       };
+       u32             type_source_ctl;
+};
+
+/* control descriptor types */
+#define CTD_TYPE_SYNC_ON_CLIENT                0
+#define CTD_TYPE_SYNC_ON_LIST          1
+#define CTD_TYPE_SYNC_ON_EXT           2
+#define CTD_TYPE_SYNC_ON_LM_TIMER      3
+#define CTD_TYPE_SYNC_ON_CHANNEL       4
+#define CTD_TYPE_CHNG_CLIENT_IRQ       5
+#define CTD_TYPE_SEND_IRQ              6
+#define CTD_TYPE_RELOAD_LIST           7
+#define CTD_TYPE_ABORT_CHANNEL         8
+
+#define CTD_PKT_TYPE           0xc
+
+/* timer_value */
+#define CTD_TIMER_VALUE_MASK   0xffff
+#define CTD_TIMER_VALUE_SHFT   0
+
+/* pixel_line_count */
+#define CTD_PIXEL_COUNT_MASK   0xffff
+#define CTD_PIXEL_COUNT_SHFT   16
+#define CTD_LINE_COUNT_MASK    0xffff
+#define CTD_LINE_COUNT_SHFT    0
+
+/* list_size */
+#define CTD_LIST_SIZE_MASK     0xffff
+#define CTD_LIST_SIZE_SHFT     0
+
+/* event */
+#define CTD_EVENT_MASK         0x0f
+#define CTD_EVENT_SHFT         0
+
+/* fid_ctl */
+#define CTD_FID2_MASK          0x03
+#define CTD_FID2_SHFT          4
+#define CTD_FID1_MASK          0x03
+#define CTD_FID1_SHFT          2
+#define CTD_FID0_MASK          0x03
+#define CTD_FID0_SHFT          0
+
+/* type_source_ctl */
+#define CTD_PKT_TYPE_MASK      0x1f
+#define CTD_PKT_TYPE_SHFT      27
+#define CTD_SOURCE_MASK                0xff
+#define CTD_SOURCE_SHFT                16
+#define CTD_CONTROL_MASK       0x0f
+#define CTD_CONTROL_SHFT       0
+
+static inline u32 ctd_pixel_line_count(int pixel_count, int line_count)
+{
+       return (pixel_count << CTD_PIXEL_COUNT_SHFT) | line_count;
+}
+
+static inline u32 ctd_set_fid_ctl(int fid0, int fid1, int fid2)
+{
+       return (fid2 << CTD_FID2_SHFT) | (fid1 << CTD_FID1_SHFT) | fid0;
+}
+
+static inline u32 ctd_type_source_ctl(int source, int control)
+{
+       return (CTD_PKT_TYPE << CTD_PKT_TYPE_SHFT) |
+               (source << CTD_SOURCE_SHFT) | control;
+}
+
+static inline u32 ctd_get_pixel_count(struct vpdma_ctd *ctd)
+{
+       return ctd->pixel_line_count >> CTD_PIXEL_COUNT_SHFT;
+}
+
+static inline int ctd_get_line_count(struct vpdma_ctd *ctd)
+{
+       return ctd->pixel_line_count & CTD_LINE_COUNT_MASK;
+}
+
+static inline int ctd_get_event(struct vpdma_ctd *ctd)
+{
+       return ctd->event & CTD_EVENT_MASK;
+}
+
+static inline int ctd_get_fid2_ctl(struct vpdma_ctd *ctd)
+{
+       return (ctd->fid_ctl >> CTD_FID2_SHFT) & CTD_FID2_MASK;
+}
+
+static inline int ctd_get_fid1_ctl(struct vpdma_ctd *ctd)
+{
+       return (ctd->fid_ctl >> CTD_FID1_SHFT) & CTD_FID1_MASK;
+}
+
+static inline int ctd_get_fid0_ctl(struct vpdma_ctd *ctd)
+{
+       return ctd->fid_ctl & CTD_FID2_MASK;
+}
+
+static inline int ctd_get_pkt_type(struct vpdma_ctd *ctd)
+{
+       return ctd->type_source_ctl >> CTD_PKT_TYPE_SHFT;
+}
+
+static inline int ctd_get_source(struct vpdma_ctd *ctd)
+{
+       return (ctd->type_source_ctl >> CTD_SOURCE_SHFT) & CTD_SOURCE_MASK;
+}
+
+static inline int ctd_get_ctl(struct vpdma_ctd *ctd)
+{
+       return ctd->type_source_ctl & CTD_CONTROL_MASK;
+}
+
+#endif
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
new file mode 100644 (file)
index 0000000..4e58069
--- /dev/null
@@ -0,0 +1,2099 @@
+/*
+ * TI VPE mem2mem driver, based on the virtual v4l2-mem2mem example driver
+ *
+ * Copyright (c) 2013 Texas Instruments Inc.
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * Based on the virtual v4l2-mem2mem example device
+ *
+ * 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/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "vpdma.h"
+#include "vpe_regs.h"
+
+#define VPE_MODULE_NAME "vpe"
+
+/* minimum and maximum frame sizes */
+#define MIN_W          128
+#define MIN_H          128
+#define MAX_W          1920
+#define MAX_H          1080
+
+/* required alignments */
+#define S_ALIGN                0       /* multiple of 1 */
+#define H_ALIGN                1       /* multiple of 2 */
+#define W_ALIGN                1       /* multiple of 2 */
+
+/* multiple of 128 bits, line stride, 16 bytes */
+#define L_ALIGN                4
+
+/* flags that indicate a format can be used for capture/output */
+#define VPE_FMT_TYPE_CAPTURE   (1 << 0)
+#define VPE_FMT_TYPE_OUTPUT    (1 << 1)
+
+/* used as plane indices */
+#define VPE_MAX_PLANES 2
+#define VPE_LUMA       0
+#define VPE_CHROMA     1
+
+/* per m2m context info */
+#define VPE_MAX_SRC_BUFS       3       /* need 3 src fields to de-interlace */
+
+#define VPE_DEF_BUFS_PER_JOB   1       /* default one buffer per batch job */
+
+/*
+ * each VPE context can need up to 3 config desciptors, 7 input descriptors,
+ * 3 output descriptors, and 10 control descriptors
+ */
+#define VPE_DESC_LIST_SIZE     (10 * VPDMA_DTD_DESC_SIZE +     \
+                                       13 * VPDMA_CFD_CTD_DESC_SIZE)
+
+#define vpe_dbg(vpedev, fmt, arg...)   \
+               dev_dbg((vpedev)->v4l2_dev.dev, fmt, ##arg)
+#define vpe_err(vpedev, fmt, arg...)   \
+               dev_err((vpedev)->v4l2_dev.dev, fmt, ##arg)
+
+struct vpe_us_coeffs {
+       unsigned short  anchor_fid0_c0;
+       unsigned short  anchor_fid0_c1;
+       unsigned short  anchor_fid0_c2;
+       unsigned short  anchor_fid0_c3;
+       unsigned short  interp_fid0_c0;
+       unsigned short  interp_fid0_c1;
+       unsigned short  interp_fid0_c2;
+       unsigned short  interp_fid0_c3;
+       unsigned short  anchor_fid1_c0;
+       unsigned short  anchor_fid1_c1;
+       unsigned short  anchor_fid1_c2;
+       unsigned short  anchor_fid1_c3;
+       unsigned short  interp_fid1_c0;
+       unsigned short  interp_fid1_c1;
+       unsigned short  interp_fid1_c2;
+       unsigned short  interp_fid1_c3;
+};
+
+/*
+ * Default upsampler coefficients
+ */
+static const struct vpe_us_coeffs us_coeffs[] = {
+       {
+               /* Coefficients for progressive input */
+               0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8,
+               0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8,
+       },
+       {
+               /* Coefficients for Top Field Interlaced input */
+               0x0051, 0x03D5, 0x3FE3, 0x3FF7, 0x3FB5, 0x02E9, 0x018F, 0x3FD3,
+               /* Coefficients for Bottom Field Interlaced input */
+               0x016B, 0x0247, 0x00B1, 0x3F9D, 0x3FCF, 0x03DB, 0x005D, 0x3FF9,
+       },
+};
+
+/*
+ * the following registers are for configuring some of the parameters of the
+ * motion and edge detection blocks inside DEI, these generally remain the same,
+ * these could be passed later via userspace if some one needs to tweak these.
+ */
+struct vpe_dei_regs {
+       unsigned long mdt_spacial_freq_thr_reg;         /* VPE_DEI_REG2 */
+       unsigned long edi_config_reg;                   /* VPE_DEI_REG3 */
+       unsigned long edi_lut_reg0;                     /* VPE_DEI_REG4 */
+       unsigned long edi_lut_reg1;                     /* VPE_DEI_REG5 */
+       unsigned long edi_lut_reg2;                     /* VPE_DEI_REG6 */
+       unsigned long edi_lut_reg3;                     /* VPE_DEI_REG7 */
+};
+
+/*
+ * default expert DEI register values, unlikely to be modified.
+ */
+static const struct vpe_dei_regs dei_regs = {
+       0x020C0804u,
+       0x0118100Fu,
+       0x08040200u,
+       0x1010100Cu,
+       0x10101010u,
+       0x10101010u,
+};
+
+/*
+ * The port_data structure contains per-port data.
+ */
+struct vpe_port_data {
+       enum vpdma_channel channel;     /* VPDMA channel */
+       u8      vb_index;               /* input frame f, f-1, f-2 index */
+       u8      vb_part;                /* plane index for co-panar formats */
+};
+
+/*
+ * Define indices into the port_data tables
+ */
+#define VPE_PORT_LUMA1_IN      0
+#define VPE_PORT_CHROMA1_IN    1
+#define VPE_PORT_LUMA2_IN      2
+#define VPE_PORT_CHROMA2_IN    3
+#define VPE_PORT_LUMA3_IN      4
+#define VPE_PORT_CHROMA3_IN    5
+#define VPE_PORT_MV_IN         6
+#define VPE_PORT_MV_OUT                7
+#define VPE_PORT_LUMA_OUT      8
+#define VPE_PORT_CHROMA_OUT    9
+#define VPE_PORT_RGB_OUT       10
+
+static const struct vpe_port_data port_data[11] = {
+       [VPE_PORT_LUMA1_IN] = {
+               .channel        = VPE_CHAN_LUMA1_IN,
+               .vb_index       = 0,
+               .vb_part        = VPE_LUMA,
+       },
+       [VPE_PORT_CHROMA1_IN] = {
+               .channel        = VPE_CHAN_CHROMA1_IN,
+               .vb_index       = 0,
+               .vb_part        = VPE_CHROMA,
+       },
+       [VPE_PORT_LUMA2_IN] = {
+               .channel        = VPE_CHAN_LUMA2_IN,
+               .vb_index       = 1,
+               .vb_part        = VPE_LUMA,
+       },
+       [VPE_PORT_CHROMA2_IN] = {
+               .channel        = VPE_CHAN_CHROMA2_IN,
+               .vb_index       = 1,
+               .vb_part        = VPE_CHROMA,
+       },
+       [VPE_PORT_LUMA3_IN] = {
+               .channel        = VPE_CHAN_LUMA3_IN,
+               .vb_index       = 2,
+               .vb_part        = VPE_LUMA,
+       },
+       [VPE_PORT_CHROMA3_IN] = {
+               .channel        = VPE_CHAN_CHROMA3_IN,
+               .vb_index       = 2,
+               .vb_part        = VPE_CHROMA,
+       },
+       [VPE_PORT_MV_IN] = {
+               .channel        = VPE_CHAN_MV_IN,
+       },
+       [VPE_PORT_MV_OUT] = {
+               .channel        = VPE_CHAN_MV_OUT,
+       },
+       [VPE_PORT_LUMA_OUT] = {
+               .channel        = VPE_CHAN_LUMA_OUT,
+               .vb_part        = VPE_LUMA,
+       },
+       [VPE_PORT_CHROMA_OUT] = {
+               .channel        = VPE_CHAN_CHROMA_OUT,
+               .vb_part        = VPE_CHROMA,
+       },
+       [VPE_PORT_RGB_OUT] = {
+               .channel        = VPE_CHAN_RGB_OUT,
+               .vb_part        = VPE_LUMA,
+       },
+};
+
+
+/* driver info for each of the supported video formats */
+struct vpe_fmt {
+       char    *name;                  /* human-readable name */
+       u32     fourcc;                 /* standard format identifier */
+       u8      types;                  /* CAPTURE and/or OUTPUT */
+       u8      coplanar;               /* set for unpacked Luma and Chroma */
+       /* vpdma format info for each plane */
+       struct vpdma_data_format const *vpdma_fmt[VPE_MAX_PLANES];
+};
+
+static struct vpe_fmt vpe_formats[] = {
+       {
+               .name           = "YUV 422 co-planar",
+               .fourcc         = V4L2_PIX_FMT_NV16,
+               .types          = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
+               .coplanar       = 1,
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y444],
+                                   &vpdma_yuv_fmts[VPDMA_DATA_FMT_C444],
+                                 },
+       },
+       {
+               .name           = "YUV 420 co-planar",
+               .fourcc         = V4L2_PIX_FMT_NV12,
+               .types          = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
+               .coplanar       = 1,
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420],
+                                   &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420],
+                                 },
+       },
+       {
+               .name           = "YUYV 422 packed",
+               .fourcc         = V4L2_PIX_FMT_YUYV,
+               .types          = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
+               .coplanar       = 0,
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YC422],
+                                 },
+       },
+       {
+               .name           = "UYVY 422 packed",
+               .fourcc         = V4L2_PIX_FMT_UYVY,
+               .types          = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
+               .coplanar       = 0,
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CY422],
+                                 },
+       },
+};
+
+/*
+ * per-queue, driver-specific private data.
+ * there is one source queue and one destination queue for each m2m context.
+ */
+struct vpe_q_data {
+       unsigned int            width;                          /* frame width */
+       unsigned int            height;                         /* frame height */
+       unsigned int            bytesperline[VPE_MAX_PLANES];   /* bytes per line in memory */
+       enum v4l2_colorspace    colorspace;
+       enum v4l2_field         field;                          /* supported field value */
+       unsigned int            flags;
+       unsigned int            sizeimage[VPE_MAX_PLANES];      /* image size in memory */
+       struct v4l2_rect        c_rect;                         /* crop/compose rectangle */
+       struct vpe_fmt          *fmt;                           /* format info */
+};
+
+/* vpe_q_data flag bits */
+#define        Q_DATA_FRAME_1D         (1 << 0)
+#define        Q_DATA_MODE_TILED       (1 << 1)
+#define        Q_DATA_INTERLACED       (1 << 2)
+
+enum {
+       Q_DATA_SRC = 0,
+       Q_DATA_DST = 1,
+};
+
+/* find our format description corresponding to the passed v4l2_format */
+static struct vpe_fmt *find_format(struct v4l2_format *f)
+{
+       struct vpe_fmt *fmt;
+       unsigned int k;
+
+       for (k = 0; k < ARRAY_SIZE(vpe_formats); k++) {
+               fmt = &vpe_formats[k];
+               if (fmt->fourcc == f->fmt.pix.pixelformat)
+                       return fmt;
+       }
+
+       return NULL;
+}
+
+/*
+ * there is one vpe_dev structure in the driver, it is shared by
+ * all instances.
+ */
+struct vpe_dev {
+       struct v4l2_device      v4l2_dev;
+       struct video_device     vfd;
+       struct v4l2_m2m_dev     *m2m_dev;
+
+       atomic_t                num_instances;  /* count of driver instances */
+       dma_addr_t              loaded_mmrs;    /* shadow mmrs in device */
+       struct mutex            dev_mutex;
+       spinlock_t              lock;
+
+       int                     irq;
+       void __iomem            *base;
+
+       struct vb2_alloc_ctx    *alloc_ctx;
+       struct vpdma_data       *vpdma;         /* vpdma data handle */
+};
+
+/*
+ * There is one vpe_ctx structure for each m2m context.
+ */
+struct vpe_ctx {
+       struct v4l2_fh          fh;
+       struct vpe_dev          *dev;
+       struct v4l2_m2m_ctx     *m2m_ctx;
+       struct v4l2_ctrl_handler hdl;
+
+       unsigned int            field;                  /* current field */
+       unsigned int            sequence;               /* current frame/field seq */
+       unsigned int            aborting;               /* abort after next irq */
+
+       unsigned int            bufs_per_job;           /* input buffers per batch */
+       unsigned int            bufs_completed;         /* bufs done in this batch */
+
+       struct vpe_q_data       q_data[2];              /* src & dst queue data */
+       struct vb2_buffer       *src_vbs[VPE_MAX_SRC_BUFS];
+       struct vb2_buffer       *dst_vb;
+
+       dma_addr_t              mv_buf_dma[2];          /* dma addrs of motion vector in/out bufs */
+       void                    *mv_buf[2];             /* virtual addrs of motion vector bufs */
+       size_t                  mv_buf_size;            /* current motion vector buffer size */
+       struct vpdma_buf        mmr_adb;                /* shadow reg addr/data block */
+       struct vpdma_desc_list  desc_list;              /* DMA descriptor list */
+
+       bool                    deinterlacing;          /* using de-interlacer */
+       bool                    load_mmrs;              /* have new shadow reg values */
+
+       unsigned int            src_mv_buf_selector;
+};
+
+
+/*
+ * M2M devices get 2 queues.
+ * Return the queue given the type.
+ */
+static struct vpe_q_data *get_q_data(struct vpe_ctx *ctx,
+                                    enum v4l2_buf_type type)
+{
+       switch (type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               return &ctx->q_data[Q_DATA_SRC];
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               return &ctx->q_data[Q_DATA_DST];
+       default:
+               BUG();
+       }
+       return NULL;
+}
+
+static u32 read_reg(struct vpe_dev *dev, int offset)
+{
+       return ioread32(dev->base + offset);
+}
+
+static void write_reg(struct vpe_dev *dev, int offset, u32 value)
+{
+       iowrite32(value, dev->base + offset);
+}
+
+/* register field read/write helpers */
+static int get_field(u32 value, u32 mask, int shift)
+{
+       return (value & (mask << shift)) >> shift;
+}
+
+static int read_field_reg(struct vpe_dev *dev, int offset, u32 mask, int shift)
+{
+       return get_field(read_reg(dev, offset), mask, shift);
+}
+
+static void write_field(u32 *valp, u32 field, u32 mask, int shift)
+{
+       u32 val = *valp;
+
+       val &= ~(mask << shift);
+       val |= (field & mask) << shift;
+       *valp = val;
+}
+
+static void write_field_reg(struct vpe_dev *dev, int offset, u32 field,
+               u32 mask, int shift)
+{
+       u32 val = read_reg(dev, offset);
+
+       write_field(&val, field, mask, shift);
+
+       write_reg(dev, offset, val);
+}
+
+/*
+ * DMA address/data block for the shadow registers
+ */
+struct vpe_mmr_adb {
+       struct vpdma_adb_hdr    out_fmt_hdr;
+       u32                     out_fmt_reg[1];
+       u32                     out_fmt_pad[3];
+       struct vpdma_adb_hdr    us1_hdr;
+       u32                     us1_regs[8];
+       struct vpdma_adb_hdr    us2_hdr;
+       u32                     us2_regs[8];
+       struct vpdma_adb_hdr    us3_hdr;
+       u32                     us3_regs[8];
+       struct vpdma_adb_hdr    dei_hdr;
+       u32                     dei_regs[8];
+       struct vpdma_adb_hdr    sc_hdr;
+       u32                     sc_regs[1];
+       u32                     sc_pad[3];
+       struct vpdma_adb_hdr    csc_hdr;
+       u32                     csc_regs[6];
+       u32                     csc_pad[2];
+};
+
+#define VPE_SET_MMR_ADB_HDR(ctx, hdr, regs, offset_a)  \
+       VPDMA_SET_MMR_ADB_HDR(ctx->mmr_adb, vpe_mmr_adb, hdr, regs, offset_a)
+/*
+ * Set the headers for all of the address/data block structures.
+ */
+static void init_adb_hdrs(struct vpe_ctx *ctx)
+{
+       VPE_SET_MMR_ADB_HDR(ctx, out_fmt_hdr, out_fmt_reg, VPE_CLK_FORMAT_SELECT);
+       VPE_SET_MMR_ADB_HDR(ctx, us1_hdr, us1_regs, VPE_US1_R0);
+       VPE_SET_MMR_ADB_HDR(ctx, us2_hdr, us2_regs, VPE_US2_R0);
+       VPE_SET_MMR_ADB_HDR(ctx, us3_hdr, us3_regs, VPE_US3_R0);
+       VPE_SET_MMR_ADB_HDR(ctx, dei_hdr, dei_regs, VPE_DEI_FRAME_SIZE);
+       VPE_SET_MMR_ADB_HDR(ctx, sc_hdr, sc_regs, VPE_SC_MP_SC0);
+       VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, VPE_CSC_CSC00);
+};
+
+/*
+ * Allocate or re-allocate the motion vector DMA buffers
+ * There are two buffers, one for input and one for output.
+ * However, the roles are reversed after each field is processed.
+ * In other words, after each field is processed, the previous
+ * output (dst) MV buffer becomes the new input (src) MV buffer.
+ */
+static int realloc_mv_buffers(struct vpe_ctx *ctx, size_t size)
+{
+       struct device *dev = ctx->dev->v4l2_dev.dev;
+
+       if (ctx->mv_buf_size == size)
+               return 0;
+
+       if (ctx->mv_buf[0])
+               dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[0],
+                       ctx->mv_buf_dma[0]);
+
+       if (ctx->mv_buf[1])
+               dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[1],
+                       ctx->mv_buf_dma[1]);
+
+       if (size == 0)
+               return 0;
+
+       ctx->mv_buf[0] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[0],
+                               GFP_KERNEL);
+       if (!ctx->mv_buf[0]) {
+               vpe_err(ctx->dev, "failed to allocate motion vector buffer\n");
+               return -ENOMEM;
+       }
+
+       ctx->mv_buf[1] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[1],
+                               GFP_KERNEL);
+       if (!ctx->mv_buf[1]) {
+               vpe_err(ctx->dev, "failed to allocate motion vector buffer\n");
+               dma_free_coherent(dev, size, ctx->mv_buf[0],
+                       ctx->mv_buf_dma[0]);
+
+               return -ENOMEM;
+       }
+
+       ctx->mv_buf_size = size;
+       ctx->src_mv_buf_selector = 0;
+
+       return 0;
+}
+
+static void free_mv_buffers(struct vpe_ctx *ctx)
+{
+       realloc_mv_buffers(ctx, 0);
+}
+
+/*
+ * While de-interlacing, we keep the two most recent input buffers
+ * around.  This function frees those two buffers when we have
+ * finished processing the current stream.
+ */
+static void free_vbs(struct vpe_ctx *ctx)
+{
+       struct vpe_dev *dev = ctx->dev;
+       unsigned long flags;
+
+       if (ctx->src_vbs[2] == NULL)
+               return;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (ctx->src_vbs[2]) {
+               v4l2_m2m_buf_done(ctx->src_vbs[2], VB2_BUF_STATE_DONE);
+               v4l2_m2m_buf_done(ctx->src_vbs[1], VB2_BUF_STATE_DONE);
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ * Enable or disable the VPE clocks
+ */
+static void vpe_set_clock_enable(struct vpe_dev *dev, bool on)
+{
+       u32 val = 0;
+
+       if (on)
+               val = VPE_DATA_PATH_CLK_ENABLE | VPE_VPEDMA_CLK_ENABLE;
+       write_reg(dev, VPE_CLK_ENABLE, val);
+}
+
+static void vpe_top_reset(struct vpe_dev *dev)
+{
+
+       write_field_reg(dev, VPE_CLK_RESET, 1, VPE_DATA_PATH_CLK_RESET_MASK,
+               VPE_DATA_PATH_CLK_RESET_SHIFT);
+
+       usleep_range(100, 150);
+
+       write_field_reg(dev, VPE_CLK_RESET, 0, VPE_DATA_PATH_CLK_RESET_MASK,
+               VPE_DATA_PATH_CLK_RESET_SHIFT);
+}
+
+static void vpe_top_vpdma_reset(struct vpe_dev *dev)
+{
+       write_field_reg(dev, VPE_CLK_RESET, 1, VPE_VPDMA_CLK_RESET_MASK,
+               VPE_VPDMA_CLK_RESET_SHIFT);
+
+       usleep_range(100, 150);
+
+       write_field_reg(dev, VPE_CLK_RESET, 0, VPE_VPDMA_CLK_RESET_MASK,
+               VPE_VPDMA_CLK_RESET_SHIFT);
+}
+
+/*
+ * Load the correct of upsampler coefficients into the shadow MMRs
+ */
+static void set_us_coefficients(struct vpe_ctx *ctx)
+{
+       struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+       struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
+       u32 *us1_reg = &mmr_adb->us1_regs[0];
+       u32 *us2_reg = &mmr_adb->us2_regs[0];
+       u32 *us3_reg = &mmr_adb->us3_regs[0];
+       const unsigned short *cp, *end_cp;
+
+       cp = &us_coeffs[0].anchor_fid0_c0;
+
+       if (s_q_data->flags & Q_DATA_INTERLACED)        /* interlaced */
+               cp += sizeof(us_coeffs[0]) / sizeof(*cp);
+
+       end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp);
+
+       while (cp < end_cp) {
+               write_field(us1_reg, *cp++, VPE_US_C0_MASK, VPE_US_C0_SHIFT);
+               write_field(us1_reg, *cp++, VPE_US_C1_MASK, VPE_US_C1_SHIFT);
+               *us2_reg++ = *us1_reg;
+               *us3_reg++ = *us1_reg++;
+       }
+       ctx->load_mmrs = true;
+}
+
+/*
+ * Set the upsampler config mode and the VPDMA line mode in the shadow MMRs.
+ */
+static void set_cfg_and_line_modes(struct vpe_ctx *ctx)
+{
+       struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt;
+       struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+       u32 *us1_reg0 = &mmr_adb->us1_regs[0];
+       u32 *us2_reg0 = &mmr_adb->us2_regs[0];
+       u32 *us3_reg0 = &mmr_adb->us3_regs[0];
+       int line_mode = 1;
+       int cfg_mode = 1;
+
+       /*
+        * Cfg Mode 0: YUV420 source, enable upsampler, DEI is de-interlacing.
+        * Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing.
+        */
+
+       if (fmt->fourcc == V4L2_PIX_FMT_NV12) {
+               cfg_mode = 0;
+               line_mode = 0;          /* double lines to line buffer */
+       }
+
+       write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
+       write_field(us2_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
+       write_field(us3_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
+
+       /* regs for now */
+       vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA1_IN);
+       vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA2_IN);
+       vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA3_IN);
+
+       /* frame start for input luma */
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_LUMA1_IN);
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_LUMA2_IN);
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_LUMA3_IN);
+
+       /* frame start for input chroma */
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_CHROMA1_IN);
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_CHROMA2_IN);
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_CHROMA3_IN);
+
+       /* frame start for MV in client */
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_MV_IN);
+
+       ctx->load_mmrs = true;
+}
+
+/*
+ * Set the shadow registers that are modified when the source
+ * format changes.
+ */
+static void set_src_registers(struct vpe_ctx *ctx)
+{
+       set_us_coefficients(ctx);
+}
+
+/*
+ * Set the shadow registers that are modified when the destination
+ * format changes.
+ */
+static void set_dst_registers(struct vpe_ctx *ctx)
+{
+       struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+       struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt;
+       u32 val = 0;
+
+       /* select RGB path when color space conversion is supported in future */
+       if (fmt->fourcc == V4L2_PIX_FMT_RGB24)
+               val |= VPE_RGB_OUT_SELECT | VPE_CSC_SRC_DEI_SCALER;
+       else if (fmt->fourcc == V4L2_PIX_FMT_NV16)
+               val |= VPE_COLOR_SEPARATE_422;
+
+       /* The source of CHR_DS is always the scaler, whether it's used or not */
+       val |= VPE_DS_SRC_DEI_SCALER;
+
+       if (fmt->fourcc != V4L2_PIX_FMT_NV12)
+               val |= VPE_DS_BYPASS;
+
+       mmr_adb->out_fmt_reg[0] = val;
+
+       ctx->load_mmrs = true;
+}
+
+/*
+ * Set the de-interlacer shadow register values
+ */
+static void set_dei_regs(struct vpe_ctx *ctx)
+{
+       struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+       struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
+       unsigned int src_h = s_q_data->c_rect.height;
+       unsigned int src_w = s_q_data->c_rect.width;
+       u32 *dei_mmr0 = &mmr_adb->dei_regs[0];
+       bool deinterlace = true;
+       u32 val = 0;
+
+       /*
+        * according to TRM, we should set DEI in progressive bypass mode when
+        * the input content is progressive, however, DEI is bypassed correctly
+        * for both progressive and interlace content in interlace bypass mode.
+        * It has been recommended not to use progressive bypass mode.
+        */
+       if ((!ctx->deinterlacing && (s_q_data->flags & Q_DATA_INTERLACED)) ||
+                       !(s_q_data->flags & Q_DATA_INTERLACED)) {
+               deinterlace = false;
+               val = VPE_DEI_INTERLACE_BYPASS;
+       }
+
+       src_h = deinterlace ? src_h * 2 : src_h;
+
+       val |= (src_h << VPE_DEI_HEIGHT_SHIFT) |
+               (src_w << VPE_DEI_WIDTH_SHIFT) |
+               VPE_DEI_FIELD_FLUSH;
+
+       *dei_mmr0 = val;
+
+       ctx->load_mmrs = true;
+}
+
+static void set_dei_shadow_registers(struct vpe_ctx *ctx)
+{
+       struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+       u32 *dei_mmr = &mmr_adb->dei_regs[0];
+       const struct vpe_dei_regs *cur = &dei_regs;
+
+       dei_mmr[2]  = cur->mdt_spacial_freq_thr_reg;
+       dei_mmr[3]  = cur->edi_config_reg;
+       dei_mmr[4]  = cur->edi_lut_reg0;
+       dei_mmr[5]  = cur->edi_lut_reg1;
+       dei_mmr[6]  = cur->edi_lut_reg2;
+       dei_mmr[7]  = cur->edi_lut_reg3;
+
+       ctx->load_mmrs = true;
+}
+
+static void set_csc_coeff_bypass(struct vpe_ctx *ctx)
+{
+       struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+       u32 *shadow_csc_reg5 = &mmr_adb->csc_regs[5];
+
+       *shadow_csc_reg5 |= VPE_CSC_BYPASS;
+
+       ctx->load_mmrs = true;
+}
+
+static void set_sc_regs_bypass(struct vpe_ctx *ctx)
+{
+       struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+       u32 *sc_reg0 = &mmr_adb->sc_regs[0];
+       u32 val = 0;
+
+       val |= VPE_SC_BYPASS;
+       *sc_reg0 = val;
+
+       ctx->load_mmrs = true;
+}
+
+/*
+ * Set the shadow registers whose values are modified when either the
+ * source or destination format is changed.
+ */
+static int set_srcdst_params(struct vpe_ctx *ctx)
+{
+       struct vpe_q_data *s_q_data =  &ctx->q_data[Q_DATA_SRC];
+       struct vpe_q_data *d_q_data =  &ctx->q_data[Q_DATA_DST];
+       size_t mv_buf_size;
+       int ret;
+
+       ctx->sequence = 0;
+       ctx->field = V4L2_FIELD_TOP;
+
+       if ((s_q_data->flags & Q_DATA_INTERLACED) &&
+                       !(d_q_data->flags & Q_DATA_INTERLACED)) {
+               const struct vpdma_data_format *mv =
+                       &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
+
+               ctx->deinterlacing = 1;
+               mv_buf_size =
+                       (s_q_data->width * s_q_data->height * mv->depth) >> 3;
+       } else {
+               ctx->deinterlacing = 0;
+               mv_buf_size = 0;
+       }
+
+       free_vbs(ctx);
+
+       ret = realloc_mv_buffers(ctx, mv_buf_size);
+       if (ret)
+               return ret;
+
+       set_cfg_and_line_modes(ctx);
+       set_dei_regs(ctx);
+       set_csc_coeff_bypass(ctx);
+       set_sc_regs_bypass(ctx);
+
+       return 0;
+}
+
+/*
+ * Return the vpe_ctx structure for a given struct file
+ */
+static struct vpe_ctx *file2ctx(struct file *file)
+{
+       return container_of(file->private_data, struct vpe_ctx, fh);
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+/**
+ * job_ready() - check whether an instance is ready to be scheduled to run
+ */
+static int job_ready(void *priv)
+{
+       struct vpe_ctx *ctx = priv;
+       int needed = ctx->bufs_per_job;
+
+       if (ctx->deinterlacing && ctx->src_vbs[2] == NULL)
+               needed += 2;    /* need additional two most recent fields */
+
+       if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < needed)
+               return 0;
+
+       return 1;
+}
+
+static void job_abort(void *priv)
+{
+       struct vpe_ctx *ctx = priv;
+
+       /* Will cancel the transaction in the next interrupt handler */
+       ctx->aborting = 1;
+}
+
+/*
+ * Lock access to the device
+ */
+static void vpe_lock(void *priv)
+{
+       struct vpe_ctx *ctx = priv;
+       struct vpe_dev *dev = ctx->dev;
+       mutex_lock(&dev->dev_mutex);
+}
+
+static void vpe_unlock(void *priv)
+{
+       struct vpe_ctx *ctx = priv;
+       struct vpe_dev *dev = ctx->dev;
+       mutex_unlock(&dev->dev_mutex);
+}
+
+static void vpe_dump_regs(struct vpe_dev *dev)
+{
+#define DUMPREG(r) vpe_dbg(dev, "%-35s %08x\n", #r, read_reg(dev, VPE_##r))
+
+       vpe_dbg(dev, "VPE Registers:\n");
+
+       DUMPREG(PID);
+       DUMPREG(SYSCONFIG);
+       DUMPREG(INT0_STATUS0_RAW);
+       DUMPREG(INT0_STATUS0);
+       DUMPREG(INT0_ENABLE0);
+       DUMPREG(INT0_STATUS1_RAW);
+       DUMPREG(INT0_STATUS1);
+       DUMPREG(INT0_ENABLE1);
+       DUMPREG(CLK_ENABLE);
+       DUMPREG(CLK_RESET);
+       DUMPREG(CLK_FORMAT_SELECT);
+       DUMPREG(CLK_RANGE_MAP);
+       DUMPREG(US1_R0);
+       DUMPREG(US1_R1);
+       DUMPREG(US1_R2);
+       DUMPREG(US1_R3);
+       DUMPREG(US1_R4);
+       DUMPREG(US1_R5);
+       DUMPREG(US1_R6);
+       DUMPREG(US1_R7);
+       DUMPREG(US2_R0);
+       DUMPREG(US2_R1);
+       DUMPREG(US2_R2);
+       DUMPREG(US2_R3);
+       DUMPREG(US2_R4);
+       DUMPREG(US2_R5);
+       DUMPREG(US2_R6);
+       DUMPREG(US2_R7);
+       DUMPREG(US3_R0);
+       DUMPREG(US3_R1);
+       DUMPREG(US3_R2);
+       DUMPREG(US3_R3);
+       DUMPREG(US3_R4);
+       DUMPREG(US3_R5);
+       DUMPREG(US3_R6);
+       DUMPREG(US3_R7);
+       DUMPREG(DEI_FRAME_SIZE);
+       DUMPREG(MDT_BYPASS);
+       DUMPREG(MDT_SF_THRESHOLD);
+       DUMPREG(EDI_CONFIG);
+       DUMPREG(DEI_EDI_LUT_R0);
+       DUMPREG(DEI_EDI_LUT_R1);
+       DUMPREG(DEI_EDI_LUT_R2);
+       DUMPREG(DEI_EDI_LUT_R3);
+       DUMPREG(DEI_FMD_WINDOW_R0);
+       DUMPREG(DEI_FMD_WINDOW_R1);
+       DUMPREG(DEI_FMD_CONTROL_R0);
+       DUMPREG(DEI_FMD_CONTROL_R1);
+       DUMPREG(DEI_FMD_STATUS_R0);
+       DUMPREG(DEI_FMD_STATUS_R1);
+       DUMPREG(DEI_FMD_STATUS_R2);
+       DUMPREG(SC_MP_SC0);
+       DUMPREG(SC_MP_SC1);
+       DUMPREG(SC_MP_SC2);
+       DUMPREG(SC_MP_SC3);
+       DUMPREG(SC_MP_SC4);
+       DUMPREG(SC_MP_SC5);
+       DUMPREG(SC_MP_SC6);
+       DUMPREG(SC_MP_SC8);
+       DUMPREG(SC_MP_SC9);
+       DUMPREG(SC_MP_SC10);
+       DUMPREG(SC_MP_SC11);
+       DUMPREG(SC_MP_SC12);
+       DUMPREG(SC_MP_SC13);
+       DUMPREG(SC_MP_SC17);
+       DUMPREG(SC_MP_SC18);
+       DUMPREG(SC_MP_SC19);
+       DUMPREG(SC_MP_SC20);
+       DUMPREG(SC_MP_SC21);
+       DUMPREG(SC_MP_SC22);
+       DUMPREG(SC_MP_SC23);
+       DUMPREG(SC_MP_SC24);
+       DUMPREG(SC_MP_SC25);
+       DUMPREG(CSC_CSC00);
+       DUMPREG(CSC_CSC01);
+       DUMPREG(CSC_CSC02);
+       DUMPREG(CSC_CSC03);
+       DUMPREG(CSC_CSC04);
+       DUMPREG(CSC_CSC05);
+#undef DUMPREG
+}
+
+static void add_out_dtd(struct vpe_ctx *ctx, int port)
+{
+       struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_DST];
+       const struct vpe_port_data *p_data = &port_data[port];
+       struct vb2_buffer *vb = ctx->dst_vb;
+       struct v4l2_rect *c_rect = &q_data->c_rect;
+       struct vpe_fmt *fmt = q_data->fmt;
+       const struct vpdma_data_format *vpdma_fmt;
+       int mv_buf_selector = !ctx->src_mv_buf_selector;
+       dma_addr_t dma_addr;
+       u32 flags = 0;
+
+       if (port == VPE_PORT_MV_OUT) {
+               vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
+               dma_addr = ctx->mv_buf_dma[mv_buf_selector];
+       } else {
+               /* to incorporate interleaved formats */
+               int plane = fmt->coplanar ? p_data->vb_part : 0;
+
+               vpdma_fmt = fmt->vpdma_fmt[plane];
+               dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
+               if (!dma_addr) {
+                       vpe_err(ctx->dev,
+                               "acquiring output buffer(%d) dma_addr failed\n",
+                               port);
+                       return;
+               }
+       }
+
+       if (q_data->flags & Q_DATA_FRAME_1D)
+               flags |= VPDMA_DATA_FRAME_1D;
+       if (q_data->flags & Q_DATA_MODE_TILED)
+               flags |= VPDMA_DATA_MODE_TILED;
+
+       vpdma_add_out_dtd(&ctx->desc_list, c_rect, vpdma_fmt, dma_addr,
+               p_data->channel, flags);
+}
+
+static void add_in_dtd(struct vpe_ctx *ctx, int port)
+{
+       struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC];
+       const struct vpe_port_data *p_data = &port_data[port];
+       struct vb2_buffer *vb = ctx->src_vbs[p_data->vb_index];
+       struct v4l2_rect *c_rect = &q_data->c_rect;
+       struct vpe_fmt *fmt = q_data->fmt;
+       const struct vpdma_data_format *vpdma_fmt;
+       int mv_buf_selector = ctx->src_mv_buf_selector;
+       int field = vb->v4l2_buf.field == V4L2_FIELD_BOTTOM;
+       dma_addr_t dma_addr;
+       u32 flags = 0;
+
+       if (port == VPE_PORT_MV_IN) {
+               vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
+               dma_addr = ctx->mv_buf_dma[mv_buf_selector];
+       } else {
+               /* to incorporate interleaved formats */
+               int plane = fmt->coplanar ? p_data->vb_part : 0;
+
+               vpdma_fmt = fmt->vpdma_fmt[plane];
+
+               dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
+               if (!dma_addr) {
+                       vpe_err(ctx->dev,
+                               "acquiring input buffer(%d) dma_addr failed\n",
+                               port);
+                       return;
+               }
+       }
+
+       if (q_data->flags & Q_DATA_FRAME_1D)
+               flags |= VPDMA_DATA_FRAME_1D;
+       if (q_data->flags & Q_DATA_MODE_TILED)
+               flags |= VPDMA_DATA_MODE_TILED;
+
+       vpdma_add_in_dtd(&ctx->desc_list, q_data->width, q_data->height,
+               c_rect, vpdma_fmt, dma_addr, p_data->channel, field, flags);
+}
+
+/*
+ * Enable the expected IRQ sources
+ */
+static void enable_irqs(struct vpe_ctx *ctx)
+{
+       write_reg(ctx->dev, VPE_INT0_ENABLE0_SET, VPE_INT0_LIST0_COMPLETE);
+       write_reg(ctx->dev, VPE_INT0_ENABLE1_SET, VPE_DEI_ERROR_INT |
+                               VPE_DS1_UV_ERROR_INT);
+
+       vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, true);
+}
+
+static void disable_irqs(struct vpe_ctx *ctx)
+{
+       write_reg(ctx->dev, VPE_INT0_ENABLE0_CLR, 0xffffffff);
+       write_reg(ctx->dev, VPE_INT0_ENABLE1_CLR, 0xffffffff);
+
+       vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, false);
+}
+
+/* device_run() - prepares and starts the device
+ *
+ * This function is only called when both the source and destination
+ * buffers are in place.
+ */
+static void device_run(void *priv)
+{
+       struct vpe_ctx *ctx = priv;
+       struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST];
+
+       if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) {
+               ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+               WARN_ON(ctx->src_vbs[2] == NULL);
+               ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+               WARN_ON(ctx->src_vbs[1] == NULL);
+       }
+
+       ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+       WARN_ON(ctx->src_vbs[0] == NULL);
+       ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+       WARN_ON(ctx->dst_vb == NULL);
+
+       /* config descriptors */
+       if (ctx->dev->loaded_mmrs != ctx->mmr_adb.dma_addr || ctx->load_mmrs) {
+               vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->mmr_adb);
+               vpdma_add_cfd_adb(&ctx->desc_list, CFD_MMR_CLIENT, &ctx->mmr_adb);
+               ctx->dev->loaded_mmrs = ctx->mmr_adb.dma_addr;
+               ctx->load_mmrs = false;
+       }
+
+       /* output data descriptors */
+       if (ctx->deinterlacing)
+               add_out_dtd(ctx, VPE_PORT_MV_OUT);
+
+       add_out_dtd(ctx, VPE_PORT_LUMA_OUT);
+       if (d_q_data->fmt->coplanar)
+               add_out_dtd(ctx, VPE_PORT_CHROMA_OUT);
+
+       /* input data descriptors */
+       if (ctx->deinterlacing) {
+               add_in_dtd(ctx, VPE_PORT_LUMA3_IN);
+               add_in_dtd(ctx, VPE_PORT_CHROMA3_IN);
+
+               add_in_dtd(ctx, VPE_PORT_LUMA2_IN);
+               add_in_dtd(ctx, VPE_PORT_CHROMA2_IN);
+       }
+
+       add_in_dtd(ctx, VPE_PORT_LUMA1_IN);
+       add_in_dtd(ctx, VPE_PORT_CHROMA1_IN);
+
+       if (ctx->deinterlacing)
+               add_in_dtd(ctx, VPE_PORT_MV_IN);
+
+       /* sync on channel control descriptors for input ports */
+       vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA1_IN);
+       vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA1_IN);
+
+       if (ctx->deinterlacing) {
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+                       VPE_CHAN_LUMA2_IN);
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+                       VPE_CHAN_CHROMA2_IN);
+
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+                       VPE_CHAN_LUMA3_IN);
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+                       VPE_CHAN_CHROMA3_IN);
+
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_IN);
+       }
+
+       /* sync on channel control descriptors for output ports */
+       vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA_OUT);
+       if (d_q_data->fmt->coplanar)
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA_OUT);
+
+       if (ctx->deinterlacing)
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_OUT);
+
+       enable_irqs(ctx);
+
+       vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->desc_list.buf);
+       vpdma_submit_descs(ctx->dev->vpdma, &ctx->desc_list);
+}
+
+static void dei_error(struct vpe_ctx *ctx)
+{
+       dev_warn(ctx->dev->v4l2_dev.dev,
+               "received DEI error interrupt\n");
+}
+
+static void ds1_uv_error(struct vpe_ctx *ctx)
+{
+       dev_warn(ctx->dev->v4l2_dev.dev,
+               "received downsampler error interrupt\n");
+}
+
+static irqreturn_t vpe_irq(int irq_vpe, void *data)
+{
+       struct vpe_dev *dev = (struct vpe_dev *)data;
+       struct vpe_ctx *ctx;
+       struct vpe_q_data *d_q_data;
+       struct vb2_buffer *s_vb, *d_vb;
+       struct v4l2_buffer *s_buf, *d_buf;
+       unsigned long flags;
+       u32 irqst0, irqst1;
+
+       irqst0 = read_reg(dev, VPE_INT0_STATUS0);
+       if (irqst0) {
+               write_reg(dev, VPE_INT0_STATUS0_CLR, irqst0);
+               vpe_dbg(dev, "INT0_STATUS0 = 0x%08x\n", irqst0);
+       }
+
+       irqst1 = read_reg(dev, VPE_INT0_STATUS1);
+       if (irqst1) {
+               write_reg(dev, VPE_INT0_STATUS1_CLR, irqst1);
+               vpe_dbg(dev, "INT0_STATUS1 = 0x%08x\n", irqst1);
+       }
+
+       ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+       if (!ctx) {
+               vpe_err(dev, "instance released before end of transaction\n");
+               goto handled;
+       }
+
+       if (irqst1) {
+               if (irqst1 & VPE_DEI_ERROR_INT) {
+                       irqst1 &= ~VPE_DEI_ERROR_INT;
+                       dei_error(ctx);
+               }
+               if (irqst1 & VPE_DS1_UV_ERROR_INT) {
+                       irqst1 &= ~VPE_DS1_UV_ERROR_INT;
+                       ds1_uv_error(ctx);
+               }
+       }
+
+       if (irqst0) {
+               if (irqst0 & VPE_INT0_LIST0_COMPLETE)
+                       vpdma_clear_list_stat(ctx->dev->vpdma);
+
+               irqst0 &= ~(VPE_INT0_LIST0_COMPLETE);
+       }
+
+       if (irqst0 | irqst1) {
+               dev_warn(dev->v4l2_dev.dev, "Unexpected interrupt: "
+                       "INT0_STATUS0 = 0x%08x, INT0_STATUS1 = 0x%08x\n",
+                       irqst0, irqst1);
+       }
+
+       disable_irqs(ctx);
+
+       vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf);
+       vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb);
+
+       vpdma_reset_desc_list(&ctx->desc_list);
+
+        /* the previous dst mv buffer becomes the next src mv buffer */
+       ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector;
+
+       if (ctx->aborting)
+               goto finished;
+
+       s_vb = ctx->src_vbs[0];
+       d_vb = ctx->dst_vb;
+       s_buf = &s_vb->v4l2_buf;
+       d_buf = &d_vb->v4l2_buf;
+
+       d_buf->timestamp = s_buf->timestamp;
+       if (s_buf->flags & V4L2_BUF_FLAG_TIMECODE) {
+               d_buf->flags |= V4L2_BUF_FLAG_TIMECODE;
+               d_buf->timecode = s_buf->timecode;
+       }
+       d_buf->sequence = ctx->sequence;
+       d_buf->field = ctx->field;
+
+       d_q_data = &ctx->q_data[Q_DATA_DST];
+       if (d_q_data->flags & Q_DATA_INTERLACED) {
+               if (ctx->field == V4L2_FIELD_BOTTOM) {
+                       ctx->sequence++;
+                       ctx->field = V4L2_FIELD_TOP;
+               } else {
+                       WARN_ON(ctx->field != V4L2_FIELD_TOP);
+                       ctx->field = V4L2_FIELD_BOTTOM;
+               }
+       } else {
+               ctx->sequence++;
+       }
+
+       if (ctx->deinterlacing)
+               s_vb = ctx->src_vbs[2];
+
+       spin_lock_irqsave(&dev->lock, flags);
+       v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE);
+       v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE);
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       if (ctx->deinterlacing) {
+               ctx->src_vbs[2] = ctx->src_vbs[1];
+               ctx->src_vbs[1] = ctx->src_vbs[0];
+       }
+
+       ctx->bufs_completed++;
+       if (ctx->bufs_completed < ctx->bufs_per_job) {
+               device_run(ctx);
+               goto handled;
+       }
+
+finished:
+       vpe_dbg(ctx->dev, "finishing transaction\n");
+       ctx->bufs_completed = 0;
+       v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
+handled:
+       return IRQ_HANDLED;
+}
+
+/*
+ * video ioctls
+ */
+static int vpe_querycap(struct file *file, void *priv,
+                       struct v4l2_capability *cap)
+{
+       strncpy(cap->driver, VPE_MODULE_NAME, sizeof(cap->driver) - 1);
+       strncpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card) - 1);
+       strlcpy(cap->bus_info, VPE_MODULE_NAME, sizeof(cap->bus_info));
+       cap->device_caps  = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int __enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+       int i, index;
+       struct vpe_fmt *fmt = NULL;
+
+       index = 0;
+       for (i = 0; i < ARRAY_SIZE(vpe_formats); ++i) {
+               if (vpe_formats[i].types & type) {
+                       if (index == f->index) {
+                               fmt = &vpe_formats[i];
+                               break;
+                       }
+                       index++;
+               }
+       }
+
+       if (!fmt)
+               return -EINVAL;
+
+       strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+       f->pixelformat = fmt->fourcc;
+       return 0;
+}
+
+static int vpe_enum_fmt(struct file *file, void *priv,
+                               struct v4l2_fmtdesc *f)
+{
+       if (V4L2_TYPE_IS_OUTPUT(f->type))
+               return __enum_fmt(f, VPE_FMT_TYPE_OUTPUT);
+
+       return __enum_fmt(f, VPE_FMT_TYPE_CAPTURE);
+}
+
+static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+       struct vpe_ctx *ctx = file2ctx(file);
+       struct vb2_queue *vq;
+       struct vpe_q_data *q_data;
+       int i;
+
+       vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+       if (!vq)
+               return -EINVAL;
+
+       q_data = get_q_data(ctx, f->type);
+
+       pix->width = q_data->width;
+       pix->height = q_data->height;
+       pix->pixelformat = q_data->fmt->fourcc;
+       pix->field = q_data->field;
+
+       if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+               pix->colorspace = q_data->colorspace;
+       } else {
+               struct vpe_q_data *s_q_data;
+
+               /* get colorspace from the source queue */
+               s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+               pix->colorspace = s_q_data->colorspace;
+       }
+
+       pix->num_planes = q_data->fmt->coplanar ? 2 : 1;
+
+       for (i = 0; i < pix->num_planes; i++) {
+               pix->plane_fmt[i].bytesperline = q_data->bytesperline[i];
+               pix->plane_fmt[i].sizeimage = q_data->sizeimage[i];
+       }
+
+       return 0;
+}
+
+static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
+                      struct vpe_fmt *fmt, int type)
+{
+       struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+       struct v4l2_plane_pix_format *plane_fmt;
+       int i;
+
+       if (!fmt || !(fmt->types & type)) {
+               vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
+                       pix->pixelformat);
+               return -EINVAL;
+       }
+
+       if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE)
+               pix->field = V4L2_FIELD_NONE;
+
+       v4l_bound_align_image(&pix->width, MIN_W, MAX_W, W_ALIGN,
+                             &pix->height, MIN_H, MAX_H, H_ALIGN,
+                             S_ALIGN);
+
+       pix->num_planes = fmt->coplanar ? 2 : 1;
+       pix->pixelformat = fmt->fourcc;
+
+       if (type == VPE_FMT_TYPE_CAPTURE) {
+               struct vpe_q_data *s_q_data;
+
+               /* get colorspace from the source queue */
+               s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+               pix->colorspace = s_q_data->colorspace;
+       } else {
+               if (!pix->colorspace)
+                       pix->colorspace = V4L2_COLORSPACE_SMPTE240M;
+       }
+
+       for (i = 0; i < pix->num_planes; i++) {
+               int depth;
+
+               plane_fmt = &pix->plane_fmt[i];
+               depth = fmt->vpdma_fmt[i]->depth;
+
+               if (i == VPE_LUMA)
+                       plane_fmt->bytesperline =
+                                       round_up((pix->width * depth) >> 3,
+                                               1 << L_ALIGN);
+               else
+                       plane_fmt->bytesperline = pix->width;
+
+               plane_fmt->sizeimage =
+                               (pix->height * pix->width * depth) >> 3;
+       }
+
+       return 0;
+}
+
+static int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+       struct vpe_fmt *fmt = find_format(f);
+
+       if (V4L2_TYPE_IS_OUTPUT(f->type))
+               return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_OUTPUT);
+       else
+               return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_CAPTURE);
+}
+
+static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f)
+{
+       struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+       struct v4l2_plane_pix_format *plane_fmt;
+       struct vpe_q_data *q_data;
+       struct vb2_queue *vq;
+       int i;
+
+       vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+       if (!vq)
+               return -EINVAL;
+
+       if (vb2_is_busy(vq)) {
+               vpe_err(ctx->dev, "queue busy\n");
+               return -EBUSY;
+       }
+
+       q_data = get_q_data(ctx, f->type);
+       if (!q_data)
+               return -EINVAL;
+
+       q_data->fmt             = find_format(f);
+       q_data->width           = pix->width;
+       q_data->height          = pix->height;
+       q_data->colorspace      = pix->colorspace;
+       q_data->field           = pix->field;
+
+       for (i = 0; i < pix->num_planes; i++) {
+               plane_fmt = &pix->plane_fmt[i];
+
+               q_data->bytesperline[i] = plane_fmt->bytesperline;
+               q_data->sizeimage[i]    = plane_fmt->sizeimage;
+       }
+
+       q_data->c_rect.left     = 0;
+       q_data->c_rect.top      = 0;
+       q_data->c_rect.width    = q_data->width;
+       q_data->c_rect.height   = q_data->height;
+
+       if (q_data->field == V4L2_FIELD_ALTERNATE)
+               q_data->flags |= Q_DATA_INTERLACED;
+       else
+               q_data->flags &= ~Q_DATA_INTERLACED;
+
+       vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d",
+               f->type, q_data->width, q_data->height, q_data->fmt->fourcc,
+               q_data->bytesperline[VPE_LUMA]);
+       if (q_data->fmt->coplanar)
+               vpe_dbg(ctx->dev, " bpl_uv %d\n",
+                       q_data->bytesperline[VPE_CHROMA]);
+
+       return 0;
+}
+
+static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       int ret;
+       struct vpe_ctx *ctx = file2ctx(file);
+
+       ret = vpe_try_fmt(file, priv, f);
+       if (ret)
+               return ret;
+
+       ret = __vpe_s_fmt(ctx, f);
+       if (ret)
+               return ret;
+
+       if (V4L2_TYPE_IS_OUTPUT(f->type))
+               set_src_registers(ctx);
+       else
+               set_dst_registers(ctx);
+
+       return set_srcdst_params(ctx);
+}
+
+static int vpe_reqbufs(struct file *file, void *priv,
+                      struct v4l2_requestbuffers *reqbufs)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+
+       return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vpe_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+
+       return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vpe_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+
+       return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vpe_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+
+       return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vpe_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+
+       return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vpe_streamoff(struct file *file, void *priv, enum v4l2_buf_type type)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+
+       vpe_dump_regs(ctx->dev);
+       vpdma_dump_regs(ctx->dev->vpdma);
+
+       return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+/*
+ * defines number of buffers/frames a context can process with VPE before
+ * switching to a different context. default value is 1 buffer per context
+ */
+#define V4L2_CID_VPE_BUFS_PER_JOB              (V4L2_CID_USER_TI_VPE_BASE + 0)
+
+static int vpe_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vpe_ctx *ctx =
+               container_of(ctrl->handler, struct vpe_ctx, hdl);
+
+       switch (ctrl->id) {
+       case V4L2_CID_VPE_BUFS_PER_JOB:
+               ctx->bufs_per_job = ctrl->val;
+               break;
+
+       default:
+               vpe_err(ctx->dev, "Invalid control\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vpe_ctrl_ops = {
+       .s_ctrl = vpe_s_ctrl,
+};
+
+static const struct v4l2_ioctl_ops vpe_ioctl_ops = {
+       .vidioc_querycap        = vpe_querycap,
+
+       .vidioc_enum_fmt_vid_cap_mplane = vpe_enum_fmt,
+       .vidioc_g_fmt_vid_cap_mplane    = vpe_g_fmt,
+       .vidioc_try_fmt_vid_cap_mplane  = vpe_try_fmt,
+       .vidioc_s_fmt_vid_cap_mplane    = vpe_s_fmt,
+
+       .vidioc_enum_fmt_vid_out_mplane = vpe_enum_fmt,
+       .vidioc_g_fmt_vid_out_mplane    = vpe_g_fmt,
+       .vidioc_try_fmt_vid_out_mplane  = vpe_try_fmt,
+       .vidioc_s_fmt_vid_out_mplane    = vpe_s_fmt,
+
+       .vidioc_reqbufs         = vpe_reqbufs,
+       .vidioc_querybuf        = vpe_querybuf,
+
+       .vidioc_qbuf            = vpe_qbuf,
+       .vidioc_dqbuf           = vpe_dqbuf,
+
+       .vidioc_streamon        = vpe_streamon,
+       .vidioc_streamoff       = vpe_streamoff,
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*
+ * Queue operations
+ */
+static int vpe_queue_setup(struct vb2_queue *vq,
+                          const struct v4l2_format *fmt,
+                          unsigned int *nbuffers, unsigned int *nplanes,
+                          unsigned int sizes[], void *alloc_ctxs[])
+{
+       int i;
+       struct vpe_ctx *ctx = vb2_get_drv_priv(vq);
+       struct vpe_q_data *q_data;
+
+       q_data = get_q_data(ctx, vq->type);
+
+       *nplanes = q_data->fmt->coplanar ? 2 : 1;
+
+       for (i = 0; i < *nplanes; i++) {
+               sizes[i] = q_data->sizeimage[i];
+               alloc_ctxs[i] = ctx->dev->alloc_ctx;
+       }
+
+       vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers,
+               sizes[VPE_LUMA]);
+       if (q_data->fmt->coplanar)
+               vpe_dbg(ctx->dev, " and %d\n", sizes[VPE_CHROMA]);
+
+       return 0;
+}
+
+static int vpe_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+       struct vpe_q_data *q_data;
+       int i, num_planes;
+
+       vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+
+       q_data = get_q_data(ctx, vb->vb2_queue->type);
+       num_planes = q_data->fmt->coplanar ? 2 : 1;
+
+       for (i = 0; i < num_planes; i++) {
+               if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) {
+                       vpe_err(ctx->dev,
+                               "data will not fit into plane (%lu < %lu)\n",
+                               vb2_plane_size(vb, i),
+                               (long) q_data->sizeimage[i]);
+                       return -EINVAL;
+               }
+       }
+
+       for (i = 0; i < num_planes; i++)
+               vb2_set_plane_payload(vb, i, q_data->sizeimage[i]);
+
+       return 0;
+}
+
+static void vpe_buf_queue(struct vb2_buffer *vb)
+{
+       struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+       v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static void vpe_wait_prepare(struct vb2_queue *q)
+{
+       struct vpe_ctx *ctx = vb2_get_drv_priv(q);
+       vpe_unlock(ctx);
+}
+
+static void vpe_wait_finish(struct vb2_queue *q)
+{
+       struct vpe_ctx *ctx = vb2_get_drv_priv(q);
+       vpe_lock(ctx);
+}
+
+static struct vb2_ops vpe_qops = {
+       .queue_setup     = vpe_queue_setup,
+       .buf_prepare     = vpe_buf_prepare,
+       .buf_queue       = vpe_buf_queue,
+       .wait_prepare    = vpe_wait_prepare,
+       .wait_finish     = vpe_wait_finish,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+                     struct vb2_queue *dst_vq)
+{
+       struct vpe_ctx *ctx = priv;
+       int ret;
+
+       memset(src_vq, 0, sizeof(*src_vq));
+       src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+       src_vq->io_modes = VB2_MMAP;
+       src_vq->drv_priv = ctx;
+       src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+       src_vq->ops = &vpe_qops;
+       src_vq->mem_ops = &vb2_dma_contig_memops;
+       src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+       ret = vb2_queue_init(src_vq);
+       if (ret)
+               return ret;
+
+       memset(dst_vq, 0, sizeof(*dst_vq));
+       dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+       dst_vq->io_modes = VB2_MMAP;
+       dst_vq->drv_priv = ctx;
+       dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+       dst_vq->ops = &vpe_qops;
+       dst_vq->mem_ops = &vb2_dma_contig_memops;
+       dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+       return vb2_queue_init(dst_vq);
+}
+
+static const struct v4l2_ctrl_config vpe_bufs_per_job = {
+       .ops = &vpe_ctrl_ops,
+       .id = V4L2_CID_VPE_BUFS_PER_JOB,
+       .name = "Buffers Per Transaction",
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .def = VPE_DEF_BUFS_PER_JOB,
+       .min = 1,
+       .max = VIDEO_MAX_FRAME,
+       .step = 1,
+};
+
+/*
+ * File operations
+ */
+static int vpe_open(struct file *file)
+{
+       struct vpe_dev *dev = video_drvdata(file);
+       struct vpe_ctx *ctx = NULL;
+       struct vpe_q_data *s_q_data;
+       struct v4l2_ctrl_handler *hdl;
+       int ret;
+
+       vpe_dbg(dev, "vpe_open\n");
+
+       if (!dev->vpdma->ready) {
+               vpe_err(dev, "vpdma firmware not loaded\n");
+               return -ENODEV;
+       }
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->dev = dev;
+
+       if (mutex_lock_interruptible(&dev->dev_mutex)) {
+               ret = -ERESTARTSYS;
+               goto free_ctx;
+       }
+
+       ret = vpdma_create_desc_list(&ctx->desc_list, VPE_DESC_LIST_SIZE,
+                       VPDMA_LIST_TYPE_NORMAL);
+       if (ret != 0)
+               goto unlock;
+
+       ret = vpdma_alloc_desc_buf(&ctx->mmr_adb, sizeof(struct vpe_mmr_adb));
+       if (ret != 0)
+               goto free_desc_list;
+
+       init_adb_hdrs(ctx);
+
+       v4l2_fh_init(&ctx->fh, video_devdata(file));
+       file->private_data = &ctx->fh;
+
+       hdl = &ctx->hdl;
+       v4l2_ctrl_handler_init(hdl, 1);
+       v4l2_ctrl_new_custom(hdl, &vpe_bufs_per_job, NULL);
+       if (hdl->error) {
+               ret = hdl->error;
+               goto exit_fh;
+       }
+       ctx->fh.ctrl_handler = hdl;
+       v4l2_ctrl_handler_setup(hdl);
+
+       s_q_data = &ctx->q_data[Q_DATA_SRC];
+       s_q_data->fmt = &vpe_formats[2];
+       s_q_data->width = 1920;
+       s_q_data->height = 1080;
+       s_q_data->sizeimage[VPE_LUMA] = (s_q_data->width * s_q_data->height *
+                       s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3;
+       s_q_data->colorspace = V4L2_COLORSPACE_SMPTE240M;
+       s_q_data->field = V4L2_FIELD_NONE;
+       s_q_data->c_rect.left = 0;
+       s_q_data->c_rect.top = 0;
+       s_q_data->c_rect.width = s_q_data->width;
+       s_q_data->c_rect.height = s_q_data->height;
+       s_q_data->flags = 0;
+
+       ctx->q_data[Q_DATA_DST] = *s_q_data;
+
+       set_dei_shadow_registers(ctx);
+       set_src_registers(ctx);
+       set_dst_registers(ctx);
+       ret = set_srcdst_params(ctx);
+       if (ret)
+               goto exit_fh;
+
+       ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+
+       if (IS_ERR(ctx->m2m_ctx)) {
+               ret = PTR_ERR(ctx->m2m_ctx);
+               goto exit_fh;
+       }
+
+       v4l2_fh_add(&ctx->fh);
+
+       /*
+        * for now, just report the creation of the first instance, we can later
+        * optimize the driver to enable or disable clocks when the first
+        * instance is created or the last instance released
+        */
+       if (atomic_inc_return(&dev->num_instances) == 1)
+               vpe_dbg(dev, "first instance created\n");
+
+       ctx->bufs_per_job = VPE_DEF_BUFS_PER_JOB;
+
+       ctx->load_mmrs = true;
+
+       vpe_dbg(dev, "created instance %p, m2m_ctx: %p\n",
+               ctx, ctx->m2m_ctx);
+
+       mutex_unlock(&dev->dev_mutex);
+
+       return 0;
+exit_fh:
+       v4l2_ctrl_handler_free(hdl);
+       v4l2_fh_exit(&ctx->fh);
+       vpdma_free_desc_buf(&ctx->mmr_adb);
+free_desc_list:
+       vpdma_free_desc_list(&ctx->desc_list);
+unlock:
+       mutex_unlock(&dev->dev_mutex);
+free_ctx:
+       kfree(ctx);
+       return ret;
+}
+
+static int vpe_release(struct file *file)
+{
+       struct vpe_dev *dev = video_drvdata(file);
+       struct vpe_ctx *ctx = file2ctx(file);
+
+       vpe_dbg(dev, "releasing instance %p\n", ctx);
+
+       mutex_lock(&dev->dev_mutex);
+       free_vbs(ctx);
+       free_mv_buffers(ctx);
+       vpdma_free_desc_list(&ctx->desc_list);
+       vpdma_free_desc_buf(&ctx->mmr_adb);
+
+       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);
+
+       /*
+        * for now, just report the release of the last instance, we can later
+        * optimize the driver to enable or disable clocks when the first
+        * instance is created or the last instance released
+        */
+       if (atomic_dec_return(&dev->num_instances) == 0)
+               vpe_dbg(dev, "last instance released\n");
+
+       mutex_unlock(&dev->dev_mutex);
+
+       return 0;
+}
+
+static unsigned int vpe_poll(struct file *file,
+                            struct poll_table_struct *wait)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+       struct vpe_dev *dev = ctx->dev;
+       int ret;
+
+       mutex_lock(&dev->dev_mutex);
+       ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+       mutex_unlock(&dev->dev_mutex);
+       return ret;
+}
+
+static int vpe_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+       struct vpe_dev *dev = ctx->dev;
+       int ret;
+
+       if (mutex_lock_interruptible(&dev->dev_mutex))
+               return -ERESTARTSYS;
+       ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+       mutex_unlock(&dev->dev_mutex);
+       return ret;
+}
+
+static const struct v4l2_file_operations vpe_fops = {
+       .owner          = THIS_MODULE,
+       .open           = vpe_open,
+       .release        = vpe_release,
+       .poll           = vpe_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = vpe_mmap,
+};
+
+static struct video_device vpe_videodev = {
+       .name           = VPE_MODULE_NAME,
+       .fops           = &vpe_fops,
+       .ioctl_ops      = &vpe_ioctl_ops,
+       .minor          = -1,
+       .release        = video_device_release,
+       .vfl_dir        = VFL_DIR_M2M,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+       .device_run     = device_run,
+       .job_ready      = job_ready,
+       .job_abort      = job_abort,
+       .lock           = vpe_lock,
+       .unlock         = vpe_unlock,
+};
+
+static int vpe_runtime_get(struct platform_device *pdev)
+{
+       int r;
+
+       dev_dbg(&pdev->dev, "vpe_runtime_get\n");
+
+       r = pm_runtime_get_sync(&pdev->dev);
+       WARN_ON(r < 0);
+       return r < 0 ? r : 0;
+}
+
+static void vpe_runtime_put(struct platform_device *pdev)
+{
+
+       int r;
+
+       dev_dbg(&pdev->dev, "vpe_runtime_put\n");
+
+       r = pm_runtime_put_sync(&pdev->dev);
+       WARN_ON(r < 0 && r != -ENOSYS);
+}
+
+static int vpe_probe(struct platform_device *pdev)
+{
+       struct vpe_dev *dev;
+       struct video_device *vfd;
+       struct resource *res;
+       int ret, irq, func;
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+       if (IS_ERR(dev))
+               return PTR_ERR(dev);
+
+       spin_lock_init(&dev->lock);
+
+       ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+       if (ret)
+               return ret;
+
+       atomic_set(&dev->num_instances, 0);
+       mutex_init(&dev->dev_mutex);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpe_top");
+       /*
+        * HACK: we get resource info from device tree in the form of a list of
+        * VPE sub blocks, the driver currently uses only the base of vpe_top
+        * for register access, the driver should be changed later to access
+        * registers based on the sub block base addresses
+        */
+       dev->base = devm_ioremap(&pdev->dev, res->start, SZ_32K);
+       if (IS_ERR(dev->base)) {
+               ret = PTR_ERR(dev->base);
+               goto v4l2_dev_unreg;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       ret = devm_request_irq(&pdev->dev, irq, vpe_irq, 0, VPE_MODULE_NAME,
+                       dev);
+       if (ret)
+               goto v4l2_dev_unreg;
+
+       platform_set_drvdata(pdev, dev);
+
+       dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+       if (IS_ERR(dev->alloc_ctx)) {
+               vpe_err(dev, "Failed to alloc vb2 context\n");
+               ret = PTR_ERR(dev->alloc_ctx);
+               goto v4l2_dev_unreg;
+       }
+
+       dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+       if (IS_ERR(dev->m2m_dev)) {
+               vpe_err(dev, "Failed to init mem2mem device\n");
+               ret = PTR_ERR(dev->m2m_dev);
+               goto rel_ctx;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+
+       ret = vpe_runtime_get(pdev);
+       if (ret)
+               goto rel_m2m;
+
+       /* Perform clk enable followed by reset */
+       vpe_set_clock_enable(dev, 1);
+
+       vpe_top_reset(dev);
+
+       func = read_field_reg(dev, VPE_PID, VPE_PID_FUNC_MASK,
+               VPE_PID_FUNC_SHIFT);
+       vpe_dbg(dev, "VPE PID function %x\n", func);
+
+       vpe_top_vpdma_reset(dev);
+
+       dev->vpdma = vpdma_create(pdev);
+       if (IS_ERR(dev->vpdma))
+               goto runtime_put;
+
+       vfd = &dev->vfd;
+       *vfd = vpe_videodev;
+       vfd->lock = &dev->dev_mutex;
+       vfd->v4l2_dev = &dev->v4l2_dev;
+
+       ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+       if (ret) {
+               vpe_err(dev, "Failed to register video device\n");
+               goto runtime_put;
+       }
+
+       video_set_drvdata(vfd, dev);
+       snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name);
+       dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n",
+               vfd->num);
+
+       return 0;
+
+runtime_put:
+       vpe_runtime_put(pdev);
+rel_m2m:
+       pm_runtime_disable(&pdev->dev);
+       v4l2_m2m_release(dev->m2m_dev);
+rel_ctx:
+       vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+v4l2_dev_unreg:
+       v4l2_device_unregister(&dev->v4l2_dev);
+
+       return ret;
+}
+
+static int vpe_remove(struct platform_device *pdev)
+{
+       struct vpe_dev *dev =
+               (struct vpe_dev *) platform_get_drvdata(pdev);
+
+       v4l2_info(&dev->v4l2_dev, "Removing " VPE_MODULE_NAME);
+
+       v4l2_m2m_release(dev->m2m_dev);
+       video_unregister_device(&dev->vfd);
+       v4l2_device_unregister(&dev->v4l2_dev);
+       vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+
+       vpe_set_clock_enable(dev, 0);
+       vpe_runtime_put(pdev);
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id vpe_of_match[] = {
+       {
+               .compatible = "ti,vpe",
+       },
+       {},
+};
+#else
+#define vpe_of_match NULL
+#endif
+
+static struct platform_driver vpe_pdrv = {
+       .probe          = vpe_probe,
+       .remove         = vpe_remove,
+       .driver         = {
+               .name   = VPE_MODULE_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = vpe_of_match,
+       },
+};
+
+static void __exit vpe_exit(void)
+{
+       platform_driver_unregister(&vpe_pdrv);
+}
+
+static int __init vpe_init(void)
+{
+       return platform_driver_register(&vpe_pdrv);
+}
+
+module_init(vpe_init);
+module_exit(vpe_exit);
+
+MODULE_DESCRIPTION("TI VPE driver");
+MODULE_AUTHOR("Dale Farnsworth, <dale@farnsworth.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/ti-vpe/vpe_regs.h b/drivers/media/platform/ti-vpe/vpe_regs.h
new file mode 100644 (file)
index 0000000..ed214e8
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __TI_VPE_REGS_H
+#define __TI_VPE_REGS_H
+
+/* VPE register offsets and field selectors */
+
+/* VPE top level regs */
+#define VPE_PID                                0x0000
+#define VPE_PID_MINOR_MASK             0x3f
+#define VPE_PID_MINOR_SHIFT            0
+#define VPE_PID_CUSTOM_MASK            0x03
+#define VPE_PID_CUSTOM_SHIFT           6
+#define VPE_PID_MAJOR_MASK             0x07
+#define VPE_PID_MAJOR_SHIFT            8
+#define VPE_PID_RTL_MASK               0x1f
+#define VPE_PID_RTL_SHIFT              11
+#define VPE_PID_FUNC_MASK              0xfff
+#define VPE_PID_FUNC_SHIFT             16
+#define VPE_PID_SCHEME_MASK            0x03
+#define VPE_PID_SCHEME_SHIFT           30
+
+#define VPE_SYSCONFIG                  0x0010
+#define VPE_SYSCONFIG_IDLE_MASK                0x03
+#define VPE_SYSCONFIG_IDLE_SHIFT       2
+#define VPE_SYSCONFIG_STANDBY_MASK     0x03
+#define VPE_SYSCONFIG_STANDBY_SHIFT    4
+#define VPE_FORCE_IDLE_MODE            0
+#define VPE_NO_IDLE_MODE               1
+#define VPE_SMART_IDLE_MODE            2
+#define VPE_SMART_IDLE_WAKEUP_MODE     3
+#define VPE_FORCE_STANDBY_MODE         0
+#define VPE_NO_STANDBY_MODE            1
+#define VPE_SMART_STANDBY_MODE         2
+#define VPE_SMART_STANDBY_WAKEUP_MODE  3
+
+#define VPE_INT0_STATUS0_RAW_SET       0x0020
+#define VPE_INT0_STATUS0_RAW           VPE_INT0_STATUS0_RAW_SET
+#define VPE_INT0_STATUS0_CLR           0x0028
+#define VPE_INT0_STATUS0               VPE_INT0_STATUS0_CLR
+#define VPE_INT0_ENABLE0_SET           0x0030
+#define VPE_INT0_ENABLE0               VPE_INT0_ENABLE0_SET
+#define VPE_INT0_ENABLE0_CLR           0x0038
+#define VPE_INT0_LIST0_COMPLETE                (1 << 0)
+#define VPE_INT0_LIST0_NOTIFY          (1 << 1)
+#define VPE_INT0_LIST1_COMPLETE                (1 << 2)
+#define VPE_INT0_LIST1_NOTIFY          (1 << 3)
+#define VPE_INT0_LIST2_COMPLETE                (1 << 4)
+#define VPE_INT0_LIST2_NOTIFY          (1 << 5)
+#define VPE_INT0_LIST3_COMPLETE                (1 << 6)
+#define VPE_INT0_LIST3_NOTIFY          (1 << 7)
+#define VPE_INT0_LIST4_COMPLETE                (1 << 8)
+#define VPE_INT0_LIST4_NOTIFY          (1 << 9)
+#define VPE_INT0_LIST5_COMPLETE                (1 << 10)
+#define VPE_INT0_LIST5_NOTIFY          (1 << 11)
+#define VPE_INT0_LIST6_COMPLETE                (1 << 12)
+#define VPE_INT0_LIST6_NOTIFY          (1 << 13)
+#define VPE_INT0_LIST7_COMPLETE                (1 << 14)
+#define VPE_INT0_LIST7_NOTIFY          (1 << 15)
+#define VPE_INT0_DESCRIPTOR            (1 << 16)
+#define VPE_DEI_FMD_INT                        (1 << 18)
+
+#define VPE_INT0_STATUS1_RAW_SET       0x0024
+#define VPE_INT0_STATUS1_RAW           VPE_INT0_STATUS1_RAW_SET
+#define VPE_INT0_STATUS1_CLR           0x002c
+#define VPE_INT0_STATUS1               VPE_INT0_STATUS1_CLR
+#define VPE_INT0_ENABLE1_SET           0x0034
+#define VPE_INT0_ENABLE1               VPE_INT0_ENABLE1_SET
+#define VPE_INT0_ENABLE1_CLR           0x003c
+#define VPE_INT0_CHANNEL_GROUP0                (1 << 0)
+#define VPE_INT0_CHANNEL_GROUP1                (1 << 1)
+#define VPE_INT0_CHANNEL_GROUP2                (1 << 2)
+#define VPE_INT0_CHANNEL_GROUP3                (1 << 3)
+#define VPE_INT0_CHANNEL_GROUP4                (1 << 4)
+#define VPE_INT0_CHANNEL_GROUP5                (1 << 5)
+#define VPE_INT0_CLIENT                        (1 << 7)
+#define VPE_DEI_ERROR_INT              (1 << 16)
+#define VPE_DS1_UV_ERROR_INT           (1 << 22)
+
+#define VPE_INTC_EOI                   0x00a0
+
+#define VPE_CLK_ENABLE                 0x0100
+#define VPE_VPEDMA_CLK_ENABLE          (1 << 0)
+#define VPE_DATA_PATH_CLK_ENABLE       (1 << 1)
+
+#define VPE_CLK_RESET                  0x0104
+#define VPE_VPDMA_CLK_RESET_MASK       0x1
+#define VPE_VPDMA_CLK_RESET_SHIFT      0
+#define VPE_DATA_PATH_CLK_RESET_MASK   0x1
+#define VPE_DATA_PATH_CLK_RESET_SHIFT  1
+#define VPE_MAIN_RESET_MASK            0x1
+#define VPE_MAIN_RESET_SHIFT           31
+
+#define VPE_CLK_FORMAT_SELECT          0x010c
+#define VPE_CSC_SRC_SELECT_MASK                0x03
+#define VPE_CSC_SRC_SELECT_SHIFT       0
+#define VPE_RGB_OUT_SELECT             (1 << 8)
+#define VPE_DS_SRC_SELECT_MASK         0x07
+#define VPE_DS_SRC_SELECT_SHIFT                9
+#define VPE_DS_BYPASS                  (1 << 16)
+#define VPE_COLOR_SEPARATE_422         (1 << 18)
+
+#define VPE_DS_SRC_DEI_SCALER          (5 << VPE_DS_SRC_SELECT_SHIFT)
+#define VPE_CSC_SRC_DEI_SCALER         (3 << VPE_CSC_SRC_SELECT_SHIFT)
+
+#define VPE_CLK_RANGE_MAP              0x011c
+#define VPE_RANGE_RANGE_MAP_Y_MASK     0x07
+#define VPE_RANGE_RANGE_MAP_Y_SHIFT    0
+#define VPE_RANGE_RANGE_MAP_UV_MASK    0x07
+#define VPE_RANGE_RANGE_MAP_UV_SHIFT   3
+#define VPE_RANGE_MAP_ON               (1 << 6)
+#define VPE_RANGE_REDUCTION_ON         (1 << 28)
+
+/* VPE chrominance upsampler regs */
+#define VPE_US1_R0                     0x0304
+#define VPE_US2_R0                     0x0404
+#define VPE_US3_R0                     0x0504
+#define VPE_US_C1_MASK                 0x3fff
+#define VPE_US_C1_SHIFT                        2
+#define VPE_US_C0_MASK                 0x3fff
+#define VPE_US_C0_SHIFT                        18
+#define VPE_US_MODE_MASK               0x03
+#define VPE_US_MODE_SHIFT              16
+#define VPE_ANCHOR_FID0_C1_MASK                0x3fff
+#define VPE_ANCHOR_FID0_C1_SHIFT       2
+#define VPE_ANCHOR_FID0_C0_MASK                0x3fff
+#define VPE_ANCHOR_FID0_C0_SHIFT       18
+
+#define VPE_US1_R1                     0x0308
+#define VPE_US2_R1                     0x0408
+#define VPE_US3_R1                     0x0508
+#define VPE_ANCHOR_FID0_C3_MASK                0x3fff
+#define VPE_ANCHOR_FID0_C3_SHIFT       2
+#define VPE_ANCHOR_FID0_C2_MASK                0x3fff
+#define VPE_ANCHOR_FID0_C2_SHIFT       18
+
+#define VPE_US1_R2                     0x030c
+#define VPE_US2_R2                     0x040c
+#define VPE_US3_R2                     0x050c
+#define VPE_INTERP_FID0_C1_MASK                0x3fff
+#define VPE_INTERP_FID0_C1_SHIFT       2
+#define VPE_INTERP_FID0_C0_MASK                0x3fff
+#define VPE_INTERP_FID0_C0_SHIFT       18
+
+#define VPE_US1_R3                     0x0310
+#define VPE_US2_R3                     0x0410
+#define VPE_US3_R3                     0x0510
+#define VPE_INTERP_FID0_C3_MASK                0x3fff
+#define VPE_INTERP_FID0_C3_SHIFT       2
+#define VPE_INTERP_FID0_C2_MASK                0x3fff
+#define VPE_INTERP_FID0_C2_SHIFT       18
+
+#define VPE_US1_R4                     0x0314
+#define VPE_US2_R4                     0x0414
+#define VPE_US3_R4                     0x0514
+#define VPE_ANCHOR_FID1_C1_MASK                0x3fff
+#define VPE_ANCHOR_FID1_C1_SHIFT       2
+#define VPE_ANCHOR_FID1_C0_MASK                0x3fff
+#define VPE_ANCHOR_FID1_C0_SHIFT       18
+
+#define VPE_US1_R5                     0x0318
+#define VPE_US2_R5                     0x0418
+#define VPE_US3_R5                     0x0518
+#define VPE_ANCHOR_FID1_C3_MASK                0x3fff
+#define VPE_ANCHOR_FID1_C3_SHIFT       2
+#define VPE_ANCHOR_FID1_C2_MASK                0x3fff
+#define VPE_ANCHOR_FID1_C2_SHIFT       18
+
+#define VPE_US1_R6                     0x031c
+#define VPE_US2_R6                     0x041c
+#define VPE_US3_R6                     0x051c
+#define VPE_INTERP_FID1_C1_MASK                0x3fff
+#define VPE_INTERP_FID1_C1_SHIFT       2
+#define VPE_INTERP_FID1_C0_MASK                0x3fff
+#define VPE_INTERP_FID1_C0_SHIFT       18
+
+#define VPE_US1_R7                     0x0320
+#define VPE_US2_R7                     0x0420
+#define VPE_US3_R7                     0x0520
+#define VPE_INTERP_FID0_C3_MASK                0x3fff
+#define VPE_INTERP_FID0_C3_SHIFT       2
+#define VPE_INTERP_FID0_C2_MASK                0x3fff
+#define VPE_INTERP_FID0_C2_SHIFT       18
+
+/* VPE de-interlacer regs */
+#define VPE_DEI_FRAME_SIZE             0x0600
+#define VPE_DEI_WIDTH_MASK             0x07ff
+#define VPE_DEI_WIDTH_SHIFT            0
+#define VPE_DEI_HEIGHT_MASK            0x07ff
+#define VPE_DEI_HEIGHT_SHIFT           16
+#define VPE_DEI_INTERLACE_BYPASS       (1 << 29)
+#define VPE_DEI_FIELD_FLUSH            (1 << 30)
+#define VPE_DEI_PROGRESSIVE            (1 << 31)
+
+#define VPE_MDT_BYPASS                 0x0604
+#define VPE_MDT_TEMPMAX_BYPASS         (1 << 0)
+#define VPE_MDT_SPATMAX_BYPASS         (1 << 1)
+
+#define VPE_MDT_SF_THRESHOLD           0x0608
+#define VPE_MDT_SF_SC_THR1_MASK                0xff
+#define VPE_MDT_SF_SC_THR1_SHIFT       0
+#define VPE_MDT_SF_SC_THR2_MASK                0xff
+#define VPE_MDT_SF_SC_THR2_SHIFT       0
+#define VPE_MDT_SF_SC_THR3_MASK                0xff
+#define VPE_MDT_SF_SC_THR3_SHIFT       0
+
+#define VPE_EDI_CONFIG                 0x060c
+#define VPE_EDI_INP_MODE_MASK          0x03
+#define VPE_EDI_INP_MODE_SHIFT         0
+#define VPE_EDI_ENABLE_3D              (1 << 2)
+#define VPE_EDI_ENABLE_CHROMA_3D       (1 << 3)
+#define VPE_EDI_CHROMA3D_COR_THR_MASK  0xff
+#define VPE_EDI_CHROMA3D_COR_THR_SHIFT 8
+#define VPE_EDI_DIR_COR_LOWER_THR_MASK 0xff
+#define VPE_EDI_DIR_COR_LOWER_THR_SHIFT        16
+#define VPE_EDI_COR_SCALE_FACTOR_MASK  0xff
+#define VPE_EDI_COR_SCALE_FACTOR_SHIFT 23
+
+#define VPE_DEI_EDI_LUT_R0             0x0610
+#define VPE_EDI_LUT0_MASK              0x1f
+#define VPE_EDI_LUT0_SHIFT             0
+#define VPE_EDI_LUT1_MASK              0x1f
+#define VPE_EDI_LUT1_SHIFT             8
+#define VPE_EDI_LUT2_MASK              0x1f
+#define VPE_EDI_LUT2_SHIFT             16
+#define VPE_EDI_LUT3_MASK              0x1f
+#define VPE_EDI_LUT3_SHIFT             24
+
+#define VPE_DEI_EDI_LUT_R1             0x0614
+#define VPE_EDI_LUT0_MASK              0x1f
+#define VPE_EDI_LUT0_SHIFT             0
+#define VPE_EDI_LUT1_MASK              0x1f
+#define VPE_EDI_LUT1_SHIFT             8
+#define VPE_EDI_LUT2_MASK              0x1f
+#define VPE_EDI_LUT2_SHIFT             16
+#define VPE_EDI_LUT3_MASK              0x1f
+#define VPE_EDI_LUT3_SHIFT             24
+
+#define VPE_DEI_EDI_LUT_R2             0x0618
+#define VPE_EDI_LUT4_MASK              0x1f
+#define VPE_EDI_LUT4_SHIFT             0
+#define VPE_EDI_LUT5_MASK              0x1f
+#define VPE_EDI_LUT5_SHIFT             8
+#define VPE_EDI_LUT6_MASK              0x1f
+#define VPE_EDI_LUT6_SHIFT             16
+#define VPE_EDI_LUT7_MASK              0x1f
+#define VPE_EDI_LUT7_SHIFT             24
+
+#define VPE_DEI_EDI_LUT_R3             0x061c
+#define VPE_EDI_LUT8_MASK              0x1f
+#define VPE_EDI_LUT8_SHIFT             0
+#define VPE_EDI_LUT9_MASK              0x1f
+#define VPE_EDI_LUT9_SHIFT             8
+#define VPE_EDI_LUT10_MASK             0x1f
+#define VPE_EDI_LUT10_SHIFT            16
+#define VPE_EDI_LUT11_MASK             0x1f
+#define VPE_EDI_LUT11_SHIFT            24
+
+#define VPE_DEI_FMD_WINDOW_R0          0x0620
+#define VPE_FMD_WINDOW_MINX_MASK       0x07ff
+#define VPE_FMD_WINDOW_MINX_SHIFT      0
+#define VPE_FMD_WINDOW_MAXX_MASK       0x07ff
+#define VPE_FMD_WINDOW_MAXX_SHIFT      16
+#define VPE_FMD_WINDOW_ENABLE          (1 << 31)
+
+#define VPE_DEI_FMD_WINDOW_R1          0x0624
+#define VPE_FMD_WINDOW_MINY_MASK       0x07ff
+#define VPE_FMD_WINDOW_MINY_SHIFT      0
+#define VPE_FMD_WINDOW_MAXY_MASK       0x07ff
+#define VPE_FMD_WINDOW_MAXY_SHIFT      16
+
+#define VPE_DEI_FMD_CONTROL_R0         0x0628
+#define VPE_FMD_ENABLE                 (1 << 0)
+#define VPE_FMD_LOCK                   (1 << 1)
+#define VPE_FMD_JAM_DIR                        (1 << 2)
+#define VPE_FMD_BED_ENABLE             (1 << 3)
+#define VPE_FMD_CAF_FIELD_THR_MASK     0xff
+#define VPE_FMD_CAF_FIELD_THR_SHIFT    16
+#define VPE_FMD_CAF_LINE_THR_MASK      0xff
+#define VPE_FMD_CAF_LINE_THR_SHIFT     24
+
+#define VPE_DEI_FMD_CONTROL_R1         0x062c
+#define VPE_FMD_CAF_THR_MASK           0x000fffff
+#define VPE_FMD_CAF_THR_SHIFT          0
+
+#define VPE_DEI_FMD_STATUS_R0          0x0630
+#define VPE_FMD_CAF_MASK               0x000fffff
+#define VPE_FMD_CAF_SHIFT              0
+#define VPE_FMD_RESET                  (1 << 24)
+
+#define VPE_DEI_FMD_STATUS_R1          0x0634
+#define VPE_FMD_FIELD_DIFF_MASK                0x0fffffff
+#define VPE_FMD_FIELD_DIFF_SHIFT       0
+
+#define VPE_DEI_FMD_STATUS_R2          0x0638
+#define VPE_FMD_FRAME_DIFF_MASK                0x000fffff
+#define VPE_FMD_FRAME_DIFF_SHIFT       0
+
+/* VPE scaler regs */
+#define VPE_SC_MP_SC0                  0x0700
+#define VPE_INTERLACE_O                        (1 << 0)
+#define VPE_LINEAR                     (1 << 1)
+#define VPE_SC_BYPASS                  (1 << 2)
+#define VPE_INVT_FID                   (1 << 3)
+#define VPE_USE_RAV                    (1 << 4)
+#define VPE_ENABLE_EV                  (1 << 5)
+#define VPE_AUTO_HS                    (1 << 6)
+#define VPE_DCM_2X                     (1 << 7)
+#define VPE_DCM_4X                     (1 << 8)
+#define VPE_HP_BYPASS                  (1 << 9)
+#define VPE_INTERLACE_I                        (1 << 10)
+#define VPE_ENABLE_SIN2_VER_INTP       (1 << 11)
+#define VPE_Y_PK_EN                    (1 << 14)
+#define VPE_TRIM                       (1 << 15)
+#define VPE_SELFGEN_FID                        (1 << 16)
+
+#define VPE_SC_MP_SC1                  0x0704
+#define VPE_ROW_ACC_INC_MASK           0x07ffffff
+#define VPE_ROW_ACC_INC_SHIFT          0
+
+#define VPE_SC_MP_SC2                  0x0708
+#define VPE_ROW_ACC_OFFSET_MASK                0x0fffffff
+#define VPE_ROW_ACC_OFFSET_SHIFT       0
+
+#define VPE_SC_MP_SC3                  0x070c
+#define VPE_ROW_ACC_OFFSET_B_MASK      0x0fffffff
+#define VPE_ROW_ACC_OFFSET_B_SHIFT     0
+
+#define VPE_SC_MP_SC4                  0x0710
+#define VPE_TAR_H_MASK                 0x07ff
+#define VPE_TAR_H_SHIFT                        0
+#define VPE_TAR_W_MASK                 0x07ff
+#define VPE_TAR_W_SHIFT                        12
+#define VPE_LIN_ACC_INC_U_MASK         0x07
+#define VPE_LIN_ACC_INC_U_SHIFT                24
+#define VPE_NLIN_ACC_INIT_U_MASK       0x07
+#define VPE_NLIN_ACC_INIT_U_SHIFT      28
+
+#define VPE_SC_MP_SC5                  0x0714
+#define VPE_SRC_H_MASK                 0x07ff
+#define VPE_SRC_H_SHIFT                        0
+#define VPE_SRC_W_MASK                 0x07ff
+#define VPE_SRC_W_SHIFT                        12
+#define VPE_NLIN_ACC_INC_U_MASK                0x07
+#define VPE_NLIN_ACC_INC_U_SHIFT       24
+
+#define VPE_SC_MP_SC6                  0x0718
+#define VPE_ROW_ACC_INIT_RAV_MASK      0x03ff
+#define VPE_ROW_ACC_INIT_RAV_SHIFT     0
+#define VPE_ROW_ACC_INIT_RAV_B_MASK    0x03ff
+#define VPE_ROW_ACC_INIT_RAV_B_SHIFT   10
+
+#define VPE_SC_MP_SC8                  0x0720
+#define VPE_NLIN_LEFT_MASK             0x07ff
+#define VPE_NLIN_LEFT_SHIFT            0
+#define VPE_NLIN_RIGHT_MASK            0x07ff
+#define VPE_NLIN_RIGHT_SHIFT           12
+
+#define VPE_SC_MP_SC9                  0x0724
+#define VPE_LIN_ACC_INC                        VPE_SC_MP_SC9
+
+#define VPE_SC_MP_SC10                 0x0728
+#define VPE_NLIN_ACC_INIT              VPE_SC_MP_SC10
+
+#define VPE_SC_MP_SC11                 0x072c
+#define VPE_NLIN_ACC_INC               VPE_SC_MP_SC11
+
+#define VPE_SC_MP_SC12                 0x0730
+#define VPE_COL_ACC_OFFSET_MASK                0x01ffffff
+#define VPE_COL_ACC_OFFSET_SHIFT       0
+
+#define VPE_SC_MP_SC13                 0x0734
+#define VPE_SC_FACTOR_RAV_MASK         0x03ff
+#define VPE_SC_FACTOR_RAV_SHIFT                0
+#define VPE_CHROMA_INTP_THR_MASK       0x03ff
+#define VPE_CHROMA_INTP_THR_SHIFT      12
+#define VPE_DELTA_CHROMA_THR_MASK      0x0f
+#define VPE_DELTA_CHROMA_THR_SHIFT     24
+
+#define VPE_SC_MP_SC17                 0x0744
+#define VPE_EV_THR_MASK                        0x03ff
+#define VPE_EV_THR_SHIFT               12
+#define VPE_DELTA_LUMA_THR_MASK                0x0f
+#define VPE_DELTA_LUMA_THR_SHIFT       24
+#define VPE_DELTA_EV_THR_MASK          0x0f
+#define VPE_DELTA_EV_THR_SHIFT         28
+
+#define VPE_SC_MP_SC18                 0x0748
+#define VPE_HS_FACTOR_MASK             0x03ff
+#define VPE_HS_FACTOR_SHIFT            0
+#define VPE_CONF_DEFAULT_MASK          0x01ff
+#define VPE_CONF_DEFAULT_SHIFT         16
+
+#define VPE_SC_MP_SC19                 0x074c
+#define VPE_HPF_COEFF0_MASK            0xff
+#define VPE_HPF_COEFF0_SHIFT           0
+#define VPE_HPF_COEFF1_MASK            0xff
+#define VPE_HPF_COEFF1_SHIFT           8
+#define VPE_HPF_COEFF2_MASK            0xff
+#define VPE_HPF_COEFF2_SHIFT           16
+#define VPE_HPF_COEFF3_MASK            0xff
+#define VPE_HPF_COEFF3_SHIFT           23
+
+#define VPE_SC_MP_SC20                 0x0750
+#define VPE_HPF_COEFF4_MASK            0xff
+#define VPE_HPF_COEFF4_SHIFT           0
+#define VPE_HPF_COEFF5_MASK            0xff
+#define VPE_HPF_COEFF5_SHIFT           8
+#define VPE_HPF_NORM_SHIFT_MASK                0x07
+#define VPE_HPF_NORM_SHIFT_SHIFT       16
+#define VPE_NL_LIMIT_MASK              0x1ff
+#define VPE_NL_LIMIT_SHIFT             20
+
+#define VPE_SC_MP_SC21                 0x0754
+#define VPE_NL_LO_THR_MASK             0x01ff
+#define VPE_NL_LO_THR_SHIFT            0
+#define VPE_NL_LO_SLOPE_MASK           0xff
+#define VPE_NL_LO_SLOPE_SHIFT          16
+
+#define VPE_SC_MP_SC22                 0x0758
+#define VPE_NL_HI_THR_MASK             0x01ff
+#define VPE_NL_HI_THR_SHIFT            0
+#define VPE_NL_HI_SLOPE_SH_MASK                0x07
+#define VPE_NL_HI_SLOPE_SH_SHIFT       16
+
+#define VPE_SC_MP_SC23                 0x075c
+#define VPE_GRADIENT_THR_MASK          0x07ff
+#define VPE_GRADIENT_THR_SHIFT         0
+#define VPE_GRADIENT_THR_RANGE_MASK    0x0f
+#define VPE_GRADIENT_THR_RANGE_SHIFT   12
+#define VPE_MIN_GY_THR_MASK            0xff
+#define VPE_MIN_GY_THR_SHIFT           16
+#define VPE_MIN_GY_THR_RANGE_MASK      0x0f
+#define VPE_MIN_GY_THR_RANGE_SHIFT     28
+
+#define VPE_SC_MP_SC24                 0x0760
+#define VPE_ORG_H_MASK                 0x07ff
+#define VPE_ORG_H_SHIFT                        0
+#define VPE_ORG_W_MASK                 0x07ff
+#define VPE_ORG_W_SHIFT                        16
+
+#define VPE_SC_MP_SC25                 0x0764
+#define VPE_OFF_H_MASK                 0x07ff
+#define VPE_OFF_H_SHIFT                        0
+#define VPE_OFF_W_MASK                 0x07ff
+#define VPE_OFF_W_SHIFT                        16
+
+/* VPE color space converter regs */
+#define VPE_CSC_CSC00                  0x5700
+#define VPE_CSC_A0_MASK                        0x1fff
+#define VPE_CSC_A0_SHIFT               0
+#define VPE_CSC_B0_MASK                        0x1fff
+#define VPE_CSC_B0_SHIFT               16
+
+#define VPE_CSC_CSC01                  0x5704
+#define VPE_CSC_C0_MASK                        0x1fff
+#define VPE_CSC_C0_SHIFT               0
+#define VPE_CSC_A1_MASK                        0x1fff
+#define VPE_CSC_A1_SHIFT               16
+
+#define VPE_CSC_CSC02                  0x5708
+#define VPE_CSC_B1_MASK                        0x1fff
+#define VPE_CSC_B1_SHIFT               0
+#define VPE_CSC_C1_MASK                        0x1fff
+#define VPE_CSC_C1_SHIFT               16
+
+#define VPE_CSC_CSC03                  0x570c
+#define VPE_CSC_A2_MASK                        0x1fff
+#define VPE_CSC_A2_SHIFT               0
+#define VPE_CSC_B2_MASK                        0x1fff
+#define VPE_CSC_B2_SHIFT               16
+
+#define VPE_CSC_CSC04                  0x5710
+#define VPE_CSC_C2_MASK                        0x1fff
+#define VPE_CSC_C2_SHIFT               0
+#define VPE_CSC_D0_MASK                        0x0fff
+#define VPE_CSC_D0_SHIFT               16
+
+#define VPE_CSC_CSC05                  0x5714
+#define VPE_CSC_D1_MASK                        0x0fff
+#define VPE_CSC_D1_SHIFT               0
+#define VPE_CSC_D2_MASK                        0x0fff
+#define VPE_CSC_D2_SHIFT               16
+#define VPE_CSC_BYPASS                 (1 << 28)
+
+#endif
index b557caf..ccdadd6 100644 (file)
@@ -403,7 +403,7 @@ static int timblogiw_s_input(struct file *file, void  *priv, unsigned int input)
        return 0;
 }
 
-static int timblogiw_streamon(struct file *file, void  *priv, unsigned int type)
+static int timblogiw_streamon(struct file *file, void  *priv, enum v4l2_buf_type type)
 {
        struct video_device *vdev = video_devdata(file);
        struct timblogiw_fh *fh = priv;
@@ -420,7 +420,7 @@ static int timblogiw_streamon(struct file *file, void  *priv, unsigned int type)
 }
 
 static int timblogiw_streamoff(struct file *file, void  *priv,
-       unsigned int type)
+       enum v4l2_buf_type type)
 {
        struct video_device *vdev = video_devdata(file);
        struct timblogiw_fh *fh = priv;
@@ -565,7 +565,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
 
        desc = dmaengine_prep_slave_sg(fh->chan,
                buf->sg, sg_elems, DMA_DEV_TO_MEM,
-               DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
+               DMA_PREP_INTERRUPT);
        if (!desc) {
                spin_lock_irq(&fh->queue_lock);
                list_del_init(&vb->queue);
index 21db23b..fa39640 100644 (file)
@@ -123,7 +123,7 @@ static int keene_cmd_set(struct keene_device *radio)
        /* If bit 0 is set, then transmit mono, otherwise stereo.
           If bit 2 is set, then enable 75 us preemphasis, otherwise
           it is 50 us. */
-       radio->buffer[3] = (!radio->stereo) | (radio->preemph_75_us ? 4 : 0);
+       radio->buffer[3] = (radio->stereo ? 0 : 1) | (radio->preemph_75_us ? 4 : 0);
        radio->buffer[4] = 0x00;
        radio->buffer[5] = 0x00;
        radio->buffer[6] = 0x00;
index f1e3714..93d864e 100644 (file)
@@ -74,8 +74,8 @@ static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
        struct fmr2 *fmr2 = tea->private_data;
        u8 bits = inb(fmr2->io);
 
-       return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
-               (bits & STR_MOST) ? TEA575X_MOST : 0;
+       return  ((bits & STR_DATA) ? TEA575X_DATA : 0) |
+               ((bits & STR_MOST) ? TEA575X_MOST : 0);
 }
 
 static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
@@ -295,7 +295,6 @@ static void fmr2_remove(struct fmr2 *fmr2)
 static int fmr2_isa_remove(struct device *pdev, unsigned int ndev)
 {
        fmr2_remove(dev_get_drvdata(pdev));
-       dev_set_drvdata(pdev, NULL);
 
        return 0;
 }
index b914772..3db8a8c 100644 (file)
@@ -271,6 +271,7 @@ static void shark_unregister_leds(struct shark_device *shark)
        cancel_work_sync(&shark->led_work);
 }
 
+#ifdef CONFIG_PM
 static void shark_resume_leds(struct shark_device *shark)
 {
        if (test_bit(BLUE_IS_PULSE, &shark->brightness_new))
@@ -280,6 +281,7 @@ static void shark_resume_leds(struct shark_device *shark)
        set_bit(RED_LED, &shark->brightness_new);
        schedule_work(&shark->led_work);
 }
+#endif
 #else
 static int shark_register_leds(struct shark_device *shark, struct device *dev)
 {
index 9fb6697..d86d90d 100644 (file)
@@ -237,6 +237,7 @@ static void shark_unregister_leds(struct shark_device *shark)
        cancel_work_sync(&shark->led_work);
 }
 
+#ifdef CONFIG_PM
 static void shark_resume_leds(struct shark_device *shark)
 {
        int i;
@@ -246,6 +247,7 @@ static void shark_resume_leds(struct shark_device *shark)
 
        schedule_work(&shark->led_work);
 }
+#endif
 #else
 static int shark_register_leds(struct shark_device *shark, struct device *dev)
 {
index 97c2c18..9cf6731 100644 (file)
@@ -375,7 +375,7 @@ static int wl1273_fm_set_tx_freq(struct wl1273_device *radio, unsigned int freq)
        if (r)
                return r;
 
-       INIT_COMPLETION(radio->busy);
+       reinit_completion(&radio->busy);
 
        /* wait for the FR IRQ */
        r = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(2000));
@@ -389,7 +389,7 @@ static int wl1273_fm_set_tx_freq(struct wl1273_device *radio, unsigned int freq)
        if (r)
                return r;
 
-       INIT_COMPLETION(radio->busy);
+       reinit_completion(&radio->busy);
 
        /* wait for the POWER_ENB IRQ */
        r = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000));
@@ -444,7 +444,7 @@ static int wl1273_fm_set_rx_freq(struct wl1273_device *radio, unsigned int freq)
                goto err;
        }
 
-       INIT_COMPLETION(radio->busy);
+       reinit_completion(&radio->busy);
 
        r = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(2000));
        if (!r) {
@@ -805,7 +805,7 @@ static int wl1273_fm_set_seek(struct wl1273_device *radio,
        if (level < SCHAR_MIN || level > SCHAR_MAX)
                return -EINVAL;
 
-       INIT_COMPLETION(radio->busy);
+       reinit_completion(&radio->busy);
        dev_dbg(radio->dev, "%s: BUSY\n", __func__);
 
        r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
@@ -847,7 +847,7 @@ static int wl1273_fm_set_seek(struct wl1273_device *radio,
        if (r)
                goto out;
 
-       INIT_COMPLETION(radio->busy);
+       reinit_completion(&radio->busy);
        dev_dbg(radio->dev, "%s: BUSY\n", __func__);
 
        r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK);
index 5c57e5b..0e750ae 100644 (file)
@@ -218,7 +218,7 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
                goto done;
 
        /* wait till tune operation has completed */
-       INIT_COMPLETION(radio->completion);
+       reinit_completion(&radio->completion);
        retval = wait_for_completion_timeout(&radio->completion,
                        msecs_to_jiffies(tune_timeout));
        if (!retval)
@@ -254,7 +254,7 @@ static unsigned int si470x_get_step(struct si470x_device *radio)
        /* 2:  50 kHz */
        default:
                return 50 * 16;
-       };
+       }
 }
 
 
@@ -341,7 +341,7 @@ static int si470x_set_seek(struct si470x_device *radio,
                return retval;
 
        /* wait till tune operation has completed */
-       INIT_COMPLETION(radio->completion);
+       reinit_completion(&radio->completion);
        retval = wait_for_completion_timeout(&radio->completion,
                        msecs_to_jiffies(seek_timeout));
        if (!retval)
index e5fc9ac..2a497c8 100644 (file)
@@ -463,7 +463,7 @@ static int si470x_i2c_remove(struct i2c_client *client)
 }
 
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 /*
  * si470x_i2c_suspend - suspend the device
  */
@@ -509,7 +509,7 @@ static struct i2c_driver si470x_i2c_driver = {
        .driver = {
                .name           = "si470x",
                .owner          = THIS_MODULE,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
                .pm             = &si470x_i2c_pm,
 #endif
        },
index fe16088..9ec48cc 100644 (file)
@@ -1456,7 +1456,7 @@ static int si4713_probe(struct i2c_client *client,
 
        if (client->irq) {
                rval = request_irq(client->irq,
-                       si4713_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+                       si4713_handler, IRQF_TRIGGER_FALLING,
                        client->name, sdev);
                if (rval < 0) {
                        v4l2_err(&sdev->sd, "Could not request IRQ\n");
index 06ac692..69e3245 100644 (file)
 #define WM_SUB_TEST            0xF
 
 /* Different modes of the MSA register */
-#define MODE_BUFFER            0x0
-#define MODE_PRESET            0x1
-#define MODE_SEARCH            0x2
-#define MODE_AF_UPDATE         0x3
-#define MODE_JUMP              0x4
-#define MODE_CHECK             0x5
-#define MODE_LOAD              0x6
-#define MODE_END               0x7
-#define MODE_SHIFT             5
+#define MSA_MODE_BUFFER                0x0
+#define MSA_MODE_PRESET                0x1
+#define MSA_MODE_SEARCH                0x2
+#define MSA_MODE_AF_UPDATE     0x3
+#define MSA_MODE_JUMP          0x4
+#define MSA_MODE_CHECK         0x5
+#define MSA_MODE_LOAD          0x6
+#define MSA_MODE_END           0x7
+#define MSA_MODE_SHIFT         5
 
 struct tef6862_state {
        struct v4l2_subdev sd;
@@ -114,7 +114,7 @@ static int tef6862_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequen
 
        clamp(freq, TEF6862_LO_FREQ, TEF6862_HI_FREQ);
        pll = 1964 + ((freq - TEF6862_LO_FREQ) * 20) / FREQ_MUL;
-       i2cmsg[0] = (MODE_PRESET << MODE_SHIFT) | WM_SUB_PLLM;
+       i2cmsg[0] = (MSA_MODE_PRESET << MSA_MODE_SHIFT) | WM_SUB_PLLM;
        i2cmsg[1] = (pll >> 8) & 0xff;
        i2cmsg[2] = pll & 0xff;
 
index 253f307..4b2e9e8 100644 (file)
@@ -175,7 +175,7 @@ static int_handler_prototype int_handler_table[] = {
        fm_irq_handle_intmsk_cmd_resp
 };
 
-long (*g_st_write) (struct sk_buff *skb);
+static long (*g_st_write) (struct sk_buff *skb);
 static struct completion wait_for_fmdrv_reg_comp;
 
 static inline void fm_irq_call(struct fmdev *fmdev)
index 11e84bc..904f113 100644 (file)
@@ -322,4 +322,14 @@ config IR_GPIO_CIR
           To compile this driver as a module, choose M here: the module will
           be called gpio-ir-recv.
 
+config RC_ST
+       tristate "ST remote control receiver"
+       depends on ARCH_STI && RC_CORE
+       help
+        Say Y here if you want support for ST remote control driver
+        which allows both IR and UHF RX.
+        The driver passes raw pulse and space information to the LIRC decoder.
+
+        If you're not sure, select N here.
+
 endif #RC_DEVICES
index 56bacf0..f4eb32c 100644 (file)
@@ -30,3 +30,4 @@ obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
 obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
 obj-$(CONFIG_IR_IGUANA) += iguanair.o
 obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
+obj-$(CONFIG_RC_ST) += st_rc.o
index 82516a1..b698f3d 100644 (file)
@@ -76,8 +76,8 @@ struct fintek_dev {
        } tx;
 
        /* Config register index/data port pair */
-       u8 cr_ip;
-       u8 cr_dp;
+       u32 cr_ip;
+       u32 cr_dp;
 
        /* hardware I/O settings */
        unsigned long cir_addr;
index 07aacfa..80c611c 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
index 19632b1..fdae05c 100644 (file)
@@ -207,7 +207,7 @@ static int iguanair_send(struct iguanair *ir, unsigned size)
 {
        int rc;
 
-       INIT_COMPLETION(ir->completion);
+       reinit_completion(&ir->completion);
 
        ir->urb_out->transfer_buffer_length = size;
        rc = usb_submit_urb(ir->urb_out, GFP_KERNEL);
@@ -308,22 +308,12 @@ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t 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;
-               }
-
+               /*
+                * Calculate minimum number of 7 cycles needed so
+                * we are left with a multiple of 4; so we want to have
+                * (sevens * 7) & 3 == cycles & 3
+                */
+               sevens = (4 - cycles) & 3;
                fours = (cycles - sevens * 7) / 4;
 
                /* magic happens here */
index 31b955b..b1e19a2 100644 (file)
@@ -201,8 +201,7 @@ static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51)
 
        lirc_rx51->irq_num = omap_dm_timer_get_irq(lirc_rx51->pulse_timer);
        retval = request_irq(lirc_rx51->irq_num, lirc_rx51_interrupt_handler,
-                            IRQF_DISABLED | IRQF_SHARED,
-                            "lirc_pulse_timer", lirc_rx51);
+                            IRQF_SHARED, "lirc_pulse_timer", lirc_rx51);
        if (retval) {
                dev_err(lirc_rx51->dev, ": Failed to request interrupt line\n");
                goto err2;
index 4d13a7f..492a05a 100644 (file)
@@ -5,7 +5,7 @@
  * TODO: This table is a real mess, as it merges RC codes from several
  * devices into a big table. It also has both RC-5 and NEC codes inside.
  * It should be broken into small tables, and the protocols should properly
- * be indentificated.
+ * be identificated.
  *
  * The table were imported from dib0700_devices.c.
  *
index ba81d96..454ea59 100644 (file)
@@ -5,7 +5,7 @@
  * TODO: This table is a real mess, as it merges RC codes from several
  * devices into a big table. It also has both RC-5 and NEC codes inside.
  * It should be broken into small tables, and the protocols should properly
- * be indentificated.
+ * be identificated.
  *
  * The table were imported from dib0700_devices.c.
  *
index 7c3674f..07e8310 100644 (file)
@@ -84,8 +84,8 @@ struct nvt_dev {
        } tx;
 
        /* EFER Config register index/data pair */
-       u8 cr_efir;
-       u8 cr_efdr;
+       u32 cr_efir;
+       u32 cr_efdr;
 
        /* hardware I/O settings */
        unsigned long cir_addr;
diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
new file mode 100644 (file)
index 0000000..65120c2
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics Limited
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@st.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/kernel.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <media/rc-core.h>
+#include <linux/pinctrl/consumer.h>
+
+struct st_rc_device {
+       struct device                   *dev;
+       int                             irq;
+       int                             irq_wake;
+       struct clk                      *sys_clock;
+       void                            *base;  /* Register base address */
+       void                            *rx_base;/* RX Register base address */
+       struct rc_dev                   *rdev;
+       bool                            overclocking;
+       int                             sample_mult;
+       int                             sample_div;
+       bool                            rxuhfmode;
+};
+
+/* Registers */
+#define IRB_SAMPLE_RATE_COMM   0x64    /* sample freq divisor*/
+#define IRB_CLOCK_SEL          0x70    /* clock select       */
+#define IRB_CLOCK_SEL_STATUS   0x74    /* clock status       */
+/* IRB IR/UHF receiver registers */
+#define IRB_RX_ON               0x40   /* pulse time capture */
+#define IRB_RX_SYS              0X44   /* sym period capture */
+#define IRB_RX_INT_EN           0x48   /* IRQ enable (R/W)   */
+#define IRB_RX_INT_STATUS       0x4c   /* IRQ status (R/W)   */
+#define IRB_RX_EN               0x50   /* Receive enable     */
+#define IRB_MAX_SYM_PERIOD      0x54   /* max sym value      */
+#define IRB_RX_INT_CLEAR        0x58   /* overrun status     */
+#define IRB_RX_STATUS           0x6c   /* receive status     */
+#define IRB_RX_NOISE_SUPPR      0x5c   /* noise suppression  */
+#define IRB_RX_POLARITY_INV     0x68   /* polarity inverter  */
+
+/**
+ * IRQ set: Enable full FIFO                 1  -> bit  3;
+ *          Enable overrun IRQ               1  -> bit  2;
+ *          Enable last symbol IRQ           1  -> bit  1:
+ *          Enable RX interrupt              1  -> bit  0;
+ */
+#define IRB_RX_INTS            0x0f
+#define IRB_RX_OVERRUN_INT     0x04
+ /* maximum symbol period (microsecs),timeout to detect end of symbol train */
+#define MAX_SYMB_TIME          0x5000
+#define IRB_SAMPLE_FREQ                10000000
+#define        IRB_FIFO_NOT_EMPTY      0xff00
+#define IRB_OVERFLOW           0x4
+#define IRB_TIMEOUT            0xffff
+#define IR_ST_NAME "st-rc"
+
+static void st_rc_send_lirc_timeout(struct rc_dev *rdev)
+{
+       DEFINE_IR_RAW_EVENT(ev);
+       ev.timeout = true;
+       ir_raw_event_store(rdev, &ev);
+}
+
+/**
+ * RX graphical example to better understand the difference between ST IR block
+ * output and standard definition used by LIRC (and most of the world!)
+ *
+ *           mark                                     mark
+ *      |-IRB_RX_ON-|                            |-IRB_RX_ON-|
+ *      ___  ___  ___                            ___  ___  ___             _
+ *      | |  | |  | |                            | |  | |  | |             |
+ *      | |  | |  | |         space 0            | |  | |  | |   space 1   |
+ * _____| |__| |__| |____________________________| |__| |__| |_____________|
+ *
+ *      |--------------- IRB_RX_SYS -------------|------ IRB_RX_SYS -------|
+ *
+ *      |------------- encoding bit 0 -----------|---- encoding bit 1 -----|
+ *
+ * ST hardware returns mark (IRB_RX_ON) and total symbol time (IRB_RX_SYS), so
+ * convert to standard mark/space we have to calculate space=(IRB_RX_SYS-mark)
+ * The mark time represents the amount of time the carrier (usually 36-40kHz)
+ * is detected.The above examples shows Pulse Width Modulation encoding where
+ * bit 0 is represented by space>mark.
+ */
+
+static irqreturn_t st_rc_rx_interrupt(int irq, void *data)
+{
+       unsigned int symbol, mark = 0;
+       struct st_rc_device *dev = data;
+       int last_symbol = 0;
+       u32 status;
+       DEFINE_IR_RAW_EVENT(ev);
+
+       if (dev->irq_wake)
+               pm_wakeup_event(dev->dev, 0);
+
+       status  = readl(dev->rx_base + IRB_RX_STATUS);
+
+       while (status & (IRB_FIFO_NOT_EMPTY | IRB_OVERFLOW)) {
+               u32 int_status = readl(dev->rx_base + IRB_RX_INT_STATUS);
+               if (unlikely(int_status & IRB_RX_OVERRUN_INT)) {
+                       /* discard the entire collection in case of errors!  */
+                       ir_raw_event_reset(dev->rdev);
+                       dev_info(dev->dev, "IR RX overrun\n");
+                       writel(IRB_RX_OVERRUN_INT,
+                                       dev->rx_base + IRB_RX_INT_CLEAR);
+                       continue;
+               }
+
+               symbol = readl(dev->rx_base + IRB_RX_SYS);
+               mark = readl(dev->rx_base + IRB_RX_ON);
+
+               if (symbol == IRB_TIMEOUT)
+                       last_symbol = 1;
+
+                /* Ignore any noise */
+               if ((mark > 2) && (symbol > 1)) {
+                       symbol -= mark;
+                       if (dev->overclocking) { /* adjustments to timings */
+                               symbol *= dev->sample_mult;
+                               symbol /= dev->sample_div;
+                               mark *= dev->sample_mult;
+                               mark /= dev->sample_div;
+                       }
+
+                       ev.duration = US_TO_NS(mark);
+                       ev.pulse = true;
+                       ir_raw_event_store(dev->rdev, &ev);
+
+                       if (!last_symbol) {
+                               ev.duration = US_TO_NS(symbol);
+                               ev.pulse = false;
+                               ir_raw_event_store(dev->rdev, &ev);
+                       } else  {
+                               st_rc_send_lirc_timeout(dev->rdev);
+                       }
+
+               }
+               last_symbol = 0;
+               status  = readl(dev->rx_base + IRB_RX_STATUS);
+       }
+
+       writel(IRB_RX_INTS, dev->rx_base + IRB_RX_INT_CLEAR);
+
+       /* Empty software fifo */
+       ir_raw_event_handle(dev->rdev);
+       return IRQ_HANDLED;
+}
+
+static void st_rc_hardware_init(struct st_rc_device *dev)
+{
+       int baseclock, freqdiff;
+       unsigned int rx_max_symbol_per = MAX_SYMB_TIME;
+       unsigned int rx_sampling_freq_div;
+
+       clk_prepare_enable(dev->sys_clock);
+       baseclock = clk_get_rate(dev->sys_clock);
+
+       /* IRB input pins are inverted internally from high to low. */
+       writel(1, dev->rx_base + IRB_RX_POLARITY_INV);
+
+       rx_sampling_freq_div = baseclock / IRB_SAMPLE_FREQ;
+       writel(rx_sampling_freq_div, dev->base + IRB_SAMPLE_RATE_COMM);
+
+       freqdiff = baseclock - (rx_sampling_freq_div * IRB_SAMPLE_FREQ);
+       if (freqdiff) { /* over clocking, workout the adjustment factors */
+               dev->overclocking = true;
+               dev->sample_mult = 1000;
+               dev->sample_div = baseclock / (10000 * rx_sampling_freq_div);
+               rx_max_symbol_per = (rx_max_symbol_per * 1000)/dev->sample_div;
+       }
+
+       writel(rx_max_symbol_per, dev->rx_base + IRB_MAX_SYM_PERIOD);
+}
+
+static int st_rc_remove(struct platform_device *pdev)
+{
+       struct st_rc_device *rc_dev = platform_get_drvdata(pdev);
+       clk_disable_unprepare(rc_dev->sys_clock);
+       rc_unregister_device(rc_dev->rdev);
+       return 0;
+}
+
+static int st_rc_open(struct rc_dev *rdev)
+{
+       struct st_rc_device *dev = rdev->priv;
+       unsigned long flags;
+       local_irq_save(flags);
+       /* enable interrupts and receiver */
+       writel(IRB_RX_INTS, dev->rx_base + IRB_RX_INT_EN);
+       writel(0x01, dev->rx_base + IRB_RX_EN);
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+static void st_rc_close(struct rc_dev *rdev)
+{
+       struct st_rc_device *dev = rdev->priv;
+       /* disable interrupts and receiver */
+       writel(0x00, dev->rx_base + IRB_RX_EN);
+       writel(0x00, dev->rx_base + IRB_RX_INT_EN);
+}
+
+static int st_rc_probe(struct platform_device *pdev)
+{
+       int ret = -EINVAL;
+       struct rc_dev *rdev;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct st_rc_device *rc_dev;
+       struct device_node *np = pdev->dev.of_node;
+       const char *rx_mode;
+
+       rc_dev = devm_kzalloc(dev, sizeof(struct st_rc_device), GFP_KERNEL);
+
+       if (!rc_dev)
+               return -ENOMEM;
+
+       rdev = rc_allocate_device();
+
+       if (!rdev)
+               return -ENOMEM;
+
+       if (np && !of_property_read_string(np, "rx-mode", &rx_mode)) {
+
+               if (!strcmp(rx_mode, "uhf")) {
+                       rc_dev->rxuhfmode = true;
+               } else if (!strcmp(rx_mode, "infrared")) {
+                       rc_dev->rxuhfmode = false;
+               } else {
+                       dev_err(dev, "Unsupported rx mode [%s]\n", rx_mode);
+                       goto err;
+               }
+
+       } else {
+               goto err;
+       }
+
+       rc_dev->sys_clock = devm_clk_get(dev, NULL);
+       if (IS_ERR(rc_dev->sys_clock)) {
+               dev_err(dev, "System clock not found\n");
+               ret = PTR_ERR(rc_dev->sys_clock);
+               goto err;
+       }
+
+       rc_dev->irq = platform_get_irq(pdev, 0);
+       if (rc_dev->irq < 0) {
+               ret = rc_dev->irq;
+               goto err;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       rc_dev->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(rc_dev->base)) {
+               ret = PTR_ERR(rc_dev->base);
+               goto err;
+       }
+
+       if (rc_dev->rxuhfmode)
+               rc_dev->rx_base = rc_dev->base + 0x40;
+       else
+               rc_dev->rx_base = rc_dev->base;
+
+       rc_dev->dev = dev;
+       platform_set_drvdata(pdev, rc_dev);
+       st_rc_hardware_init(rc_dev);
+
+       rdev->driver_type = RC_DRIVER_IR_RAW;
+       rdev->allowed_protos = RC_BIT_ALL;
+       /* rx sampling rate is 10Mhz */
+       rdev->rx_resolution = 100;
+       rdev->timeout = US_TO_NS(MAX_SYMB_TIME);
+       rdev->priv = rc_dev;
+       rdev->open = st_rc_open;
+       rdev->close = st_rc_close;
+       rdev->driver_name = IR_ST_NAME;
+       rdev->map_name = RC_MAP_LIRC;
+       rdev->input_name = "ST Remote Control Receiver";
+
+       /* enable wake via this device */
+       device_set_wakeup_capable(dev, true);
+       device_set_wakeup_enable(dev, true);
+
+       ret = rc_register_device(rdev);
+       if (ret < 0)
+               goto clkerr;
+
+       rc_dev->rdev = rdev;
+       if (devm_request_irq(dev, rc_dev->irq, st_rc_rx_interrupt,
+                       IRQF_NO_SUSPEND, IR_ST_NAME, rc_dev) < 0) {
+               dev_err(dev, "IRQ %d register failed\n", rc_dev->irq);
+               ret = -EINVAL;
+               goto rcerr;
+       }
+
+       /**
+        * for LIRC_MODE_MODE2 or LIRC_MODE_PULSE or LIRC_MODE_RAW
+        * lircd expects a long space first before a signal train to sync.
+        */
+       st_rc_send_lirc_timeout(rdev);
+
+       dev_info(dev, "setup in %s mode\n", rc_dev->rxuhfmode ? "UHF" : "IR");
+
+       return ret;
+rcerr:
+       rc_unregister_device(rdev);
+       rdev = NULL;
+clkerr:
+       clk_disable_unprepare(rc_dev->sys_clock);
+err:
+       rc_free_device(rdev);
+       dev_err(dev, "Unable to register device (%d)\n", ret);
+       return ret;
+}
+
+#ifdef CONFIG_PM
+static int st_rc_suspend(struct device *dev)
+{
+       struct st_rc_device *rc_dev = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev)) {
+               if (!enable_irq_wake(rc_dev->irq))
+                       rc_dev->irq_wake = 1;
+               else
+                       return -EINVAL;
+       } else {
+               pinctrl_pm_select_sleep_state(dev);
+               writel(0x00, rc_dev->rx_base + IRB_RX_EN);
+               writel(0x00, rc_dev->rx_base + IRB_RX_INT_EN);
+               clk_disable_unprepare(rc_dev->sys_clock);
+       }
+
+       return 0;
+}
+
+static int st_rc_resume(struct device *dev)
+{
+       struct st_rc_device *rc_dev = dev_get_drvdata(dev);
+       struct rc_dev   *rdev = rc_dev->rdev;
+
+       if (rc_dev->irq_wake) {
+               disable_irq_wake(rc_dev->irq);
+               rc_dev->irq_wake = 0;
+       } else {
+               pinctrl_pm_select_default_state(dev);
+               st_rc_hardware_init(rc_dev);
+               if (rdev->users) {
+                       writel(IRB_RX_INTS, rc_dev->rx_base + IRB_RX_INT_EN);
+                       writel(0x01, rc_dev->rx_base + IRB_RX_EN);
+               }
+       }
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(st_rc_pm_ops, st_rc_suspend, st_rc_resume);
+#endif
+
+#ifdef CONFIG_OF
+static struct of_device_id st_rc_match[] = {
+       { .compatible = "st,comms-irb", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, st_rc_match);
+#endif
+
+static struct platform_driver st_rc_driver = {
+       .driver = {
+               .name = IR_ST_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(st_rc_match),
+#ifdef CONFIG_PM
+               .pm     = &st_rc_pm_ops,
+#endif
+       },
+       .probe = st_rc_probe,
+       .remove = st_rc_remove,
+};
+
+module_platform_driver(st_rc_driver);
+
+MODULE_DESCRIPTION("RC Transceiver driver for STMicroelectronics platforms");
+MODULE_AUTHOR("STMicroelectronics (R&D) Ltd");
+MODULE_LICENSE("GPL");
index 98bd496..904baf4 100644 (file)
@@ -1110,7 +1110,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
        }
 
        err = request_irq(data->irq, wbcir_irq_handler,
-                         IRQF_DISABLED, DRVNAME, device);
+                         0, DRVNAME, device);
        if (err) {
                dev_err(dev, "Failed to claim IRQ %u\n", data->irq);
                err = -EBUSY;
index 6c96e48..72971a8 100644 (file)
 #include "e4000_priv.h"
 #include <linux/math64.h>
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 /* write multiple registers */
 static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
 {
        int ret;
-       u8 buf[1 + len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
                        .addr = priv->cfg->i2c_addr,
                        .flags = 0,
-                       .len = sizeof(buf),
+                       .len = 1 + len,
                        .buf = buf,
                }
        };
 
+       if (1 + len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        buf[0] = reg;
        memcpy(&buf[1], val, len);
 
@@ -54,7 +64,7 @@ static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
 static int e4000_rd_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
 {
        int ret;
-       u8 buf[len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[2] = {
                {
                        .addr = priv->cfg->i2c_addr,
@@ -64,11 +74,18 @@ static int e4000_rd_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
                }, {
                        .addr = priv->cfg->i2c_addr,
                        .flags = I2C_M_RD,
-                       .len = sizeof(buf),
+                       .len = len,
                        .buf = buf,
                }
        };
 
+       if (len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c rd reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        ret = i2c_transfer(priv->i2c, msg, 2);
        if (ret == 2) {
                memcpy(val, buf, len);
index f4d0e79..d74e920 100644 (file)
@@ -139,7 +139,7 @@ static int fc0012_set_params(struct dvb_frontend *fe)
        unsigned char reg[7], am, pm, multi, tmp;
        unsigned long f_vco;
        unsigned short xtal_freq_khz_2, xin, xdiv;
-       int vco_select = false;
+       bool vco_select = false;
 
        if (fe->callback) {
                ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
index bd8f0f1..b416231 100644 (file)
@@ -233,7 +233,7 @@ static int fc0013_set_params(struct dvb_frontend *fe)
        unsigned char reg[7], am, pm, multi, tmp;
        unsigned long f_vco;
        unsigned short xtal_freq_khz_2, xin, xdiv;
-       int vco_select = false;
+       bool vco_select = false;
 
        if (fe->callback) {
                ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
index 81f38aa..3aecaf4 100644 (file)
@@ -20,6 +20,9 @@
 
 #include "fc2580_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 /*
  * TODO:
  * I2C write and read works only for one single register. Multiple registers
 static int fc2580_wr_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)
 {
        int ret;
-       u8 buf[1 + len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
                        .addr = priv->cfg->i2c_addr,
                        .flags = 0,
-                       .len = sizeof(buf),
+                       .len = 1 + len,
                        .buf = buf,
                }
        };
 
+       if (1 + len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        buf[0] = reg;
        memcpy(&buf[1], val, len);
 
@@ -69,7 +79,7 @@ static int fc2580_wr_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)
 static int fc2580_rd_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)
 {
        int ret;
-       u8 buf[len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[2] = {
                {
                        .addr = priv->cfg->i2c_addr,
@@ -79,11 +89,18 @@ static int fc2580_rd_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)
                }, {
                        .addr = priv->cfg->i2c_addr,
                        .flags = I2C_M_RD,
-                       .len = sizeof(buf),
+                       .len = len,
                        .buf = buf,
                }
        };
 
+       if (len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c rd reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        ret = i2c_transfer(priv->i2c, msg, 2);
        if (ret == 2) {
                memcpy(val, buf, len);
index 1c23666..d9ee43f 100644 (file)
@@ -612,10 +612,19 @@ static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type,
 
        vco_fine_tune = (data[4] & 0x30) >> 4;
 
-       if (vco_fine_tune > VCO_POWER_REF)
-               div_num = div_num - 1;
-       else if (vco_fine_tune < VCO_POWER_REF)
-               div_num = div_num + 1;
+       tuner_dbg("mix_div=%d div_num=%d vco_fine_tune=%d\n",
+                       mix_div, div_num, vco_fine_tune);
+
+       /*
+        * XXX: R828D/16MHz seems to have always vco_fine_tune=1.
+        * Due to that, this calculation goes wrong.
+        */
+       if (priv->cfg->rafael_chip != CHIP_R828D) {
+               if (vco_fine_tune > VCO_POWER_REF)
+                       div_num = div_num - 1;
+               else if (vco_fine_tune < VCO_POWER_REF)
+                       div_num = div_num + 1;
+       }
 
        rc = r820t_write_reg_mask(priv, 0x10, div_num << 5, 0xe0);
        if (rc < 0)
@@ -637,11 +646,6 @@ static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type,
                vco_fra = pll_ref * 129 / 128;
        }
 
-       if (nint > 63) {
-               tuner_info("No valid PLL values for %u kHz!\n", freq);
-               return -EINVAL;
-       }
-
        ni = (nint - 13) / 4;
        si = nint - 4 * ni - 13;
 
index e4a84ee..abe256e 100644 (file)
@@ -20,6 +20,9 @@
 
 #include "tda18212.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 struct tda18212_priv {
        struct tda18212_config *cfg;
        struct i2c_adapter *i2c;
@@ -32,16 +35,23 @@ static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
        int len)
 {
        int ret;
-       u8 buf[len+1];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
                        .addr = priv->cfg->i2c_address,
                        .flags = 0,
-                       .len = sizeof(buf),
+                       .len = 1 + len,
                        .buf = buf,
                }
        };
 
+       if (1 + len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        buf[0] = reg;
        memcpy(&buf[1], val, len);
 
@@ -61,7 +71,7 @@ static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
        int len)
 {
        int ret;
-       u8 buf[len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[2] = {
                {
                        .addr = priv->cfg->i2c_address,
@@ -71,11 +81,18 @@ static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
                }, {
                        .addr = priv->cfg->i2c_address,
                        .flags = I2C_M_RD,
-                       .len = sizeof(buf),
+                       .len = len,
                        .buf = buf,
                }
        };
 
+       if (len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c rd reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        ret = i2c_transfer(priv->i2c, msg, 2);
        if (ret == 2) {
                memcpy(val, buf, len);
index 2d31aeb..9300e93 100644 (file)
 
 #include "tda18218_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 /* write multiple registers */
 static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
 {
        int ret = 0, len2, remaining;
-       u8 buf[1 + len];
+       u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
                        .addr = priv->cfg->i2c_address,
@@ -33,6 +36,13 @@ static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
                }
        };
 
+       if (1 + len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        for (remaining = len; remaining > 0;
                        remaining -= (priv->cfg->i2c_wr_max - 1)) {
                len2 = remaining;
@@ -63,7 +73,7 @@ static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
 static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
 {
        int ret;
-       u8 buf[reg+len]; /* we must start read always from reg 0x00 */
+       u8 buf[MAX_XFER_SIZE]; /* we must start read always from reg 0x00 */
        struct i2c_msg msg[2] = {
                {
                        .addr = priv->cfg->i2c_address,
@@ -73,11 +83,18 @@ static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
                }, {
                        .addr = priv->cfg->i2c_address,
                        .flags = I2C_M_RD,
-                       .len = sizeof(buf),
+                       .len = reg + len,
                        .buf = buf,
                }
        };
 
+       if (reg + len > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
+                        KBUILD_MODNAME, reg, len);
+               return -EINVAL;
+       }
+
        ret = i2c_transfer(priv->i2c, msg, 2);
        if (ret == 2) {
                memcpy(val, &buf[reg], len);
index 300005c..9823248 100644 (file)
@@ -536,8 +536,8 @@ static int tda9887_status(struct dvb_frontend *fe)
        unsigned char buf[1];
        int rc;
 
-       memset(buf,0,sizeof(buf));
-       if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1)))
+       rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1);
+       if (rc != 1)
                tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc);
        dump_read_message(fe, buf);
        return 0;
index 878d2c4..4be5cf8 100644 (file)
@@ -24,6 +24,9 @@
 #include <linux/dvb/frontend.h>
 #include "dvb_frontend.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  80
+
 /* Registers (Write-only) */
 #define XREG_INIT         0x00
 #define XREG_RF_FREQ      0x02
@@ -547,7 +550,10 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,
 {
        struct xc2028_data *priv = fe->tuner_priv;
        int                pos, rc;
-       unsigned char      *p, *endp, buf[priv->ctrl.max_len];
+       unsigned char      *p, *endp, buf[MAX_XFER_SIZE];
+
+       if (priv->ctrl.max_len > sizeof(buf))
+               priv->ctrl.max_len = sizeof(buf);
 
        tuner_dbg("%s called\n", __func__);
 
@@ -572,7 +578,7 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,
                        return -EINVAL;
                }
 
-               size = le16_to_cpu(*(__u16 *) p);
+               size = le16_to_cpu(*(__le16 *) p);
                p += sizeof(size);
 
                if (size == 0xffff)
@@ -683,7 +689,7 @@ static int load_scode(struct dvb_frontend *fe, unsigned int type,
                /* 16 SCODE entries per file; each SCODE entry is 12 bytes and
                 * has a 2-byte size header in the firmware format. */
                if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||
-                   le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12)
+                   le16_to_cpu(*(__le16 *)(p + 14 * scode)) != 12)
                        return -EINVAL;
                p += 14 * scode + 2;
        }
index 8b6275f..0bd9690 100644 (file)
@@ -390,7 +390,7 @@ static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
                }
 
        if (fc_usb->iso_buffer != NULL)
-               pci_free_consistent(NULL,
+               usb_free_coherent(fc_usb->udev,
                        fc_usb->buffer_size, fc_usb->iso_buffer,
                        fc_usb->dma_addr);
 }
@@ -407,8 +407,8 @@ static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
                        "each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB,
                        B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
 
-       fc_usb->iso_buffer = pci_alloc_consistent(NULL,
-                       bufsize, &fc_usb->dma_addr);
+       fc_usb->iso_buffer = usb_alloc_coherent(fc_usb->udev,
+                       bufsize, GFP_KERNEL, &fc_usb->dma_addr);
        if (fc_usb->iso_buffer == NULL)
                return -ENOMEM;
 
index be17192..351a78a 100644 (file)
@@ -209,7 +209,7 @@ static void cpia2_usb_complete(struct urb *urb)
 {
        int i;
        unsigned char *cdata;
-       static int frame_ready = false;
+       static bool frame_ready = false;
        struct camera_data *cam = (struct camera_data *) urb->context;
 
        if (urb->status!=0) {
index a384f80..e9d017b 100644 (file)
@@ -978,7 +978,6 @@ static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev,
                            int minor)
 {
        int retval = -ENOMEM;
-       int errCode;
        unsigned int maxh, maxw;
 
        dev->udev = udev;
@@ -1014,8 +1013,8 @@ static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev,
        /* Cx231xx pre card setup */
        cx231xx_pre_card_setup(dev);
 
-       errCode = cx231xx_config(dev);
-       if (errCode) {
+       retval = cx231xx_config(dev);
+       if (retval) {
                cx231xx_errdev("error configuring device\n");
                return -ENOMEM;
        }
@@ -1024,12 +1023,11 @@ static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev,
        dev->norm = dev->board.norm;
 
        /* register i2c bus */
-       errCode = cx231xx_dev_init(dev);
-       if (errCode < 0) {
-               cx231xx_dev_uninit(dev);
+       retval = cx231xx_dev_init(dev);
+       if (retval) {
                cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
-                              __func__, errCode);
-               return errCode;
+                              __func__, retval);
+               goto err_dev_init;
        }
 
        /* Do board specific init */
@@ -1047,11 +1045,11 @@ static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev,
        dev->interlaced = 0;
        dev->video_input = 0;
 
-       errCode = cx231xx_config(dev);
-       if (errCode < 0) {
+       retval = cx231xx_config(dev);
+       if (retval) {
                cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n",
-                              __func__, errCode);
-               return errCode;
+                              __func__, retval);
+               goto err_dev_init;
        }
 
        /* init video dma queues */
@@ -1075,9 +1073,9 @@ static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev,
        }
 
        retval = cx231xx_register_analog_devices(dev);
-       if (retval < 0) {
-               cx231xx_release_resources(dev);
-               return retval;
+       if (retval) {
+               cx231xx_release_analog_resources(dev);
+               goto err_analog;
        }
 
        cx231xx_ir_init(dev);
@@ -1085,6 +1083,11 @@ static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev,
        cx231xx_init_extension(dev);
 
        return 0;
+err_analog:
+       cx231xx_remove_from_devlist(dev);
+err_dev_init:
+       cx231xx_dev_uninit(dev);
+       return retval;
 }
 
 #if defined(CONFIG_MODULES) && defined(MODULE)
@@ -1132,7 +1135,6 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
        char *speed;
        struct usb_interface_assoc_descriptor *assoc_desc;
 
-       udev = usb_get_dev(interface_to_usbdev(interface));
        ifnum = interface->altsetting[0].desc.bInterfaceNumber;
 
        /*
@@ -1161,6 +1163,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
                return -ENOMEM;
        }
 
+       udev = usb_get_dev(interface_to_usbdev(interface));
+
        snprintf(dev->name, 29, "cx231xx #%d", nr);
        dev->devno = nr;
        dev->model = id->driver_info;
@@ -1223,10 +1227,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
        if (assoc_desc->bFirstInterface != ifnum) {
                cx231xx_err(DRIVER_NAME ": Not found "
                            "matching IAD interface\n");
-               clear_bit(dev->devno, &cx231xx_devused);
-               kfree(dev);
-               dev = NULL;
-               return -ENODEV;
+               retval = -ENODEV;
+               goto err_if;
        }
 
        cx231xx_info("registering interface %d\n", ifnum);
@@ -1242,22 +1244,13 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
        retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
        if (retval) {
                cx231xx_errdev("v4l2_device_register failed\n");
-               clear_bit(dev->devno, &cx231xx_devused);
-               kfree(dev);
-               dev = NULL;
-               return -EIO;
+               retval = -EIO;
+               goto err_v4l2;
        }
        /* allocate device struct */
        retval = cx231xx_init_dev(dev, udev, nr);
-       if (retval) {
-               clear_bit(dev->devno, &cx231xx_devused);
-               v4l2_device_unregister(&dev->v4l2_dev);
-               kfree(dev);
-               dev = NULL;
-               usb_set_intfdata(interface, NULL);
-
-               return retval;
-       }
+       if (retval)
+               goto err_init;
 
        /* compute alternate max packet sizes for video */
        uif = udev->actconfig->interface[dev->current_pcb_config.
@@ -1275,11 +1268,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
 
        if (dev->video_mode.alt_max_pkt_size == NULL) {
                cx231xx_errdev("out of memory!\n");
-               clear_bit(dev->devno, &cx231xx_devused);
-               v4l2_device_unregister(&dev->v4l2_dev);
-               kfree(dev);
-               dev = NULL;
-               return -ENOMEM;
+               retval = -ENOMEM;
+               goto err_video_alt;
        }
 
        for (i = 0; i < dev->video_mode.num_alt; i++) {
@@ -1309,11 +1299,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
 
        if (dev->vbi_mode.alt_max_pkt_size == NULL) {
                cx231xx_errdev("out of memory!\n");
-               clear_bit(dev->devno, &cx231xx_devused);
-               v4l2_device_unregister(&dev->v4l2_dev);
-               kfree(dev);
-               dev = NULL;
-               return -ENOMEM;
+               retval = -ENOMEM;
+               goto err_vbi_alt;
        }
 
        for (i = 0; i < dev->vbi_mode.num_alt; i++) {
@@ -1344,11 +1331,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
 
        if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) {
                cx231xx_errdev("out of memory!\n");
-               clear_bit(dev->devno, &cx231xx_devused);
-               v4l2_device_unregister(&dev->v4l2_dev);
-               kfree(dev);
-               dev = NULL;
-               return -ENOMEM;
+               retval = -ENOMEM;
+               goto err_sliced_cc_alt;
        }
 
        for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
@@ -1380,11 +1364,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
 
                if (dev->ts1_mode.alt_max_pkt_size == NULL) {
                        cx231xx_errdev("out of memory!\n");
-                       clear_bit(dev->devno, &cx231xx_devused);
-                       v4l2_device_unregister(&dev->v4l2_dev);
-                       kfree(dev);
-                       dev = NULL;
-                       return -ENOMEM;
+                       retval = -ENOMEM;
+                       goto err_ts1_alt;
                }
 
                for (i = 0; i < dev->ts1_mode.num_alt; i++) {
@@ -1411,6 +1392,29 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
        request_modules(dev);
 
        return 0;
+err_ts1_alt:
+       kfree(dev->sliced_cc_mode.alt_max_pkt_size);
+err_sliced_cc_alt:
+       kfree(dev->vbi_mode.alt_max_pkt_size);
+err_vbi_alt:
+       kfree(dev->video_mode.alt_max_pkt_size);
+err_video_alt:
+       /* cx231xx_uninit_dev: */
+       cx231xx_close_extension(dev);
+       cx231xx_ir_exit(dev);
+       cx231xx_release_analog_resources(dev);
+       cx231xx_417_unregister(dev);
+       cx231xx_remove_from_devlist(dev);
+       cx231xx_dev_uninit(dev);
+err_init:
+       v4l2_device_unregister(&dev->v4l2_dev);
+err_v4l2:
+       usb_set_intfdata(interface, NULL);
+err_if:
+       usb_put_dev(udev);
+       kfree(dev);
+       clear_bit(dev->devno, &cx231xx_devused);
+       return retval;
 }
 
 /*
index d7308ab..2a34cee 100644 (file)
@@ -28,7 +28,7 @@ MODULE_PARM_DESC(pcb_debug, "enable pcb config debug messages [video]");
 
 /******************************************************************************/
 
-struct pcb_config cx231xx_Scenario[] = {
+static struct pcb_config cx231xx_Scenario[] = {
        {
         INDEX_SELFPOWER_DIGITAL_ONLY,  /* index */
         USB_SELF_POWER,        /* power_type */
@@ -672,7 +672,7 @@ u32 initialize_cx231xx(struct cx231xx *dev)
        pcb config it is related to */
        cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4);
 
-       config_info = le32_to_cpu(*((u32 *) data));
+       config_info = le32_to_cpu(*((__le32 *)data));
        usb_speed = (u8) (config_info & 0x1);
 
        /* Verify this device belongs to Bus power or Self power device */
index d556042..da47d23 100644 (file)
@@ -397,12 +397,13 @@ error:
        return ret;
 }
 
+#define AF9015_EEPROM_SIZE 256
+
 /* hash (and dump) eeprom */
 static int af9015_eeprom_hash(struct dvb_usb_device *d)
 {
        struct af9015_state *state = d_to_priv(d);
        int ret, i;
-       static const unsigned int AF9015_EEPROM_SIZE = 256;
        u8 buf[AF9015_EEPROM_SIZE];
        struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, NULL};
 
index 1ea17dc..c8fcd78 100644 (file)
@@ -21,6 +21,9 @@
 
 #include "af9035.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 static u16 af9035_checksum(const u8 *buf, size_t len)
@@ -126,10 +129,16 @@ exit:
 /* write multiple registers */
 static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len)
 {
-       u8 wbuf[6 + len];
+       u8 wbuf[MAX_XFER_SIZE];
        u8 mbox = (reg >> 16) & 0xff;
        struct usb_req req = { CMD_MEM_WR, mbox, sizeof(wbuf), wbuf, 0, NULL };
 
+       if (6 + len > sizeof(wbuf)) {
+               dev_warn(&d->udev->dev, "%s: i2c wr: len=%d is too big!\n",
+                        KBUILD_MODNAME, len);
+               return -EOPNOTSUPP;
+       }
+
        wbuf[0] = len;
        wbuf[1] = 2;
        wbuf[2] = 0;
@@ -228,9 +237,16 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
                                        msg[1].len);
                } else {
                        /* I2C */
-                       u8 buf[5 + msg[0].len];
+                       u8 buf[MAX_XFER_SIZE];
                        struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf),
                                        buf, msg[1].len, msg[1].buf };
+
+                       if (5 + msg[0].len > sizeof(buf)) {
+                               dev_warn(&d->udev->dev,
+                                        "%s: i2c xfer: len=%d is too big!\n",
+                                        KBUILD_MODNAME, msg[0].len);
+                               return -EOPNOTSUPP;
+                       }
                        req.mbox |= ((msg[0].addr & 0x80)  >>  3);
                        buf[0] = msg[1].len;
                        buf[1] = msg[0].addr << 1;
@@ -257,9 +273,16 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
                                        msg[0].len - 3);
                } else {
                        /* I2C */
-                       u8 buf[5 + msg[0].len];
+                       u8 buf[MAX_XFER_SIZE];
                        struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf,
                                        0, NULL };
+
+                       if (5 + msg[0].len > sizeof(buf)) {
+                               dev_warn(&d->udev->dev,
+                                        "%s: i2c xfer: len=%d is too big!\n",
+                                        KBUILD_MODNAME, msg[0].len);
+                               return -EOPNOTSUPP;
+                       }
                        req.mbox |= ((msg[0].addr & 0x80)  >>  3);
                        buf[0] = msg[0].len;
                        buf[1] = msg[0].addr << 1;
index e97964e..2627553 100644 (file)
@@ -23,6 +23,9 @@
 #include "lgdt3305.h"
 #include "lg2160.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 int dvb_usb_mxl111sf_debug;
 module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644);
 MODULE_PARM_DESC(debug, "set debugging level "
@@ -57,7 +60,12 @@ int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
 {
        int wo = (rbuf == NULL || rlen == 0); /* write-only */
        int ret;
-       u8 sndbuf[1+wlen];
+       u8 sndbuf[MAX_XFER_SIZE];
+
+       if (1 + wlen > sizeof(sndbuf)) {
+               pr_warn("%s: len=%d is too big!\n", __func__, wlen);
+               return -EOPNOTSUPP;
+       }
 
        pr_debug("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen);
 
index c0cd084..ecca036 100644 (file)
@@ -377,6 +377,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
        struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf};
        struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf};
        struct rtl28xxu_req req_r820t = {0x0034, CMD_I2C_RD, 1, buf};
+       struct rtl28xxu_req req_r828d = {0x0074, CMD_I2C_RD, 1, buf};
 
        dev_dbg(&d->udev->dev, "%s:\n", __func__);
 
@@ -489,6 +490,15 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
                goto found;
        }
 
+       /* check R828D ID register; reg=00 val=69 */
+       ret = rtl28xxu_ctrl_msg(d, &req_r828d);
+       if (ret == 0 && buf[0] == 0x69) {
+               priv->tuner = TUNER_RTL2832_R828D;
+               priv->tuner_name = "R828D";
+               goto found;
+       }
+
+
 found:
        dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name);
 
@@ -745,6 +755,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
                rtl2832_config = &rtl28xxu_rtl2832_e4000_config;
                break;
        case TUNER_RTL2832_R820T:
+       case TUNER_RTL2832_R828D:
                rtl2832_config = &rtl28xxu_rtl2832_r820t_config;
                break;
        default:
@@ -866,6 +877,13 @@ static const struct r820t_config rtl2832u_r820t_config = {
        .rafael_chip = CHIP_R820T,
 };
 
+static const struct r820t_config rtl2832u_r828d_config = {
+       .i2c_addr = 0x3a,
+       .xtal = 16000000,
+       .max_i2c_msg_len = 2,
+       .rafael_chip = CHIP_R828D,
+};
+
 static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 {
        int ret;
@@ -923,6 +941,27 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
                adap->fe[0]->ops.read_signal_strength =
                                adap->fe[0]->ops.tuner_ops.get_rf_strength;
                break;
+       case TUNER_RTL2832_R828D:
+               /* power off mn88472 demod on GPIO0 */
+               ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x00, 0x01);
+               if (ret)
+                       goto err;
+
+               ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x01);
+               if (ret)
+                       goto err;
+
+               ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x01, 0x01);
+               if (ret)
+                       goto err;
+
+               fe = dvb_attach(r820t_attach, adap->fe[0], &d->i2c_adap,
+                               &rtl2832u_r828d_config);
+
+               /* Use tuner to get the signal strength */
+               adap->fe[0]->ops.read_signal_strength =
+                               adap->fe[0]->ops.tuner_ops.get_rf_strength;
+               break;
        default:
                fe = NULL;
                dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME,
@@ -1388,6 +1427,9 @@ static const struct usb_device_id rtl28xxu_id_table[] = {
                &rtl2832u_props, "Leadtek WinFast DTV Dongle mini", NULL) },
        { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A,
                &rtl2832u_props, "Crypto ReDi PC 50 A", NULL) },
+
+       { DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131,
+               &rtl2832u_props, "Astrometa DVB-T2", NULL) },
        { }
 };
 MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table);
index 729b354..2142bcb 100644 (file)
@@ -83,6 +83,7 @@ enum rtl28xxu_tuner {
        TUNER_RTL2832_TDA18272,
        TUNER_RTL2832_FC0013,
        TUNER_RTL2832_R820T,
+       TUNER_RTL2832_R828D,
 };
 
 struct rtl28xxu_req {
index ea2d5ee..c11138e 100644 (file)
@@ -254,7 +254,7 @@ static const struct stb0899_s1_reg az6027_stb0899_s1_init_3[] = {
 
 
 
-struct stb0899_config az6027_stb0899_config = {
+static struct stb0899_config az6027_stb0899_config = {
        .init_dev               = az6027_stb0899_s1_init_1,
        .init_s2_demod          = stb0899_s2_init_2,
        .init_s1_demod          = az6027_stb0899_s1_init_3,
@@ -291,7 +291,7 @@ struct stb0899_config az6027_stb0899_config = {
        .tuner_set_rfsiggain    = NULL,
 };
 
-struct stb6100_config az6027_stb6100_config = {
+static struct stb6100_config az6027_stb6100_config = {
        .tuner_address  = 0xc0,
        .refclock       = 27000000,
 };
index 3940bb0..20e345d 100644 (file)
@@ -43,6 +43,9 @@
 #include "lgs8gxx.h"
 #include "atbm8830.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 /* debug */
 static int dvb_usb_cxusb_debug;
 module_param_named(debug, dvb_usb_cxusb_debug, int, 0644);
@@ -57,7 +60,14 @@ static int cxusb_ctrl_msg(struct dvb_usb_device *d,
                          u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
 {
        int wo = (rbuf == NULL || rlen == 0); /* write-only */
-       u8 sndbuf[1+wlen];
+       u8 sndbuf[MAX_XFER_SIZE];
+
+       if (1 + wlen > sizeof(sndbuf)) {
+               warn("i2c wr: len=%d is too big!\n",
+                    wlen);
+               return -EOPNOTSUPP;
+       }
+
        memset(sndbuf, 0, 1+wlen);
 
        sndbuf[0] = cmd;
@@ -158,7 +168,13 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
 
                if (msg[i].flags & I2C_M_RD) {
                        /* read only */
-                       u8 obuf[3], ibuf[1+msg[i].len];
+                       u8 obuf[3], ibuf[MAX_XFER_SIZE];
+
+                       if (1 + msg[i].len > sizeof(ibuf)) {
+                               warn("i2c rd: len=%d is too big!\n",
+                                    msg[i].len);
+                               return -EOPNOTSUPP;
+                       }
                        obuf[0] = 0;
                        obuf[1] = msg[i].len;
                        obuf[2] = msg[i].addr;
@@ -172,7 +188,18 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                } else if (i+1 < num && (msg[i+1].flags & I2C_M_RD) &&
                           msg[i].addr == msg[i+1].addr) {
                        /* write to then read from same address */
-                       u8 obuf[3+msg[i].len], ibuf[1+msg[i+1].len];
+                       u8 obuf[MAX_XFER_SIZE], ibuf[MAX_XFER_SIZE];
+
+                       if (3 + msg[i].len > sizeof(obuf)) {
+                               warn("i2c wr: len=%d is too big!\n",
+                                    msg[i].len);
+                               return -EOPNOTSUPP;
+                       }
+                       if (1 + msg[i + 1].len > sizeof(ibuf)) {
+                               warn("i2c rd: len=%d is too big!\n",
+                                    msg[i + 1].len);
+                               return -EOPNOTSUPP;
+                       }
                        obuf[0] = msg[i].len;
                        obuf[1] = msg[i+1].len;
                        obuf[2] = msg[i].addr;
@@ -191,7 +218,13 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                        i++;
                } else {
                        /* write only */
-                       u8 obuf[2+msg[i].len], ibuf;
+                       u8 obuf[MAX_XFER_SIZE], ibuf;
+
+                       if (2 + msg[i].len > sizeof(obuf)) {
+                               warn("i2c wr: len=%d is too big!\n",
+                                    msg[i].len);
+                               return -EOPNOTSUPP;
+                       }
                        obuf[0] = msg[i].addr;
                        obuf[1] = msg[i].len;
                        memcpy(&obuf[2], msg[i].buf, msg[i].len);
index c2dded9..6d68af0 100644 (file)
@@ -12,6 +12,9 @@
 #include <linux/kconfig.h>
 #include "dibusb.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 static int debug;
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "set debugging level (1=info (|-able))." DVB_USB_DEBUG_STATUS);
@@ -105,11 +108,16 @@ EXPORT_SYMBOL(dibusb2_0_power_ctrl);
 static int dibusb_i2c_msg(struct dvb_usb_device *d, u8 addr,
                          u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
 {
-       u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */
+       u8 sndbuf[MAX_XFER_SIZE]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */
        /* write only ? */
        int wo = (rbuf == NULL || rlen == 0),
                len = 2 + wlen + (wo ? 0 : 2);
 
+       if (4 + wlen > sizeof(sndbuf)) {
+               warn("i2c wr: len=%d is too big!\n", wlen);
+               return -EOPNOTSUPP;
+       }
+
        sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ;
        sndbuf[1] = (addr << 1) | (wo ? 0 : 1);
 
index 6e237b6..c1a63b2 100644 (file)
@@ -30,6 +30,9 @@
 #include "stb6100_proc.h"
 #include "m88rs2000.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 #ifndef USB_PID_DW2102
 #define USB_PID_DW2102 0x2102
 #endif
@@ -308,7 +311,14 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
        case 2: {
                /* read */
                /* first write first register number */
-               u8 ibuf[msg[1].len + 2], obuf[3];
+               u8 ibuf[MAX_XFER_SIZE], obuf[3];
+
+               if (2 + msg[1].len > sizeof(ibuf)) {
+                       warn("i2c rd: len=%d is too big!\n",
+                            msg[1].len);
+                       return -EOPNOTSUPP;
+               }
+
                obuf[0] = msg[0].addr << 1;
                obuf[1] = msg[0].len;
                obuf[2] = msg[0].buf[0];
@@ -325,7 +335,14 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
                switch (msg[0].addr) {
                case 0x68: {
                        /* write to register */
-                       u8 obuf[msg[0].len + 2];
+                       u8 obuf[MAX_XFER_SIZE];
+
+                       if (2 + msg[0].len > sizeof(obuf)) {
+                               warn("i2c wr: len=%d is too big!\n",
+                                    msg[1].len);
+                               return -EOPNOTSUPP;
+                       }
+
                        obuf[0] = msg[0].addr << 1;
                        obuf[1] = msg[0].len;
                        memcpy(obuf + 2, msg[0].buf, msg[0].len);
@@ -335,7 +352,14 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
                }
                case 0x61: {
                        /* write to tuner */
-                       u8 obuf[msg[0].len + 2];
+                       u8 obuf[MAX_XFER_SIZE];
+
+                       if (2 + msg[0].len > sizeof(obuf)) {
+                               warn("i2c wr: len=%d is too big!\n",
+                                    msg[1].len);
+                               return -EOPNOTSUPP;
+                       }
+
                        obuf[0] = msg[0].addr << 1;
                        obuf[1] = msg[0].len;
                        memcpy(obuf + 2, msg[0].buf, msg[0].len);
@@ -401,7 +425,14 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
                default: {
                        if (msg[j].flags == I2C_M_RD) {
                                /* read registers */
-                               u8  ibuf[msg[j].len + 2];
+                               u8  ibuf[MAX_XFER_SIZE];
+
+                               if (2 + msg[j].len > sizeof(ibuf)) {
+                                       warn("i2c rd: len=%d is too big!\n",
+                                            msg[j].len);
+                                       return -EOPNOTSUPP;
+                               }
+
                                dw210x_op_rw(d->udev, 0xc3,
                                                (msg[j].addr << 1) + 1, 0,
                                                ibuf, msg[j].len + 2,
@@ -430,7 +461,14 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
                                } while (len > 0);
                        } else {
                                /* write registers */
-                               u8 obuf[msg[j].len + 2];
+                               u8 obuf[MAX_XFER_SIZE];
+
+                               if (2 + msg[j].len > sizeof(obuf)) {
+                                       warn("i2c wr: len=%d is too big!\n",
+                                            msg[j].len);
+                                       return -EOPNOTSUPP;
+                               }
+
                                obuf[0] = msg[j].addr << 1;
                                obuf[1] = msg[j].len;
                                memcpy(obuf + 2, msg[j].buf, msg[j].len);
@@ -463,7 +501,13 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
        case 2: {
                /* read */
                /* first write first register number */
-               u8 ibuf[msg[1].len + 2], obuf[3];
+               u8 ibuf[MAX_XFER_SIZE], obuf[3];
+
+               if (2 + msg[1].len > sizeof(ibuf)) {
+                       warn("i2c rd: len=%d is too big!\n",
+                            msg[1].len);
+                       return -EOPNOTSUPP;
+               }
                obuf[0] = msg[0].addr << 1;
                obuf[1] = msg[0].len;
                obuf[2] = msg[0].buf[0];
@@ -481,7 +525,13 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                case 0x60:
                case 0x0c: {
                        /* write to register */
-                       u8 obuf[msg[0].len + 2];
+                       u8 obuf[MAX_XFER_SIZE];
+
+                       if (2 + msg[0].len > sizeof(obuf)) {
+                               warn("i2c wr: len=%d is too big!\n",
+                                    msg[0].len);
+                               return -EOPNOTSUPP;
+                       }
                        obuf[0] = msg[0].addr << 1;
                        obuf[1] = msg[0].len;
                        memcpy(obuf + 2, msg[0].buf, msg[0].len);
@@ -563,7 +613,14 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                default: {
                        if (msg[j].flags == I2C_M_RD) {
                                /* read registers */
-                               u8 ibuf[msg[j].len];
+                               u8 ibuf[MAX_XFER_SIZE];
+
+                               if (msg[j].len > sizeof(ibuf)) {
+                                       warn("i2c rd: len=%d is too big!\n",
+                                            msg[j].len);
+                                       return -EOPNOTSUPP;
+                               }
+
                                dw210x_op_rw(d->udev, 0x91, 0, 0,
                                                ibuf, msg[j].len,
                                                DW210X_READ_MSG);
@@ -590,7 +647,14 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                                } while (len > 0);
                        } else if (j < (num - 1)) {
                                /* write register addr before read */
-                               u8 obuf[msg[j].len + 2];
+                               u8 obuf[MAX_XFER_SIZE];
+
+                               if (2 + msg[j].len > sizeof(obuf)) {
+                                       warn("i2c wr: len=%d is too big!\n",
+                                            msg[j].len);
+                                       return -EOPNOTSUPP;
+                               }
+
                                obuf[0] = msg[j + 1].len;
                                obuf[1] = (msg[j].addr << 1);
                                memcpy(obuf + 2, msg[j].buf, msg[j].len);
@@ -602,7 +666,13 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                                break;
                        } else {
                                /* write registers */
-                               u8 obuf[msg[j].len + 2];
+                               u8 obuf[MAX_XFER_SIZE];
+
+                               if (2 + msg[j].len > sizeof(obuf)) {
+                                       warn("i2c wr: len=%d is too big!\n",
+                                            msg[j].len);
+                                       return -EOPNOTSUPP;
+                               }
                                obuf[0] = msg[j].len + 1;
                                obuf[1] = (msg[j].addr << 1);
                                memcpy(obuf + 2, msg[j].buf, msg[j].len);
@@ -955,9 +1025,10 @@ static struct ds3000_config dw2104_ds3000_config = {
        .demod_address = 0x68,
 };
 
-static struct ts2020_config dw2104_ts2020_config  = {
+static struct ts2020_config dw2104_ts2020_config = {
        .tuner_address = 0x60,
        .clk_out_div = 1,
+       .frequency_div = 1060000,
 };
 
 static struct ds3000_config s660_ds3000_config = {
@@ -966,6 +1037,12 @@ static struct ds3000_config s660_ds3000_config = {
        .set_lock_led = dw210x_led_ctrl,
 };
 
+static struct ts2020_config s660_ts2020_config = {
+       .tuner_address = 0x60,
+       .clk_out_div = 1,
+       .frequency_div = 1146000,
+};
+
 static struct stv0900_config dw2104a_stv0900_config = {
        .demod_address = 0x6a,
        .demod_mode = 0,
@@ -1205,7 +1282,7 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d)
        if (d->fe_adap[0].fe == NULL)
                return -EIO;
 
-       dvb_attach(ts2020_attach, d->fe_adap[0].fe, &dw2104_ts2020_config,
+       dvb_attach(ts2020_attach, d->fe_adap[0].fe, &s660_ts2020_config,
                &d->dev->i2c_adap);
 
        st->old_set_voltage = d->fe_adap[0].fe->ops.set_voltage;
@@ -1213,7 +1290,7 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d)
 
        dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
 
-       info("Attached ds3000+ds2020!\n");
+       info("Attached ds3000+ts2020!\n");
 
        return 0;
 }
index 73cc50a..d666741 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/i2c.h>
 #include <media/soc_camera.h>
 #include <media/mt9v011.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 
 #include "em28xx.h"
@@ -47,6 +48,7 @@ static struct soc_camera_link camlink = {
        .bus_id = 0,
        .flags = 0,
        .module_name = "em28xx",
+       .unbalanced_power = true,
 };
 
 
@@ -325,13 +327,24 @@ int em28xx_detect_sensor(struct em28xx *dev)
 
 int em28xx_init_camera(struct em28xx *dev)
 {
+       char clk_name[V4L2_SUBDEV_NAME_SIZE];
+       struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus];
+       struct i2c_adapter *adap = &dev->i2c_adap[dev->def_i2c_bus];
+       int ret = 0;
+
+       v4l2_clk_name_i2c(clk_name, sizeof(clk_name),
+                         i2c_adapter_id(adap), client->addr);
+       dev->clk = v4l2_clk_register_fixed(clk_name, "mclk", -EINVAL);
+       if (IS_ERR(dev->clk))
+               return PTR_ERR(dev->clk);
+
        switch (dev->em28xx_sensor) {
        case EM28XX_MT9V011:
        {
                struct mt9v011_platform_data pdata;
                struct i2c_board_info mt9v011_info = {
                        .type = "mt9v011",
-                       .addr = dev->i2c_client[dev->def_i2c_bus].addr,
+                       .addr = client->addr,
                        .platform_data = &pdata,
                };
 
@@ -352,10 +365,11 @@ int em28xx_init_camera(struct em28xx *dev)
                dev->sensor_xtal = 4300000;
                pdata.xtal = dev->sensor_xtal;
                if (NULL ==
-                   v4l2_i2c_new_subdev_board(&dev->v4l2_dev,
-                                             &dev->i2c_adap[dev->def_i2c_bus],
-                                             &mt9v011_info, NULL))
-                       return -ENODEV;
+                   v4l2_i2c_new_subdev_board(&dev->v4l2_dev, adap,
+                                             &mt9v011_info, NULL)) {
+                       ret = -ENODEV;
+                       break;
+               }
                /* probably means GRGB 16 bit bayer */
                dev->vinmode = 0x0d;
                dev->vinctl = 0x00;
@@ -391,7 +405,7 @@ int em28xx_init_camera(struct em28xx *dev)
                struct i2c_board_info ov2640_info = {
                        .type = "ov2640",
                        .flags = I2C_CLIENT_SCCB,
-                       .addr = dev->i2c_client[dev->def_i2c_bus].addr,
+                       .addr = client->addr,
                        .platform_data = &camlink,
                };
                struct v4l2_mbus_framefmt fmt;
@@ -408,9 +422,12 @@ int em28xx_init_camera(struct em28xx *dev)
                dev->sensor_yres = 480;
 
                subdev =
-                    v4l2_i2c_new_subdev_board(&dev->v4l2_dev,
-                                              &dev->i2c_adap[dev->def_i2c_bus],
+                    v4l2_i2c_new_subdev_board(&dev->v4l2_dev, adap,
                                               &ov2640_info, NULL);
+               if (NULL == subdev) {
+                       ret = -ENODEV;
+                       break;
+               }
 
                fmt.code = V4L2_MBUS_FMT_YUYV8_2X8;
                fmt.width = 640;
@@ -427,8 +444,13 @@ int em28xx_init_camera(struct em28xx *dev)
        }
        case EM28XX_NOSENSOR:
        default:
-               return -EINVAL;
+               ret = -EINVAL;
        }
 
-       return 0;
+       if (ret < 0) {
+               v4l2_clk_unregister_fixed(dev->clk);
+               dev->clk = NULL;
+       }
+
+       return ret;
 }
index dc65742..a519669 100644 (file)
@@ -36,6 +36,7 @@
 #include <media/tvaudio.h>
 #include <media/i2c-addr.h>
 #include <media/tveeprom.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 
 #include "em28xx.h"
@@ -95,8 +96,8 @@ static struct em28xx_reg_seq default_digital[] = {
 /* Board Hauppauge WinTV HVR 900 analog */
 static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
        {EM2820_R08_GPIO_CTRL,  0x2d,   ~EM_GPIO_4,     10},
-       {0x05,                  0xff,   0x10,           10},
-       {  -1,                  -1,     -1,             -1},
+       {       0x05,           0xff,   0x10,           10},
+       {       -1,             -1,     -1,             -1},
 };
 
 /* Board Hauppauge WinTV HVR 900 digital */
@@ -104,20 +105,20 @@ static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
        {EM2820_R08_GPIO_CTRL,  0x2e,   ~EM_GPIO_4,     10},
        {EM2880_R04_GPO,        0x04,   0x0f,           10},
        {EM2880_R04_GPO,        0x0c,   0x0f,           10},
-       { -1,                   -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 /* Board Hauppauge WinTV HVR 900 (R2) digital */
 static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = {
        {EM2820_R08_GPIO_CTRL,  0x2e,   ~EM_GPIO_4,     10},
        {EM2880_R04_GPO,        0x0c,   0x0f,           10},
-       { -1,                   -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 /* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
 static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
-       {EM2820_R08_GPIO_CTRL,       0x69,   ~EM_GPIO_4,         10},
-       {       -1,             -1,     -1,              -1},
+       {EM2820_R08_GPIO_CTRL,  0x69,   ~EM_GPIO_4,     10},
+       {       -1,             -1,     -1,             -1},
 };
 
 /* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
@@ -132,7 +133,7 @@ static struct em28xx_reg_seq em2882_kworld_315u_digital[] = {
        {EM2880_R04_GPO,        0x04,   0xff,           10},
        {EM2880_R04_GPO,        0x0c,   0xff,           10},
        {EM2820_R08_GPIO_CTRL,  0x7e,   0xff,           10},
-       {  -1,                  -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = {
@@ -140,19 +141,19 @@ static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = {
        {EM2880_R04_GPO,        0x0c,   0xff,           10},
        {EM2880_R04_GPO,        0x08,   0xff,           10},
        {EM2880_R04_GPO,        0x0c,   0xff,           10},
-       {  -1,                  -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 static struct em28xx_reg_seq kworld_330u_analog[] = {
        {EM2820_R08_GPIO_CTRL,  0x6d,   ~EM_GPIO_4,     10},
        {EM2880_R04_GPO,        0x00,   0xff,           10},
-       { -1,                   -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 static struct em28xx_reg_seq kworld_330u_digital[] = {
        {EM2820_R08_GPIO_CTRL,  0x6e,   ~EM_GPIO_4,     10},
        {EM2880_R04_GPO,        0x08,   0xff,           10},
-       { -1,                   -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 /* Evga inDtube
@@ -170,11 +171,11 @@ static struct em28xx_reg_seq evga_indtube_digital[] = {
        {EM2820_R08_GPIO_CTRL,  0x7a,   0xff,            1},
        {EM2880_R04_GPO,        0x04,   0xff,           10},
        {EM2880_R04_GPO,        0x0c,   0xff,            1},
-       { -1,                   -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 /*
- * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map:
+ * KWorld PlusTV 340U, UB435-Q and UB435-Q V2 (ATSC) GPIOs map:
  * EM_GPIO_0 - currently unknown
  * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on)
  * EM_GPIO_2 - currently unknown
@@ -185,8 +186,8 @@ static struct em28xx_reg_seq evga_indtube_digital[] = {
  * EM_GPIO_7 - currently unknown
  */
 static struct em28xx_reg_seq kworld_a340_digital[] = {
-       {EM2820_R08_GPIO_CTRL,  0x6d,           ~EM_GPIO_4,     10},
-       { -1,                   -1,             -1,             -1},
+       {EM2820_R08_GPIO_CTRL,  0x6d,   ~EM_GPIO_4,     10},
+       {       -1,             -1,     -1,             -1},
 };
 
 /* Pinnacle Hybrid Pro eb1a:2881 */
@@ -205,13 +206,13 @@ static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
 static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = {
        {EM2820_R08_GPIO_CTRL,  0x6d,   ~EM_GPIO_4,     10},
        {EM2880_R04_GPO,        0x00,   0xff,           10},
-       { -1,                   -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
        {EM2820_R08_GPIO_CTRL,  0x6e,   ~EM_GPIO_4,     10},
        {EM2880_R04_GPO,        0x08,   0xff,           10},
-       { -1,                   -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 /* eb1a:2868 Reddo DVB-C USB TV Box
@@ -225,7 +226,7 @@ static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
        {EM2820_R08_GPIO_CTRL,  0x7f,   0xff,           10},
        {EM2820_R08_GPIO_CTRL,  0x6f,   0xff,           10},
        {EM2820_R08_GPIO_CTRL,  0xff,   0xff,           10},
-       {-1,                    -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 /* Callback for the most boards */
@@ -233,23 +234,23 @@ static struct em28xx_reg_seq default_tuner_gpio[] = {
        {EM2820_R08_GPIO_CTRL,  EM_GPIO_4,      EM_GPIO_4,      10},
        {EM2820_R08_GPIO_CTRL,  0,              EM_GPIO_4,      10},
        {EM2820_R08_GPIO_CTRL,  EM_GPIO_4,      EM_GPIO_4,      10},
-       {  -1,                  -1,             -1,             -1},
+       {       -1,             -1,             -1,             -1},
 };
 
 /* Mute/unmute */
 static struct em28xx_reg_seq compro_unmute_tv_gpio[] = {
-       {EM2820_R08_GPIO_CTRL,  5,              7,              10},
-       {  -1,                  -1,             -1,             -1},
+       {EM2820_R08_GPIO_CTRL,  5,      7,      10},
+       {       -1,             -1,     -1,     -1},
 };
 
 static struct em28xx_reg_seq compro_unmute_svid_gpio[] = {
-       {EM2820_R08_GPIO_CTRL,  4,              7,              10},
-       {  -1,                  -1,             -1,             -1},
+       {EM2820_R08_GPIO_CTRL,  4,      7,      10},
+       {       -1,             -1,     -1,     -1},
 };
 
 static struct em28xx_reg_seq compro_mute_gpio[] = {
-       {EM2820_R08_GPIO_CTRL,  6,              7,              10},
-       {  -1,                  -1,             -1,             -1},
+       {EM2820_R08_GPIO_CTRL,  6,      7,      10},
+       {       -1,             -1,     -1,     -1},
 };
 
 /* Terratec AV350 */
@@ -279,21 +280,21 @@ static struct em28xx_reg_seq vc211a_enable[] = {
 static struct em28xx_reg_seq dikom_dk300_digital[] = {
        {EM2820_R08_GPIO_CTRL,  0x6e,   ~EM_GPIO_4,     10},
        {EM2880_R04_GPO,        0x08,   0xff,           10},
-       { -1,                   -1,     -1,             -1},
+       {       -1,             -1,     -1,             -1},
 };
 
 
 /* Reset for the most [digital] boards */
 static struct em28xx_reg_seq leadership_digital[] = {
        {EM2874_R80_GPIO_P0_CTRL,       0x70,   0xff,   10},
-       {       -1,             -1,     -1,     -1},
+       {       -1,                     -1,     -1,     -1},
 };
 
 static struct em28xx_reg_seq leadership_reset[] = {
        {EM2874_R80_GPIO_P0_CTRL,       0xf0,   0xff,   10},
        {EM2874_R80_GPIO_P0_CTRL,       0xb0,   0xff,   10},
        {EM2874_R80_GPIO_P0_CTRL,       0xf0,   0xff,   10},
-       {       -1,             -1,     -1,     -1},
+       {       -1,                     -1,     -1,     -1},
 };
 
 /* 2013:024f PCTV nanoStick T2 290e
@@ -304,7 +305,7 @@ static struct em28xx_reg_seq pctv_290e[] = {
        {EM2874_R80_GPIO_P0_CTRL,       0x00,   0xff,   80},
        {EM2874_R80_GPIO_P0_CTRL,       0x40,   0xff,   80}, /* GPIO_6 = 1 */
        {EM2874_R80_GPIO_P0_CTRL,       0xc0,   0xff,   80}, /* GPIO_7 = 1 */
-       {-1,                    -1,     -1,             -1},
+       {       -1,                     -1,     -1,     -1},
 };
 
 #if 0
@@ -313,14 +314,14 @@ static struct em28xx_reg_seq terratec_h5_gpio[] = {
        {EM2874_R80_GPIO_P0_CTRL,       0xf6,   0xff,   100},
        {EM2874_R80_GPIO_P0_CTRL,       0xf2,   0xff,   50},
        {EM2874_R80_GPIO_P0_CTRL,       0xf6,   0xff,   50},
-       { -1,                   -1,     -1,     -1},
+       {       -1,                     -1,     -1,     -1},
 };
 
 static struct em28xx_reg_seq terratec_h5_digital[] = {
        {EM2874_R80_GPIO_P0_CTRL,       0xf6,   0xff,   10},
        {EM2874_R80_GPIO_P0_CTRL,       0xe6,   0xff,   100},
        {EM2874_R80_GPIO_P0_CTRL,       0xa6,   0xff,   10},
-       { -1,                   -1,     -1,     -1},
+       {       -1,                     -1,     -1,     -1},
 };
 #endif
 
@@ -335,12 +336,12 @@ static struct em28xx_reg_seq terratec_h5_digital[] = {
  * GPIO_7 - LED (green LED)
  */
 static struct em28xx_reg_seq pctv_460e[] = {
-       {EM2874_R80_GPIO_P0_CTRL, 0x01, 0xff,  50},
-       {0x0d,            0xff, 0xff,  50},
-       {EM2874_R80_GPIO_P0_CTRL, 0x41, 0xff,  50}, /* GPIO_6=1 */
-       {0x0d,            0x42, 0xff,  50},
-       {EM2874_R80_GPIO_P0_CTRL, 0x61, 0xff,  50}, /* GPIO_5=1 */
-       {             -1,   -1,   -1,  -1},
+       {EM2874_R80_GPIO_P0_CTRL,       0x01,   0xff,   50},
+       {       0x0d,                   0xff,   0xff,   50},
+       {EM2874_R80_GPIO_P0_CTRL,       0x41,   0xff,   50}, /* GPIO_6=1 */
+       {       0x0d,                   0x42,   0xff,   50},
+       {EM2874_R80_GPIO_P0_CTRL,       0x61,   0xff,   50}, /* GPIO_5=1 */
+       {       -1,                     -1,     -1,     -1},
 };
 
 static struct em28xx_reg_seq c3tech_digital_duo_digital[] = {
@@ -352,7 +353,7 @@ static struct em28xx_reg_seq c3tech_digital_duo_digital[] = {
        {EM2874_R80_GPIO_P0_CTRL,       0xfe,   0xff,   10},
        {EM2874_R80_GPIO_P0_CTRL,       0xbe,   0xff,   10},
        {EM2874_R80_GPIO_P0_CTRL,       0xfe,   0xff,   20},
-       { -1,                   -1,     -1,     -1},
+       {       -1,                     -1,     -1,     -1},
 };
 
 #if 0
@@ -361,14 +362,14 @@ static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
        {EM2874_R80_GPIO_P0_CTRL,       0x4f,   0xff,   10}, /* xc5000 reset */
        {EM2874_R80_GPIO_P0_CTRL,       0x6f,   0xff,   10},
        {EM2874_R80_GPIO_P0_CTRL,       0x4f,   0xff,   10},
-       { -1,                   -1,     -1,     -1},
+       {       -1,                     -1,     -1,     -1},
 };
 
 static struct em28xx_reg_seq hauppauge_930c_digital[] = {
        {EM2874_R80_GPIO_P0_CTRL,       0xf6,   0xff,   10},
        {EM2874_R80_GPIO_P0_CTRL,       0xe6,   0xff,   100},
        {EM2874_R80_GPIO_P0_CTRL,       0xa6,   0xff,   10},
-       { -1,                   -1,     -1,     -1},
+       {       -1,                     -1,     -1,     -1},
 };
 #endif
 
@@ -378,10 +379,10 @@ static struct em28xx_reg_seq hauppauge_930c_digital[] = {
  * GPIO_7 - LED, 0=active
  */
 static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
-       {EM2874_R80_GPIO_P0_CTRL,  0x83,  0xff,  100},
-       {EM2874_R80_GPIO_P0_CTRL,  0xc3,  0xff,  100}, /* GPIO_6 = 1 */
-       {EM2874_R80_GPIO_P0_CTRL,  0x43,  0xff,  000}, /* GPIO_7 = 0 */
-       {-1,                 -1,    -1,   -1},
+       {EM2874_R80_GPIO_P0_CTRL,       0x83,   0xff,   100},
+       {EM2874_R80_GPIO_P0_CTRL,       0xc3,   0xff,   100}, /* GPIO_6 = 1 */
+       {EM2874_R80_GPIO_P0_CTRL,       0x43,   0xff,   000}, /* GPIO_7 = 0 */
+       {       -1,                     -1,     -1,     -1},
 };
 
 /* 2304:0242 PCTV QuatroStick (510e)
@@ -391,10 +392,10 @@ static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
  * GPIO_7: LED, 1=active
  */
 static struct em28xx_reg_seq pctv_510e[] = {
-       {EM2874_R80_GPIO_P0_CTRL, 0x10, 0xff, 100},
-       {EM2874_R80_GPIO_P0_CTRL, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
-       {EM2874_R80_GPIO_P0_CTRL, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
-       {             -1,   -1,   -1,  -1},
+       {EM2874_R80_GPIO_P0_CTRL,       0x10,   0xff,   100},
+       {EM2874_R80_GPIO_P0_CTRL,       0x14,   0xff,   100}, /* GPIO_2 = 1 */
+       {EM2874_R80_GPIO_P0_CTRL,       0x54,   0xff,   050}, /* GPIO_6 = 1 */
+       {       -1,                     -1,     -1,     -1},
 };
 
 /* 2013:0251 PCTV QuatroStick nano (520e)
@@ -404,11 +405,11 @@ static struct em28xx_reg_seq pctv_510e[] = {
  * GPIO_7: LED, 1=active
  */
 static struct em28xx_reg_seq pctv_520e[] = {
-       {EM2874_R80_GPIO_P0_CTRL, 0x10, 0xff, 100},
-       {EM2874_R80_GPIO_P0_CTRL, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
-       {EM2874_R80_GPIO_P0_CTRL, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
-       {EM2874_R80_GPIO_P0_CTRL, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */
-       {             -1,   -1,   -1,  -1},
+       {EM2874_R80_GPIO_P0_CTRL,       0x10,   0xff,   100},
+       {EM2874_R80_GPIO_P0_CTRL,       0x14,   0xff,   100}, /* GPIO_2 = 1 */
+       {EM2874_R80_GPIO_P0_CTRL,       0x54,   0xff,   050}, /* GPIO_6 = 1 */
+       {EM2874_R80_GPIO_P0_CTRL,       0xd4,   0xff,   000}, /* GPIO_7 = 1 */
+       {       -1,                     -1,     -1,     -1},
 };
 
 /*
@@ -2030,6 +2031,18 @@ struct em28xx_board em28xx_boards[] = {
                .i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE |
                                EM28XX_I2C_FREQ_400_KHZ,
        },
+       /*
+        * 1b80:e346 KWorld USB ATSC TV Stick UB435-Q V2
+        * Empia EM2874B + LG DT3305 + NXP TDA18271HDC2
+        */
+       [EM2874_BOARD_KWORLD_UB435Q_V2] = {
+               .name           = "KWorld USB ATSC TV Stick UB435-Q V2",
+               .tuner_type     = TUNER_ABSENT,
+               .has_dvb        = 1,
+               .dvb_gpio       = kworld_a340_digital,
+               .tuner_gpio     = default_tuner_gpio,
+               .def_i2c_bus    = 1,
+       },
 };
 const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
 
@@ -2173,6 +2186,8 @@ struct usb_device_id em28xx_id_table[] = {
                        .driver_info = EM2860_BOARD_GADMEI_UTV330 },
        { USB_DEVICE(0x1b80, 0xa340),
                        .driver_info = EM2870_BOARD_KWORLD_A340 },
+       { USB_DEVICE(0x1b80, 0xe346),
+                       .driver_info = EM2874_BOARD_KWORLD_UB435Q_V2 },
        { USB_DEVICE(0x2013, 0x024f),
                        .driver_info = EM28174_BOARD_PCTV_290E },
        { USB_DEVICE(0x2013, 0x024c),
@@ -2857,6 +2872,8 @@ void em28xx_release_resources(struct em28xx *dev)
        if (dev->def_i2c_bus)
                em28xx_i2c_unregister(dev, 1);
        em28xx_i2c_unregister(dev, 0);
+       if (dev->clk)
+               v4l2_clk_unregister_fixed(dev->clk);
 
        v4l2_ctrl_handler_free(&dev->ctrl_handler);
 
index bb1e8dc..344042b 100644 (file)
@@ -298,6 +298,18 @@ static struct lgdt3305_config em2870_lgdt3304_dev = {
        .qam_if_khz         = 4000,
 };
 
+static struct lgdt3305_config em2874_lgdt3305_dev = {
+       .i2c_addr           = 0x0e,
+       .demod_chip         = LGDT3305,
+       .spectral_inversion = 1,
+       .deny_i2c_rptr      = 0,
+       .mpeg_mode          = LGDT3305_MPEG_SERIAL,
+       .tpclk_edge         = LGDT3305_TPCLK_FALLING_EDGE,
+       .tpvalid_polarity   = LGDT3305_TP_VALID_HIGH,
+       .vsb_if_khz         = 3250,
+       .qam_if_khz         = 4000,
+};
+
 static struct s921_config sharp_isdbt = {
        .demod_address = 0x30 >> 1
 };
@@ -329,6 +341,11 @@ static struct tda18271_config kworld_a340_config = {
        .std_map           = &kworld_a340_std_map,
 };
 
+static struct tda18271_config kworld_ub435q_v2_config = {
+       .std_map        = &kworld_a340_std_map,
+       .gate           = TDA18271_GATE_DIGITAL,
+};
+
 static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = {
        .demod_address = (0x1e >> 1),
        .no_tuner = 1,
@@ -384,7 +401,10 @@ static struct drxk_config maxmedia_ub425_tc_drxk = {
        .adr = 0x29,
        .single_master = 1,
        .no_i2c_bridge = 1,
+       .microcode_name = "dvb-demod-drxk-01.fw",
+       .chunk_size = 62,
        .load_firmware_sync = true,
+       .qam_demod_parameter_count = 2,
 };
 
 static struct drxk_config pctv_520e_drxk = {
@@ -424,7 +444,7 @@ static void hauppauge_hvr930c_init(struct em28xx *dev)
                {EM2874_R80_GPIO_P0_CTRL,       0xff,   0xff,   0x65},
                {EM2874_R80_GPIO_P0_CTRL,       0xfb,   0xff,   0x32},
                {EM2874_R80_GPIO_P0_CTRL,       0xff,   0xff,   0xb8},
-               { -1,                   -1,     -1,     -1},
+               {       -1,                     -1,     -1,     -1},
        };
        struct em28xx_reg_seq hauppauge_hvr930c_end[] = {
                {EM2874_R80_GPIO_P0_CTRL,       0xef,   0xff,   0x01},
@@ -439,7 +459,7 @@ static void hauppauge_hvr930c_init(struct em28xx *dev)
                {EM2874_R80_GPIO_P0_CTRL,       0xcf,   0xff,   0x0b},
                {EM2874_R80_GPIO_P0_CTRL,       0xef,   0xff,   0x65},
 
-               { -1,                   -1,     -1,     -1},
+               {       -1,                     -1,     -1,     -1},
        };
 
        struct {
@@ -491,13 +511,13 @@ static void terratec_h5_init(struct em28xx *dev)
                {EM2874_R80_GPIO_P0_CTRL,       0xf6,   0xff,   100},
                {EM2874_R80_GPIO_P0_CTRL,       0xf2,   0xff,   50},
                {EM2874_R80_GPIO_P0_CTRL,       0xf6,   0xff,   100},
-               { -1,                   -1,     -1,     -1},
+               {       -1,                     -1,     -1,     -1},
        };
        struct em28xx_reg_seq terratec_h5_end[] = {
                {EM2874_R80_GPIO_P0_CTRL,       0xe6,   0xff,   100},
                {EM2874_R80_GPIO_P0_CTRL,       0xa6,   0xff,   50},
                {EM2874_R80_GPIO_P0_CTRL,       0xe6,   0xff,   100},
-               { -1,                   -1,     -1,     -1},
+               {       -1,                     -1,     -1,     -1},
        };
        struct {
                unsigned char r[4];
@@ -547,12 +567,12 @@ static void terratec_htc_stick_init(struct em28xx *dev)
                {EM2874_R80_GPIO_P0_CTRL,       0xf6,   0xff,   100},
                {EM2874_R80_GPIO_P0_CTRL,       0xe6,   0xff,   50},
                {EM2874_R80_GPIO_P0_CTRL,       0xf6,   0xff,   100},
-               { -1,                   -1,     -1,     -1},
+               {       -1,                     -1,     -1,     -1},
        };
        struct em28xx_reg_seq terratec_htc_stick_end[] = {
                {EM2874_R80_GPIO_P0_CTRL,       0xb6,   0xff,   100},
                {EM2874_R80_GPIO_P0_CTRL,       0xf6,   0xff,   50},
-               { -1,                   -1,     -1,     -1},
+               {       -1,                     -1,     -1,     -1},
        };
 
        /*
@@ -594,13 +614,13 @@ static void terratec_htc_usb_xs_init(struct em28xx *dev)
                {EM2874_R80_GPIO_P0_CTRL,       0xb2,   0xff,   100},
                {EM2874_R80_GPIO_P0_CTRL,       0xb2,   0xff,   50},
                {EM2874_R80_GPIO_P0_CTRL,       0xb6,   0xff,   100},
-               { -1,                   -1,     -1,     -1},
+               {       -1,                     -1,     -1,     -1},
        };
        struct em28xx_reg_seq terratec_htc_usb_xs_end[] = {
                {EM2874_R80_GPIO_P0_CTRL,       0xa6,   0xff,   100},
                {EM2874_R80_GPIO_P0_CTRL,       0xa6,   0xff,   50},
                {EM2874_R80_GPIO_P0_CTRL,       0xe6,   0xff,   100},
-               { -1,                   -1,     -1,     -1},
+               {       -1,                     -1,     -1,     -1},
        };
 
        /*
@@ -1227,18 +1247,14 @@ static int em28xx_dvb_init(struct em28xx *dev)
                        dvb->fe[0]->ops.i2c_gate_ctrl = NULL;
 
                        /* attach tuner */
-                       if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0],
-                                       &dev->i2c_adap[dev->def_i2c_bus], 0x60)) {
+                       if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+                                       &dev->i2c_adap[dev->def_i2c_bus],
+                                       &em28xx_cxd2820r_tda18271_config)) {
                                dvb_frontend_detach(dvb->fe[0]);
                                result = -EINVAL;
                                goto out_free;
                        }
                }
-
-               /* TODO: we need drx-3913k firmware in order to support DVB-T */
-               em28xx_info("MaxMedia UB425-TC/Delock 61959: only DVB-C " \
-                               "supported by that driver version\n");
-
                break;
        case EM2884_BOARD_PCTV_510E:
        case EM2884_BOARD_PCTV_520E:
@@ -1297,6 +1313,23 @@ static int em28xx_dvb_init(struct em28xx *dev)
                        goto out_free;
                }
                break;
+       case EM2874_BOARD_KWORLD_UB435Q_V2:
+               dvb->fe[0] = dvb_attach(lgdt3305_attach,
+                                       &em2874_lgdt3305_dev,
+                                       &dev->i2c_adap[dev->def_i2c_bus]);
+               if (!dvb->fe[0]) {
+                       result = -EINVAL;
+                       goto out_free;
+               }
+
+               /* Attach the demodulator. */
+               if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+                               &dev->i2c_adap[dev->def_i2c_bus],
+                               &kworld_ub435q_v2_config)) {
+                       result = -EINVAL;
+                       goto out_free;
+               }
+               break;
        default:
                em28xx_errdev("/2: The frontend of your DVB/ATSC card"
                                " isn't supported yet\n");
index 9d10334..fc5d60e 100644 (file)
@@ -638,7 +638,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
        if (rc)
                return rc;
 
-       if (dev->streaming_users++ == 0) {
+       if (dev->streaming_users == 0) {
                /* First active streaming user, so allocate all the URBs */
 
                /* Allocate the USB bandwidth */
@@ -657,7 +657,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
                                          dev->packet_multiplier,
                                          em28xx_urb_data_copy);
                if (rc < 0)
-                       goto fail;
+                       return rc;
 
                /*
                 * djh: it's not clear whether this code is still needed.  I'm
@@ -675,7 +675,8 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
                v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
        }
 
-fail:
+       dev->streaming_users++;
+
        return rc;
 }
 
index 205e903..f8726ad 100644 (file)
 #define EM2884_BOARD_TERRATEC_HTC_USB_XS         87
 #define EM2884_BOARD_C3TECH_DIGITAL_DUO                  88
 #define EM2874_BOARD_DELOCK_61959                89
+#define EM2874_BOARD_KWORLD_UB435Q_V2            90
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
@@ -492,6 +493,7 @@ struct em28xx {
 
        struct v4l2_device v4l2_dev;
        struct v4l2_ctrl_handler ctrl_handler;
+       struct v4l2_clk *clk;
        struct em28xx_board board;
 
        /* Webcam specific fields */
index 38714df..2e15c80 100644 (file)
@@ -783,7 +783,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
 
        /* create the JPEG header */
-       jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+       jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width,
                        0x22);          /* JPEG 411 */
        jpeg_set_qual(sd->jpeg_hdr, QUALITY);
 
index 064b530..f23df4a 100644 (file)
@@ -1553,9 +1553,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
                sd->params.format.videoSize = VIDEOSIZE_CIF;
 
        sd->params.roi.colEnd = sd->params.roi.colStart +
-                               (gspca_dev->width >> 3);
+                               (gspca_dev->pixfmt.width >> 3);
        sd->params.roi.rowEnd = sd->params.roi.rowStart +
-                               (gspca_dev->height >> 2);
+                               (gspca_dev->pixfmt.height >> 2);
 
        /* And now set the camera to a known state */
        ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode,
index 048507b..f3a7ace 100644 (file)
@@ -504,8 +504,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
        unsigned int frsz;
        int i;
 
-       i = gspca_dev->curr_mode;
-       frsz = gspca_dev->cam.cam_mode[i].sizeimage;
+       frsz = gspca_dev->pixfmt.sizeimage;
        PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz);
        frsz = PAGE_ALIGN(frsz);
        if (count >= GSPCA_MAX_FRAMES)
@@ -627,16 +626,14 @@ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
 static u32 which_bandwidth(struct gspca_dev *gspca_dev)
 {
        u32 bandwidth;
-       int i;
 
        /* get the (max) image size */
-       i = gspca_dev->curr_mode;
-       bandwidth = gspca_dev->cam.cam_mode[i].sizeimage;
+       bandwidth = gspca_dev->pixfmt.sizeimage;
 
        /* if the image is compressed, estimate its mean size */
        if (!gspca_dev->cam.needs_full_bandwidth &&
-           bandwidth < gspca_dev->cam.cam_mode[i].width *
-                               gspca_dev->cam.cam_mode[i].height)
+           bandwidth < gspca_dev->pixfmt.width *
+                               gspca_dev->pixfmt.height)
                bandwidth = bandwidth * 3 / 8;  /* 0.375 */
 
        /* estimate the frame rate */
@@ -650,7 +647,7 @@ static u32 which_bandwidth(struct gspca_dev *gspca_dev)
 
                /* don't hope more than 15 fps with USB 1.1 and
                 * image resolution >= 640x480 */
-               if (gspca_dev->width >= 640
+               if (gspca_dev->pixfmt.width >= 640
                 && gspca_dev->dev->speed == USB_SPEED_FULL)
                        bandwidth *= 15;                /* 15 fps */
                else
@@ -982,9 +979,7 @@ static void gspca_set_default_mode(struct gspca_dev *gspca_dev)
 
        i = gspca_dev->cam.nmodes - 1;  /* take the highest mode */
        gspca_dev->curr_mode = i;
-       gspca_dev->width = gspca_dev->cam.cam_mode[i].width;
-       gspca_dev->height = gspca_dev->cam.cam_mode[i].height;
-       gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat;
+       gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i];
 
        /* does nothing if ctrl_handler == NULL */
        v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
@@ -1105,10 +1100,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                            struct v4l2_format *fmt)
 {
        struct gspca_dev *gspca_dev = video_drvdata(file);
-       int mode;
 
-       mode = gspca_dev->curr_mode;
-       fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
+       fmt->fmt.pix = gspca_dev->pixfmt;
        /* some drivers use priv internally, zero it before giving it to
           userspace */
        fmt->fmt.pix.priv = 0;
@@ -1140,6 +1133,12 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
                        mode = mode2;
        }
        fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
+       if (gspca_dev->sd_desc->try_fmt) {
+               /* pass original resolution to subdriver try_fmt */
+               fmt->fmt.pix.width = w;
+               fmt->fmt.pix.height = h;
+               gspca_dev->sd_desc->try_fmt(gspca_dev, fmt);
+       }
        /* some drivers use priv internally, zero it before giving it to
           userspace */
        fmt->fmt.pix.priv = 0;
@@ -1178,19 +1177,16 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                goto out;
        }
 
-       if (ret == gspca_dev->curr_mode) {
-               ret = 0;
-               goto out;                       /* same mode */
-       }
-
        if (gspca_dev->streaming) {
                ret = -EBUSY;
                goto out;
        }
-       gspca_dev->width = fmt->fmt.pix.width;
-       gspca_dev->height = fmt->fmt.pix.height;
-       gspca_dev->pixfmt = fmt->fmt.pix.pixelformat;
        gspca_dev->curr_mode = ret;
+       if (gspca_dev->sd_desc->try_fmt)
+               /* subdriver try_fmt can modify format parameters */
+               gspca_dev->pixfmt = fmt->fmt.pix;
+       else
+               gspca_dev->pixfmt = gspca_dev->cam.cam_mode[ret];
 
        ret = 0;
 out:
@@ -1205,6 +1201,9 @@ static int vidioc_enum_framesizes(struct file *file, void *priv,
        int i;
        __u32 index = 0;
 
+       if (gspca_dev->sd_desc->enum_framesizes)
+               return gspca_dev->sd_desc->enum_framesizes(gspca_dev, fsize);
+
        for (i = 0; i < gspca_dev->cam.nmodes; i++) {
                if (fsize->pixel_format !=
                                gspca_dev->cam.cam_mode[i].pixelformat)
@@ -1471,8 +1470,9 @@ static int vidioc_streamon(struct file *file, void *priv,
                if (ret < 0)
                        goto out;
        }
-       PDEBUG_MODE(gspca_dev, D_STREAM, "stream on OK", gspca_dev->pixfmt,
-                   gspca_dev->width, gspca_dev->height);
+       PDEBUG_MODE(gspca_dev, D_STREAM, "stream on OK",
+                   gspca_dev->pixfmt.pixelformat,
+                   gspca_dev->pixfmt.width, gspca_dev->pixfmt.height);
        ret = 0;
 out:
        mutex_unlock(&gspca_dev->queue_lock);
index ac0b11f..300642d 100644 (file)
@@ -88,6 +88,10 @@ typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
 typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev,
                                u8 *data,
                                int len);
+typedef void (*cam_format_op) (struct gspca_dev *gspca_dev,
+                               struct v4l2_format *fmt);
+typedef int (*cam_frmsize_op) (struct gspca_dev *gspca_dev,
+                               struct v4l2_frmsizeenum *fsize);
 
 /* subdriver description */
 struct sd_desc {
@@ -109,6 +113,8 @@ struct sd_desc {
        cam_set_jpg_op set_jcomp;
        cam_streamparm_op get_streamparm;
        cam_streamparm_op set_streamparm;
+       cam_format_op try_fmt;
+       cam_frmsize_op enum_framesizes;
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        cam_set_reg_op set_register;
        cam_get_reg_op get_register;
@@ -183,9 +189,7 @@ struct gspca_dev {
        __u8 streaming;                 /* protected by both mutexes (*) */
 
        __u8 curr_mode;                 /* current camera mode */
-       __u32 pixfmt;                   /* current mode parameters */
-       __u16 width;
-       __u16 height;
+       struct v4l2_pix_format pixfmt;  /* current mode parameters */
        __u32 sequence;                 /* frame sequence number */
 
        wait_queue_head_t wq;           /* wait queue */
index 8da3dde..19736e2 100644 (file)
@@ -378,11 +378,12 @@ static int sd_start(struct gspca_dev *gspca_dev)
        struct sd *dev = (struct sd *) gspca_dev;
 
        /* create the JPEG header */
-       jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+       jpeg_define(dev->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width,
                        0x21);          /* JPEG 422 */
        jpeg_set_qual(dev->jpeg_hdr, dev->quality);
        PDEBUG(D_STREAM, "Start streaming at %dx%d",
-               gspca_dev->height, gspca_dev->width);
+               gspca_dev->pixfmt.height, gspca_dev->pixfmt.width);
        jlj_start(gspca_dev);
        return gspca_dev->usb_err;
 }
index fdaeeb1..5b481fa 100644 (file)
@@ -455,7 +455,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
        sd->cap_mode = gspca_dev->cam.cam_mode;
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 640:
                PDEBUG(D_STREAM, "Start streaming at vga resolution");
                jl2005c_stream_start_vga_lg(gspca_dev);
index cfa4663..27fcef1 100644 (file)
@@ -266,7 +266,7 @@ static int mt9m111_set_hvflip(struct gspca_dev *gspca_dev)
                return err;
 
        data[0] = MT9M111_RMB_OVER_SIZED;
-       if (gspca_dev->width == 640) {
+       if (gspca_dev->pixfmt.width == 640) {
                data[1] = MT9M111_RMB_ROW_SKIP_2X |
                          MT9M111_RMB_COLUMN_SKIP_2X |
                          (hflip << 1) | vflip;
index ff2c5ab..779a878 100644 (file)
@@ -254,7 +254,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        int i;
 
        /* create the JPEG header */
-       jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+       jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width,
                        0x21);          /* JPEG 422 */
        jpeg_set_qual(sd->jpeg_hdr, QUALITY);
 
@@ -270,8 +271,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        data[0] = 0x00;         /* address */
        data[1] = 0x0c | 0x01;  /* reg 0 */
        data[2] = 0x01;         /* reg 1 */
-       data[3] = gspca_dev->width / 8;         /* h_size , reg 2 */
-       data[4] = gspca_dev->height / 8;        /* v_size , reg 3 */
+       data[3] = gspca_dev->pixfmt.width / 8;  /* h_size , reg 2 */
+       data[4] = gspca_dev->pixfmt.height / 8; /* v_size , reg 3 */
        data[5] = 0x30;         /* reg 4, MI, PAS5101 :
                                 *      0x30 for 24mhz , 0x28 for 12mhz */
        data[6] = 0x02;         /* reg 5, H start - was 0x04 */
index 68bb2f3..f006e29 100644 (file)
@@ -521,7 +521,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev)
        if (sd->sensor_type)
                data[5] = 0xbb;
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 160:
                data[9] |= 0x04;  /* reg 8, 2:1 scale down from 320 */
                /* fall thru */
@@ -618,7 +618,7 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
                data[10] = 0x18;
        }
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 160:
                data[9] |= 0x0c;  /* reg 8, 4:1 scale down */
                /* fall thru */
@@ -847,7 +847,7 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv)
                u8 clockdiv = (60 * expo + 7999) / 8000;
 
                /* Limit framerate to not exceed usb bandwidth */
-               if (clockdiv < min_clockdiv && gspca_dev->width >= 320)
+               if (clockdiv < min_clockdiv && gspca_dev->pixfmt.width >= 320)
                        clockdiv = min_clockdiv;
                else if (clockdiv < 2)
                        clockdiv = 2;
index 44c9964..599f755 100644 (file)
@@ -1708,7 +1708,7 @@ static void setautogain(struct gspca_dev *gspca_dev, s32 val)
 
        reg_r(gspca_dev, 0x1004, 1);
        if (gspca_dev->usb_buf[0] & 0x04) {     /* if AE_FULL_FRM */
-               sd->ae_res = gspca_dev->width * gspca_dev->height;
+               sd->ae_res = gspca_dev->pixfmt.width * gspca_dev->pixfmt.height;
        } else {                                /* get the AE window size */
                reg_r(gspca_dev, 0x1011, 8);
                w = (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]
@@ -1717,7 +1717,8 @@ static void setautogain(struct gspca_dev *gspca_dev, s32 val)
                  - (gspca_dev->usb_buf[7] << 8) - gspca_dev->usb_buf[6];
                sd->ae_res = h * w;
                if (sd->ae_res == 0)
-                       sd->ae_res = gspca_dev->width * gspca_dev->height;
+                       sd->ae_res = gspca_dev->pixfmt.width *
+                                       gspca_dev->pixfmt.height;
        }
 }
 
@@ -1856,21 +1857,21 @@ static int sd_start(struct gspca_dev *gspca_dev)
        reg_w_buf(gspca_dev, cmd);
        switch (sd->webcam) {
        case P35u:
-               if (gspca_dev->width == 320)
+               if (gspca_dev->pixfmt.width == 320)
                        reg_w_buf(gspca_dev, nw801_start_qvga);
                else
                        reg_w_buf(gspca_dev, nw801_start_vga);
                reg_w_buf(gspca_dev, nw801_start_2);
                break;
        case Kr651us:
-               if (gspca_dev->width == 320)
+               if (gspca_dev->pixfmt.width == 320)
                        reg_w_buf(gspca_dev, kr651_start_qvga);
                else
                        reg_w_buf(gspca_dev, kr651_start_vga);
                reg_w_buf(gspca_dev, kr651_start_2);
                break;
        case Proscope:
-               if (gspca_dev->width == 320)
+               if (gspca_dev->pixfmt.width == 320)
                        reg_w_buf(gspca_dev, proscope_start_qvga);
                else
                        reg_w_buf(gspca_dev, proscope_start_vga);
index 8937d79..c95f32a 100644 (file)
@@ -3468,7 +3468,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
 
        switch (sd->bridge) {
        case BRIDGE_OVFX2:
-               if (gspca_dev->width != 800)
+               if (gspca_dev->pixfmt.width != 800)
                        gspca_dev->cam.bulk_size = OVFX2_BULK_SIZE;
                else
                        gspca_dev->cam.bulk_size = 7 * 4096;
@@ -3507,8 +3507,8 @@ static void ov511_mode_init_regs(struct sd *sd)
        /* Here I'm assuming that snapshot size == image size.
         * I hope that's always true. --claudio
         */
-       hsegs = (sd->gspca_dev.width >> 3) - 1;
-       vsegs = (sd->gspca_dev.height >> 3) - 1;
+       hsegs = (sd->gspca_dev.pixfmt.width >> 3) - 1;
+       vsegs = (sd->gspca_dev.pixfmt.height >> 3) - 1;
 
        reg_w(sd, R511_CAM_PXCNT, hsegs);
        reg_w(sd, R511_CAM_LNCNT, vsegs);
@@ -3541,7 +3541,7 @@ static void ov511_mode_init_regs(struct sd *sd)
        case SEN_OV7640:
        case SEN_OV7648:
        case SEN_OV76BE:
-               if (sd->gspca_dev.width == 320)
+               if (sd->gspca_dev.pixfmt.width == 320)
                        interlaced = 1;
                /* Fall through */
        case SEN_OV6630:
@@ -3551,7 +3551,7 @@ static void ov511_mode_init_regs(struct sd *sd)
                case 30:
                case 25:
                        /* Not enough bandwidth to do 640x480 @ 30 fps */
-                       if (sd->gspca_dev.width != 640) {
+                       if (sd->gspca_dev.pixfmt.width != 640) {
                                sd->clockdiv = 0;
                                break;
                        }
@@ -3584,7 +3584,8 @@ static void ov511_mode_init_regs(struct sd *sd)
 
        /* Check if we have enough bandwidth to disable compression */
        fps = (interlaced ? 60 : 30) / (sd->clockdiv + 1) + 1;
-       needed = fps * sd->gspca_dev.width * sd->gspca_dev.height * 3 / 2;
+       needed = fps * sd->gspca_dev.pixfmt.width *
+                       sd->gspca_dev.pixfmt.height * 3 / 2;
        /* 1000 isoc packets/sec */
        if (needed > 1000 * packet_size) {
                /* Enable Y and UV quantization and compression */
@@ -3646,8 +3647,8 @@ static void ov518_mode_init_regs(struct sd *sd)
                reg_w(sd, 0x38, 0x80);
        }
 
-       hsegs = sd->gspca_dev.width / 16;
-       vsegs = sd->gspca_dev.height / 4;
+       hsegs = sd->gspca_dev.pixfmt.width / 16;
+       vsegs = sd->gspca_dev.pixfmt.height / 4;
 
        reg_w(sd, 0x29, hsegs);
        reg_w(sd, 0x2a, vsegs);
@@ -3686,7 +3687,8 @@ static void ov518_mode_init_regs(struct sd *sd)
                         * happened to be with revision < 2 cams using an
                         * OV7620 and revision 2 cams using an OV7620AE.
                         */
-                       if (sd->revision > 0 && sd->gspca_dev.width == 640) {
+                       if (sd->revision > 0 &&
+                                       sd->gspca_dev.pixfmt.width == 640) {
                                reg_w(sd, 0x20, 0x60);
                                reg_w(sd, 0x21, 0x1f);
                        } else {
@@ -3812,8 +3814,8 @@ static void ov519_mode_init_regs(struct sd *sd)
                break;
        }
 
-       reg_w(sd, OV519_R10_H_SIZE,     sd->gspca_dev.width >> 4);
-       reg_w(sd, OV519_R11_V_SIZE,     sd->gspca_dev.height >> 3);
+       reg_w(sd, OV519_R10_H_SIZE,     sd->gspca_dev.pixfmt.width >> 4);
+       reg_w(sd, OV519_R11_V_SIZE,     sd->gspca_dev.pixfmt.height >> 3);
        if (sd->sensor == SEN_OV7670 &&
            sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
                reg_w(sd, OV519_R12_X_OFFSETL, 0x04);
@@ -3947,14 +3949,16 @@ static void mode_init_ov_sensor_regs(struct sd *sd)
            }
        case SEN_OV3610:
                if (qvga) {
-                       xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4);
-                       ystart = (776 - gspca_dev->height) / 2;
+                       xstart = (1040 - gspca_dev->pixfmt.width) / 2 +
+                               (0x1f << 4);
+                       ystart = (776 - gspca_dev->pixfmt.height) / 2;
                } else {
-                       xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4);
-                       ystart = (1544 - gspca_dev->height) / 2;
+                       xstart = (2076 - gspca_dev->pixfmt.width) / 2 +
+                               (0x10 << 4);
+                       ystart = (1544 - gspca_dev->pixfmt.height) / 2;
                }
-               xend = xstart + gspca_dev->width;
-               yend = ystart + gspca_dev->height;
+               xend = xstart + gspca_dev->pixfmt.width;
+               yend = ystart + gspca_dev->pixfmt.height;
                /* Writing to the COMH register resets the other windowing regs
                   to their default values, so we must do this first. */
                i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0);
@@ -4229,8 +4233,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
 
        /* Default for most bridges, allow bridge_mode_init_regs to override */
-       sd->sensor_width = sd->gspca_dev.width;
-       sd->sensor_height = sd->gspca_dev.height;
+       sd->sensor_width = sd->gspca_dev.pixfmt.width;
+       sd->sensor_height = sd->gspca_dev.pixfmt.height;
 
        switch (sd->bridge) {
        case BRIDGE_OV511:
@@ -4345,12 +4349,13 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
                ov51x_handle_button(gspca_dev, (in[8] >> 2) & 1);
                if (in[8] & 0x80) {
                        /* Frame end */
-                       if ((in[9] + 1) * 8 != gspca_dev->width ||
-                           (in[10] + 1) * 8 != gspca_dev->height) {
+                       if ((in[9] + 1) * 8 != gspca_dev->pixfmt.width ||
+                           (in[10] + 1) * 8 != gspca_dev->pixfmt.height) {
                                PERR("Invalid frame size, got: %dx%d,"
                                        " requested: %dx%d\n",
                                        (in[9] + 1) * 8, (in[10] + 1) * 8,
-                                       gspca_dev->width, gspca_dev->height);
+                                       gspca_dev->pixfmt.width,
+                                       gspca_dev->pixfmt.height);
                                gspca_dev->last_packet_type = DISCARD_PACKET;
                                return;
                        }
@@ -4470,7 +4475,8 @@ static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev,
                if (sd->first_frame) {
                        sd->first_frame--;
                        if (gspca_dev->image_len <
-                                 sd->gspca_dev.width * sd->gspca_dev.height)
+                                 sd->gspca_dev.pixfmt.width *
+                                       sd->gspca_dev.pixfmt.height)
                                gspca_dev->last_packet_type = DISCARD_PACKET;
                }
                gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
index 03a33c4..90f0d63 100644 (file)
@@ -1440,9 +1440,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                /* If this packet is marked as EOF, end the frame */
                } else if (data[1] & UVC_STREAM_EOF) {
                        sd->last_pts = 0;
-                       if (gspca_dev->pixfmt == V4L2_PIX_FMT_YUYV
+                       if (gspca_dev->pixfmt.pixelformat == V4L2_PIX_FMT_YUYV
                         && gspca_dev->image_len + len - 12 !=
-                                  gspca_dev->width * gspca_dev->height * 2) {
+                                  gspca_dev->pixfmt.width *
+                                       gspca_dev->pixfmt.height * 2) {
                                PDEBUG(D_PACK, "wrong sized frame");
                                goto discard;
                        }
index c4cd028..47085cf 100644 (file)
@@ -59,6 +59,7 @@ enum sensors {
        SENSOR_OV965x,          /* ov9657 */
        SENSOR_OV971x,          /* ov9712 */
        SENSOR_OV562x,          /* ov5621 */
+       SENSOR_OV361x,          /* ov3610 */
        NSENSORS
 };
 
@@ -106,6 +107,274 @@ static const struct v4l2_pix_format ov562x_mode[] = {
        }
 };
 
+enum ov361x {
+       ov361x_2048 = 0,
+       ov361x_1600,
+       ov361x_1024,
+       ov361x_640,
+       ov361x_320,
+       ov361x_160,
+       ov361x_last
+};
+
+static const struct v4l2_pix_format ov361x_mode[] = {
+       {0x800, 0x600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 0x800,
+               .sizeimage = 0x800 * 0x600,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+       {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 1600,
+               .sizeimage = 1600 * 1200,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+       {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 768,
+               .sizeimage = 1024 * 768,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+       {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+       {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+       {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 160,
+               .sizeimage = 160 * 120,
+               .colorspace = V4L2_COLORSPACE_SRGB}
+};
+
+static const u8 ov361x_start_2048[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0c},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x00},
+       {0x17, 0x10},
+       {0x18, 0x90},
+       {0x19, 0x00},
+       {0x1a, 0xc0},
+};
+static const u8 ov361x_bridge_start_2048[][2] = {
+       {0xf1, 0x60},
+       {0x88, 0x00},
+       {0x89, 0x08},
+       {0x8a, 0x00},
+       {0x8b, 0x06},
+       {0x8c, 0x01},
+       {0x8d, 0x10},
+       {0x1c, 0x00},
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},
+       {0x1d, 0x2e},
+       {0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_1600[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0C},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x00},
+       {0x17, 0x10},
+       {0x18, 0x90},
+       {0x19, 0x00},
+       {0x1a, 0xc0},
+};
+static const u8 ov361x_bridge_start_1600[][2] = {
+       {0xf1, 0x60},  /* Hsize[7:0] */
+       {0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+       {0x89, 0x08},  /* Vsize[7:0] */
+       {0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+       {0x8b, 0x06},  /* for Iso */
+       {0x8c, 0x01},  /* RAW input */
+       {0x8d, 0x10},
+       {0x1c, 0x00},  /* RAW output, Iso transfer */
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+       {0x1d, 0x2e},  /* for Iso */
+       {0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_1024[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0C},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x40},
+       {0x17, 0x1f},
+       {0x18, 0x5f},
+       {0x19, 0x00},
+       {0x1a, 0x68},
+};
+static const u8 ov361x_bridge_start_1024[][2] = {
+       {0xf1, 0x60},  /* Hsize[7:0] */
+       {0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+       {0x89, 0x04},  /* Vsize[7:0] */
+       {0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+       {0x8b, 0x03},  /* for Iso */
+       {0x8c, 0x01},  /* RAW input  */
+       {0x8d, 0x10},
+       {0x1c, 0x00},  /* RAW output, Iso transfer */
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+       {0x1d, 0x2e},  /* for Iso */
+       {0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_640[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0C},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x40},
+       {0x17, 0x1f},
+       {0x18, 0x5f},
+       {0x19, 0x00},
+       {0x1a, 0x68},
+};
+
+static const u8 ov361x_bridge_start_640[][2] = {
+       {0xf1, 0x60},  /* Hsize[7:0]*/
+       {0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+       {0x89, 0x04},  /* Vsize[7:0] */
+       {0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+       {0x8b, 0x03},  /* for Iso */
+       {0x8c, 0x01},  /* RAW input */
+       {0x8d, 0x10},
+       {0x1c, 0x00},  /* RAW output, Iso transfer */
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+       {0x1d, 0x2e},  /* for Iso */
+       {0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_320[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0C},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x40},
+       {0x17, 0x1f},
+       {0x18, 0x5f},
+       {0x19, 0x00},
+       {0x1a, 0x68},
+};
+
+static const u8 ov361x_bridge_start_320[][2] = {
+       {0xf1, 0x60},  /* Hsize[7:0] */
+       {0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+       {0x89, 0x04},  /* Vsize[7:0] */
+       {0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+       {0x8b, 0x03},  /* for Iso */
+       {0x8c, 0x01},  /* RAW input */
+       {0x8d, 0x10},
+       {0x1c, 0x00},  /* RAW output, Iso transfer; */
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+       {0x1d, 0x2e},  /* for Iso */
+       {0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_160[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0C},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x40},
+       {0x17, 0x1f},
+       {0x18, 0x5f},
+       {0x19, 0x00},
+       {0x1a, 0x68},
+};
+
+static const u8 ov361x_bridge_start_160[][2] = {
+       {0xf1, 0x60},  /* Hsize[7:0] */
+       {0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+       {0x89, 0x04},  /* Vsize[7:0] */
+       {0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+       {0x8b, 0x03},  /* for Iso */
+       {0x8c, 0x01},  /* RAW input */
+       {0x8d, 0x10},
+       {0x1c, 0x00},  /* RAW output, Iso transfer */
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+       {0x1d, 0x2e},  /* for Iso */
+       {0x1d, 0x1e},
+};
+
 static const u8 bridge_init[][2] = {
        {0x88, 0xf8},
        {0x89, 0xff},
@@ -898,7 +1167,7 @@ static int sccb_check_status(struct gspca_dev *gspca_dev)
        int i;
 
        for (i = 0; i < 5; i++) {
-               msleep(10);
+               msleep(20);
                data = reg_r(gspca_dev, OV534_REG_STATUS);
 
                switch (data) {
@@ -1221,6 +1490,13 @@ static int sd_init(struct gspca_dev *gspca_dev)
                sccb_w_array(gspca_dev, ov562x_init_2,
                                ARRAY_SIZE(ov562x_init_2));
                reg_w(gspca_dev, 0xe0, 0x00);
+       } else if ((sensor_id & 0xfff0) == 0x3610) {
+               sd->sensor = SENSOR_OV361x;
+               gspca_dev->cam.cam_mode = ov361x_mode;
+               gspca_dev->cam.nmodes = ARRAY_SIZE(ov361x_mode);
+               reg_w(gspca_dev, 0xe7, 0x3a);
+               reg_w(gspca_dev, 0xf1, 0x60);
+               sccb_write(gspca_dev, 0x12, 0x80);
        } else {
                pr_err("Unknown sensor %04x", sensor_id);
                return -EINVAL;
@@ -1229,6 +1505,53 @@ static int sd_init(struct gspca_dev *gspca_dev)
        return gspca_dev->usb_err;
 }
 
+static int sd_start_ov361x(struct gspca_dev *gspca_dev)
+{
+       sccb_write(gspca_dev, 0x12, 0x80);
+       msleep(20);
+       switch (gspca_dev->curr_mode % (ov361x_last)) {
+       case ov361x_2048:
+               reg_w_array(gspca_dev, ov361x_bridge_start_2048,
+                           ARRAY_SIZE(ov361x_bridge_start_2048));
+               sccb_w_array(gspca_dev, ov361x_start_2048,
+                            ARRAY_SIZE(ov361x_start_2048));
+               break;
+       case ov361x_1600:
+               reg_w_array(gspca_dev, ov361x_bridge_start_1600,
+                           ARRAY_SIZE(ov361x_bridge_start_1600));
+               sccb_w_array(gspca_dev, ov361x_start_1600,
+                            ARRAY_SIZE(ov361x_start_1600));
+               break;
+       case ov361x_1024:
+               reg_w_array(gspca_dev, ov361x_bridge_start_1024,
+                           ARRAY_SIZE(ov361x_bridge_start_1024));
+               sccb_w_array(gspca_dev, ov361x_start_1024,
+                            ARRAY_SIZE(ov361x_start_1024));
+               break;
+       case ov361x_640:
+               reg_w_array(gspca_dev, ov361x_bridge_start_640,
+                           ARRAY_SIZE(ov361x_bridge_start_640));
+               sccb_w_array(gspca_dev, ov361x_start_640,
+                            ARRAY_SIZE(ov361x_start_640));
+               break;
+       case ov361x_320:
+               reg_w_array(gspca_dev, ov361x_bridge_start_320,
+                           ARRAY_SIZE(ov361x_bridge_start_320));
+               sccb_w_array(gspca_dev, ov361x_start_320,
+                            ARRAY_SIZE(ov361x_start_320));
+               break;
+       case ov361x_160:
+               reg_w_array(gspca_dev, ov361x_bridge_start_160,
+                           ARRAY_SIZE(ov361x_bridge_start_160));
+               sccb_w_array(gspca_dev, ov361x_start_160,
+                            ARRAY_SIZE(ov361x_start_160));
+               break;
+       }
+       reg_w(gspca_dev, 0xe0, 0x00); /* start transfer */
+
+       return gspca_dev->usb_err;
+}
+
 static int sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -1237,6 +1560,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
                return gspca_dev->usb_err;
        if (sd->sensor == SENSOR_OV562x)
                return gspca_dev->usb_err;
+       if (sd->sensor == SENSOR_OV361x)
+               return sd_start_ov361x(gspca_dev);
 
        switch (gspca_dev->curr_mode) {
        case QVGA_MODE:                 /* 320x240 */
@@ -1290,6 +1615,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
 {
+       if (((struct sd *)gspca_dev)->sensor == SENSOR_OV361x) {
+               reg_w(gspca_dev, 0xe0, 0x01); /* stop transfer */
+               /* reg_w(gspca_dev, 0x31, 0x09); */
+               return;
+       }
        reg_w(gspca_dev, 0xe0, 0x01);
        set_led(gspca_dev, 0);
        reg_w(gspca_dev, 0xe0, 0x00);
@@ -1425,6 +1755,8 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
 
        if (sd->sensor == SENSOR_OV971x)
                return 0;
+       if (sd->sensor == SENSOR_OV361x)
+               return 0;
        gspca_dev->vdev.ctrl_handler = hdl;
        v4l2_ctrl_handler_init(hdl, 7);
        if (sd->sensor == SENSOR_OV562x) {
index 83519be..cd79c18 100644 (file)
@@ -299,7 +299,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
        pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[3], 8);
 
        /* Compression Balance */
-       if (gspca_dev->width == 176)
+       if (gspca_dev->pixfmt.width == 176)
                pac207_write_reg(gspca_dev, 0x4a, 0xff);
        else
                pac207_write_reg(gspca_dev, 0x4a, 0x30);
@@ -317,7 +317,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                mode = 0x00;
        else
                mode = 0x02;
-       if (gspca_dev->width == 176) {  /* 176x144 */
+       if (gspca_dev->pixfmt.width == 176) {   /* 176x144 */
                mode |= 0x01;
                PDEBUG(D_STREAM, "pac207_start mode 176x144");
        } else {                                /* 352x288 */
index 1a5bdc8..25f86b1 100644 (file)
@@ -326,7 +326,7 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 val)
         *  640x480 mode and page 4 reg 2 <= 3 then it must be 9
         */
        reg_w(gspca_dev, 0xff, 0x01);
-       if (gspca_dev->width != 640 && val <= 3)
+       if (gspca_dev->pixfmt.width != 640 && val <= 3)
                reg_w(gspca_dev, 0x08, 0x09);
        else
                reg_w(gspca_dev, 0x08, 0x08);
@@ -337,7 +337,7 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 val)
         * camera to use higher compression or we may run out of
         * bandwidth.
         */
-       if (gspca_dev->width == 640 && val == 2)
+       if (gspca_dev->pixfmt.width == 640 && val == 2)
                reg_w(gspca_dev, 0x80, 0x01);
        else
                reg_w(gspca_dev, 0x80, 0x1c);
@@ -615,7 +615,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 
                /* Start the new frame with the jpeg header */
                pac_start_frame(gspca_dev,
-                       gspca_dev->height, gspca_dev->width);
+                       gspca_dev->pixfmt.height, gspca_dev->pixfmt.width);
        }
        gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
index 5f729b8..5102cea 100644 (file)
@@ -354,9 +354,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        /* set size + mode */
        se401_write_req(gspca_dev, SE401_REQ_SET_WIDTH,
-                       gspca_dev->width * mult, 0);
+                       gspca_dev->pixfmt.width * mult, 0);
        se401_write_req(gspca_dev, SE401_REQ_SET_HEIGHT,
-                       gspca_dev->height * mult, 0);
+                       gspca_dev->pixfmt.height * mult, 0);
        /*
         * HDG: disabled this as it does not seem to do anything
         * se401_write_req(gspca_dev, SE401_REQ_SET_OUTPUT_MODE,
@@ -480,7 +480,7 @@ static void sd_complete_frame(struct gspca_dev *gspca_dev, u8 *data, int len)
 static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len)
 {
        struct sd *sd = (struct sd *)gspca_dev;
-       int imagesize = gspca_dev->width * gspca_dev->height;
+       int imagesize = gspca_dev->pixfmt.width * gspca_dev->pixfmt.height;
        int i, plen, bits, pixels, info, count;
 
        if (sd->restart_stream)
index f4453d5..2a38621 100644 (file)
@@ -1955,7 +1955,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
                        return 0;
                }
 
-               switch (gspca_dev->width) {
+               switch (gspca_dev->pixfmt.width) {
                case 160: /* 160x120 */
                        gspca_dev->alt = 2;
                        break;
@@ -1985,8 +1985,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
-       int width = gspca_dev->width;
-       int height = gspca_dev->height;
+       int width = gspca_dev->pixfmt.width;
+       int height = gspca_dev->pixfmt.height;
        u8 fmt, scale = 0;
 
        jpeg_define(sd->jpeg_hdr, height, width,
index d7ff3b9..7277dbd 100644 (file)
@@ -513,10 +513,7 @@ static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buf)
                if (gspca_dev->usb_buf[0] & 0x04) {
                        if (gspca_dev->usb_buf[0] & 0x08) {
                                dev_err(gspca_dev->v4l2_dev.dev,
-                                       "i2c error writing %02x %02x %02x %02x"
-                                       " %02x %02x %02x %02x\n",
-                                       buf[0], buf[1], buf[2], buf[3],
-                                       buf[4], buf[5], buf[6], buf[7]);
+                                       "i2c error writing %8ph\n", buf);
                                gspca_dev->usb_err = -EIO;
                        }
                        return;
@@ -753,7 +750,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
                /* In 640x480, if the reg11 has less than 4, the image is
                   unstable (the bridge goes into a higher compression mode
                   which we have not reverse engineered yet). */
-               if (gspca_dev->width == 640 && reg11 < 4)
+               if (gspca_dev->pixfmt.width == 640 && reg11 < 4)
                        reg11 = 4;
 
                /* frame exposure time in ms = 1000 * reg11 / 30    ->
index 3b5ccb1..c69b45d 100644 (file)
@@ -2204,7 +2204,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
                                { 0x14, 0xe7, 0x1e, 0xdd };
 
        /* create the JPEG header */
-       jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+       jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width,
                        0x21);          /* JPEG 422 */
 
        /* initialize the bridge */
index 688592b..f38fd89 100644 (file)
@@ -255,7 +255,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
 
        /* initialize the JPEG header */
-       jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+       jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width,
                        0x22);          /* JPEG 411 */
 
        /* the JPEG quality shall be 85% */
index 9f8bf51..f011a30 100644 (file)
@@ -608,7 +608,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        __u8 xmult, ymult;
 
        /* create the JPEG header */
-       jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+       jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width,
                        0x22);          /* JPEG 411 */
        jpeg_set_qual(sd->jpeg_hdr, QUALITY);
 
index acb19fb..aa21edc 100644 (file)
@@ -272,7 +272,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        dev->cap_mode = gspca_dev->cam.cam_mode;
        /* "Open the shutter" and set size, to start capture */
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 640:
                PDEBUG(D_STREAM, "Start streaming at high resolution");
                dev->cap_mode++;
index b10d082..e274cf1 100644 (file)
@@ -906,7 +906,8 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
 
        gspca_dev->cam.bulk_nurbs = 1;  /* there must be one URB only */
        sd->do_ctrl = 0;
-       gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8;
+       gspca_dev->cam.bulk_size = gspca_dev->pixfmt.width *
+                       gspca_dev->pixfmt.height + 8;
        return 0;
 }
 
index 8c09826..b0c70fe 100644 (file)
@@ -250,7 +250,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        int ret, value;
 
        /* create the JPEG header */
-       jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+       jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width,
                        0x22);          /* JPEG 411 */
        jpeg_set_qual(sd->jpeg_hdr, QUALITY);
 
@@ -261,7 +262,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
        set_par(gspca_dev, 0x00000000);
        set_par(gspca_dev, 0x8002e001);
        set_par(gspca_dev, 0x14000000);
-       if (gspca_dev->width > 320)
+       if (gspca_dev->pixfmt.width > 320)
                value = 0x8002e001;             /* 640x480 */
        else
                value = 0x4001f000;             /* 320x240 */
index 5858688..1fc80af 100644 (file)
@@ -48,42 +48,11 @@ struct sd {
 };
 
 static const struct v4l2_pix_format stk1135_modes[] = {
-       {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
-               .bytesperline = 160,
-               .sizeimage = 160 * 120,
-               .colorspace = V4L2_COLORSPACE_SRGB},
-       {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
-               .bytesperline = 176,
-               .sizeimage = 176 * 144,
-               .colorspace = V4L2_COLORSPACE_SRGB},
-       {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
-               .bytesperline = 320,
-               .sizeimage = 320 * 240,
-               .colorspace = V4L2_COLORSPACE_SRGB},
-       {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
-               .bytesperline = 352,
-               .sizeimage = 352 * 288,
-               .colorspace = V4L2_COLORSPACE_SRGB},
+       /* default mode (this driver supports variable resolution) */
        {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
                .bytesperline = 640,
                .sizeimage = 640 * 480,
                .colorspace = V4L2_COLORSPACE_SRGB},
-       {720, 576, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
-               .bytesperline = 720,
-               .sizeimage = 720 * 576,
-               .colorspace = V4L2_COLORSPACE_SRGB},
-       {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
-               .bytesperline = 800,
-               .sizeimage = 800 * 600,
-               .colorspace = V4L2_COLORSPACE_SRGB},
-       {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
-               .bytesperline = 1024,
-               .sizeimage = 1024 * 768,
-               .colorspace = V4L2_COLORSPACE_SRGB},
-       {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
-               .bytesperline = 1280,
-               .sizeimage = 1280 * 1024,
-               .colorspace = V4L2_COLORSPACE_SRGB},
 };
 
 /* -- read a register -- */
@@ -347,16 +316,16 @@ static void stk1135_configure_mt9m112(struct gspca_dev *gspca_dev)
                sensor_write(gspca_dev, cfg[i].reg, cfg[i].val);
 
        /* set output size */
-       width = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].width;
-       height = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].height;
-       if (width <= 640) { /* use context A (half readout speed by default) */
+       width = gspca_dev->pixfmt.width;
+       height = gspca_dev->pixfmt.height;
+       if (width <= 640 && height <= 512) { /* context A (half readout speed)*/
                sensor_write(gspca_dev, 0x1a7, width);
                sensor_write(gspca_dev, 0x1aa, height);
                /* set read mode context A */
                sensor_write(gspca_dev, 0x0c8, 0x0000);
                /* set resize, read mode, vblank, hblank context A */
                sensor_write(gspca_dev, 0x2c8, 0x0000);
-       } else { /* use context B (full readout speed by default) */
+       } else { /* context B (full readout speed) */
                sensor_write(gspca_dev, 0x1a1, width);
                sensor_write(gspca_dev, 0x1a4, height);
                /* set read mode context B */
@@ -484,8 +453,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, STK1135_REG_CISPO + 3, 0x00);
 
        /* set capture end position */
-       width = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].width;
-       height = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].height;
+       width = gspca_dev->pixfmt.width;
+       height = gspca_dev->pixfmt.height;
        reg_w(gspca_dev, STK1135_REG_CIEPO + 0, width & 0xff);
        reg_w(gspca_dev, STK1135_REG_CIEPO + 1, width >> 8);
        reg_w(gspca_dev, STK1135_REG_CIEPO + 2, height & 0xff);
@@ -643,6 +612,35 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
        return 0;
 }
 
+static void stk1135_try_fmt(struct gspca_dev *gspca_dev, struct v4l2_format *fmt)
+{
+       fmt->fmt.pix.width = clamp(fmt->fmt.pix.width, 32U, 1280U);
+       fmt->fmt.pix.height = clamp(fmt->fmt.pix.height, 32U, 1024U);
+       /* round up to even numbers */
+       fmt->fmt.pix.width += (fmt->fmt.pix.width & 1);
+       fmt->fmt.pix.height += (fmt->fmt.pix.height & 1);
+
+       fmt->fmt.pix.bytesperline = fmt->fmt.pix.width;
+       fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height;
+}
+
+static int stk1135_enum_framesizes(struct gspca_dev *gspca_dev,
+                       struct v4l2_frmsizeenum *fsize)
+{
+       if (fsize->index != 0 || fsize->pixel_format != V4L2_PIX_FMT_SBGGR8)
+               return -EINVAL;
+
+       fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+       fsize->stepwise.min_width = 32;
+       fsize->stepwise.min_height = 32;
+       fsize->stepwise.max_width = 1280;
+       fsize->stepwise.max_height = 1024;
+       fsize->stepwise.step_width = 2;
+       fsize->stepwise.step_height = 2;
+
+       return 0;
+}
+
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
        .name = MODULE_NAME,
@@ -653,6 +651,8 @@ static const struct sd_desc sd_desc = {
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
        .dq_callback = stk1135_dq_callback,
+       .try_fmt = stk1135_try_fmt,
+       .enum_framesizes = stk1135_enum_framesizes,
 };
 
 /* -- module initialisation -- */
index 55ee7a6..49d209b 100644 (file)
@@ -452,7 +452,7 @@ frame_data:
                                        NULL, 0);
 
                        if (sd->bridge == BRIDGE_ST6422)
-                               sd->to_skip = gspca_dev->width * 4;
+                               sd->to_skip = gspca_dev->pixfmt.width * 4;
 
                        if (chunk_len)
                                PERR("Chunk length is "
index 8206b77..8d785ed 100644 (file)
@@ -421,7 +421,7 @@ static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val)
 
        /* Number of pixels counted by the sensor when subsampling the pixels.
         * Slightly larger than the real value to avoid oscillation */
-       totalpixels = gspca_dev->width * gspca_dev->height;
+       totalpixels = gspca_dev->pixfmt.width * gspca_dev->pixfmt.height;
        totalpixels = totalpixels/(8*8) + totalpixels/(64*64);
 
        brightpixels = (totalpixels * val) >> 8;
index af8767a..a517d18 100644 (file)
@@ -715,7 +715,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        int enable;
 
        /* create the JPEG header */
-       jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+       jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width,
                        0x22);          /* JPEG 411 */
        jpeg_set_qual(sd->jpeg_hdr, QUALITY);
 
index 4cb511c..640c2fe 100644 (file)
@@ -3856,7 +3856,7 @@ static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
 
        if (sd->bridge == BRIDGE_TP6800) {
                val |= 0x08;            /* grid compensation enable */
-               if (gspca_dev->width == 640)
+               if (gspca_dev->pixfmt.width == 640)
                        reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
                else
                        val |= 0x04;            /* scaling down enable */
@@ -3880,7 +3880,7 @@ 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) {
+       if (gspca_dev->pixfmt.width == 320) {
                reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
                msleep(100);
                i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
@@ -3924,7 +3924,7 @@ static int get_fr_idx(struct gspca_dev *gspca_dev)
 
                /* 640x480 * 30 fps does not work */
                if (i == 6                      /* if 30 fps */
-                && gspca_dev->width == 640)
+                && gspca_dev->pixfmt.width == 640)
                        i = 0x05;               /* 15 fps */
        } else {
                for (i = 0; i < ARRAY_SIZE(rates_6810) - 1; i++) {
@@ -3935,7 +3935,7 @@ static int get_fr_idx(struct gspca_dev *gspca_dev)
 
                /* 640x480 * 30 fps does not work */
                if (i == 7                      /* if 30 fps */
-                && gspca_dev->width == 640)
+                && gspca_dev->pixfmt.width == 640)
                        i = 6;                  /* 15 fps */
                i |= 0x80;                      /* clock * 1 */
        }
@@ -4554,7 +4554,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width);
+       jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width);
        set_dqt(gspca_dev, sd->quality);
        if (sd->bridge == BRIDGE_TP6800) {
                if (sd->sensor == SENSOR_CX0342)
@@ -4737,7 +4738,7 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
                        (gspca_dev->usb_buf[26] << 8) + gspca_dev->usb_buf[25] +
                        (gspca_dev->usb_buf[29] << 8) + gspca_dev->usb_buf[28])
                                / 8;
-               if (gspca_dev->width == 640)
+               if (gspca_dev->pixfmt.width == 640)
                        luma /= 4;
                reg_w(gspca_dev, 0x7d, 0x00);
 
index 8591324..d497ba3 100644 (file)
@@ -268,7 +268,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        packet_type0 = packet_type1 = INTER_PACKET;
        if (gspca_dev->empty_packet) {
                gspca_dev->empty_packet = 0;
-               sd->packet = gspca_dev->height / 2;
+               sd->packet = gspca_dev->pixfmt.height / 2;
                packet_type0 = FIRST_PACKET;
        } else if (sd->packet == 0)
                return;                 /* 2 more lines in 352x288 ! */
@@ -284,9 +284,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
         * - 4 bytes
         */
        gspca_frame_add(gspca_dev, packet_type0,
-                       data + 2, gspca_dev->width);
+                       data + 2, gspca_dev->pixfmt.width);
        gspca_frame_add(gspca_dev, packet_type1,
-                       data + gspca_dev->width + 5, gspca_dev->width);
+                       data + gspca_dev->pixfmt.width + 5,
+                       gspca_dev->pixfmt.width);
 }
 
 static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
index a2275cf..103f6c4 100644 (file)
@@ -121,13 +121,13 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
 
        memset(req_data, 0, 16);
        req_data[0] = gain;
-       if (gspca_dev->width == 256)
+       if (gspca_dev->pixfmt.width == 256)
                req_data[1] |= 0x01; /* low nibble x-scale */
-       if (gspca_dev->height <= 122) {
+       if (gspca_dev->pixfmt.height <= 122) {
                req_data[1] |= 0x10; /* high nibble y-scale */
-               unscaled_height = gspca_dev->height * 2;
+               unscaled_height = gspca_dev->pixfmt.height * 2;
        } else
-               unscaled_height = gspca_dev->height;
+               unscaled_height = gspca_dev->pixfmt.height;
        req_data[2] = 0x90; /* unknown, does not seem to do anything */
        if (unscaled_height <= 200)
                req_data[3] = 0x06; /* vend? */
index 2165da0..fb9fe2e 100644 (file)
@@ -430,11 +430,11 @@ static void w9968cf_set_crop_window(struct sd *sd)
        #define SC(x) ((x) << 10)
 
        /* Scaling factors */
-       fw = SC(sd->gspca_dev.width) / max_width;
-       fh = SC(sd->gspca_dev.height) / max_height;
+       fw = SC(sd->gspca_dev.pixfmt.width) / max_width;
+       fh = SC(sd->gspca_dev.pixfmt.height) / max_height;
 
-       cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width) / fh;
-       ch = (fw >= fh) ? SC(sd->gspca_dev.height) / fw : max_height;
+       cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.pixfmt.width) / fh;
+       ch = (fw >= fh) ? SC(sd->gspca_dev.pixfmt.height) / fw : max_height;
 
        sd->sensor_width = max_width;
        sd->sensor_height = max_height;
@@ -454,34 +454,34 @@ static void w9968cf_mode_init_regs(struct sd *sd)
 
        w9968cf_set_crop_window(sd);
 
-       reg_w(sd, 0x14, sd->gspca_dev.width);
-       reg_w(sd, 0x15, sd->gspca_dev.height);
+       reg_w(sd, 0x14, sd->gspca_dev.pixfmt.width);
+       reg_w(sd, 0x15, sd->gspca_dev.pixfmt.height);
 
        /* JPEG width & height */
-       reg_w(sd, 0x30, sd->gspca_dev.width);
-       reg_w(sd, 0x31, sd->gspca_dev.height);
+       reg_w(sd, 0x30, sd->gspca_dev.pixfmt.width);
+       reg_w(sd, 0x31, sd->gspca_dev.pixfmt.height);
 
        /* Y & UV frame buffer strides (in WORD) */
        if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
            V4L2_PIX_FMT_JPEG) {
-               reg_w(sd, 0x2c, sd->gspca_dev.width / 2);
-               reg_w(sd, 0x2d, sd->gspca_dev.width / 4);
+               reg_w(sd, 0x2c, sd->gspca_dev.pixfmt.width / 2);
+               reg_w(sd, 0x2d, sd->gspca_dev.pixfmt.width / 4);
        } else
-               reg_w(sd, 0x2c, sd->gspca_dev.width);
+               reg_w(sd, 0x2c, sd->gspca_dev.pixfmt.width);
 
        reg_w(sd, 0x00, 0xbf17); /* reset everything */
        reg_w(sd, 0x00, 0xbf10); /* normal operation */
 
        /* Transfer size in WORDS (for UYVY format only) */
-       val = sd->gspca_dev.width * sd->gspca_dev.height;
+       val = sd->gspca_dev.pixfmt.width * sd->gspca_dev.pixfmt.height;
        reg_w(sd, 0x3d, val & 0xffff); /* low bits */
        reg_w(sd, 0x3e, val >> 16);    /* high bits */
 
        if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
            V4L2_PIX_FMT_JPEG) {
                /* We may get called multiple times (usb isoc bw negotiat.) */
-               jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height,
-                           sd->gspca_dev.width, 0x22); /* JPEG 420 */
+               jpeg_define(sd->jpeg_hdr, sd->gspca_dev.pixfmt.height,
+                           sd->gspca_dev.pixfmt.width, 0x22); /* JPEG 420 */
                jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
                w9968cf_upload_quantizationtables(sd);
                v4l2_ctrl_grab(sd->jpegqual, true);
index 7eaf64e..a41aa78 100644 (file)
@@ -1471,14 +1471,14 @@ static int cit_get_clock_div(struct gspca_dev *gspca_dev)
 
        while (clock_div > 3 &&
                        1000 * packet_size >
-                       gspca_dev->width * gspca_dev->height *
+                       gspca_dev->pixfmt.width * gspca_dev->pixfmt.height *
                        fps[clock_div - 1] * 3 / 2)
                clock_div--;
 
        PDEBUG(D_PROBE,
               "PacketSize: %d, res: %dx%d -> using clockdiv: %d (%d fps)",
-              packet_size, gspca_dev->width, gspca_dev->height, clock_div,
-              fps[clock_div]);
+              packet_size, gspca_dev->pixfmt.width, gspca_dev->pixfmt.height,
+              clock_div, fps[clock_div]);
 
        return clock_div;
 }
@@ -1502,7 +1502,7 @@ static int cit_start_model0(struct gspca_dev *gspca_dev)
        cit_write_reg(gspca_dev, 0x0002, 0x0426);
        cit_write_reg(gspca_dev, 0x0014, 0x0427);
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 160: /* 160x120 */
                cit_write_reg(gspca_dev, 0x0004, 0x010b);
                cit_write_reg(gspca_dev, 0x0001, 0x010a);
@@ -1643,7 +1643,7 @@ static int cit_start_model1(struct gspca_dev *gspca_dev)
        cit_write_reg(gspca_dev, 0x00, 0x0101);
        cit_write_reg(gspca_dev, 0x00, 0x010a);
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 128: /* 128x96 */
                cit_write_reg(gspca_dev, 0x80, 0x0103);
                cit_write_reg(gspca_dev, 0x60, 0x0105);
@@ -1700,7 +1700,7 @@ static int cit_start_model1(struct gspca_dev *gspca_dev)
        }
 
        /* Assorted init */
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 128: /* 128x96 */
                cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
                cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */
@@ -1753,7 +1753,7 @@ static int cit_start_model2(struct gspca_dev *gspca_dev)
        cit_write_reg(gspca_dev, 0x0000, 0x0108);
        cit_write_reg(gspca_dev, 0x0001, 0x0133);
        cit_write_reg(gspca_dev, 0x0001, 0x0102);
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 176: /* 176x144 */
                cit_write_reg(gspca_dev, 0x002c, 0x0103);       /* All except 320x240 */
                cit_write_reg(gspca_dev, 0x0000, 0x0104);       /* Same */
@@ -1792,7 +1792,7 @@ static int cit_start_model2(struct gspca_dev *gspca_dev)
 
        cit_write_reg(gspca_dev, 0x0000, 0x0100);       /* LED on */
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 176: /* 176x144 */
                cit_write_reg(gspca_dev, 0x0050, 0x0111);
                cit_write_reg(gspca_dev, 0x00d0, 0x0111);
@@ -1840,7 +1840,7 @@ static int cit_start_model2(struct gspca_dev *gspca_dev)
         * Magic control of CMOS sensor. Only lower values like
         * 0-3 work, and picture shifts left or right. Don't change.
         */
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 176: /* 176x144 */
                cit_model2_Packet1(gspca_dev, 0x0014, 0x0002);
                cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
@@ -1899,7 +1899,7 @@ static int cit_start_model2(struct gspca_dev *gspca_dev)
         * does not allow arbitrary values and apparently is a bit mask, to
         * be activated only at appropriate time. Don't change it randomly!
         */
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 176: /* 176x144 */
                cit_model2_Packet1(gspca_dev, 0x0026, 0x00c2);
                break;
@@ -2023,7 +2023,7 @@ static int cit_start_model3(struct gspca_dev *gspca_dev)
        cit_model3_Packet1(gspca_dev, 0x009e, 0x0096);
        cit_model3_Packet1(gspca_dev, 0x009f, 0x000a);
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 160:
                cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
                cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
@@ -2134,7 +2134,7 @@ static int cit_start_model3(struct gspca_dev *gspca_dev)
           like with the IBM netcam pro). */
        cit_write_reg(gspca_dev, clock_div, 0x0111); /* Clock Divider */
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 160:
                cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
                cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
@@ -2211,7 +2211,7 @@ static int cit_start_model4(struct gspca_dev *gspca_dev)
        cit_write_reg(gspca_dev, 0xfffa, 0x0124);
        cit_model4_Packet1(gspca_dev, 0x0034, 0x0000);
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 128: /* 128x96 */
                cit_write_reg(gspca_dev, 0x0070, 0x0119);
                cit_write_reg(gspca_dev, 0x00d0, 0x0111);
@@ -2531,7 +2531,7 @@ static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev)
        cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
        cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 160: /* 160x120 */
                cit_write_reg(gspca_dev, 0x0024, 0x010b);
                cit_write_reg(gspca_dev, 0x0089, 0x0119);
@@ -2635,7 +2635,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
        struct usb_host_interface *alt;
        int max_packet_size;
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 160:
                max_packet_size = 450;
                break;
@@ -2659,7 +2659,7 @@ static int sd_isoc_nego(struct gspca_dev *gspca_dev)
        int ret, packet_size, min_packet_size;
        struct usb_host_interface *alt;
 
-       switch (gspca_dev->width) {
+       switch (gspca_dev->pixfmt.width) {
        case 160:
                min_packet_size = 200;
                break;
@@ -2780,7 +2780,7 @@ static u8 *cit_find_sof(struct gspca_dev *gspca_dev, u8 *data, int len)
        case CIT_MODEL1:
        case CIT_MODEL3:
        case CIT_IBM_NETCAM_PRO:
-               switch (gspca_dev->width) {
+               switch (gspca_dev->pixfmt.width) {
                case 160: /* 160x120 */
                        byte3 = 0x02;
                        byte4 = 0x0a;
@@ -2864,20 +2864,16 @@ static u8 *cit_find_sof(struct gspca_dev *gspca_dev, u8 *data, int len)
                                if (data[i] == 0xff) {
                                        if (i >= 4)
                                                PDEBUG(D_FRAM,
-                                                      "header found at offset: %d: %02x %02x 00 %02x %02x %02x\n",
+                                                      "header found at offset: %d: %02x %02x 00 %3ph\n",
                                                       i - 1,
                                                       data[i - 4],
                                                       data[i - 3],
-                                                      data[i],
-                                                      data[i + 1],
-                                                      data[i + 2]);
+                                                      &data[i]);
                                        else
                                                PDEBUG(D_FRAM,
-                                                      "header found at offset: %d: 00 %02x %02x %02x\n",
+                                                      "header found at offset: %d: 00 %3ph\n",
                                                       i - 1,
-                                                      data[i],
-                                                      data[i + 1],
-                                                      data[i + 2]);
+                                                      &data[i]);
                                        return data + i + (sd->sof_len - 1);
                                }
                                break;
index cbfc2f9..7b95d8e 100644 (file)
@@ -6700,7 +6700,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        };
 
        /* create the JPEG header */
-       jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+       jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width,
                        0x21);          /* JPEG 422 */
 
        mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
index 6e50707..2f0c89c 100644 (file)
@@ -78,7 +78,8 @@ void hdpvr_delete(struct hdpvr_device *dev)
 
 static void challenge(u8 *bytes)
 {
-       u64 *i64P, tmp64;
+       __le64 *i64P;
+       u64 tmp64;
        uint i, idx;
 
        for (idx = 0; idx < 32; ++idx) {
@@ -106,10 +107,10 @@ static void challenge(u8 *bytes)
                        for (i = 0; i < 3; i++)
                                bytes[1] *= bytes[6] + 1;
                        for (i = 0; i < 3; i++) {
-                               i64P = (u64 *)bytes;
+                               i64P = (__le64 *)bytes;
                                tmp64 = le64_to_cpup(i64P);
-                               tmp64 <<= bytes[7] & 0x0f;
-                               *i64P += cpu_to_le64(tmp64);
+                               tmp64 = tmp64 + (tmp64 << (bytes[7] & 0x0f));
+                               *i64P = cpu_to_le64(tmp64);
                        }
                        break;
                }
@@ -301,8 +302,6 @@ static int hdpvr_probe(struct usb_interface *interface,
                goto error;
        }
 
-       dev->workqueue = 0;
-
        /* init video transfer queues first of all */
        /* to prevent oops in hdpvr_delete() on error paths */
        INIT_LIST_HEAD(&dev->free_buff_list);
index c4d51d7..ea05f67 100644 (file)
@@ -2868,7 +2868,7 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
                pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \
        }
 
-v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw)
+static v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw)
 {
        v4l2_std_id std;
        std = (v4l2_std_id)hdw->std_mask_avail;
index 03761c6..05bd91a 100644 (file)
@@ -209,8 +209,10 @@ static int smsusb_sendrequest(void *context, void *buffer, size_t size)
        struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) buffer;
        int dummy;
 
-       if (dev->state != SMSUSB_ACTIVE)
+       if (dev->state != SMSUSB_ACTIVE) {
+               sms_debug("Device not active yet");
                return -ENOENT;
+       }
 
        sms_debug("sending %s(%d) size: %d",
                  smscore_translate_msg(phdr->msg_type), phdr->msg_type,
@@ -243,6 +245,9 @@ static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id)
        int rc, dummy;
        char *fw_filename;
 
+       if (id < 0)
+               id = sms_get_board(board_id)->default_mode;
+
        if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) {
                sms_err("invalid firmware id specified %d", id);
                return -EINVAL;
@@ -445,14 +450,15 @@ static int smsusb_probe(struct usb_interface *intf,
        char devpath[32];
        int i, rc;
 
-       sms_info("interface number %d",
+       sms_info("board id=%lu, interface number %d",
+                id->driver_info,
                 intf->cur_altsetting->desc.bInterfaceNumber);
 
        if (sms_get_board(id->driver_info)->intf_num !=
            intf->cur_altsetting->desc.bInterfaceNumber) {
-               sms_err("interface number is %d expecting %d",
-                       sms_get_board(id->driver_info)->intf_num,
-                       intf->cur_altsetting->desc.bInterfaceNumber);
+               sms_debug("interface %d won't be used. Expecting interface %d to popup",
+                       intf->cur_altsetting->desc.bInterfaceNumber,
+                       sms_get_board(id->driver_info)->intf_num);
                return -ENODEV;
        }
 
@@ -483,22 +489,32 @@ static int smsusb_probe(struct usb_interface *intf,
        }
        if ((udev->actconfig->desc.bNumInterfaces == 2) &&
            (intf->cur_altsetting->desc.bInterfaceNumber == 0)) {
-               sms_err("rom interface 0 is not used");
+               sms_debug("rom interface 0 is not used");
                return -ENODEV;
        }
 
        if (id->driver_info == SMS1XXX_BOARD_SIANO_STELLAR_ROM) {
-               sms_info("stellar device was found.");
+               /* Detected a Siano Stellar uninitialized */
+
                snprintf(devpath, sizeof(devpath), "usb\\%d-%s",
                         udev->bus->busnum, udev->devpath);
-               sms_info("stellar device was found.");
-               return smsusb1_load_firmware(
+               sms_info("stellar device in cold state was found at %s.", devpath);
+               rc = smsusb1_load_firmware(
                                udev, smscore_registry_getmode(devpath),
                                id->driver_info);
+
+               /* This device will reset and gain another USB ID */
+               if (!rc)
+                       sms_info("stellar device now in warm state");
+               else
+                       sms_err("Failed to put stellar in warm state. Error: %d", rc);
+
+               return rc;
+       } else {
+               rc = smsusb_init_device(intf, id->driver_info);
        }
 
-       rc = smsusb_init_device(intf, id->driver_info);
-       sms_info("rc %d", rc);
+       sms_info("Device initialized with return code %d", rc);
        sms_board_load_modules(id->driver_info);
        return rc;
 }
@@ -550,10 +566,13 @@ static int smsusb_resume(struct usb_interface *intf)
 }
 
 static const struct usb_device_id smsusb_id_table[] = {
+       /* This device is only present before firmware load */
        { USB_DEVICE(0x187f, 0x0010),
-               .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
+               .driver_info = SMS1XXX_BOARD_SIANO_STELLAR_ROM },
+       /* This device pops up after firmware load */
        { USB_DEVICE(0x187f, 0x0100),
                .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
+
        { USB_DEVICE(0x187f, 0x0200),
                .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A },
        { USB_DEVICE(0x187f, 0x0201),
index 95f94e5..3316caa 100644 (file)
@@ -232,7 +232,7 @@ static int firmware_download(struct usb_device *udev)
                goto out;
        }
 
-       max_packet_size = udev->ep_out[0x1]->desc.wMaxPacketSize;
+       max_packet_size = le16_to_cpu(udev->ep_out[0x1]->desc.wMaxPacketSize);
        log("\t\t download size : %d", (int)max_packet_size);
 
        for (offset = 0; offset < fwlength; offset += max_packet_size) {
index e52c3b9..29724af 100644 (file)
@@ -366,7 +366,7 @@ static int ttusb_dec_get_stb_state (struct ttusb_dec *dec, unsigned int *mode,
                }
                return 0;
        } else {
-               return -1;
+               return -ENOENT;
        }
 }
 
@@ -1241,6 +1241,8 @@ static void ttusb_dec_init_v_pes(struct ttusb_dec *dec)
 
 static int ttusb_dec_init_usb(struct ttusb_dec *dec)
 {
+       int result;
+
        dprintk("%s\n", __func__);
 
        mutex_init(&dec->usb_mutex);
@@ -1258,7 +1260,7 @@ static int ttusb_dec_init_usb(struct ttusb_dec *dec)
                        return -ENOMEM;
                }
                dec->irq_buffer = usb_alloc_coherent(dec->udev,IRQ_PACKET_SIZE,
-                                       GFP_ATOMIC, &dec->irq_dma_handle);
+                                       GFP_KERNEL, &dec->irq_dma_handle);
                if(!dec->irq_buffer) {
                        usb_free_urb(dec->irq_urb);
                        return -ENOMEM;
@@ -1270,7 +1272,13 @@ static int ttusb_dec_init_usb(struct ttusb_dec *dec)
                dec->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        }
 
-       return ttusb_dec_alloc_iso_urbs(dec);
+       result = ttusb_dec_alloc_iso_urbs(dec);
+       if (result) {
+               usb_free_urb(dec->irq_urb);
+               usb_free_coherent(dec->udev, IRQ_PACKET_SIZE,
+                                 dec->irq_buffer, dec->irq_dma_handle);
+       }
+       return result;
 }
 
 static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
@@ -1293,10 +1301,11 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
 
        dprintk("%s\n", __func__);
 
-       if (request_firmware(&fw_entry, dec->firmware_name, &dec->udev->dev)) {
+       result = request_firmware(&fw_entry, dec->firmware_name, &dec->udev->dev);
+       if (result) {
                printk(KERN_ERR "%s: Firmware (%s) unavailable.\n",
                       __func__, dec->firmware_name);
-               return 1;
+               return result;
        }
 
        firmware = fw_entry->data;
@@ -1306,7 +1315,7 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
                printk("%s: firmware size too small for DSP code (%zu < 60).\n",
                        __func__, firmware_size);
                release_firmware(fw_entry);
-               return -1;
+               return -ENOENT;
        }
 
        /* a 32 bit checksum over the first 56 bytes of the DSP Code is stored
@@ -1320,7 +1329,7 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
                       "0x%08x != 0x%08x in file), file invalid.\n",
                        __func__, crc32_csum, crc32_check);
                release_firmware(fw_entry);
-               return -1;
+               return -ENOENT;
        }
        memcpy(idstring, &firmware[36], 20);
        idstring[20] = '\0';
@@ -1389,55 +1398,48 @@ static int ttusb_dec_init_stb(struct ttusb_dec *dec)
        dprintk("%s\n", __func__);
 
        result = ttusb_dec_get_stb_state(dec, &mode, &model, &version);
+       if (result)
+               return result;
 
-       if (!result) {
-               if (!mode) {
-                       if (version == 0xABCDEFAB)
-                               printk(KERN_INFO "ttusb_dec: no version "
-                                      "info in Firmware\n");
-                       else
-                               printk(KERN_INFO "ttusb_dec: Firmware "
-                                      "%x.%02x%c%c\n",
-                                      version >> 24, (version >> 16) & 0xff,
-                                      (version >> 8) & 0xff, version & 0xff);
-
-                       result = ttusb_dec_boot_dsp(dec);
-                       if (result)
-                               return result;
-                       else
-                               return 1;
-               } else {
-                       /* We can't trust the USB IDs that some firmwares
-                          give the box */
-                       switch (model) {
-                       case 0x00070001:
-                       case 0x00070008:
-                       case 0x0007000c:
-                               ttusb_dec_set_model(dec, TTUSB_DEC3000S);
-                               break;
-                       case 0x00070009:
-                       case 0x00070013:
-                               ttusb_dec_set_model(dec, TTUSB_DEC2000T);
-                               break;
-                       case 0x00070011:
-                               ttusb_dec_set_model(dec, TTUSB_DEC2540T);
-                               break;
-                       default:
-                               printk(KERN_ERR "%s: unknown model returned "
-                                      "by firmware (%08x) - please report\n",
-                                      __func__, model);
-                               return -1;
-                               break;
-                       }
+       if (!mode) {
+               if (version == 0xABCDEFAB)
+                       printk(KERN_INFO "ttusb_dec: no version "
+                              "info in Firmware\n");
+               else
+                       printk(KERN_INFO "ttusb_dec: Firmware "
+                              "%x.%02x%c%c\n",
+                              version >> 24, (version >> 16) & 0xff,
+                              (version >> 8) & 0xff, version & 0xff);
 
+               result = ttusb_dec_boot_dsp(dec);
+               if (result)
+                       return result;
+       } else {
+               /* We can't trust the USB IDs that some firmwares
+                  give the box */
+               switch (model) {
+               case 0x00070001:
+               case 0x00070008:
+               case 0x0007000c:
+                       ttusb_dec_set_model(dec, TTUSB_DEC3000S);
+                       break;
+               case 0x00070009:
+               case 0x00070013:
+                       ttusb_dec_set_model(dec, TTUSB_DEC2000T);
+                       break;
+               case 0x00070011:
+                       ttusb_dec_set_model(dec, TTUSB_DEC2540T);
+                       break;
+               default:
+                       printk(KERN_ERR "%s: unknown model returned "
+                              "by firmware (%08x) - please report\n",
+                              __func__, model);
+                       return -ENOENT;
+               }
                        if (version >= 0x01770000)
                                dec->can_playback = 1;
-
-                       return 0;
-               }
        }
-       else
-               return result;
+       return 0;
 }
 
 static int ttusb_dec_init_dvb(struct ttusb_dec *dec)
@@ -1539,19 +1541,7 @@ static void ttusb_dec_exit_dvb(struct ttusb_dec *dec)
 
 static void ttusb_dec_exit_rc(struct ttusb_dec *dec)
 {
-
        dprintk("%s\n", __func__);
-       /* we have to check whether the irq URB is already submitted.
-         * As the irq is submitted after the interface is changed,
-         * this is the best method i figured out.
-         * Any others?*/
-       if (dec->interface == TTUSB_DEC_INTERFACE_IN)
-               usb_kill_urb(dec->irq_urb);
-
-       usb_free_urb(dec->irq_urb);
-
-       usb_free_coherent(dec->udev,IRQ_PACKET_SIZE,
-                         dec->irq_buffer, dec->irq_dma_handle);
 
        if (dec->rc_input_dev) {
                input_unregister_device(dec->rc_input_dev);
@@ -1566,6 +1556,20 @@ static void ttusb_dec_exit_usb(struct ttusb_dec *dec)
 
        dprintk("%s\n", __func__);
 
+       if (enable_rc) {
+               /* we have to check whether the irq URB is already submitted.
+                * As the irq is submitted after the interface is changed,
+                * this is the best method i figured out.
+                * Any others?*/
+               if (dec->interface == TTUSB_DEC_INTERFACE_IN)
+                       usb_kill_urb(dec->irq_urb);
+
+               usb_free_urb(dec->irq_urb);
+
+               usb_free_coherent(dec->udev, IRQ_PACKET_SIZE,
+                                 dec->irq_buffer, dec->irq_dma_handle);
+       }
+
        dec->iso_stream_count = 0;
 
        for (i = 0; i < ISO_BUF_COUNT; i++)
@@ -1623,6 +1627,7 @@ static int ttusb_dec_probe(struct usb_interface *intf,
 {
        struct usb_device *udev;
        struct ttusb_dec *dec;
+       int result;
 
        dprintk("%s\n", __func__);
 
@@ -1651,13 +1656,15 @@ static int ttusb_dec_probe(struct usb_interface *intf,
 
        dec->udev = udev;
 
-       if (ttusb_dec_init_usb(dec))
-               return 0;
-       if (ttusb_dec_init_stb(dec)) {
-               ttusb_dec_exit_usb(dec);
-               return 0;
-       }
-       ttusb_dec_init_dvb(dec);
+       result = ttusb_dec_init_usb(dec);
+       if (result)
+               goto err_usb;
+       result = ttusb_dec_init_stb(dec);
+       if (result)
+               goto err_stb;
+       result = ttusb_dec_init_dvb(dec);
+       if (result)
+               goto err_stb;
 
        dec->adapter.priv = dec;
        switch (id->idProduct) {
@@ -1696,6 +1703,11 @@ static int ttusb_dec_probe(struct usb_interface *intf,
                ttusb_init_rc(dec);
 
        return 0;
+err_stb:
+       ttusb_dec_exit_usb(dec);
+err_usb:
+       kfree(dec);
+       return result;
 }
 
 static void ttusb_dec_disconnect(struct usb_interface *intf)
index a2f4501..0eb8210 100644 (file)
@@ -664,7 +664,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
                .size           = 32,
                .offset         = 0,
                .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
-               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
        },
        {
                .id             = V4L2_CID_TILT_ABSOLUTE,
@@ -674,7 +674,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
                .size           = 32,
                .offset         = 32,
                .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
-               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
        },
        {
                .id             = V4L2_CID_PRIVACY,
index 3394c34..899cb6d 100644 (file)
@@ -680,7 +680,8 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
                  stream->dev->name,
                  sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
                  y, ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC,
-                 v4l2_buf->timestamp.tv_sec, v4l2_buf->timestamp.tv_usec,
+                 v4l2_buf->timestamp.tv_sec,
+                 (unsigned long)v4l2_buf->timestamp.tv_usec,
                  x1, first->host_sof, first->dev_sof,
                  x2, last->host_sof, last->dev_sof, y1, y2);
 
index ddc9379..20c0922 100644 (file)
@@ -43,7 +43,7 @@
 
 #define UNSET (-1U)
 
-#define PREFIX (t->i2c->driver->driver.name)
+#define PREFIX (t->i2c->dev.driver->name)
 
 /*
  * Driver modprobe parameters
@@ -247,7 +247,7 @@ static const struct analog_demod_ops tuner_analog_ops = {
 /**
  * set_type - Sets the tuner type for a given device
  *
- * @c:                 i2c_client descriptoy
+ * @c:                 i2c_client descriptor
  * @type:              type of the tuner (e. g. tuner number)
  * @new_mode_mask:     Indicates if tuner supports TV and/or Radio
  * @new_config:                an optional parameter used by a few tuners to adjust
@@ -452,7 +452,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
        }
 
        tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
-                 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
+                 c->adapter->name, c->dev.driver->name, c->addr << 1, type,
                  t->mode_mask);
        return;
 
@@ -556,7 +556,7 @@ static void tuner_lookup(struct i2c_adapter *adap,
                int mode_mask;
 
                if (pos->i2c->adapter != adap ||
-                   strcmp(pos->i2c->driver->driver.name, "tuner"))
+                   strcmp(pos->i2c->dev.driver->name, "tuner"))
                        continue;
 
                mode_mask = pos->mode_mask;
index c85d69d..85a6a34 100644 (file)
@@ -189,30 +189,53 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
        struct v4l2_subdev *sd, *tmp;
        unsigned int notif_n_subdev = notifier->num_subdevs;
        unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
-       struct device *dev[n_subdev];
+       struct device **dev;
        int i = 0;
 
        if (!notifier->v4l2_dev)
                return;
 
+       dev = kmalloc(n_subdev * sizeof(*dev), GFP_KERNEL);
+       if (!dev) {
+               dev_err(notifier->v4l2_dev->dev,
+                       "Failed to allocate device cache!\n");
+       }
+
        mutex_lock(&list_lock);
 
        list_del(&notifier->list);
 
        list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) {
-               dev[i] = get_device(sd->dev);
+               struct device *d;
+
+               d = get_device(sd->dev);
 
                v4l2_async_cleanup(sd);
 
                /* If we handled USB devices, we'd have to lock the parent too */
-               device_release_driver(dev[i++]);
+               device_release_driver(d);
 
                if (notifier->unbind)
                        notifier->unbind(notifier, sd, sd->asd);
+
+               /*
+                * Store device at the device cache, in order to call
+                * put_device() on the final step
+                */
+               if (dev)
+                       dev[i++] = d;
+               else
+                       put_device(d);
        }
 
        mutex_unlock(&list_lock);
 
+       /*
+        * Call device_attach() to reprobe devices
+        *
+        * NOTE: If dev allocation fails, i is 0, and the whole loop won't be
+        * executed.
+        */
        while (i--) {
                struct device *d = dev[i];
 
@@ -228,6 +251,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
                }
                put_device(d);
        }
+       kfree(dev);
 
        notifier->v4l2_dev = NULL;
 
index b67de86..e18cc04 100644 (file)
@@ -240,3 +240,42 @@ void v4l2_clk_unregister(struct v4l2_clk *clk)
        kfree(clk);
 }
 EXPORT_SYMBOL(v4l2_clk_unregister);
+
+struct v4l2_clk_fixed {
+       unsigned long rate;
+       struct v4l2_clk_ops ops;
+};
+
+static unsigned long fixed_get_rate(struct v4l2_clk *clk)
+{
+       struct v4l2_clk_fixed *priv = clk->priv;
+       return priv->rate;
+}
+
+struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id,
+               const char *id, unsigned long rate, struct module *owner)
+{
+       struct v4l2_clk *clk;
+       struct v4l2_clk_fixed *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+
+       if (!priv)
+               return ERR_PTR(-ENOMEM);
+
+       priv->rate = rate;
+       priv->ops.get_rate = fixed_get_rate;
+       priv->ops.owner = owner;
+
+       clk = v4l2_clk_register(&priv->ops, dev_id, id, priv);
+       if (IS_ERR(clk))
+               kfree(priv);
+
+       return clk;
+}
+EXPORT_SYMBOL(__v4l2_clk_register_fixed);
+
+void v4l2_clk_unregister_fixed(struct v4l2_clk *clk)
+{
+       kfree(clk->priv);
+       v4l2_clk_unregister(clk);
+}
+EXPORT_SYMBOL(v4l2_clk_unregister_fixed);
index 037d7a5..433d6d7 100644 (file)
@@ -236,14 +236,14 @@ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
        v4l2_subdev_init(sd, ops);
        sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
        /* the owner is the same as the i2c_client's driver owner */
-       sd->owner = client->driver->driver.owner;
+       sd->owner = client->dev.driver->owner;
        sd->dev = &client->dev;
        /* i2c_client and v4l2_subdev point to one another */
        v4l2_set_subdevdata(sd, client);
        i2c_set_clientdata(client, sd);
        /* initialize name */
        snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
-               client->driver->driver.name, i2c_adapter_id(client->adapter),
+               client->dev.driver->name, i2c_adapter_id(client->adapter),
                client->addr);
 }
 EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);
@@ -274,11 +274,11 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
           loaded. This delay-load mechanism doesn't work if other drivers
           want to use the i2c device, so explicitly loading the module
           is the best alternative. */
-       if (client == NULL || client->driver == NULL)
+       if (client == NULL || client->dev.driver == NULL)
                goto error;
 
        /* Lock the module so we can safely get the v4l2_subdev pointer */
-       if (!try_module_get(client->driver->driver.owner))
+       if (!try_module_get(client->dev.driver->owner))
                goto error;
        sd = i2c_get_clientdata(client);
 
@@ -287,7 +287,7 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
        if (v4l2_device_register_subdev(v4l2_dev, sd))
                sd = NULL;
        /* Decrease the module use count to match the first try_module_get. */
-       module_put(client->driver->driver.owner);
+       module_put(client->dev.driver->owner);
 
 error:
        /* If we have a client but no subdev, then something went wrong and
index c3f0803..60dcc0f 100644 (file)
@@ -565,13 +565,13 @@ EXPORT_SYMBOL(v4l2_ctrl_get_menu);
  * Returns NULL or an s64 type array containing the menu for given
  * control ID. The total number of the menu items is returned in @len.
  */
-const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len)
+const s64 *v4l2_ctrl_get_int_menu(u32 id, u32 *len)
 {
-       static const s64 const qmenu_int_vpx_num_partitions[] = {
+       static const s64 qmenu_int_vpx_num_partitions[] = {
                1, 2, 4, 8,
        };
 
-       static const s64 const qmenu_int_vpx_num_ref_frames[] = {
+       static const s64 qmenu_int_vpx_num_ref_frames[] = {
                1, 2, 3,
        };
 
@@ -583,7 +583,7 @@ const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len)
        default:
                *len = 0;
                return NULL;
-       };
+       }
 }
 EXPORT_SYMBOL(v4l2_ctrl_get_int_menu);
 
index 7c43712..73035ee 100644 (file)
@@ -41,6 +41,8 @@ module_param(debug, bool, 0644);
 #define TRANS_QUEUED           (1 << 0)
 /* Instance is currently running in hardware */
 #define TRANS_RUNNING          (1 << 1)
+/* Instance is currently aborting */
+#define TRANS_ABORT            (1 << 2)
 
 
 /* Offset base for buffers on the destination queue - used to distinguish
@@ -221,6 +223,14 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
        }
 
        spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job);
+
+       /* If the context is aborted then don't schedule it */
+       if (m2m_ctx->job_flags & TRANS_ABORT) {
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+               dprintk("Aborted context\n");
+               return;
+       }
+
        if (m2m_ctx->job_flags & TRANS_QUEUED) {
                spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
                dprintk("On job queue already\n");
@@ -280,6 +290,8 @@ static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx)
 
        m2m_dev = m2m_ctx->m2m_dev;
        spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+
+       m2m_ctx->job_flags |= TRANS_ABORT;
        if (m2m_ctx->job_flags & TRANS_RUNNING) {
                spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
                m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
@@ -480,13 +492,15 @@ int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
        m2m_dev = m2m_ctx->m2m_dev;
        spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job);
        /* We should not be scheduled anymore, since we're dropping a queue. */
-       INIT_LIST_HEAD(&m2m_ctx->queue);
+       if (m2m_ctx->job_flags & TRANS_QUEUED)
+               list_del(&m2m_ctx->queue);
        m2m_ctx->job_flags = 0;
 
        spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
        /* Drop queue, since streamoff returns device to the same state as after
         * calling reqbufs. */
        INIT_LIST_HEAD(&q_ctx->rdy_queue);
+       q_ctx->num_rdy = 0;
        spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
 
        if (m2m_dev->curr_ctx == m2m_ctx) {
index de0e87f..b19b306 100644 (file)
@@ -241,7 +241,8 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
                q->bufs[q->num_buffers + buffer] = vb;
        }
 
-       __setup_offsets(q, buffer);
+       if (memory == V4L2_MEMORY_MMAP)
+               __setup_offsets(q, buffer);
 
        dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
                        buffer, num_planes);
@@ -1015,6 +1016,10 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 
                /* Check if the provided plane buffer is large enough */
                if (planes[plane].length < q->plane_sizes[plane]) {
+                       dprintk(1, "qbuf: provided buffer size %u is less than "
+                                               "setup size %u for plane %d\n",
+                                               planes[plane].length,
+                                               q->plane_sizes[plane], plane);
                        ret = -EINVAL;
                        goto err;
                }
@@ -1205,8 +1210,11 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
        int ret;
 
        ret = __verify_length(vb, b);
-       if (ret < 0)
+       if (ret < 0) {
+               dprintk(1, "%s(): plane parameters verification failed: %d\n",
+                       __func__, ret);
                return ret;
+       }
 
        switch (q->memory) {
        case V4L2_MEMORY_MMAP:
@@ -2469,10 +2477,11 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
 }
 EXPORT_SYMBOL_GPL(vb2_read);
 
-size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
+size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
                loff_t *ppos, int nonblocking)
 {
-       return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0);
+       return __vb2_perform_fileio(q, (char __user *) data, count,
+                                                       ppos, nonblocking, 0);
 }
 EXPORT_SYMBOL_GPL(vb2_write);
 
@@ -2633,7 +2642,7 @@ int vb2_fop_release(struct file *file)
 }
 EXPORT_SYMBOL_GPL(vb2_fop_release);
 
-ssize_t vb2_fop_write(struct file *file, char __user *buf,
+ssize_t vb2_fop_write(struct file *file, const char __user *buf,
                size_t count, loff_t *ppos)
 {
        struct video_device *vdev = video_devdata(file);
index 16ae3dc..2f86054 100644 (file)
@@ -35,17 +35,61 @@ struct vb2_dma_sg_buf {
        struct page                     **pages;
        int                             write;
        int                             offset;
-       struct vb2_dma_sg_desc          sg_desc;
+       struct sg_table                 sg_table;
+       size_t                          size;
+       unsigned int                    num_pages;
        atomic_t                        refcount;
        struct vb2_vmarea_handler       handler;
 };
 
 static void vb2_dma_sg_put(void *buf_priv);
 
+static int vb2_dma_sg_alloc_compacted(struct vb2_dma_sg_buf *buf,
+               gfp_t gfp_flags)
+{
+       unsigned int last_page = 0;
+       int size = buf->size;
+
+       while (size > 0) {
+               struct page *pages;
+               int order;
+               int i;
+
+               order = get_order(size);
+               /* Dont over allocate*/
+               if ((PAGE_SIZE << order) > size)
+                       order--;
+
+               pages = NULL;
+               while (!pages) {
+                       pages = alloc_pages(GFP_KERNEL | __GFP_ZERO |
+                                       __GFP_NOWARN | gfp_flags, order);
+                       if (pages)
+                               break;
+
+                       if (order == 0) {
+                               while (last_page--)
+                                       __free_page(buf->pages[last_page]);
+                               return -ENOMEM;
+                       }
+                       order--;
+               }
+
+               split_page(pages, order);
+               for (i = 0; i < (1 << order); i++)
+                       buf->pages[last_page++] = &pages[i];
+
+               size -= PAGE_SIZE << order;
+       }
+
+       return 0;
+}
+
 static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_flags)
 {
        struct vb2_dma_sg_buf *buf;
-       int i;
+       int ret;
+       int num_pages;
 
        buf = kzalloc(sizeof *buf, GFP_KERNEL);
        if (!buf)
@@ -54,29 +98,23 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_fla
        buf->vaddr = NULL;
        buf->write = 0;
        buf->offset = 0;
-       buf->sg_desc.size = size;
+       buf->size = size;
        /* size is already page aligned */
-       buf->sg_desc.num_pages = size >> PAGE_SHIFT;
+       buf->num_pages = size >> PAGE_SHIFT;
 
-       buf->sg_desc.sglist = vzalloc(buf->sg_desc.num_pages *
-                                     sizeof(*buf->sg_desc.sglist));
-       if (!buf->sg_desc.sglist)
-               goto fail_sglist_alloc;
-       sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
-
-       buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
+       buf->pages = kzalloc(buf->num_pages * sizeof(struct page *),
                             GFP_KERNEL);
        if (!buf->pages)
                goto fail_pages_array_alloc;
 
-       for (i = 0; i < buf->sg_desc.num_pages; ++i) {
-               buf->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO |
-                                          __GFP_NOWARN | gfp_flags);
-               if (NULL == buf->pages[i])
-                       goto fail_pages_alloc;
-               sg_set_page(&buf->sg_desc.sglist[i],
-                           buf->pages[i], PAGE_SIZE, 0);
-       }
+       ret = vb2_dma_sg_alloc_compacted(buf, gfp_flags);
+       if (ret)
+               goto fail_pages_alloc;
+
+       ret = sg_alloc_table_from_pages(&buf->sg_table, buf->pages,
+                       buf->num_pages, 0, size, gfp_flags);
+       if (ret)
+               goto fail_table_alloc;
 
        buf->handler.refcount = &buf->refcount;
        buf->handler.put = vb2_dma_sg_put;
@@ -85,18 +123,16 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_fla
        atomic_inc(&buf->refcount);
 
        dprintk(1, "%s: Allocated buffer of %d pages\n",
-               __func__, buf->sg_desc.num_pages);
+               __func__, buf->num_pages);
        return buf;
 
+fail_table_alloc:
+       num_pages = buf->num_pages;
+       while (num_pages--)
+               __free_page(buf->pages[num_pages]);
 fail_pages_alloc:
-       while (--i >= 0)
-               __free_page(buf->pages[i]);
        kfree(buf->pages);
-
 fail_pages_array_alloc:
-       vfree(buf->sg_desc.sglist);
-
-fail_sglist_alloc:
        kfree(buf);
        return NULL;
 }
@@ -104,14 +140,14 @@ fail_sglist_alloc:
 static void vb2_dma_sg_put(void *buf_priv)
 {
        struct vb2_dma_sg_buf *buf = buf_priv;
-       int i = buf->sg_desc.num_pages;
+       int i = buf->num_pages;
 
        if (atomic_dec_and_test(&buf->refcount)) {
                dprintk(1, "%s: Freeing buffer of %d pages\n", __func__,
-                       buf->sg_desc.num_pages);
+                       buf->num_pages);
                if (buf->vaddr)
-                       vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
-               vfree(buf->sg_desc.sglist);
+                       vm_unmap_ram(buf->vaddr, buf->num_pages);
+               sg_free_table(&buf->sg_table);
                while (--i >= 0)
                        __free_page(buf->pages[i]);
                kfree(buf->pages);
@@ -124,7 +160,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
 {
        struct vb2_dma_sg_buf *buf;
        unsigned long first, last;
-       int num_pages_from_user, i;
+       int num_pages_from_user;
 
        buf = kzalloc(sizeof *buf, GFP_KERNEL);
        if (!buf)
@@ -133,56 +169,41 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
        buf->vaddr = NULL;
        buf->write = write;
        buf->offset = vaddr & ~PAGE_MASK;
-       buf->sg_desc.size = size;
+       buf->size = size;
 
        first = (vaddr           & PAGE_MASK) >> PAGE_SHIFT;
        last  = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
-       buf->sg_desc.num_pages = last - first + 1;
-
-       buf->sg_desc.sglist = vzalloc(
-               buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist));
-       if (!buf->sg_desc.sglist)
-               goto userptr_fail_sglist_alloc;
-
-       sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
+       buf->num_pages = last - first + 1;
 
-       buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
+       buf->pages = kzalloc(buf->num_pages * sizeof(struct page *),
                             GFP_KERNEL);
        if (!buf->pages)
-               goto userptr_fail_pages_array_alloc;
+               return NULL;
 
        num_pages_from_user = get_user_pages(current, current->mm,
                                             vaddr & PAGE_MASK,
-                                            buf->sg_desc.num_pages,
+                                            buf->num_pages,
                                             write,
                                             1, /* force */
                                             buf->pages,
                                             NULL);
 
-       if (num_pages_from_user != buf->sg_desc.num_pages)
+       if (num_pages_from_user != buf->num_pages)
                goto userptr_fail_get_user_pages;
 
-       sg_set_page(&buf->sg_desc.sglist[0], buf->pages[0],
-                   PAGE_SIZE - buf->offset, buf->offset);
-       size -= PAGE_SIZE - buf->offset;
-       for (i = 1; i < buf->sg_desc.num_pages; ++i) {
-               sg_set_page(&buf->sg_desc.sglist[i], buf->pages[i],
-                           min_t(size_t, PAGE_SIZE, size), 0);
-               size -= min_t(size_t, PAGE_SIZE, size);
-       }
+       if (sg_alloc_table_from_pages(&buf->sg_table, buf->pages,
+                       buf->num_pages, buf->offset, size, 0))
+               goto userptr_fail_alloc_table_from_pages;
+
        return buf;
 
+userptr_fail_alloc_table_from_pages:
 userptr_fail_get_user_pages:
        dprintk(1, "get_user_pages requested/got: %d/%d]\n",
-              num_pages_from_user, buf->sg_desc.num_pages);
+              num_pages_from_user, buf->num_pages);
        while (--num_pages_from_user >= 0)
                put_page(buf->pages[num_pages_from_user]);
        kfree(buf->pages);
-
-userptr_fail_pages_array_alloc:
-       vfree(buf->sg_desc.sglist);
-
-userptr_fail_sglist_alloc:
        kfree(buf);
        return NULL;
 }
@@ -194,18 +215,18 @@ userptr_fail_sglist_alloc:
 static void vb2_dma_sg_put_userptr(void *buf_priv)
 {
        struct vb2_dma_sg_buf *buf = buf_priv;
-       int i = buf->sg_desc.num_pages;
+       int i = buf->num_pages;
 
        dprintk(1, "%s: Releasing userspace buffer of %d pages\n",
-              __func__, buf->sg_desc.num_pages);
+              __func__, buf->num_pages);
        if (buf->vaddr)
-               vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
+               vm_unmap_ram(buf->vaddr, buf->num_pages);
+       sg_free_table(&buf->sg_table);
        while (--i >= 0) {
                if (buf->write)
                        set_page_dirty_lock(buf->pages[i]);
                put_page(buf->pages[i]);
        }
-       vfree(buf->sg_desc.sglist);
        kfree(buf->pages);
        kfree(buf);
 }
@@ -218,7 +239,7 @@ static void *vb2_dma_sg_vaddr(void *buf_priv)
 
        if (!buf->vaddr)
                buf->vaddr = vm_map_ram(buf->pages,
-                                       buf->sg_desc.num_pages,
+                                       buf->num_pages,
                                        -1,
                                        PAGE_KERNEL);
 
@@ -274,7 +295,7 @@ static void *vb2_dma_sg_cookie(void *buf_priv)
 {
        struct vb2_dma_sg_buf *buf = buf_priv;
 
-       return &buf->sg_desc;
+       return &buf->sg_table;
 }
 
 const struct vb2_mem_ops vb2_dma_sg_memops = {
index bbf4aea..a0547db 100644 (file)
@@ -253,7 +253,7 @@ void memstick_new_req(struct memstick_host *host)
 {
        if (host->card) {
                host->retries = cmd_retries;
-               INIT_COMPLETION(host->card->mrq_complete);
+               reinit_completion(&host->card->mrq_complete);
                host->request(host);
        }
 }
index 9188ef5..24f2f84 100644 (file)
@@ -401,10 +401,10 @@ again:
                        sizeof(struct ms_status_register)))
                        return 0;
 
-               msb->state = MSB_RP_RECIVE_STATUS_REG;
+               msb->state = MSB_RP_RECEIVE_STATUS_REG;
                return 0;
 
-       case MSB_RP_RECIVE_STATUS_REG:
+       case MSB_RP_RECEIVE_STATUS_REG:
                msb->regs.status = *(struct ms_status_register *)mrq->data;
                msb->state = MSB_RP_SEND_OOB_READ;
                /* fallthrough */
index 96e6375..c75198d 100644 (file)
@@ -223,7 +223,7 @@ enum msb_readpage_states {
        MSB_RP_RECEIVE_INT_REQ_RESULT,
 
        MSB_RP_SEND_READ_STATUS_REG,
-       MSB_RP_RECIVE_STATUS_REG,
+       MSB_RP_RECEIVE_STATUS_REG,
 
        MSB_RP_SEND_OOB_READ,
        MSB_RP_RECEIVE_OOB_READ,
index 1b6e913..31727bf 100644 (file)
@@ -290,7 +290,7 @@ static int r592_transfer_fifo_dma(struct r592_device *dev)
        dbg_verbose("doing dma transfer");
 
        dev->dma_error = 0;
-       INIT_COMPLETION(dev->dma_done);
+       reinit_completion(&dev->dma_done);
 
        /* TODO: hidden assumption about nenth beeing always 1 */
        sg_count = dma_map_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ?
index 7ebe9ef..c9b1f64 100644 (file)
@@ -1247,7 +1247,7 @@ static struct i2c_driver pm860x_driver = {
                .name   = "88PM860x",
                .owner  = THIS_MODULE,
                .pm     = &pm860x_pm_ops,
-               .of_match_table = of_match_ptr(pm860x_dt_ids),
+               .of_match_table = pm860x_dt_ids,
        },
        .probe          = pm860x_probe,
        .remove         = pm860x_remove,
index 914c3d1..62a60ca 100644 (file)
@@ -27,6 +27,18 @@ config MFD_AS3711
        help
          Support for the AS3711 PMIC from AMS
 
+config MFD_AS3722
+       bool "ams AS3722 Power Management IC"
+       select MFD_CORE
+       select REGMAP_I2C
+       select REGMAP_IRQ
+       depends on I2C && OF
+       help
+         The ams AS3722 is a compact system PMU suitable for mobile phones,
+         tablets etc. It has 4 DC/DC step-down regulators, 3 DC/DC step-down
+         controllers, 11 LDOs, RTC, automatic battery, temperature and
+         over current monitoring, GPIOs, ADC and a watchdog.
+
 config PMIC_ADP5520
        bool "Analog Devices ADP5520/01 MFD PMIC Core Support"
        depends on I2C=y
@@ -664,14 +676,14 @@ menu "STMicroelectronics STMPE Interface Drivers"
 depends on MFD_STMPE
 
 config STMPE_I2C
-       bool "STMicroelectronics STMPE I2C Inteface"
+       bool "STMicroelectronics STMPE I2C Interface"
        depends on I2C=y
        default y
        help
          This is used to enable I2C interface of STMPE
 
 config STMPE_SPI
-       bool "STMicroelectronics STMPE SPI Inteface"
+       bool "STMicroelectronics STMPE SPI Interface"
        depends on SPI_MASTER
        help
          This is used to enable SPI interface of STMPE
@@ -1151,6 +1163,16 @@ config MFD_WM8994
          core support for the WM8994, in order to use the actual
          functionaltiy of the device other drivers must be enabled.
 
+config MFD_STW481X
+       bool "Support for ST Microelectronics STw481x"
+       depends on I2C && ARCH_NOMADIK
+       select REGMAP_I2C
+       select MFD_CORE
+       help
+         Select this option to enable the STw481x chip driver used
+         in various ST Microelectronics and ST-Ericsson embedded
+         Nomadik series.
+
 endmenu
 endif
 
index 15b905c..8a28dc9 100644 (file)
@@ -162,3 +162,5 @@ obj-$(CONFIG_MFD_LM3533)    += lm3533-core.o lm3533-ctrlbank.o
 obj-$(CONFIG_VEXPRESS_CONFIG)  += vexpress-config.o vexpress-sysreg.o
 obj-$(CONFIG_MFD_RETU)         += retu-mfd.o
 obj-$(CONFIG_MFD_AS3711)       += as3711.o
+obj-$(CONFIG_MFD_AS3722)       += as3722.o
+obj-$(CONFIG_MFD_STW481X)      += stw481x.o
index 6f68472..14d9542 100644 (file)
@@ -293,7 +293,7 @@ static ssize_t aat2870_reg_write_file(struct file *file,
        unsigned long addr, val;
        int ret;
 
-       buf_size = min(count, (sizeof(buf)-1));
+       buf_size = min(count, (size_t)(sizeof(buf)-1));
        if (copy_from_user(buf, user_buf, buf_size)) {
                dev_err(aat2870->dev, "Failed to copy from user\n");
                return -EFAULT;
index 022b186..75e180c 100644 (file)
@@ -540,7 +540,7 @@ static int arizona_of_get_core_pdata(struct arizona *arizona)
                for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
                        if (arizona->pdata.gpio_defaults[i] > 0xffff)
                                arizona->pdata.gpio_defaults[i] = 0;
-                       if (arizona->pdata.gpio_defaults[i] == 0)
+                       else if (arizona->pdata.gpio_defaults[i] == 0)
                                arizona->pdata.gpio_defaults[i] = 0x10000;
                }
        } else {
@@ -633,11 +633,11 @@ int arizona_dev_init(struct arizona *arizona)
        dev_set_drvdata(arizona->dev, arizona);
        mutex_init(&arizona->clk_lock);
 
-       arizona_of_get_core_pdata(arizona);
-
        if (dev_get_platdata(arizona->dev))
                memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
                       sizeof(arizona->pdata));
+       else
+               arizona_of_get_core_pdata(arizona);
 
        regcache_cache_only(arizona->regmap, true);
 
index 51dbabf..beccb79 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #include <linux/mfd/arizona/core.h>
 
index 47be7b3..1ca554b 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/spi/spi.h>
+#include <linux/of.h>
 
 #include <linux/mfd/arizona/core.h>
 
index abd3ab7..ec684fc 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/mfd/as3711.h>
 #include <linux/mfd/core.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
 
diff --git a/drivers/mfd/as3722.c b/drivers/mfd/as3722.c
new file mode 100644 (file)
index 0000000..f161f2e
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * Core driver for ams AS3722 PMICs
+ *
+ * Copyright (C) 2013 AMS AG
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * Author: Florian Lobmaier <florian.lobmaier@ams.com>
+ * Author: Laxman Dewangan <ldewangan@nvidia.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/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/as3722.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define AS3722_DEVICE_ID       0x0C
+
+static const struct resource as3722_rtc_resource[] = {
+       {
+               .name = "as3722-rtc-alarm",
+               .start = AS3722_IRQ_RTC_ALARM,
+               .end = AS3722_IRQ_RTC_ALARM,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static const struct resource as3722_adc_resource[] = {
+       {
+               .name = "as3722-adc",
+               .start = AS3722_IRQ_ADC,
+               .end = AS3722_IRQ_ADC,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct mfd_cell as3722_devs[] = {
+       {
+               .name = "as3722-pinctrl",
+       },
+       {
+               .name = "as3722-regulator",
+       },
+       {
+               .name = "as3722-rtc",
+               .num_resources = ARRAY_SIZE(as3722_rtc_resource),
+               .resources = as3722_rtc_resource,
+       },
+       {
+               .name = "as3722-adc",
+               .num_resources = ARRAY_SIZE(as3722_adc_resource),
+               .resources = as3722_adc_resource,
+       },
+       {
+               .name = "as3722-power-off",
+       },
+};
+
+static const struct regmap_irq as3722_irqs[] = {
+       /* INT1 IRQs */
+       [AS3722_IRQ_LID] = {
+               .mask = AS3722_INTERRUPT_MASK1_LID,
+       },
+       [AS3722_IRQ_ACOK] = {
+               .mask = AS3722_INTERRUPT_MASK1_ACOK,
+       },
+       [AS3722_IRQ_ENABLE1] = {
+               .mask = AS3722_INTERRUPT_MASK1_ENABLE1,
+       },
+       [AS3722_IRQ_OCCUR_ALARM_SD0] = {
+               .mask = AS3722_INTERRUPT_MASK1_OCURR_ALARM_SD0,
+       },
+       [AS3722_IRQ_ONKEY_LONG_PRESS] = {
+               .mask = AS3722_INTERRUPT_MASK1_ONKEY_LONG,
+       },
+       [AS3722_IRQ_ONKEY] = {
+               .mask = AS3722_INTERRUPT_MASK1_ONKEY,
+       },
+       [AS3722_IRQ_OVTMP] = {
+               .mask = AS3722_INTERRUPT_MASK1_OVTMP,
+       },
+       [AS3722_IRQ_LOWBAT] = {
+               .mask = AS3722_INTERRUPT_MASK1_LOWBAT,
+       },
+
+       /* INT2 IRQs */
+       [AS3722_IRQ_SD0_LV] = {
+               .mask = AS3722_INTERRUPT_MASK2_SD0_LV,
+               .reg_offset = 1,
+       },
+       [AS3722_IRQ_SD1_LV] = {
+               .mask = AS3722_INTERRUPT_MASK2_SD1_LV,
+               .reg_offset = 1,
+       },
+       [AS3722_IRQ_SD2_LV] = {
+               .mask = AS3722_INTERRUPT_MASK2_SD2345_LV,
+               .reg_offset = 1,
+       },
+       [AS3722_IRQ_PWM1_OV_PROT] = {
+               .mask = AS3722_INTERRUPT_MASK2_PWM1_OV_PROT,
+               .reg_offset = 1,
+       },
+       [AS3722_IRQ_PWM2_OV_PROT] = {
+               .mask = AS3722_INTERRUPT_MASK2_PWM2_OV_PROT,
+               .reg_offset = 1,
+       },
+       [AS3722_IRQ_ENABLE2] = {
+               .mask = AS3722_INTERRUPT_MASK2_ENABLE2,
+               .reg_offset = 1,
+       },
+       [AS3722_IRQ_SD6_LV] = {
+               .mask = AS3722_INTERRUPT_MASK2_SD6_LV,
+               .reg_offset = 1,
+       },
+       [AS3722_IRQ_RTC_REP] = {
+               .mask = AS3722_INTERRUPT_MASK2_RTC_REP,
+               .reg_offset = 1,
+       },
+
+       /* INT3 IRQs */
+       [AS3722_IRQ_RTC_ALARM] = {
+               .mask = AS3722_INTERRUPT_MASK3_RTC_ALARM,
+               .reg_offset = 2,
+       },
+       [AS3722_IRQ_GPIO1] = {
+               .mask = AS3722_INTERRUPT_MASK3_GPIO1,
+               .reg_offset = 2,
+       },
+       [AS3722_IRQ_GPIO2] = {
+               .mask = AS3722_INTERRUPT_MASK3_GPIO2,
+               .reg_offset = 2,
+       },
+       [AS3722_IRQ_GPIO3] = {
+               .mask = AS3722_INTERRUPT_MASK3_GPIO3,
+               .reg_offset = 2,
+       },
+       [AS3722_IRQ_GPIO4] = {
+               .mask = AS3722_INTERRUPT_MASK3_GPIO4,
+               .reg_offset = 2,
+       },
+       [AS3722_IRQ_GPIO5] = {
+               .mask = AS3722_INTERRUPT_MASK3_GPIO5,
+               .reg_offset = 2,
+       },
+       [AS3722_IRQ_WATCHDOG] = {
+               .mask = AS3722_INTERRUPT_MASK3_WATCHDOG,
+               .reg_offset = 2,
+       },
+       [AS3722_IRQ_ENABLE3] = {
+               .mask = AS3722_INTERRUPT_MASK3_ENABLE3,
+               .reg_offset = 2,
+       },
+
+       /* INT4 IRQs */
+       [AS3722_IRQ_TEMP_SD0_SHUTDOWN] = {
+               .mask = AS3722_INTERRUPT_MASK4_TEMP_SD0_SHUTDOWN,
+               .reg_offset = 3,
+       },
+       [AS3722_IRQ_TEMP_SD1_SHUTDOWN] = {
+               .mask = AS3722_INTERRUPT_MASK4_TEMP_SD1_SHUTDOWN,
+               .reg_offset = 3,
+       },
+       [AS3722_IRQ_TEMP_SD2_SHUTDOWN] = {
+               .mask = AS3722_INTERRUPT_MASK4_TEMP_SD6_SHUTDOWN,
+               .reg_offset = 3,
+       },
+       [AS3722_IRQ_TEMP_SD0_ALARM] = {
+               .mask = AS3722_INTERRUPT_MASK4_TEMP_SD0_ALARM,
+               .reg_offset = 3,
+       },
+       [AS3722_IRQ_TEMP_SD1_ALARM] = {
+               .mask = AS3722_INTERRUPT_MASK4_TEMP_SD1_ALARM,
+               .reg_offset = 3,
+       },
+       [AS3722_IRQ_TEMP_SD6_ALARM] = {
+               .mask = AS3722_INTERRUPT_MASK4_TEMP_SD6_ALARM,
+               .reg_offset = 3,
+       },
+       [AS3722_IRQ_OCCUR_ALARM_SD6] = {
+               .mask = AS3722_INTERRUPT_MASK4_OCCUR_ALARM_SD6,
+               .reg_offset = 3,
+       },
+       [AS3722_IRQ_ADC] = {
+               .mask = AS3722_INTERRUPT_MASK4_ADC,
+               .reg_offset = 3,
+       },
+};
+
+static const struct regmap_irq_chip as3722_irq_chip = {
+       .name = "as3722",
+       .irqs = as3722_irqs,
+       .num_irqs = ARRAY_SIZE(as3722_irqs),
+       .num_regs = 4,
+       .status_base = AS3722_INTERRUPT_STATUS1_REG,
+       .mask_base = AS3722_INTERRUPT_MASK1_REG,
+};
+
+static int as3722_check_device_id(struct as3722 *as3722)
+{
+       u32 val;
+       int ret;
+
+       /* Check that this is actually a AS3722 */
+       ret = as3722_read(as3722, AS3722_ASIC_ID1_REG, &val);
+       if (ret < 0) {
+               dev_err(as3722->dev, "ASIC_ID1 read failed: %d\n", ret);
+               return ret;
+       }
+
+       if (val != AS3722_DEVICE_ID) {
+               dev_err(as3722->dev, "Device is not AS3722, ID is 0x%x\n", val);
+               return -ENODEV;
+       }
+
+       ret = as3722_read(as3722, AS3722_ASIC_ID2_REG, &val);
+       if (ret < 0) {
+               dev_err(as3722->dev, "ASIC_ID2 read failed: %d\n", ret);
+               return ret;
+       }
+
+       dev_info(as3722->dev, "AS3722 with revision 0x%x found\n", val);
+       return 0;
+}
+
+static int as3722_configure_pullups(struct as3722 *as3722)
+{
+       int ret;
+       u32 val = 0;
+
+       if (as3722->en_intern_int_pullup)
+               val |= AS3722_INT_PULL_UP;
+       if (as3722->en_intern_i2c_pullup)
+               val |= AS3722_I2C_PULL_UP;
+
+       ret = as3722_update_bits(as3722, AS3722_IOVOLTAGE_REG,
+                       AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, val);
+       if (ret < 0)
+               dev_err(as3722->dev, "IOVOLTAGE_REG update failed: %d\n", ret);
+       return ret;
+}
+
+static const struct regmap_range as3722_readable_ranges[] = {
+       regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_SD6_VOLTAGE_REG),
+       regmap_reg_range(AS3722_GPIO0_CONTROL_REG, AS3722_LDO7_VOLTAGE_REG),
+       regmap_reg_range(AS3722_LDO9_VOLTAGE_REG, AS3722_REG_SEQU_MOD3_REG),
+       regmap_reg_range(AS3722_SD_PHSW_CTRL_REG, AS3722_PWM_CONTROL_H_REG),
+       regmap_reg_range(AS3722_WATCHDOG_TIMER_REG, AS3722_WATCHDOG_TIMER_REG),
+       regmap_reg_range(AS3722_WATCHDOG_SOFTWARE_SIGNAL_REG,
+                                       AS3722_BATTERY_VOLTAGE_MONITOR2_REG),
+       regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_PWM_VCONTROL4_REG),
+       regmap_reg_range(AS3722_BB_CHARGER_REG, AS3722_SRAM_REG),
+       regmap_reg_range(AS3722_RTC_ACCESS_REG, AS3722_RTC_ACCESS_REG),
+       regmap_reg_range(AS3722_RTC_STATUS_REG, AS3722_TEMP_STATUS_REG),
+       regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC_CONFIGURATION_REG),
+       regmap_reg_range(AS3722_ASIC_ID1_REG, AS3722_ASIC_ID2_REG),
+       regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG),
+};
+
+static const struct regmap_access_table as3722_readable_table = {
+       .yes_ranges = as3722_readable_ranges,
+       .n_yes_ranges = ARRAY_SIZE(as3722_readable_ranges),
+};
+
+static const struct regmap_range as3722_writable_ranges[] = {
+       regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_SD6_VOLTAGE_REG),
+       regmap_reg_range(AS3722_GPIO0_CONTROL_REG, AS3722_LDO7_VOLTAGE_REG),
+       regmap_reg_range(AS3722_LDO9_VOLTAGE_REG, AS3722_GPIO_SIGNAL_OUT_REG),
+       regmap_reg_range(AS3722_REG_SEQU_MOD1_REG, AS3722_REG_SEQU_MOD3_REG),
+       regmap_reg_range(AS3722_SD_PHSW_CTRL_REG, AS3722_PWM_CONTROL_H_REG),
+       regmap_reg_range(AS3722_WATCHDOG_TIMER_REG, AS3722_WATCHDOG_TIMER_REG),
+       regmap_reg_range(AS3722_WATCHDOG_SOFTWARE_SIGNAL_REG,
+                                       AS3722_BATTERY_VOLTAGE_MONITOR2_REG),
+       regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_PWM_VCONTROL4_REG),
+       regmap_reg_range(AS3722_BB_CHARGER_REG, AS3722_SRAM_REG),
+       regmap_reg_range(AS3722_INTERRUPT_MASK1_REG, AS3722_TEMP_STATUS_REG),
+       regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC1_CONTROL_REG),
+       regmap_reg_range(AS3722_ADC1_THRESHOLD_HI_MSB_REG,
+                                       AS3722_ADC_CONFIGURATION_REG),
+       regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG),
+};
+
+static const struct regmap_access_table as3722_writable_table = {
+       .yes_ranges = as3722_writable_ranges,
+       .n_yes_ranges = ARRAY_SIZE(as3722_writable_ranges),
+};
+
+static const struct regmap_range as3722_cacheable_ranges[] = {
+       regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_LDO11_VOLTAGE_REG),
+       regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_LDOCONTROL1_REG),
+};
+
+static const struct regmap_access_table as3722_volatile_table = {
+       .no_ranges = as3722_cacheable_ranges,
+       .n_no_ranges = ARRAY_SIZE(as3722_cacheable_ranges),
+};
+
+static const struct regmap_config as3722_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = AS3722_MAX_REGISTER,
+       .cache_type = REGCACHE_RBTREE,
+       .rd_table = &as3722_readable_table,
+       .wr_table = &as3722_writable_table,
+       .volatile_table = &as3722_volatile_table,
+};
+
+static int as3722_i2c_of_probe(struct i2c_client *i2c,
+                       struct as3722 *as3722)
+{
+       struct device_node *np = i2c->dev.of_node;
+       struct irq_data *irq_data;
+
+       if (!np) {
+               dev_err(&i2c->dev, "Device Tree not found\n");
+               return -EINVAL;
+       }
+
+       irq_data = irq_get_irq_data(i2c->irq);
+       if (!irq_data) {
+               dev_err(&i2c->dev, "Invalid IRQ: %d\n", i2c->irq);
+               return -EINVAL;
+       }
+
+       as3722->en_intern_int_pullup = of_property_read_bool(np,
+                                       "ams,enable-internal-int-pullup");
+       as3722->en_intern_i2c_pullup = of_property_read_bool(np,
+                                       "ams,enable-internal-i2c-pullup");
+       as3722->irq_flags = irqd_get_trigger_type(irq_data);
+       dev_dbg(&i2c->dev, "IRQ flags are 0x%08lx\n", as3722->irq_flags);
+       return 0;
+}
+
+static int as3722_i2c_probe(struct i2c_client *i2c,
+                       const struct i2c_device_id *id)
+{
+       struct as3722 *as3722;
+       unsigned long irq_flags;
+       int ret;
+
+       as3722 = devm_kzalloc(&i2c->dev, sizeof(struct as3722), GFP_KERNEL);
+       if (!as3722)
+               return -ENOMEM;
+
+       as3722->dev = &i2c->dev;
+       as3722->chip_irq = i2c->irq;
+       i2c_set_clientdata(i2c, as3722);
+
+       ret = as3722_i2c_of_probe(i2c, as3722);
+       if (ret < 0)
+               return ret;
+
+       as3722->regmap = devm_regmap_init_i2c(i2c, &as3722_regmap_config);
+       if (IS_ERR(as3722->regmap)) {
+               ret = PTR_ERR(as3722->regmap);
+               dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
+               return ret;
+       }
+
+       ret = as3722_check_device_id(as3722);
+       if (ret < 0)
+               return ret;
+
+       irq_flags = as3722->irq_flags | IRQF_ONESHOT;
+       ret = regmap_add_irq_chip(as3722->regmap, as3722->chip_irq,
+                       irq_flags, -1, &as3722_irq_chip,
+                       &as3722->irq_data);
+       if (ret < 0) {
+               dev_err(as3722->dev, "Failed to add regmap irq: %d\n", ret);
+               return ret;
+       }
+
+       ret = as3722_configure_pullups(as3722);
+       if (ret < 0)
+               goto scrub;
+
+       ret = mfd_add_devices(&i2c->dev, -1, as3722_devs,
+                       ARRAY_SIZE(as3722_devs), NULL, 0,
+                       regmap_irq_get_domain(as3722->irq_data));
+       if (ret) {
+               dev_err(as3722->dev, "Failed to add MFD devices: %d\n", ret);
+               goto scrub;
+       }
+
+       dev_dbg(as3722->dev, "AS3722 core driver initialized successfully\n");
+       return 0;
+
+scrub:
+       regmap_del_irq_chip(as3722->chip_irq, as3722->irq_data);
+       return ret;
+}
+
+static int as3722_i2c_remove(struct i2c_client *i2c)
+{
+       struct as3722 *as3722 = i2c_get_clientdata(i2c);
+
+       mfd_remove_devices(as3722->dev);
+       regmap_del_irq_chip(as3722->chip_irq, as3722->irq_data);
+       return 0;
+}
+
+static const struct of_device_id as3722_of_match[] = {
+       { .compatible = "ams,as3722", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, as3722_of_match);
+
+static const struct i2c_device_id as3722_i2c_id[] = {
+       { "as3722", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, as3722_i2c_id);
+
+static struct i2c_driver as3722_i2c_driver = {
+       .driver = {
+               .name = "as3722",
+               .owner = THIS_MODULE,
+               .of_match_table = as3722_of_match,
+       },
+       .probe = as3722_i2c_probe,
+       .remove = as3722_i2c_remove,
+       .id_table = as3722_i2c_id,
+};
+
+module_i2c_driver(as3722_i2c_driver);
+
+MODULE_DESCRIPTION("I2C support for AS3722 PMICs");
+MODULE_AUTHOR("Florian Lobmaier <florian.lobmaier@ams.com>");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL");
index 6a9fec4..c319c4e 100644 (file)
@@ -86,7 +86,11 @@ static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
        return 0;
 }
 
-static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
+/*
+ * According to errata item 24, multiwrite mode should be avoided
+ * in order to prevent register data corruption after power-down.
+ */
+static int da9052_i2c_disable_multiwrite(struct da9052 *da9052)
 {
        int reg_val, ret;
 
@@ -94,8 +98,8 @@ static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
        if (ret < 0)
                return ret;
 
-       if (reg_val & DA9052_CONTROL_B_WRITEMODE) {
-               reg_val &= ~DA9052_CONTROL_B_WRITEMODE;
+       if (!(reg_val & DA9052_CONTROL_B_WRITEMODE)) {
+               reg_val |= DA9052_CONTROL_B_WRITEMODE;
                ret = regmap_write(da9052->regmap, DA9052_CONTROL_B_REG,
                                   reg_val);
                if (ret < 0)
@@ -154,7 +158,7 @@ static int da9052_i2c_probe(struct i2c_client *client,
                return ret;
        }
 
-       ret = da9052_i2c_enable_multiwrite(da9052);
+       ret = da9052_i2c_disable_multiwrite(da9052);
        if (ret < 0)
                return ret;
 
index 7245b0c..2ed774e 100644 (file)
@@ -394,16 +394,12 @@ static int pcap_add_subdev(struct pcap_chip *pcap,
 static int ezx_pcap_remove(struct spi_device *spi)
 {
        struct pcap_chip *pcap = spi_get_drvdata(spi);
-       struct pcap_platform_data *pdata = dev_get_platdata(&spi->dev);
-       int i, adc_irq;
+       int i;
 
        /* remove all registered subdevs */
        device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);
 
        /* cleanup ADC */
-       adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ?
-                               PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE);
-       devm_free_irq(&spi->dev, adc_irq, pcap);
        mutex_lock(&pcap->adc_mutex);
        for (i = 0; i < PCAP_ADC_MAXQ; i++)
                kfree(pcap->adc_queue[i]);
@@ -509,8 +505,6 @@ static int ezx_pcap_probe(struct spi_device *spi)
 
 remove_subdevs:
        device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);
-/* free_adc: */
-       devm_free_irq(&spi->dev, adc_irq, pcap);
 free_irqchip:
        for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++)
                irq_set_chip_and_handler(i, NULL, NULL);
index 9483bc8..da1c656 100644 (file)
@@ -53,6 +53,7 @@
  *     document number TBD : Wellsburg
  *     document number TBD : Avoton SoC
  *     document number TBD : Coleto Creek
+ *     document number TBD : Wildcat Point-LP
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -211,6 +212,7 @@ enum lpc_chipsets {
        LPC_WBG,        /* Wellsburg */
        LPC_AVN,        /* Avoton SoC */
        LPC_COLETO,     /* Coleto Creek */
+       LPC_WPT_LP,     /* Wildcat Point-LP */
 };
 
 static struct lpc_ich_info lpc_chipset_info[] = {
@@ -503,6 +505,10 @@ static struct lpc_ich_info lpc_chipset_info[] = {
                .name = "Coleto Creek",
                .iTCO_version = 2,
        },
+       [LPC_WPT_LP] = {
+               .name = "Lynx Point_LP",
+               .iTCO_version = 2,
+       },
 };
 
 /*
@@ -721,6 +727,13 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {
        { PCI_VDEVICE(INTEL, 0x1f3a), LPC_AVN},
        { PCI_VDEVICE(INTEL, 0x1f3b), LPC_AVN},
        { PCI_VDEVICE(INTEL, 0x2390), LPC_COLETO},
+       { PCI_VDEVICE(INTEL, 0x9cc1), LPC_WPT_LP},
+       { PCI_VDEVICE(INTEL, 0x9cc2), LPC_WPT_LP},
+       { PCI_VDEVICE(INTEL, 0x9cc3), LPC_WPT_LP},
+       { PCI_VDEVICE(INTEL, 0x9cc5), LPC_WPT_LP},
+       { PCI_VDEVICE(INTEL, 0x9cc6), LPC_WPT_LP},
+       { PCI_VDEVICE(INTEL, 0x9cc7), LPC_WPT_LP},
+       { PCI_VDEVICE(INTEL, 0x9cc9), LPC_WPT_LP},
        { 0, },                 /* End of list */
 };
 MODULE_DEVICE_TABLE(pci, lpc_ich_ids);
@@ -969,7 +982,6 @@ static int lpc_ich_probe(struct pci_dev *dev,
        if (!cell_added) {
                dev_warn(&dev->dev, "No MFD cells added\n");
                lpc_ich_restore_config_space(dev);
-               pci_set_drvdata(dev, NULL);
                return -ENODEV;
        }
 
@@ -980,7 +992,6 @@ static void lpc_ich_remove(struct pci_dev *dev)
 {
        mfd_remove_devices(&dev->dev);
        lpc_ich_restore_config_space(dev);
-       pci_set_drvdata(dev, NULL);
 }
 
 static struct pci_driver lpc_ich_driver = {
index 8cc6aac..fbfbf0b 100644 (file)
@@ -59,18 +59,21 @@ static struct mfd_cell isch_smbus_cell = {
        .name = "isch_smbus",
        .num_resources = 1,
        .resources = &smbus_sch_resource,
+       .ignore_resource_conflicts = true,
 };
 
 static struct mfd_cell sch_gpio_cell = {
        .name = "sch_gpio",
        .num_resources = 1,
        .resources = &gpio_sch_resource,
+       .ignore_resource_conflicts = true,
 };
 
 static struct mfd_cell wdt_sch_cell = {
        .name = "ie6xx_wdt",
        .num_resources = 1,
        .resources = &wdt_sch_resource,
+       .ignore_resource_conflicts = true,
 };
 
 static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
index 522be67..34520cb 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/mfd/max77686.h>
 #include <linux/mfd/max77686-private.h>
 #include <linux/err.h>
+#include <linux/of.h>
 
 #define I2C_ADDR_RTC   (0x0C >> 1)
 
index 1029d01..66b58fe 100644 (file)
@@ -128,7 +128,8 @@ static void max77693_irq_sync_unlock(struct irq_data *data)
 static const inline struct max77693_irq_data *
 irq_to_max77693_irq(struct max77693_dev *max77693, int irq)
 {
-       return &max77693_irqs[irq];
+       struct irq_data *data = irq_get_irq_data(irq);
+       return &max77693_irqs[data->hwirq];
 }
 
 static void max77693_irq_mask(struct irq_data *data)
index c04723e..9f92463 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/i2c.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
+#include <linux/of.h>
 #include <linux/pm_runtime.h>
 #include <linux/mutex.h>
 #include <linux/mfd/core.h>
@@ -110,15 +111,9 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
                              const struct i2c_device_id *id)
 {
        struct max77693_dev *max77693;
-       struct max77693_platform_data *pdata = dev_get_platdata(&i2c->dev);
        u8 reg_data;
        int ret = 0;
 
-       if (!pdata) {
-               dev_err(&i2c->dev, "No platform data found.\n");
-               return -EINVAL;
-       }
-
        max77693 = devm_kzalloc(&i2c->dev,
                        sizeof(struct max77693_dev), GFP_KERNEL);
        if (max77693 == NULL)
@@ -138,8 +133,6 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
                return ret;
        }
 
-       max77693->wakeup = pdata->wakeup;
-
        ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2,
                                &reg_data);
        if (ret < 0) {
@@ -179,8 +172,6 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
        if (ret < 0)
                goto err_mfd;
 
-       device_init_wakeup(max77693->dev, pdata->wakeup);
-
        return ret;
 
 err_mfd:
@@ -235,11 +226,19 @@ static const struct dev_pm_ops max77693_pm = {
        .resume = max77693_resume,
 };
 
+#ifdef CONFIG_OF
+static struct of_device_id max77693_dt_match[] = {
+       { .compatible = "maxim,max77693" },
+       {},
+};
+#endif
+
 static struct i2c_driver max77693_i2c_driver = {
        .driver = {
                   .name = "max77693",
                   .owner = THIS_MODULE,
                   .pm = &max77693_pm,
+                  .of_match_table = of_match_ptr(max77693_dt_match),
        },
        .probe = max77693_i2c_probe,
        .remove = max77693_i2c_remove,
index e9b1c93..3bbfedc 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/mfd/core.h>
 #include <linux/mfd/max8907.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
index de7fb80..176aa26 100644 (file)
@@ -238,7 +238,7 @@ static struct i2c_driver max8925_driver = {
                .name   = "max8925",
                .owner  = THIS_MODULE,
                .pm     = &max8925_pm_ops,
-               .of_match_table = of_match_ptr(max8925_dt_ids),
+               .of_match_table = max8925_dt_ids,
        },
        .probe          = max8925_probe,
        .remove         = max8925_remove,
index cee098c..791aea3 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
+#include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/interrupt.h>
 #include <linux/pm_runtime.h>
index f745e27..898bd33 100644 (file)
@@ -78,7 +78,6 @@ static int mc13xxx_i2c_probe(struct i2c_client *client,
                ret = PTR_ERR(mc13xxx->regmap);
                dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
                                ret);
-               dev_set_drvdata(&client->dev, NULL);
                return ret;
        }
 
index adc8ea3..2676492 100644 (file)
@@ -64,7 +64,8 @@ int mfd_cell_disable(struct platform_device *pdev)
 EXPORT_SYMBOL(mfd_cell_disable);
 
 static int mfd_platform_add_cell(struct platform_device *pdev,
-                                const struct mfd_cell *cell)
+                                const struct mfd_cell *cell,
+                                atomic_t *usage_count)
 {
        if (!cell)
                return 0;
@@ -73,11 +74,12 @@ static int mfd_platform_add_cell(struct platform_device *pdev,
        if (!pdev->mfd_cell)
                return -ENOMEM;
 
+       pdev->mfd_cell->usage_count = usage_count;
        return 0;
 }
 
 static int mfd_add_device(struct device *parent, int id,
-                         const struct mfd_cell *cell,
+                         const struct mfd_cell *cell, atomic_t *usage_count,
                          struct resource *mem_base,
                          int irq_base, struct irq_domain *domain)
 {
@@ -123,7 +125,7 @@ static int mfd_add_device(struct device *parent, int id,
                        goto fail_alias;
        }
 
-       ret = mfd_platform_add_cell(pdev, cell);
+       ret = mfd_platform_add_cell(pdev, cell, usage_count);
        if (ret)
                goto fail_alias;
 
@@ -192,12 +194,12 @@ fail_alloc:
 }
 
 int mfd_add_devices(struct device *parent, int id,
-                   struct mfd_cell *cells, int n_devs,
+                   const struct mfd_cell *cells, int n_devs,
                    struct resource *mem_base,
                    int irq_base, struct irq_domain *domain)
 {
        int i;
-       int ret = 0;
+       int ret;
        atomic_t *cnts;
 
        /* initialize reference counting for all cells */
@@ -207,16 +209,19 @@ int mfd_add_devices(struct device *parent, int id,
 
        for (i = 0; i < n_devs; i++) {
                atomic_set(&cnts[i], 0);
-               cells[i].usage_count = &cnts[i];
-               ret = mfd_add_device(parent, id, cells + i, mem_base,
+               ret = mfd_add_device(parent, id, cells + i, cnts + i, mem_base,
                                     irq_base, domain);
                if (ret)
-                       break;
+                       goto fail;
        }
 
-       if (ret)
-               mfd_remove_devices(parent);
+       return 0;
 
+fail:
+       if (i)
+               mfd_remove_devices(parent);
+       else
+               kfree(cnts);
        return ret;
 }
 EXPORT_SYMBOL(mfd_add_devices);
@@ -271,8 +276,8 @@ int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)
        for (i = 0; i < n_clones; i++) {
                cell_entry.name = clones[i];
                /* don't give up if a single call fails; just report error */
-               if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0,
-                                  NULL))
+               if (mfd_add_device(pdev->dev.parent, -1, &cell_entry,
+                                  cell_entry.usage_count, NULL, 0, NULL))
                        dev_err(dev, "failed to create platform device '%s'\n",
                                        clones[i]);
        }
index 29ee54d..142650f 100644 (file)
@@ -328,13 +328,13 @@ static int usbhs_runtime_resume(struct device *dev)
        omap_tll_enable(pdata);
 
        if (!IS_ERR(omap->ehci_logic_fck))
-               clk_enable(omap->ehci_logic_fck);
+               clk_prepare_enable(omap->ehci_logic_fck);
 
        for (i = 0; i < omap->nports; i++) {
                switch (pdata->port_mode[i]) {
                case OMAP_EHCI_PORT_MODE_HSIC:
                        if (!IS_ERR(omap->hsic60m_clk[i])) {
-                               r = clk_enable(omap->hsic60m_clk[i]);
+                               r = clk_prepare_enable(omap->hsic60m_clk[i]);
                                if (r) {
                                        dev_err(dev,
                                         "Can't enable port %d hsic60m clk:%d\n",
@@ -343,7 +343,7 @@ static int usbhs_runtime_resume(struct device *dev)
                        }
 
                        if (!IS_ERR(omap->hsic480m_clk[i])) {
-                               r = clk_enable(omap->hsic480m_clk[i]);
+                               r = clk_prepare_enable(omap->hsic480m_clk[i]);
                                if (r) {
                                        dev_err(dev,
                                         "Can't enable port %d hsic480m clk:%d\n",
@@ -354,7 +354,7 @@ static int usbhs_runtime_resume(struct device *dev)
 
                case OMAP_EHCI_PORT_MODE_TLL:
                        if (!IS_ERR(omap->utmi_clk[i])) {
-                               r = clk_enable(omap->utmi_clk[i]);
+                               r = clk_prepare_enable(omap->utmi_clk[i]);
                                if (r) {
                                        dev_err(dev,
                                         "Can't enable port %d clk : %d\n",
@@ -382,15 +382,15 @@ static int usbhs_runtime_suspend(struct device *dev)
                switch (pdata->port_mode[i]) {
                case OMAP_EHCI_PORT_MODE_HSIC:
                        if (!IS_ERR(omap->hsic60m_clk[i]))
-                               clk_disable(omap->hsic60m_clk[i]);
+                               clk_disable_unprepare(omap->hsic60m_clk[i]);
 
                        if (!IS_ERR(omap->hsic480m_clk[i]))
-                               clk_disable(omap->hsic480m_clk[i]);
+                               clk_disable_unprepare(omap->hsic480m_clk[i]);
                /* Fall through as utmi_clks were used in HSIC mode */
 
                case OMAP_EHCI_PORT_MODE_TLL:
                        if (!IS_ERR(omap->utmi_clk[i]))
-                               clk_disable(omap->utmi_clk[i]);
+                               clk_disable_unprepare(omap->utmi_clk[i]);
                        break;
                default:
                        break;
@@ -398,7 +398,7 @@ static int usbhs_runtime_suspend(struct device *dev)
        }
 
        if (!IS_ERR(omap->ehci_logic_fck))
-               clk_disable(omap->ehci_logic_fck);
+               clk_disable_unprepare(omap->ehci_logic_fck);
 
        omap_tll_disable(pdata);
 
@@ -893,7 +893,7 @@ static struct platform_driver usbhs_omap_driver = {
                .name           = (char *)usbhs_driver_name,
                .owner          = THIS_MODULE,
                .pm             = &usbhsomap_dev_pm_ops,
-               .of_match_table = of_match_ptr(usbhs_omap_dt_ids),
+               .of_match_table = usbhs_omap_dt_ids,
        },
        .remove         = usbhs_omap_remove,
 };
index e59ac4c..0d946ae 100644 (file)
@@ -320,7 +320,7 @@ static struct platform_driver usbtll_omap_driver = {
        .driver = {
                .name           = (char *)usbtll_driver_name,
                .owner          = THIS_MODULE,
-               .of_match_table = of_match_ptr(usbtll_omap_dt_ids),
+               .of_match_table = usbtll_omap_dt_ids,
        },
        .probe          = usbtll_omap_probe,
        .remove         = usbtll_omap_remove,
@@ -429,7 +429,7 @@ int omap_tll_enable(struct usbhs_omap_platform_data *pdata)
                        if (IS_ERR(tll->ch_clk[i]))
                                continue;
 
-                       r = clk_enable(tll->ch_clk[i]);
+                       r = clk_prepare_enable(tll->ch_clk[i]);
                        if (r) {
                                dev_err(tll_dev,
                                 "Error enabling ch %d clock: %d\n", i, r);
@@ -460,7 +460,7 @@ int omap_tll_disable(struct usbhs_omap_platform_data *pdata)
        for (i = 0; i < tll->nch; i++) {
                if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
                        if (!IS_ERR(tll->ch_clk[i]))
-                               clk_disable(tll->ch_clk[i]);
+                               clk_disable_unprepare(tll->ch_clk[i]);
                }
        }
 
index 135afab..d280d78 100644 (file)
@@ -368,6 +368,7 @@ static const struct of_device_id of_palmas_match_tbl[] = {
        },
        { },
 };
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
 
 static int palmas_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
@@ -402,7 +403,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
        palmas->dev = &i2c->dev;
        palmas->irq = i2c->irq;
 
-       match = of_match_device(of_match_ptr(of_palmas_match_tbl), &i2c->dev);
+       match = of_match_device(of_palmas_match_tbl, &i2c->dev);
 
        if (!match)
                return -ENODATA;
@@ -421,7 +422,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
                                dev_err(palmas->dev,
                                        "can't attach client %d\n", i);
                                ret = -ENOMEM;
-                               goto err;
+                               goto err_i2c;
                        }
                        palmas->i2c_clients[i]->dev.of_node = of_node_get(node);
                }
@@ -432,7 +433,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
                        dev_err(palmas->dev,
                                "Failed to allocate regmap %d, err: %d\n",
                                i, ret);
-                       goto err;
+                       goto err_i2c;
                }
        }
 
@@ -451,7 +452,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
                        reg);
        if (ret < 0) {
                dev_err(palmas->dev, "POLARITY_CTRL updat failed: %d\n", ret);
-               goto err;
+               goto err_i2c;
        }
 
        /* Change IRQ into clear on read mode for efficiency */
@@ -465,7 +466,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
                        IRQF_ONESHOT | pdata->irq_flags, 0, &palmas_irq_chip,
                        &palmas->irq_data);
        if (ret < 0)
-               goto err;
+               goto err_i2c;
 
 no_irq:
        slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE);
@@ -551,7 +552,6 @@ no_irq:
                } else if (pdata->pm_off && !pm_power_off) {
                        palmas_dev = palmas;
                        pm_power_off = palmas_power_off;
-                       return ret;
                }
        }
 
@@ -559,17 +559,31 @@ no_irq:
 
 err_irq:
        regmap_del_irq_chip(palmas->irq, palmas->irq_data);
-err:
+err_i2c:
+       for (i = 1; i < PALMAS_NUM_CLIENTS; i++) {
+               if (palmas->i2c_clients[i])
+                       i2c_unregister_device(palmas->i2c_clients[i]);
+       }
        return ret;
 }
 
 static int palmas_i2c_remove(struct i2c_client *i2c)
 {
        struct palmas *palmas = i2c_get_clientdata(i2c);
+       int i;
 
-       mfd_remove_devices(palmas->dev);
        regmap_del_irq_chip(palmas->irq, palmas->irq_data);
 
+       for (i = 1; i < PALMAS_NUM_CLIENTS; i++) {
+               if (palmas->i2c_clients[i])
+                       i2c_unregister_device(palmas->i2c_clients[i]);
+       }
+
+       if (palmas == palmas_dev) {
+               pm_power_off = NULL;
+               palmas_dev = NULL;
+       }
+
        return 0;
 }
 
index a6841f7..484fe66 100644 (file)
@@ -171,11 +171,12 @@ static int pm8921_remove(struct platform_device *pdev)
        drvdata = platform_get_drvdata(pdev);
        if (drvdata)
                pmic = drvdata->pm_chip_data;
-       if (pmic)
+       if (pmic) {
                mfd_remove_devices(pmic->dev);
-       if (pmic->irq_chip) {
-               pm8xxx_irq_exit(pmic->irq_chip);
-               pmic->irq_chip = NULL;
+               if (pmic->irq_chip) {
+                       pm8xxx_irq_exit(pmic->irq_chip);
+                       pmic->irq_chip = NULL;
+               }
        }
 
        return 0;
index 3b835f5..573de7b 100644 (file)
@@ -130,13 +130,57 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr)
 {
        int err;
 
-       err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV, 0xFE46);
+       err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV,
+                       PHY_REG_REV_RESV | PHY_REG_REV_RXIDLE_LATCHED |
+                       PHY_REG_REV_P1_EN | PHY_REG_REV_RXIDLE_EN |
+                       PHY_REG_REV_RX_PWST | PHY_REG_REV_CLKREQ_DLY_TIMER_1_0 |
+                       PHY_REG_REV_STOP_CLKRD | PHY_REG_REV_STOP_CLKWR);
        if (err < 0)
                return err;
 
        msleep(1);
 
-       return rtsx_pci_write_phy_register(pcr, PHY_BPCR, 0x05C0);
+       err = rtsx_pci_write_phy_register(pcr, PHY_BPCR,
+                       PHY_BPCR_IBRXSEL | PHY_BPCR_IBTXSEL |
+                       PHY_BPCR_IB_FILTER | PHY_BPCR_CMIRROR_EN);
+       if (err < 0)
+               return err;
+       err = rtsx_pci_write_phy_register(pcr, PHY_PCR,
+                       PHY_PCR_FORCE_CODE | PHY_PCR_OOBS_CALI_50 |
+                       PHY_PCR_OOBS_VCM_08 | PHY_PCR_OOBS_SEN_90 |
+                       PHY_PCR_RSSI_EN);
+       if (err < 0)
+               return err;
+       err = rtsx_pci_write_phy_register(pcr, PHY_RCR2,
+                       PHY_RCR2_EMPHASE_EN | PHY_RCR2_NADJR |
+                       PHY_RCR2_CDR_CP_10 | PHY_RCR2_CDR_SR_2 |
+                       PHY_RCR2_FREQSEL_12 | PHY_RCR2_CPADJEN |
+                       PHY_RCR2_CDR_SC_8 | PHY_RCR2_CALIB_LATE);
+       if (err < 0)
+               return err;
+       err = rtsx_pci_write_phy_register(pcr, PHY_FLD4,
+                       PHY_FLD4_FLDEN_SEL | PHY_FLD4_REQ_REF |
+                       PHY_FLD4_RXAMP_OFF | PHY_FLD4_REQ_ADDA |
+                       PHY_FLD4_BER_COUNT | PHY_FLD4_BER_TIMER |
+                       PHY_FLD4_BER_CHK_EN);
+       if (err < 0)
+               return err;
+       err = rtsx_pci_write_phy_register(pcr, PHY_RDR, PHY_RDR_RXDSEL_1_9);
+       if (err < 0)
+               return err;
+       err = rtsx_pci_write_phy_register(pcr, PHY_RCR1,
+                       PHY_RCR1_ADP_TIME | PHY_RCR1_VCO_COARSE);
+       if (err < 0)
+               return err;
+       err = rtsx_pci_write_phy_register(pcr, PHY_FLD3,
+                       PHY_FLD3_TIMER_4 | PHY_FLD3_TIMER_6 |
+                       PHY_FLD3_RXDELINK);
+       if (err < 0)
+               return err;
+       return rtsx_pci_write_phy_register(pcr, PHY_TUNE,
+                       PHY_TUNE_TUNEREF_1_0 | PHY_TUNE_VBGSEL_1252 |
+                       PHY_TUNE_SDBUS_33 | PHY_TUNE_TUNED18 |
+                       PHY_TUNE_TUNED12);
 }
 
 static int rts5249_turn_on_led(struct rtsx_pcr *pcr)
index e6ae772..11e20af 100644 (file)
@@ -1149,7 +1149,7 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,
        pcr->remap_addr = ioremap_nocache(base, len);
        if (!pcr->remap_addr) {
                ret = -ENOMEM;
-               goto free_host;
+               goto free_handle;
        }
 
        pcr->rtsx_resv_buf = dma_alloc_coherent(&(pcidev->dev),
@@ -1209,8 +1209,6 @@ disable_msi:
                        pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr);
 unmap:
        iounmap(pcr->remap_addr);
-free_host:
-       dev_set_drvdata(&pcidev->dev, NULL);
 free_handle:
        kfree(handle);
 free_pcr:
@@ -1242,7 +1240,6 @@ static void rtsx_pci_remove(struct pci_dev *pcidev)
                pci_disable_msi(pcr->pci);
        iounmap(pcr->remap_addr);
 
-       dev_set_drvdata(&pcidev->dev, NULL);
        pci_release_regions(pcidev);
        pci_disable_device(pcidev);
 
index f530e4b..34c18fb 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
+#include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/interrupt.h>
 #include <linux/pm_runtime.h>
index 33f040c..c2c8c91 100644 (file)
@@ -1232,7 +1232,7 @@ static ssize_t sm501_dbg_regs(struct device *dev,
 }
 
 
-static DEVICE_ATTR(dbg_regs, 0666, sm501_dbg_regs, NULL);
+static DEVICE_ATTR(dbg_regs, 0444, sm501_dbg_regs, NULL);
 
 /* sm501_init_reg
  *
@@ -1660,7 +1660,6 @@ static int sm501_pci_probe(struct pci_dev *dev,
  err3:
        pci_disable_device(dev);
  err2:
-       pci_set_drvdata(dev, NULL);
        kfree(sm);
  err1:
        return err;
@@ -1695,7 +1694,6 @@ static void sm501_pci_remove(struct pci_dev *dev)
        release_resource(sm->regs_claim);
        kfree(sm->regs_claim);
 
-       pci_set_drvdata(dev, NULL);
        pci_disable_device(dev);
 }
 
diff --git a/drivers/mfd/stw481x.c b/drivers/mfd/stw481x.c
new file mode 100644 (file)
index 0000000..1243d5c
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Core driver for STw4810/STw4811
+ *
+ * Copyright (C) 2013 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stw481x.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+/*
+ * This driver can only access the non-USB portions of STw4811, the register
+ * range 0x00-0x10 dealing with USB is bound to the two special I2C pins used
+ * for USB control.
+ */
+
+/* Registers inside the power control address space */
+#define STW_PC_VCORE_SEL       0x05U
+#define STW_PC_VAUX_SEL                0x06U
+#define STW_PC_VPLL_SEL                0x07U
+
+/**
+ * stw481x_get_pctl_reg() - get a power control register
+ * @stw481x: handle to the stw481x chip
+ * @reg: power control register to fetch
+ *
+ * The power control registers is a set of one-time-programmable registers
+ * in its own register space, accessed by writing addess bits to these
+ * two registers: bits 7,6,5 of PCTL_REG_LO corresponds to the 3 LSBs of
+ * the address and bits 8,9 of PCTL_REG_HI corresponds to the 2 MSBs of
+ * the address, forming an address space of 5 bits, i.e. 32 registers
+ * 0x00 ... 0x1f can be obtained.
+ */
+static int stw481x_get_pctl_reg(struct stw481x *stw481x, u8 reg)
+{
+       u8 msb = (reg >> 3) & 0x03;
+       u8 lsb = (reg << 5) & 0xe0;
+       unsigned int val;
+       u8 vrfy;
+       int ret;
+
+       ret = regmap_write(stw481x->map, STW_PCTL_REG_HI, msb);
+       if (ret)
+               return ret;
+       ret = regmap_write(stw481x->map, STW_PCTL_REG_LO, lsb);
+       if (ret)
+               return ret;
+       ret = regmap_read(stw481x->map, STW_PCTL_REG_HI, &val);
+       if (ret)
+               return ret;
+       vrfy = (val & 0x03) << 3;
+       ret = regmap_read(stw481x->map, STW_PCTL_REG_LO, &val);
+       if (ret)
+               return ret;
+       vrfy |= ((val >> 5) & 0x07);
+       if (vrfy != reg)
+               return -EIO;
+       return (val >> 1) & 0x0f;
+}
+
+static int stw481x_startup(struct stw481x *stw481x)
+{
+       /* Voltages multiplied by 100 */
+       u8 vcore_val[] = { 100, 105, 110, 115, 120, 122, 124, 126, 128,
+                          130, 132, 134, 136, 138, 140, 145 };
+       u8 vpll_val[] = { 105, 120, 130, 180 };
+       u8 vaux_val[] = { 15, 18, 25, 28 };
+       u8 vcore;
+       u8 vcore_slp;
+       u8 vpll;
+       u8 vaux;
+       bool vaux_en;
+       bool it_warn;
+       int ret;
+       unsigned int val;
+
+       ret = regmap_read(stw481x->map, STW_CONF1, &val);
+       if (ret)
+               return ret;
+       vaux_en = !!(val & STW_CONF1_PDN_VAUX);
+       it_warn = !!(val & STW_CONF1_IT_WARN);
+
+       dev_info(&stw481x->client->dev, "voltages %s\n",
+               (val & STW_CONF1_V_MONITORING) ? "OK" : "LOW");
+       dev_info(&stw481x->client->dev, "MMC level shifter %s\n",
+               (val & STW_CONF1_MMC_LS_STATUS) ? "high impedance" : "ON");
+       dev_info(&stw481x->client->dev, "VMMC: %s\n",
+               (val & STW_CONF1_PDN_VMMC) ? "ON" : "disabled");
+
+       dev_info(&stw481x->client->dev, "STw481x power control registers:\n");
+
+       ret = stw481x_get_pctl_reg(stw481x, STW_PC_VCORE_SEL);
+       if (ret < 0)
+               return ret;
+       vcore = ret & 0x0f;
+
+       ret = stw481x_get_pctl_reg(stw481x, STW_PC_VAUX_SEL);
+       if (ret < 0)
+               return ret;
+       vaux = (ret >> 2) & 3;
+       vpll = (ret >> 4) & 1; /* Save bit 4 */
+
+       ret = stw481x_get_pctl_reg(stw481x, STW_PC_VPLL_SEL);
+       if (ret < 0)
+               return ret;
+       vpll |= (ret >> 1) & 2;
+
+       dev_info(&stw481x->client->dev, "VCORE: %u.%uV %s\n",
+               vcore_val[vcore] / 100, vcore_val[vcore] % 100,
+               (ret & 4) ? "ON" : "OFF");
+
+       dev_info(&stw481x->client->dev, "VPLL:  %u.%uV %s\n",
+               vpll_val[vpll] / 100, vpll_val[vpll] % 100,
+               (ret & 0x10) ? "ON" : "OFF");
+
+       dev_info(&stw481x->client->dev, "VAUX:  %u.%uV %s\n",
+               vaux_val[vaux] / 10, vaux_val[vaux] % 10,
+               vaux_en ? "ON" : "OFF");
+
+       ret = regmap_read(stw481x->map, STW_CONF2, &val);
+       if (ret)
+               return ret;
+
+       dev_info(&stw481x->client->dev, "TWARN: %s threshold, %s\n",
+               it_warn ? "below" : "above",
+               (val & STW_CONF2_MASK_TWARN) ?
+                "enabled" : "mask through VDDOK");
+       dev_info(&stw481x->client->dev, "VMMC: %s\n",
+               (val & STW_CONF2_VMMC_EXT) ? "internal" : "external");
+       dev_info(&stw481x->client->dev, "IT WAKE UP: %s\n",
+               (val & STW_CONF2_MASK_IT_WAKE_UP) ? "enabled" : "masked");
+       dev_info(&stw481x->client->dev, "GPO1: %s\n",
+               (val & STW_CONF2_GPO1) ? "low" : "high impedance");
+       dev_info(&stw481x->client->dev, "GPO2: %s\n",
+               (val & STW_CONF2_GPO2) ? "low" : "high impedance");
+
+       ret = regmap_read(stw481x->map, STW_VCORE_SLEEP, &val);
+       if (ret)
+               return ret;
+       vcore_slp = val & 0x0f;
+       dev_info(&stw481x->client->dev, "VCORE SLEEP: %u.%uV\n",
+               vcore_val[vcore_slp] / 100, vcore_val[vcore_slp] % 100);
+
+       return 0;
+}
+
+/*
+ * MFD cells - we have one cell which is selected operation
+ * mode, and we always have a GPIO cell.
+ */
+static struct mfd_cell stw481x_cells[] = {
+       {
+               .of_compatible = "st,stw481x-vmmc",
+               .name = "stw481x-vmmc-regulator",
+               .id = -1,
+       },
+};
+
+const struct regmap_config stw481x_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+
+static int stw481x_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct stw481x                  *stw481x;
+       int ret;
+       int i;
+
+       stw481x = devm_kzalloc(&client->dev, sizeof(*stw481x), GFP_KERNEL);
+       if (!stw481x)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, stw481x);
+       stw481x->client = client;
+       stw481x->map = devm_regmap_init_i2c(client, &stw481x_regmap_config);
+
+       ret = stw481x_startup(stw481x);
+       if (ret) {
+               dev_err(&client->dev, "chip initialization failed\n");
+               return ret;
+       }
+
+       /* Set up and register the platform devices. */
+       for (i = 0; i < ARRAY_SIZE(stw481x_cells); i++) {
+               /* One state holder for all drivers, this is simple */
+               stw481x_cells[i].platform_data = stw481x;
+               stw481x_cells[i].pdata_size = sizeof(*stw481x);
+       }
+
+       ret = mfd_add_devices(&client->dev, 0, stw481x_cells,
+                       ARRAY_SIZE(stw481x_cells), NULL, 0, NULL);
+       if (ret)
+               return ret;
+
+       dev_info(&client->dev, "initialized STw481x device\n");
+
+       return ret;
+}
+
+static int stw481x_remove(struct i2c_client *client)
+{
+       mfd_remove_devices(&client->dev);
+       return 0;
+}
+
+/*
+ * This ID table is completely unused, as this is a pure
+ * device-tree probed driver, but it has to be here due to
+ * the structure of the I2C core.
+ */
+static const struct i2c_device_id stw481x_id[] = {
+       { "stw481x", 0 },
+       { },
+};
+
+static const struct of_device_id stw481x_match[] = {
+       { .compatible = "st,stw4810", },
+       { .compatible = "st,stw4811", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, stw481x_match);
+
+static struct i2c_driver stw481x_driver = {
+       .driver = {
+               .name   = "stw481x",
+               .of_match_table = stw481x_match,
+       },
+       .probe          = stw481x_probe,
+       .remove         = stw481x_remove,
+       .id_table       = stw481x_id,
+};
+
+module_i2c_driver(stw481x_driver);
+
+MODULE_AUTHOR("Linus Walleij");
+MODULE_DESCRIPTION("STw481x PMIC driver");
+MODULE_LICENSE("GPL v2");
index 70f4909..87ea51d 100644 (file)
 #include <linux/mfd/core.h>
 #include <linux/mfd/tc3589x.h>
 
+/**
+ * enum tc3589x_version - indicates the TC3589x version
+ */
+enum tc3589x_version {
+       TC3589X_TC35890,
+       TC3589X_TC35892,
+       TC3589X_TC35893,
+       TC3589X_TC35894,
+       TC3589X_TC35895,
+       TC3589X_TC35896,
+       TC3589X_UNKNOWN,
+};
+
 #define TC3589x_CLKMODE_MODCTL_SLEEP           0x0
 #define TC3589x_CLKMODE_MODCTL_OPERATION       (1 << 0)
 
@@ -361,7 +374,21 @@ static int tc3589x_probe(struct i2c_client *i2c,
        tc3589x->i2c = i2c;
        tc3589x->pdata = pdata;
        tc3589x->irq_base = pdata->irq_base;
-       tc3589x->num_gpio = id->driver_data;
+
+       switch (id->driver_data) {
+       case TC3589X_TC35893:
+       case TC3589X_TC35895:
+       case TC3589X_TC35896:
+               tc3589x->num_gpio = 20;
+               break;
+       case TC3589X_TC35890:
+       case TC3589X_TC35892:
+       case TC3589X_TC35894:
+       case TC3589X_UNKNOWN:
+       default:
+               tc3589x->num_gpio = 24;
+               break;
+       }
 
        i2c_set_clientdata(i2c, tc3589x);
 
@@ -432,7 +459,13 @@ static int tc3589x_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume);
 
 static const struct i2c_device_id tc3589x_id[] = {
-       { "tc3589x", 24 },
+       { "tc35890", TC3589X_TC35890 },
+       { "tc35892", TC3589X_TC35892 },
+       { "tc35893", TC3589X_TC35893 },
+       { "tc35894", TC3589X_TC35894 },
+       { "tc35895", TC3589X_TC35895 },
+       { "tc35896", TC3589X_TC35896 },
+       { "tc3589x", TC3589X_UNKNOWN },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, tc3589x_id);
index 1c2b994..71e3e0c 100644 (file)
@@ -445,7 +445,6 @@ static int ti_ssp_remove(struct platform_device *pdev)
        iounmap(ssp->regs);
        release_mem_region(ssp->res->start, resource_size(ssp->res));
        kfree(ssp);
-       dev_set_drvdata(dev, NULL);
        return 0;
 }
 
index baaf5a8..88718ab 100644 (file)
@@ -56,21 +56,25 @@ EXPORT_SYMBOL_GPL(am335x_tsc_se_update);
 
 void am335x_tsc_se_set(struct ti_tscadc_dev *tsadc, u32 val)
 {
-       spin_lock(&tsadc->reg_lock);
+       unsigned long flags;
+
+       spin_lock_irqsave(&tsadc->reg_lock, flags);
        tsadc->reg_se_cache = tscadc_readl(tsadc, REG_SE);
        tsadc->reg_se_cache |= val;
        am335x_tsc_se_update(tsadc);
-       spin_unlock(&tsadc->reg_lock);
+       spin_unlock_irqrestore(&tsadc->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(am335x_tsc_se_set);
 
 void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val)
 {
-       spin_lock(&tsadc->reg_lock);
+       unsigned long flags;
+
+       spin_lock_irqsave(&tsadc->reg_lock, flags);
        tsadc->reg_se_cache = tscadc_readl(tsadc, REG_SE);
        tsadc->reg_se_cache &= ~val;
        am335x_tsc_se_update(tsadc);
-       spin_unlock(&tsadc->reg_lock);
+       spin_unlock_irqrestore(&tsadc->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(am335x_tsc_se_clr);
 
@@ -95,7 +99,7 @@ static        int ti_tscadc_probe(struct platform_device *pdev)
        const __be32            *cur;
        u32                     val;
        int                     err, ctrl;
-       int                     clk_value, clock_rate;
+       int                     clock_rate;
        int                     tsc_wires = 0, adc_channels = 0, total_channels;
        int                     readouts = 0;
 
@@ -196,11 +200,11 @@ static    int ti_tscadc_probe(struct platform_device *pdev)
        }
        clock_rate = clk_get_rate(clk);
        clk_put(clk);
-       clk_value = clock_rate / ADC_CLK;
+       tscadc->clk_div = clock_rate / ADC_CLK;
 
        /* TSCADC_CLKDIV needs to be configured to the value minus 1 */
-       clk_value = clk_value - 1;
-       tscadc_writel(tscadc, REG_CLKDIV, clk_value);
+       tscadc->clk_div--;
+       tscadc_writel(tscadc, REG_CLKDIV, tscadc->clk_div);
 
        /* Set the control register bits */
        ctrl = CNTRLREG_STEPCONFIGWRT |
@@ -303,6 +307,8 @@ static int tscadc_resume(struct device *dev)
        tscadc_writel(tscadc_dev, REG_CTRL,
                        (restore | CNTRLREG_TSCSSENB));
 
+       tscadc_writel(tscadc_dev, REG_CLKDIV, tscadc_dev->clk_div);
+
        return 0;
 }
 
@@ -326,7 +332,7 @@ static struct platform_driver ti_tscadc_driver = {
                .name   = "ti_am3359-tscadc",
                .owner  = THIS_MODULE,
                .pm     = TSCADC_PM_OPS,
-               .of_match_table = of_match_ptr(ti_tscadc_dt_ids),
+               .of_match_table = ti_tscadc_dt_ids,
        },
        .probe  = ti_tscadc_probe,
        .remove = ti_tscadc_remove,
index a6755ec..dbb34f9 100644 (file)
@@ -678,7 +678,7 @@ static int timb_probe(struct pci_dev *dev,
        priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;
        if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {
                dev_err(&dev->dev, "Failed to request ctl mem\n");
-               goto err_request;
+               goto err_start;
        }
 
        priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE);
@@ -828,13 +828,10 @@ err_config:
        iounmap(priv->ctl_membase);
 err_ioremap:
        release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
-err_request:
-       pci_set_drvdata(dev, NULL);
 err_start:
        pci_disable_device(dev);
 err_enable:
        kfree(priv);
-       pci_set_drvdata(dev, NULL);
        return -ENODEV;
 }
 
@@ -851,7 +848,6 @@ static void timb_remove(struct pci_dev *dev)
 
        pci_disable_msix(dev);
        pci_disable_device(dev);
-       pci_set_drvdata(dev, NULL);
        kfree(priv);
 }
 
index 5ad4b77..a081b92 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
+#include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/tps6507x.h>
index b8f4864..b7be0b2 100644 (file)
@@ -245,7 +245,7 @@ static struct i2c_driver tps65217_driver = {
        .driver         = {
                .name   = "tps65217",
                .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(tps65217_of_match),
+               .of_match_table = tps65217_of_match,
        },
        .id_table       = tps65217_id_table,
        .probe          = tps65217_probe,
index f54fe4d..ee61fd7 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/of.h>
 
 #include <linux/mfd/core.h>
 #include <linux/mfd/tps6586x.h>
@@ -124,6 +125,7 @@ struct tps6586x {
        struct i2c_client       *client;
        struct regmap           *regmap;
 
+       int                     irq;
        struct irq_chip         irq_chip;
        struct mutex            irq_lock;
        int                     irq_base;
@@ -261,12 +263,23 @@ static void tps6586x_irq_sync_unlock(struct irq_data *data)
        mutex_unlock(&tps6586x->irq_lock);
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int tps6586x_irq_set_wake(struct irq_data *irq_data, unsigned int on)
+{
+       struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
+       return irq_set_irq_wake(tps6586x->irq, on);
+}
+#else
+#define tps6586x_irq_set_wake NULL
+#endif
+
 static struct irq_chip tps6586x_irq_chip = {
        .name = "tps6586x",
        .irq_bus_lock = tps6586x_irq_lock,
        .irq_bus_sync_unlock = tps6586x_irq_sync_unlock,
        .irq_disable = tps6586x_irq_disable,
        .irq_enable = tps6586x_irq_enable,
+       .irq_set_wake = tps6586x_irq_set_wake,
 };
 
 static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq,
@@ -331,6 +344,8 @@ static int tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
        int new_irq_base;
        int irq_num = ARRAY_SIZE(tps6586x_irqs);
 
+       tps6586x->irq = irq;
+
        mutex_init(&tps6586x->irq_lock);
        for (i = 0; i < 5; i++) {
                tps6586x->mask_reg[i] = 0xff;
@@ -360,10 +375,8 @@ static int tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
        ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT,
                                   "tps6586x", tps6586x);
 
-       if (!ret) {
+       if (!ret)
                device_init_wakeup(tps6586x->dev, 1);
-               enable_irq_wake(irq);
-       }
 
        return ret;
 }
index d792772..c0f608e 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/mfd/core.h>
 #include <linux/regmap.h>
 #include <linux/mfd/tps65910.h>
+#include <linux/of.h>
 #include <linux/of_device.h>
 
 static struct resource rtc_resources[] = {
@@ -410,14 +411,10 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
        ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop);
        if (!ret)
                board_info->vmbch_threshold = prop;
-       else if (*chip_id == TPS65911)
-               dev_warn(&client->dev, "VMBCH-Threshold not specified");
 
        ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop);
        if (!ret)
                board_info->vmbch2_threshold = prop;
-       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;
index daf6694..0779d5a 100644 (file)
@@ -565,13 +565,13 @@ static int twl6040_probe(struct i2c_client *client,
                                      twl6040->supplies);
        if (ret != 0) {
                dev_err(&client->dev, "Failed to get supplies: %d\n", ret);
-               goto regulator_get_err;
+               return ret;
        }
 
        ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
        if (ret != 0) {
                dev_err(&client->dev, "Failed to enable supplies: %d\n", ret);
-               goto regulator_get_err;
+               return ret;
        }
 
        twl6040->dev = &client->dev;
@@ -619,7 +619,7 @@ static int twl6040_probe(struct i2c_client *client,
                                        "twl6040_irq_th", twl6040);
        if (ret) {
                dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret);
-               goto thirq_err;
+               goto readyirq_err;
        }
 
        /* dual-access registers controlled by I2C only */
@@ -659,21 +659,14 @@ static int twl6040_probe(struct i2c_client *client,
        ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
                              NULL, 0, NULL);
        if (ret)
-               goto mfd_err;
+               goto readyirq_err;
 
        return 0;
 
-mfd_err:
-       devm_free_irq(&client->dev, twl6040->irq_th, twl6040);
-thirq_err:
-       devm_free_irq(&client->dev, twl6040->irq_ready, twl6040);
 readyirq_err:
        regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
 gpio_err:
        regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
-regulator_get_err:
-       i2c_set_clientdata(client, NULL);
-
        return ret;
 }
 
@@ -684,12 +677,9 @@ static int twl6040_remove(struct i2c_client *client)
        if (twl6040->power_count)
                twl6040_power(twl6040, 0);
 
-       devm_free_irq(&client->dev, twl6040->irq_ready, twl6040);
-       devm_free_irq(&client->dev, twl6040->irq_th, twl6040);
        regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
 
        mfd_remove_devices(&client->dev);
-       i2c_set_clientdata(client, NULL);
 
        regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
 
index d5966e6..0313f83 100644 (file)
@@ -553,6 +553,7 @@ static int ucb1x00_probe(struct mcp *mcp)
        if (ucb->irq_base < 0) {
                dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n",
                        ucb->irq_base);
+               ret = ucb->irq_base;
                goto err_irq_alloc;
        }
 
index 802dd3c..1e9a4b2 100644 (file)
@@ -903,7 +903,6 @@ static const struct reg_default wm5102_reg_default[] = {
        { 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 */ 
index 3113e39..bf8b3b5 100644 (file)
@@ -243,6 +243,12 @@ int wm5110_patch(struct arizona *arizona)
 EXPORT_SYMBOL_GPL(wm5110_patch);
 
 static const struct regmap_irq wm5110_aod_irqs[ARIZONA_NUM_IRQ] = {
+       [ARIZONA_IRQ_MICD_CLAMP_FALL] = {
+               .mask = ARIZONA_MICD_CLAMP_FALL_EINT1
+       },
+       [ARIZONA_IRQ_MICD_CLAMP_RISE] = {
+               .mask = ARIZONA_MICD_CLAMP_RISE_EINT1
+       },
        [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 },
@@ -505,6 +511,7 @@ static const struct reg_default wm5110_reg_default[] = {
        { 0x00000293, 0x0000 },    /* R659   - Accessory Detect Mode 1 */
        { 0x0000029B, 0x0020 },    /* R667   - Headphone Detect 1 */
        { 0x0000029C, 0x0000 },    /* R668   - Headphone Detect 2 */
+       { 0x000002A2, 0x0000 },    /* R674   - Micd clamp control */
        { 0x000002A3, 0x1102 },    /* R675   - Mic Detect 1 */
        { 0x000002A4, 0x009F },    /* R676   - Mic Detect 2 */
        { 0x000002A5, 0x0000 },    /* R677   - Mic Detect 3 */
@@ -592,7 +599,7 @@ static const struct reg_default wm5110_reg_default[] = {
        { 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 */
+       { 0x00000458, 0x0000 },    /* 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 */
@@ -1204,7 +1211,6 @@ static const struct reg_default wm5110_reg_default[] = {
        { 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 */
@@ -1440,6 +1446,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
        case ARIZONA_ACCESSORY_DETECT_MODE_1:
        case ARIZONA_HEADPHONE_DETECT_1:
        case ARIZONA_HEADPHONE_DETECT_2:
+       case ARIZONA_MICD_CLAMP_CONTROL:
        case ARIZONA_MIC_DETECT_1:
        case ARIZONA_MIC_DETECT_2:
        case ARIZONA_MIC_DETECT_3:
@@ -2291,21 +2298,37 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
        case ARIZONA_DSP1_STATUS_1:
        case ARIZONA_DSP1_STATUS_2:
        case ARIZONA_DSP1_STATUS_3:
+       case ARIZONA_DSP1_SCRATCH_0:
+       case ARIZONA_DSP1_SCRATCH_1:
+       case ARIZONA_DSP1_SCRATCH_2:
+       case ARIZONA_DSP1_SCRATCH_3:
        case ARIZONA_DSP2_CONTROL_1:
        case ARIZONA_DSP2_CLOCKING_1:
        case ARIZONA_DSP2_STATUS_1:
        case ARIZONA_DSP2_STATUS_2:
        case ARIZONA_DSP2_STATUS_3:
+       case ARIZONA_DSP2_SCRATCH_0:
+       case ARIZONA_DSP2_SCRATCH_1:
+       case ARIZONA_DSP2_SCRATCH_2:
+       case ARIZONA_DSP2_SCRATCH_3:
        case ARIZONA_DSP3_CONTROL_1:
        case ARIZONA_DSP3_CLOCKING_1:
        case ARIZONA_DSP3_STATUS_1:
        case ARIZONA_DSP3_STATUS_2:
        case ARIZONA_DSP3_STATUS_3:
+       case ARIZONA_DSP3_SCRATCH_0:
+       case ARIZONA_DSP3_SCRATCH_1:
+       case ARIZONA_DSP3_SCRATCH_2:
+       case ARIZONA_DSP3_SCRATCH_3:
        case ARIZONA_DSP4_CONTROL_1:
        case ARIZONA_DSP4_CLOCKING_1:
        case ARIZONA_DSP4_STATUS_1:
        case ARIZONA_DSP4_STATUS_2:
        case ARIZONA_DSP4_STATUS_3:
+       case ARIZONA_DSP4_SCRATCH_0:
+       case ARIZONA_DSP4_SCRATCH_1:
+       case ARIZONA_DSP4_SCRATCH_2:
+       case ARIZONA_DSP4_SCRATCH_3:
                return true;
        default:
                return false;
@@ -2347,25 +2370,41 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg)
        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_FX_CTRL2:
        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_DSP1_STATUS_3:
+       case ARIZONA_DSP1_SCRATCH_0:
+       case ARIZONA_DSP1_SCRATCH_1:
+       case ARIZONA_DSP1_SCRATCH_2:
+       case ARIZONA_DSP1_SCRATCH_3:
        case ARIZONA_DSP2_STATUS_1:
        case ARIZONA_DSP2_STATUS_2:
        case ARIZONA_DSP2_STATUS_3:
+       case ARIZONA_DSP2_SCRATCH_0:
+       case ARIZONA_DSP2_SCRATCH_1:
+       case ARIZONA_DSP2_SCRATCH_2:
+       case ARIZONA_DSP2_SCRATCH_3:
        case ARIZONA_DSP3_STATUS_1:
        case ARIZONA_DSP3_STATUS_2:
        case ARIZONA_DSP3_STATUS_3:
+       case ARIZONA_DSP3_SCRATCH_0:
+       case ARIZONA_DSP3_SCRATCH_1:
+       case ARIZONA_DSP3_SCRATCH_2:
+       case ARIZONA_DSP3_SCRATCH_3:
        case ARIZONA_DSP4_STATUS_1:
        case ARIZONA_DSP4_STATUS_2:
        case ARIZONA_DSP4_STATUS_3:
+       case ARIZONA_DSP4_SCRATCH_0:
+       case ARIZONA_DSP4_SCRATCH_1:
+       case ARIZONA_DSP4_SCRATCH_2:
+       case ARIZONA_DSP4_SCRATCH_3:
                return true;
        default:
                return false;
index e1c283e..0308275 100644 (file)
 
 #include "wm8994.h"
 
-/**
- * wm8994_reg_read: Read a single WM8994 register.
- *
- * @wm8994: Device to read from.
- * @reg: Register to read.
- */
-int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg)
-{
-       unsigned int val;
-       int ret;
-
-       ret = regmap_read(wm8994->regmap, reg, &val);
-
-       if (ret < 0)
-               return ret;
-       else
-               return val;
-}
-EXPORT_SYMBOL_GPL(wm8994_reg_read);
-
-/**
- * wm8994_bulk_read: Read multiple WM8994 registers
- *
- * @wm8994: Device to read from
- * @reg: First register
- * @count: Number of registers
- * @buf: Buffer to fill.  The data will be returned big endian.
- */
-int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
-                    int count, u16 *buf)
-{
-       return regmap_bulk_read(wm8994->regmap, reg, buf, count);
-}
-
-/**
- * wm8994_reg_write: Write a single WM8994 register.
- *
- * @wm8994: Device to write to.
- * @reg: Register to write to.
- * @val: Value to write.
- */
-int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
-                    unsigned short val)
-{
-       return regmap_write(wm8994->regmap, reg, val);
-}
-EXPORT_SYMBOL_GPL(wm8994_reg_write);
-
-/**
- * wm8994_bulk_write: Write multiple WM8994 registers
- *
- * @wm8994: Device to write to
- * @reg: First register
- * @count: Number of registers
- * @buf: Buffer to write from.  Data must be big-endian formatted.
- */
-int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg,
-                     int count, const u16 *buf)
-{
-       return regmap_raw_write(wm8994->regmap, reg, buf, count * sizeof(u16));
-}
-EXPORT_SYMBOL_GPL(wm8994_bulk_write);
-
-/**
- * wm8994_set_bits: Set the value of a bitfield in a WM8994 register
- *
- * @wm8994: Device to write to.
- * @reg: Register to write to.
- * @mask: Mask of bits to set.
- * @val: Value to set (unshifted)
- */
-int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
-                   unsigned short mask, unsigned short val)
-{
-       return regmap_update_bits(wm8994->regmap, reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(wm8994_set_bits);
-
 static struct mfd_cell wm8994_regulator_devs[] = {
        {
                .name = "wm8994-ldo",
index e760715..a3e291d 100644 (file)
@@ -381,19 +381,6 @@ config HMC6352
          This driver provides support for the Honeywell HMC6352 compass,
          providing configuration and heading data via sysfs.
 
-config EP93XX_PWM
-       tristate "EP93xx PWM support"
-       depends on ARCH_EP93XX
-       help
-         This option enables device driver support for the PWM channels
-         on the Cirrus EP93xx processors.  The EP9307 chip only has one
-         PWM channel all the others have two, the second channel is an
-         alternate function of the EGPIO14 pin.  A sysfs interface is
-         provided to control the PWM channels.
-
-         To compile this driver as a module, choose M here: the module will
-         be called ep93xx_pwm.
-
 config DS1682
        tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm"
        depends on I2C
index 0b7ea3e..f45473e 100644 (file)
@@ -33,7 +33,6 @@ obj-$(CONFIG_APDS9802ALS)     += apds9802als.o
 obj-$(CONFIG_ISL29003)         += isl29003.o
 obj-$(CONFIG_ISL29020)         += isl29020.o
 obj-$(CONFIG_SENSORS_TSL2550)  += tsl2550.o
-obj-$(CONFIG_EP93XX_PWM)       += ep93xx_pwm.o
 obj-$(CONFIG_DS1682)           += ds1682.o
 obj-$(CONFIG_TI_DAC7512)       += ti_dac7512.o
 obj-$(CONFIG_C2PORT)           += c2port/
index 08b18f3..9e2b985 100644 (file)
@@ -633,8 +633,7 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf)
        struct dma_async_tx_descriptor *tx;
        dma_cookie_t cookie;
        dma_addr_t dst, src;
-       unsigned long dma_flags = DMA_COMPL_SKIP_DEST_UNMAP |
-                                 DMA_COMPL_SKIP_SRC_UNMAP;
+       unsigned long dma_flags = 0;
 
        dst_sg = buf->vb.sglist;
        dst_nents = buf->vb.sglen;
index 94b8a33..d87f77f 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/jiffies.h>
 #include <linux/of.h>
 #include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
 
 /*
  * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c
deleted file mode 100644 (file)
index cdb67a9..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- *  Simple PWM driver for EP93XX
- *
- *     (c) Copyright 2009  Matthieu Crapet <mcrapet@gmail.com>
- *     (c) Copyright 2009  H Hartley Sweeten <hsweeten@visionengravers.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.
- *
- *  EP9307 has only one channel:
- *    - PWMOUT
- *
- *  EP9301/02/12/15 have two channels:
- *    - PWMOUT
- *    - PWMOUT1 (alternate function for EGPIO14)
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/io.h>
-
-#include <mach/platform.h>
-
-#define EP93XX_PWMx_TERM_COUNT 0x00
-#define EP93XX_PWMx_DUTY_CYCLE 0x04
-#define EP93XX_PWMx_ENABLE     0x08
-#define EP93XX_PWMx_INVERT     0x0C
-
-#define EP93XX_PWM_MAX_COUNT   0xFFFF
-
-struct ep93xx_pwm {
-       void __iomem    *mmio_base;
-       struct clk      *clk;
-       u32             duty_percent;
-};
-
-/*
- * /sys/devices/platform/ep93xx-pwm.N
- *   /min_freq      read-only   minimum pwm output frequency
- *   /max_req       read-only   maximum pwm output frequency
- *   /freq          read-write  pwm output frequency (0 = disable output)
- *   /duty_percent  read-write  pwm duty cycle percent (1..99)
- *   /invert        read-write  invert pwm output
- */
-
-static ssize_t ep93xx_pwm_get_min_freq(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
-       unsigned long rate = clk_get_rate(pwm->clk);
-
-       return sprintf(buf, "%ld\n", rate / (EP93XX_PWM_MAX_COUNT + 1));
-}
-
-static ssize_t ep93xx_pwm_get_max_freq(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
-       unsigned long rate = clk_get_rate(pwm->clk);
-
-       return sprintf(buf, "%ld\n", rate / 2);
-}
-
-static ssize_t ep93xx_pwm_get_freq(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
-
-       if (readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) {
-               unsigned long rate = clk_get_rate(pwm->clk);
-               u16 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
-
-               return sprintf(buf, "%ld\n", rate / (term + 1));
-       } else {
-               return sprintf(buf, "disabled\n");
-       }
-}
-
-static ssize_t ep93xx_pwm_set_freq(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
-       long val;
-       int err;
-
-       err = kstrtol(buf, 10, &val);
-       if (err)
-               return -EINVAL;
-
-       if (val == 0) {
-               writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE);
-       } else if (val <= (clk_get_rate(pwm->clk) / 2)) {
-               u32 term, duty;
-
-               val = (clk_get_rate(pwm->clk) / val) - 1;
-               if (val > EP93XX_PWM_MAX_COUNT)
-                       val = EP93XX_PWM_MAX_COUNT;
-               if (val < 1)
-                       val = 1;
-
-               term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
-               duty = ((val + 1) * pwm->duty_percent / 100) - 1;
-
-               /* If pwm is running, order is important */
-               if (val > term) {
-                       writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
-                       writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
-               } else {
-                       writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
-                       writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
-               }
-
-               if (!readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1)
-                       writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE);
-       } else {
-               return -EINVAL;
-       }
-
-       return count;
-}
-
-static ssize_t ep93xx_pwm_get_duty_percent(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
-
-       return sprintf(buf, "%d\n", pwm->duty_percent);
-}
-
-static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
-       long val;
-       int err;
-
-       err = kstrtol(buf, 10, &val);
-       if (err)
-               return -EINVAL;
-
-       if (val > 0 && val < 100) {
-               u32 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
-               u32 duty = ((term + 1) * val / 100) - 1;
-
-               writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
-               pwm->duty_percent = val;
-               return count;
-       }
-
-       return -EINVAL;
-}
-
-static ssize_t ep93xx_pwm_get_invert(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
-       int inverted = readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1;
-
-       return sprintf(buf, "%d\n", inverted);
-}
-
-static ssize_t ep93xx_pwm_set_invert(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
-       long val;
-       int err;
-
-       err = kstrtol(buf, 10, &val);
-       if (err)
-               return -EINVAL;
-
-       if (val == 0)
-               writel(0x0, pwm->mmio_base + EP93XX_PWMx_INVERT);
-       else if (val == 1)
-               writel(0x1, pwm->mmio_base + EP93XX_PWMx_INVERT);
-       else
-               return -EINVAL;
-
-       return count;
-}
-
-static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL);
-static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL);
-static DEVICE_ATTR(freq, S_IWUSR | S_IRUGO,
-                  ep93xx_pwm_get_freq, ep93xx_pwm_set_freq);
-static DEVICE_ATTR(duty_percent, S_IWUSR | S_IRUGO,
-                  ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent);
-static DEVICE_ATTR(invert, S_IWUSR | S_IRUGO,
-                  ep93xx_pwm_get_invert, ep93xx_pwm_set_invert);
-
-static struct attribute *ep93xx_pwm_attrs[] = {
-       &dev_attr_min_freq.attr,
-       &dev_attr_max_freq.attr,
-       &dev_attr_freq.attr,
-       &dev_attr_duty_percent.attr,
-       &dev_attr_invert.attr,
-       NULL
-};
-
-static const struct attribute_group ep93xx_pwm_sysfs_files = {
-       .attrs  = ep93xx_pwm_attrs,
-};
-
-static int ep93xx_pwm_probe(struct platform_device *pdev)
-{
-       struct ep93xx_pwm *pwm;
-       struct resource *res;
-       int ret;
-
-       pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
-       if (!pwm)
-               return -ENOMEM;
-
-       pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk");
-       if (IS_ERR(pwm->clk))
-               return PTR_ERR(pwm->clk);
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       pwm->mmio_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(pwm->mmio_base))
-               return PTR_ERR(pwm->mmio_base);
-
-       ret = ep93xx_pwm_acquire_gpio(pdev);
-       if (ret)
-               return ret;
-
-       ret = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
-       if (ret) {
-               ep93xx_pwm_release_gpio(pdev);
-               return ret;
-       }
-
-       pwm->duty_percent = 50;
-
-       /* disable pwm at startup. Avoids zero value. */
-       writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE);
-       writel(EP93XX_PWM_MAX_COUNT, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
-       writel(EP93XX_PWM_MAX_COUNT/2, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
-
-       clk_enable(pwm->clk);
-
-       platform_set_drvdata(pdev, pwm);
-       return 0;
-}
-
-static int ep93xx_pwm_remove(struct platform_device *pdev)
-{
-       struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
-
-       writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE);
-       clk_disable(pwm->clk);
-       sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
-       ep93xx_pwm_release_gpio(pdev);
-
-       return 0;
-}
-
-static struct platform_driver ep93xx_pwm_driver = {
-       .driver         = {
-               .name   = "ep93xx-pwm",
-               .owner  = THIS_MODULE,
-       },
-       .probe          = ep93xx_pwm_probe,
-       .remove         = ep93xx_pwm_remove,
-};
-module_platform_driver(ep93xx_pwm_driver);
-
-MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, "
-             "H Hartley Sweeten <hsweeten@visionengravers.com>");
-MODULE_DESCRIPTION("EP93xx PWM driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ep93xx-pwm");
index 914cc9b..8aa42e7 100644 (file)
@@ -493,7 +493,7 @@ static int mic_remove_device(struct mic_device_desc __iomem *d,
                        ioread8(&dc->config_change), ioread8(&d->type), mvdev);
 
                status = ioread8(&d->status);
-               INIT_COMPLETION(mvdev->reset_done);
+               reinit_completion(&mvdev->reset_done);
                unregister_virtio_device(&mvdev->vdev);
                mic_free_card_irq(mvdev->virtio_cookie, mvdev);
                if (status & VIRTIO_CONFIG_S_DRIVER_OK)
index b079c65..7558d91 100644 (file)
@@ -38,7 +38,7 @@ static void mic_reset(struct mic_device *mdev)
 
 #define MIC_RESET_TO (45)
 
-       INIT_COMPLETION(mdev->reset_wait);
+       reinit_completion(&mdev->reset_wait);
        mdev->ops->reset_fw_ready(mdev);
        mdev->ops->reset(mdev);
 
index 83907c7..96853a0 100644 (file)
@@ -218,7 +218,7 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
 
        pr_debug("%s", __func__);
 
-       INIT_COMPLETION(kim_gdata->kim_rcvd);
+       reinit_completion(&kim_gdata->kim_rcvd);
        if (4 != st_int_write(kim_gdata->core_data, read_ver_cmd, 4)) {
                pr_err("kim: couldn't write 4 bytes");
                return -EIO;
@@ -229,7 +229,7 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
                pr_err(" waiting for ver info- timed out ");
                return -ETIMEDOUT;
        }
-       INIT_COMPLETION(kim_gdata->kim_rcvd);
+       reinit_completion(&kim_gdata->kim_rcvd);
        /* the positions 12 & 13 in the response buffer provide with the
         * chip, major & minor numbers
         */
@@ -362,7 +362,7 @@ static long download_firmware(struct kim_data_s *kim_gdata)
                        /* reinit completion before sending for the
                         * relevant wait
                         */
-                       INIT_COMPLETION(kim_gdata->kim_rcvd);
+                       reinit_completion(&kim_gdata->kim_rcvd);
 
                        /*
                         * Free space found in uart buffer, call st_int_write
@@ -398,7 +398,7 @@ static long download_firmware(struct kim_data_s *kim_gdata)
                                release_firmware(kim_gdata->fw_entry);
                                return -ETIMEDOUT;
                        }
-                       INIT_COMPLETION(kim_gdata->kim_rcvd);
+                       reinit_completion(&kim_gdata->kim_rcvd);
                        break;
                case ACTION_DELAY:      /* sleep */
                        pr_info("sleep command in scr");
@@ -474,7 +474,7 @@ long st_kim_start(void *kim_data)
                gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH);
                mdelay(100);
                /* re-initialize the completion */
-               INIT_COMPLETION(kim_gdata->ldisc_installed);
+               reinit_completion(&kim_gdata->ldisc_installed);
                /* send notification to UIM */
                kim_gdata->ldisc_install = 1;
                pr_info("ldisc_install = 1");
@@ -525,7 +525,7 @@ long st_kim_stop(void *kim_data)
                kim_gdata->kim_pdev->dev.platform_data;
        struct tty_struct       *tty = kim_gdata->core_data->tty;
 
-       INIT_COMPLETION(kim_gdata->ldisc_installed);
+       reinit_completion(&kim_gdata->ldisc_installed);
 
        if (tty) {      /* can be called before ldisc is installed */
                /* Flush any pending characters in the driver and discipline. */
index 1a3163f..29d5d98 100644 (file)
@@ -2448,7 +2448,6 @@ static int _mmc_blk_suspend(struct mmc_card *card)
        struct mmc_blk_data *md = mmc_get_drvdata(card);
 
        if (md) {
-               pm_runtime_get_sync(&card->dev);
                mmc_queue_suspend(&md->queue);
                list_for_each_entry(part_md, &md->part, part) {
                        mmc_queue_suspend(&part_md->queue);
@@ -2483,7 +2482,6 @@ static int mmc_blk_resume(struct mmc_card *card)
                list_for_each_entry(part_md, &md->part, part) {
                        mmc_queue_resume(&part_md->queue);
                }
-               pm_runtime_put(&card->dev);
        }
        return 0;
 }
index 3e227bd..64145a3 100644 (file)
@@ -342,7 +342,7 @@ int mmc_add_card(struct mmc_card *card)
                break;
        }
 
-       if (mmc_sd_card_uhs(card) &&
+       if (mmc_card_uhs(card) &&
                (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
                uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];
 
index bf18b6b..57a2b40 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/log2.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_wakeup.h>
 #include <linux/suspend.h>
 #include <linux/fault-inject.h>
 #include <linux/random.h>
@@ -301,7 +302,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
        }
 
        err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                       EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal);
+                       EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true);
        if (err) {
                pr_warn("%s: Error %d starting bkops\n",
                        mmc_hostname(card->host), err);
@@ -918,31 +919,6 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
 EXPORT_SYMBOL(__mmc_claim_host);
 
 /**
- *     mmc_try_claim_host - try exclusively to claim a host
- *     @host: mmc host to claim
- *
- *     Returns %1 if the host is claimed, %0 otherwise.
- */
-int mmc_try_claim_host(struct mmc_host *host)
-{
-       int claimed_host = 0;
-       unsigned long flags;
-
-       spin_lock_irqsave(&host->lock, flags);
-       if (!host->claimed || host->claimer == current) {
-               host->claimed = 1;
-               host->claimer = current;
-               host->claim_cnt += 1;
-               claimed_host = 1;
-       }
-       spin_unlock_irqrestore(&host->lock, flags);
-       if (host->ops->enable && claimed_host && host->claim_cnt == 1)
-               host->ops->enable(host);
-       return claimed_host;
-}
-EXPORT_SYMBOL(mmc_try_claim_host);
-
-/**
  *     mmc_release_host - release a host
  *     @host: mmc host to release
  *
@@ -1382,22 +1358,31 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
 {
        int bit;
 
-       ocr &= host->ocr_avail;
+       /*
+        * Sanity check the voltages that the card claims to
+        * support.
+        */
+       if (ocr & 0x7F) {
+               dev_warn(mmc_dev(host),
+               "card claims to support voltages below defined range\n");
+               ocr &= ~0x7F;
+       }
 
-       bit = ffs(ocr);
-       if (bit) {
-               bit -= 1;
+       ocr &= host->ocr_avail;
+       if (!ocr) {
+               dev_warn(mmc_dev(host), "no support for card's volts\n");
+               return 0;
+       }
 
+       if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
+               bit = ffs(ocr) - 1;
                ocr &= 3 << bit;
-
-               mmc_host_clk_hold(host);
-               host->ios.vdd = bit;
-               mmc_set_ios(host);
-               mmc_host_clk_release(host);
+               mmc_power_cycle(host, ocr);
        } else {
-               pr_warning("%s: host doesn't support card's voltages\n",
-                               mmc_hostname(host));
-               ocr = 0;
+               bit = fls(ocr) - 1;
+               ocr &= 3 << bit;
+               if (bit != host->ios.vdd)
+                       dev_warn(mmc_dev(host), "exceeding card's volts\n");
        }
 
        return ocr;
@@ -1422,7 +1407,7 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
 
 }
 
-int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
+int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
 {
        struct mmc_command cmd = {0};
        int err = 0;
@@ -1504,7 +1489,7 @@ power_cycle:
        if (err) {
                pr_debug("%s: Signal voltage switch failed, "
                        "power cycling card\n", mmc_hostname(host));
-               mmc_power_cycle(host);
+               mmc_power_cycle(host, ocr);
        }
 
        mmc_host_clk_release(host);
@@ -1545,22 +1530,14 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
  * If a host does all the power sequencing itself, ignore the
  * initial MMC_POWER_UP stage.
  */
-void mmc_power_up(struct mmc_host *host)
+void mmc_power_up(struct mmc_host *host, u32 ocr)
 {
-       int bit;
-
        if (host->ios.power_mode == MMC_POWER_ON)
                return;
 
        mmc_host_clk_hold(host);
 
-       /* If ocr is set, we use it */
-       if (host->ocr)
-               bit = ffs(host->ocr) - 1;
-       else
-               bit = fls(host->ocr_avail) - 1;
-
-       host->ios.vdd = bit;
+       host->ios.vdd = fls(ocr) - 1;
        if (mmc_host_is_spi(host))
                host->ios.chip_select = MMC_CS_HIGH;
        else
@@ -1604,13 +1581,6 @@ void mmc_power_off(struct mmc_host *host)
        host->ios.clock = 0;
        host->ios.vdd = 0;
 
-
-       /*
-        * Reset ocr mask to be the highest possible voltage supported for
-        * this mmc host. This value will be used at next power up.
-        */
-       host->ocr = 1 << (fls(host->ocr_avail) - 1);
-
        if (!mmc_host_is_spi(host)) {
                host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
                host->ios.chip_select = MMC_CS_DONTCARE;
@@ -1630,12 +1600,12 @@ void mmc_power_off(struct mmc_host *host)
        mmc_host_clk_release(host);
 }
 
-void mmc_power_cycle(struct mmc_host *host)
+void mmc_power_cycle(struct mmc_host *host, u32 ocr)
 {
        mmc_power_off(host);
        /* Wait at least 1 ms according to SD spec */
        mmc_delay(1);
-       mmc_power_up(host);
+       mmc_power_up(host, ocr);
 }
 
 /*
@@ -1723,6 +1693,28 @@ void mmc_detach_bus(struct mmc_host *host)
        mmc_bus_put(host);
 }
 
+static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
+                               bool cd_irq)
+{
+#ifdef CONFIG_MMC_DEBUG
+       unsigned long flags;
+       spin_lock_irqsave(&host->lock, flags);
+       WARN_ON(host->removed);
+       spin_unlock_irqrestore(&host->lock, flags);
+#endif
+
+       /*
+        * If the device is configured as wakeup, we prevent a new sleep for
+        * 5 s to give provision for user space to consume the event.
+        */
+       if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) &&
+               device_can_wakeup(mmc_dev(host)))
+               pm_wakeup_event(mmc_dev(host), 5000);
+
+       host->detect_change = 1;
+       mmc_schedule_delayed_work(&host->detect, delay);
+}
+
 /**
  *     mmc_detect_change - process change of state on a MMC socket
  *     @host: host which changed state.
@@ -1735,16 +1727,8 @@ void mmc_detach_bus(struct mmc_host *host)
  */
 void mmc_detect_change(struct mmc_host *host, unsigned long delay)
 {
-#ifdef CONFIG_MMC_DEBUG
-       unsigned long flags;
-       spin_lock_irqsave(&host->lock, flags);
-       WARN_ON(host->removed);
-       spin_unlock_irqrestore(&host->lock, flags);
-#endif
-       host->detect_change = 1;
-       mmc_schedule_delayed_work(&host->detect, delay);
+       _mmc_detect_change(host, delay, true);
 }
-
 EXPORT_SYMBOL(mmc_detect_change);
 
 void mmc_init_erase(struct mmc_card *card)
@@ -2334,7 +2318,7 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
        pr_info("%s: %s: trying to init card at %u Hz\n",
                mmc_hostname(host), __func__, host->f_init);
 #endif
-       mmc_power_up(host);
+       mmc_power_up(host, host->ocr_avail);
 
        /*
         * Some eMMCs (with VCCQ always on) may not be reset after power up, so
@@ -2423,7 +2407,7 @@ int mmc_detect_card_removed(struct mmc_host *host)
                         * rescan handle the card removal.
                         */
                        cancel_delayed_work(&host->detect);
-                       mmc_detect_change(host, 0);
+                       _mmc_detect_change(host, 0, false);
                }
        }
 
@@ -2504,8 +2488,8 @@ void mmc_start_host(struct mmc_host *host)
        if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
                mmc_power_off(host);
        else
-               mmc_power_up(host);
-       mmc_detect_change(host, 0);
+               mmc_power_up(host, host->ocr_avail);
+       _mmc_detect_change(host, 0, false);
 }
 
 void mmc_stop_host(struct mmc_host *host)
@@ -2583,7 +2567,7 @@ int mmc_power_restore_host(struct mmc_host *host)
                return -EINVAL;
        }
 
-       mmc_power_up(host);
+       mmc_power_up(host, host->card->ocr);
        ret = host->bus_ops->power_restore(host);
 
        mmc_bus_put(host);
@@ -2657,28 +2641,6 @@ EXPORT_SYMBOL(mmc_cache_ctrl);
 
 #ifdef CONFIG_PM
 
-/**
- *     mmc_suspend_host - suspend a host
- *     @host: mmc host
- */
-int mmc_suspend_host(struct mmc_host *host)
-{
-       /* This function is deprecated */
-       return 0;
-}
-EXPORT_SYMBOL(mmc_suspend_host);
-
-/**
- *     mmc_resume_host - resume a previously suspended host
- *     @host: mmc host
- */
-int mmc_resume_host(struct mmc_host *host)
-{
-       /* This function is deprecated */
-       return 0;
-}
-EXPORT_SYMBOL(mmc_resume_host);
-
 /* Do the card removal on suspend if card is assumed removeable
  * Do that in pm notifier while userspace isn't yet frozen, so we will be able
    to sync the card.
@@ -2724,7 +2686,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
                spin_lock_irqsave(&host->lock, flags);
                host->rescan_disable = 0;
                spin_unlock_irqrestore(&host->lock, flags);
-               mmc_detect_change(host, 0);
+               _mmc_detect_change(host, 0, false);
 
        }
 
index 5345d15..443a584 100644 (file)
@@ -42,13 +42,13 @@ void mmc_set_ungated(struct mmc_host *host);
 void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
 void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
 u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
-int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
+int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);
 int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
 void mmc_set_timing(struct mmc_host *host, unsigned int timing);
 void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
-void mmc_power_up(struct mmc_host *host);
+void mmc_power_up(struct mmc_host *host, u32 ocr);
 void mmc_power_off(struct mmc_host *host);
-void mmc_power_cycle(struct mmc_host *host);
+void mmc_power_cycle(struct mmc_host *host, u32 ocr);
 
 static inline void mmc_delay(unsigned int ms)
 {
index 6d02012..f631f5a 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/stat.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
@@ -934,6 +935,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                        goto err;
                }
 
+               card->ocr = ocr;
                card->type = MMC_TYPE_MMC;
                card->rca = 1;
                memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
@@ -1404,9 +1406,9 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
        if (notify_type == EXT_CSD_POWER_OFF_LONG)
                timeout = card->ext_csd.power_off_longtime;
 
-       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                        EXT_CSD_POWER_OFF_NOTIFICATION,
-                        notify_type, timeout);
+       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                       EXT_CSD_POWER_OFF_NOTIFICATION,
+                       notify_type, timeout, true, false);
        if (err)
                pr_err("%s: Power Off Notification timed out, %u\n",
                       mmc_hostname(card->host), timeout);
@@ -1477,6 +1479,9 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
 
        mmc_claim_host(host);
 
+       if (mmc_card_suspended(host->card))
+               goto out;
+
        if (mmc_card_doing_bkops(host->card)) {
                err = mmc_stop_bkops(host->card);
                if (err)
@@ -1496,51 +1501,93 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
                err = mmc_deselect_cards(host);
        host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
 
-       if (!err)
+       if (!err) {
                mmc_power_off(host);
+               mmc_card_set_suspended(host->card);
+       }
 out:
        mmc_release_host(host);
        return err;
 }
 
 /*
- * Suspend callback from host.
+ * Suspend callback
  */
 static int mmc_suspend(struct mmc_host *host)
 {
-       return _mmc_suspend(host, true);
-}
+       int err;
 
-/*
- * Shutdown callback
- */
-static int mmc_shutdown(struct mmc_host *host)
-{
-       return _mmc_suspend(host, false);
+       err = _mmc_suspend(host, true);
+       if (!err) {
+               pm_runtime_disable(&host->card->dev);
+               pm_runtime_set_suspended(&host->card->dev);
+       }
+
+       return err;
 }
 
 /*
- * Resume callback from host.
- *
  * This function tries to determine if the same card is still present
  * and, if so, restore all state to it.
  */
-static int mmc_resume(struct mmc_host *host)
+static int _mmc_resume(struct mmc_host *host)
 {
-       int err;
+       int err = 0;
 
        BUG_ON(!host);
        BUG_ON(!host->card);
 
        mmc_claim_host(host);
-       mmc_power_up(host);
-       mmc_select_voltage(host, host->ocr);
-       err = mmc_init_card(host, host->ocr, host->card);
+
+       if (!mmc_card_suspended(host->card))
+               goto out;
+
+       mmc_power_up(host, host->card->ocr);
+       err = mmc_init_card(host, host->card->ocr, host->card);
+       mmc_card_clr_suspended(host->card);
+
+out:
        mmc_release_host(host);
+       return err;
+}
+
+/*
+ * Shutdown callback
+ */
+static int mmc_shutdown(struct mmc_host *host)
+{
+       int err = 0;
+
+       /*
+        * In a specific case for poweroff notify, we need to resume the card
+        * before we can shutdown it properly.
+        */
+       if (mmc_can_poweroff_notify(host->card) &&
+               !(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
+               err = _mmc_resume(host);
+
+       if (!err)
+               err = _mmc_suspend(host, false);
 
        return err;
 }
 
+/*
+ * Callback for resume.
+ */
+static int mmc_resume(struct mmc_host *host)
+{
+       int err = 0;
+
+       if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
+               err = _mmc_resume(host);
+               pm_runtime_set_active(&host->card->dev);
+               pm_runtime_mark_last_busy(&host->card->dev);
+       }
+       pm_runtime_enable(&host->card->dev);
+
+       return err;
+}
 
 /*
  * Callback for runtime_suspend.
@@ -1552,18 +1599,11 @@ static int mmc_runtime_suspend(struct mmc_host *host)
        if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
                return 0;
 
-       mmc_claim_host(host);
-
-       err = mmc_suspend(host);
-       if (err) {
+       err = _mmc_suspend(host, true);
+       if (err)
                pr_err("%s: error %d doing aggessive suspend\n",
                        mmc_hostname(host), err);
-               goto out;
-       }
-       mmc_power_off(host);
 
-out:
-       mmc_release_host(host);
        return err;
 }
 
@@ -1574,18 +1614,14 @@ static int mmc_runtime_resume(struct mmc_host *host)
 {
        int err;
 
-       if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+       if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))
                return 0;
 
-       mmc_claim_host(host);
-
-       mmc_power_up(host);
-       err = mmc_resume(host);
+       err = _mmc_resume(host);
        if (err)
                pr_err("%s: error %d doing aggessive resume\n",
                        mmc_hostname(host), err);
 
-       mmc_release_host(host);
        return 0;
 }
 
@@ -1595,7 +1631,7 @@ static int mmc_power_restore(struct mmc_host *host)
 
        host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
        mmc_claim_host(host);
-       ret = mmc_init_card(host, host->ocr, host->card);
+       ret = mmc_init_card(host, host->card->ocr, host->card);
        mmc_release_host(host);
 
        return ret;
@@ -1640,7 +1676,7 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
 int mmc_attach_mmc(struct mmc_host *host)
 {
        int err;
-       u32 ocr;
+       u32 ocr, rocr;
 
        BUG_ON(!host);
        WARN_ON(!host->claimed);
@@ -1666,23 +1702,12 @@ int mmc_attach_mmc(struct mmc_host *host)
                        goto err;
        }
 
-       /*
-        * Sanity check the voltages that the card claims to
-        * support.
-        */
-       if (ocr & 0x7F) {
-               pr_warning("%s: card claims to support voltages "
-                      "below the defined range. These will be ignored.\n",
-                      mmc_hostname(host));
-               ocr &= ~0x7F;
-       }
-
-       host->ocr = mmc_select_voltage(host, ocr);
+       rocr = mmc_select_voltage(host, ocr);
 
        /*
         * Can we support the voltage of the card?
         */
-       if (!host->ocr) {
+       if (!rocr) {
                err = -EINVAL;
                goto err;
        }
@@ -1690,7 +1715,7 @@ int mmc_attach_mmc(struct mmc_host *host)
        /*
         * Detect and init the card.
         */
-       err = mmc_init_card(host, host->ocr, NULL);
+       err = mmc_init_card(host, rocr, NULL);
        if (err)
                goto err;
 
index ef18348..e5b5eeb 100644 (file)
 
 #define MMC_OPS_TIMEOUT_MS     (10 * 60 * 1000) /* 10 minute timeout */
 
+static inline int __mmc_send_status(struct mmc_card *card, u32 *status,
+                                   bool ignore_crc)
+{
+       int err;
+       struct mmc_command cmd = {0};
+
+       BUG_ON(!card);
+       BUG_ON(!card->host);
+
+       cmd.opcode = MMC_SEND_STATUS;
+       if (!mmc_host_is_spi(card->host))
+               cmd.arg = card->rca << 16;
+       cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+       if (ignore_crc)
+               cmd.flags &= ~MMC_RSP_CRC;
+
+       err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+       if (err)
+               return err;
+
+       /* NOTE: callers are required to understand the difference
+        * between "native" and SPI format status words!
+        */
+       if (status)
+               *status = cmd.resp[0];
+
+       return 0;
+}
+
+int mmc_send_status(struct mmc_card *card, u32 *status)
+{
+       return __mmc_send_status(card, status, false);
+}
+
 static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
 {
        int err;
@@ -370,16 +404,18 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
  *     @timeout_ms: timeout (ms) for operation performed by register write,
  *                   timeout of zero implies maximum possible timeout
  *     @use_busy_signal: use the busy signal as response type
+ *     @send_status: send status cmd to poll for busy
  *
  *     Modifies the EXT_CSD register for selected card.
  */
 int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
-              unsigned int timeout_ms, bool use_busy_signal)
+               unsigned int timeout_ms, bool use_busy_signal, bool send_status)
 {
        int err;
        struct mmc_command cmd = {0};
        unsigned long timeout;
-       u32 status;
+       u32 status = 0;
+       bool ignore_crc = false;
 
        BUG_ON(!card);
        BUG_ON(!card->host);
@@ -408,17 +444,37 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
        if (!use_busy_signal)
                return 0;
 
-       /* Must check status to be sure of no errors */
+       /*
+        * Must check status to be sure of no errors
+        * If CMD13 is to check the busy completion of the timing change,
+        * disable the check of CRC error.
+        */
+       if (index == EXT_CSD_HS_TIMING &&
+           !(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY))
+               ignore_crc = true;
+
        timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
        do {
-               err = mmc_send_status(card, &status);
-               if (err)
-                       return err;
+               if (send_status) {
+                       err = __mmc_send_status(card, &status, ignore_crc);
+                       if (err)
+                               return err;
+               }
                if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
                        break;
                if (mmc_host_is_spi(card->host))
                        break;
 
+               /*
+                * We are not allowed to issue a status command and the host
+                * does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only
+                * rely on waiting for the stated timeout to be sufficient.
+                */
+               if (!send_status) {
+                       mmc_delay(timeout_ms);
+                       return 0;
+               }
+
                /* Timeout if the device never leaves the program state. */
                if (time_after(jiffies, timeout)) {
                        pr_err("%s: Card stuck in programming state! %s\n",
@@ -445,36 +501,10 @@ EXPORT_SYMBOL_GPL(__mmc_switch);
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
                unsigned int timeout_ms)
 {
-       return __mmc_switch(card, set, index, value, timeout_ms, true);
+       return __mmc_switch(card, set, index, value, timeout_ms, true, true);
 }
 EXPORT_SYMBOL_GPL(mmc_switch);
 
-int mmc_send_status(struct mmc_card *card, u32 *status)
-{
-       int err;
-       struct mmc_command cmd = {0};
-
-       BUG_ON(!card);
-       BUG_ON(!card->host);
-
-       cmd.opcode = MMC_SEND_STATUS;
-       if (!mmc_host_is_spi(card->host))
-               cmd.arg = card->rca << 16;
-       cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
-
-       err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
-       if (err)
-               return err;
-
-       /* NOTE: callers are required to understand the difference
-        * between "native" and SPI format status words!
-        */
-       if (status)
-               *status = cmd.resp[0];
-
-       return 0;
-}
-
 static int
 mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
                  u8 len)
index 5e8823d..6f42050 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/stat.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
@@ -721,6 +722,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
        int err;
        u32 max_current;
        int retries = 10;
+       u32 pocr = ocr;
 
 try_again:
        if (!retries) {
@@ -773,7 +775,8 @@ try_again:
         */
        if (!mmc_host_is_spi(host) && rocr &&
           ((*rocr & 0x41000000) == 0x41000000)) {
-               err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
+               err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
+                                       pocr);
                if (err == -EAGAIN) {
                        retries--;
                        goto try_again;
@@ -935,6 +938,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
                if (IS_ERR(card))
                        return PTR_ERR(card);
 
+               card->ocr = ocr;
                card->type = MMC_TYPE_SD;
                memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
        }
@@ -1064,10 +1068,7 @@ static void mmc_sd_detect(struct mmc_host *host)
        }
 }
 
-/*
- * Suspend callback from host.
- */
-static int mmc_sd_suspend(struct mmc_host *host)
+static int _mmc_sd_suspend(struct mmc_host *host)
 {
        int err = 0;
 
@@ -1075,34 +1076,77 @@ static int mmc_sd_suspend(struct mmc_host *host)
        BUG_ON(!host->card);
 
        mmc_claim_host(host);
+
+       if (mmc_card_suspended(host->card))
+               goto out;
+
        if (!mmc_host_is_spi(host))
                err = mmc_deselect_cards(host);
        host->card->state &= ~MMC_STATE_HIGHSPEED;
-       if (!err)
+       if (!err) {
                mmc_power_off(host);
+               mmc_card_set_suspended(host->card);
+       }
+
+out:
        mmc_release_host(host);
+       return err;
+}
+
+/*
+ * Callback for suspend
+ */
+static int mmc_sd_suspend(struct mmc_host *host)
+{
+       int err;
+
+       err = _mmc_sd_suspend(host);
+       if (!err) {
+               pm_runtime_disable(&host->card->dev);
+               pm_runtime_set_suspended(&host->card->dev);
+       }
 
        return err;
 }
 
 /*
- * Resume callback from host.
- *
  * This function tries to determine if the same card is still present
  * and, if so, restore all state to it.
  */
-static int mmc_sd_resume(struct mmc_host *host)
+static int _mmc_sd_resume(struct mmc_host *host)
 {
-       int err;
+       int err = 0;
 
        BUG_ON(!host);
        BUG_ON(!host->card);
 
        mmc_claim_host(host);
-       mmc_power_up(host);
-       mmc_select_voltage(host, host->ocr);
-       err = mmc_sd_init_card(host, host->ocr, host->card);
+
+       if (!mmc_card_suspended(host->card))
+               goto out;
+
+       mmc_power_up(host, host->card->ocr);
+       err = mmc_sd_init_card(host, host->card->ocr, host->card);
+       mmc_card_clr_suspended(host->card);
+
+out:
        mmc_release_host(host);
+       return err;
+}
+
+/*
+ * Callback for resume
+ */
+static int mmc_sd_resume(struct mmc_host *host)
+{
+       int err = 0;
+
+       if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
+               err = _mmc_sd_resume(host);
+               pm_runtime_set_active(&host->card->dev);
+               pm_runtime_mark_last_busy(&host->card->dev);
+       }
+       pm_runtime_enable(&host->card->dev);
 
        return err;
 }
@@ -1117,18 +1161,11 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host)
        if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
                return 0;
 
-       mmc_claim_host(host);
-
-       err = mmc_sd_suspend(host);
-       if (err) {
+       err = _mmc_sd_suspend(host);
+       if (err)
                pr_err("%s: error %d doing aggessive suspend\n",
                        mmc_hostname(host), err);
-               goto out;
-       }
-       mmc_power_off(host);
 
-out:
-       mmc_release_host(host);
        return err;
 }
 
@@ -1139,18 +1176,14 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
 {
        int err;
 
-       if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+       if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))
                return 0;
 
-       mmc_claim_host(host);
-
-       mmc_power_up(host);
-       err = mmc_sd_resume(host);
+       err = _mmc_sd_resume(host);
        if (err)
                pr_err("%s: error %d doing aggessive resume\n",
                        mmc_hostname(host), err);
 
-       mmc_release_host(host);
        return 0;
 }
 
@@ -1160,7 +1193,7 @@ static int mmc_sd_power_restore(struct mmc_host *host)
 
        host->card->state &= ~MMC_STATE_HIGHSPEED;
        mmc_claim_host(host);
-       ret = mmc_sd_init_card(host, host->ocr, host->card);
+       ret = mmc_sd_init_card(host, host->card->ocr, host->card);
        mmc_release_host(host);
 
        return ret;
@@ -1205,7 +1238,7 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host)
 int mmc_attach_sd(struct mmc_host *host)
 {
        int err;
-       u32 ocr;
+       u32 ocr, rocr;
 
        BUG_ON(!host);
        WARN_ON(!host->claimed);
@@ -1229,31 +1262,12 @@ int mmc_attach_sd(struct mmc_host *host)
                        goto err;
        }
 
-       /*
-        * Sanity check the voltages that the card claims to
-        * support.
-        */
-       if (ocr & 0x7F) {
-               pr_warning("%s: card claims to support voltages "
-                      "below the defined range. These will be ignored.\n",
-                      mmc_hostname(host));
-               ocr &= ~0x7F;
-       }
-
-       if ((ocr & MMC_VDD_165_195) &&
-           !(host->ocr_avail_sd & MMC_VDD_165_195)) {
-               pr_warning("%s: SD card claims to support the "
-                      "incompletely defined 'low voltage range'. This "
-                      "will be ignored.\n", mmc_hostname(host));
-               ocr &= ~MMC_VDD_165_195;
-       }
-
-       host->ocr = mmc_select_voltage(host, ocr);
+       rocr = mmc_select_voltage(host, ocr);
 
        /*
         * Can we support the voltage(s) of the card(s)?
         */
-       if (!host->ocr) {
+       if (!rocr) {
                err = -EINVAL;
                goto err;
        }
@@ -1261,7 +1275,7 @@ int mmc_attach_sd(struct mmc_host *host)
        /*
         * Detect and init the card.
         */
-       err = mmc_sd_init_card(host, host->ocr, NULL);
+       err = mmc_sd_init_card(host, rocr, NULL);
        if (err)
                goto err;
 
index 80d89cf..4d721c6 100644 (file)
@@ -593,23 +593,28 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
        struct mmc_card *card;
        int err;
        int retries = 10;
+       u32 rocr = 0;
+       u32 ocr_card = ocr;
 
        BUG_ON(!host);
        WARN_ON(!host->claimed);
 
+       /* to query card if 1.8V signalling is supported */
+       if (mmc_host_uhs(host))
+               ocr |= R4_18V_PRESENT;
+
 try_again:
        if (!retries) {
                pr_warning("%s: Skipping voltage switch\n",
                                mmc_hostname(host));
                ocr &= ~R4_18V_PRESENT;
-               host->ocr &= ~R4_18V_PRESENT;
        }
 
        /*
         * Inform the card of the voltage
         */
        if (!powered_resume) {
-               err = mmc_send_io_op_cond(host, host->ocr, &ocr);
+               err = mmc_send_io_op_cond(host, ocr, &rocr);
                if (err)
                        goto err;
        }
@@ -632,8 +637,8 @@ try_again:
                goto err;
        }
 
-       if ((ocr & R4_MEMORY_PRESENT) &&
-           mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) {
+       if ((rocr & R4_MEMORY_PRESENT) &&
+           mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
                card->type = MMC_TYPE_SD_COMBO;
 
                if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
@@ -663,8 +668,9 @@ try_again:
         * systems that claim 1.8v signalling in fact do not support
         * it.
         */
-       if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) {
-               err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
+       if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) {
+               err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
+                                       ocr);
                if (err == -EAGAIN) {
                        sdio_reset(host);
                        mmc_go_idle(host);
@@ -674,12 +680,10 @@ try_again:
                        goto try_again;
                } else if (err) {
                        ocr &= ~R4_18V_PRESENT;
-                       host->ocr &= ~R4_18V_PRESENT;
                }
                err = 0;
        } else {
                ocr &= ~R4_18V_PRESENT;
-               host->ocr &= ~R4_18V_PRESENT;
        }
 
        /*
@@ -759,6 +763,7 @@ try_again:
 
                card = oldcard;
        }
+       card->ocr = ocr_card;
        mmc_fixup_device(card, NULL);
 
        if (card->type == MMC_TYPE_SD_COMBO) {
@@ -981,8 +986,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
 
        /* Restore power if needed */
        if (!mmc_card_keep_power(host)) {
-               mmc_power_up(host);
-               mmc_select_voltage(host, host->ocr);
+               mmc_power_up(host, host->card->ocr);
                /*
                 * Tell runtime PM core we just powered up the card,
                 * since it still believes the card is powered off.
@@ -1000,7 +1004,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
        if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
                sdio_reset(host);
                mmc_go_idle(host);
-               err = mmc_sdio_init_card(host, host->ocr, host->card,
+               err = mmc_sdio_init_card(host, host->card->ocr, host->card,
                                        mmc_card_keep_power(host));
        } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
                /* We may have switched to 1-bit mode during suspend */
@@ -1040,7 +1044,6 @@ static int mmc_sdio_resume(struct mmc_host *host)
 static int mmc_sdio_power_restore(struct mmc_host *host)
 {
        int ret;
-       u32 ocr;
 
        BUG_ON(!host);
        BUG_ON(!host->card);
@@ -1062,32 +1065,17 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
         * for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and
         * harmless in other situations.
         *
-        * With these steps taken, mmc_select_voltage() is also required to
-        * restore the correct voltage setting of the card.
         */
 
        sdio_reset(host);
        mmc_go_idle(host);
        mmc_send_if_cond(host, host->ocr_avail);
 
-       ret = mmc_send_io_op_cond(host, 0, &ocr);
+       ret = mmc_send_io_op_cond(host, 0, NULL);
        if (ret)
                goto out;
 
-       if (host->ocr_avail_sdio)
-               host->ocr_avail = host->ocr_avail_sdio;
-
-       host->ocr = mmc_select_voltage(host, ocr & ~0x7F);
-       if (!host->ocr) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       if (mmc_host_uhs(host))
-               /* to query card if 1.8V signalling is supported */
-               host->ocr |= R4_18V_PRESENT;
-
-       ret = mmc_sdio_init_card(host, host->ocr, host->card,
+       ret = mmc_sdio_init_card(host, host->card->ocr, host->card,
                                mmc_card_keep_power(host));
        if (!ret && host->sdio_irqs)
                mmc_signal_sdio_irq(host);
@@ -1108,7 +1096,7 @@ static int mmc_sdio_runtime_suspend(struct mmc_host *host)
 static int mmc_sdio_runtime_resume(struct mmc_host *host)
 {
        /* Restore power and re-initialize. */
-       mmc_power_up(host);
+       mmc_power_up(host, host->card->ocr);
        return mmc_sdio_power_restore(host);
 }
 
@@ -1131,7 +1119,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
 int mmc_attach_sdio(struct mmc_host *host)
 {
        int err, i, funcs;
-       u32 ocr;
+       u32 ocr, rocr;
        struct mmc_card *card;
 
        BUG_ON(!host);
@@ -1145,23 +1133,13 @@ int mmc_attach_sdio(struct mmc_host *host)
        if (host->ocr_avail_sdio)
                host->ocr_avail = host->ocr_avail_sdio;
 
-       /*
-        * Sanity check the voltages that the card claims to
-        * support.
-        */
-       if (ocr & 0x7F) {
-               pr_warning("%s: card claims to support voltages "
-                      "below the defined range. These will be ignored.\n",
-                      mmc_hostname(host));
-               ocr &= ~0x7F;
-       }
 
-       host->ocr = mmc_select_voltage(host, ocr);
+       rocr = mmc_select_voltage(host, ocr);
 
        /*
         * Can we support the voltage(s) of the card(s)?
         */
-       if (!host->ocr) {
+       if (!rocr) {
                err = -EINVAL;
                goto err;
        }
@@ -1169,22 +1147,10 @@ int mmc_attach_sdio(struct mmc_host *host)
        /*
         * Detect and init the card.
         */
-       if (mmc_host_uhs(host))
-               /* to query card if 1.8V signalling is supported */
-               host->ocr |= R4_18V_PRESENT;
+       err = mmc_sdio_init_card(host, rocr, NULL, 0);
+       if (err)
+               goto err;
 
-       err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
-       if (err) {
-               if (err == -EAGAIN) {
-                       /*
-                        * Retry initialization with S18R set to 0.
-                        */
-                       host->ocr &= ~R4_18V_PRESENT;
-                       err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
-               }
-               if (err)
-                       goto err;
-       }
        card = host->card;
 
        /*
index ef89565..157b570 100644 (file)
@@ -308,8 +308,7 @@ static void sdio_acpi_set_handle(struct sdio_func *func)
        struct mmc_host *host = func->card->host;
        u64 addr = (host->slotno << 16) | func->num;
 
-       ACPI_HANDLE_SET(&func->dev,
-                       acpi_get_child(ACPI_HANDLE(host->parent), addr));
+       acpi_preset_companion(&func->dev, ACPI_HANDLE(host->parent), addr);
 }
 #else
 static inline void sdio_acpi_set_handle(struct sdio_func *func) {}
index 69e438e..2cbb451 100644 (file)
@@ -255,7 +255,6 @@ struct atmel_mci_slot {
 #define ATMCI_CARD_PRESENT     0
 #define ATMCI_CARD_NEED_INIT   1
 #define ATMCI_SHUTDOWN         2
-#define ATMCI_SUSPENDED                3
 
        int                     detect_pin;
        int                     wp_pin;
@@ -589,6 +588,13 @@ static void atmci_timeout_timer(unsigned long data)
        if (host->mrq->cmd->data) {
                host->mrq->cmd->data->error = -ETIMEDOUT;
                host->data = NULL;
+               /*
+                * With some SDIO modules, sometimes DMA transfer hangs. If
+                * stop_transfer() is not called then the DMA request is not
+                * removed, following ones are queued and never computed.
+                */
+               if (host->state == STATE_DATA_XFER)
+                       host->stop_transfer(host);
        } else {
                host->mrq->cmd->error = -ETIMEDOUT;
                host->cmd = NULL;
@@ -1803,12 +1809,14 @@ static void atmci_tasklet_func(unsigned long priv)
                        if (unlikely(status)) {
                                host->stop_transfer(host);
                                host->data = NULL;
-                               if (status & ATMCI_DTOE) {
-                                       data->error = -ETIMEDOUT;
-                               } else if (status & ATMCI_DCRCE) {
-                                       data->error = -EILSEQ;
-                               } else {
-                                       data->error = -EIO;
+                               if (data) {
+                                       if (status & ATMCI_DTOE) {
+                                               data->error = -ETIMEDOUT;
+                                       } else if (status & ATMCI_DCRCE) {
+                                               data->error = -EILSEQ;
+                                       } else {
+                                               data->error = -EIO;
+                                       }
                                }
                        }
 
@@ -2520,70 +2528,10 @@ static int __exit atmci_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int atmci_suspend(struct device *dev)
-{
-       struct atmel_mci *host = dev_get_drvdata(dev);
-       int i;
-
-        for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
-               struct atmel_mci_slot *slot = host->slot[i];
-               int ret;
-
-               if (!slot)
-                       continue;
-               ret = mmc_suspend_host(slot->mmc);
-               if (ret < 0) {
-                       while (--i >= 0) {
-                               slot = host->slot[i];
-                               if (slot
-                               && test_bit(ATMCI_SUSPENDED, &slot->flags)) {
-                                       mmc_resume_host(host->slot[i]->mmc);
-                                       clear_bit(ATMCI_SUSPENDED, &slot->flags);
-                               }
-                       }
-                       return ret;
-               } else {
-                       set_bit(ATMCI_SUSPENDED, &slot->flags);
-               }
-       }
-
-       return 0;
-}
-
-static int atmci_resume(struct device *dev)
-{
-       struct atmel_mci *host = dev_get_drvdata(dev);
-       int i;
-       int ret = 0;
-
-       for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
-               struct atmel_mci_slot *slot = host->slot[i];
-               int err;
-
-               slot = host->slot[i];
-               if (!slot)
-                       continue;
-               if (!test_bit(ATMCI_SUSPENDED, &slot->flags))
-                       continue;
-               err = mmc_resume_host(slot->mmc);
-               if (err < 0)
-                       ret = err;
-               else
-                       clear_bit(ATMCI_SUSPENDED, &slot->flags);
-       }
-
-       return ret;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume);
-
 static struct platform_driver atmci_driver = {
        .remove         = __exit_p(atmci_remove),
        .driver         = {
                .name           = "atmel_mci",
-               .pm             = &atmci_pm,
                .of_match_table = of_match_ptr(atmci_dt_ids),
        },
 };
index df9becd..f5443a6 100644 (file)
@@ -1157,11 +1157,6 @@ static int au1xmmc_remove(struct platform_device *pdev)
 static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct au1xmmc_host *host = platform_get_drvdata(pdev);
-       int ret;
-
-       ret = mmc_suspend_host(host->mmc);
-       if (ret)
-               return ret;
 
        au_writel(0, HOST_CONFIG2(host));
        au_writel(0, HOST_CONFIG(host));
@@ -1178,7 +1173,7 @@ static int au1xmmc_resume(struct platform_device *pdev)
 
        au1xmmc_reset_controller(host);
 
-       return mmc_resume_host(host->mmc);
+       return 0;
 }
 #else
 #define au1xmmc_suspend NULL
index 94fae2f..2b7f37e 100644 (file)
@@ -391,6 +391,7 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                /* Disable 4 bit SDIO */
                cfg &= ~SD4E;
        }
+       bfin_write_SDH_CFG(cfg);
 
        host->power_mode = ios->power_mode;
 #ifndef RSI_BLKSZ
@@ -415,7 +416,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                cfg &= ~SD_CMD_OD;
 # endif
 
-
        if (ios->power_mode != MMC_POWER_OFF)
                cfg |= PWR_ON;
        else
@@ -433,7 +433,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                clk_ctl |= CLK_E;
                host->clk_div = clk_div;
                bfin_write_SDH_CLK_CTL(clk_ctl);
-
        } else
                sdh_stop_clock(host);
 
@@ -640,21 +639,15 @@ static int sdh_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int sdh_suspend(struct platform_device *dev, pm_message_t state)
 {
-       struct mmc_host *mmc = platform_get_drvdata(dev);
        struct bfin_sd_host *drv_data = get_sdh_data(dev);
-       int ret = 0;
-
-       if (mmc)
-               ret = mmc_suspend_host(mmc);
 
        peripheral_free_list(drv_data->pin_req);
 
-       return ret;
+       return 0;
 }
 
 static int sdh_resume(struct platform_device *dev)
 {
-       struct mmc_host *mmc = platform_get_drvdata(dev);
        struct bfin_sd_host *drv_data = get_sdh_data(dev);
        int ret = 0;
 
@@ -665,10 +658,6 @@ static int sdh_resume(struct platform_device *dev)
        }
 
        sdh_reset();
-
-       if (mmc)
-               ret = mmc_resume_host(mmc);
-
        return ret;
 }
 #else
index 9d6e2b8..1087b4c 100644 (file)
@@ -667,12 +667,6 @@ static const struct mmc_host_ops cb710_mmc_host = {
 static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
-       struct mmc_host *mmc = cb710_slot_to_mmc(slot);
-       int err;
-
-       err = mmc_suspend_host(mmc);
-       if (err)
-               return err;
 
        cb710_mmc_enable_irq(slot, 0, ~0);
        return 0;
@@ -681,11 +675,9 @@ static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
 static int cb710_mmc_resume(struct platform_device *pdev)
 {
        struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
-       struct mmc_host *mmc = cb710_slot_to_mmc(slot);
 
        cb710_mmc_enable_irq(slot, 0, ~0);
-
-       return mmc_resume_host(mmc);
+       return 0;
 }
 
 #endif /* CONFIG_PM */
index e9fa87d..d615374 100644 (file)
@@ -193,7 +193,6 @@ struct mmc_davinci_host {
 #define DAVINCI_MMC_DATADIR_READ       1
 #define DAVINCI_MMC_DATADIR_WRITE      2
        unsigned char data_dir;
-       unsigned char suspended;
 
        /* buffer is used during PIO of one scatterlist segment, and
         * is updated along with buffer_bytes_left.  bytes_left applies
@@ -1435,38 +1434,23 @@ static int davinci_mmcsd_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct mmc_davinci_host *host = platform_get_drvdata(pdev);
-       int ret;
 
-       ret = mmc_suspend_host(host->mmc);
-       if (!ret) {
-               writel(0, host->base + DAVINCI_MMCIM);
-               mmc_davinci_reset_ctrl(host, 1);
-               clk_disable(host->clk);
-               host->suspended = 1;
-       } else {
-               host->suspended = 0;
-       }
+       writel(0, host->base + DAVINCI_MMCIM);
+       mmc_davinci_reset_ctrl(host, 1);
+       clk_disable(host->clk);
 
-       return ret;
+       return 0;
 }
 
 static int davinci_mmcsd_resume(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct mmc_davinci_host *host = platform_get_drvdata(pdev);
-       int ret;
-
-       if (!host->suspended)
-               return 0;
 
        clk_enable(host->clk);
-
        mmc_davinci_reset_ctrl(host, 0);
-       ret = mmc_resume_host(host->mmc);
-       if (!ret)
-               host->suspended = 0;
 
-       return ret;
+       return 0;
 }
 
 static const struct dev_pm_ops davinci_mmcsd_pm = {
index 6a1fa21..3423c5e 100644 (file)
 #include <linux/clk.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/dw_mmc.h>
+#include <linux/mmc/mmc.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/slab.h>
 
 #include "dw_mmc.h"
 #include "dw_mmc-pltfm.h"
 #define SDMMC_CLKSEL_TIMING(x, y, z)   (SDMMC_CLKSEL_CCLK_SAMPLE(x) |  \
                                        SDMMC_CLKSEL_CCLK_DRIVE(y) |    \
                                        SDMMC_CLKSEL_CCLK_DIVIDER(z))
+#define SDMMC_CLKSEL_WAKEUP_INT                BIT(11)
 
 #define EXYNOS4210_FIXED_CIU_CLK_DIV   2
 #define EXYNOS4412_FIXED_CIU_CLK_DIV   4
 
+/* Block number in eMMC */
+#define DWMCI_BLOCK_NUM                0xFFFFFFFF
+
+#define SDMMC_EMMCP_BASE       0x1000
+#define SDMMC_MPSECURITY       (SDMMC_EMMCP_BASE + 0x0010)
+#define SDMMC_MPSBEGIN0                (SDMMC_EMMCP_BASE + 0x0200)
+#define SDMMC_MPSEND0          (SDMMC_EMMCP_BASE + 0x0204)
+#define SDMMC_MPSCTRL0         (SDMMC_EMMCP_BASE + 0x020C)
+
+/* SMU control bits */
+#define DWMCI_MPSCTRL_SECURE_READ_BIT          BIT(7)
+#define DWMCI_MPSCTRL_SECURE_WRITE_BIT         BIT(6)
+#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT      BIT(5)
+#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT     BIT(4)
+#define DWMCI_MPSCTRL_USE_FUSE_KEY             BIT(3)
+#define DWMCI_MPSCTRL_ECB_MODE                 BIT(2)
+#define DWMCI_MPSCTRL_ENCRYPTION               BIT(1)
+#define DWMCI_MPSCTRL_VALID                    BIT(0)
+
+#define EXYNOS_CCLKIN_MIN      50000000        /* unit: HZ */
+
 /* Variations in Exynos specific dw-mshc controller */
 enum dw_mci_exynos_type {
        DW_MCI_TYPE_EXYNOS4210,
        DW_MCI_TYPE_EXYNOS4412,
        DW_MCI_TYPE_EXYNOS5250,
        DW_MCI_TYPE_EXYNOS5420,
+       DW_MCI_TYPE_EXYNOS5420_SMU,
 };
 
 /* Exynos implementation specific driver private data */
@@ -48,6 +73,7 @@ struct dw_mci_exynos_priv_data {
        u8                              ciu_div;
        u32                             sdr_timing;
        u32                             ddr_timing;
+       u32                             cur_speed;
 };
 
 static struct dw_mci_exynos_compatible {
@@ -66,44 +92,80 @@ static struct dw_mci_exynos_compatible {
        }, {
                .compatible     = "samsung,exynos5420-dw-mshc",
                .ctrl_type      = DW_MCI_TYPE_EXYNOS5420,
+       }, {
+               .compatible     = "samsung,exynos5420-dw-mshc-smu",
+               .ctrl_type      = DW_MCI_TYPE_EXYNOS5420_SMU,
        },
 };
 
 static int dw_mci_exynos_priv_init(struct dw_mci *host)
 {
-       struct dw_mci_exynos_priv_data *priv;
-       int idx;
-
-       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv) {
-               dev_err(host->dev, "mem alloc failed for private data\n");
-               return -ENOMEM;
-       }
+       struct dw_mci_exynos_priv_data *priv = host->priv;
 
-       for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
-               if (of_device_is_compatible(host->dev->of_node,
-                                       exynos_compat[idx].compatible))
-                       priv->ctrl_type = exynos_compat[idx].ctrl_type;
+       if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) {
+               mci_writel(host, MPSBEGIN0, 0);
+               mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
+               mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
+                          DWMCI_MPSCTRL_NON_SECURE_READ_BIT |
+                          DWMCI_MPSCTRL_VALID |
+                          DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT);
        }
 
-       host->priv = priv;
        return 0;
 }
 
 static int dw_mci_exynos_setup_clock(struct dw_mci *host)
 {
        struct dw_mci_exynos_priv_data *priv = host->priv;
+       unsigned long rate = clk_get_rate(host->ciu_clk);
 
-       if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250 ||
-               priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420)
-               host->bus_hz /= (priv->ciu_div + 1);
-       else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
-               host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
-       else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
-               host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
+       host->bus_hz = rate / (priv->ciu_div + 1);
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int dw_mci_exynos_suspend(struct device *dev)
+{
+       struct dw_mci *host = dev_get_drvdata(dev);
+
+       return dw_mci_suspend(host);
+}
+
+static int dw_mci_exynos_resume(struct device *dev)
+{
+       struct dw_mci *host = dev_get_drvdata(dev);
+
+       dw_mci_exynos_priv_init(host);
+       return dw_mci_resume(host);
+}
+
+/**
+ * dw_mci_exynos_resume_noirq - Exynos-specific resume code
+ *
+ * On exynos5420 there is a silicon errata that will sometimes leave the
+ * WAKEUP_INT bit in the CLKSEL register asserted.  This bit is 1 to indicate
+ * that it fired and we can clear it by writing a 1 back.  Clear it to prevent
+ * interrupts from going off constantly.
+ *
+ * We run this code on all exynos variants because it doesn't hurt.
+ */
+
+static int dw_mci_exynos_resume_noirq(struct device *dev)
+{
+       struct dw_mci *host = dev_get_drvdata(dev);
+       u32 clksel;
+
+       clksel = mci_readl(host, CLKSEL);
+       if (clksel & SDMMC_CLKSEL_WAKEUP_INT)
+               mci_writel(host, CLKSEL, clksel);
 
        return 0;
 }
+#else
+#define dw_mci_exynos_suspend          NULL
+#define dw_mci_exynos_resume           NULL
+#define dw_mci_exynos_resume_noirq     NULL
+#endif /* CONFIG_PM_SLEEP */
 
 static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
 {
@@ -121,23 +183,68 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
 static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
 {
        struct dw_mci_exynos_priv_data *priv = host->priv;
+       unsigned int wanted = ios->clock;
+       unsigned long actual;
+       u8 div = priv->ciu_div + 1;
 
-       if (ios->timing == MMC_TIMING_UHS_DDR50)
+       if (ios->timing == MMC_TIMING_UHS_DDR50) {
                mci_writel(host, CLKSEL, priv->ddr_timing);
-       else
+               /* Should be double rate for DDR mode */
+               if (ios->bus_width == MMC_BUS_WIDTH_8)
+                       wanted <<= 1;
+       } else {
                mci_writel(host, CLKSEL, priv->sdr_timing);
+       }
+
+       /* Don't care if wanted clock is zero */
+       if (!wanted)
+               return;
+
+       /* Guaranteed minimum frequency for cclkin */
+       if (wanted < EXYNOS_CCLKIN_MIN)
+               wanted = EXYNOS_CCLKIN_MIN;
+
+       if (wanted != priv->cur_speed) {
+               int ret = clk_set_rate(host->ciu_clk, wanted * div);
+               if (ret)
+                       dev_warn(host->dev,
+                               "failed to set clk-rate %u error: %d\n",
+                                wanted * div, ret);
+               actual = clk_get_rate(host->ciu_clk);
+               host->bus_hz = actual / div;
+               priv->cur_speed = wanted;
+               host->current_speed = 0;
+       }
 }
 
 static int dw_mci_exynos_parse_dt(struct dw_mci *host)
 {
-       struct dw_mci_exynos_priv_data *priv = host->priv;
+       struct dw_mci_exynos_priv_data *priv;
        struct device_node *np = host->dev->of_node;
        u32 timing[2];
        u32 div = 0;
+       int idx;
        int ret;
 
-       of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
-       priv->ciu_div = div;
+       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(host->dev, "mem alloc failed for private data\n");
+               return -ENOMEM;
+       }
+
+       for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
+               if (of_device_is_compatible(np, exynos_compat[idx].compatible))
+                       priv->ctrl_type = exynos_compat[idx].ctrl_type;
+       }
+
+       if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
+               priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1;
+       else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
+               priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1;
+       else {
+               of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
+               priv->ciu_div = div;
+       }
 
        ret = of_property_read_u32_array(np,
                        "samsung,dw-mshc-sdr-timing", timing, 2);
@@ -152,9 +259,131 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
                return ret;
 
        priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
+       host->priv = priv;
        return 0;
 }
 
+static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
+{
+       return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
+}
+
+static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
+{
+       u32 clksel;
+       clksel = mci_readl(host, CLKSEL);
+       clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
+       mci_writel(host, CLKSEL, clksel);
+}
+
+static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
+{
+       u32 clksel;
+       u8 sample;
+
+       clksel = mci_readl(host, CLKSEL);
+       sample = (clksel + 1) & 0x7;
+       clksel = (clksel & ~0x7) | sample;
+       mci_writel(host, CLKSEL, clksel);
+       return sample;
+}
+
+static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
+{
+       const u8 iter = 8;
+       u8 __c;
+       s8 i, loc = -1;
+
+       for (i = 0; i < iter; i++) {
+               __c = ror8(candiates, i);
+               if ((__c & 0xc7) == 0xc7) {
+                       loc = i;
+                       goto out;
+               }
+       }
+
+       for (i = 0; i < iter; i++) {
+               __c = ror8(candiates, i);
+               if ((__c & 0x83) == 0x83) {
+                       loc = i;
+                       goto out;
+               }
+       }
+
+out:
+       return loc;
+}
+
+static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
+                                       struct dw_mci_tuning_data *tuning_data)
+{
+       struct dw_mci *host = slot->host;
+       struct mmc_host *mmc = slot->mmc;
+       const u8 *blk_pattern = tuning_data->blk_pattern;
+       u8 *blk_test;
+       unsigned int blksz = tuning_data->blksz;
+       u8 start_smpl, smpl, candiates = 0;
+       s8 found = -1;
+       int ret = 0;
+
+       blk_test = kmalloc(blksz, GFP_KERNEL);
+       if (!blk_test)
+               return -ENOMEM;
+
+       start_smpl = dw_mci_exynos_get_clksmpl(host);
+
+       do {
+               struct mmc_request mrq = {NULL};
+               struct mmc_command cmd = {0};
+               struct mmc_command stop = {0};
+               struct mmc_data data = {0};
+               struct scatterlist sg;
+
+               cmd.opcode = opcode;
+               cmd.arg = 0;
+               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+               stop.opcode = MMC_STOP_TRANSMISSION;
+               stop.arg = 0;
+               stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+
+               data.blksz = blksz;
+               data.blocks = 1;
+               data.flags = MMC_DATA_READ;
+               data.sg = &sg;
+               data.sg_len = 1;
+
+               sg_init_one(&sg, blk_test, blksz);
+               mrq.cmd = &cmd;
+               mrq.stop = &stop;
+               mrq.data = &data;
+               host->mrq = &mrq;
+
+               mci_writel(host, TMOUT, ~0);
+               smpl = dw_mci_exynos_move_next_clksmpl(host);
+
+               mmc_wait_for_req(mmc, &mrq);
+
+               if (!cmd.error && !data.error) {
+                       if (!memcmp(blk_pattern, blk_test, blksz))
+                               candiates |= (1 << smpl);
+               } else {
+                       dev_dbg(host->dev,
+                               "Tuning error: cmd.error:%d, data.error:%d\n",
+                               cmd.error, data.error);
+               }
+       } while (start_smpl != smpl);
+
+       found = dw_mci_exynos_get_best_clksmpl(candiates);
+       if (found >= 0)
+               dw_mci_exynos_set_clksmpl(host, found);
+       else
+               ret = -EIO;
+
+       kfree(blk_test);
+       return ret;
+}
+
 /* Common capabilities of Exynos4/Exynos5 SoC */
 static unsigned long exynos_dwmmc_caps[4] = {
        MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
@@ -171,6 +400,7 @@ static const struct dw_mci_drv_data exynos_drv_data = {
        .prepare_command        = dw_mci_exynos_prepare_command,
        .set_ios                = dw_mci_exynos_set_ios,
        .parse_dt               = dw_mci_exynos_parse_dt,
+       .execute_tuning         = dw_mci_exynos_execute_tuning,
 };
 
 static const struct of_device_id dw_mci_exynos_match[] = {
@@ -180,6 +410,8 @@ static const struct of_device_id dw_mci_exynos_match[] = {
                        .data = &exynos_drv_data, },
        { .compatible = "samsung,exynos5420-dw-mshc",
                        .data = &exynos_drv_data, },
+       { .compatible = "samsung,exynos5420-dw-mshc-smu",
+                       .data = &exynos_drv_data, },
        {},
 };
 MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
@@ -194,13 +426,20 @@ static int dw_mci_exynos_probe(struct platform_device *pdev)
        return dw_mci_pltfm_register(pdev, drv_data);
 }
 
+const struct dev_pm_ops dw_mci_exynos_pmops = {
+       SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume)
+       .resume_noirq = dw_mci_exynos_resume_noirq,
+       .thaw_noirq = dw_mci_exynos_resume_noirq,
+       .restore_noirq = dw_mci_exynos_resume_noirq,
+};
+
 static struct platform_driver dw_mci_exynos_pltfm_driver = {
        .probe          = dw_mci_exynos_probe,
        .remove         = __exit_p(dw_mci_pltfm_remove),
        .driver         = {
                .name           = "dwmmc_exynos",
                .of_match_table = dw_mci_exynos_match,
-               .pm             = &dw_mci_pltfm_pmops,
+               .pm             = &dw_mci_exynos_pmops,
        },
 };
 
index 2089752..5c49656 100644 (file)
@@ -39,7 +39,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
 {
        struct dw_mci *host;
        struct resource *regs;
-       int ret;
 
        host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
        if (!host)
@@ -59,12 +58,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
        if (IS_ERR(host->regs))
                return PTR_ERR(host->regs);
 
-       if (drv_data && drv_data->init) {
-               ret = drv_data->init(host);
-               if (ret)
-                       return ret;
-       }
-
        platform_set_drvdata(pdev, host);
        return dw_mci_probe(host);
 }
index 14b5961..3e8e53a 100644 (file)
@@ -38,21 +38,6 @@ struct dw_mci_socfpga_priv_data {
 
 static int dw_mci_socfpga_priv_init(struct dw_mci *host)
 {
-       struct dw_mci_socfpga_priv_data *priv;
-
-       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv) {
-               dev_err(host->dev, "mem alloc failed for private data\n");
-               return -ENOMEM;
-       }
-
-       priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
-       if (IS_ERR(priv->sysreg)) {
-               dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
-               return PTR_ERR(priv->sysreg);
-       }
-       host->priv = priv;
-
        return 0;
 }
 
@@ -79,12 +64,24 @@ static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr)
 
 static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
 {
-       struct dw_mci_socfpga_priv_data *priv = host->priv;
+       struct dw_mci_socfpga_priv_data *priv;
        struct device_node *np = host->dev->of_node;
        u32 timing[2];
        u32 div = 0;
        int ret;
 
+       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(host->dev, "mem alloc failed for private data\n");
+               return -ENOMEM;
+       }
+
+       priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
+       if (IS_ERR(priv->sysreg)) {
+               dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
+               return PTR_ERR(priv->sysreg);
+       }
+
        ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div);
        if (ret)
                dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1");
@@ -96,6 +93,7 @@ static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
                return ret;
 
        priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
+       host->priv = priv;
        return 0;
 }
 
@@ -113,7 +111,7 @@ static const struct of_device_id dw_mci_socfpga_match[] = {
 };
 MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match);
 
-int dw_mci_socfpga_probe(struct platform_device *pdev)
+static int dw_mci_socfpga_probe(struct platform_device *pdev)
 {
        const struct dw_mci_drv_data *drv_data;
        const struct of_device_id *match;
@@ -128,7 +126,7 @@ static struct platform_driver dw_mci_socfpga_pltfm_driver = {
        .remove         = __exit_p(dw_mci_pltfm_remove),
        .driver         = {
                .name           = "dwmmc_socfpga",
-               .of_match_table = of_match_ptr(dw_mci_socfpga_match),
+               .of_match_table = dw_mci_socfpga_match,
                .pm             = &dw_mci_pltfm_pmops,
        },
 };
index 018f365..4bce0de 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/irq.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
 #include <linux/mmc/dw_mmc.h>
 #include <linux/bitops.h>
 #include <linux/regulator/consumer.h>
@@ -50,6 +51,9 @@
 #define DW_MCI_RECV_STATUS     2
 #define DW_MCI_DMA_THRESHOLD   16
 
+#define DW_MCI_FREQ_MAX        200000000       /* unit: HZ */
+#define DW_MCI_FREQ_MIN        400000          /* unit: HZ */
+
 #ifdef CONFIG_MMC_DW_IDMAC
 #define IDMAC_INT_CLR          (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
                                 SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
@@ -76,42 +80,39 @@ struct idmac_desc {
 };
 #endif /* CONFIG_MMC_DW_IDMAC */
 
-/**
- * struct dw_mci_slot - MMC slot state
- * @mmc: The mmc_host representing this slot.
- * @host: The MMC controller this slot is using.
- * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
- * @wp_gpio: If gpio_is_valid() we'll use this to read write protect.
- * @ctype: Card type for this slot.
- * @mrq: mmc_request currently being processed or waiting to be
- *     processed, or NULL when the slot is idle.
- * @queue_node: List node for placing this node in the @queue list of
- *     &struct dw_mci.
- * @clock: Clock rate configured by set_ios(). Protected by host->lock.
- * @flags: Random state bits associated with the slot.
- * @id: Number of this slot.
- * @last_detect_state: Most recently observed card detect state.
- */
-struct dw_mci_slot {
-       struct mmc_host         *mmc;
-       struct dw_mci           *host;
-
-       int                     quirks;
-       int                     wp_gpio;
-
-       u32                     ctype;
-
-       struct mmc_request      *mrq;
-       struct list_head        queue_node;
+static const u8 tuning_blk_pattern_4bit[] = {
+       0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
+       0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
+       0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
+       0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
+       0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
+       0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
+       0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
+       0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
+};
 
-       unsigned int            clock;
-       unsigned long           flags;
-#define DW_MMC_CARD_PRESENT    0
-#define DW_MMC_CARD_NEED_INIT  1
-       int                     id;
-       int                     last_detect_state;
+static const u8 tuning_blk_pattern_8bit[] = {
+       0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
+       0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
+       0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
+       0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
+       0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
+       0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
+       0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
+       0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
+       0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
+       0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
+       0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
+       0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
+       0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
+       0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
+       0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
+       0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
 };
 
+static inline bool dw_mci_fifo_reset(struct dw_mci *host);
+static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host);
+
 #if defined(CONFIG_DEBUG_FS)
 static int dw_mci_req_show(struct seq_file *s, void *v)
 {
@@ -249,10 +250,15 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 
        cmdr = cmd->opcode;
 
-       if (cmdr == MMC_STOP_TRANSMISSION)
+       if (cmd->opcode == MMC_STOP_TRANSMISSION ||
+           cmd->opcode == MMC_GO_IDLE_STATE ||
+           cmd->opcode == MMC_GO_INACTIVE_STATE ||
+           (cmd->opcode == SD_IO_RW_DIRECT &&
+            ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT))
                cmdr |= SDMMC_CMD_STOP;
        else
-               cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+               if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
+                       cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
 
        if (cmd->flags & MMC_RSP_PRESENT) {
                /* We expect a response, so set this bit */
@@ -279,6 +285,40 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
        return cmdr;
 }
 
+static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
+{
+       struct mmc_command *stop;
+       u32 cmdr;
+
+       if (!cmd->data)
+               return 0;
+
+       stop = &host->stop_abort;
+       cmdr = cmd->opcode;
+       memset(stop, 0, sizeof(struct mmc_command));
+
+       if (cmdr == MMC_READ_SINGLE_BLOCK ||
+           cmdr == MMC_READ_MULTIPLE_BLOCK ||
+           cmdr == MMC_WRITE_BLOCK ||
+           cmdr == MMC_WRITE_MULTIPLE_BLOCK) {
+               stop->opcode = MMC_STOP_TRANSMISSION;
+               stop->arg = 0;
+               stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
+       } else if (cmdr == SD_IO_RW_EXTENDED) {
+               stop->opcode = SD_IO_RW_DIRECT;
+               stop->arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) |
+                            ((cmd->arg >> 28) & 0x7);
+               stop->flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
+       } else {
+               return 0;
+       }
+
+       cmdr = stop->opcode | SDMMC_CMD_STOP |
+               SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP;
+
+       return cmdr;
+}
+
 static void dw_mci_start_command(struct dw_mci *host,
                                 struct mmc_command *cmd, u32 cmd_flags)
 {
@@ -293,9 +333,10 @@ static void dw_mci_start_command(struct dw_mci *host,
        mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
 }
 
-static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
+static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data)
 {
-       dw_mci_start_command(host, data->stop, host->stop_cmdr);
+       struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort;
+       dw_mci_start_command(host, stop, host->stop_cmdr);
 }
 
 /* DMA interface functions */
@@ -304,10 +345,10 @@ static void dw_mci_stop_dma(struct dw_mci *host)
        if (host->using_dma) {
                host->dma_ops->stop(host);
                host->dma_ops->cleanup(host);
-       } else {
-               /* Data transfer was stopped by the interrupt handler */
-               set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
        }
+
+       /* Data transfer was stopped by the interrupt handler */
+       set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
 }
 
 static int dw_mci_get_dma_dir(struct mmc_data *data)
@@ -331,6 +372,14 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)
                                     dw_mci_get_dma_dir(data));
 }
 
+static void dw_mci_idmac_reset(struct dw_mci *host)
+{
+       u32 bmod = mci_readl(host, BMOD);
+       /* Software reset of DMA */
+       bmod |= SDMMC_IDMAC_SWRESET;
+       mci_writel(host, BMOD, bmod);
+}
+
 static void dw_mci_idmac_stop_dma(struct dw_mci *host)
 {
        u32 temp;
@@ -344,6 +393,7 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host)
        /* Stop the IDMAC running */
        temp = mci_readl(host, BMOD);
        temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB);
+       temp |= SDMMC_IDMAC_SWRESET;
        mci_writel(host, BMOD, temp);
 }
 
@@ -435,7 +485,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)
        p->des3 = host->sg_dma;
        p->des0 = IDMAC_DES0_ER;
 
-       mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET);
+       dw_mci_idmac_reset(host);
 
        /* Mask out interrupts - get Tx & Rx complete only */
        mci_writel(host, IDSTS, IDMAC_INT_CLR);
@@ -532,6 +582,78 @@ static void dw_mci_post_req(struct mmc_host *mmc,
        data->host_cookie = 0;
 }
 
+static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
+{
+#ifdef CONFIG_MMC_DW_IDMAC
+       unsigned int blksz = data->blksz;
+       const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
+       u32 fifo_width = 1 << host->data_shift;
+       u32 blksz_depth = blksz / fifo_width, fifoth_val;
+       u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers;
+       int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1;
+
+       tx_wmark = (host->fifo_depth) / 2;
+       tx_wmark_invers = host->fifo_depth - tx_wmark;
+
+       /*
+        * MSIZE is '1',
+        * if blksz is not a multiple of the FIFO width
+        */
+       if (blksz % fifo_width) {
+               msize = 0;
+               rx_wmark = 1;
+               goto done;
+       }
+
+       do {
+               if (!((blksz_depth % mszs[idx]) ||
+                    (tx_wmark_invers % mszs[idx]))) {
+                       msize = idx;
+                       rx_wmark = mszs[idx] - 1;
+                       break;
+               }
+       } while (--idx > 0);
+       /*
+        * If idx is '0', it won't be tried
+        * Thus, initial values are uesed
+        */
+done:
+       fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark);
+       mci_writel(host, FIFOTH, fifoth_val);
+#endif
+}
+
+static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data)
+{
+       unsigned int blksz = data->blksz;
+       u32 blksz_depth, fifo_depth;
+       u16 thld_size;
+
+       WARN_ON(!(data->flags & MMC_DATA_READ));
+
+       if (host->timing != MMC_TIMING_MMC_HS200 &&
+           host->timing != MMC_TIMING_UHS_SDR104)
+               goto disable;
+
+       blksz_depth = blksz / (1 << host->data_shift);
+       fifo_depth = host->fifo_depth;
+
+       if (blksz_depth > fifo_depth)
+               goto disable;
+
+       /*
+        * If (blksz_depth) >= (fifo_depth >> 1), should be 'thld_size <= blksz'
+        * If (blksz_depth) <  (fifo_depth >> 1), should be thld_size = blksz
+        * Currently just choose blksz.
+        */
+       thld_size = blksz;
+       mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1));
+       return;
+
+disable:
+       mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0));
+}
+
 static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
 {
        int sg_len;
@@ -556,6 +678,14 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
                 (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
                 sg_len);
 
+       /*
+        * Decide the MSIZE and RX/TX Watermark.
+        * If current block size is same with previous size,
+        * no need to update fifoth.
+        */
+       if (host->prev_blksz != data->blksz)
+               dw_mci_adjust_fifoth(host, data);
+
        /* Enable the DMA interface */
        temp = mci_readl(host, CTRL);
        temp |= SDMMC_CTRL_DMA_ENABLE;
@@ -581,10 +711,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
        host->sg = NULL;
        host->data = data;
 
-       if (data->flags & MMC_DATA_READ)
+       if (data->flags & MMC_DATA_READ) {
                host->dir_status = DW_MCI_RECV_STATUS;
-       else
+               dw_mci_ctrl_rd_thld(host, data);
+       } else {
                host->dir_status = DW_MCI_SEND_STATUS;
+       }
 
        if (dw_mci_submit_data_dma(host, data)) {
                int flags = SG_MITER_ATOMIC;
@@ -606,6 +738,21 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
                temp = mci_readl(host, CTRL);
                temp &= ~SDMMC_CTRL_DMA_ENABLE;
                mci_writel(host, CTRL, temp);
+
+               /*
+                * Use the initial fifoth_val for PIO mode.
+                * If next issued data may be transfered by DMA mode,
+                * prev_blksz should be invalidated.
+                */
+               mci_writel(host, FIFOTH, host->fifoth_val);
+               host->prev_blksz = 0;
+       } else {
+               /*
+                * Keep the current block size.
+                * It will be used to decide whether to update
+                * fifoth register next time.
+                */
+               host->prev_blksz = data->blksz;
        }
 }
 
@@ -632,24 +779,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
 static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
 {
        struct dw_mci *host = slot->host;
+       unsigned int clock = slot->clock;
        u32 div;
        u32 clk_en_a;
 
-       if (slot->clock != host->current_speed || force_clkinit) {
-               div = host->bus_hz / slot->clock;
-               if (host->bus_hz % slot->clock && host->bus_hz > slot->clock)
+       if (!clock) {
+               mci_writel(host, CLKENA, 0);
+               mci_send_cmd(slot,
+                            SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+       } else if (clock != host->current_speed || force_clkinit) {
+               div = host->bus_hz / clock;
+               if (host->bus_hz % clock && host->bus_hz > clock)
                        /*
                         * move the + 1 after the divide to prevent
                         * over-clocking the card.
                         */
                        div += 1;
 
-               div = (host->bus_hz != slot->clock) ? DIV_ROUND_UP(div, 2) : 0;
+               div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0;
 
-               dev_info(&slot->mmc->class_dev,
-                        "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
-                        " div = %d)\n", slot->id, host->bus_hz, slot->clock,
-                        div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+               if ((clock << div) != slot->__clk_old || force_clkinit)
+                       dev_info(&slot->mmc->class_dev,
+                                "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
+                                slot->id, host->bus_hz, clock,
+                                div ? ((host->bus_hz / div) >> 1) :
+                                host->bus_hz, div);
 
                /* disable clock */
                mci_writel(host, CLKENA, 0);
@@ -676,9 +830,12 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
                mci_send_cmd(slot,
                             SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
 
-               host->current_speed = slot->clock;
+               /* keep the clock with reflecting clock dividor */
+               slot->__clk_old = clock << div;
        }
 
+       host->current_speed = clock;
+
        /* Set the current slot bus width */
        mci_writel(host, CTYPE, (slot->ctype << slot->id));
 }
@@ -700,7 +857,9 @@ static void __dw_mci_start_request(struct dw_mci *host,
 
        host->pending_events = 0;
        host->completed_events = 0;
+       host->cmd_status = 0;
        host->data_status = 0;
+       host->dir_status = 0;
 
        data = cmd->data;
        if (data) {
@@ -724,6 +883,8 @@ static void __dw_mci_start_request(struct dw_mci *host,
 
        if (mrq->stop)
                host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
+       else
+               host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd);
 }
 
 static void dw_mci_start_request(struct dw_mci *host,
@@ -806,14 +967,13 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                regs &= ~((0x1 << slot->id) << 16);
 
        mci_writel(slot->host, UHS_REG, regs);
+       slot->host->timing = ios->timing;
 
-       if (ios->clock) {
-               /*
-                * Use mirror of ios->clock to prevent race with mmc
-                * core ios update when finding the minimum.
-                */
-               slot->clock = ios->clock;
-       }
+       /*
+        * Use mirror of ios->clock to prevent race with mmc
+        * core ios update when finding the minimum.
+        */
+       slot->clock = ios->clock;
 
        if (drv_data && drv_data->set_ios)
                drv_data->set_ios(slot->host, ios);
@@ -939,6 +1099,38 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
        }
 }
 
+static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+       struct dw_mci_slot *slot = mmc_priv(mmc);
+       struct dw_mci *host = slot->host;
+       const struct dw_mci_drv_data *drv_data = host->drv_data;
+       struct dw_mci_tuning_data tuning_data;
+       int err = -ENOSYS;
+
+       if (opcode == MMC_SEND_TUNING_BLOCK_HS200) {
+               if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
+                       tuning_data.blk_pattern = tuning_blk_pattern_8bit;
+                       tuning_data.blksz = sizeof(tuning_blk_pattern_8bit);
+               } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
+                       tuning_data.blk_pattern = tuning_blk_pattern_4bit;
+                       tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
+               } else {
+                       return -EINVAL;
+               }
+       } else if (opcode == MMC_SEND_TUNING_BLOCK) {
+               tuning_data.blk_pattern = tuning_blk_pattern_4bit;
+               tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
+       } else {
+               dev_err(host->dev,
+                       "Undefined command(%d) for tuning\n", opcode);
+               return -EINVAL;
+       }
+
+       if (drv_data && drv_data->execute_tuning)
+               err = drv_data->execute_tuning(slot, opcode, &tuning_data);
+       return err;
+}
+
 static const struct mmc_host_ops dw_mci_ops = {
        .request                = dw_mci_request,
        .pre_req                = dw_mci_pre_req,
@@ -947,6 +1139,7 @@ static const struct mmc_host_ops dw_mci_ops = {
        .get_ro                 = dw_mci_get_ro,
        .get_cd                 = dw_mci_get_cd,
        .enable_sdio_irq        = dw_mci_enable_sdio_irq,
+       .execute_tuning         = dw_mci_execute_tuning,
 };
 
 static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
@@ -978,7 +1171,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
        spin_lock(&host->lock);
 }
 
-static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
+static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
 {
        u32 status = host->cmd_status;
 
@@ -1012,12 +1205,52 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd
                /* newer ip versions need a delay between retries */
                if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
                        mdelay(20);
+       }
 
-               if (cmd->data) {
-                       dw_mci_stop_dma(host);
-                       host->data = NULL;
+       return cmd->error;
+}
+
+static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
+{
+       u32 status = host->data_status;
+
+       if (status & DW_MCI_DATA_ERROR_FLAGS) {
+               if (status & SDMMC_INT_DRTO) {
+                       data->error = -ETIMEDOUT;
+               } else if (status & SDMMC_INT_DCRC) {
+                       data->error = -EILSEQ;
+               } else if (status & SDMMC_INT_EBE) {
+                       if (host->dir_status ==
+                               DW_MCI_SEND_STATUS) {
+                               /*
+                                * No data CRC status was returned.
+                                * The number of bytes transferred
+                                * will be exaggerated in PIO mode.
+                                */
+                               data->bytes_xfered = 0;
+                               data->error = -ETIMEDOUT;
+                       } else if (host->dir_status ==
+                                       DW_MCI_RECV_STATUS) {
+                               data->error = -EIO;
+                       }
+               } else {
+                       /* SDMMC_INT_SBE is included */
+                       data->error = -EIO;
                }
+
+               dev_err(host->dev, "data error, status 0x%08x\n", status);
+
+               /*
+                * After an error, there may be data lingering
+                * in the FIFO
+                */
+               dw_mci_fifo_reset(host);
+       } else {
+               data->bytes_xfered = data->blocks * data->blksz;
+               data->error = 0;
        }
+
+       return data->error;
 }
 
 static void dw_mci_tasklet_func(unsigned long priv)
@@ -1025,14 +1258,16 @@ static void dw_mci_tasklet_func(unsigned long priv)
        struct dw_mci *host = (struct dw_mci *)priv;
        struct mmc_data *data;
        struct mmc_command *cmd;
+       struct mmc_request *mrq;
        enum dw_mci_state state;
        enum dw_mci_state prev_state;
-       u32 status, ctrl;
+       unsigned int err;
 
        spin_lock(&host->lock);
 
        state = host->state;
        data = host->data;
+       mrq = host->mrq;
 
        do {
                prev_state = state;
@@ -1049,16 +1284,23 @@ static void dw_mci_tasklet_func(unsigned long priv)
                        cmd = host->cmd;
                        host->cmd = NULL;
                        set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
-                       dw_mci_command_complete(host, cmd);
-                       if (cmd == host->mrq->sbc && !cmd->error) {
+                       err = dw_mci_command_complete(host, cmd);
+                       if (cmd == mrq->sbc && !err) {
                                prev_state = state = STATE_SENDING_CMD;
                                __dw_mci_start_request(host, host->cur_slot,
-                                                      host->mrq->cmd);
+                                                      mrq->cmd);
                                goto unlock;
                        }
 
-                       if (!host->mrq->data || cmd->error) {
-                               dw_mci_request_end(host, host->mrq);
+                       if (cmd->data && err) {
+                               dw_mci_stop_dma(host);
+                               send_stop_abort(host, data);
+                               state = STATE_SENDING_STOP;
+                               break;
+                       }
+
+                       if (!cmd->data || err) {
+                               dw_mci_request_end(host, mrq);
                                goto unlock;
                        }
 
@@ -1069,8 +1311,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
                        if (test_and_clear_bit(EVENT_DATA_ERROR,
                                               &host->pending_events)) {
                                dw_mci_stop_dma(host);
-                               if (data->stop)
-                                       send_stop_cmd(host, data);
+                               send_stop_abort(host, data);
                                state = STATE_DATA_ERROR;
                                break;
                        }
@@ -1090,60 +1331,27 @@ static void dw_mci_tasklet_func(unsigned long priv)
 
                        host->data = NULL;
                        set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
-                       status = host->data_status;
-
-                       if (status & DW_MCI_DATA_ERROR_FLAGS) {
-                               if (status & SDMMC_INT_DRTO) {
-                                       data->error = -ETIMEDOUT;
-                               } else if (status & SDMMC_INT_DCRC) {
-                                       data->error = -EILSEQ;
-                               } else if (status & SDMMC_INT_EBE &&
-                                          host->dir_status ==
-                                                       DW_MCI_SEND_STATUS) {
-                                       /*
-                                        * No data CRC status was returned.
-                                        * The number of bytes transferred will
-                                        * be exaggerated in PIO mode.
-                                        */
-                                       data->bytes_xfered = 0;
-                                       data->error = -ETIMEDOUT;
-                               } else {
-                                       dev_err(host->dev,
-                                               "data FIFO error "
-                                               "(status=%08x)\n",
-                                               status);
-                                       data->error = -EIO;
-                               }
-                               /*
-                                * After an error, there may be data lingering
-                                * in the FIFO, so reset it - doing so
-                                * generates a block interrupt, hence setting
-                                * the scatter-gather pointer to NULL.
-                                */
-                               sg_miter_stop(&host->sg_miter);
-                               host->sg = NULL;
-                               ctrl = mci_readl(host, CTRL);
-                               ctrl |= SDMMC_CTRL_FIFO_RESET;
-                               mci_writel(host, CTRL, ctrl);
-                       } else {
-                               data->bytes_xfered = data->blocks * data->blksz;
-                               data->error = 0;
-                       }
+                       err = dw_mci_data_complete(host, data);
 
-                       if (!data->stop) {
-                               dw_mci_request_end(host, host->mrq);
-                               goto unlock;
-                       }
+                       if (!err) {
+                               if (!data->stop || mrq->sbc) {
+                                       if (mrq->sbc)
+                                               data->stop->error = 0;
+                                       dw_mci_request_end(host, mrq);
+                                       goto unlock;
+                               }
 
-                       if (host->mrq->sbc && !data->error) {
-                               data->stop->error = 0;
-                               dw_mci_request_end(host, host->mrq);
-                               goto unlock;
+                               /* stop command for open-ended transfer*/
+                               if (data->stop)
+                                       send_stop_abort(host, data);
                        }
 
+                       /*
+                        * If err has non-zero,
+                        * stop-abort command has been already issued.
+                        */
                        prev_state = state = STATE_SENDING_STOP;
-                       if (!data->error)
-                               send_stop_cmd(host, data);
+
                        /* fall through */
 
                case STATE_SENDING_STOP:
@@ -1151,9 +1359,19 @@ static void dw_mci_tasklet_func(unsigned long priv)
                                                &host->pending_events))
                                break;
 
+                       /* CMD error in data command */
+                       if (mrq->cmd->error && mrq->data)
+                               dw_mci_fifo_reset(host);
+
                        host->cmd = NULL;
-                       dw_mci_command_complete(host, host->mrq->stop);
-                       dw_mci_request_end(host, host->mrq);
+                       host->data = NULL;
+
+                       if (mrq->stop)
+                               dw_mci_command_complete(host, mrq->stop);
+                       else
+                               host->cmd_status = 0;
+
+                       dw_mci_request_end(host, mrq);
                        goto unlock;
 
                case STATE_DATA_ERROR:
@@ -1697,7 +1915,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
                struct mmc_host *mmc = slot->mmc;
                struct mmc_request *mrq;
                int present;
-               u32 ctrl;
 
                present = dw_mci_get_cd(mmc);
                while (present != slot->last_detect_state) {
@@ -1736,11 +1953,10 @@ static void dw_mci_work_routine_card(struct work_struct *work)
                                        case STATE_DATA_ERROR:
                                                if (mrq->data->error == -EINPROGRESS)
                                                        mrq->data->error = -ENOMEDIUM;
-                                               if (!mrq->stop)
-                                                       break;
                                                /* fall through */
                                        case STATE_SENDING_STOP:
-                                               mrq->stop->error = -ENOMEDIUM;
+                                               if (mrq->stop)
+                                                       mrq->stop->error = -ENOMEDIUM;
                                                break;
                                        }
 
@@ -1763,23 +1979,10 @@ static void dw_mci_work_routine_card(struct work_struct *work)
                        if (present == 0) {
                                clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
 
-                               /*
-                                * Clear down the FIFO - doing so generates a
-                                * block interrupt, hence setting the
-                                * scatter-gather pointer to NULL.
-                                */
-                               sg_miter_stop(&host->sg_miter);
-                               host->sg = NULL;
-
-                               ctrl = mci_readl(host, CTRL);
-                               ctrl |= SDMMC_CTRL_FIFO_RESET;
-                               mci_writel(host, CTRL, ctrl);
-
+                               /* Clear down the FIFO */
+                               dw_mci_fifo_reset(host);
 #ifdef CONFIG_MMC_DW_IDMAC
-                               ctrl = mci_readl(host, BMOD);
-                               /* Software reset of DMA */
-                               ctrl |= SDMMC_IDMAC_SWRESET;
-                               mci_writel(host, BMOD, ctrl);
+                               dw_mci_idmac_reset(host);
 #endif
 
                        }
@@ -1901,6 +2104,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        struct dw_mci_slot *slot;
        const struct dw_mci_drv_data *drv_data = host->drv_data;
        int ctrl_id, ret;
+       u32 freq[2];
        u8 bus_width;
 
        mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
@@ -1916,8 +2120,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id);
 
        mmc->ops = &dw_mci_ops;
-       mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
-       mmc->f_max = host->bus_hz;
+       if (of_property_read_u32_array(host->dev->of_node,
+                                      "clock-freq-min-max", freq, 2)) {
+               mmc->f_min = DW_MCI_FREQ_MIN;
+               mmc->f_max = DW_MCI_FREQ_MAX;
+       } else {
+               mmc->f_min = freq[0];
+               mmc->f_max = freq[1];
+       }
 
        if (host->pdata->get_ocr)
                mmc->ocr_avail = host->pdata->get_ocr(id);
@@ -1964,9 +2174,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
                mmc->caps |= MMC_CAP_4_BIT_DATA;
        }
 
-       if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
-               mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
-
        if (host->pdata->blk_settings) {
                mmc->max_segs = host->pdata->blk_settings->max_segs;
                mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
@@ -2008,12 +2215,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        /* Card initially undetected */
        slot->last_detect_state = 0;
 
-       /*
-        * Card may have been plugged in prior to boot so we
-        * need to run the detect tasklet
-        */
-       queue_work(host->card_workqueue, &host->card_work);
-
        return 0;
 
 err_setup_bus:
@@ -2074,36 +2275,57 @@ no_dma:
        return;
 }
 
-static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
+static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset)
 {
        unsigned long timeout = jiffies + msecs_to_jiffies(500);
-       unsigned int ctrl;
+       u32 ctrl;
 
-       mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
-                               SDMMC_CTRL_DMA_RESET));
+       ctrl = mci_readl(host, CTRL);
+       ctrl |= reset;
+       mci_writel(host, CTRL, ctrl);
 
        /* wait till resets clear */
        do {
                ctrl = mci_readl(host, CTRL);
-               if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
-                             SDMMC_CTRL_DMA_RESET)))
+               if (!(ctrl & reset))
                        return true;
        } while (time_before(jiffies, timeout));
 
-       dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
+       dev_err(host->dev,
+               "Timeout resetting block (ctrl reset %#x)\n",
+               ctrl & reset);
 
        return false;
 }
 
+static inline bool dw_mci_fifo_reset(struct dw_mci *host)
+{
+       /*
+        * Reseting generates a block interrupt, hence setting
+        * the scatter-gather pointer to NULL.
+        */
+       if (host->sg) {
+               sg_miter_stop(&host->sg_miter);
+               host->sg = NULL;
+       }
+
+       return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET);
+}
+
+static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host)
+{
+       return dw_mci_ctrl_reset(host,
+                                SDMMC_CTRL_FIFO_RESET |
+                                SDMMC_CTRL_RESET |
+                                SDMMC_CTRL_DMA_RESET);
+}
+
 #ifdef CONFIG_OF
 static struct dw_mci_of_quirks {
        char *quirk;
        int id;
 } of_quirks[] = {
        {
-               .quirk  = "supports-highspeed",
-               .id     = DW_MCI_QUIRK_HIGHSPEED,
-       }, {
                .quirk  = "broken-cd",
                .id     = DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
        },
@@ -2158,6 +2380,15 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
        if (of_find_property(np, "enable-sdio-wakeup", NULL))
                pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
 
+       if (of_find_property(np, "supports-highspeed", NULL))
+               pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+
+       if (of_find_property(np, "caps2-mmc-hs200-1_8v", NULL))
+               pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+
+       if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL))
+               pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
+
        return pdata;
 }
 
@@ -2221,6 +2452,15 @@ int dw_mci_probe(struct dw_mci *host)
                host->bus_hz = clk_get_rate(host->ciu_clk);
        }
 
+       if (drv_data && drv_data->init) {
+               ret = drv_data->init(host);
+               if (ret) {
+                       dev_err(host->dev,
+                               "implementation specific init failed\n");
+                       goto err_clk_ciu;
+               }
+       }
+
        if (drv_data && drv_data->setup_clock) {
                ret = drv_data->setup_clock(host);
                if (ret) {
@@ -2287,7 +2527,7 @@ int dw_mci_probe(struct dw_mci *host)
        }
 
        /* Reset all blocks */
-       if (!mci_wait_reset(host->dev, host))
+       if (!dw_mci_ctrl_all_reset(host))
                return -ENODEV;
 
        host->dma_ops = host->pdata->dma_ops;
@@ -2317,8 +2557,8 @@ int dw_mci_probe(struct dw_mci *host)
                fifo_size = host->pdata->fifo_depth;
        }
        host->fifo_depth = fifo_size;
-       host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
-                       ((fifo_size/2) << 0));
+       host->fifoth_val =
+               SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2);
        mci_writel(host, FIFOTH, host->fifoth_val);
 
        /* disable clock to CIU */
@@ -2456,23 +2696,6 @@ EXPORT_SYMBOL(dw_mci_remove);
  */
 int dw_mci_suspend(struct dw_mci *host)
 {
-       int i, ret = 0;
-
-       for (i = 0; i < host->num_slots; i++) {
-               struct dw_mci_slot *slot = host->slot[i];
-               if (!slot)
-                       continue;
-               ret = mmc_suspend_host(slot->mmc);
-               if (ret < 0) {
-                       while (--i >= 0) {
-                               slot = host->slot[i];
-                               if (slot)
-                                       mmc_resume_host(host->slot[i]->mmc);
-                       }
-                       return ret;
-               }
-       }
-
        if (host->vmmc)
                regulator_disable(host->vmmc);
 
@@ -2493,7 +2716,7 @@ int dw_mci_resume(struct dw_mci *host)
                }
        }
 
-       if (!mci_wait_reset(host->dev, host)) {
+       if (!dw_mci_ctrl_all_reset(host)) {
                ret = -ENODEV;
                return ret;
        }
@@ -2501,8 +2724,15 @@ int dw_mci_resume(struct dw_mci *host)
        if (host->use_dma && host->dma_ops->init)
                host->dma_ops->init(host);
 
-       /* Restore the old value at FIFOTH register */
+       /*
+        * Restore the initial value at FIFOTH register
+        * And Invalidate the prev_blksz with zero
+        */
        mci_writel(host, FIFOTH, host->fifoth_val);
+       host->prev_blksz = 0;
+
+       /* Put in max timeout */
+       mci_writel(host, TMOUT, 0xFFFFFFFF);
 
        mci_writel(host, RINTSTS, 0xFFFFFFFF);
        mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
@@ -2518,10 +2748,6 @@ int dw_mci_resume(struct dw_mci *host)
                        dw_mci_set_ios(slot->mmc, &slot->mmc->ios);
                        dw_mci_setup_bus(slot, true);
                }
-
-               ret = mmc_resume_host(host->slot[i]->mmc);
-               if (ret < 0)
-                       return ret;
        }
        return 0;
 }
index 81b2994..6bf24ab 100644 (file)
@@ -53,6 +53,7 @@
 #define SDMMC_IDINTEN          0x090
 #define SDMMC_DSCADDR          0x094
 #define SDMMC_BUFADDR          0x098
+#define SDMMC_CDTHRCTL         0x100
 #define SDMMC_DATA(x)          (x)
 
 /*
 #define SDMMC_CMD_INDX(n)              ((n) & 0x1F)
 /* Status register defines */
 #define SDMMC_GET_FCNT(x)              (((x)>>17) & 0x1FFF)
+/* FIFOTH register defines */
+#define SDMMC_SET_FIFOTH(m, r, t)      (((m) & 0x7) << 28 | \
+                                        ((r) & 0xFFF) << 16 | \
+                                        ((t) & 0xFFF))
 /* Internal DMAC interrupt defines */
 #define SDMMC_IDMAC_INT_AI             BIT(9)
 #define SDMMC_IDMAC_INT_NI             BIT(8)
 #define SDMMC_IDMAC_SWRESET            BIT(0)
 /* Version ID register define */
 #define SDMMC_GET_VERID(x)             ((x) & 0xFFFF)
+/* Card read threshold */
+#define SDMMC_SET_RD_THLD(v, x)                (((v) & 0x1FFF) << 16 | (x))
 
 /* Register access macros */
 #define mci_readl(dev, reg)                    \
@@ -184,6 +191,52 @@ extern int dw_mci_resume(struct dw_mci *host);
 #endif
 
 /**
+ * struct dw_mci_slot - MMC slot state
+ * @mmc: The mmc_host representing this slot.
+ * @host: The MMC controller this slot is using.
+ * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
+ * @wp_gpio: If gpio_is_valid() we'll use this to read write protect.
+ * @ctype: Card type for this slot.
+ * @mrq: mmc_request currently being processed or waiting to be
+ *     processed, or NULL when the slot is idle.
+ * @queue_node: List node for placing this node in the @queue list of
+ *     &struct dw_mci.
+ * @clock: Clock rate configured by set_ios(). Protected by host->lock.
+ * @__clk_old: The last updated clock with reflecting clock divider.
+ *     Keeping track of this helps us to avoid spamming the console
+ *     with CONFIG_MMC_CLKGATE.
+ * @flags: Random state bits associated with the slot.
+ * @id: Number of this slot.
+ * @last_detect_state: Most recently observed card detect state.
+ */
+struct dw_mci_slot {
+       struct mmc_host         *mmc;
+       struct dw_mci           *host;
+
+       int                     quirks;
+       int                     wp_gpio;
+
+       u32                     ctype;
+
+       struct mmc_request      *mrq;
+       struct list_head        queue_node;
+
+       unsigned int            clock;
+       unsigned int            __clk_old;
+
+       unsigned long           flags;
+#define DW_MMC_CARD_PRESENT    0
+#define DW_MMC_CARD_NEED_INIT  1
+       int                     id;
+       int                     last_detect_state;
+};
+
+struct dw_mci_tuning_data {
+       const u8 *blk_pattern;
+       unsigned int blksz;
+};
+
+/**
  * dw_mci driver data - dw-mshc implementation specific driver data.
  * @caps: mmc subsystem specified capabilities of the controller(s).
  * @init: early implementation specific initialization.
@@ -203,5 +256,7 @@ struct dw_mci_drv_data {
        void            (*prepare_command)(struct dw_mci *host, u32 *cmdr);
        void            (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
        int             (*parse_dt)(struct dw_mci *host);
+       int             (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode,
+                                       struct dw_mci_tuning_data *tuning_data);
 };
 #endif /* _DW_MMC_H_ */
index 6651633..de2139c 100644 (file)
@@ -880,8 +880,6 @@ static int jz4740_mmc_suspend(struct device *dev)
 {
        struct jz4740_mmc_host *host = dev_get_drvdata(dev);
 
-       mmc_suspend_host(host->mmc);
-
        jz_gpio_bulk_suspend(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
 
        return 0;
@@ -893,8 +891,6 @@ static int jz4740_mmc_resume(struct device *dev)
 
        jz_gpio_bulk_resume(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
 
-       mmc_resume_host(host->mmc);
-
        return 0;
 }
 
index d135c76..f320579 100644 (file)
@@ -1730,37 +1730,28 @@ static int mmci_suspend(struct device *dev)
 {
        struct amba_device *adev = to_amba_device(dev);
        struct mmc_host *mmc = amba_get_drvdata(adev);
-       int ret = 0;
 
        if (mmc) {
                struct mmci_host *host = mmc_priv(mmc);
-
-               ret = mmc_suspend_host(mmc);
-               if (ret == 0) {
-                       pm_runtime_get_sync(dev);
-                       writel(0, host->base + MMCIMASK0);
-               }
+               pm_runtime_get_sync(dev);
+               writel(0, host->base + MMCIMASK0);
        }
 
-       return ret;
+       return 0;
 }
 
 static int mmci_resume(struct device *dev)
 {
        struct amba_device *adev = to_amba_device(dev);
        struct mmc_host *mmc = amba_get_drvdata(adev);
-       int ret = 0;
 
        if (mmc) {
                struct mmci_host *host = mmc_priv(mmc);
-
                writel(MCI_IRQENABLE, host->base + MMCIMASK0);
                pm_runtime_put(dev);
-
-               ret = mmc_resume_host(mmc);
        }
 
-       return ret;
+       return 0;
 }
 #endif
 
index b900de4..9405ecd 100644 (file)
@@ -1416,28 +1416,10 @@ ioremap_free:
 }
 
 #ifdef CONFIG_PM
-#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
-static void
-do_resume_work(struct work_struct *work)
-{
-       struct msmsdcc_host *host =
-               container_of(work, struct msmsdcc_host, resume_task);
-       struct mmc_host *mmc = host->mmc;
-
-       if (mmc) {
-               mmc_resume_host(mmc);
-               if (host->stat_irq)
-                       enable_irq(host->stat_irq);
-       }
-}
-#endif
-
-
 static int
 msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
 {
        struct mmc_host *mmc = mmc_get_drvdata(dev);
-       int rc = 0;
 
        if (mmc) {
                struct msmsdcc_host *host = mmc_priv(mmc);
@@ -1445,14 +1427,11 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
                if (host->stat_irq)
                        disable_irq(host->stat_irq);
 
-               if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
-                       rc = mmc_suspend_host(mmc);
-               if (!rc)
-                       msmsdcc_writel(host, 0, MMCIMASK0);
+               msmsdcc_writel(host, 0, MMCIMASK0);
                if (host->clks_on)
                        msmsdcc_disable_clocks(host, 0);
        }
-       return rc;
+       return 0;
 }
 
 static int
@@ -1467,8 +1446,6 @@ msmsdcc_resume(struct platform_device *dev)
 
                msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0);
 
-               if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
-                       mmc_resume_host(mmc);
                if (host->stat_irq)
                        enable_irq(host->stat_irq);
 #if BUSCLK_PWRSAVE
index deecee0..45aa220 100644 (file)
@@ -775,9 +775,9 @@ static int mvsd_probe(struct platform_device *pdev)
 
        spin_lock_init(&host->lock);
 
-       host->base = devm_request_and_ioremap(&pdev->dev, r);
-       if (!host->base) {
-               ret = -ENOMEM;
+       host->base = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(host->base)) {
+               ret = PTR_ERR(host->base);
                goto out;
        }
 
@@ -838,33 +838,6 @@ static int mvsd_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int mvsd_suspend(struct platform_device *dev, pm_message_t state)
-{
-       struct mmc_host *mmc = platform_get_drvdata(dev);
-       int ret = 0;
-
-       if (mmc)
-               ret = mmc_suspend_host(mmc);
-
-       return ret;
-}
-
-static int mvsd_resume(struct platform_device *dev)
-{
-       struct mmc_host *mmc = platform_get_drvdata(dev);
-       int ret = 0;
-
-       if (mmc)
-               ret = mmc_resume_host(mmc);
-
-       return ret;
-}
-#else
-#define mvsd_suspend   NULL
-#define mvsd_resume    NULL
-#endif
-
 static const struct of_device_id mvsdio_dt_ids[] = {
        { .compatible = "marvell,orion-sdio" },
        { /* sentinel */ }
@@ -874,8 +847,6 @@ MODULE_DEVICE_TABLE(of, mvsdio_dt_ids);
 static struct platform_driver mvsd_driver = {
        .probe          = mvsd_probe,
        .remove         = mvsd_remove,
-       .suspend        = mvsd_suspend,
-       .resume         = mvsd_resume,
        .driver         = {
                .name   = DRIVER_NAME,
                .of_match_table = mvsdio_dt_ids,
index c174c6a..f7199c8 100644 (file)
@@ -1250,28 +1250,20 @@ static int mxcmci_suspend(struct device *dev)
 {
        struct mmc_host *mmc = dev_get_drvdata(dev);
        struct mxcmci_host *host = mmc_priv(mmc);
-       int ret = 0;
 
-       if (mmc)
-               ret = mmc_suspend_host(mmc);
        clk_disable_unprepare(host->clk_per);
        clk_disable_unprepare(host->clk_ipg);
-
-       return ret;
+       return 0;
 }
 
 static int mxcmci_resume(struct device *dev)
 {
        struct mmc_host *mmc = dev_get_drvdata(dev);
        struct mxcmci_host *host = mmc_priv(mmc);
-       int ret = 0;
 
        clk_prepare_enable(host->clk_per);
        clk_prepare_enable(host->clk_ipg);
-       if (mmc)
-               ret = mmc_resume_host(mmc);
-
-       return ret;
+       return 0;
 }
 
 static const struct dev_pm_ops mxcmci_pm_ops = {
index e1fa3ef..50fc9df 100644 (file)
@@ -724,13 +724,9 @@ static int mxs_mmc_suspend(struct device *dev)
        struct mmc_host *mmc = dev_get_drvdata(dev);
        struct mxs_mmc_host *host = mmc_priv(mmc);
        struct mxs_ssp *ssp = &host->ssp;
-       int ret = 0;
-
-       ret = mmc_suspend_host(mmc);
 
        clk_disable_unprepare(ssp->clk);
-
-       return ret;
+       return 0;
 }
 
 static int mxs_mmc_resume(struct device *dev)
@@ -738,13 +734,9 @@ static int mxs_mmc_resume(struct device *dev)
        struct mmc_host *mmc = dev_get_drvdata(dev);
        struct mxs_mmc_host *host = mmc_priv(mmc);
        struct mxs_ssp *ssp = &host->ssp;
-       int ret = 0;
 
        clk_prepare_enable(ssp->clk);
-
-       ret = mmc_resume_host(mmc);
-
-       return ret;
+       return 0;
 }
 
 static const struct dev_pm_ops mxs_mmc_pm_ops = {
index b94f38e..0b10a90 100644 (file)
@@ -128,7 +128,6 @@ struct mmc_omap_slot {
 
 struct mmc_omap_host {
        int                     initialized;
-       int                     suspended;
        struct mmc_request *    mrq;
        struct mmc_command *    cmd;
        struct mmc_data *       data;
@@ -1513,61 +1512,9 @@ static int mmc_omap_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg)
-{
-       int i, ret = 0;
-       struct mmc_omap_host *host = platform_get_drvdata(pdev);
-
-       if (host == NULL || host->suspended)
-               return 0;
-
-       for (i = 0; i < host->nr_slots; i++) {
-               struct mmc_omap_slot *slot;
-
-               slot = host->slots[i];
-               ret = mmc_suspend_host(slot->mmc);
-               if (ret < 0) {
-                       while (--i >= 0) {
-                               slot = host->slots[i];
-                               mmc_resume_host(slot->mmc);
-                       }
-                       return ret;
-               }
-       }
-       host->suspended = 1;
-       return 0;
-}
-
-static int mmc_omap_resume(struct platform_device *pdev)
-{
-       int i, ret = 0;
-       struct mmc_omap_host *host = platform_get_drvdata(pdev);
-
-       if (host == NULL || !host->suspended)
-               return 0;
-
-       for (i = 0; i < host->nr_slots; i++) {
-               struct mmc_omap_slot *slot;
-               slot = host->slots[i];
-               ret = mmc_resume_host(slot->mmc);
-               if (ret < 0)
-                       return ret;
-
-               host->suspended = 0;
-       }
-       return 0;
-}
-#else
-#define mmc_omap_suspend       NULL
-#define mmc_omap_resume                NULL
-#endif
-
 static struct platform_driver mmc_omap_driver = {
        .probe          = mmc_omap_probe,
        .remove         = mmc_omap_remove,
-       .suspend        = mmc_omap_suspend,
-       .resume         = mmc_omap_resume,
        .driver         = {
                .name   = DRIVER_NAME,
                .owner  = THIS_MODULE,
index 6ac63df..dbd32ad 100644 (file)
@@ -75,6 +75,7 @@
 #define ICE                    0x1
 #define ICS                    0x2
 #define CEN                    (1 << 2)
+#define CLKD_MAX               0x3FF           /* max clock divisor: 1023 */
 #define CLKD_MASK              0x0000FFC0
 #define CLKD_SHIFT             6
 #define DTO_MASK               0x000F0000
                BRR_EN | BWR_EN | TC_EN | CC_EN)
 
 #define MMC_AUTOSUSPEND_DELAY  100
-#define MMC_TIMEOUT_MS         20
+#define MMC_TIMEOUT_MS         20              /* 20 mSec */
+#define MMC_TIMEOUT_US         20000           /* 20000 micro Sec */
 #define OMAP_MMC_MIN_CLOCK     400000
 #define OMAP_MMC_MAX_CLOCK     52000000
 #define DRIVER_NAME            "omap_hsmmc"
@@ -171,6 +173,10 @@ struct omap_hsmmc_host {
        unsigned char           bus_mode;
        unsigned char           power_mode;
        int                     suspended;
+       u32                     con;
+       u32                     hctl;
+       u32                     sysctl;
+       u32                     capa;
        int                     irq;
        int                     use_dma, dma_ch;
        struct dma_chan         *tx_chan;
@@ -183,7 +189,6 @@ struct omap_hsmmc_host {
        int                     use_reg;
        int                     req_in_progress;
        struct omap_hsmmc_next  next_data;
-
        struct  omap_mmc_platform_data  *pdata;
 };
 
@@ -493,8 +498,8 @@ static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios)
 
        if (ios->clock) {
                dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock);
-               if (dsor > 250)
-                       dsor = 250;
+               if (dsor > CLKD_MAX)
+                       dsor = CLKD_MAX;
        }
 
        return dsor;
@@ -597,25 +602,20 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)
 static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 {
        struct mmc_ios *ios = &host->mmc->ios;
-       struct omap_mmc_platform_data *pdata = host->pdata;
-       int context_loss = 0;
        u32 hctl, capa;
        unsigned long timeout;
 
-       if (pdata->get_context_loss_count) {
-               context_loss = pdata->get_context_loss_count(host->dev);
-               if (context_loss < 0)
-                       return 1;
-       }
-
-       dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
-               context_loss == host->context_loss ? "not " : "");
-       if (host->context_loss == context_loss)
-               return 1;
-
        if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
                return 1;
 
+       if (host->con == OMAP_HSMMC_READ(host->base, CON) &&
+           host->hctl == OMAP_HSMMC_READ(host->base, HCTL) &&
+           host->sysctl == OMAP_HSMMC_READ(host->base, SYSCTL) &&
+           host->capa == OMAP_HSMMC_READ(host->base, CAPA))
+               return 0;
+
+       host->context_loss++;
+
        if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
                if (host->power_mode != MMC_POWER_OFF &&
                    (1 << ios->vdd) <= MMC_VDD_23_24)
@@ -655,9 +655,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
        omap_hsmmc_set_bus_mode(host);
 
 out:
-       host->context_loss = context_loss;
-
-       dev_dbg(mmc_dev(host->mmc), "context is restored\n");
+       dev_dbg(mmc_dev(host->mmc), "context is restored: restore count %d\n",
+               host->context_loss);
        return 0;
 }
 
@@ -666,15 +665,10 @@ out:
  */
 static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
 {
-       struct omap_mmc_platform_data *pdata = host->pdata;
-       int context_loss;
-
-       if (pdata->get_context_loss_count) {
-               context_loss = pdata->get_context_loss_count(host->dev);
-               if (context_loss < 0)
-                       return;
-               host->context_loss = context_loss;
-       }
+       host->con =  OMAP_HSMMC_READ(host->base, CON);
+       host->hctl = OMAP_HSMMC_READ(host->base, HCTL);
+       host->sysctl =  OMAP_HSMMC_READ(host->base, SYSCTL);
+       host->capa = OMAP_HSMMC_READ(host->base, CAPA);
 }
 
 #else
@@ -975,8 +969,7 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
                                                   unsigned long bit)
 {
        unsigned long i = 0;
-       unsigned long limit = (loops_per_jiffy *
-                               msecs_to_jiffies(MMC_TIMEOUT_MS));
+       unsigned long limit = MMC_TIMEOUT_US;
 
        OMAP_HSMMC_WRITE(host->base, SYSCTL,
                         OMAP_HSMMC_READ(host->base, SYSCTL) | bit);
@@ -988,13 +981,13 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
        if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) {
                while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit))
                                        && (i++ < limit))
-                       cpu_relax();
+                       udelay(1);
        }
        i = 0;
 
        while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) &&
                (i++ < limit))
-               cpu_relax();
+               udelay(1);
 
        if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit)
                dev_err(mmc_dev(host->mmc),
@@ -1178,9 +1171,6 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
        struct omap_mmc_slot_data *slot = &mmc_slot(host);
        int carddetect;
 
-       if (host->suspended)
-               return IRQ_HANDLED;
-
        sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
 
        if (slot->card_detect)
@@ -1635,18 +1625,9 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
 {
        struct mmc_host *mmc = s->private;
        struct omap_hsmmc_host *host = mmc_priv(mmc);
-       int context_loss = 0;
-
-       if (host->pdata->get_context_loss_count)
-               context_loss = host->pdata->get_context_loss_count(host->dev);
 
-       seq_printf(s, "mmc%d:\n ctx_loss:\t%d:%d\n\nregs:\n",
-                       mmc->index, host->context_loss, context_loss);
-
-       if (host->suspended) {
-               seq_printf(s, "host suspended, can't read registers\n");
-               return 0;
-       }
+       seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n",
+                       mmc->index, host->context_loss);
 
        pm_runtime_get_sync(host->dev);
 
@@ -1838,13 +1819,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
        mmc->ops        = &omap_hsmmc_ops;
 
-       /*
-        * If regulator_disable can only put vcc_aux to sleep then there is
-        * no off state.
-        */
-       if (mmc_slot(host).vcc_aux_disable_is_sleep)
-               mmc_slot(host).no_off = 1;
-
        mmc->f_min = OMAP_MMC_MIN_CLOCK;
 
        if (pdata->max_freq > 0)
@@ -1874,7 +1848,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
        omap_hsmmc_context_save(host);
 
        /* This can be removed once we support PBIAS with DT */
-       if (host->dev->of_node && host->mapbase == 0x4809c000)
+       if (host->dev->of_node && res->start == 0x4809c000)
                host->pbias_disable = 1;
 
        host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
@@ -2119,23 +2093,12 @@ static void omap_hsmmc_complete(struct device *dev)
 
 static int omap_hsmmc_suspend(struct device *dev)
 {
-       int ret = 0;
        struct omap_hsmmc_host *host = dev_get_drvdata(dev);
 
        if (!host)
                return 0;
 
-       if (host && host->suspended)
-               return 0;
-
        pm_runtime_get_sync(host->dev);
-       host->suspended = 1;
-       ret = mmc_suspend_host(host->mmc);
-
-       if (ret) {
-               host->suspended = 0;
-               goto err;
-       }
 
        if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
                omap_hsmmc_disable_irq(host);
@@ -2145,23 +2108,19 @@ static int omap_hsmmc_suspend(struct device *dev)
 
        if (host->dbclk)
                clk_disable_unprepare(host->dbclk);
-err:
+
        pm_runtime_put_sync(host->dev);
-       return ret;
+       return 0;
 }
 
 /* Routine to resume the MMC device */
 static int omap_hsmmc_resume(struct device *dev)
 {
-       int ret = 0;
        struct omap_hsmmc_host *host = dev_get_drvdata(dev);
 
        if (!host)
                return 0;
 
-       if (host && !host->suspended)
-               return 0;
-
        pm_runtime_get_sync(host->dev);
 
        if (host->dbclk)
@@ -2172,16 +2131,9 @@ static int omap_hsmmc_resume(struct device *dev)
 
        omap_hsmmc_protect_card(host);
 
-       /* Notify the core to resume the host */
-       ret = mmc_resume_host(host->mmc);
-       if (ret == 0)
-               host->suspended = 0;
-
        pm_runtime_mark_last_busy(host->dev);
        pm_runtime_put_autosuspend(host->dev);
-
-       return ret;
-
+       return 0;
 }
 
 #else
index 1956a3d..32fe113 100644 (file)
@@ -880,35 +880,6 @@ static int pxamci_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int pxamci_suspend(struct device *dev)
-{
-       struct mmc_host *mmc = dev_get_drvdata(dev);
-       int ret = 0;
-
-       if (mmc)
-               ret = mmc_suspend_host(mmc);
-
-       return ret;
-}
-
-static int pxamci_resume(struct device *dev)
-{
-       struct mmc_host *mmc = dev_get_drvdata(dev);
-       int ret = 0;
-
-       if (mmc)
-               ret = mmc_resume_host(mmc);
-
-       return ret;
-}
-
-static const struct dev_pm_ops pxamci_pm_ops = {
-       .suspend        = pxamci_suspend,
-       .resume         = pxamci_resume,
-};
-#endif
-
 static struct platform_driver pxamci_driver = {
        .probe          = pxamci_probe,
        .remove         = pxamci_remove,
@@ -916,9 +887,6 @@ static struct platform_driver pxamci_driver = {
                .name   = DRIVER_NAME,
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(pxa_mmc_dt_ids),
-#ifdef CONFIG_PM
-               .pm     = &pxamci_pm_ops,
-#endif
        },
 };
 
index 375a880..c46feda 100644 (file)
@@ -364,7 +364,7 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
        struct mmc_host *mmc = host->mmc;
        struct mmc_card *card = mmc->card;
        struct mmc_data *data = mrq->data;
-       int uhs = mmc_sd_card_uhs(card);
+       int uhs = mmc_card_uhs(card);
        int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
        u8 cfg2, trans_mode;
        int err;
@@ -1197,37 +1197,6 @@ static const struct mmc_host_ops realtek_pci_sdmmc_ops = {
        .execute_tuning = sdmmc_execute_tuning,
 };
 
-#ifdef CONFIG_PM
-static int rtsx_pci_sdmmc_suspend(struct platform_device *pdev,
-               pm_message_t state)
-{
-       struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
-       struct mmc_host *mmc = host->mmc;
-       int err;
-
-       dev_dbg(sdmmc_dev(host), "--> %s\n", __func__);
-
-       err = mmc_suspend_host(mmc);
-       if (err)
-               return err;
-
-       return 0;
-}
-
-static int rtsx_pci_sdmmc_resume(struct platform_device *pdev)
-{
-       struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
-       struct mmc_host *mmc = host->mmc;
-
-       dev_dbg(sdmmc_dev(host), "--> %s\n", __func__);
-
-       return mmc_resume_host(mmc);
-}
-#else /* CONFIG_PM */
-#define rtsx_pci_sdmmc_suspend NULL
-#define rtsx_pci_sdmmc_resume NULL
-#endif /* CONFIG_PM */
-
 static void init_extra_caps(struct realtek_pci_sdmmc *host)
 {
        struct mmc_host *mmc = host->mmc;
@@ -1367,8 +1336,6 @@ static struct platform_driver rtsx_pci_sdmmc_driver = {
        .probe          = rtsx_pci_sdmmc_drv_probe,
        .remove         = rtsx_pci_sdmmc_drv_remove,
        .id_table       = rtsx_pci_sdmmc_ids,
-       .suspend        = rtsx_pci_sdmmc_suspend,
-       .resume         = rtsx_pci_sdmmc_resume,
        .driver         = {
                .owner  = THIS_MODULE,
                .name   = DRV_NAME_RTSX_PCI_SDMMC,
index 8d6794c..2fce5ea 100644 (file)
@@ -1949,39 +1949,10 @@ static struct platform_device_id s3cmci_driver_ids[] = {
 
 MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids);
 
-
-#ifdef CONFIG_PM
-
-static int s3cmci_suspend(struct device *dev)
-{
-       struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev));
-
-       return mmc_suspend_host(mmc);
-}
-
-static int s3cmci_resume(struct device *dev)
-{
-       struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev));
-
-       return mmc_resume_host(mmc);
-}
-
-static const struct dev_pm_ops s3cmci_pm = {
-       .suspend        = s3cmci_suspend,
-       .resume         = s3cmci_resume,
-};
-
-#define s3cmci_pm_ops &s3cmci_pm
-#else /* CONFIG_PM */
-#define s3cmci_pm_ops NULL
-#endif /* CONFIG_PM */
-
-
 static struct platform_driver s3cmci_driver = {
        .driver = {
                .name   = "s3c-sdi",
                .owner  = THIS_MODULE,
-               .pm     = s3cmci_pm_ops,
        },
        .id_table       = s3cmci_driver_ids,
        .probe          = s3cmci_probe,
index 85472d3..7a190fe 100644 (file)
@@ -316,19 +316,7 @@ err_pltfm_free:
 
 static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev)
 {
-       struct sdhci_host *host = platform_get_drvdata(pdev);
-       int dead;
-       u32 scratch;
-
-       dead = 0;
-       scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
-       if (scratch == (u32)-1)
-               dead = 1;
-       sdhci_remove_host(host, dead);
-
-       sdhci_free_host(host);
-
-       return 0;
+       return sdhci_pltfm_unregister(pdev);
 }
 
 static struct platform_driver sdhci_bcm_kona_driver = {
index 36fa2df..f6d8d67 100644 (file)
@@ -178,13 +178,7 @@ err:
 
 static int bcm2835_sdhci_remove(struct platform_device *pdev)
 {
-       struct sdhci_host *host = platform_get_drvdata(pdev);
-       int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
-
-       sdhci_remove_host(host, dead);
-       sdhci_pltfm_free(pdev);
-
-       return 0;
+       return sdhci_pltfm_unregister(pdev);
 }
 
 static const struct of_device_id bcm2835_sdhci_of_match[] = {
index abc8cf0..461a4c3 100644 (file)
 /* VENDOR SPEC register */
 #define ESDHC_VENDOR_SPEC              0xc0
 #define  ESDHC_VENDOR_SPEC_SDIO_QUIRK  (1 << 1)
+#define  ESDHC_VENDOR_SPEC_VSELECT     (1 << 1)
+#define  ESDHC_VENDOR_SPEC_FRC_SDCLK_ON        (1 << 8)
 #define ESDHC_WTMK_LVL                 0x44
 #define ESDHC_MIX_CTRL                 0x48
+#define  ESDHC_MIX_CTRL_DDREN          (1 << 3)
 #define  ESDHC_MIX_CTRL_AC23EN         (1 << 7)
+#define  ESDHC_MIX_CTRL_EXE_TUNE       (1 << 22)
+#define  ESDHC_MIX_CTRL_SMPCLK_SEL     (1 << 23)
+#define  ESDHC_MIX_CTRL_FBCLK_SEL      (1 << 25)
 /* Bits 3 and 6 are not SDHCI standard definitions */
 #define  ESDHC_MIX_CTRL_SDHCI_MASK     0xb7
 
+/* dll control register */
+#define ESDHC_DLL_CTRL                 0x60
+#define ESDHC_DLL_OVERRIDE_VAL_SHIFT   9
+#define ESDHC_DLL_OVERRIDE_EN_SHIFT    8
+
+/* tune control register */
+#define ESDHC_TUNE_CTRL_STATUS         0x68
+#define  ESDHC_TUNE_CTRL_STEP          1
+#define  ESDHC_TUNE_CTRL_MIN           0
+#define  ESDHC_TUNE_CTRL_MAX           ((1 << 7) - 1)
+
+#define ESDHC_TUNING_CTRL              0xcc
+#define ESDHC_STD_TUNING_EN            (1 << 24)
+/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
+#define ESDHC_TUNING_START_TAP         0x1
+
+#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64
+
+/* pinctrl state */
+#define ESDHC_PINCTRL_STATE_100MHZ     "state_100mhz"
+#define ESDHC_PINCTRL_STATE_200MHZ     "state_200mhz"
+
 /*
  * Our interpretation of the SDHCI_HOST_CONTROL register
  */
  * As a result, the TC flag is not asserted and SW  received timeout
  * exeception. Bit1 of Vendor Spec registor is used to fix it.
  */
-#define ESDHC_FLAG_MULTIBLK_NO_INT     (1 << 1)
-
-enum imx_esdhc_type {
-       IMX25_ESDHC,
-       IMX35_ESDHC,
-       IMX51_ESDHC,
-       IMX53_ESDHC,
-       IMX6Q_USDHC,
+#define ESDHC_FLAG_MULTIBLK_NO_INT     BIT(1)
+/*
+ * The flag enables the workaround for ESDHC errata ENGcm07207 which
+ * affects i.MX25 and i.MX35.
+ */
+#define ESDHC_FLAG_ENGCM07207          BIT(2)
+/*
+ * The flag tells that the ESDHC controller is an USDHC block that is
+ * integrated on the i.MX6 series.
+ */
+#define ESDHC_FLAG_USDHC               BIT(3)
+/* The IP supports manual tuning process */
+#define ESDHC_FLAG_MAN_TUNING          BIT(4)
+/* The IP supports standard tuning process */
+#define ESDHC_FLAG_STD_TUNING          BIT(5)
+/* The IP has SDHCI_CAPABILITIES_1 register */
+#define ESDHC_FLAG_HAVE_CAP1           BIT(6)
+
+struct esdhc_soc_data {
+       u32 flags;
+};
+
+static struct esdhc_soc_data esdhc_imx25_data = {
+       .flags = ESDHC_FLAG_ENGCM07207,
+};
+
+static struct esdhc_soc_data esdhc_imx35_data = {
+       .flags = ESDHC_FLAG_ENGCM07207,
+};
+
+static struct esdhc_soc_data esdhc_imx51_data = {
+       .flags = 0,
+};
+
+static struct esdhc_soc_data esdhc_imx53_data = {
+       .flags = ESDHC_FLAG_MULTIBLK_NO_INT,
+};
+
+static struct esdhc_soc_data usdhc_imx6q_data = {
+       .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING,
+};
+
+static struct esdhc_soc_data usdhc_imx6sl_data = {
+       .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+                       | ESDHC_FLAG_HAVE_CAP1,
 };
 
 struct pltfm_imx_data {
-       int flags;
        u32 scratchpad;
-       enum imx_esdhc_type devtype;
        struct pinctrl *pinctrl;
+       struct pinctrl_state *pins_default;
+       struct pinctrl_state *pins_100mhz;
+       struct pinctrl_state *pins_200mhz;
+       const struct esdhc_soc_data *socdata;
        struct esdhc_platform_data boarddata;
        struct clk *clk_ipg;
        struct clk *clk_ahb;
@@ -90,25 +157,20 @@ struct pltfm_imx_data {
                MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
                WAIT_FOR_INT,        /* sent CMD12, waiting for response INT */
        } multiblock_status;
-
+       u32 uhs_mode;
+       u32 is_ddr;
 };
 
 static struct platform_device_id imx_esdhc_devtype[] = {
        {
                .name = "sdhci-esdhc-imx25",
-               .driver_data = IMX25_ESDHC,
+               .driver_data = (kernel_ulong_t) &esdhc_imx25_data,
        }, {
                .name = "sdhci-esdhc-imx35",
-               .driver_data = IMX35_ESDHC,
+               .driver_data = (kernel_ulong_t) &esdhc_imx35_data,
        }, {
                .name = "sdhci-esdhc-imx51",
-               .driver_data = IMX51_ESDHC,
-       }, {
-               .name = "sdhci-esdhc-imx53",
-               .driver_data = IMX53_ESDHC,
-       }, {
-               .name = "sdhci-usdhc-imx6q",
-               .driver_data = IMX6Q_USDHC,
+               .driver_data = (kernel_ulong_t) &esdhc_imx51_data,
        }, {
                /* sentinel */
        }
@@ -116,38 +178,34 @@ static struct platform_device_id imx_esdhc_devtype[] = {
 MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);
 
 static const struct of_device_id imx_esdhc_dt_ids[] = {
-       { .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], },
-       { .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], },
-       { .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], },
-       { .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], },
-       { .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], },
+       { .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
+       { .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
+       { .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
+       { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
+       { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
+       { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
 
 static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
 {
-       return data->devtype == IMX25_ESDHC;
-}
-
-static inline int is_imx35_esdhc(struct pltfm_imx_data *data)
-{
-       return data->devtype == IMX35_ESDHC;
+       return data->socdata == &esdhc_imx25_data;
 }
 
-static inline int is_imx51_esdhc(struct pltfm_imx_data *data)
+static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
 {
-       return data->devtype == IMX51_ESDHC;
+       return data->socdata == &esdhc_imx53_data;
 }
 
-static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
+static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
 {
-       return data->devtype == IMX53_ESDHC;
+       return data->socdata == &usdhc_imx6q_data;
 }
 
-static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
+static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
 {
-       return data->devtype == IMX6Q_USDHC;
+       return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
 }
 
 static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
@@ -164,7 +222,21 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
        u32 val = readl(host->ioaddr + reg);
 
+       if (unlikely(reg == SDHCI_PRESENT_STATE)) {
+               u32 fsl_prss = val;
+               /* save the least 20 bits */
+               val = fsl_prss & 0x000FFFFF;
+               /* move dat[0-3] bits */
+               val |= (fsl_prss & 0x0F000000) >> 4;
+               /* move cmd line bit */
+               val |= (fsl_prss & 0x00800000) << 1;
+       }
+
        if (unlikely(reg == SDHCI_CAPABILITIES)) {
+               /* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
+               if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
+                       val &= 0xffff0000;
+
                /* In FSL esdhc IC module, only bit20 is used to indicate the
                 * ADMA2 capability of esdhc, but this bit is messed up on
                 * some SOCs (e.g. on MX25, MX35 this bit is set, but they
@@ -178,6 +250,25 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
                }
        }
 
+       if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
+               if (esdhc_is_usdhc(imx_data)) {
+                       if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
+                               val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
+                       else
+                               /* imx6q/dl does not have cap_1 register, fake one */
+                               val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
+                                       | SDHCI_SUPPORT_SDR50
+                                       | SDHCI_USE_SDR50_TUNING;
+               }
+       }
+
+       if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
+               val = 0;
+               val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
+               val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
+               val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
+       }
+
        if (unlikely(reg == SDHCI_INT_STATUS)) {
                if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
                        val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
@@ -224,7 +315,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
                }
        }
 
-       if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
+       if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
                                && (reg == SDHCI_INT_STATUS)
                                && (val & SDHCI_INT_DATA_END))) {
                        u32 v;
@@ -256,10 +347,12 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       u16 ret = 0;
+       u32 val;
 
        if (unlikely(reg == SDHCI_HOST_VERSION)) {
                reg ^= 2;
-               if (is_imx6q_usdhc(imx_data)) {
+               if (esdhc_is_usdhc(imx_data)) {
                        /*
                         * The usdhc register returns a wrong host version.
                         * Correct it here.
@@ -268,6 +361,30 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
                }
        }
 
+       if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
+               val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+               if (val & ESDHC_VENDOR_SPEC_VSELECT)
+                       ret |= SDHCI_CTRL_VDD_180;
+
+               if (esdhc_is_usdhc(imx_data)) {
+                       if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
+                               val = readl(host->ioaddr + ESDHC_MIX_CTRL);
+                       else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
+                               /* the std tuning bits is in ACMD12_ERR for imx6sl */
+                               val = readl(host->ioaddr + SDHCI_ACMD12_ERR);
+               }
+
+               if (val & ESDHC_MIX_CTRL_EXE_TUNE)
+                       ret |= SDHCI_CTRL_EXEC_TUNING;
+               if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
+                       ret |= SDHCI_CTRL_TUNED_CLK;
+
+               ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK);
+               ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
+
+               return ret;
+       }
+
        return readw(host->ioaddr + reg);
 }
 
@@ -275,10 +392,59 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       u32 new_val = 0;
 
        switch (reg) {
+       case SDHCI_CLOCK_CONTROL:
+               new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+               if (val & SDHCI_CLOCK_CARD_EN)
+                       new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
+               else
+                       new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
+                       writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
+               return;
+       case SDHCI_HOST_CONTROL2:
+               new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+               if (val & SDHCI_CTRL_VDD_180)
+                       new_val |= ESDHC_VENDOR_SPEC_VSELECT;
+               else
+                       new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
+               writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
+               imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK;
+               if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
+                       new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
+                       if (val & SDHCI_CTRL_TUNED_CLK)
+                               new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
+                       else
+                               new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+                       writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
+               } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
+                       u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
+                       u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
+                       new_val = readl(host->ioaddr + ESDHC_TUNING_CTRL);
+                       if (val & SDHCI_CTRL_EXEC_TUNING) {
+                               new_val |= ESDHC_STD_TUNING_EN |
+                                               ESDHC_TUNING_START_TAP;
+                               v |= ESDHC_MIX_CTRL_EXE_TUNE;
+                               m |= ESDHC_MIX_CTRL_FBCLK_SEL;
+                       } else {
+                               new_val &= ~ESDHC_STD_TUNING_EN;
+                               v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
+                               m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
+                       }
+
+                       if (val & SDHCI_CTRL_TUNED_CLK)
+                               v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
+                       else
+                               v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+
+                       writel(new_val, host->ioaddr + ESDHC_TUNING_CTRL);
+                       writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
+                       writel(m, host->ioaddr + ESDHC_MIX_CTRL);
+               }
+               return;
        case SDHCI_TRANSFER_MODE:
-               if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
+               if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
                                && (host->cmd->opcode == SD_IO_RW_EXTENDED)
                                && (host->cmd->data->blocks > 1)
                                && (host->cmd->data->flags & MMC_DATA_READ)) {
@@ -288,7 +454,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
                        writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
                }
 
-               if (is_imx6q_usdhc(imx_data)) {
+               if (esdhc_is_usdhc(imx_data)) {
                        u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
                        /* Swap AC23 bit */
                        if (val & SDHCI_TRNS_AUTO_CMD23) {
@@ -310,10 +476,10 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
                        val |= SDHCI_CMD_ABORTCMD;
 
                if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
-                   (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
+                   (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
                        imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
 
-               if (is_imx6q_usdhc(imx_data))
+               if (esdhc_is_usdhc(imx_data))
                        writel(val << 16,
                               host->ioaddr + SDHCI_TRANSFER_MODE);
                else
@@ -379,8 +545,10 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
                 * The reset on usdhc fails to clear MIX_CTRL register.
                 * Do it manually here.
                 */
-               if (is_imx6q_usdhc(imx_data))
+               if (esdhc_is_usdhc(imx_data)) {
                        writel(0, host->ioaddr + ESDHC_MIX_CTRL);
+                       imx_data->is_ddr = 0;
+               }
        }
 }
 
@@ -409,8 +577,60 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
                                         unsigned int clock)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       unsigned int host_clock = clk_get_rate(pltfm_host->clk);
+       int pre_div = 2;
+       int div = 1;
+       u32 temp, val;
+
+       if (clock == 0) {
+               if (esdhc_is_usdhc(imx_data)) {
+                       val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+                       writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
+                                       host->ioaddr + ESDHC_VENDOR_SPEC);
+               }
+               goto out;
+       }
+
+       if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr)
+               pre_div = 1;
+
+       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
+       temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
+               | ESDHC_CLOCK_MASK);
+       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+
+       while (host_clock / pre_div / 16 > clock && pre_div < 256)
+               pre_div *= 2;
+
+       while (host_clock / pre_div / div > clock && div < 16)
+               div++;
 
-       esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk));
+       host->mmc->actual_clock = host_clock / pre_div / div;
+       dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
+               clock, host->mmc->actual_clock);
+
+       if (imx_data->is_ddr)
+               pre_div >>= 2;
+       else
+               pre_div >>= 1;
+       div--;
+
+       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
+       temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
+               | (div << ESDHC_DIVIDER_SHIFT)
+               | (pre_div << ESDHC_PREDIV_SHIFT));
+       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+
+       if (esdhc_is_usdhc(imx_data)) {
+               val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+               writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
+               host->ioaddr + ESDHC_VENDOR_SPEC);
+       }
+
+       mdelay(1);
+out:
+       host->clock = clock;
 }
 
 static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
@@ -454,7 +674,192 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
        return 0;
 }
 
-static const struct sdhci_ops sdhci_esdhc_ops = {
+static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
+{
+       u32 reg;
+
+       /* FIXME: delay a bit for card to be ready for next tuning due to errors */
+       mdelay(1);
+
+       reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
+       reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
+                       ESDHC_MIX_CTRL_FBCLK_SEL;
+       writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
+       writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+       dev_dbg(mmc_dev(host->mmc),
+               "tunning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
+                       val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
+}
+
+static void esdhc_request_done(struct mmc_request *mrq)
+{
+       complete(&mrq->completion);
+}
+
+static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
+{
+       struct mmc_command cmd = {0};
+       struct mmc_request mrq = {0};
+       struct mmc_data data = {0};
+       struct scatterlist sg;
+       char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
+
+       cmd.opcode = opcode;
+       cmd.arg = 0;
+       cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+       data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN;
+       data.blocks = 1;
+       data.flags = MMC_DATA_READ;
+       data.sg = &sg;
+       data.sg_len = 1;
+
+       sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern));
+
+       mrq.cmd = &cmd;
+       mrq.cmd->mrq = &mrq;
+       mrq.data = &data;
+       mrq.data->mrq = &mrq;
+       mrq.cmd->data = mrq.data;
+
+       mrq.done = esdhc_request_done;
+       init_completion(&(mrq.completion));
+
+       disable_irq(host->irq);
+       spin_lock(&host->lock);
+       host->mrq = &mrq;
+
+       sdhci_send_command(host, mrq.cmd);
+
+       spin_unlock(&host->lock);
+       enable_irq(host->irq);
+
+       wait_for_completion(&mrq.completion);
+
+       if (cmd.error)
+               return cmd.error;
+       if (data.error)
+               return data.error;
+
+       return 0;
+}
+
+static void esdhc_post_tuning(struct sdhci_host *host)
+{
+       u32 reg;
+
+       reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
+       reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
+       writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
+}
+
+static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
+{
+       int min, max, avg, ret;
+
+       /* find the mininum delay first which can pass tuning */
+       min = ESDHC_TUNE_CTRL_MIN;
+       while (min < ESDHC_TUNE_CTRL_MAX) {
+               esdhc_prepare_tuning(host, min);
+               if (!esdhc_send_tuning_cmd(host, opcode))
+                       break;
+               min += ESDHC_TUNE_CTRL_STEP;
+       }
+
+       /* find the maxinum delay which can not pass tuning */
+       max = min + ESDHC_TUNE_CTRL_STEP;
+       while (max < ESDHC_TUNE_CTRL_MAX) {
+               esdhc_prepare_tuning(host, max);
+               if (esdhc_send_tuning_cmd(host, opcode)) {
+                       max -= ESDHC_TUNE_CTRL_STEP;
+                       break;
+               }
+               max += ESDHC_TUNE_CTRL_STEP;
+       }
+
+       /* use average delay to get the best timing */
+       avg = (min + max) / 2;
+       esdhc_prepare_tuning(host, avg);
+       ret = esdhc_send_tuning_cmd(host, opcode);
+       esdhc_post_tuning(host);
+
+       dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
+               ret ? "failed" : "passed", avg, ret);
+
+       return ret;
+}
+
+static int esdhc_change_pinstate(struct sdhci_host *host,
+                                               unsigned int uhs)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       struct pinctrl_state *pinctrl;
+
+       dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
+
+       if (IS_ERR(imx_data->pinctrl) ||
+               IS_ERR(imx_data->pins_default) ||
+               IS_ERR(imx_data->pins_100mhz) ||
+               IS_ERR(imx_data->pins_200mhz))
+               return -EINVAL;
+
+       switch (uhs) {
+       case MMC_TIMING_UHS_SDR50:
+               pinctrl = imx_data->pins_100mhz;
+               break;
+       case MMC_TIMING_UHS_SDR104:
+               pinctrl = imx_data->pins_200mhz;
+               break;
+       default:
+               /* back to default state for other legacy timing */
+               pinctrl = imx_data->pins_default;
+       }
+
+       return pinctrl_select_state(imx_data->pinctrl, pinctrl);
+}
+
+static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+
+       switch (uhs) {
+       case MMC_TIMING_UHS_SDR12:
+               imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12;
+               break;
+       case MMC_TIMING_UHS_SDR25:
+               imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25;
+               break;
+       case MMC_TIMING_UHS_SDR50:
+               imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50;
+               break;
+       case MMC_TIMING_UHS_SDR104:
+               imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104;
+               break;
+       case MMC_TIMING_UHS_DDR50:
+               imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50;
+               writel(readl(host->ioaddr + ESDHC_MIX_CTRL) |
+                               ESDHC_MIX_CTRL_DDREN,
+                               host->ioaddr + ESDHC_MIX_CTRL);
+               imx_data->is_ddr = 1;
+               if (boarddata->delay_line) {
+                       u32 v;
+                       v = boarddata->delay_line <<
+                               ESDHC_DLL_OVERRIDE_VAL_SHIFT |
+                               (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
+                       if (is_imx53_esdhc(imx_data))
+                               v <<= 1;
+                       writel(v, host->ioaddr + ESDHC_DLL_CTRL);
+               }
+               break;
+       }
+
+       return esdhc_change_pinstate(host, uhs);
+}
+
+static struct sdhci_ops sdhci_esdhc_ops = {
        .read_l = esdhc_readl_le,
        .read_w = esdhc_readw_le,
        .write_l = esdhc_writel_le,
@@ -465,6 +870,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
        .get_min_clock = esdhc_pltfm_get_min_clock,
        .get_ro = esdhc_pltfm_get_ro,
        .platform_bus_width = esdhc_pltfm_bus_width,
+       .set_uhs_signaling = esdhc_set_uhs_signaling,
 };
 
 static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
@@ -506,6 +912,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
 
        of_property_read_u32(np, "max-frequency", &boarddata->f_max);
 
+       if (of_find_property(np, "no-1-8-v", NULL))
+               boarddata->support_vsel = false;
+       else
+               boarddata->support_vsel = true;
+
+       if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
+               boarddata->delay_line = 0;
+
        return 0;
 }
 #else
@@ -539,9 +953,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                goto free_sdhci;
        }
 
-       if (of_id)
-               pdev->id_entry = of_id->data;
-       imx_data->devtype = pdev->id_entry->driver_data;
+       imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *)
+                                                 pdev->id_entry->driver_data;
        pltfm_host->priv = imx_data;
 
        imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
@@ -568,29 +981,39 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
        clk_prepare_enable(imx_data->clk_ipg);
        clk_prepare_enable(imx_data->clk_ahb);
 
-       imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
        if (IS_ERR(imx_data->pinctrl)) {
                err = PTR_ERR(imx_data->pinctrl);
                goto disable_clk;
        }
 
+       imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl,
+                                               PINCTRL_STATE_DEFAULT);
+       if (IS_ERR(imx_data->pins_default)) {
+               err = PTR_ERR(imx_data->pins_default);
+               dev_err(mmc_dev(host->mmc), "could not get default state\n");
+               goto disable_clk;
+       }
+
        host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
 
-       if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data))
+       if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207)
                /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
                host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
                        | SDHCI_QUIRK_BROKEN_ADMA;
 
-       if (is_imx53_esdhc(imx_data))
-               imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
-
        /*
         * The imx6q ROM code will change the default watermark level setting
         * to something insane.  Change it back here.
         */
-       if (is_imx6q_usdhc(imx_data))
+       if (esdhc_is_usdhc(imx_data)) {
                writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
+               host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
+       }
 
+       if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
+               sdhci_esdhc_ops.platform_execute_tuning =
+                                       esdhc_executing_tuning;
        boarddata = &imx_data->boarddata;
        if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
                if (!host->mmc->parent->platform_data) {
@@ -650,6 +1073,23 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                break;
        }
 
+       /* sdr50 and sdr104 needs work on 1.8v signal voltage */
+       if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) {
+               imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
+                                               ESDHC_PINCTRL_STATE_100MHZ);
+               imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
+                                               ESDHC_PINCTRL_STATE_200MHZ);
+               if (IS_ERR(imx_data->pins_100mhz) ||
+                               IS_ERR(imx_data->pins_200mhz)) {
+                       dev_warn(mmc_dev(host->mmc),
+                               "could not get ultra high speed state, work on normal mode\n");
+                       /* fall back to not support uhs by specify no 1.8v quirk */
+                       host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+               }
+       } else {
+               host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+       }
+
        err = sdhci_add_host(host);
        if (err)
                goto disable_clk;
index a2a0642..a7d9f95 100644 (file)
 
 #define ESDHC_HOST_CONTROL_RES 0x05
 
-static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock,
-                                  unsigned int host_clock)
-{
-       int pre_div = 2;
-       int div = 1;
-       u32 temp;
-
-       if (clock == 0)
-               goto out;
-
-       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
-       temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
-               | ESDHC_CLOCK_MASK);
-       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
-
-       while (host_clock / pre_div / 16 > clock && pre_div < 256)
-               pre_div *= 2;
-
-       while (host_clock / pre_div / div > clock && div < 16)
-               div++;
-
-       dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
-               clock, host_clock / pre_div / div);
-
-       pre_div >>= 1;
-       div--;
-
-       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
-       temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
-               | (div << ESDHC_DIVIDER_SHIFT)
-               | (pre_div << ESDHC_PREDIV_SHIFT));
-       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
-       mdelay(1);
-out:
-       host->clock = clock;
-}
-
 #endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */
index e328252..0b24997 100644 (file)
@@ -199,6 +199,14 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
 
 static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
 {
+
+       int pre_div = 2;
+       int div = 1;
+       u32 temp;
+
+       if (clock == 0)
+               goto out;
+
        /* Workaround to reduce the clock frequency for p1010 esdhc */
        if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
                if (clock > 20000000)
@@ -207,8 +215,31 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
                        clock -= 5000000;
        }
 
-       /* Set the clock */
-       esdhc_set_clock(host, clock, host->max_clk);
+       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
+       temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
+               | ESDHC_CLOCK_MASK);
+       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+
+       while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
+               pre_div *= 2;
+
+       while (host->max_clk / pre_div / div > clock && div < 16)
+               div++;
+
+       dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
+               clock, host->max_clk / pre_div / div);
+
+       pre_div >>= 1;
+       div--;
+
+       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
+       temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
+               | (div << ESDHC_DIVIDER_SHIFT)
+               | (pre_div << ESDHC_PREDIV_SHIFT));
+       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+       mdelay(1);
+out:
+       host->clock = clock;
 }
 
 #ifdef CONFIG_PM
index d7d6bc8..8f75381 100644 (file)
 #define PCI_DEVICE_ID_INTEL_BYT_SDIO   0x0f15
 #define PCI_DEVICE_ID_INTEL_BYT_SD     0x0f16
 #define PCI_DEVICE_ID_INTEL_BYT_EMMC2  0x0f50
+#define PCI_DEVICE_ID_INTEL_MRFL_MMC   0x1190
+#define PCI_DEVICE_ID_INTEL_CLV_SDIO0  0x08f9
+#define PCI_DEVICE_ID_INTEL_CLV_SDIO1  0x08fa
+#define PCI_DEVICE_ID_INTEL_CLV_SDIO2  0x08fb
+#define PCI_DEVICE_ID_INTEL_CLV_EMMC0  0x08e5
+#define PCI_DEVICE_ID_INTEL_CLV_EMMC1  0x08e6
 
 /*
  * PCI registers
@@ -356,6 +362,28 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
        .allow_runtime_pm = true,
 };
 
+/* Define Host controllers for Intel Merrifield platform */
+#define INTEL_MRFL_EMMC_0      0
+#define INTEL_MRFL_EMMC_1      1
+
+static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
+{
+       if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_0) &&
+           (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1))
+               /* SD support is not ready yet */
+               return -ENODEV;
+
+       slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
+                                MMC_CAP_1_8V_DDR;
+
+       return 0;
+}
+
+static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
+       .quirks         = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+       .probe_slot     = intel_mrfl_mmc_probe_slot,
+};
+
 /* O2Micro extra registers */
 #define O2_SD_LOCK_WP          0xD3
 #define O2_SD_MULTI_VCC3V      0xEE
@@ -939,6 +967,54 @@ static const struct pci_device_id pci_ids[] = {
                .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_emmc,
        },
 
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_CLV_SDIO0,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mfd_sd,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_CLV_SDIO1,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mfd_sdio,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_CLV_SDIO2,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mfd_sdio,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_CLV_EMMC0,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mfd_emmc,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_CLV_EMMC1,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mfd_emmc,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_MRFL_MMC,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mrfl_mmc,
+       },
        {
                .vendor         = PCI_VENDOR_ID_O2,
                .device         = PCI_DEVICE_ID_O2_8120,
index 7a7fb4f..bd8a098 100644 (file)
@@ -49,7 +49,6 @@ static unsigned int debug_quirks2;
 
 static void sdhci_finish_data(struct sdhci_host *);
 
-static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
 static void sdhci_finish_command(struct sdhci_host *);
 static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
 static void sdhci_tuning_timer(unsigned long data);
@@ -981,7 +980,7 @@ static void sdhci_finish_data(struct sdhci_host *host)
                tasklet_schedule(&host->finish_tasklet);
 }
 
-static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 {
        int flags;
        u32 mask;
@@ -1053,6 +1052,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 
        sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
 }
+EXPORT_SYMBOL_GPL(sdhci_send_command);
 
 static void sdhci_finish_command(struct sdhci_host *host)
 {
@@ -1435,7 +1435,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
        }
 
        if (host->version >= SDHCI_SPEC_300 &&
-               (ios->power_mode == MMC_POWER_UP))
+               (ios->power_mode == MMC_POWER_UP) &&
+               !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
                sdhci_enable_preset_value(host, false);
 
        sdhci_set_clock(host, ios->clock);
@@ -1875,6 +1876,14 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
                return 0;
        }
 
+       if (host->ops->platform_execute_tuning) {
+               spin_unlock(&host->lock);
+               enable_irq(host->irq);
+               err = host->ops->platform_execute_tuning(host, opcode);
+               sdhci_runtime_pm_put(host);
+               return err;
+       }
+
        sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
 
        /*
@@ -1981,6 +1990,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
        if (!tuning_loop_counter || !timeout) {
                ctrl &= ~SDHCI_CTRL_TUNED_CLK;
                sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+               err = -EIO;
        } else {
                if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
                        pr_info(DRIVER_NAME ": Tuning procedure"
@@ -2491,6 +2501,14 @@ again:
        result = IRQ_HANDLED;
 
        intmask = sdhci_readl(host, SDHCI_INT_STATUS);
+
+       /*
+        * If we know we'll call the driver to signal SDIO IRQ, disregard
+        * further indications of Card Interrupt in the status to avoid a
+        * needless loop.
+        */
+       if (cardint)
+               intmask &= ~SDHCI_INT_CARD_INT;
        if (intmask && --max_loops)
                goto again;
 out:
@@ -2546,8 +2564,6 @@ EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);
 
 int sdhci_suspend_host(struct sdhci_host *host)
 {
-       int ret;
-
        if (host->ops->platform_suspend)
                host->ops->platform_suspend(host);
 
@@ -2559,19 +2575,6 @@ int sdhci_suspend_host(struct sdhci_host *host)
                host->flags &= ~SDHCI_NEEDS_RETUNING;
        }
 
-       ret = mmc_suspend_host(host->mmc);
-       if (ret) {
-               if (host->flags & SDHCI_USING_RETUNING_TIMER) {
-                       host->flags |= SDHCI_NEEDS_RETUNING;
-                       mod_timer(&host->tuning_timer, jiffies +
-                                       host->tuning_count * HZ);
-               }
-
-               sdhci_enable_card_detection(host);
-
-               return ret;
-       }
-
        if (!device_may_wakeup(mmc_dev(host->mmc))) {
                sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
                free_irq(host->irq, host);
@@ -2579,14 +2582,14 @@ int sdhci_suspend_host(struct sdhci_host *host)
                sdhci_enable_irq_wakeups(host);
                enable_irq_wake(host->irq);
        }
-       return ret;
+       return 0;
 }
 
 EXPORT_SYMBOL_GPL(sdhci_suspend_host);
 
 int sdhci_resume_host(struct sdhci_host *host)
 {
-       int ret;
+       int ret = 0;
 
        if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
                if (host->ops->enable_dma)
@@ -2615,7 +2618,6 @@ int sdhci_resume_host(struct sdhci_host *host)
                mmiowb();
        }
 
-       ret = mmc_resume_host(host->mmc);
        sdhci_enable_card_detection(host);
 
        if (host->ops->platform_resume)
index b037f18..0a3ed01 100644 (file)
@@ -288,6 +288,7 @@ struct sdhci_ops {
        unsigned int    (*get_ro)(struct sdhci_host *host);
        void    (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
        void    (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
+       int     (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
        int     (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
        void    (*hw_reset)(struct sdhci_host *host);
        void    (*platform_suspend)(struct sdhci_host *host);
@@ -393,6 +394,8 @@ static inline void *sdhci_priv(struct sdhci_host *host)
 extern void sdhci_card_detect(struct sdhci_host *host);
 extern int sdhci_add_host(struct sdhci_host *host);
 extern void sdhci_remove_host(struct sdhci_host *host, int dead);
+extern void sdhci_send_command(struct sdhci_host *host,
+                               struct mmc_command *cmd);
 
 #ifdef CONFIG_PM
 extern int sdhci_suspend_host(struct sdhci_host *host);
index 50adbd1..b7e3057 100644 (file)
@@ -516,9 +516,7 @@ static void sdricoh_pcmcia_detach(struct pcmcia_device *link)
 #ifdef CONFIG_PM
 static int sdricoh_pcmcia_suspend(struct pcmcia_device *link)
 {
-       struct mmc_host *mmc = link->priv;
        dev_dbg(&link->dev, "suspend\n");
-       mmc_suspend_host(mmc);
        return 0;
 }
 
@@ -527,7 +525,6 @@ static int sdricoh_pcmcia_resume(struct pcmcia_device *link)
        struct mmc_host *mmc = link->priv;
        dev_dbg(&link->dev, "resume\n");
        sdricoh_reset(mmc_priv(mmc));
-       mmc_resume_host(mmc);
        return 0;
 }
 #else
index 36629a0..d032b08 100644 (file)
@@ -964,7 +964,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
 static int sh_mmcif_clk_update(struct sh_mmcif_host *host)
 {
-       int ret = clk_enable(host->hclk);
+       int ret = clk_prepare_enable(host->hclk);
 
        if (!ret) {
                host->clk = clk_get_rate(host->hclk);
@@ -1018,7 +1018,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                }
                if (host->power) {
                        pm_runtime_put_sync(&host->pd->dev);
-                       clk_disable(host->hclk);
+                       clk_disable_unprepare(host->hclk);
                        host->power = false;
                        if (ios->power_mode == MMC_POWER_OFF)
                                sh_mmcif_set_power(host, ios);
@@ -1466,7 +1466,7 @@ static int sh_mmcif_probe(struct platform_device *pdev)
 
        mutex_init(&host->thread_lock);
 
-       clk_disable(host->hclk);
+       clk_disable_unprepare(host->hclk);
        ret = mmc_add_host(mmc);
        if (ret < 0)
                goto emmcaddh;
@@ -1487,7 +1487,7 @@ ereqirq1:
 ereqirq0:
        pm_runtime_suspend(&pdev->dev);
 eresume:
-       clk_disable(host->hclk);
+       clk_disable_unprepare(host->hclk);
 eclkupdate:
        clk_put(host->hclk);
 eclkget:
@@ -1505,7 +1505,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)
        int irq[2];
 
        host->dying = true;
-       clk_enable(host->hclk);
+       clk_prepare_enable(host->hclk);
        pm_runtime_get_sync(&pdev->dev);
 
        dev_pm_qos_hide_latency_limit(&pdev->dev);
@@ -1530,7 +1530,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)
        if (irq[1] >= 0)
                free_irq(irq[1], host);
 
-       clk_disable(host->hclk);
+       clk_disable_unprepare(host->hclk);
        mmc_free_host(host->mmc);
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
@@ -1538,28 +1538,21 @@ static int sh_mmcif_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int sh_mmcif_suspend(struct device *dev)
 {
        struct sh_mmcif_host *host = dev_get_drvdata(dev);
-       int ret = mmc_suspend_host(host->mmc);
 
-       if (!ret)
-               sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
+       sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
 
-       return ret;
+       return 0;
 }
 
 static int sh_mmcif_resume(struct device *dev)
 {
-       struct sh_mmcif_host *host = dev_get_drvdata(dev);
-
-       return mmc_resume_host(host->mmc);
+       return 0;
 }
-#else
-#define sh_mmcif_suspend       NULL
-#define sh_mmcif_resume                NULL
-#endif /* CONFIG_PM */
+#endif
 
 static const struct of_device_id mmcif_of_match[] = {
        { .compatible = "renesas,sh-mmcif" },
@@ -1568,8 +1561,7 @@ static const struct of_device_id mmcif_of_match[] = {
 MODULE_DEVICE_TABLE(of, mmcif_of_match);
 
 static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
-       .suspend = sh_mmcif_suspend,
-       .resume = sh_mmcif_resume,
+       SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
 };
 
 static struct platform_driver sh_mmcif_driver = {
index 43d9628..d1760eb 100644 (file)
@@ -1030,7 +1030,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
 
 static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state)
 {
-       return mmc_suspend_host(tifm_get_drvdata(sock));
+       return 0;
 }
 
 static int tifm_sd_resume(struct tifm_dev *sock)
@@ -1044,8 +1044,6 @@ static int tifm_sd_resume(struct tifm_dev *sock)
 
        if (rc)
                host->eject = 1;
-       else
-               rc = mmc_resume_host(mmc);
 
        return rc;
 }
index b380225..f3b2d8c 100644 (file)
@@ -1145,12 +1145,9 @@ int tmio_mmc_host_suspend(struct device *dev)
 {
        struct mmc_host *mmc = dev_get_drvdata(dev);
        struct tmio_mmc_host *host = mmc_priv(mmc);
-       int ret = mmc_suspend_host(mmc);
 
-       if (!ret)
-               tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
-
-       return ret;
+       tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
+       return 0;
 }
 EXPORT_SYMBOL(tmio_mmc_host_suspend);
 
@@ -1163,7 +1160,7 @@ int tmio_mmc_host_resume(struct device *dev)
 
        /* The MMC core will perform the complete set up */
        host->resuming = true;
-       return mmc_resume_host(mmc);
+       return 0;
 }
 EXPORT_SYMBOL(tmio_mmc_host_resume);
 
index 4f84586..63fac78 100644 (file)
@@ -1269,21 +1269,18 @@ static void via_init_sdc_pm(struct via_crdr_mmc_host *host)
 static int via_sd_suspend(struct pci_dev *pcidev, pm_message_t state)
 {
        struct via_crdr_mmc_host *host;
-       int ret = 0;
 
        host = pci_get_drvdata(pcidev);
 
        via_save_pcictrlreg(host);
        via_save_sdcreg(host);
 
-       ret = mmc_suspend_host(host->mmc);
-
        pci_save_state(pcidev);
        pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0);
        pci_disable_device(pcidev);
        pci_set_power_state(pcidev, pci_choose_state(pcidev, state));
 
-       return ret;
+       return 0;
 }
 
 static int via_sd_resume(struct pci_dev *pcidev)
@@ -1316,8 +1313,6 @@ static int via_sd_resume(struct pci_dev *pcidev)
        via_restore_pcictrlreg(sdhost);
        via_init_sdc_pm(sdhost);
 
-       ret = mmc_resume_host(sdhost->mmc);
-
        return ret;
 }
 
index e9028ad..4262296 100644 (file)
@@ -2392,26 +2392,12 @@ static void vub300_disconnect(struct usb_interface *interface)
 #ifdef CONFIG_PM
 static int vub300_suspend(struct usb_interface *intf, pm_message_t message)
 {
-       struct vub300_mmc_host *vub300 = usb_get_intfdata(intf);
-       if (!vub300 || !vub300->mmc) {
-               return 0;
-       } else {
-               struct mmc_host *mmc = vub300->mmc;
-               mmc_suspend_host(mmc);
-               return 0;
-       }
+       return 0;
 }
 
 static int vub300_resume(struct usb_interface *intf)
 {
-       struct vub300_mmc_host *vub300 = usb_get_intfdata(intf);
-       if (!vub300 || !vub300->mmc) {
-               return 0;
-       } else {
-               struct mmc_host *mmc = vub300->mmc;
-               mmc_resume_host(mmc);
-               return 0;
-       }
+       return 0;
 }
 #else
 #define vub300_suspend NULL
index e954b77..1defd5e 100644 (file)
@@ -1814,28 +1814,11 @@ static void wbsd_pnp_remove(struct pnp_dev *dev)
 
 #ifdef CONFIG_PM
 
-static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
-{
-       BUG_ON(host == NULL);
-
-       return mmc_suspend_host(host->mmc);
-}
-
-static int wbsd_resume(struct wbsd_host *host)
-{
-       BUG_ON(host == NULL);
-
-       wbsd_init_device(host);
-
-       return mmc_resume_host(host->mmc);
-}
-
 static int wbsd_platform_suspend(struct platform_device *dev,
                                 pm_message_t state)
 {
        struct mmc_host *mmc = platform_get_drvdata(dev);
        struct wbsd_host *host;
-       int ret;
 
        if (mmc == NULL)
                return 0;
@@ -1844,12 +1827,7 @@ static int wbsd_platform_suspend(struct platform_device *dev,
 
        host = mmc_priv(mmc);
 
-       ret = wbsd_suspend(host, state);
-       if (ret)
-               return ret;
-
        wbsd_chip_poweroff(host);
-
        return 0;
 }
 
@@ -1872,7 +1850,8 @@ static int wbsd_platform_resume(struct platform_device *dev)
         */
        mdelay(5);
 
-       return wbsd_resume(host);
+       wbsd_init_device(host);
+       return 0;
 }
 
 #ifdef CONFIG_PNP
@@ -1880,16 +1859,12 @@ static int wbsd_platform_resume(struct platform_device *dev)
 static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
 {
        struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
-       struct wbsd_host *host;
 
        if (mmc == NULL)
                return 0;
 
        DBGF("Suspending...\n");
-
-       host = mmc_priv(mmc);
-
-       return wbsd_suspend(host, state);
+       return 0;
 }
 
 static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
@@ -1922,7 +1897,8 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
         */
        mdelay(5);
 
-       return wbsd_resume(host);
+       wbsd_init_device(host);
+       return 0;
 }
 
 #endif /* CONFIG_PNP */
index 34231d5..e902ed7 100644 (file)
@@ -212,28 +212,14 @@ struct wmt_mci_priv {
 
 static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable)
 {
-       u32 reg_tmp;
-       if (enable) {
-               if (priv->power_inverted) {
-                       reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
-                       writeb(reg_tmp | BM_SD_OFF,
-                              priv->sdmmc_base + SDMMC_BUSMODE);
-               } else {
-                       reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
-                       writeb(reg_tmp & (~BM_SD_OFF),
-                              priv->sdmmc_base + SDMMC_BUSMODE);
-               }
-       } else {
-               if (priv->power_inverted) {
-                       reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
-                       writeb(reg_tmp & (~BM_SD_OFF),
-                              priv->sdmmc_base + SDMMC_BUSMODE);
-               } else {
-                       reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
-                       writeb(reg_tmp | BM_SD_OFF,
-                              priv->sdmmc_base + SDMMC_BUSMODE);
-               }
-       }
+       u32 reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+
+       if (enable ^ priv->power_inverted)
+               reg_tmp &= ~BM_SD_OFF;
+       else
+               reg_tmp |= BM_SD_OFF;
+
+       writeb(reg_tmp, priv->sdmmc_base + SDMMC_BUSMODE);
 }
 
 static void wmt_mci_read_response(struct mmc_host *mmc)
@@ -939,28 +925,23 @@ static int wmt_mci_suspend(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct mmc_host *mmc = platform_get_drvdata(pdev);
        struct wmt_mci_priv *priv;
-       int ret;
 
        if (!mmc)
                return 0;
 
        priv = mmc_priv(mmc);
-       ret = mmc_suspend_host(mmc);
-
-       if (!ret) {
-               reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
-               writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
-                      SDMMC_BUSMODE);
+       reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+       writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
+              SDMMC_BUSMODE);
 
-               reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
-               writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN);
+       reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
+       writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN);
 
-               writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
-               writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
+       writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
+       writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
 
-               clk_disable(priv->clk_sdmmc);
-       }
-       return ret;
+       clk_disable(priv->clk_sdmmc);
+       return 0;
 }
 
 static int wmt_mci_resume(struct device *dev)
@@ -969,7 +950,6 @@ static int wmt_mci_resume(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct mmc_host *mmc = platform_get_drvdata(pdev);
        struct wmt_mci_priv *priv;
-       int ret = 0;
 
        if (mmc) {
                priv = mmc_priv(mmc);
@@ -987,10 +967,9 @@ static int wmt_mci_resume(struct device *dev)
                writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base +
                       SDMMC_INTMASK0);
 
-               ret = mmc_resume_host(mmc);
        }
 
-       return ret;
+       return 0;
 }
 
 static const struct dev_pm_ops wmt_mci_pm = {
index d78a97d..59f08c4 100644 (file)
@@ -375,8 +375,7 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
 
        dma_dev = host->dma_chan->device;
 
-       flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP |
-               DMA_COMPL_SKIP_DEST_UNMAP;
+       flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
 
        phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
        if (dma_mapping_error(dma_dev->dev, phys_addr)) {
index bd1cb67..1b0265e 100644 (file)
@@ -491,7 +491,7 @@ static uint8_t docg4_read_byte(struct mtd_info *mtd)
                return status;
        }
 
-       dev_warn(doc->dev, "unexpectd call to read_byte()\n");
+       dev_warn(doc->dev, "unexpected call to read_byte()\n");
 
        return 0;
 }
index 3dc1a75..8b27522 100644 (file)
@@ -573,8 +573,6 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
        dma_dev = chan->device;
        dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction);
 
-       flags |= DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP;
-
        if (direction == DMA_TO_DEVICE) {
                dma_src = dma_addr;
                dma_dst = host->data_pa;
index 4edea7f..9dfdb06 100644 (file)
@@ -396,7 +396,7 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq)
 
        if (useirq) {
                if (!host->devtype_data->check_int(host)) {
-                       INIT_COMPLETION(host->op_completion);
+                       reinit_completion(&host->op_completion);
                        irq_control(host, 1);
                        wait_for_completion(&host->op_completion);
                }
index 9dcf02d..325930d 100644 (file)
@@ -181,7 +181,7 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read)
        /* Set dma direction */
        dev->dma_dir = do_read;
        dev->dma_stage = 1;
-       INIT_COMPLETION(dev->dma_done);
+       reinit_completion(&dev->dma_done);
 
        dbg_verbose("doing dma %s ", do_read ? "read" : "write");
 
index 2362909..6547c84 100644 (file)
@@ -159,7 +159,7 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
                                syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
                }
 
-               INIT_COMPLETION(c->irq_done);
+               reinit_completion(&c->irq_done);
                if (c->gpio_irq) {
                        result = gpio_get_value(c->gpio_irq);
                        if (result == -1) {
@@ -349,7 +349,7 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area,
        omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
                                 dma_dst, 0, 0);
 
-       INIT_COMPLETION(c->dma_done);
+       reinit_completion(&c->dma_done);
        omap_start_dma(c->dma_channel);
 
        timeout = jiffies + msecs_to_jiffies(20);
@@ -420,7 +420,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area,
        omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
                                 dma_dst, 0, 0);
 
-       INIT_COMPLETION(c->dma_done);
+       reinit_completion(&c->dma_done);
        omap_start_dma(c->dma_channel);
 
        timeout = jiffies + msecs_to_jiffies(20);
@@ -499,7 +499,7 @@ static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area,
        omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
                                 dma_dst, 0, 0);
 
-       INIT_COMPLETION(c->dma_done);
+       reinit_completion(&c->dma_done);
        omap_start_dma(c->dma_channel);
        wait_for_completion(&c->dma_done);
 
@@ -544,7 +544,7 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area,
        omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
                                 dma_dst, 0, 0);
 
-       INIT_COMPLETION(c->dma_done);
+       reinit_completion(&c->dma_done);
        omap_start_dma(c->dma_channel);
        wait_for_completion(&c->dma_done);
 
index bc8fd36..0ec2a7e 100644 (file)
@@ -524,8 +524,9 @@ static ssize_t bonding_store_arp_interval(struct device *d,
                goto out;
        }
        if (bond->params.mode == BOND_MODE_ALB ||
-           bond->params.mode == BOND_MODE_TLB) {
-               pr_info("%s: ARP monitoring cannot be used with ALB/TLB. Only MII monitoring is supported on %s.\n",
+           bond->params.mode == BOND_MODE_TLB ||
+           bond->params.mode == BOND_MODE_8023AD) {
+               pr_info("%s: ARP monitoring cannot be used with ALB/TLB/802.3ad. Only MII monitoring is supported on %s.\n",
                        bond->dev->name, bond->dev->name);
                ret = -EINVAL;
                goto out;
@@ -603,15 +604,14 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                return restart_syscall();
 
        targets = bond->params.arp_targets;
-       newtarget = in_aton(buf + 1);
+       if (!in4_pton(buf + 1, -1, (u8 *)&newtarget, -1, NULL) ||
+           IS_IP_TARGET_UNUSABLE_ADDRESS(newtarget)) {
+               pr_err("%s: invalid ARP target %pI4 specified for addition\n",
+                      bond->dev->name, &newtarget);
+               goto out;
+       }
        /* look for adds */
        if (buf[0] == '+') {
-               if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) {
-                       pr_err("%s: invalid ARP target %pI4 specified for addition\n",
-                              bond->dev->name, &newtarget);
-                       goto out;
-               }
-
                if (bond_get_targets_ip(targets, newtarget) != -1) { /* dup */
                        pr_err("%s: ARP target %pI4 is already present\n",
                               bond->dev->name, &newtarget);
@@ -634,12 +634,6 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                targets[ind] = newtarget;
                write_unlock_bh(&bond->lock);
        } else if (buf[0] == '-')       {
-               if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) {
-                       pr_err("%s: invalid ARP target %pI4 specified for removal\n",
-                              bond->dev->name, &newtarget);
-                       goto out;
-               }
-
                ind = bond_get_targets_ip(targets, newtarget);
                if (ind == -1) {
                        pr_err("%s: unable to remove nonexistent ARP target %pI4.\n",
@@ -701,6 +695,8 @@ static ssize_t bonding_store_downdelay(struct device *d,
        int new_value, ret = count;
        struct bonding *bond = to_bond(d);
 
+       if (!rtnl_trylock())
+               return restart_syscall();
        if (!(bond->params.miimon)) {
                pr_err("%s: Unable to set down delay as MII monitoring is disabled\n",
                       bond->dev->name);
@@ -734,6 +730,7 @@ static ssize_t bonding_store_downdelay(struct device *d,
        }
 
 out:
+       rtnl_unlock();
        return ret;
 }
 static DEVICE_ATTR(downdelay, S_IRUGO | S_IWUSR,
@@ -756,6 +753,8 @@ static ssize_t bonding_store_updelay(struct device *d,
        int new_value, ret = count;
        struct bonding *bond = to_bond(d);
 
+       if (!rtnl_trylock())
+               return restart_syscall();
        if (!(bond->params.miimon)) {
                pr_err("%s: Unable to set up delay as MII monitoring is disabled\n",
                       bond->dev->name);
@@ -789,6 +788,7 @@ static ssize_t bonding_store_updelay(struct device *d,
        }
 
 out:
+       rtnl_unlock();
        return ret;
 }
 static DEVICE_ATTR(updelay, S_IRUGO | S_IWUSR,
index 77a07a1..ca31286 100644 (file)
@@ -63,6 +63,9 @@
                (((mode) == BOND_MODE_TLB) ||   \
                 ((mode) == BOND_MODE_ALB))
 
+#define IS_IP_TARGET_UNUSABLE_ADDRESS(a)       \
+       ((htonl(INADDR_BROADCAST) == a) ||      \
+        ipv4_is_zeronet(a))
 /*
  * Less bad way to call ioctl from within the kernel; this needs to be
  * done some other way to get the call out of interrupt context.
index b9ed128..9856086 100644 (file)
@@ -686,18 +686,19 @@ static int cfv_probe(struct virtio_device *vdev)
                goto err;
 
        /* Get the CAIF configuration from virtio config space, if available */
-#define GET_VIRTIO_CONFIG_OPS(_v, _var, _f) \
-       ((_v)->config->get(_v, offsetof(struct virtio_caif_transf_config, _f), \
-                          &_var, \
-                          FIELD_SIZEOF(struct virtio_caif_transf_config, _f)))
-
        if (vdev->config->get) {
-               GET_VIRTIO_CONFIG_OPS(vdev, cfv->tx_hr, headroom);
-               GET_VIRTIO_CONFIG_OPS(vdev, cfv->rx_hr, headroom);
-               GET_VIRTIO_CONFIG_OPS(vdev, cfv->tx_tr, tailroom);
-               GET_VIRTIO_CONFIG_OPS(vdev, cfv->rx_tr, tailroom);
-               GET_VIRTIO_CONFIG_OPS(vdev, cfv->mtu, mtu);
-               GET_VIRTIO_CONFIG_OPS(vdev, cfv->mru, mtu);
+               virtio_cread(vdev, struct virtio_caif_transf_config, headroom,
+                            &cfv->tx_hr);
+               virtio_cread(vdev, struct virtio_caif_transf_config, headroom,
+                            &cfv->rx_hr);
+               virtio_cread(vdev, struct virtio_caif_transf_config, tailroom,
+                            &cfv->tx_tr);
+               virtio_cread(vdev, struct virtio_caif_transf_config, tailroom,
+                            &cfv->rx_tr);
+               virtio_cread(vdev, struct virtio_caif_transf_config, mtu,
+                            &cfv->mtu);
+               virtio_cread(vdev, struct virtio_caif_transf_config, mtu,
+                            &cfv->mru);
        } else {
                cfv->tx_hr = CFV_DEF_HEADROOM;
                cfv->rx_hr = CFV_DEF_HEADROOM;
index 5aa5e81..c3c4c26 100644 (file)
@@ -1388,6 +1388,9 @@ static int alx_resume(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct alx_priv *alx = pci_get_drvdata(pdev);
+       struct alx_hw *hw = &alx->hw;
+
+       alx_reset_phy(hw);
 
        if (!netif_running(alx->dev))
                return 0;
index 4e01c57..a1f66e2 100644 (file)
@@ -1376,7 +1376,6 @@ enum {
        BNX2X_SP_RTNL_RX_MODE,
        BNX2X_SP_RTNL_HYPERVISOR_VLAN,
        BNX2X_SP_RTNL_TX_STOP,
-       BNX2X_SP_RTNL_TX_RESUME,
 };
 
 struct bnx2x_prev_path_list {
index dcafbda..ec96130 100644 (file)
@@ -2959,6 +2959,10 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link)
 
        bp->port.pmf = 0;
 
+       /* clear pending work in rtnl task */
+       bp->sp_rtnl_state = 0;
+       smp_mb();
+
        /* Free SKBs, SGEs, TPA pool and driver internals */
        bnx2x_free_skbs(bp);
        if (CNIC_LOADED(bp))
index fcf2761..fdace20 100644 (file)
@@ -778,11 +778,6 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state)
 
                /* ets may affect cmng configuration: reinit it in hw */
                bnx2x_set_local_cmng(bp);
-
-               set_bit(BNX2X_SP_RTNL_TX_RESUME, &bp->sp_rtnl_state);
-
-               schedule_delayed_work(&bp->sp_rtnl_task, 0);
-
                return;
        case BNX2X_DCBX_STATE_TX_RELEASED:
                DP(BNX2X_MSG_DCB, "BNX2X_DCBX_STATE_TX_RELEASED\n");
index e622cc1..814d0ec 100644 (file)
@@ -577,7 +577,9 @@ void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
        rc = bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
        if (rc) {
                BNX2X_ERR("DMAE returned failure %d\n", rc);
+#ifdef BNX2X_STOP_ON_ERROR
                bnx2x_panic();
+#endif
        }
 }
 
@@ -614,7 +616,9 @@ void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32)
        rc = bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
        if (rc) {
                BNX2X_ERR("DMAE returned failure %d\n", rc);
+#ifdef BNX2X_STOP_ON_ERROR
                bnx2x_panic();
+#endif
        }
 }
 
@@ -5231,18 +5235,18 @@ static void bnx2x_eq_int(struct bnx2x *bp)
 
                case EVENT_RING_OPCODE_STOP_TRAFFIC:
                        DP(BNX2X_MSG_SP | BNX2X_MSG_DCB, "got STOP TRAFFIC\n");
+                       bnx2x_dcbx_set_params(bp, BNX2X_DCBX_STATE_TX_PAUSED);
                        if (f_obj->complete_cmd(bp, f_obj,
                                                BNX2X_F_CMD_TX_STOP))
                                break;
-                       bnx2x_dcbx_set_params(bp, BNX2X_DCBX_STATE_TX_PAUSED);
                        goto next_spqe;
 
                case EVENT_RING_OPCODE_START_TRAFFIC:
                        DP(BNX2X_MSG_SP | BNX2X_MSG_DCB, "got START TRAFFIC\n");
+                       bnx2x_dcbx_set_params(bp, BNX2X_DCBX_STATE_TX_RELEASED);
                        if (f_obj->complete_cmd(bp, f_obj,
                                                BNX2X_F_CMD_TX_START))
                                break;
-                       bnx2x_dcbx_set_params(bp, BNX2X_DCBX_STATE_TX_RELEASED);
                        goto next_spqe;
 
                case EVENT_RING_OPCODE_FUNCTION_UPDATE:
@@ -9352,6 +9356,10 @@ static int bnx2x_process_kill(struct bnx2x *bp, bool global)
        bnx2x_process_kill_chip_reset(bp, global);
        barrier();
 
+       /* clear errors in PGB */
+       if (!CHIP_IS_E1x(bp))
+               REG_WR(bp, PGLUE_B_REG_LATCHED_ERRORS_CLR, 0x7f);
+
        /* Recover after reset: */
        /* MCP */
        if (global && bnx2x_reset_mcp_comp(bp, val))
@@ -9706,11 +9714,10 @@ sp_rtnl_not_reset:
                               &bp->sp_rtnl_state))
                bnx2x_pf_set_vfs_vlan(bp);
 
-       if (test_and_clear_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state))
+       if (test_and_clear_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state)) {
                bnx2x_dcbx_stop_hw_tx(bp);
-
-       if (test_and_clear_bit(BNX2X_SP_RTNL_TX_RESUME, &bp->sp_rtnl_state))
                bnx2x_dcbx_resume_hw_tx(bp);
+       }
 
        /* work which needs rtnl lock not-taken (as it takes the lock itself and
         * can be called from other contexts as well)
index 5ecf267..3efbb35 100644 (file)
 #define PGLUE_B_REG_INTERNAL_PFID_ENABLE_TARGET_READ            0x9430
 #define PGLUE_B_REG_INTERNAL_PFID_ENABLE_TARGET_WRITE           0x9434
 #define PGLUE_B_REG_INTERNAL_VFID_ENABLE                        0x9438
+/* [W 7] Writing 1 to each bit in this register clears a corresponding error
+ * details register and enables logging new error details. Bit 0 - clears
+ * INCORRECT_RCV_DETAILS; Bit 1 - clears RX_ERR_DETAILS; Bit 2 - clears
+ * TX_ERR_WR_ADD_31_0 TX_ERR_WR_ADD_63_32 TX_ERR_WR_DETAILS
+ * TX_ERR_WR_DETAILS2 TX_ERR_RD_ADD_31_0 TX_ERR_RD_ADD_63_32
+ * TX_ERR_RD_DETAILS TX_ERR_RD_DETAILS2 TX_ERR_WR_DETAILS_ICPL; Bit 3 -
+ * clears VF_LENGTH_VIOLATION_DETAILS. Bit 4 - clears
+ * VF_GRC_SPACE_VIOLATION_DETAILS. Bit 5 - clears RX_TCPL_ERR_DETAILS. Bit 6
+ * - clears TCPL_IN_TWO_RCBS_DETAILS. */
+#define PGLUE_B_REG_LATCHED_ERRORS_CLR                          0x943c
+
 /* [R 9] Interrupt register #0 read */
 #define PGLUE_B_REG_PGLUE_B_INT_STS                             0x9298
 /* [RC 9] Interrupt register #0 read clear */
index 9199adf..efa8a15 100644 (file)
@@ -152,7 +152,7 @@ static int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping)
        if (bp->old_bulletin.valid_bitmap & 1 << CHANNEL_DOWN) {
                DP(BNX2X_MSG_IOV, "detecting channel down. Aborting message\n");
                *done = PFVF_STATUS_SUCCESS;
-               return 0;
+               return -EINVAL;
        }
 
        /* Write message address */
index 00c5be8..a9e0684 100644 (file)
@@ -13618,16 +13618,9 @@ static int tg3_hwtstamp_ioctl(struct net_device *dev,
        if (stmpconf.flags)
                return -EINVAL;
 
-       switch (stmpconf.tx_type) {
-       case HWTSTAMP_TX_ON:
-               tg3_flag_set(tp, TX_TSTAMP_EN);
-               break;
-       case HWTSTAMP_TX_OFF:
-               tg3_flag_clear(tp, TX_TSTAMP_EN);
-               break;
-       default:
+       if (stmpconf.tx_type != HWTSTAMP_TX_ON &&
+           stmpconf.tx_type != HWTSTAMP_TX_OFF)
                return -ERANGE;
-       }
 
        switch (stmpconf.rx_filter) {
        case HWTSTAMP_FILTER_NONE:
@@ -13689,6 +13682,11 @@ static int tg3_hwtstamp_ioctl(struct net_device *dev,
                tw32(TG3_RX_PTP_CTL,
                     tp->rxptpctl | TG3_RX_PTP_CTL_HWTS_INTERLOCK);
 
+       if (stmpconf.tx_type == HWTSTAMP_TX_ON)
+               tg3_flag_set(tp, TX_TSTAMP_EN);
+       else
+               tg3_flag_clear(tp, TX_TSTAMP_EN);
+
        return copy_to_user(ifr->ifr_data, &stmpconf, sizeof(stmpconf)) ?
                -EFAULT : 0;
 }
index 7fb0edf..dbcd526 100644 (file)
@@ -1758,7 +1758,7 @@ err:
 
 /* Uses sycnhronous mcc */
 int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
-                       u32 num, bool untagged, bool promiscuous)
+                      u32 num, bool promiscuous)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_vlan_config *req;
@@ -1778,7 +1778,7 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
 
        req->interface_id = if_id;
        req->promiscuous = promiscuous;
-       req->untagged = untagged;
+       req->untagged = BE_IF_FLAGS_UNTAGGED & be_if_cap_flags(adapter) ? 1 : 0;
        req->num_vlan = num;
        if (!promiscuous) {
                memcpy(req->normal_vlan, vtag_array,
@@ -1847,7 +1847,19 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
                        memcpy(req->mcast_mac[i++].byte, ha->addr, ETH_ALEN);
        }
 
+       if ((req->if_flags_mask & cpu_to_le32(be_if_cap_flags(adapter))) !=
+            req->if_flags_mask) {
+               dev_warn(&adapter->pdev->dev,
+                        "Cannot set rx filter flags 0x%x\n",
+                        req->if_flags_mask);
+               dev_warn(&adapter->pdev->dev,
+                        "Interface is capable of 0x%x flags only\n",
+                        be_if_cap_flags(adapter));
+       }
+       req->if_flags_mask &= cpu_to_le32(be_if_cap_flags(adapter));
+
        status = be_mcc_notify_wait(adapter);
+
 err:
        spin_unlock_bh(&adapter->mcc_lock);
        return status;
index edf3e8a..0075686 100644 (file)
@@ -1984,7 +1984,7 @@ int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
                      char *fw_on_flash);
 int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *, int num);
 int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
-                      u32 num, bool untagged, bool promiscuous);
+                      u32 num, bool promiscuous);
 int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 status);
 int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc);
 int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc);
index eaecaad..abde974 100644 (file)
@@ -1079,7 +1079,7 @@ static int be_vid_config(struct be_adapter *adapter)
                        vids[num++] = cpu_to_le16(i);
 
        status = be_cmd_vlan_config(adapter, adapter->if_handle,
-                                   vids, num, 1, 0);
+                                   vids, num, 0);
 
        if (status) {
                /* Set to VLAN promisc mode as setting VLAN filter failed */
@@ -2676,6 +2676,11 @@ static int be_close(struct net_device *netdev)
 
        be_rx_qs_destroy(adapter);
 
+       for (i = 1; i < (adapter->uc_macs + 1); i++)
+               be_cmd_pmac_del(adapter, adapter->if_handle,
+                               adapter->pmac_id[i], 0);
+       adapter->uc_macs = 0;
+
        for_all_evt_queues(adapter, eqo, i) {
                if (msix_enabled(adapter))
                        synchronize_irq(be_msix_vec_get(adapter, eqo));
index b2793b9..4cbebf3 100644 (file)
@@ -386,7 +386,14 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
         */
        bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
                        FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
-
+       if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
+               bdp->cbd_bufaddr = 0;
+               fep->tx_skbuff[index] = NULL;
+               dev_kfree_skb_any(skb);
+               if (net_ratelimit())
+                       netdev_err(ndev, "Tx DMA memory map failed\n");
+               return NETDEV_TX_OK;
+       }
        /* Send it on its way.  Tell FEC it's ready, interrupt when done,
         * it's the last BD of the frame, and to put the CRC on the end.
         */
@@ -861,6 +868,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
        struct  bufdesc_ex *ebdp = NULL;
        bool    vlan_packet_rcvd = false;
        u16     vlan_tag;
+       int     index = 0;
 
 #ifdef CONFIG_M532x
        flush_cache_all();
@@ -916,10 +924,15 @@ fec_enet_rx(struct net_device *ndev, int budget)
                ndev->stats.rx_packets++;
                pkt_len = bdp->cbd_datlen;
                ndev->stats.rx_bytes += pkt_len;
-               data = (__u8*)__va(bdp->cbd_bufaddr);
 
-               dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
-                               FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
+               if (fep->bufdesc_ex)
+                       index = (struct bufdesc_ex *)bdp -
+                               (struct bufdesc_ex *)fep->rx_bd_base;
+               else
+                       index = bdp - fep->rx_bd_base;
+               data = fep->rx_skbuff[index]->data;
+               dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr,
+                                       FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
 
                if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
                        swap_buffer(data, pkt_len);
@@ -999,8 +1012,8 @@ fec_enet_rx(struct net_device *ndev, int budget)
                        napi_gro_receive(&fep->napi, skb);
                }
 
-               bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
-                               FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
+               dma_sync_single_for_device(&fep->pdev->dev, bdp->cbd_bufaddr,
+                                       FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
 rx_processing_done:
                /* Clear the status flags for this buffer */
                status &= ~BD_ENET_RX_STATS;
@@ -1719,6 +1732,12 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
 
                bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
                                FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+               if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
+                       fec_enet_free_buffers(ndev);
+                       if (net_ratelimit())
+                               netdev_err(ndev, "Rx DMA memory map failed\n");
+                       return -ENOMEM;
+               }
                bdp->cbd_sc = BD_ENET_RX_EMPTY;
 
                if (fep->bufdesc_ex) {
index aedd573..8d3945a 100644 (file)
@@ -3482,10 +3482,10 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca)
  * specified. Matching the kind of event packet is not supported, with the
  * exception of "all V2 events regardless of level 2 or 4".
  **/
-static int e1000e_config_hwtstamp(struct e1000_adapter *adapter)
+static int e1000e_config_hwtstamp(struct e1000_adapter *adapter,
+                                 struct hwtstamp_config *config)
 {
        struct e1000_hw *hw = &adapter->hw;
-       struct hwtstamp_config *config = &adapter->hwtstamp_config;
        u32 tsync_tx_ctl = E1000_TSYNCTXCTL_ENABLED;
        u32 tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
        u32 rxmtrl = 0;
@@ -3586,6 +3586,8 @@ static int e1000e_config_hwtstamp(struct e1000_adapter *adapter)
                return -ERANGE;
        }
 
+       adapter->hwtstamp_config = *config;
+
        /* enable/disable Tx h/w time stamping */
        regval = er32(TSYNCTXCTL);
        regval &= ~E1000_TSYNCTXCTL_ENABLED;
@@ -3874,7 +3876,7 @@ void e1000e_reset(struct e1000_adapter *adapter)
        e1000e_reset_adaptive(hw);
 
        /* initialize systim and reset the ns time counter */
-       e1000e_config_hwtstamp(adapter);
+       e1000e_config_hwtstamp(adapter, &adapter->hwtstamp_config);
 
        /* Set EEE advertisement as appropriate */
        if (adapter->flags2 & FLAG2_HAS_EEE) {
@@ -5797,14 +5799,10 @@ static int e1000e_hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
        if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
                return -EFAULT;
 
-       adapter->hwtstamp_config = config;
-
-       ret_val = e1000e_config_hwtstamp(adapter);
+       ret_val = e1000e_config_hwtstamp(adapter, &config);
        if (ret_val)
                return ret_val;
 
-       config = adapter->hwtstamp_config;
-
        switch (config.rx_filter) {
        case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
        case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
index 00cd36e..61088a6 100644 (file)
@@ -2890,7 +2890,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
                                         PHY_INTERFACE_MODE_GMII);
                if (!mp->phy)
                        err = -ENODEV;
-               phy_addr_set(mp, mp->phy->addr);
+               else
+                       phy_addr_set(mp, mp->phy->addr);
        } else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) {
                mp->phy = phy_scan(mp, pd->phy_addr);
 
index fda2667..1949282 100644 (file)
@@ -1774,7 +1774,7 @@ void mlx4_opreq_action(struct work_struct *work)
                                   MLX4_CMD_GET_OP_REQ, MLX4_CMD_TIME_CLASS_A,
                                   MLX4_CMD_NATIVE);
                if (err) {
-                       mlx4_err(dev, "Failed to retreive required operation: %d\n",
+                       mlx4_err(dev, "Failed to retrieve required operation: %d\n",
                                 err);
                        return;
                }
index 6ca3073..8675d26 100644 (file)
@@ -98,6 +98,7 @@ enum {
 static struct mlx5_cmd_work_ent *alloc_cmd(struct mlx5_cmd *cmd,
                                           struct mlx5_cmd_msg *in,
                                           struct mlx5_cmd_msg *out,
+                                          void *uout, int uout_size,
                                           mlx5_cmd_cbk_t cbk,
                                           void *context, int page_queue)
 {
@@ -110,6 +111,8 @@ static struct mlx5_cmd_work_ent *alloc_cmd(struct mlx5_cmd *cmd,
 
        ent->in         = in;
        ent->out        = out;
+       ent->uout       = uout;
+       ent->uout_size  = uout_size;
        ent->callback   = cbk;
        ent->context    = context;
        ent->cmd        = cmd;
@@ -534,6 +537,7 @@ static void cmd_work_handler(struct work_struct *work)
        ent->lay = lay;
        memset(lay, 0, sizeof(*lay));
        memcpy(lay->in, ent->in->first.data, sizeof(lay->in));
+       ent->op = be32_to_cpu(lay->in[0]) >> 16;
        if (ent->in->next)
                lay->in_ptr = cpu_to_be64(ent->in->next->dma);
        lay->inlen = cpu_to_be32(ent->in->len);
@@ -628,7 +632,8 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
  *    2. page queue commands do not support asynchrous completion
  */
 static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
-                          struct mlx5_cmd_msg *out, mlx5_cmd_cbk_t callback,
+                          struct mlx5_cmd_msg *out, void *uout, int uout_size,
+                          mlx5_cmd_cbk_t callback,
                           void *context, int page_queue, u8 *status)
 {
        struct mlx5_cmd *cmd = &dev->cmd;
@@ -642,7 +647,8 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
        if (callback && page_queue)
                return -EINVAL;
 
-       ent = alloc_cmd(cmd, in, out, callback, context, page_queue);
+       ent = alloc_cmd(cmd, in, out, uout, uout_size, callback, context,
+                       page_queue);
        if (IS_ERR(ent))
                return PTR_ERR(ent);
 
@@ -670,10 +676,10 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
                op = be16_to_cpu(((struct mlx5_inbox_hdr *)in->first.data)->opcode);
                if (op < ARRAY_SIZE(cmd->stats)) {
                        stats = &cmd->stats[op];
-                       spin_lock(&stats->lock);
+                       spin_lock_irq(&stats->lock);
                        stats->sum += ds;
                        ++stats->n;
-                       spin_unlock(&stats->lock);
+                       spin_unlock_irq(&stats->lock);
                }
                mlx5_core_dbg_mask(dev, 1 << MLX5_CMD_TIME,
                                   "fw exec time for %s is %lld nsec\n",
@@ -826,7 +832,7 @@ static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev,
        int n;
        int i;
 
-       msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       msg = kzalloc(sizeof(*msg), flags);
        if (!msg)
                return ERR_PTR(-ENOMEM);
 
@@ -1109,6 +1115,19 @@ void mlx5_cmd_use_polling(struct mlx5_core_dev *dev)
                up(&cmd->sem);
 }
 
+static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg)
+{
+       unsigned long flags;
+
+       if (msg->cache) {
+               spin_lock_irqsave(&msg->cache->lock, flags);
+               list_add_tail(&msg->list, &msg->cache->head);
+               spin_unlock_irqrestore(&msg->cache->lock, flags);
+       } else {
+               mlx5_free_cmd_msg(dev, msg);
+       }
+}
+
 void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector)
 {
        struct mlx5_cmd *cmd = &dev->cmd;
@@ -1117,6 +1136,10 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector)
        void *context;
        int err;
        int i;
+       ktime_t t1, t2, delta;
+       s64 ds;
+       struct mlx5_cmd_stats *stats;
+       unsigned long flags;
 
        for (i = 0; i < (1 << cmd->log_sz); i++) {
                if (test_bit(i, &vector)) {
@@ -1141,9 +1164,29 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector)
                        }
                        free_ent(cmd, ent->idx);
                        if (ent->callback) {
+                               t1 = timespec_to_ktime(ent->ts1);
+                               t2 = timespec_to_ktime(ent->ts2);
+                               delta = ktime_sub(t2, t1);
+                               ds = ktime_to_ns(delta);
+                               if (ent->op < ARRAY_SIZE(cmd->stats)) {
+                                       stats = &cmd->stats[ent->op];
+                                       spin_lock_irqsave(&stats->lock, flags);
+                                       stats->sum += ds;
+                                       ++stats->n;
+                                       spin_unlock_irqrestore(&stats->lock, flags);
+                               }
+
                                callback = ent->callback;
                                context = ent->context;
                                err = ent->ret;
+                               if (!err)
+                                       err = mlx5_copy_from_msg(ent->uout,
+                                                                ent->out,
+                                                                ent->uout_size);
+
+                               mlx5_free_cmd_msg(dev, ent->out);
+                               free_msg(dev, ent->in);
+
                                free_cmd(ent);
                                callback(err, context);
                        } else {
@@ -1160,7 +1203,8 @@ static int status_to_err(u8 status)
        return status ? -1 : 0; /* TBD more meaningful codes */
 }
 
-static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size)
+static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size,
+                                     gfp_t gfp)
 {
        struct mlx5_cmd_msg *msg = ERR_PTR(-ENOMEM);
        struct mlx5_cmd *cmd = &dev->cmd;
@@ -1172,7 +1216,7 @@ static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size)
                ent = &cmd->cache.med;
 
        if (ent) {
-               spin_lock(&ent->lock);
+               spin_lock_irq(&ent->lock);
                if (!list_empty(&ent->head)) {
                        msg = list_entry(ent->head.next, typeof(*msg), list);
                        /* For cached lists, we must explicitly state what is
@@ -1181,43 +1225,34 @@ static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size)
                        msg->len = in_size;
                        list_del(&msg->list);
                }
-               spin_unlock(&ent->lock);
+               spin_unlock_irq(&ent->lock);
        }
 
        if (IS_ERR(msg))
-               msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, in_size);
+               msg = mlx5_alloc_cmd_msg(dev, gfp, in_size);
 
        return msg;
 }
 
-static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg)
-{
-       if (msg->cache) {
-               spin_lock(&msg->cache->lock);
-               list_add_tail(&msg->list, &msg->cache->head);
-               spin_unlock(&msg->cache->lock);
-       } else {
-               mlx5_free_cmd_msg(dev, msg);
-       }
-}
-
 static int is_manage_pages(struct mlx5_inbox_hdr *in)
 {
        return be16_to_cpu(in->opcode) == MLX5_CMD_OP_MANAGE_PAGES;
 }
 
-int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
-                 int out_size)
+static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
+                   int out_size, mlx5_cmd_cbk_t callback, void *context)
 {
        struct mlx5_cmd_msg *inb;
        struct mlx5_cmd_msg *outb;
        int pages_queue;
+       gfp_t gfp;
        int err;
        u8 status = 0;
 
        pages_queue = is_manage_pages(in);
+       gfp = callback ? GFP_ATOMIC : GFP_KERNEL;
 
-       inb = alloc_msg(dev, in_size);
+       inb = alloc_msg(dev, in_size, gfp);
        if (IS_ERR(inb)) {
                err = PTR_ERR(inb);
                return err;
@@ -1229,13 +1264,14 @@ int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
                goto out_in;
        }
 
-       outb = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, out_size);
+       outb = mlx5_alloc_cmd_msg(dev, gfp, out_size);
        if (IS_ERR(outb)) {
                err = PTR_ERR(outb);
                goto out_in;
        }
 
-       err = mlx5_cmd_invoke(dev, inb, outb, NULL, NULL, pages_queue, &status);
+       err = mlx5_cmd_invoke(dev, inb, outb, out, out_size, callback, context,
+                             pages_queue, &status);
        if (err)
                goto out_out;
 
@@ -1248,14 +1284,30 @@ int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
        err = mlx5_copy_from_msg(out, outb, out_size);
 
 out_out:
-       mlx5_free_cmd_msg(dev, outb);
+       if (!callback)
+               mlx5_free_cmd_msg(dev, outb);
 
 out_in:
-       free_msg(dev, inb);
+       if (!callback)
+               free_msg(dev, inb);
        return err;
 }
+
+int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
+                 int out_size)
+{
+       return cmd_exec(dev, in, in_size, out, out_size, NULL, NULL);
+}
 EXPORT_SYMBOL(mlx5_cmd_exec);
 
+int mlx5_cmd_exec_cb(struct mlx5_core_dev *dev, void *in, int in_size,
+                    void *out, int out_size, mlx5_cmd_cbk_t callback,
+                    void *context)
+{
+       return cmd_exec(dev, in, in_size, out, out_size, callback, context);
+}
+EXPORT_SYMBOL(mlx5_cmd_exec_cb);
+
 static void destroy_msg_cache(struct mlx5_core_dev *dev)
 {
        struct mlx5_cmd *cmd = &dev->cmd;
index 9c7194b..80f6d12 100644 (file)
@@ -154,10 +154,10 @@ static ssize_t average_read(struct file *filp, char __user *buf, size_t count,
                return 0;
 
        stats = filp->private_data;
-       spin_lock(&stats->lock);
+       spin_lock_irq(&stats->lock);
        if (stats->n)
                field = div64_u64(stats->sum, stats->n);
-       spin_unlock(&stats->lock);
+       spin_unlock_irq(&stats->lock);
        ret = snprintf(tbuf, sizeof(tbuf), "%llu\n", field);
        if (ret > 0) {
                if (copy_to_user(buf, tbuf, ret))
@@ -175,10 +175,10 @@ static ssize_t average_write(struct file *filp, const char __user *buf,
        struct mlx5_cmd_stats *stats;
 
        stats = filp->private_data;
-       spin_lock(&stats->lock);
+       spin_lock_irq(&stats->lock);
        stats->sum = 0;
        stats->n = 0;
-       spin_unlock(&stats->lock);
+       spin_unlock_irq(&stats->lock);
 
        *pos += count;
 
index 2231d93..64a61b2 100644 (file)
@@ -354,7 +354,7 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
        in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_EQ);
        in->ctx.log_sz_usr_page = cpu_to_be32(ilog2(eq->nent) << 24 | uar->index);
        in->ctx.intr = vecidx;
-       in->ctx.log_page_size = PAGE_SHIFT - 12;
+       in->ctx.log_page_size = eq->buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT;
        in->events_mask = cpu_to_be64(mask);
 
        err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
index bc0f5fb..40a9f5e 100644 (file)
@@ -159,6 +159,36 @@ struct mlx5_reg_host_endianess {
        u8      rsvd[15];
 };
 
+
+#define CAP_MASK(pos, size) ((u64)((1 << (size)) - 1) << (pos))
+
+enum {
+       MLX5_CAP_BITS_RW_MASK   = CAP_MASK(MLX5_CAP_OFF_CMDIF_CSUM, 2) |
+                                 CAP_MASK(MLX5_CAP_OFF_DCT, 1),
+};
+
+/* selectively copy writable fields clearing any reserved area
+ */
+static void copy_rw_fields(struct mlx5_hca_cap *to, struct mlx5_hca_cap *from)
+{
+       u64 v64;
+
+       to->log_max_qp = from->log_max_qp & 0x1f;
+       to->log_max_ra_req_dc = from->log_max_ra_req_dc & 0x3f;
+       to->log_max_ra_res_dc = from->log_max_ra_res_dc & 0x3f;
+       to->log_max_ra_req_qp = from->log_max_ra_req_qp & 0x3f;
+       to->log_max_ra_res_qp = from->log_max_ra_res_qp & 0x3f;
+       to->log_max_atomic_size_qp = from->log_max_atomic_size_qp;
+       to->log_max_atomic_size_dc = from->log_max_atomic_size_dc;
+       v64 = be64_to_cpu(from->flags) & MLX5_CAP_BITS_RW_MASK;
+       to->flags = cpu_to_be64(v64);
+}
+
+enum {
+       HCA_CAP_OPMOD_GET_MAX   = 0,
+       HCA_CAP_OPMOD_GET_CUR   = 1,
+};
+
 static int handle_hca_cap(struct mlx5_core_dev *dev)
 {
        struct mlx5_cmd_query_hca_cap_mbox_out *query_out = NULL;
@@ -180,7 +210,7 @@ static int handle_hca_cap(struct mlx5_core_dev *dev)
        }
 
        query_ctx.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_HCA_CAP);
-       query_ctx.hdr.opmod  = cpu_to_be16(0x1);
+       query_ctx.hdr.opmod  = cpu_to_be16(HCA_CAP_OPMOD_GET_CUR);
        err = mlx5_cmd_exec(dev, &query_ctx, sizeof(query_ctx),
                                 query_out, sizeof(*query_out));
        if (err)
@@ -192,8 +222,7 @@ static int handle_hca_cap(struct mlx5_core_dev *dev)
                goto query_ex;
        }
 
-       memcpy(&set_ctx->hca_cap, &query_out->hca_cap,
-              sizeof(set_ctx->hca_cap));
+       copy_rw_fields(&set_ctx->hca_cap, &query_out->hca_cap);
 
        if (dev->profile->mask & MLX5_PROF_MASK_QP_SIZE)
                set_ctx->hca_cap.log_max_qp = dev->profile->log_max_qp;
index 5b44e2e..35e514d 100644 (file)
 #include "mlx5_core.h"
 
 int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
-                         struct mlx5_create_mkey_mbox_in *in, int inlen)
+                         struct mlx5_create_mkey_mbox_in *in, int inlen,
+                         mlx5_cmd_cbk_t callback, void *context,
+                         struct mlx5_create_mkey_mbox_out *out)
 {
-       struct mlx5_create_mkey_mbox_out out;
+       struct mlx5_create_mkey_mbox_out lout;
        int err;
        u8 key;
 
-       memset(&out, 0, sizeof(out));
-       spin_lock(&dev->priv.mkey_lock);
+       memset(&lout, 0, sizeof(lout));
+       spin_lock_irq(&dev->priv.mkey_lock);
        key = dev->priv.mkey_key++;
-       spin_unlock(&dev->priv.mkey_lock);
+       spin_unlock_irq(&dev->priv.mkey_lock);
        in->seg.qpn_mkey7_0 |= cpu_to_be32(key);
        in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_MKEY);
-       err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
+       if (callback) {
+               err = mlx5_cmd_exec_cb(dev, in, inlen, out, sizeof(*out),
+                                      callback, context);
+               return err;
+       } else {
+               err = mlx5_cmd_exec(dev, in, inlen, &lout, sizeof(lout));
+       }
+
        if (err) {
                mlx5_core_dbg(dev, "cmd exec faile %d\n", err);
                return err;
        }
 
-       if (out.hdr.status) {
-               mlx5_core_dbg(dev, "status %d\n", out.hdr.status);
-               return mlx5_cmd_status_to_err(&out.hdr);
+       if (lout.hdr.status) {
+               mlx5_core_dbg(dev, "status %d\n", lout.hdr.status);
+               return mlx5_cmd_status_to_err(&lout.hdr);
        }
 
-       mr->key = mlx5_idx_to_mkey(be32_to_cpu(out.mkey) & 0xffffff) | key;
-       mlx5_core_dbg(dev, "out 0x%x, key 0x%x, mkey 0x%x\n", be32_to_cpu(out.mkey), key, mr->key);
+       mr->key = mlx5_idx_to_mkey(be32_to_cpu(lout.mkey) & 0xffffff) | key;
+       mlx5_core_dbg(dev, "out 0x%x, key 0x%x, mkey 0x%x\n",
+                     be32_to_cpu(lout.mkey), key, mr->key);
 
        return err;
 }
index 7b12acf..37b6ad1 100644 (file)
@@ -57,10 +57,13 @@ struct mlx5_pages_req {
 };
 
 struct fw_page {
-       struct rb_node  rb_node;
-       u64             addr;
-       struct page     *page;
-       u16             func_id;
+       struct rb_node          rb_node;
+       u64                     addr;
+       struct page            *page;
+       u16                     func_id;
+       unsigned long           bitmask;
+       struct list_head        list;
+       unsigned                free_count;
 };
 
 struct mlx5_query_pages_inbox {
@@ -94,6 +97,11 @@ enum {
        MAX_RECLAIM_TIME_MSECS  = 5000,
 };
 
+enum {
+       MLX5_MAX_RECLAIM_TIME_MILI      = 5000,
+       MLX5_NUM_4K_IN_PAGE             = PAGE_SIZE / 4096,
+};
+
 static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u16 func_id)
 {
        struct rb_root *root = &dev->priv.page_root;
@@ -101,6 +109,7 @@ static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u
        struct rb_node *parent = NULL;
        struct fw_page *nfp;
        struct fw_page *tfp;
+       int i;
 
        while (*new) {
                parent = *new;
@@ -113,25 +122,29 @@ static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u
                        return -EEXIST;
        }
 
-       nfp = kmalloc(sizeof(*nfp), GFP_KERNEL);
+       nfp = kzalloc(sizeof(*nfp), GFP_KERNEL);
        if (!nfp)
                return -ENOMEM;
 
        nfp->addr = addr;
        nfp->page = page;
        nfp->func_id = func_id;
+       nfp->free_count = MLX5_NUM_4K_IN_PAGE;
+       for (i = 0; i < MLX5_NUM_4K_IN_PAGE; i++)
+               set_bit(i, &nfp->bitmask);
 
        rb_link_node(&nfp->rb_node, parent, new);
        rb_insert_color(&nfp->rb_node, root);
+       list_add(&nfp->list, &dev->priv.free_list);
 
        return 0;
 }
 
-static struct page *remove_page(struct mlx5_core_dev *dev, u64 addr)
+static struct fw_page *find_fw_page(struct mlx5_core_dev *dev, u64 addr)
 {
        struct rb_root *root = &dev->priv.page_root;
        struct rb_node *tmp = root->rb_node;
-       struct page *result = NULL;
+       struct fw_page *result = NULL;
        struct fw_page *tfp;
 
        while (tmp) {
@@ -141,9 +154,7 @@ static struct page *remove_page(struct mlx5_core_dev *dev, u64 addr)
                } else if (tfp->addr > addr) {
                        tmp = tmp->rb_right;
                } else {
-                       rb_erase(&tfp->rb_node, root);
-                       result = tfp->page;
-                       kfree(tfp);
+                       result = tfp;
                        break;
                }
        }
@@ -176,12 +187,98 @@ static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id,
        return err;
 }
 
+static int alloc_4k(struct mlx5_core_dev *dev, u64 *addr)
+{
+       struct fw_page *fp;
+       unsigned n;
+
+       if (list_empty(&dev->priv.free_list)) {
+               return -ENOMEM;
+               mlx5_core_warn(dev, "\n");
+       }
+
+       fp = list_entry(dev->priv.free_list.next, struct fw_page, list);
+       n = find_first_bit(&fp->bitmask, 8 * sizeof(fp->bitmask));
+       if (n >= MLX5_NUM_4K_IN_PAGE) {
+               mlx5_core_warn(dev, "alloc 4k bug\n");
+               return -ENOENT;
+       }
+       clear_bit(n, &fp->bitmask);
+       fp->free_count--;
+       if (!fp->free_count)
+               list_del(&fp->list);
+
+       *addr = fp->addr + n * 4096;
+
+       return 0;
+}
+
+static void free_4k(struct mlx5_core_dev *dev, u64 addr)
+{
+       struct fw_page *fwp;
+       int n;
+
+       fwp = find_fw_page(dev, addr & PAGE_MASK);
+       if (!fwp) {
+               mlx5_core_warn(dev, "page not found\n");
+               return;
+       }
+
+       n = (addr & ~PAGE_MASK) % 4096;
+       fwp->free_count++;
+       set_bit(n, &fwp->bitmask);
+       if (fwp->free_count == MLX5_NUM_4K_IN_PAGE) {
+               rb_erase(&fwp->rb_node, &dev->priv.page_root);
+               if (fwp->free_count != 1)
+                       list_del(&fwp->list);
+               dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
+               __free_page(fwp->page);
+               kfree(fwp);
+       } else if (fwp->free_count == 1) {
+               list_add(&fwp->list, &dev->priv.free_list);
+       }
+}
+
+static int alloc_system_page(struct mlx5_core_dev *dev, u16 func_id)
+{
+       struct page *page;
+       u64 addr;
+       int err;
+
+       page = alloc_page(GFP_HIGHUSER);
+       if (!page) {
+               mlx5_core_warn(dev, "failed to allocate page\n");
+               return -ENOMEM;
+       }
+       addr = dma_map_page(&dev->pdev->dev, page, 0,
+                           PAGE_SIZE, DMA_BIDIRECTIONAL);
+       if (dma_mapping_error(&dev->pdev->dev, addr)) {
+               mlx5_core_warn(dev, "failed dma mapping page\n");
+               err = -ENOMEM;
+               goto out_alloc;
+       }
+       err = insert_page(dev, addr, page, func_id);
+       if (err) {
+               mlx5_core_err(dev, "failed to track allocated page\n");
+               goto out_mapping;
+       }
+
+       return 0;
+
+out_mapping:
+       dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
+
+out_alloc:
+       __free_page(page);
+
+       return err;
+}
 static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
                      int notify_fail)
 {
        struct mlx5_manage_pages_inbox *in;
        struct mlx5_manage_pages_outbox out;
-       struct page *page;
+       struct mlx5_manage_pages_inbox *nin;
        int inlen;
        u64 addr;
        int err;
@@ -196,27 +293,15 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
        memset(&out, 0, sizeof(out));
 
        for (i = 0; i < npages; i++) {
-               page = alloc_page(GFP_HIGHUSER);
-               if (!page) {
-                       err = -ENOMEM;
-                       mlx5_core_warn(dev, "failed to allocate page\n");
-                       goto out_alloc;
-               }
-               addr = dma_map_page(&dev->pdev->dev, page, 0,
-                                   PAGE_SIZE, DMA_BIDIRECTIONAL);
-               if (dma_mapping_error(&dev->pdev->dev, addr)) {
-                       mlx5_core_warn(dev, "failed dma mapping page\n");
-                       __free_page(page);
-                       err = -ENOMEM;
-                       goto out_alloc;
-               }
-               err = insert_page(dev, addr, page, func_id);
+retry:
+               err = alloc_4k(dev, &addr);
                if (err) {
-                       mlx5_core_err(dev, "failed to track allocated page\n");
-                       dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
-                       __free_page(page);
-                       err = -ENOMEM;
-                       goto out_alloc;
+                       if (err == -ENOMEM)
+                               err = alloc_system_page(dev, func_id);
+                       if (err)
+                               goto out_4k;
+
+                       goto retry;
                }
                in->pas[i] = cpu_to_be64(addr);
        }
@@ -226,7 +311,6 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
        in->func_id = cpu_to_be16(func_id);
        in->num_entries = cpu_to_be32(npages);
        err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
-       mlx5_core_dbg(dev, "err %d\n", err);
        if (err) {
                mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n", func_id, npages, err);
                goto out_alloc;
@@ -247,25 +331,22 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
 
 out_alloc:
        if (notify_fail) {
-               memset(in, 0, inlen);
-               memset(&out, 0, sizeof(out));
-               in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES);
-               in->hdr.opmod = cpu_to_be16(MLX5_PAGES_CANT_GIVE);
-               if (mlx5_cmd_exec(dev, in, sizeof(*in), &out, sizeof(out)))
-                       mlx5_core_warn(dev, "\n");
-       }
-       for (i--; i >= 0; i--) {
-               addr = be64_to_cpu(in->pas[i]);
-               page = remove_page(dev, addr);
-               if (!page) {
-                       mlx5_core_err(dev, "BUG: can't remove page at addr 0x%llx\n",
-                                     addr);
-                       continue;
+               nin = kzalloc(sizeof(*nin), GFP_KERNEL);
+               if (!nin) {
+                       mlx5_core_warn(dev, "allocation failed\n");
+                       goto out_4k;
                }
-               dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
-               __free_page(page);
+               memset(&out, 0, sizeof(out));
+               nin->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES);
+               nin->hdr.opmod = cpu_to_be16(MLX5_PAGES_CANT_GIVE);
+               if (mlx5_cmd_exec(dev, nin, sizeof(*nin), &out, sizeof(out)))
+                       mlx5_core_warn(dev, "page notify failed\n");
+               kfree(nin);
        }
 
+out_4k:
+       for (i--; i >= 0; i--)
+               free_4k(dev, be64_to_cpu(in->pas[i]));
 out_free:
        mlx5_vfree(in);
        return err;
@@ -276,7 +357,6 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
 {
        struct mlx5_manage_pages_inbox   in;
        struct mlx5_manage_pages_outbox *out;
-       struct page *page;
        int num_claimed;
        int outlen;
        u64 addr;
@@ -315,13 +395,7 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
 
        for (i = 0; i < num_claimed; i++) {
                addr = be64_to_cpu(out->pas[i]);
-               page = remove_page(dev, addr);
-               if (!page) {
-                       mlx5_core_warn(dev, "FW reported unknown DMA address 0x%llx\n", addr);
-               } else {
-                       dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
-                       __free_page(page);
-               }
+               free_4k(dev, addr);
        }
 
 out_free:
@@ -381,14 +455,19 @@ int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot)
        return give_pages(dev, func_id, npages, 0);
 }
 
+enum {
+       MLX5_BLKS_FOR_RECLAIM_PAGES = 12
+};
+
 static int optimal_reclaimed_pages(void)
 {
        struct mlx5_cmd_prot_block *block;
        struct mlx5_cmd_layout *lay;
        int ret;
 
-       ret = (sizeof(lay->in) + sizeof(block->data) -
-              sizeof(struct mlx5_manage_pages_outbox)) / 8;
+       ret = (sizeof(lay->out) + MLX5_BLKS_FOR_RECLAIM_PAGES * sizeof(block->data) -
+              sizeof(struct mlx5_manage_pages_outbox)) /
+              FIELD_SIZEOF(struct mlx5_manage_pages_outbox, pas[0]);
 
        return ret;
 }
@@ -427,6 +506,7 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
 void mlx5_pagealloc_init(struct mlx5_core_dev *dev)
 {
        dev->priv.page_root = RB_ROOT;
+       INIT_LIST_HEAD(&dev->priv.free_list);
 }
 
 void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev)
index 0951f7a..822616e 100644 (file)
@@ -459,8 +459,7 @@ static int ks8842_tx_frame_dma(struct sk_buff *skb, struct net_device *netdev)
                sg_dma_len(&ctl->sg) += 4 - sg_dma_len(&ctl->sg) % 4;
 
        ctl->adesc = dmaengine_prep_slave_sg(ctl->chan,
-               &ctl->sg, 1, DMA_MEM_TO_DEV,
-               DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
+               &ctl->sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
        if (!ctl->adesc)
                return NETDEV_TX_BUSY;
 
@@ -571,8 +570,7 @@ static int __ks8842_start_new_rx_dma(struct net_device *netdev)
                sg_dma_len(sg) = DMA_BUFFER_SIZE;
 
                ctl->adesc = dmaengine_prep_slave_sg(ctl->chan,
-                       sg, 1, DMA_DEV_TO_MEM,
-                       DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
+                       sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
 
                if (!ctl->adesc)
                        goto out;
index 5a0f04c..27ffe0e 100644 (file)
@@ -245,16 +245,8 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
        /* Get ieee1588's dev information */
        pdev = adapter->ptp_pdev;
 
-       switch (cfg.tx_type) {
-       case HWTSTAMP_TX_OFF:
-               adapter->hwts_tx_en = 0;
-               break;
-       case HWTSTAMP_TX_ON:
-               adapter->hwts_tx_en = 1;
-               break;
-       default:
+       if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON)
                return -ERANGE;
-       }
 
        switch (cfg.rx_filter) {
        case HWTSTAMP_FILTER_NONE:
@@ -284,6 +276,8 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
                return -ERANGE;
        }
 
+       adapter->hwts_tx_en = cfg.tx_type == HWTSTAMP_TX_ON;
+
        /* Clear out any old time stamps. */
        pch_ch_event_write(pdev, TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED);
 
index 09810dd..b1cb0ff 100644 (file)
@@ -1730,7 +1730,7 @@ static void qlcnic_extend_lb_idc_cmpltn_wait(struct qlcnic_adapter *adapter,
        struct qlcnic_hardware_context *ahw = adapter->ahw;
        int temp;
 
-       netdev_info(adapter->netdev, "Recieved loopback IDC time extend event for 0x%x seconds\n",
+       netdev_info(adapter->netdev, "Received loopback IDC time extend event for 0x%x seconds\n",
                    ahw->extend_lb_time);
        temp = ahw->extend_lb_time * 1000;
        *max_wait_count += temp / QLC_83XX_LB_MSLEEP_COUNT;
@@ -3537,7 +3537,7 @@ int qlcnic_83xx_resume(struct qlcnic_adapter *adapter)
 
 void qlcnic_83xx_reinit_mbx_work(struct qlcnic_mailbox *mbx)
 {
-       INIT_COMPLETION(mbx->completion);
+       reinit_completion(&mbx->completion);
        set_bit(QLC_83XX_MBX_READY, &mbx->status);
 }
 
index 8d4ccd3..8a7a23a 100644 (file)
@@ -435,16 +435,9 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
        if (config.flags)
                return -EINVAL;
 
-       switch (config.tx_type) {
-       case HWTSTAMP_TX_OFF:
-               priv->hwts_tx_en = 0;
-               break;
-       case HWTSTAMP_TX_ON:
-               priv->hwts_tx_en = 1;
-               break;
-       default:
+       if (config.tx_type != HWTSTAMP_TX_OFF &&
+           config.tx_type != HWTSTAMP_TX_ON)
                return -ERANGE;
-       }
 
        if (priv->adv_ts) {
                switch (config.rx_filter) {
@@ -576,6 +569,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
                }
        }
        priv->hwts_rx_en = ((config.rx_filter == HWTSTAMP_FILTER_NONE) ? 0 : 1);
+       priv->hwts_tx_en = config.tx_type == HWTSTAMP_TX_ON;
 
        if (!priv->hwts_tx_en && !priv->hwts_rx_en)
                priv->hw->ptp->config_hw_tstamping(priv->ioaddr, 0);
index 90d41d2..7536a4c 100644 (file)
@@ -967,14 +967,19 @@ static inline void cpsw_add_dual_emac_def_ale_entries(
                priv->host_port, ALE_VLAN, slave->port_vlan);
 }
 
-static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
+static void soft_reset_slave(struct cpsw_slave *slave)
 {
        char name[32];
-       u32 slave_port;
-
-       sprintf(name, "slave-%d", slave->slave_num);
 
+       snprintf(name, sizeof(name), "slave-%d", slave->slave_num);
        soft_reset(name, &slave->sliver->soft_reset);
+}
+
+static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
+{
+       u32 slave_port;
+
+       soft_reset_slave(slave);
 
        /* setup priority mapping */
        __raw_writel(RX_PRIORITY_MAPPING, &slave->sliver->rx_pri_map);
@@ -1323,6 +1328,10 @@ static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
        struct cpts *cpts = priv->cpts;
        struct hwtstamp_config cfg;
 
+       if (priv->version != CPSW_VERSION_1 &&
+           priv->version != CPSW_VERSION_2)
+               return -EOPNOTSUPP;
+
        if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
                return -EFAULT;
 
@@ -1330,16 +1339,8 @@ static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
        if (cfg.flags)
                return -EINVAL;
 
-       switch (cfg.tx_type) {
-       case HWTSTAMP_TX_OFF:
-               cpts->tx_enable = 0;
-               break;
-       case HWTSTAMP_TX_ON:
-               cpts->tx_enable = 1;
-               break;
-       default:
+       if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON)
                return -ERANGE;
-       }
 
        switch (cfg.rx_filter) {
        case HWTSTAMP_FILTER_NONE:
@@ -1366,6 +1367,8 @@ static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
                return -ERANGE;
        }
 
+       cpts->tx_enable = cfg.tx_type == HWTSTAMP_TX_ON;
+
        switch (priv->version) {
        case CPSW_VERSION_1:
                cpsw_hwtstamp_v1(priv);
@@ -1374,7 +1377,7 @@ static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
                cpsw_hwtstamp_v2(priv);
                break;
        default:
-               return -ENOTSUPP;
+               WARN_ON(1);
        }
 
        return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
@@ -2173,8 +2176,9 @@ static int cpsw_suspend(struct device *dev)
 
        if (netif_running(ndev))
                cpsw_ndo_stop(ndev);
-       soft_reset("sliver 0", &priv->slaves[0].sliver->soft_reset);
-       soft_reset("sliver 1", &priv->slaves[1].sliver->soft_reset);
+
+       for_each_slave(priv, soft_reset_slave);
+
        pm_runtime_put_sync(&pdev->dev);
 
        /* Select sleep pin state */
index e78802e..bcc224a 100644 (file)
@@ -389,16 +389,8 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
        ch = PORT2CHANNEL(port);
        regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
 
-       switch (cfg.tx_type) {
-       case HWTSTAMP_TX_OFF:
-               port->hwts_tx_en = 0;
-               break;
-       case HWTSTAMP_TX_ON:
-               port->hwts_tx_en = 1;
-               break;
-       default:
+       if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON)
                return -ERANGE;
-       }
 
        switch (cfg.rx_filter) {
        case HWTSTAMP_FILTER_NONE:
@@ -416,6 +408,8 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
                return -ERANGE;
        }
 
+       port->hwts_tx_en = cfg.tx_type == HWTSTAMP_TX_ON;
+
        /* Clear out any old time stamps. */
        __raw_writel(TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED,
                     &regs->channel[ch].ch_event);
index 6f10b49..2cbe1c2 100644 (file)
@@ -561,7 +561,7 @@ at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
 
        spin_lock_irqsave(&lp->lock, flags);
        lp->is_tx = 1;
-       INIT_COMPLETION(lp->tx_complete);
+       reinit_completion(&lp->tx_complete);
        spin_unlock_irqrestore(&lp->lock, flags);
 
        rc = at86rf230_write_fbuf(lp, skb->data, skb->len);
index 0632d34..c6e46d6 100644 (file)
@@ -343,7 +343,7 @@ static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
        if (ret)
                goto err;
 
-       INIT_COMPLETION(devrec->tx_complete);
+       reinit_completion(&devrec->tx_complete);
 
        /* Set TXNTRIG bit of TXNCON to send packet */
        ret = read_short_reg(devrec, REG_TXNCON, &val);
index 7bbd318..befa45f 100644 (file)
@@ -627,7 +627,7 @@ static int ali_ircc_setup(chipio_t *info)
 /*
  * Function ali_ircc_read_dongle_id (int index, info)
  *
- * Try to read dongle indentification. This procedure needs to be executed
+ * Try to read dongle identification. This procedure needs to be executed
  * once after power-on/reset. It also needs to be used whenever you suspect
  * that the user may have plugged/unplugged the IrDA Dongle.
  */
index ceeb537..66bc03b 100644 (file)
@@ -1035,7 +1035,7 @@ static int nsc_ircc_setup(chipio_t *info)
 /*
  * Function nsc_ircc_read_dongle_id (void)
  *
- * Try to read dongle indentification. This procedure needs to be executed
+ * Try to read dongle identification. This procedure needs to be executed
  * once after power-on/reset. It also needs to be used whenever you suspect
  * that the user may have plugged/unplugged the IrDA Dongle.
  */
index 9dccb1e..dc76670 100644 (file)
@@ -628,6 +628,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
                                const struct iovec *iv, unsigned long total_len,
                                size_t count, int noblock)
 {
+       int good_linear = SKB_MAX_HEAD(NET_IP_ALIGN);
        struct sk_buff *skb;
        struct macvlan_dev *vlan;
        unsigned long len = total_len;
@@ -670,6 +671,8 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
 
        if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY)) {
                copylen = vnet_hdr.hdr_len ? vnet_hdr.hdr_len : GOODCOPY_LEN;
+               if (copylen > good_linear)
+                       copylen = good_linear;
                linear = copylen;
                if (iov_pages(iv, vnet_hdr_len + copylen, count)
                    <= MAX_SKB_FRAGS)
@@ -678,7 +681,10 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
 
        if (!zerocopy) {
                copylen = len;
-               linear = vnet_hdr.hdr_len;
+               if (vnet_hdr.hdr_len > good_linear)
+                       linear = good_linear;
+               else
+                       linear = vnet_hdr.hdr_len;
        }
 
        skb = macvtap_alloc_skb(&q->sk, NET_IP_ALIGN, copylen,
index 74630e9..d6447b3 100644 (file)
@@ -697,7 +697,7 @@ static int genphy_config_advert(struct phy_device *phydev)
  *   to the values in phydev. Assumes that the values are valid.
  *   Please see phy_sanitize_settings().
  */
-static int genphy_setup_forced(struct phy_device *phydev)
+int genphy_setup_forced(struct phy_device *phydev)
 {
        int err;
        int ctl = 0;
@@ -716,7 +716,7 @@ static int genphy_setup_forced(struct phy_device *phydev)
 
        return err;
 }
-
+EXPORT_SYMBOL(genphy_setup_forced);
 
 /**
  * genphy_restart_aneg - Enable and Restart Autonegotiation
index 69b482b..508e435 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Author: Kriston Carson
  *
- * Copyright (c) 2005, 2009 Freescale Semiconductor, Inc.
+ * Copyright (c) 2005, 2009, 2011 Freescale Semiconductor, Inc.
  *
  * This program is free software; you can redistribute  it and/or modify it
  * under  the terms of  the GNU General  Public License as published by the
 #include <linux/ethtool.h>
 #include <linux/phy.h>
 
+/* Vitesse Extended Page Magic Register(s) */
+#define MII_VSC82X4_EXT_PAGE_16E       0x10
+#define MII_VSC82X4_EXT_PAGE_17E       0x11
+#define MII_VSC82X4_EXT_PAGE_18E       0x12
+
 /* Vitesse Extended Control Register 1 */
 #define MII_VSC8244_EXT_CON1           0x17
 #define MII_VSC8244_EXTCON1_INIT       0x0000
 #define MII_VSC8221_AUXCONSTAT_INIT    0x0004 /* need to set this bit? */
 #define MII_VSC8221_AUXCONSTAT_RESERVED        0x0004
 
+/* Vitesse Extended Page Access Register */
+#define MII_VSC82X4_EXT_PAGE_ACCESS    0x1f
+
+#define PHY_ID_VSC8234                 0x000fc620
 #define PHY_ID_VSC8244                 0x000fc6c0
+#define PHY_ID_VSC8574                 0x000704a0
+#define PHY_ID_VSC8662                 0x00070660
 #define PHY_ID_VSC8221                 0x000fc550
 #define PHY_ID_VSC8211                 0x000fc4b0
 
@@ -118,7 +129,9 @@ static int vsc82xx_config_intr(struct phy_device *phydev)
 
        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
                err = phy_write(phydev, MII_VSC8244_IMASK,
-                       phydev->drv->phy_id == PHY_ID_VSC8244 ?
+                       (phydev->drv->phy_id == PHY_ID_VSC8234 ||
+                        phydev->drv->phy_id == PHY_ID_VSC8244 ||
+                        phydev->drv->phy_id == PHY_ID_VSC8574) ?
                                MII_VSC8244_IMASK_MASK :
                                MII_VSC8221_IMASK_MASK);
        else {
@@ -149,21 +162,114 @@ static int vsc8221_config_init(struct phy_device *phydev)
         */
 }
 
-/* Vitesse 824x */
+/* vsc82x4_config_autocross_enable - Enable auto MDI/MDI-X for forced links
+ * @phydev: target phy_device struct
+ *
+ * Enable auto MDI/MDI-X when in 10/100 forced link speeds by writing
+ * special values in the VSC8234/VSC8244 extended reserved registers
+ */
+static int vsc82x4_config_autocross_enable(struct phy_device *phydev)
+{
+       int ret;
+
+       if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed > SPEED_100)
+               return 0;
+
+       /* map extended registers set 0x10 - 0x1e */
+       ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x52b5);
+       if (ret >= 0)
+               ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_18E, 0x0012);
+       if (ret >= 0)
+               ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_17E, 0x2803);
+       if (ret >= 0)
+               ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_16E, 0x87fa);
+       /* map standard registers set 0x10 - 0x1e */
+       if (ret >= 0)
+               ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000);
+       else
+               phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000);
+
+       return ret;
+}
+
+/* vsc82x4_config_aneg - restart auto-negotiation or write BMCR
+ * @phydev: target phy_device struct
+ *
+ * Description: If auto-negotiation is enabled, we configure the
+ *   advertising, and then restart auto-negotiation.  If it is not
+ *   enabled, then we write the BMCR and also start the auto
+ *   MDI/MDI-X feature
+ */
+static int vsc82x4_config_aneg(struct phy_device *phydev)
+{
+       int ret;
+
+       /* Enable auto MDI/MDI-X when in 10/100 forced link speeds by
+        * writing special values in the VSC8234 extended reserved registers
+        */
+       if (phydev->autoneg != AUTONEG_ENABLE && phydev->speed <= SPEED_100) {
+               ret = genphy_setup_forced(phydev);
+
+               if (ret < 0) /* error */
+                       return ret;
+
+               return vsc82x4_config_autocross_enable(phydev);
+       }
+
+       return genphy_config_aneg(phydev);
+}
+
+/* Vitesse 82xx */
 static struct phy_driver vsc82xx_driver[] = {
 {
+       .phy_id         = PHY_ID_VSC8234,
+       .name           = "Vitesse VSC8234",
+       .phy_id_mask    = 0x000ffff0,
+       .features       = PHY_GBIT_FEATURES,
+       .flags          = PHY_HAS_INTERRUPT,
+       .config_init    = &vsc824x_config_init,
+       .config_aneg    = &vsc82x4_config_aneg,
+       .read_status    = &genphy_read_status,
+       .ack_interrupt  = &vsc824x_ack_interrupt,
+       .config_intr    = &vsc82xx_config_intr,
+       .driver         = { .owner = THIS_MODULE,},
+}, {
        .phy_id         = PHY_ID_VSC8244,
        .name           = "Vitesse VSC8244",
        .phy_id_mask    = 0x000fffc0,
        .features       = PHY_GBIT_FEATURES,
        .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &vsc824x_config_init,
-       .config_aneg    = &genphy_config_aneg,
+       .config_aneg    = &vsc82x4_config_aneg,
        .read_status    = &genphy_read_status,
        .ack_interrupt  = &vsc824x_ack_interrupt,
        .config_intr    = &vsc82xx_config_intr,
        .driver         = { .owner = THIS_MODULE,},
 }, {
+       .phy_id         = PHY_ID_VSC8574,
+       .name           = "Vitesse VSC8574",
+       .phy_id_mask    = 0x000ffff0,
+       .features       = PHY_GBIT_FEATURES,
+       .flags          = PHY_HAS_INTERRUPT,
+       .config_init    = &vsc824x_config_init,
+       .config_aneg    = &vsc82x4_config_aneg,
+       .read_status    = &genphy_read_status,
+       .ack_interrupt  = &vsc824x_ack_interrupt,
+       .config_intr    = &vsc82xx_config_intr,
+       .driver         = { .owner = THIS_MODULE,},
+}, {
+       .phy_id         = PHY_ID_VSC8662,
+       .name           = "Vitesse VSC8662",
+       .phy_id_mask    = 0x000ffff0,
+       .features       = PHY_GBIT_FEATURES,
+       .flags          = PHY_HAS_INTERRUPT,
+       .config_init    = &vsc824x_config_init,
+       .config_aneg    = &vsc82x4_config_aneg,
+       .read_status    = &genphy_read_status,
+       .ack_interrupt  = &vsc824x_ack_interrupt,
+       .config_intr    = &vsc82xx_config_intr,
+       .driver         = { .owner = THIS_MODULE,},
+}, {
        /* Vitesse 8221 */
        .phy_id         = PHY_ID_VSC8221,
        .phy_id_mask    = 0x000ffff0,
@@ -207,7 +313,10 @@ module_init(vsc82xx_init);
 module_exit(vsc82xx_exit);
 
 static struct mdio_device_id __maybe_unused vitesse_tbl[] = {
+       { PHY_ID_VSC8234, 0x000ffff0 },
        { PHY_ID_VSC8244, 0x000fffc0 },
+       { PHY_ID_VSC8574, 0x000ffff0 },
+       { PHY_ID_VSC8662, 0x000ffff0 },
        { PHY_ID_VSC8221, 0x000ffff0 },
        { PHY_ID_VSC8211, 0x000ffff0 },
        { }
index 5f66e30..82ee6ed 100644 (file)
@@ -979,8 +979,6 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (error < 0)
                goto end;
 
-       m->msg_namelen = 0;
-
        if (skb) {
                total_len = min_t(size_t, total_len, skb->len);
                error = skb_copy_datagram_iovec(skb, 0, m->msg_iov, total_len);
index 6574eb8..34b0de0 100644 (file)
@@ -2650,7 +2650,7 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb,
        return err;
 }
 
-static struct genl_ops team_nl_ops[] = {
+static const struct genl_ops team_nl_ops[] = {
        {
                .cmd = TEAM_CMD_NOOP,
                .doit = team_nl_cmd_noop,
@@ -2676,15 +2676,15 @@ static struct genl_ops team_nl_ops[] = {
        },
 };
 
-static struct genl_multicast_group team_change_event_mcgrp = {
-       .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
+static const struct genl_multicast_group team_nl_mcgrps[] = {
+       { .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, },
 };
 
 static int team_nl_send_multicast(struct sk_buff *skb,
                                  struct team *team, u32 portid)
 {
-       return genlmsg_multicast_netns(dev_net(team->dev), skb, 0,
-                                      team_change_event_mcgrp.id, GFP_KERNEL);
+       return genlmsg_multicast_netns(&team_nl_family, dev_net(team->dev),
+                                      skb, 0, 0, GFP_KERNEL);
 }
 
 static int team_nl_send_event_options_get(struct team *team,
@@ -2703,23 +2703,8 @@ static int team_nl_send_event_port_get(struct team *team,
 
 static int team_nl_init(void)
 {
-       int err;
-
-       err = genl_register_family_with_ops(&team_nl_family, team_nl_ops,
-                                           ARRAY_SIZE(team_nl_ops));
-       if (err)
-               return err;
-
-       err = genl_register_mc_group(&team_nl_family, &team_change_event_mcgrp);
-       if (err)
-               goto err_change_event_grp_reg;
-
-       return 0;
-
-err_change_event_grp_reg:
-       genl_unregister_family(&team_nl_family);
-
-       return err;
+       return genl_register_family_with_ops_groups(&team_nl_family, team_nl_ops,
+                                                   team_nl_mcgrps);
 }
 
 static void team_nl_fini(void)
index 7cb105c..782e38b 100644 (file)
@@ -981,6 +981,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
        struct sk_buff *skb;
        size_t len = total_len, align = NET_SKB_PAD, linear;
        struct virtio_net_hdr gso = { 0 };
+       int good_linear;
        int offset = 0;
        int copylen;
        bool zerocopy = false;
@@ -1021,12 +1022,16 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
                        return -EINVAL;
        }
 
+       good_linear = SKB_MAX_HEAD(align);
+
        if (msg_control) {
                /* There are 256 bytes to be copied in skb, so there is
                 * enough room for skb expand head in case it is used.
                 * The rest of the buffer is mapped from userspace.
                 */
                copylen = gso.hdr_len ? gso.hdr_len : GOODCOPY_LEN;
+               if (copylen > good_linear)
+                       copylen = good_linear;
                linear = copylen;
                if (iov_pages(iv, offset + copylen, count) <= MAX_SKB_FRAGS)
                        zerocopy = true;
@@ -1034,7 +1039,10 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
 
        if (!zerocopy) {
                copylen = len;
-               linear = gso.hdr_len;
+               if (gso.hdr_len > good_linear)
+                       linear = good_linear;
+               else
+                       linear = gso.hdr_len;
        }
 
        skb = tun_alloc_skb(tfile, align, copylen, linear, noblock);
index f74786a..e15ec2b 100644 (file)
@@ -66,7 +66,7 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);
 static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer);
 static struct usb_driver cdc_ncm_driver;
 
-static u8 cdc_ncm_setup(struct usbnet *dev)
+static int cdc_ncm_setup(struct usbnet *dev)
 {
        struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
        struct usb_cdc_ncm_ntb_parameters ncm_parm;
index f3fce41..5107372 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/ipv6.h>
 
 /* Version Information */
-#define DRIVER_VERSION "v1.01.0 (2013/08/12)"
+#define DRIVER_VERSION "v1.02.0 (2013/10/28)"
 #define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>"
 #define DRIVER_DESC "Realtek RTL8152 Based USB 2.0 Ethernet Adapters"
 #define MODULENAME "r8152"
@@ -307,22 +307,22 @@ enum rtl8152_flags {
 #define MCU_TYPE_USB                   0x0000
 
 struct rx_desc {
-       u32 opts1;
+       __le32 opts1;
 #define RX_LEN_MASK                    0x7fff
-       u32 opts2;
-       u32 opts3;
-       u32 opts4;
-       u32 opts5;
-       u32 opts6;
+       __le32 opts2;
+       __le32 opts3;
+       __le32 opts4;
+       __le32 opts5;
+       __le32 opts6;
 };
 
 struct tx_desc {
-       u32 opts1;
+       __le32 opts1;
 #define TX_FS                  (1 << 31) /* First segment of a packet */
 #define TX_LS                  (1 << 30) /* Final segment of a packet */
 #define TX_LEN_MASK            0x3ffff
 
-       u32 opts2;
+       __le32 opts2;
 #define UDP_CS                 (1 << 31) /* Calculate UDP/IP checksum */
 #define TCP_CS                 (1 << 30) /* Calculate TCP/IP checksum */
 #define IPV4_CS                        (1 << 29) /* Calculate IPv4 checksum */
@@ -365,6 +365,7 @@ struct r8152 {
        struct mii_if_info mii;
        int intr_interval;
        u32 msg_enable;
+       u32 tx_qlen;
        u16 ocp_base;
        u8 *intr_buff;
        u8 version;
@@ -876,7 +877,7 @@ static void write_bulk_callback(struct urb *urb)
 static void intr_callback(struct urb *urb)
 {
        struct r8152 *tp;
-       __u16 *d;
+       __le16 *d;
        int status = urb->status;
        int res;
 
@@ -1136,14 +1137,14 @@ r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, struct sk_buff *skb)
 
 static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
 {
-       u32 remain;
+       int remain;
        u8 *tx_data;
 
        tx_data = agg->head;
        agg->skb_num = agg->skb_len = 0;
-       remain = rx_buf_sz - sizeof(struct tx_desc);
+       remain = rx_buf_sz;
 
-       while (remain >= ETH_ZLEN) {
+       while (remain >= ETH_ZLEN + sizeof(struct tx_desc)) {
                struct tx_desc *tx_desc;
                struct sk_buff *skb;
                unsigned int len;
@@ -1152,12 +1153,14 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
                if (!skb)
                        break;
 
+               remain -= sizeof(*tx_desc);
                len = skb->len;
                if (remain < len) {
                        skb_queue_head(&tp->tx_queue, skb);
                        break;
                }
 
+               tx_data = tx_agg_align(tx_data);
                tx_desc = (struct tx_desc *)tx_data;
                tx_data += sizeof(*tx_desc);
 
@@ -1167,11 +1170,18 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
                agg->skb_len += len;
                dev_kfree_skb_any(skb);
 
-               tx_data = tx_agg_align(tx_data + len);
-               remain = rx_buf_sz - sizeof(*tx_desc) -
-                        (u32)((void *)tx_data - agg->head);
+               tx_data += len;
+               remain = rx_buf_sz - (int)(tx_agg_align(tx_data) - agg->head);
        }
 
+       netif_tx_lock(tp->netdev);
+
+       if (netif_queue_stopped(tp->netdev) &&
+           skb_queue_len(&tp->tx_queue) < tp->tx_qlen)
+               netif_wake_queue(tp->netdev);
+
+       netif_tx_unlock(tp->netdev);
+
        usb_fill_bulk_urb(agg->urb, tp->udev, usb_sndbulkpipe(tp->udev, 2),
                          agg->head, (int)(tx_data - (u8 *)agg->head),
                          (usb_complete_t)write_bulk_callback, agg);
@@ -1188,7 +1198,6 @@ static void rx_bottom(struct r8152 *tp)
        list_for_each_safe(cursor, next, &tp->rx_done) {
                struct rx_desc *rx_desc;
                struct rx_agg *agg;
-               unsigned pkt_len;
                int len_used = 0;
                struct urb *urb;
                u8 *rx_data;
@@ -1204,17 +1213,22 @@ static void rx_bottom(struct r8152 *tp)
 
                rx_desc = agg->head;
                rx_data = agg->head;
-               pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
-               len_used += sizeof(struct rx_desc) + pkt_len;
+               len_used += sizeof(struct rx_desc);
 
-               while (urb->actual_length >= len_used) {
+               while (urb->actual_length > len_used) {
                        struct net_device *netdev = tp->netdev;
                        struct net_device_stats *stats;
+                       unsigned int pkt_len;
                        struct sk_buff *skb;
 
+                       pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
                        if (pkt_len < ETH_ZLEN)
                                break;
 
+                       len_used += pkt_len;
+                       if (urb->actual_length < len_used)
+                               break;
+
                        stats = rtl8152_get_stats(netdev);
 
                        pkt_len -= 4; /* CRC */
@@ -1234,9 +1248,8 @@ static void rx_bottom(struct r8152 *tp)
 
                        rx_data = rx_agg_align(rx_data + pkt_len + 4);
                        rx_desc = (struct rx_desc *)rx_data;
-                       pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
                        len_used = (int)(rx_data - (u8 *)agg->head);
-                       len_used += sizeof(struct rx_desc) + pkt_len;
+                       len_used += sizeof(struct rx_desc);
                }
 
 submit:
@@ -1384,53 +1397,17 @@ static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb,
                                            struct net_device *netdev)
 {
        struct r8152 *tp = netdev_priv(netdev);
-       struct net_device_stats *stats = rtl8152_get_stats(netdev);
-       unsigned long flags;
-       struct tx_agg *agg = NULL;
-       struct tx_desc *tx_desc;
-       unsigned int len;
-       u8 *tx_data;
-       int res;
 
        skb_tx_timestamp(skb);
 
-       /* If tx_queue is not empty, it means at least one previous packt */
-       /* is waiting for sending. Don't send current one before it.      */
-       if (skb_queue_empty(&tp->tx_queue))
-               agg = r8152_get_tx_agg(tp);
-
-       if (!agg) {
-               skb_queue_tail(&tp->tx_queue, skb);
-               return NETDEV_TX_OK;
-       }
+       skb_queue_tail(&tp->tx_queue, skb);
 
-       tx_desc = (struct tx_desc *)agg->head;
-       tx_data = agg->head + sizeof(*tx_desc);
-       agg->skb_num = agg->skb_len = 0;
+       if (list_empty(&tp->tx_free) &&
+           skb_queue_len(&tp->tx_queue) > tp->tx_qlen)
+               netif_stop_queue(netdev);
 
-       len = skb->len;
-       r8152_tx_csum(tp, tx_desc, skb);
-       memcpy(tx_data, skb->data, len);
-       dev_kfree_skb_any(skb);
-       agg->skb_num++;
-       agg->skb_len += len;
-       usb_fill_bulk_urb(agg->urb, tp->udev, usb_sndbulkpipe(tp->udev, 2),
-                         agg->head, len + sizeof(*tx_desc),
-                         (usb_complete_t)write_bulk_callback, agg);
-       res = usb_submit_urb(agg->urb, GFP_ATOMIC);
-       if (res) {
-               /* Can we get/handle EPIPE here? */
-               if (res == -ENODEV) {
-                       netif_device_detach(tp->netdev);
-               } else {
-                       netif_warn(tp, tx_err, netdev,
-                                  "failed tx_urb %d\n", res);
-                       stats->tx_dropped++;
-                       spin_lock_irqsave(&tp->tx_lock, flags);
-                       list_add_tail(&agg->list, &tp->tx_free);
-                       spin_unlock_irqrestore(&tp->tx_lock, flags);
-               }
-       }
+       if (!list_empty(&tp->tx_free))
+               tasklet_schedule(&tp->tl);
 
        return NETDEV_TX_OK;
 }
@@ -1459,6 +1436,14 @@ static void rtl8152_nic_reset(struct r8152 *tp)
        }
 }
 
+static void set_tx_qlen(struct r8152 *tp)
+{
+       struct net_device *netdev = tp->netdev;
+
+       tp->tx_qlen = rx_buf_sz / (netdev->mtu + VLAN_ETH_HLEN + VLAN_HLEN +
+                                  sizeof(struct tx_desc));
+}
+
 static inline u8 rtl8152_get_speed(struct r8152 *tp)
 {
        return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS);
@@ -1470,6 +1455,7 @@ static int rtl8152_enable(struct r8152 *tp)
        int i, ret;
        u8 speed;
 
+       set_tx_qlen(tp);
        speed = rtl8152_get_speed(tp);
        if (speed & _10bps) {
                ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR);
index 90a429b..8494bb5 100644 (file)
@@ -204,9 +204,6 @@ static void intr_complete (struct urb *urb)
                break;
        }
 
-       if (!netif_running (dev->net))
-               return;
-
        status = usb_submit_urb (urb, GFP_ATOMIC);
        if (status != 0)
                netif_err(dev, timer, dev->net,
index bf7c734..7bab4de 100644 (file)
@@ -36,7 +36,10 @@ module_param(csum, bool, 0444);
 module_param(gso, bool, 0444);
 
 /* FIXME: MTU in config. */
-#define MAX_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN)
+#define GOOD_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN)
+#define MERGE_BUFFER_LEN (ALIGN(GOOD_PACKET_LEN + \
+                                sizeof(struct virtio_net_hdr_mrg_rxbuf), \
+                                L1_CACHE_BYTES))
 #define GOOD_COPY_LEN  128
 
 #define VIRTNET_DRIVER_VERSION "1.0.0"
@@ -314,10 +317,10 @@ static int receive_mergeable(struct receive_queue *rq, struct sk_buff *head_skb)
                        head_skb->dev->stats.rx_length_errors++;
                        return -EINVAL;
                }
-               if (unlikely(len > MAX_PACKET_LEN)) {
+               if (unlikely(len > MERGE_BUFFER_LEN)) {
                        pr_debug("%s: rx error: merge buffer too long\n",
                                 head_skb->dev->name);
-                       len = MAX_PACKET_LEN;
+                       len = MERGE_BUFFER_LEN;
                }
                if (unlikely(num_skb_frags == MAX_SKB_FRAGS)) {
                        struct sk_buff *nskb = alloc_skb(0, GFP_ATOMIC);
@@ -336,18 +339,17 @@ static int receive_mergeable(struct receive_queue *rq, struct sk_buff *head_skb)
                if (curr_skb != head_skb) {
                        head_skb->data_len += len;
                        head_skb->len += len;
-                       head_skb->truesize += MAX_PACKET_LEN;
+                       head_skb->truesize += MERGE_BUFFER_LEN;
                }
                page = virt_to_head_page(buf);
                offset = buf - (char *)page_address(page);
                if (skb_can_coalesce(curr_skb, num_skb_frags, page, offset)) {
                        put_page(page);
                        skb_coalesce_rx_frag(curr_skb, num_skb_frags - 1,
-                                            len, MAX_PACKET_LEN);
+                                            len, MERGE_BUFFER_LEN);
                } else {
                        skb_add_rx_frag(curr_skb, num_skb_frags, page,
-                                       offset, len,
-                                       MAX_PACKET_LEN);
+                                       offset, len, MERGE_BUFFER_LEN);
                }
                --rq->num;
        }
@@ -383,7 +385,7 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
                struct page *page = virt_to_head_page(buf);
                skb = page_to_skb(rq, page,
                                  (char *)buf - (char *)page_address(page),
-                                 len, MAX_PACKET_LEN);
+                                 len, MERGE_BUFFER_LEN);
                if (unlikely(!skb)) {
                        dev->stats.rx_dropped++;
                        put_page(page);
@@ -471,11 +473,11 @@ static int add_recvbuf_small(struct receive_queue *rq, gfp_t gfp)
        struct skb_vnet_hdr *hdr;
        int err;
 
-       skb = __netdev_alloc_skb_ip_align(vi->dev, MAX_PACKET_LEN, gfp);
+       skb = __netdev_alloc_skb_ip_align(vi->dev, GOOD_PACKET_LEN, gfp);
        if (unlikely(!skb))
                return -ENOMEM;
 
-       skb_put(skb, MAX_PACKET_LEN);
+       skb_put(skb, GOOD_PACKET_LEN);
 
        hdr = skb_vnet_hdr(skb);
        sg_set_buf(rq->sg, &hdr->hdr, sizeof hdr->hdr);
@@ -542,20 +544,20 @@ static int add_recvbuf_mergeable(struct receive_queue *rq, gfp_t gfp)
        int err;
 
        if (gfp & __GFP_WAIT) {
-               if (skb_page_frag_refill(MAX_PACKET_LEN, &vi->alloc_frag,
+               if (skb_page_frag_refill(MERGE_BUFFER_LEN, &vi->alloc_frag,
                                         gfp)) {
                        buf = (char *)page_address(vi->alloc_frag.page) +
                              vi->alloc_frag.offset;
                        get_page(vi->alloc_frag.page);
-                       vi->alloc_frag.offset += MAX_PACKET_LEN;
+                       vi->alloc_frag.offset += MERGE_BUFFER_LEN;
                }
        } else {
-               buf = netdev_alloc_frag(MAX_PACKET_LEN);
+               buf = netdev_alloc_frag(MERGE_BUFFER_LEN);
        }
        if (!buf)
                return -ENOMEM;
 
-       sg_init_one(rq->sg, buf, MAX_PACKET_LEN);
+       sg_init_one(rq->sg, buf, MERGE_BUFFER_LEN);
        err = virtqueue_add_inbuf(rq->vq, rq->sg, 1, buf, gfp);
        if (err < 0)
                put_page(virt_to_head_page(buf));
@@ -591,7 +593,8 @@ static bool try_fill_recv(struct receive_queue *rq, gfp_t gfp)
        } while (rq->vq->num_free);
        if (unlikely(rq->num > rq->max))
                rq->max = rq->num;
-       virtqueue_kick(rq->vq);
+       if (unlikely(!virtqueue_kick(rq->vq)))
+               return false;
        return !oom;
 }
 
@@ -797,7 +800,7 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
        err = xmit_skb(sq, skb);
 
        /* This should not happen! */
-       if (unlikely(err)) {
+       if (unlikely(err) || unlikely(!virtqueue_kick(sq->vq))) {
                dev->stats.tx_fifo_errors++;
                if (net_ratelimit())
                        dev_warn(&dev->dev,
@@ -806,7 +809,6 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
                kfree_skb(skb);
                return NETDEV_TX_OK;
        }
-       virtqueue_kick(sq->vq);
 
        /* Don't wait up for transmitted skbs to be freed. */
        skb_orphan(skb);
@@ -865,12 +867,14 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,
        BUG_ON(virtqueue_add_sgs(vi->cvq, sgs, out_num, in_num, vi, GFP_ATOMIC)
               < 0);
 
-       virtqueue_kick(vi->cvq);
+       if (unlikely(!virtqueue_kick(vi->cvq)))
+               return status == VIRTIO_NET_OK;
 
        /* Spin for a response, the kick causes an ioport write, trapping
         * into the hypervisor, so the request should be handled immediately.
         */
-       while (!virtqueue_get_buf(vi->cvq, &tmp))
+       while (!virtqueue_get_buf(vi->cvq, &tmp) &&
+              !virtqueue_is_broken(vi->cvq))
                cpu_relax();
 
        return status == VIRTIO_NET_OK;
@@ -898,8 +902,13 @@ static int virtnet_set_mac_address(struct net_device *dev, void *p)
                        return -EINVAL;
                }
        } else if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) {
-               vdev->config->set(vdev, offsetof(struct virtio_net_config, mac),
-                                 addr->sa_data, dev->addr_len);
+               unsigned int i;
+
+               /* Naturally, this has an atomicity problem. */
+               for (i = 0; i < dev->addr_len; i++)
+                       virtio_cwrite8(vdev,
+                                      offsetof(struct virtio_net_config, mac) +
+                                      i, addr->sa_data[i]);
        }
 
        eth_commit_mac_addr_change(dev, p);
@@ -1281,9 +1290,8 @@ static void virtnet_config_changed_work(struct work_struct *work)
        if (!vi->config_enable)
                goto done;
 
-       if (virtio_config_val(vi->vdev, VIRTIO_NET_F_STATUS,
-                             offsetof(struct virtio_net_config, status),
-                             &v) < 0)
+       if (virtio_cread_feature(vi->vdev, VIRTIO_NET_F_STATUS,
+                                struct virtio_net_config, status, &v) < 0)
                goto done;
 
        if (v & VIRTIO_NET_S_ANNOUNCE) {
@@ -1507,9 +1515,9 @@ static int virtnet_probe(struct virtio_device *vdev)
        u16 max_queue_pairs;
 
        /* Find if host supports multiqueue virtio_net device */
-       err = virtio_config_val(vdev, VIRTIO_NET_F_MQ,
-                               offsetof(struct virtio_net_config,
-                               max_virtqueue_pairs), &max_queue_pairs);
+       err = virtio_cread_feature(vdev, VIRTIO_NET_F_MQ,
+                                  struct virtio_net_config,
+                                  max_virtqueue_pairs, &max_queue_pairs);
 
        /* We need at least 2 queue's */
        if (err || max_queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
@@ -1561,9 +1569,11 @@ static int virtnet_probe(struct virtio_device *vdev)
        dev->vlan_features = dev->features;
 
        /* Configuration may specify what MAC to use.  Otherwise random. */
-       if (virtio_config_val_len(vdev, VIRTIO_NET_F_MAC,
-                                 offsetof(struct virtio_net_config, mac),
-                                 dev->dev_addr, dev->addr_len) < 0)
+       if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC))
+               virtio_cread_bytes(vdev,
+                                  offsetof(struct virtio_net_config, mac),
+                                  dev->dev_addr, dev->addr_len);
+       else
                eth_hw_addr_random(dev);
 
        /* Set up our device-specific information */
@@ -1611,8 +1621,8 @@ static int virtnet_probe(struct virtio_device *vdev)
        if (err)
                goto free_stats;
 
-       netif_set_real_num_tx_queues(dev, 1);
-       netif_set_real_num_rx_queues(dev, 1);
+       netif_set_real_num_tx_queues(dev, vi->curr_queue_pairs);
+       netif_set_real_num_rx_queues(dev, vi->curr_queue_pairs);
 
        err = register_netdev(dev);
        if (err) {
@@ -1704,7 +1714,7 @@ static void virtnet_remove(struct virtio_device *vdev)
        free_netdev(vi->dev);
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int virtnet_freeze(struct virtio_device *vdev)
 {
        struct virtnet_info *vi = vdev->priv;
@@ -1795,7 +1805,7 @@ static struct virtio_driver virtio_net_driver = {
        .probe =        virtnet_probe,
        .remove =       virtnet_remove,
        .config_changed = virtnet_config_changed,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
        .freeze =       virtnet_freeze,
        .restore =      virtnet_restore,
 #endif
index 3118d75..edae50b 100644 (file)
@@ -534,7 +534,7 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc)
        u16 credit_count;
        u16 credit_size;
 
-       INIT_COMPLETION(htc->ctl_resp);
+       reinit_completion(&htc->ctl_resp);
 
        status = ath10k_hif_start(htc->ar);
        if (status) {
@@ -669,7 +669,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
        req_msg->flags = __cpu_to_le16(flags);
        req_msg->service_id = __cpu_to_le16(conn_req->service_id);
 
-       INIT_COMPLETION(htc->ctl_resp);
+       reinit_completion(&htc->ctl_resp);
 
        status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
        if (status) {
index 0b1cc51..97ac8c8 100644 (file)
@@ -92,7 +92,7 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       INIT_COMPLETION(ar->install_key_done);
+       reinit_completion(&ar->install_key_done);
 
        ret = ath10k_send_key(arvif, key, cmd, macaddr);
        if (ret)
@@ -438,7 +438,7 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       INIT_COMPLETION(ar->vdev_setup_done);
+       reinit_completion(&ar->vdev_setup_done);
 
        arg.vdev_id = arvif->vdev_id;
        arg.dtim_period = arvif->dtim_period;
@@ -491,7 +491,7 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif)
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       INIT_COMPLETION(ar->vdev_setup_done);
+       reinit_completion(&ar->vdev_setup_done);
 
        ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
        if (ret) {
@@ -1666,7 +1666,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
                }
 
                spin_lock_bh(&ar->data_lock);
-               INIT_COMPLETION(ar->offchan_tx_completed);
+               reinit_completion(&ar->offchan_tx_completed);
                ar->offchan_tx_skb = skb;
                spin_unlock_bh(&ar->data_lock);
 
@@ -2476,8 +2476,8 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw,
                goto exit;
        }
 
-       INIT_COMPLETION(ar->scan.started);
-       INIT_COMPLETION(ar->scan.completed);
+       reinit_completion(&ar->scan.started);
+       reinit_completion(&ar->scan.completed);
        ar->scan.in_progress = true;
        ar->scan.aborting = false;
        ar->scan.is_roc = false;
@@ -2832,9 +2832,9 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
                goto exit;
        }
 
-       INIT_COMPLETION(ar->scan.started);
-       INIT_COMPLETION(ar->scan.completed);
-       INIT_COMPLETION(ar->scan.on_channel);
+       reinit_completion(&ar->scan.started);
+       reinit_completion(&ar->scan.completed);
+       reinit_completion(&ar->scan.on_channel);
        ar->scan.in_progress = true;
        ar->scan.aborting = false;
        ar->scan.is_roc = true;
index f8d59c7..9e86a81 100644 (file)
@@ -2363,7 +2363,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
                break;
        default:
                ret = -ENODEV;
-               ath10k_err("Unkown device ID: %d\n", pci_dev->device);
+               ath10k_err("Unknown device ID: %d\n", pci_dev->device);
                goto err_ar_pci;
        }
 
index ce86f15..ba200b2 100644 (file)
@@ -661,7 +661,7 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
                        ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1,
                                                AR5K_SISR1_QCU_TXEOL);
 
-               /* Currently this is not much usefull since we treat
+               /* Currently this is not much useful since we treat
                 * all queues the same way if we get a TXURN (update
                 * tx trigger level) but we might need it later on*/
                if (pisr & AR5K_ISR_TXURN)
index b07f164..20e4909 100644 (file)
@@ -187,17 +187,17 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
                INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
                               ar9485_1_1_baseband_core_txfir_coeff_japan_2484);
 
-               /* Load PCIE SERDES settings from INI */
-
-               /* Awake Setting */
-
-               INIT_INI_ARRAY(&ah->iniPcieSerdes,
-                               ar9485_1_1_pcie_phy_clkreq_disable_L1);
-
-               /* Sleep Setting */
-
-               INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
-                               ar9485_1_1_pcie_phy_clkreq_disable_L1);
+               if (ah->config.no_pll_pwrsave) {
+                       INIT_INI_ARRAY(&ah->iniPcieSerdes,
+                                      ar9485_1_1_pcie_phy_clkreq_disable_L1);
+                       INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+                                      ar9485_1_1_pcie_phy_clkreq_disable_L1);
+               } else {
+                       INIT_INI_ARRAY(&ah->iniPcieSerdes,
+                                      ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
+                       INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+                                      ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
+               }
        } else if (AR_SREV_9462_21(ah)) {
                INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
                               ar9462_2p1_mac_core);
index 11f5358..d39b79f 100644 (file)
@@ -701,6 +701,54 @@ static int ar9550_hw_get_modes_txgain_index(struct ath_hw *ah,
        return ret;
 }
 
+static void ar9003_doubler_fix(struct ath_hw *ah)
+{
+       if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9550(ah)) {
+               REG_RMW(ah, AR_PHY_65NM_CH0_RXTX2,
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S |
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0);
+               REG_RMW(ah, AR_PHY_65NM_CH1_RXTX2,
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S |
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0);
+               REG_RMW(ah, AR_PHY_65NM_CH2_RXTX2,
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S |
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0);
+
+               udelay(200);
+
+               REG_CLR_BIT(ah, AR_PHY_65NM_CH0_RXTX2,
+                           AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK);
+               REG_CLR_BIT(ah, AR_PHY_65NM_CH1_RXTX2,
+                           AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK);
+               REG_CLR_BIT(ah, AR_PHY_65NM_CH2_RXTX2,
+                           AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK);
+
+               udelay(1);
+
+               REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX2,
+                             AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1);
+               REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX2,
+                             AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1);
+               REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX2,
+                             AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1);
+
+               udelay(200);
+
+               REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_SYNTH12,
+                             AR_PHY_65NM_CH0_SYNTH12_VREFMUL3, 0xf);
+
+               REG_RMW(ah, AR_PHY_65NM_CH0_RXTX2, 0,
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S |
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S);
+               REG_RMW(ah, AR_PHY_65NM_CH1_RXTX2, 0,
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S |
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S);
+               REG_RMW(ah, AR_PHY_65NM_CH2_RXTX2, 0,
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S |
+                       1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S);
+       }
+}
+
 static int ar9003_hw_process_ini(struct ath_hw *ah,
                                 struct ath9k_channel *chan)
 {
@@ -726,6 +774,8 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
                                           modesIndex);
        }
 
+       ar9003_doubler_fix(ah);
+
        /*
         * RXGAIN initvals.
         */
index fca6243..2af667b 100644 (file)
 #define AR_PHY_SYNTH4_LONG_SHIFT_SELECT   ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x00000001 : 0x00000002)
 #define AR_PHY_SYNTH4_LONG_SHIFT_SELECT_S ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0 : 1)
 #define AR_PHY_65NM_CH0_SYNTH7      0x16098
+#define AR_PHY_65NM_CH0_SYNTH12     0x160ac
 #define AR_PHY_65NM_CH0_BIAS1       0x160c0
 #define AR_PHY_65NM_CH0_BIAS2       0x160c4
 #define AR_PHY_65NM_CH0_BIAS4       0x160cc
+#define AR_PHY_65NM_CH0_RXTX2       0x16104
+#define AR_PHY_65NM_CH1_RXTX2       0x16504
+#define AR_PHY_65NM_CH2_RXTX2       0x16904
 #define AR_PHY_65NM_CH0_RXTX4       0x1610c
 #define AR_PHY_65NM_CH1_RXTX4       0x1650c
 #define AR_PHY_65NM_CH2_RXTX4       0x1690c
 
+#define AR_PHY_65NM_CH0_SYNTH12_VREFMUL3           0x00780000
+#define AR_PHY_65NM_CH0_SYNTH12_VREFMUL3_S         19
+#define AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK         0x00000004
+#define AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S       2
+#define AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK        0x00000008
+#define AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S      3
+
 #define AR_CH0_TOP     (AR_SREV_9300(ah) ? 0x16288 : \
                         (((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x1628c : 0x16280)))
 #define AR_CH0_TOP_XPABIASLVL (AR_SREV_9550(ah) ? 0x3c0 : 0x300)
index 4dbc294..57fc5f4 100644 (file)
@@ -361,7 +361,7 @@ static const u32 ar9462_2p1_baseband_postamble[][5] = {
        {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32365a5e},
        {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
-       {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+       {0x00009e20, 0x000003a5, 0x000003a5, 0x000003a5, 0x000003a5},
        {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
        {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282},
        {0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27},
@@ -400,7 +400,7 @@ static const u32 ar9462_2p1_baseband_postamble[][5] = {
        {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000},
        {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
-       {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce},
+       {0x0000ae20, 0x000001a6, 0x000001a6, 0x000001aa, 0x000001aa},
        {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550},
 };
 
@@ -472,7 +472,7 @@ static const u32 ar9462_2p1_radio_postamble[][5] = {
 
 static const u32 ar9462_2p1_soc_preamble[][2] = {
        /* Addr      allmodes  */
-       {0x000040a4, 0x00a0c1c9},
+       {0x000040a4, 0x00a0c9c9},
        {0x00007020, 0x00000000},
        {0x00007034, 0x00000002},
        {0x00007038, 0x000004c2},
index 6f899c6..7c18452 100644 (file)
@@ -32,13 +32,6 @@ static const u32 ar9485_1_1_mac_postamble[][5] = {
        {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
 };
 
-static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = {
-       /* Addr      allmodes  */
-       {0x00018c00, 0x18012e5e},
-       {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0000080c},
-};
-
 static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = {
        /* Addr      allmodes  */
        {0x00009e00, 0x037216a0},
@@ -1101,20 +1094,6 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = {
        {0x0000a1fc, 0x00000296},
 };
 
-static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = {
-       /* Addr      allmodes  */
-       {0x00018c00, 0x18052e5e},
-       {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0000080c},
-};
-
-static const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = {
-       /* Addr      allmodes  */
-       {0x00018c00, 0x18053e5e},
-       {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0000080c},
-};
-
 static const u32 ar9485_1_1_soc_preamble[][2] = {
        /* Addr      allmodes  */
        {0x00004014, 0xba280400},
@@ -1173,13 +1152,6 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = {
        {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
 };
 
-static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = {
-       /* Addr      allmodes  */
-       {0x00018c00, 0x18013e5e},
-       {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0000080c},
-};
-
 static const u32 ar9485_1_1_radio_postamble[][2] = {
        /* Addr      allmodes  */
        {0x0001609c, 0x0b283f31},
@@ -1358,4 +1330,18 @@ static const u32 ar9485_1_1_baseband_core_txfir_coeff_japan_2484[][2] = {
        {0x0000a3a0, 0xca9228ee},
 };
 
+static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = {
+       /* Addr      allmodes  */
+       {0x00018c00, 0x18013e5e},
+       {0x00018c04, 0x000801d8},
+       {0x00018c08, 0x0000080c},
+};
+
+static const u32 ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1[][2] = {
+       /* Addr      allmodes  */
+       {0x00018c00, 0x1801265e},
+       {0x00018c04, 0x000801d8},
+       {0x00018c08, 0x0000080c},
+};
+
 #endif /* INITVALS_9485_H */
index e7a38d8..60a5da5 100644 (file)
@@ -632,15 +632,16 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
 /* Main driver core */
 /********************/
 
-#define ATH9K_PCI_CUS198      0x0001
-#define ATH9K_PCI_CUS230      0x0002
-#define ATH9K_PCI_CUS217      0x0004
-#define ATH9K_PCI_CUS252      0x0008
-#define ATH9K_PCI_WOW         0x0010
-#define ATH9K_PCI_BT_ANT_DIV  0x0020
-#define ATH9K_PCI_D3_L1_WAR   0x0040
-#define ATH9K_PCI_AR9565_1ANT 0x0080
-#define ATH9K_PCI_AR9565_2ANT 0x0100
+#define ATH9K_PCI_CUS198          0x0001
+#define ATH9K_PCI_CUS230          0x0002
+#define ATH9K_PCI_CUS217          0x0004
+#define ATH9K_PCI_CUS252          0x0008
+#define ATH9K_PCI_WOW             0x0010
+#define ATH9K_PCI_BT_ANT_DIV      0x0020
+#define ATH9K_PCI_D3_L1_WAR       0x0040
+#define ATH9K_PCI_AR9565_1ANT     0x0080
+#define ATH9K_PCI_AR9565_2ANT     0x0100
+#define ATH9K_PCI_NO_PLL_PWRSAVE  0x0200
 
 /*
  * Default cache line size, in bytes.
index 90b8342..8824610 100644 (file)
@@ -44,14 +44,20 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf,
        if (buf == NULL)
                return -ENOMEM;
 
-       if (sc->dfs_detector)
-               dfs_pool_stats = sc->dfs_detector->get_stats(sc->dfs_detector);
-
        len += scnprintf(buf + len, size - len, "DFS support for "
                         "macVersion = 0x%x, macRev = 0x%x: %s\n",
                         hw_ver->macVersion, hw_ver->macRev,
                         (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_DFS) ?
                                        "enabled" : "disabled");
+
+       if (!sc->dfs_detector) {
+               len += scnprintf(buf + len, size - len,
+                                "DFS detector not enabled\n");
+               goto exit;
+       }
+
+       dfs_pool_stats = sc->dfs_detector->get_stats(sc->dfs_detector);
+
        len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n");
        ATH9K_DFS_STAT("pulse events reported   ", pulses_total);
        ATH9K_DFS_STAT("invalid pulse events    ", pulses_no_dfs);
@@ -76,6 +82,7 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf,
        ATH9K_DFS_POOL_STAT("Seqs. alloc error       ", pseq_alloc_error);
        ATH9K_DFS_POOL_STAT("Seqs. in use            ", pseq_used);
 
+exit:
        if (len > size)
                len = size;
 
index 9ea24f1..a2c9a5d 100644 (file)
@@ -316,6 +316,7 @@ struct ath9k_ops_config {
        u32 ant_ctrl_comm2g_switch_enable;
        bool xatten_margin_cfg;
        bool alt_mingainidx;
+       bool no_pll_pwrsave;
 };
 
 enum ath9k_int {
index d8643eb..710192e 100644 (file)
@@ -609,6 +609,11 @@ static void ath9k_init_platform(struct ath_softc *sc)
                ah->config.pcie_waen = 0x0040473b;
                ath_info(common, "Enable WAR for ASPM D3/L1\n");
        }
+
+       if (sc->driver_data & ATH9K_PCI_NO_PLL_PWRSAVE) {
+               ah->config.no_pll_pwrsave = true;
+               ath_info(common, "Disable PLL PowerSave\n");
+       }
 }
 
 static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob,
@@ -863,8 +868,8 @@ static const struct ieee80211_iface_combination if_comb[] = {
                .max_interfaces = 1,
                .num_different_channels = 1,
                .beacon_int_infra_match = true,
-               .radar_detect_widths =  BIT(NL80211_CHAN_NO_HT) |
-                                       BIT(NL80211_CHAN_HT20),
+               .radar_detect_widths =  BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                       BIT(NL80211_CHAN_WIDTH_20),
        }
 };
 
index 7e4c252..b5656fc 100644 (file)
@@ -195,6 +195,93 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
                         0x3219),
          .driver_data = ATH9K_PCI_BT_ANT_DIV },
 
+       /* AR9485 cards with PLL power-save disabled by default. */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2C97),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2100),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x1C56, /* ASKEY */
+                        0x4001),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x11AD, /* LITEON */
+                        0x6627),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x11AD, /* LITEON */
+                        0x6628),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE04E),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE04F),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x144F, /* ASKEY */
+                        0x7197),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x1B9A, /* XAVI */
+                        0x2000),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x1B9A, /* XAVI */
+                        0x2001),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1186),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1F86),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1195),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1F95),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x1B9A, /* XAVI */
+                        0x1C00),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x1B9A, /* XAVI */
+                        0x1C01),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_ASUSTEK,
+                        0x850D),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+
        { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E  AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E  AR9580 */
 
index 307bc0d..ca115f3 100644 (file)
@@ -773,7 +773,7 @@ void carl9170_usb_stop(struct ar9170 *ar)
        complete_all(&ar->cmd_wait);
 
        /* This is required to prevent an early completion on _start */
-       INIT_COMPLETION(ar->cmd_wait);
+       reinit_completion(&ar->cmd_wait);
 
        /*
         * Note:
index c00687e..1217c52 100644 (file)
@@ -362,7 +362,8 @@ static int __ath_reg_dyn_country(struct wiphy *wiphy,
 {
        u16 country_code;
 
-       if (!ath_is_world_regd(reg))
+       if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+           !ath_is_world_regd(reg))
                return -EINVAL;
 
        country_code = ath_regd_find_country_by_name(request->alpha2);
index 5b84f7a..ef44a2d 100644 (file)
@@ -126,7 +126,7 @@ static ssize_t write_file_dump(struct file *file,
                if (begin == NULL)
                        break;
 
-               if (kstrtoul(begin, 0, (unsigned long *)(arg + i)) != 0)
+               if (kstrtou32(begin, 0, &arg[i]) != 0)
                        break;
        }
 
index f8c3a10..de9eb2c 100644 (file)
@@ -1286,7 +1286,8 @@ int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif,
        } else {
                wcn36xx_err("Beacon is to big: beacon size=%d\n",
                              msg_body.beacon_length);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out;
        }
        memcpy(msg_body.bssid, vif->addr, ETH_ALEN);
 
@@ -1327,7 +1328,8 @@ int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn,
        if (skb->len > BEACON_TEMPLATE_SIZE) {
                wcn36xx_warn("probe response template is too big: %d\n",
                             skb->len);
-               return -E2BIG;
+               ret = -E2BIG;
+               goto out;
        }
 
        msg.probe_resp_template_len = skb->len;
@@ -1606,7 +1608,8 @@ int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
                /* TODO: it also support ARP response type */
        } else {
                wcn36xx_warn("unknow keep alive packet type %d\n", packet_type);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
index 0a2844c..fd30cdd 100644 (file)
@@ -250,7 +250,7 @@ int wil_reset(struct wil6210_priv *wil)
 
        /* init after reset */
        wil->pending_connect_cid = -1;
-       INIT_COMPLETION(wil->wmi_ready);
+       reinit_completion(&wil->wmi_ready);
 
        /* TODO: release MAC reset */
        wil6210_enable_irq(wil);
index d7a9745..4a22930 100644 (file)
@@ -823,6 +823,7 @@ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
                }
                err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state,
                                      action, P2PAPI_BSSCFG_DEVICE);
+               kfree(chanspecs);
        }
 exit:
        if (err)
@@ -1148,7 +1149,7 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p)
 
        pri_vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
 
-       INIT_COMPLETION(afx_hdl->act_frm_scan);
+       reinit_completion(&afx_hdl->act_frm_scan);
        set_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status);
        afx_hdl->is_active = true;
        afx_hdl->peer_chan = P2P_INVALID_CHANNEL;
@@ -1501,7 +1502,7 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       INIT_COMPLETION(p2p->send_af_done);
+       reinit_completion(&p2p->send_af_done);
        clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
        clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
 
index 668dd27..cc6a0a5 100644 (file)
@@ -913,7 +913,10 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
        char *p2;
        struct debug_data *d = f->private_data;
 
-       pdata = kmalloc(cnt, GFP_KERNEL);
+       if (cnt == 0)
+               return 0;
+
+       pdata = kmalloc(cnt + 1, GFP_KERNEL);
        if (pdata == NULL)
                return 0;
 
@@ -922,6 +925,7 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
                kfree(pdata);
                return 0;
        }
+       pdata[cnt] = '\0';
 
        p0 = pdata;
        for (i = 0; i < num_of_items; i++) {
index ef8c98e..f499efc 100644 (file)
@@ -902,6 +902,7 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
        if (card->model == MODEL_UNKNOWN) {
                pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n",
                       p_dev->manf_id, p_dev->card_id);
+               ret = -ENODEV;
                goto out2;
        }
 
index de0df86..9df7bc9 100644 (file)
@@ -2097,7 +2097,7 @@ out:
 }
 
 /* Generic Netlink operations array */
-static struct genl_ops hwsim_ops[] = {
+static const struct genl_ops hwsim_ops[] = {
        {
                .cmd = HWSIM_CMD_REGISTER,
                .policy = hwsim_genl_policy,
@@ -2148,8 +2148,7 @@ static int hwsim_init_netlink(void)
 
        printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
 
-       rc = genl_register_family_with_ops(&hwsim_genl_family,
-               hwsim_ops, ARRAY_SIZE(hwsim_ops));
+       rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops);
        if (rc)
                goto failure;
 
index fbad00a..aeaea0e 100644 (file)
@@ -2210,8 +2210,10 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
                priv->bss_started = 0;
                priv->bss_num = 0;
 
-               if (mwifiex_cfg80211_init_p2p_client(priv))
-                       return ERR_PTR(-EFAULT);
+               if (mwifiex_cfg80211_init_p2p_client(priv)) {
+                       wdev = ERR_PTR(-EFAULT);
+                       goto done;
+               }
 
                break;
        default:
@@ -2224,7 +2226,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
        if (!dev) {
                wiphy_err(wiphy, "no memory available for netdevice\n");
                priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
-               return ERR_PTR(-ENOMEM);
+               wdev = ERR_PTR(-ENOMEM);
+               goto done;
        }
 
        mwifiex_init_priv_params(priv, dev);
@@ -2264,7 +2267,9 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
                wiphy_err(wiphy, "cannot register virtual network device\n");
                free_netdev(dev);
                priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
-               return ERR_PTR(-EFAULT);
+               priv->netdev = NULL;
+               wdev = ERR_PTR(-EFAULT);
+               goto done;
        }
 
        sema_init(&priv->async_sem, 1);
@@ -2274,6 +2279,13 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
 #ifdef CONFIG_DEBUG_FS
        mwifiex_dev_debugfs_init(priv);
 #endif
+
+done:
+       if (IS_ERR(wdev)) {
+               kfree(priv->wdev);
+               priv->wdev = NULL;
+       }
+
        return wdev;
 }
 EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf);
@@ -2298,7 +2310,10 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
                unregister_netdevice(wdev->netdev);
 
        /* Clear the priv in adapter */
+       priv->netdev->ieee80211_ptr = NULL;
        priv->netdev = NULL;
+       kfree(wdev);
+       priv->wdev = NULL;
 
        priv->media_connected = false;
 
index f80f30b..c8385ec 100644 (file)
@@ -1020,8 +1020,8 @@ struct mwifiex_power_group {
 } __packed;
 
 struct mwifiex_types_power_group {
-       u16 type;
-       u16 length;
+       __le16 type;
+       __le16 length;
 } __packed;
 
 struct host_cmd_ds_txpwr_cfg {
index 220af4f..81ac001 100644 (file)
@@ -82,7 +82,7 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
                             struct mwifiex_ie_list *ie_list)
 {
        u16 travel_len, index, mask;
-       s16 input_len;
+       s16 input_len, tlv_len;
        struct mwifiex_ie *ie;
        u8 *tmp;
 
@@ -91,11 +91,13 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
 
        ie_list->len = 0;
 
-       while (input_len > 0) {
+       while (input_len >= sizeof(struct mwifiex_ie_types_header)) {
                ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
-               input_len -= le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE;
-               travel_len += le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE;
+               tlv_len = le16_to_cpu(ie->ie_length);
+               travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE;
 
+               if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE)
+                       return -1;
                index = le16_to_cpu(ie->ie_index);
                mask = le16_to_cpu(ie->mgmt_subtype_mask);
 
@@ -132,6 +134,7 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
                le16_add_cpu(&ie_list->len,
                             le16_to_cpu(priv->mgmt_ie[index].ie_length) +
                             MWIFIEX_IE_HDR_SIZE);
+               input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE;
        }
 
        if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
index 9d7c9d3..78e8a66 100644 (file)
@@ -411,13 +411,14 @@ static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
  */
 static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
 {
-       int ret, i;
+       int ret;
        char fmt[64];
        struct mwifiex_private *priv;
        struct mwifiex_adapter *adapter = context;
        struct mwifiex_fw_image fw;
        struct semaphore *sem = adapter->card_sem;
        bool init_failed = false;
+       struct wireless_dev *wdev;
 
        if (!firmware) {
                dev_err(adapter->dev,
@@ -469,14 +470,16 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        priv = adapter->priv[MWIFIEX_BSS_ROLE_STA];
        if (mwifiex_register_cfg80211(adapter)) {
                dev_err(adapter->dev, "cannot register with cfg80211\n");
-               goto err_register_cfg80211;
+               goto err_init_fw;
        }
 
        rtnl_lock();
        /* Create station interface by default */
-       if (!mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d",
-                                     NL80211_IFTYPE_STATION, NULL, NULL)) {
+       wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d",
+                                       NL80211_IFTYPE_STATION, NULL, NULL);
+       if (IS_ERR(wdev)) {
                dev_err(adapter->dev, "cannot create default STA interface\n");
+               rtnl_unlock();
                goto err_add_intf;
        }
        rtnl_unlock();
@@ -486,17 +489,6 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        goto done;
 
 err_add_intf:
-       for (i = 0; i < adapter->priv_num; i++) {
-               priv = adapter->priv[i];
-
-               if (!priv)
-                       continue;
-
-               if (priv->wdev && priv->netdev)
-                       mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev);
-       }
-       rtnl_unlock();
-err_register_cfg80211:
        wiphy_unregister(adapter->wiphy);
        wiphy_free(adapter->wiphy);
 err_init_fw:
@@ -1006,12 +998,6 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem)
        wiphy_unregister(priv->wdev->wiphy);
        wiphy_free(priv->wdev->wiphy);
 
-       for (i = 0; i < adapter->priv_num; i++) {
-               priv = adapter->priv[i];
-               if (priv)
-                       kfree(priv->wdev);
-       }
-
        mwifiex_terminate_workqueue(adapter);
 
        /* Unregister device */
index 33fa943..03688aa 100644 (file)
@@ -232,7 +232,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
        }
 
        mwifiex_remove_card(card->adapter, &add_remove_card_sem);
-       kfree(card);
 }
 
 static void mwifiex_pcie_shutdown(struct pci_dev *pdev)
@@ -2313,6 +2312,7 @@ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter)
                pci_release_region(pdev, 0);
                pci_set_drvdata(pdev, NULL);
        }
+       kfree(card);
 }
 
 /*
index 1576104..b44a315 100644 (file)
@@ -196,7 +196,6 @@ mwifiex_sdio_remove(struct sdio_func *func)
        }
 
        mwifiex_remove_card(card->adapter, &add_remove_card_sem);
-       kfree(card);
 }
 
 /*
@@ -1029,7 +1028,10 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter,
                                    struct sk_buff *skb, u32 upld_typ)
 {
        u8 *cmd_buf;
+       __le16 *curr_ptr = (__le16 *)skb->data;
+       u16 pkt_len = le16_to_cpu(*curr_ptr);
 
+       skb_trim(skb, pkt_len);
        skb_pull(skb, INTF_HEADER_LEN);
 
        switch (upld_typ) {
@@ -1742,7 +1744,6 @@ mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
                sdio_claim_host(card->func);
                sdio_disable_func(card->func);
                sdio_release_host(card->func);
-               sdio_set_drvdata(card->func, NULL);
        }
 }
 
@@ -1770,7 +1771,6 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
                return ret;
        }
 
-       sdio_set_drvdata(func, card);
 
        adapter->dev = &func->dev;
 
@@ -1798,6 +1798,8 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
        int ret;
        u8 sdio_ireg;
 
+       sdio_set_drvdata(card->func, card);
+
        /*
         * Read the HOST_INT_STATUS_REG for ACK the first interrupt got
         * from the bootloader. If we don't do this we get a interrupt
@@ -1880,6 +1882,8 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter)
        kfree(card->mpa_rx.len_arr);
        kfree(card->mpa_tx.buf);
        kfree(card->mpa_rx.buf);
+       sdio_set_drvdata(card->func, NULL);
+       kfree(card);
 }
 
 /*
index 7d66018..2181ee2 100644 (file)
@@ -239,14 +239,14 @@ static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd,
                        memmove(cmd_txp_cfg, txp,
                                sizeof(struct host_cmd_ds_txpwr_cfg) +
                                sizeof(struct mwifiex_types_power_group) +
-                               pg_tlv->length);
+                               le16_to_cpu(pg_tlv->length));
 
                        pg_tlv = (struct mwifiex_types_power_group *) ((u8 *)
                                  cmd_txp_cfg +
                                  sizeof(struct host_cmd_ds_txpwr_cfg));
                        cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) +
                                  sizeof(struct mwifiex_types_power_group) +
-                                 pg_tlv->length);
+                                 le16_to_cpu(pg_tlv->length));
                } else {
                        memmove(cmd_txp_cfg, txp, sizeof(*txp));
                }
index 58a6013..2675ca7 100644 (file)
@@ -274,17 +274,20 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
        struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg;
        struct mwifiex_rate_scope *rate_scope;
        struct mwifiex_ie_types_header *head;
-       u16 tlv, tlv_buf_len;
+       u16 tlv, tlv_buf_len, tlv_buf_left;
        u8 *tlv_buf;
        u32 i;
 
-       tlv_buf = ((u8 *)rate_cfg) +
-                       sizeof(struct host_cmd_ds_tx_rate_cfg);
-       tlv_buf_len = le16_to_cpu(*(__le16 *) (tlv_buf + sizeof(u16)));
+       tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg);
+       tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg);
 
-       while (tlv_buf && tlv_buf_len > 0) {
-               tlv = (*tlv_buf);
-               tlv = tlv | (*(tlv_buf + 1) << 8);
+       while (tlv_buf_left >= sizeof(*head)) {
+               head = (struct mwifiex_ie_types_header *)tlv_buf;
+               tlv = le16_to_cpu(head->type);
+               tlv_buf_len = le16_to_cpu(head->len);
+
+               if (tlv_buf_left < (sizeof(*head) + tlv_buf_len))
+                       break;
 
                switch (tlv) {
                case TLV_TYPE_RATE_SCOPE:
@@ -304,9 +307,8 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
                        /* Add RATE_DROP tlv here */
                }
 
-               head = (struct mwifiex_ie_types_header *) tlv_buf;
-               tlv_buf += le16_to_cpu(head->len) + sizeof(*head);
-               tlv_buf_len -= le16_to_cpu(head->len);
+               tlv_buf += (sizeof(*head) + tlv_buf_len);
+               tlv_buf_left -= (sizeof(*head) + tlv_buf_len);
        }
 
        priv->is_data_rate_auto = mwifiex_is_rate_auto(priv);
@@ -340,13 +342,17 @@ static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf)
                ((u8 *) data_buf + sizeof(struct host_cmd_ds_txpwr_cfg));
        pg = (struct mwifiex_power_group *)
                ((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group));
-       length = pg_tlv_hdr->length;
-       if (length > 0) {
-               max_power = pg->power_max;
-               min_power = pg->power_min;
-               length -= sizeof(struct mwifiex_power_group);
-       }
-       while (length) {
+       length = le16_to_cpu(pg_tlv_hdr->length);
+
+       /* At least one structure required to update power */
+       if (length < sizeof(struct mwifiex_power_group))
+               return 0;
+
+       max_power = pg->power_max;
+       min_power = pg->power_min;
+       length -= sizeof(struct mwifiex_power_group);
+
+       while (length >= sizeof(struct mwifiex_power_group)) {
                pg++;
                if (max_power < pg->power_max)
                        max_power = pg->power_max;
@@ -356,10 +362,8 @@ static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf)
 
                length -= sizeof(struct mwifiex_power_group);
        }
-       if (pg_tlv_hdr->length > 0) {
-               priv->min_tx_power_level = (u8) min_power;
-               priv->max_tx_power_level = (u8) max_power;
-       }
+       priv->min_tx_power_level = (u8) min_power;
+       priv->max_tx_power_level = (u8) max_power;
 
        return 0;
 }
index f084412..c8e029d 100644 (file)
@@ -638,8 +638,9 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv,
                txp_cfg->mode = cpu_to_le32(1);
                pg_tlv = (struct mwifiex_types_power_group *)
                         (buf + sizeof(struct host_cmd_ds_txpwr_cfg));
-               pg_tlv->type = TLV_TYPE_POWER_GROUP;
-               pg_tlv->length = 4 * sizeof(struct mwifiex_power_group);
+               pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP);
+               pg_tlv->length =
+                       cpu_to_le16(4 * sizeof(struct mwifiex_power_group));
                pg = (struct mwifiex_power_group *)
                     (buf + sizeof(struct host_cmd_ds_txpwr_cfg)
                      + sizeof(struct mwifiex_types_power_group));
index 1cfe5a7..92f76d6 100644 (file)
@@ -97,6 +97,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        struct mwifiex_txinfo *tx_info;
        int hdr_chop;
        struct timeval tv;
+       struct ethhdr *p_ethhdr;
        u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
 
        uap_rx_pd = (struct uap_rxpd *)(skb->data);
@@ -112,14 +113,36 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        }
 
        if (!memcmp(&rx_pkt_hdr->rfc1042_hdr,
-                   rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)))
+                   rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr))) {
+               /* Replace the 803 header and rfc1042 header (llc/snap) with
+                * an Ethernet II header, keep the src/dst and snap_type
+                * (ethertype).
+                *
+                * The firmware only passes up SNAP frames converting all RX
+                * data from 802.11 to 802.2/LLC/SNAP frames.
+                *
+                * To create the Ethernet II, just move the src, dst address
+                * right before the snap_type.
+                */
+               p_ethhdr = (struct ethhdr *)
+                       ((u8 *)(&rx_pkt_hdr->eth803_hdr)
+                        + sizeof(rx_pkt_hdr->eth803_hdr)
+                        + sizeof(rx_pkt_hdr->rfc1042_hdr)
+                        - sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
+                        - sizeof(rx_pkt_hdr->eth803_hdr.h_source)
+                        - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
+               memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source,
+                      sizeof(p_ethhdr->h_source));
+               memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
+                      sizeof(p_ethhdr->h_dest));
                /* Chop off the rxpd + the excess memory from
                 * 802.2/llc/snap header that was removed.
                 */
-               hdr_chop = (u8 *)eth_hdr - (u8 *)uap_rx_pd;
-       else
+               hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd;
+       } else {
                /* Chop off the rxpd */
                hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd;
+       }
 
        /* Chop off the leading header bytes so the it points
         * to the start of either the reconstructed EthII frame
index 1c70b8d..edf5b7a 100644 (file)
@@ -350,7 +350,6 @@ static int mwifiex_usb_probe(struct usb_interface *intf,
 
        card->udev = udev;
        card->intf = intf;
-       usb_card = card;
 
        pr_debug("info: bcdUSB=%#x Device Class=%#x SubClass=%#x Protocol=%#x\n",
                 udev->descriptor.bcdUSB, udev->descriptor.bDeviceClass,
@@ -525,25 +524,28 @@ static int mwifiex_usb_resume(struct usb_interface *intf)
 static void mwifiex_usb_disconnect(struct usb_interface *intf)
 {
        struct usb_card_rec *card = usb_get_intfdata(intf);
-       struct mwifiex_adapter *adapter;
 
-       if (!card || !card->adapter) {
-               pr_err("%s: card or card->adapter is NULL\n", __func__);
+       if (!card) {
+               pr_err("%s: card is NULL\n", __func__);
                return;
        }
 
-       adapter = card->adapter;
-       if (!adapter->priv_num)
-               return;
-
        mwifiex_usb_free(card);
 
-       dev_dbg(adapter->dev, "%s: removing card\n", __func__);
-       mwifiex_remove_card(adapter, &add_remove_card_sem);
+       if (card->adapter) {
+               struct mwifiex_adapter *adapter = card->adapter;
+
+               if (!adapter->priv_num)
+                       return;
+
+               dev_dbg(adapter->dev, "%s: removing card\n", __func__);
+               mwifiex_remove_card(adapter, &add_remove_card_sem);
+       }
 
        usb_set_intfdata(intf, NULL);
        usb_put_dev(interface_to_usbdev(intf));
        kfree(card);
+       usb_card = NULL;
 
        return;
 }
@@ -754,6 +756,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
        card->adapter = adapter;
        adapter->dev = &card->udev->dev;
        strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME);
+       usb_card = card;
 
        return 0;
 }
@@ -762,7 +765,7 @@ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
 {
        struct usb_card_rec *card = (struct usb_card_rec *)adapter->card;
 
-       usb_set_intfdata(card->intf, NULL);
+       card->adapter = NULL;
 }
 
 static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
@@ -1004,7 +1007,7 @@ static void mwifiex_usb_cleanup_module(void)
        if (!down_interruptible(&add_remove_card_sem))
                up(&add_remove_card_sem);
 
-       if (usb_card) {
+       if (usb_card && usb_card->adapter) {
                struct mwifiex_adapter *adapter = usb_card->adapter;
                int i;
 
index 5dd0ccc..13eaeed 100644 (file)
@@ -722,6 +722,9 @@ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
                tlv_hdr = (struct mwifiex_ie_types_data *) curr;
                tlv_len = le16_to_cpu(tlv_hdr->header.len);
 
+               if (resp_len < tlv_len + sizeof(tlv_hdr->header))
+                       break;
+
                switch (le16_to_cpu(tlv_hdr->header.type)) {
                case TLV_TYPE_WMMQSTATUS:
                        tlv_wmm_qstatus =
index 41a16d3..e05d9b4 100644 (file)
@@ -811,6 +811,10 @@ static const struct net_device_ops islpci_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
 };
 
+static struct device_type wlan_type = {
+       .name   = "wlan",
+};
+
 struct net_device *
 islpci_setup(struct pci_dev *pdev)
 {
@@ -821,9 +825,8 @@ islpci_setup(struct pci_dev *pdev)
                return ndev;
 
        pci_set_drvdata(pdev, ndev);
-#if defined(SET_NETDEV_DEV)
        SET_NETDEV_DEV(ndev, &pdev->dev);
-#endif
+       SET_NETDEV_DEVTYPE(ndev, &wlan_type);
 
        /* setup the structure members */
        ndev->base_addr = pci_resource_start(pdev, 0);
index c5738f1..776aff3 100644 (file)
@@ -2640,7 +2640,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev,
 
        if (rt2x00_rt(rt2x00dev, RT5392)) {
                rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
-               if (info->default_power1 > POWER_BOUND)
+               if (info->default_power2 > POWER_BOUND)
                        rt2x00_set_field8(&rfcsr, RFCSR50_TX, POWER_BOUND);
                else
                        rt2x00_set_field8(&rfcsr, RFCSR50_TX,
index ae15228..a8cc736 100644 (file)
@@ -446,7 +446,7 @@ static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
                if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
                        break;
 
-               if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) {
+               if (!kfifo_put(&rt2x00dev->txstatus_fifo, status)) {
                        rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n");
                        break;
                }
index 997df03..a81ceb6 100644 (file)
@@ -164,7 +164,7 @@ static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
 
        valid = rt2x00_get_field32(tx_status, TX_STA_FIFO_VALID);
        if (valid) {
-               if (!kfifo_put(&rt2x00dev->txstatus_fifo, &tx_status))
+               if (!kfifo_put(&rt2x00dev->txstatus_fifo, tx_status))
                        rt2x00_warn(rt2x00dev, "TX status FIFO overrun\n");
 
                queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
index 080b1fc..9dd92a7 100644 (file)
@@ -181,6 +181,7 @@ static void rt2x00lib_autowakeup(struct work_struct *work)
 static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac,
                                     struct ieee80211_vif *vif)
 {
+       struct ieee80211_tx_control control = {};
        struct rt2x00_dev *rt2x00dev = data;
        struct sk_buff *skb;
 
@@ -195,7 +196,7 @@ static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac,
         */
        skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif);
        while (skb) {
-               rt2x00mac_tx(rt2x00dev->hw, NULL, skb);
+               rt2x00mac_tx(rt2x00dev->hw, &control, skb);
                skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif);
        }
 }
index a093598..7f40ab8 100644 (file)
@@ -146,7 +146,7 @@ void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length);
  * @local: frame is not from mac80211
  */
 int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
-                              bool local);
+                              struct ieee80211_sta *sta, bool local);
 
 /**
  * rt2x00queue_update_beacon - Send new beacon from mac80211
index 7c15785..2183e79 100644 (file)
@@ -90,7 +90,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev,
                                  frag_skb->data, data_length, tx_info,
                                  (struct ieee80211_rts *)(skb->data));
 
-       retval = rt2x00queue_write_tx_frame(queue, skb, true);
+       retval = rt2x00queue_write_tx_frame(queue, skb, NULL, true);
        if (retval) {
                dev_kfree_skb_any(skb);
                rt2x00_warn(rt2x00dev, "Failed to send RTS/CTS frame\n");
@@ -151,7 +151,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw,
                        goto exit_fail;
        }
 
-       if (unlikely(rt2x00queue_write_tx_frame(queue, skb, false)))
+       if (unlikely(rt2x00queue_write_tx_frame(queue, skb, control->sta, false)))
                goto exit_fail;
 
        /*
index 50590b1..a5d38e8 100644 (file)
@@ -635,7 +635,7 @@ static void rt2x00queue_bar_check(struct queue_entry *entry)
 }
 
 int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
-                              bool local)
+                              struct ieee80211_sta *sta, bool local)
 {
        struct ieee80211_tx_info *tx_info;
        struct queue_entry *entry;
@@ -649,7 +649,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
         * after that we are free to use the skb->cb array
         * for our information.
         */
-       rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, NULL);
+       rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, sta);
 
        /*
         * All information is retrieved from the skb->cb array,
index 9a78e3d..ff78407 100644 (file)
@@ -37,6 +37,7 @@
 
 #include <linux/ip.h>
 #include <linux/module.h>
+#include <linux/udp.h>
 
 /*
  *NOTICE!!!: This file will be very big, we should
@@ -1074,64 +1075,52 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
        if (!ieee80211_is_data(fc))
                return false;
 
+       ip = (const struct iphdr *)(skb->data + mac_hdr_len +
+                                   SNAP_SIZE + PROTOC_TYPE_SIZE);
+       ether_type = be16_to_cpup((__be16 *)
+                                 (skb->data + mac_hdr_len + SNAP_SIZE));
 
-       ip = (struct iphdr *)((u8 *) skb->data + mac_hdr_len +
-                             SNAP_SIZE + PROTOC_TYPE_SIZE);
-       ether_type = *(u16 *) ((u8 *) skb->data + mac_hdr_len + SNAP_SIZE);
-       /*      ether_type = ntohs(ether_type); */
-
-       if (ETH_P_IP == ether_type) {
-               if (IPPROTO_UDP == ip->protocol) {
-                       struct udphdr *udp = (struct udphdr *)((u8 *) ip +
-                                                              (ip->ihl << 2));
-                       if (((((u8 *) udp)[1] == 68) &&
-                            (((u8 *) udp)[3] == 67)) ||
-                           ((((u8 *) udp)[1] == 67) &&
-                            (((u8 *) udp)[3] == 68))) {
-                               /*
-                                * 68 : UDP BOOTP client
-                                * 67 : UDP BOOTP server
-                                */
-                               RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV),
-                                        DBG_DMESG, "dhcp %s !!\n",
-                                        is_tx ? "Tx" : "Rx");
-
-                               if (is_tx) {
-                                       rtlpriv->enter_ps = false;
-                                       schedule_work(&rtlpriv->
-                                                     works.lps_change_work);
-                                       ppsc->last_delaylps_stamp_jiffies =
-                                           jiffies;
-                               }
+       switch (ether_type) {
+       case ETH_P_IP: {
+               struct udphdr *udp;
+               u16 src;
+               u16 dst;
 
-                               return true;
-                       }
-               }
-       } else if (ETH_P_ARP == ether_type) {
-               if (is_tx) {
-                       rtlpriv->enter_ps = false;
-                       schedule_work(&rtlpriv->works.lps_change_work);
-                       ppsc->last_delaylps_stamp_jiffies = jiffies;
-               }
+               if (ip->protocol != IPPROTO_UDP)
+                       return false;
+               udp = (struct udphdr *)((u8 *)ip + (ip->ihl << 2));
+               src = be16_to_cpu(udp->source);
+               dst = be16_to_cpu(udp->dest);
 
-               return true;
-       } else if (ETH_P_PAE == ether_type) {
+               /* If this case involves port 68 (UDP BOOTP client) connecting
+                * with port 67 (UDP BOOTP server), then return true so that
+                * the lowest speed is used.
+                */
+               if (!((src == 68 && dst == 67) || (src == 67 && dst == 68)))
+                       return false;
+
+               RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG,
+                        "dhcp %s !!\n", is_tx ? "Tx" : "Rx");
+               break;
+       }
+       case ETH_P_ARP:
+               break;
+       case ETH_P_PAE:
                RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG,
                         "802.1X %s EAPOL pkt!!\n", is_tx ? "Tx" : "Rx");
-
-               if (is_tx) {
-                       rtlpriv->enter_ps = false;
-                       schedule_work(&rtlpriv->works.lps_change_work);
-                       ppsc->last_delaylps_stamp_jiffies = jiffies;
-               }
-
-               return true;
-       } else if (ETH_P_IPV6 == ether_type) {
-               /* IPv6 */
-               return true;
+               break;
+       case ETH_P_IPV6:
+               /* TODO: Is this right? */
+               return false;
+       default:
+               return false;
        }
-
-       return false;
+       if (is_tx) {
+               rtlpriv->enter_ps = false;
+               schedule_work(&rtlpriv->works.lps_change_work);
+               ppsc->last_delaylps_stamp_jiffies = jiffies;
+       }
+       return true;
 }
 EXPORT_SYMBOL_GPL(rtl_is_special_data);
 
index ae13fb9..2ffc729 100644 (file)
@@ -262,9 +262,9 @@ void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf)
                            sizeof(u8), GFP_ATOMIC);
        if (!efuse_tbl)
                return;
-       efuse_word = kmalloc(EFUSE_MAX_WORD_UNIT * sizeof(u16 *), GFP_ATOMIC);
+       efuse_word = kzalloc(EFUSE_MAX_WORD_UNIT * sizeof(u16 *), GFP_ATOMIC);
        if (!efuse_word)
-               goto done;
+               goto out;
        for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
                efuse_word[i] = kmalloc(efuse_max_section * sizeof(u16),
                                        GFP_ATOMIC);
@@ -378,6 +378,7 @@ done:
        for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++)
                kfree(efuse_word[i]);
        kfree(efuse_word);
+out:
        kfree(efuse_tbl);
 }
 
index 3936853..e26312f 100644 (file)
@@ -769,7 +769,7 @@ static long _rtl92c_signal_scale_mapping(struct ieee80211_hw *hw,
 
 static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw,
                                      struct rtl_stats *pstats,
-                                     struct rx_desc_92c *pdesc,
+                                     struct rx_desc_92c *p_desc,
                                      struct rx_fwinfo_92c *p_drvinfo,
                                      bool packet_match_bssid,
                                      bool packet_toself,
@@ -784,11 +784,11 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw,
        u32 rssi, total_rssi = 0;
        bool in_powersavemode = false;
        bool is_cck_rate;
+       u8 *pdesc = (u8 *)p_desc;
 
-       is_cck_rate = RX_HAL_IS_CCK_RATE(pdesc);
+       is_cck_rate = RX_HAL_IS_CCK_RATE(p_desc);
        pstats->packet_matchbssid = packet_match_bssid;
        pstats->packet_toself = packet_toself;
-       pstats->is_cck = is_cck_rate;
        pstats->packet_beacon = packet_beacon;
        pstats->is_cck = is_cck_rate;
        pstats->RX_SIGQ[0] = -1;
index 25e50ff..1bc21cc 100644 (file)
@@ -303,10 +303,10 @@ out:
 bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw,
                           struct rtl_stats *stats,
                           struct ieee80211_rx_status *rx_status,
-                          u8 *p_desc, struct sk_buff *skb)
+                          u8 *pdesc, struct sk_buff *skb)
 {
        struct rx_fwinfo_92c *p_drvinfo;
-       struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc;
+       struct rx_desc_92c *p_desc = (struct rx_desc_92c *)pdesc;
        u32 phystatus = GET_RX_DESC_PHY_STATUS(pdesc);
 
        stats->length = (u16) GET_RX_DESC_PKT_LEN(pdesc);
@@ -345,11 +345,11 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw,
        if (phystatus) {
                p_drvinfo = (struct rx_fwinfo_92c *)(skb->data +
                                                     stats->rx_bufshift);
-               rtl92c_translate_rx_signal_stuff(hw, skb, stats, pdesc,
+               rtl92c_translate_rx_signal_stuff(hw, skb, stats, p_desc,
                                                 p_drvinfo);
        }
        /*rx_status->qual = stats->signal; */
-       rx_status->signal = stats->rssi + 10;
+       rx_status->signal = stats->recvsignalpower + 10;
        return true;
 }
 
index 945ddec..0eb0f4a 100644 (file)
@@ -525,7 +525,7 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
                                                   p_drvinfo);
        }
        /*rx_status->qual = stats->signal; */
-       rx_status->signal = stats->rssi + 10;
+       rx_status->signal = stats->recvsignalpower + 10;
        return true;
 }
 
index 5061f1d..92d38ab 100644 (file)
@@ -265,7 +265,7 @@ static void _rtl92s_get_txpower_writeval_byregulatory(struct ieee80211_hw *hw,
                                    rtlefuse->pwrgroup_ht40
                                    [RF90_PATH_A][chnl - 1]) {
                                        pwrdiff_limit[i] =
-                                         rtlefuse->pwrgroup_ht20
+                                         rtlefuse->pwrgroup_ht40
                                          [RF90_PATH_A][chnl - 1];
                                }
                        } else {
index 222d2e7..27efbcd 100644 (file)
@@ -329,7 +329,7 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
        }
 
        /*rx_status->qual = stats->signal; */
-       rx_status->signal = stats->rssi + 10;
+       rx_status->signal = stats->recvsignalpower + 10;
 
        return true;
 }
index d224dc3..0c65386 100644 (file)
 #define RTL_SLOT_TIME_9                                9
 #define RTL_SLOT_TIME_20                       20
 
-/*related with tcp/ip. */
-/*if_ehther.h*/
-#define ETH_P_PAE              0x888E  /*Port Access Entity (IEEE 802.1X) */
-#define ETH_P_IP               0x0800  /*Internet Protocol packet */
-#define ETH_P_ARP              0x0806  /*Address Resolution packet */
+/*related to tcp/ip. */
 #define SNAP_SIZE              6
 #define PROTOC_TYPE_SIZE       2
 
index 7ef0b4a..84d94f5 100644 (file)
@@ -1619,7 +1619,7 @@ static void prepare_read_regs_int(struct zd_usb *usb,
        atomic_set(&intr->read_regs_enabled, 1);
        intr->read_regs.req = req;
        intr->read_regs.req_count = count;
-       INIT_COMPLETION(intr->read_regs.completion);
+       reinit_completion(&intr->read_regs.completion);
        spin_unlock_irq(&intr->lock);
 }
 
index b78ee10..2329ccc 100644 (file)
@@ -461,6 +461,9 @@ void xenvif_disconnect(struct xenvif *vif)
        if (netif_carrier_ok(vif->dev))
                xenvif_carrier_off(vif);
 
+       if (vif->task)
+               kthread_stop(vif->task);
+
        if (vif->tx_irq) {
                if (vif->tx_irq == vif->rx_irq)
                        unbind_from_irqhandler(vif->tx_irq, vif);
@@ -471,9 +474,6 @@ void xenvif_disconnect(struct xenvif *vif)
                vif->tx_irq = 0;
        }
 
-       if (vif->task)
-               kthread_stop(vif->task);
-
        xenvif_unmap_frontend_rings(vif);
 }
 
index d85e669..e59acb1 100644 (file)
@@ -277,12 +277,13 @@ static void xennet_alloc_rx_buffers(struct net_device *dev)
                if (!page) {
                        kfree_skb(skb);
 no_skb:
-                       /* Any skbuffs queued for refill? Force them out. */
-                       if (i != 0)
-                               goto refill;
                        /* Could not allocate any skbuffs. Try again later. */
                        mod_timer(&np->rx_refill_timer,
                                  jiffies + (HZ/10));
+
+                       /* Any skbuffs queued for refill? Force them out. */
+                       if (i != 0)
+                               goto refill;
                        break;
                }
 
index 12a9e83..d0222f1 100644 (file)
@@ -1034,10 +1034,9 @@ static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset,
        struct dma_chan *chan = qp->dma_chan;
        struct dma_device *device;
        size_t pay_off, buff_off;
-       dma_addr_t src, dest;
+       struct dmaengine_unmap_data *unmap;
        dma_cookie_t cookie;
        void *buf = entry->buf;
-       unsigned long flags;
 
        entry->len = len;
 
@@ -1045,35 +1044,49 @@ static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset,
                goto err;
 
        if (len < copy_bytes) 
-               goto err1;
+               goto err_wait;
 
        device = chan->device;
        pay_off = (size_t) offset & ~PAGE_MASK;
        buff_off = (size_t) buf & ~PAGE_MASK;
 
        if (!is_dma_copy_aligned(device, pay_off, buff_off, len))
-               goto err1;
+               goto err_wait;
 
-       dest = dma_map_single(device->dev, buf, len, DMA_FROM_DEVICE);
-       if (dma_mapping_error(device->dev, dest))
-               goto err1;
+       unmap = dmaengine_get_unmap_data(device->dev, 2, GFP_NOWAIT);
+       if (!unmap)
+               goto err_wait;
 
-       src = dma_map_single(device->dev, offset, len, DMA_TO_DEVICE);
-       if (dma_mapping_error(device->dev, src))
-               goto err2;
+       unmap->len = len;
+       unmap->addr[0] = dma_map_page(device->dev, virt_to_page(offset),
+                                     pay_off, len, DMA_TO_DEVICE);
+       if (dma_mapping_error(device->dev, unmap->addr[0]))
+               goto err_get_unmap;
+
+       unmap->to_cnt = 1;
 
-       flags = DMA_COMPL_DEST_UNMAP_SINGLE | DMA_COMPL_SRC_UNMAP_SINGLE |
-               DMA_PREP_INTERRUPT;
-       txd = device->device_prep_dma_memcpy(chan, dest, src, len, flags);
+       unmap->addr[1] = dma_map_page(device->dev, virt_to_page(buf),
+                                     buff_off, len, DMA_FROM_DEVICE);
+       if (dma_mapping_error(device->dev, unmap->addr[1]))
+               goto err_get_unmap;
+
+       unmap->from_cnt = 1;
+
+       txd = device->device_prep_dma_memcpy(chan, unmap->addr[1],
+                                            unmap->addr[0], len,
+                                            DMA_PREP_INTERRUPT);
        if (!txd)
-               goto err3;
+               goto err_get_unmap;
 
        txd->callback = ntb_rx_copy_callback;
        txd->callback_param = entry;
+       dma_set_unmap(txd, unmap);
 
        cookie = dmaengine_submit(txd);
        if (dma_submit_error(cookie))
-               goto err3;
+               goto err_set_unmap;
+
+       dmaengine_unmap_put(unmap);
 
        qp->last_cookie = cookie;
 
@@ -1081,11 +1094,11 @@ static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset,
 
        return;
 
-err3:
-       dma_unmap_single(device->dev, src, len, DMA_TO_DEVICE);
-err2:
-       dma_unmap_single(device->dev, dest, len, DMA_FROM_DEVICE);
-err1:
+err_set_unmap:
+       dmaengine_unmap_put(unmap);
+err_get_unmap:
+       dmaengine_unmap_put(unmap);
+err_wait:
        /* If the callbacks come out of order, the writing of the index to the
         * last completed will be out of order.  This may result in the
         * receive stalling forever.
@@ -1245,12 +1258,12 @@ static void ntb_async_tx(struct ntb_transport_qp *qp,
        struct dma_chan *chan = qp->dma_chan;
        struct dma_device *device;
        size_t dest_off, buff_off;
-       dma_addr_t src, dest;
+       struct dmaengine_unmap_data *unmap;
+       dma_addr_t dest;
        dma_cookie_t cookie;
        void __iomem *offset;
        size_t len = entry->len;
        void *buf = entry->buf;
-       unsigned long flags;
 
        offset = qp->tx_mw + qp->tx_max_frame * qp->tx_index;
        hdr = offset + qp->tx_max_frame - sizeof(struct ntb_payload_header);
@@ -1273,28 +1286,41 @@ static void ntb_async_tx(struct ntb_transport_qp *qp,
        if (!is_dma_copy_aligned(device, buff_off, dest_off, len))
                goto err;
 
-       src = dma_map_single(device->dev, buf, len, DMA_TO_DEVICE);
-       if (dma_mapping_error(device->dev, src))
+       unmap = dmaengine_get_unmap_data(device->dev, 1, GFP_NOWAIT);
+       if (!unmap)
                goto err;
 
-       flags = DMA_COMPL_SRC_UNMAP_SINGLE | DMA_PREP_INTERRUPT;
-       txd = device->device_prep_dma_memcpy(chan, dest, src, len, flags);
+       unmap->len = len;
+       unmap->addr[0] = dma_map_page(device->dev, virt_to_page(buf),
+                                     buff_off, len, DMA_TO_DEVICE);
+       if (dma_mapping_error(device->dev, unmap->addr[0]))
+               goto err_get_unmap;
+
+       unmap->to_cnt = 1;
+
+       txd = device->device_prep_dma_memcpy(chan, dest, unmap->addr[0], len,
+                                            DMA_PREP_INTERRUPT);
        if (!txd)
-               goto err1;
+               goto err_get_unmap;
 
        txd->callback = ntb_tx_copy_callback;
        txd->callback_param = entry;
+       dma_set_unmap(txd, unmap);
 
        cookie = dmaengine_submit(txd);
        if (dma_submit_error(cookie))
-               goto err1;
+               goto err_set_unmap;
+
+       dmaengine_unmap_put(unmap);
 
        dma_async_issue_pending(chan);
        qp->tx_async++;
 
        return;
-err1:
-       dma_unmap_single(device->dev, src, len, DMA_TO_DEVICE);
+err_set_unmap:
+       dmaengine_unmap_put(unmap);
+err_get_unmap:
+       dmaengine_unmap_put(unmap);
 err:
        ntb_memcpy_tx(entry, offset);
        qp->tx_memcpy++;
index dc82ef0..2872ece 100644 (file)
@@ -31,15 +31,18 @@ menuconfig PARPORT
 
          If unsure, say Y.
 
+config ARCH_MIGHT_HAVE_PC_PARPORT
+       bool
+       help
+         Select this config option from the architecture Kconfig if
+         the architecture might have PC parallel port hardware.
+
 if PARPORT
 
 config PARPORT_PC
        tristate "PC-style hardware"
-       depends on (!SPARC64 || PCI) && !SPARC32 && !M32R && !FRV && !S390 && \
-               (!M68K || ISA) && !MN10300 && !AVR32 && !BLACKFIN && \
-               !XTENSA && !CRIS
-
-       ---help---
+       depends on ARCH_MIGHT_HAVE_PC_PARPORT
+       help
          You should say Y here if you have a PC-style parallel port. All
          IBM PC compatible computers and some Alphas have PC-style
          parallel ports.  PA-RISC owners should only say Y here if they
index d471627..c864f82 100644 (file)
@@ -1331,7 +1331,7 @@ static unsigned int parport_ip32_fwp_wait_interrupt(struct parport *p)
                        break;
 
                /* Initialize mutex used to take interrupts into account */
-               INIT_COMPLETION(priv->irq_complete);
+               reinit_completion(&priv->irq_complete);
 
                /* Enable serviceIntr */
                parport_ip32_frob_econtrol(p, ECR_SERVINTR, 0);
@@ -1446,7 +1446,7 @@ static size_t parport_ip32_fifo_write_block_dma(struct parport *p,
        priv->irq_mode = PARPORT_IP32_IRQ_HERE;
 
        parport_ip32_dma_start(DMA_TO_DEVICE, (void *)buf, len);
-       INIT_COMPLETION(priv->irq_complete);
+       reinit_completion(&priv->irq_complete);
        parport_ip32_frob_econtrol(p, ECR_DMAEN | ECR_SERVINTR, ECR_DMAEN);
 
        nfault_timeout = min((unsigned long)physport->cad->timeout,
index 1ce8ee0..a94d850 100644 (file)
@@ -367,7 +367,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
                string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
        }
 
-       handle = DEVICE_ACPI_HANDLE(&pdev->dev);
+       handle = ACPI_HANDLE(&pdev->dev);
        if (!handle) {
                /*
                 * This hotplug controller was not listed in the ACPI name
index 26100f5..1592dbe 100644 (file)
@@ -176,7 +176,6 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot);
 u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot);
 
 /* variables */
-extern bool acpiphp_debug;
 extern bool acpiphp_disabled;
 
 #endif /* _ACPIPHP_H */
index 55ea2a0..eddddd4 100644 (file)
@@ -54,7 +54,7 @@ int pciehp_acpi_slot_detection_check(struct pci_dev *dev)
 {
        if (slot_detection_mode != PCIEHP_DETECT_ACPI)
                return 0;
-       if (acpi_pci_detect_ejectable(DEVICE_ACPI_HANDLE(&dev->dev)))
+       if (acpi_pci_detect_ejectable(ACPI_HANDLE(&dev->dev)))
                return 0;
        return -ENODEV;
 }
@@ -96,7 +96,7 @@ static int __init dummy_probe(struct pcie_device *dev)
                        dup_slot_id++;
        }
        list_add_tail(&slot->list, &dummy_slots);
-       handle = DEVICE_ACPI_HANDLE(&pdev->dev);
+       handle = ACPI_HANDLE(&pdev->dev);
        if (!acpi_slot_detected && acpi_pci_detect_ejectable(handle))
                acpi_slot_detected = 1;
        return -ENODEV;         /* dummy driver always returns error */
index b2781df..5b05a68 100644 (file)
@@ -9,6 +9,7 @@
  * Work to add BIOS PROM support was completed by Mike Habeck.
  */
 
+#include <linux/acpi.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -29,7 +30,6 @@
 #include <asm/sn/sn_feature_sets.h>
 #include <asm/sn/sn_sal.h>
 #include <asm/sn/types.h>
-#include <linux/acpi.h>
 #include <asm/sn/acpi.h>
 
 #include "../pci.h"
@@ -414,7 +414,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
                acpi_handle rethandle;
                acpi_status ret;
 
-               phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle;
+               phandle = acpi_device_handle(PCI_CONTROLLER(slot->pci_bus)->companion);
 
                if (acpi_bus_get_device(phandle, &pdevice)) {
                        dev_dbg(&slot->pci_bus->self->dev,
@@ -495,7 +495,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
 
        /* free the ACPI resources for the slot */
        if (SN_ACPI_BASE_SUPPORT() &&
-            PCI_CONTROLLER(slot->pci_bus)->acpi_handle) {
+            PCI_CONTROLLER(slot->pci_bus)->companion) {
                unsigned long long adr;
                struct acpi_device *device;
                acpi_handle phandle;
@@ -504,7 +504,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
                acpi_status ret;
 
                /* Get the rootbus node pointer */
-               phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle;
+               phandle = acpi_device_handle(PCI_CONTROLLER(slot->pci_bus)->companion);
 
                acpi_scan_lock_acquire();
                /*
index 1b90579..50ce680 100644 (file)
@@ -37,7 +37,7 @@ static int ioapic_probe(struct pci_dev *dev, const struct pci_device_id *ent)
        char *type;
        struct resource *res;
 
-       handle = DEVICE_ACPI_HANDLE(&dev->dev);
+       handle = ACPI_HANDLE(&dev->dev);
        if (!handle)
                return -EINVAL;
 
index 92a49c2..577074e 100644 (file)
@@ -173,14 +173,14 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
 
 static bool acpi_pci_power_manageable(struct pci_dev *dev)
 {
-       acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+       acpi_handle handle = ACPI_HANDLE(&dev->dev);
 
        return handle ? acpi_bus_power_manageable(handle) : false;
 }
 
 static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
 {
-       acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+       acpi_handle handle = ACPI_HANDLE(&dev->dev);
        static const u8 state_conv[] = {
                [PCI_D0] = ACPI_STATE_D0,
                [PCI_D1] = ACPI_STATE_D1,
@@ -217,7 +217,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
 
 static bool acpi_pci_can_wakeup(struct pci_dev *dev)
 {
-       acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+       acpi_handle handle = ACPI_HANDLE(&dev->dev);
 
        return handle ? acpi_bus_can_wakeup(handle) : false;
 }
index edaed6f..d51f45a 100644 (file)
@@ -263,7 +263,7 @@ device_has_dsm(struct device *dev)
        acpi_handle handle;
        struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
 
-       handle = DEVICE_ACPI_HANDLE(dev);
+       handle = ACPI_HANDLE(dev);
 
        if (!handle)
                return FALSE;
@@ -295,7 +295,7 @@ acpilabel_show(struct device *dev, struct device_attribute *attr, char *buf)
        acpi_handle handle;
        int length;
 
-       handle = DEVICE_ACPI_HANDLE(dev);
+       handle = ACPI_HANDLE(dev);
 
        if (!handle)
                return -1;
@@ -316,7 +316,7 @@ acpiindex_show(struct device *dev, struct device_attribute *attr, char *buf)
        acpi_handle handle;
        int length;
 
-       handle = DEVICE_ACPI_HANDLE(dev);
+       handle = ACPI_HANDLE(dev);
 
        if (!handle)
                return -1;
index 688e48e..b2c8881 100644 (file)
@@ -574,7 +574,7 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
        };
 
        spin_lock_irqsave(&aer_recover_ring_lock, flags);
-       if (kfifo_put(&aer_recover_ring, &entry))
+       if (kfifo_put(&aer_recover_ring, entry))
                schedule_work(&aer_recover_work);
        else
                pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n",
index 0846922..829b98c 100644 (file)
@@ -1604,6 +1604,9 @@ static inline void pcs_irq_set(struct pcs_soc_data *pcs_soc,
                pcs->write(mask, pcswi->reg);
                raw_spin_unlock(&pcs->lock);
        }
+
+       if (pcs_soc->rearm)
+               pcs_soc->rearm();
 }
 
 /**
@@ -1626,8 +1629,6 @@ static void pcs_irq_unmask(struct irq_data *d)
        struct pcs_soc_data *pcs_soc = irq_data_get_irq_chip_data(d);
 
        pcs_irq_set(pcs_soc, d->irq, true);
-       if (pcs_soc->rearm)
-               pcs_soc->rearm();
 }
 
 /**
@@ -1678,11 +1679,6 @@ static int pcs_irq_handle(struct pcs_soc_data *pcs_soc)
                }
        }
 
-       /*
-        * For debugging on omaps, you may want to call pcs_soc->rearm()
-        * here to see wake-up interrupts during runtime also.
-        */
-
        return count;
 }
 
index 8eea2ef..b9429fb 100644 (file)
@@ -289,7 +289,7 @@ static int gmux_switchto(enum vga_switcheroo_client_id id)
 static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,
                                   enum vga_switcheroo_state state)
 {
-       INIT_COMPLETION(gmux_data->powerchange_done);
+       reinit_completion(&gmux_data->powerchange_done);
 
        if (state == VGA_SWITCHEROO_ON) {
                gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
@@ -519,7 +519,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
 
        gmux_data->power_state = VGA_SWITCHEROO_ON;
 
-       gmux_data->dhandle = DEVICE_ACPI_HANDLE(&pnp->dev);
+       gmux_data->dhandle = ACPI_HANDLE(&pnp->dev);
        if (!gmux_data->dhandle) {
                pr_err("Cannot find acpi handle for pnp device %s\n",
                       dev_name(&pnp->dev));
index 747826d..14655a0 100644 (file)
@@ -89,7 +89,7 @@ static int pnpacpi_set_resources(struct pnp_dev *dev)
 
        pnp_dbg(&dev->dev, "set resources\n");
 
-       handle = DEVICE_ACPI_HANDLE(&dev->dev);
+       handle = ACPI_HANDLE(&dev->dev);
        if (!handle || acpi_bus_get_device(handle, &acpi_dev)) {
                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
                return -ENODEV;
@@ -122,7 +122,7 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev)
 
        dev_dbg(&dev->dev, "disable resources\n");
 
-       handle = DEVICE_ACPI_HANDLE(&dev->dev);
+       handle = ACPI_HANDLE(&dev->dev);
        if (!handle || acpi_bus_get_device(handle, &acpi_dev)) {
                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
                return 0;
@@ -144,7 +144,7 @@ static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
        struct acpi_device *acpi_dev;
        acpi_handle handle;
 
-       handle = DEVICE_ACPI_HANDLE(&dev->dev);
+       handle = ACPI_HANDLE(&dev->dev);
        if (!handle || acpi_bus_get_device(handle, &acpi_dev)) {
                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
                return false;
@@ -159,7 +159,7 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
        acpi_handle handle;
        int error = 0;
 
-       handle = DEVICE_ACPI_HANDLE(&dev->dev);
+       handle = ACPI_HANDLE(&dev->dev);
        if (!handle || acpi_bus_get_device(handle, &acpi_dev)) {
                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
                return 0;
@@ -194,7 +194,7 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 static int pnpacpi_resume(struct pnp_dev *dev)
 {
        struct acpi_device *acpi_dev;
-       acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+       acpi_handle handle = ACPI_HANDLE(&dev->dev);
        int error = 0;
 
        if (!handle || acpi_bus_get_device(handle, &acpi_dev)) {
index e6f92b4..5e2054a 100644 (file)
@@ -346,6 +346,12 @@ config CHARGER_BQ24190
        help
          Say Y to enable support for the TI BQ24190 battery charger.
 
+config CHARGER_BQ24735
+       tristate "TI BQ24735 battery charger support"
+       depends on I2C && GPIOLIB
+       help
+         Say Y to enable support for the TI BQ24735 battery charger.
+
 config CHARGER_SMB347
        tristate "Summit Microelectronics SMB347 Battery Charger"
        depends on I2C
index a4b7417..372b4e8 100644 (file)
@@ -52,6 +52,7 @@ obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)  += max8998_charger.o
 obj-$(CONFIG_CHARGER_BQ2415X)  += bq2415x_charger.o
 obj-$(CONFIG_CHARGER_BQ24190)  += bq24190_charger.o
+obj-$(CONFIG_CHARGER_BQ24735)  += bq24735-charger.o
 obj-$(CONFIG_POWER_AVS)                += avs/
 obj-$(CONFIG_CHARGER_SMB347)   += smb347-charger.o
 obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
index a4c4a10..19110aa 100644 (file)
@@ -766,7 +766,6 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
                        ret = -ENXIO;
                        break;
                }
-               break;
        case USB_STAT_CARKIT_1:
        case USB_STAT_CARKIT_2:
        case USB_STAT_ACA_DOCK_CHARGER:
@@ -1387,8 +1386,12 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
                 * the GPADC module independant of the AB8500 chargers
                 */
                if (!di->vddadc_en_ac) {
-                       regulator_enable(di->regu);
-                       di->vddadc_en_ac = true;
+                       ret = regulator_enable(di->regu);
+                       if (ret)
+                               dev_warn(di->dev,
+                                       "Failed to enable regulator\n");
+                       else
+                               di->vddadc_en_ac = true;
                }
 
                /* Check if the requested voltage or current is valid */
@@ -1556,8 +1559,12 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
                 * the GPADC module independant of the AB8500 chargers
                 */
                if (!di->vddadc_en_usb) {
-                       regulator_enable(di->regu);
-                       di->vddadc_en_usb = true;
+                       ret = regulator_enable(di->regu);
+                       if (ret)
+                               dev_warn(di->dev,
+                                       "Failed to enable regulator\n");
+                       else
+                               di->vddadc_en_usb = true;
                }
 
                /* Enable USB charging */
index 7549707..3cb4178 100644 (file)
@@ -574,8 +574,8 @@ int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
        }
 
        /* Return and WFI */
-       INIT_COMPLETION(di->ab8500_fg_started);
-       INIT_COMPLETION(di->ab8500_fg_complete);
+       reinit_completion(&di->ab8500_fg_started);
+       reinit_completion(&di->ab8500_fg_complete);
        enable_irq(di->irq);
 
        /* Note: cc_lock is still locked */
index 0727f92..df893dd 100644 (file)
@@ -605,9 +605,13 @@ static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq,
 {
        int val = (mV/10 - 350) / 2;
 
+       /*
+        * According to datasheet, maximum battery regulation voltage is
+        * 4440mV which is b101111 = 47.
+        */
        if (val < 0)
                val = 0;
-       else if (val > 94) /* FIXME: Max is 94 or 122 ? Set max value ? */
+       else if (val > 47)
                return -EINVAL;
 
        return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val,
diff --git a/drivers/power/bq24735-charger.c b/drivers/power/bq24735-charger.c
new file mode 100644 (file)
index 0000000..d022b82
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * Battery charger driver for TI BQ24735
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 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/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+#include <linux/power/bq24735-charger.h>
+
+#define BQ24735_CHG_OPT                        0x12
+#define BQ24735_CHG_OPT_CHARGE_DISABLE (1 << 0)
+#define BQ24735_CHG_OPT_AC_PRESENT     (1 << 4)
+#define BQ24735_CHARGE_CURRENT         0x14
+#define BQ24735_CHARGE_CURRENT_MASK    0x1fc0
+#define BQ24735_CHARGE_VOLTAGE         0x15
+#define BQ24735_CHARGE_VOLTAGE_MASK    0x7ff0
+#define BQ24735_INPUT_CURRENT          0x3f
+#define BQ24735_INPUT_CURRENT_MASK     0x1f80
+#define BQ24735_MANUFACTURER_ID                0xfe
+#define BQ24735_DEVICE_ID              0xff
+
+struct bq24735 {
+       struct power_supply     charger;
+       struct i2c_client       *client;
+       struct bq24735_platform *pdata;
+};
+
+static inline struct bq24735 *to_bq24735(struct power_supply *psy)
+{
+       return container_of(psy, struct bq24735, charger);
+}
+
+static enum power_supply_property bq24735_charger_properties[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static inline int bq24735_write_word(struct i2c_client *client, u8 reg,
+                                    u16 value)
+{
+       return i2c_smbus_write_word_data(client, reg, le16_to_cpu(value));
+}
+
+static inline int bq24735_read_word(struct i2c_client *client, u8 reg)
+{
+       s32 ret = i2c_smbus_read_word_data(client, reg);
+
+       return ret < 0 ? ret : le16_to_cpu(ret);
+}
+
+static int bq24735_update_word(struct i2c_client *client, u8 reg,
+                              u16 mask, u16 value)
+{
+       unsigned int tmp;
+       int ret;
+
+       ret = bq24735_read_word(client, reg);
+       if (ret < 0)
+               return ret;
+
+       tmp = ret & ~mask;
+       tmp |= value & mask;
+
+       return bq24735_write_word(client, reg, tmp);
+}
+
+static inline int bq24735_enable_charging(struct bq24735 *charger)
+{
+       return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
+                                  BQ24735_CHG_OPT_CHARGE_DISABLE,
+                                  ~BQ24735_CHG_OPT_CHARGE_DISABLE);
+}
+
+static inline int bq24735_disable_charging(struct bq24735 *charger)
+{
+       return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
+                                  BQ24735_CHG_OPT_CHARGE_DISABLE,
+                                  BQ24735_CHG_OPT_CHARGE_DISABLE);
+}
+
+static int bq24735_config_charger(struct bq24735 *charger)
+{
+       struct bq24735_platform *pdata = charger->pdata;
+       int ret;
+       u16 value;
+
+       if (pdata->charge_current) {
+               value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK;
+
+               ret = bq24735_write_word(charger->client,
+                                        BQ24735_CHARGE_CURRENT, value);
+               if (ret < 0) {
+                       dev_err(&charger->client->dev,
+                               "Failed to write charger current : %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       if (pdata->charge_voltage) {
+               value = pdata->charge_voltage & BQ24735_CHARGE_VOLTAGE_MASK;
+
+               ret = bq24735_write_word(charger->client,
+                                        BQ24735_CHARGE_VOLTAGE, value);
+               if (ret < 0) {
+                       dev_err(&charger->client->dev,
+                               "Failed to write charger voltage : %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       if (pdata->input_current) {
+               value = pdata->input_current & BQ24735_INPUT_CURRENT_MASK;
+
+               ret = bq24735_write_word(charger->client,
+                                        BQ24735_INPUT_CURRENT, value);
+               if (ret < 0) {
+                       dev_err(&charger->client->dev,
+                               "Failed to write input current : %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static bool bq24735_charger_is_present(struct bq24735 *charger)
+{
+       struct bq24735_platform *pdata = charger->pdata;
+       int ret;
+
+       if (pdata->status_gpio_valid) {
+               ret = gpio_get_value_cansleep(pdata->status_gpio);
+               return ret ^= pdata->status_gpio_active_low == 0;
+       } else {
+               int ac = 0;
+
+               ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
+               if (ac < 0) {
+                       dev_err(&charger->client->dev,
+                               "Failed to read charger options : %d\n",
+                               ac);
+                       return false;
+               }
+               return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false;
+       }
+
+       return false;
+}
+
+static irqreturn_t bq24735_charger_isr(int irq, void *devid)
+{
+       struct power_supply *psy = devid;
+       struct bq24735 *charger = to_bq24735(psy);
+
+       if (bq24735_charger_is_present(charger))
+               bq24735_enable_charging(charger);
+       else
+               bq24735_disable_charging(charger);
+
+       power_supply_changed(psy);
+
+       return IRQ_HANDLED;
+}
+
+static int bq24735_charger_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       struct bq24735 *charger;
+
+       charger = container_of(psy, struct bq24735, charger);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = bq24735_charger_is_present(charger) ? 1 : 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client)
+{
+       struct bq24735_platform *pdata;
+       struct device_node *np = client->dev.of_node;
+       u32 val;
+       int ret;
+       enum of_gpio_flags flags;
+
+       pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata) {
+               dev_err(&client->dev,
+                       "Memory alloc for bq24735 pdata failed\n");
+               return NULL;
+       }
+
+       pdata->status_gpio = of_get_named_gpio_flags(np, "ti,ac-detect-gpios",
+                                                    0, &flags);
+
+       if (flags & OF_GPIO_ACTIVE_LOW)
+               pdata->status_gpio_active_low = 1;
+
+       ret = of_property_read_u32(np, "ti,charge-current", &val);
+       if (!ret)
+               pdata->charge_current = val;
+
+       ret = of_property_read_u32(np, "ti,charge-voltage", &val);
+       if (!ret)
+               pdata->charge_voltage = val;
+
+       ret = of_property_read_u32(np, "ti,input-current", &val);
+       if (!ret)
+               pdata->input_current = val;
+
+       return pdata;
+}
+
+static int bq24735_charger_probe(struct i2c_client *client,
+                                const struct i2c_device_id *id)
+{
+       int ret;
+       struct bq24735 *charger;
+       struct power_supply *supply;
+       char *name;
+
+       charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL);
+       if (!charger)
+               return -ENOMEM;
+
+       charger->pdata = client->dev.platform_data;
+
+       if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node)
+               charger->pdata = bq24735_parse_dt_data(client);
+
+       if (!charger->pdata) {
+               dev_err(&client->dev, "no platform data provided\n");
+               return -EINVAL;
+       }
+
+       name = (char *)charger->pdata->name;
+       if (!name) {
+               name = kasprintf(GFP_KERNEL, "bq24735@%s",
+                                dev_name(&client->dev));
+               if (!name) {
+                       dev_err(&client->dev, "Failed to alloc device name\n");
+                       return -ENOMEM;
+               }
+       }
+
+       charger->client = client;
+
+       supply = &charger->charger;
+
+       supply->name = name;
+       supply->type = POWER_SUPPLY_TYPE_MAINS;
+       supply->properties = bq24735_charger_properties;
+       supply->num_properties = ARRAY_SIZE(bq24735_charger_properties);
+       supply->get_property = bq24735_charger_get_property;
+       supply->supplied_to = charger->pdata->supplied_to;
+       supply->num_supplicants = charger->pdata->num_supplicants;
+       supply->of_node = client->dev.of_node;
+
+       i2c_set_clientdata(client, charger);
+
+       ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
+                       ret);
+               goto err_free_name;
+       } else if (ret != 0x0040) {
+               dev_err(&client->dev,
+                       "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);
+               ret = -ENODEV;
+               goto err_free_name;
+       }
+
+       ret = bq24735_read_word(client, BQ24735_DEVICE_ID);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed to read device id : %d\n", ret);
+               goto err_free_name;
+       } else if (ret != 0x000B) {
+               dev_err(&client->dev,
+                       "device id mismatch. 0x000b != 0x%04x\n", ret);
+               ret = -ENODEV;
+               goto err_free_name;
+       }
+
+       if (gpio_is_valid(charger->pdata->status_gpio)) {
+               ret = devm_gpio_request(&client->dev,
+                                       charger->pdata->status_gpio,
+                                       name);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "Failed GPIO request for GPIO %d: %d\n",
+                               charger->pdata->status_gpio, ret);
+               }
+
+               charger->pdata->status_gpio_valid = !ret;
+       }
+
+       ret = bq24735_config_charger(charger);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed in configuring charger");
+               goto err_free_name;
+       }
+
+       /* check for AC adapter presence */
+       if (bq24735_charger_is_present(charger)) {
+               ret = bq24735_enable_charging(charger);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Failed to enable charging\n");
+                       goto err_free_name;
+               }
+       }
+
+       ret = power_supply_register(&client->dev, supply);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed to register power supply: %d\n",
+                       ret);
+               goto err_free_name;
+       }
+
+       if (client->irq) {
+               ret = devm_request_threaded_irq(&client->dev, client->irq,
+                                               NULL, bq24735_charger_isr,
+                                               IRQF_TRIGGER_RISING |
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_ONESHOT,
+                                               supply->name, supply);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "Unable to register IRQ %d err %d\n",
+                               client->irq, ret);
+                       goto err_unregister_supply;
+               }
+       }
+
+       return 0;
+err_unregister_supply:
+       power_supply_unregister(supply);
+err_free_name:
+       if (name != charger->pdata->name)
+               kfree(name);
+
+       return ret;
+}
+
+static int bq24735_charger_remove(struct i2c_client *client)
+{
+       struct bq24735 *charger = i2c_get_clientdata(client);
+
+       if (charger->client->irq)
+               devm_free_irq(&charger->client->dev, charger->client->irq,
+                             &charger->charger);
+
+       power_supply_unregister(&charger->charger);
+
+       if (charger->charger.name != charger->pdata->name)
+               kfree(charger->charger.name);
+
+       return 0;
+}
+
+static const struct i2c_device_id bq24735_charger_id[] = {
+       { "bq24735-charger", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, bq24735_charger_id);
+
+static const struct of_device_id bq24735_match_ids[] = {
+       { .compatible = "ti,bq24735", },
+       { /* end */ }
+};
+MODULE_DEVICE_TABLE(of, bq24735_match_ids);
+
+static struct i2c_driver bq24735_charger_driver = {
+       .driver = {
+               .name = "bq24735-charger",
+               .owner = THIS_MODULE,
+               .of_match_table = bq24735_match_ids,
+       },
+       .probe = bq24735_charger_probe,
+       .remove = bq24735_charger_remove,
+       .id_table = bq24735_charger_id,
+};
+
+module_i2c_driver(bq24735_charger_driver);
+
+MODULE_DESCRIPTION("bq24735 battery charging driver");
+MODULE_AUTHOR("Darbha Sriharsha <dsriharsha@nvidia.com>");
+MODULE_LICENSE("GPL v2");
index e30e847..7287c0e 100644 (file)
@@ -1378,7 +1378,8 @@ static int charger_manager_register_sysfs(struct charger_manager *cm)
                charger = &desc->charger_regulators[i];
 
                snprintf(buf, 10, "charger.%d", i);
-               str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
+               str = devm_kzalloc(cm->dev,
+                               sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
                if (!str) {
                        ret = -ENOMEM;
                        goto err;
@@ -1452,30 +1453,23 @@ static int charger_manager_probe(struct platform_device *pdev)
                        rtc_dev = NULL;
                        dev_err(&pdev->dev, "Cannot get RTC %s\n",
                                g_desc->rtc_name);
-                       ret = -ENODEV;
-                       goto err_alloc;
+                       return -ENODEV;
                }
        }
 
        if (!desc) {
                dev_err(&pdev->dev, "No platform data (desc) found\n");
-               ret = -ENODEV;
-               goto err_alloc;
+               return -ENODEV;
        }
 
-       cm = kzalloc(sizeof(struct charger_manager), GFP_KERNEL);
-       if (!cm) {
-               ret = -ENOMEM;
-               goto err_alloc;
-       }
+       cm = devm_kzalloc(&pdev->dev,
+                       sizeof(struct charger_manager), GFP_KERNEL);
+       if (!cm)
+               return -ENOMEM;
 
        /* Basic Values. Unspecified are Null or 0 */
        cm->dev = &pdev->dev;
-       cm->desc = kmemdup(desc, sizeof(struct charger_desc), GFP_KERNEL);
-       if (!cm->desc) {
-               ret = -ENOMEM;
-               goto err_alloc_desc;
-       }
+       cm->desc = desc;
        cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
 
        /*
@@ -1498,27 +1492,23 @@ static int charger_manager_probe(struct platform_device *pdev)
        }
 
        if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
-               ret = -EINVAL;
                dev_err(&pdev->dev, "charger_regulators undefined\n");
-               goto err_no_charger;
+               return -EINVAL;
        }
 
        if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
                dev_err(&pdev->dev, "No power supply defined\n");
-               ret = -EINVAL;
-               goto err_no_charger_stat;
+               return -EINVAL;
        }
 
        /* Counting index only */
        while (desc->psy_charger_stat[i])
                i++;
 
-       cm->charger_stat = kzalloc(sizeof(struct power_supply *) * (i + 1),
-                                  GFP_KERNEL);
-       if (!cm->charger_stat) {
-               ret = -ENOMEM;
-               goto err_no_charger_stat;
-       }
+       cm->charger_stat = devm_kzalloc(&pdev->dev,
+                               sizeof(struct power_supply *) * i, GFP_KERNEL);
+       if (!cm->charger_stat)
+               return -ENOMEM;
 
        for (i = 0; desc->psy_charger_stat[i]; i++) {
                cm->charger_stat[i] = power_supply_get_by_name(
@@ -1526,8 +1516,7 @@ static int charger_manager_probe(struct platform_device *pdev)
                if (!cm->charger_stat[i]) {
                        dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
                                desc->psy_charger_stat[i]);
-                       ret = -ENODEV;
-                       goto err_chg_stat;
+                       return -ENODEV;
                }
        }
 
@@ -1535,21 +1524,18 @@ static int charger_manager_probe(struct platform_device *pdev)
        if (!cm->fuel_gauge) {
                dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
                        desc->psy_fuel_gauge);
-               ret = -ENODEV;
-               goto err_chg_stat;
+               return -ENODEV;
        }
 
        if (desc->polling_interval_ms == 0 ||
            msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) {
                dev_err(&pdev->dev, "polling_interval_ms is too small\n");
-               ret = -EINVAL;
-               goto err_chg_stat;
+               return -EINVAL;
        }
 
        if (!desc->temperature_out_of_range) {
                dev_err(&pdev->dev, "there is no temperature_out_of_range\n");
-               ret = -EINVAL;
-               goto err_chg_stat;
+               return -EINVAL;
        }
 
        if (!desc->charging_max_duration_ms ||
@@ -1570,14 +1556,13 @@ static int charger_manager_probe(struct platform_device *pdev)
        cm->charger_psy.name = cm->psy_name_buf;
 
        /* Allocate for psy properties because they may vary */
-       cm->charger_psy.properties = kzalloc(sizeof(enum power_supply_property)
+       cm->charger_psy.properties = devm_kzalloc(&pdev->dev,
+                               sizeof(enum power_supply_property)
                                * (ARRAY_SIZE(default_charger_props) +
-                               NUM_CHARGER_PSY_OPTIONAL),
-                               GFP_KERNEL);
-       if (!cm->charger_psy.properties) {
-               ret = -ENOMEM;
-               goto err_chg_stat;
-       }
+                               NUM_CHARGER_PSY_OPTIONAL), GFP_KERNEL);
+       if (!cm->charger_psy.properties)
+               return -ENOMEM;
+
        memcpy(cm->charger_psy.properties, default_charger_props,
                sizeof(enum power_supply_property) *
                ARRAY_SIZE(default_charger_props));
@@ -1614,7 +1599,7 @@ static int charger_manager_probe(struct platform_device *pdev)
        if (ret) {
                dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
                        cm->charger_psy.name);
-               goto err_register;
+               return ret;
        }
 
        /* Register extcon device for charger cable */
@@ -1655,8 +1640,6 @@ err_reg_sysfs:
                charger = &desc->charger_regulators[i];
                sysfs_remove_group(&cm->charger_psy.dev->kobj,
                                &charger->attr_g);
-
-               kfree(charger->attr_g.name);
        }
 err_reg_extcon:
        for (i = 0; i < desc->num_charger_regulators; i++) {
@@ -1674,16 +1657,7 @@ err_reg_extcon:
        }
 
        power_supply_unregister(&cm->charger_psy);
-err_register:
-       kfree(cm->charger_psy.properties);
-err_chg_stat:
-       kfree(cm->charger_stat);
-err_no_charger_stat:
-err_no_charger:
-       kfree(cm->desc);
-err_alloc_desc:
-       kfree(cm);
-err_alloc:
+
        return ret;
 }
 
@@ -1718,11 +1692,6 @@ static int charger_manager_remove(struct platform_device *pdev)
 
        try_charger_enable(cm, false);
 
-       kfree(cm->charger_psy.properties);
-       kfree(cm->charger_stat);
-       kfree(cm->desc);
-       kfree(cm);
-
        return 0;
 }
 
index fc04d19..1bb3a91 100644 (file)
@@ -2,6 +2,7 @@
  * ISP1704 USB Charger Detection driver
  *
  * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2012 - 2013 Pali Rohár <pali.rohar@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
@@ -65,10 +66,6 @@ struct isp1704_charger {
        unsigned                present:1;
        unsigned                online:1;
        unsigned                current_max;
-
-       /* temp storage variables */
-       unsigned long           event;
-       unsigned                max_power;
 };
 
 static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
@@ -231,56 +228,59 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp)
        return ret;
 }
 
+static inline int isp1704_charger_detect_dcp(struct isp1704_charger *isp)
+{
+       if (isp1704_charger_detect(isp) &&
+                       isp1704_charger_type(isp) == POWER_SUPPLY_TYPE_USB_DCP)
+               return true;
+       else
+               return false;
+}
+
 static void isp1704_charger_work(struct work_struct *data)
 {
-       int                     detect;
-       unsigned long           event;
-       unsigned                power;
        struct isp1704_charger  *isp =
                container_of(data, struct isp1704_charger, work);
        static DEFINE_MUTEX(lock);
 
-       event = isp->event;
-       power = isp->max_power;
-
        mutex_lock(&lock);
 
-       if (event != USB_EVENT_NONE)
-               isp1704_charger_set_power(isp, 1);
-
-       switch (event) {
+       switch (isp->phy->last_event) {
        case USB_EVENT_VBUS:
-               isp->online = true;
+               /* do not call wall charger detection more times */
+               if (!isp->present) {
+                       isp->online = true;
+                       isp->present = 1;
+                       isp1704_charger_set_power(isp, 1);
+
+                       /* detect wall charger */
+                       if (isp1704_charger_detect_dcp(isp)) {
+                               isp->psy.type = POWER_SUPPLY_TYPE_USB_DCP;
+                               isp->current_max = 1800;
+                       } else {
+                               isp->psy.type = POWER_SUPPLY_TYPE_USB;
+                               isp->current_max = 500;
+                       }
 
-               /* detect charger */
-               detect = isp1704_charger_detect(isp);
-
-               if (detect) {
-                       isp->present = detect;
-                       isp->psy.type = isp1704_charger_type(isp);
+                       /* enable data pullups */
+                       if (isp->phy->otg->gadget)
+                               usb_gadget_connect(isp->phy->otg->gadget);
                }
 
-               switch (isp->psy.type) {
-               case POWER_SUPPLY_TYPE_USB_DCP:
-                       isp->current_max = 1800;
-                       break;
-               case POWER_SUPPLY_TYPE_USB_CDP:
+               if (isp->psy.type != POWER_SUPPLY_TYPE_USB_DCP) {
                        /*
                         * Only 500mA here or high speed chirp
                         * handshaking may break
                         */
-                       isp->current_max = 500;
-                       /* FALLTHROUGH */
-               case POWER_SUPPLY_TYPE_USB:
-               default:
-                       /* enable data pullups */
-                       if (isp->phy->otg->gadget)
-                               usb_gadget_connect(isp->phy->otg->gadget);
+                       if (isp->current_max > 500)
+                               isp->current_max = 500;
+
+                       if (isp->current_max > 100)
+                               isp->psy.type = POWER_SUPPLY_TYPE_USB_CDP;
                }
                break;
        case USB_EVENT_NONE:
                isp->online = false;
-               isp->current_max = 0;
                isp->present = 0;
                isp->current_max = 0;
                isp->psy.type = POWER_SUPPLY_TYPE_USB;
@@ -298,12 +298,6 @@ static void isp1704_charger_work(struct work_struct *data)
 
                isp1704_charger_set_power(isp, 0);
                break;
-       case USB_EVENT_ENUMERATED:
-               if (isp->present)
-                       isp->current_max = 1800;
-               else
-                       isp->current_max = power;
-               break;
        default:
                goto out;
        }
@@ -314,16 +308,11 @@ out:
 }
 
 static int isp1704_notifier_call(struct notifier_block *nb,
-               unsigned long event, void *power)
+               unsigned long val, void *v)
 {
        struct isp1704_charger *isp =
                container_of(nb, struct isp1704_charger, nb);
 
-       isp->event = event;
-
-       if (power)
-               isp->max_power = *((unsigned *)power);
-
        schedule_work(&isp->work);
 
        return NOTIFY_OK;
@@ -462,13 +451,13 @@ static int isp1704_charger_probe(struct platform_device *pdev)
        if (isp->phy->otg->gadget)
                usb_gadget_disconnect(isp->phy->otg->gadget);
 
+       if (isp->phy->last_event == USB_EVENT_NONE)
+               isp1704_charger_set_power(isp, 0);
+
        /* Detect charger if VBUS is valid (the cable was already plugged). */
-       ret = isp1704_read(isp, ULPI_USB_INT_STS);
-       isp1704_charger_set_power(isp, 0);
-       if ((ret & ULPI_INT_VBUS_VALID) && !isp->phy->otg->default_a) {
-               isp->event = USB_EVENT_VBUS;
+       if (isp->phy->last_event == USB_EVENT_VBUS &&
+                       !isp->phy->otg->default_a)
                schedule_work(&isp->work);
-       }
 
        return 0;
 fail2:
index d9686aa..6c8931d 100644 (file)
@@ -73,7 +73,7 @@ static long jz_battery_read_voltage(struct jz_battery *battery)
 
        mutex_lock(&battery->lock);
 
-       INIT_COMPLETION(battery->read_completion);
+       reinit_completion(&battery->read_completion);
 
        enable_irq(battery->irq);
        battery->cell->enable(battery->pdev);
index d664ef5..e0b22f9 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/power_supply.h>
 #include <linux/power/max17042_battery.h>
 #include <linux/of.h>
+#include <linux/regmap.h>
 
 /* Status register bits */
 #define STATUS_POR_BIT         (1 << 1)
@@ -67,6 +68,7 @@
 
 struct max17042_chip {
        struct i2c_client *client;
+       struct regmap *regmap;
        struct power_supply battery;
        enum max170xx_chip_type chip_type;
        struct max17042_platform_data *pdata;
@@ -74,35 +76,6 @@ struct max17042_chip {
        int    init_complete;
 };
 
-static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value)
-{
-       int ret = i2c_smbus_write_word_data(client, reg, value);
-
-       if (ret < 0)
-               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
-
-       return ret;
-}
-
-static int max17042_read_reg(struct i2c_client *client, u8 reg)
-{
-       int ret = i2c_smbus_read_word_data(client, reg);
-
-       if (ret < 0)
-               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
-
-       return ret;
-}
-
-static void max17042_set_reg(struct i2c_client *client,
-                            struct max17042_reg_data *data, int size)
-{
-       int i;
-
-       for (i = 0; i < size; i++)
-               max17042_write_reg(client, data[i].addr, data[i].data);
-}
-
 static enum power_supply_property max17042_battery_props[] = {
        POWER_SUPPLY_PROP_PRESENT,
        POWER_SUPPLY_PROP_CYCLE_COUNT,
@@ -125,96 +98,98 @@ static int max17042_get_property(struct power_supply *psy,
 {
        struct max17042_chip *chip = container_of(psy,
                                struct max17042_chip, battery);
+       struct regmap *map = chip->regmap;
        int ret;
+       u32 data;
 
        if (!chip->init_complete)
                return -EAGAIN;
 
        switch (psp) {
        case POWER_SUPPLY_PROP_PRESENT:
-               ret = max17042_read_reg(chip->client, MAX17042_STATUS);
+               ret = regmap_read(map, MAX17042_STATUS, &data);
                if (ret < 0)
                        return ret;
 
-               if (ret & MAX17042_STATUS_BattAbsent)
+               if (data & MAX17042_STATUS_BattAbsent)
                        val->intval = 0;
                else
                        val->intval = 1;
                break;
        case POWER_SUPPLY_PROP_CYCLE_COUNT:
-               ret = max17042_read_reg(chip->client, MAX17042_Cycles);
+               ret = regmap_read(map, MAX17042_Cycles, &data);
                if (ret < 0)
                        return ret;
 
-               val->intval = ret;
+               val->intval = data;
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-               ret = max17042_read_reg(chip->client, MAX17042_MinMaxVolt);
+               ret = regmap_read(map, MAX17042_MinMaxVolt, &data);
                if (ret < 0)
                        return ret;
 
-               val->intval = ret >> 8;
+               val->intval = data >> 8;
                val->intval *= 20000; /* Units of LSB = 20mV */
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
                if (chip->chip_type == MAX17042)
-                       ret = max17042_read_reg(chip->client, MAX17042_V_empty);
+                       ret = regmap_read(map, MAX17042_V_empty, &data);
                else
-                       ret = max17042_read_reg(chip->client, MAX17047_V_empty);
+                       ret = regmap_read(map, MAX17047_V_empty, &data);
                if (ret < 0)
                        return ret;
 
-               val->intval = ret >> 7;
+               val->intval = data >> 7;
                val->intval *= 10000; /* Units of LSB = 10mV */
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = max17042_read_reg(chip->client, MAX17042_VCELL);
+               ret = regmap_read(map, MAX17042_VCELL, &data);
                if (ret < 0)
                        return ret;
 
-               val->intval = ret * 625 / 8;
+               val->intval = data * 625 / 8;
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               ret = max17042_read_reg(chip->client, MAX17042_AvgVCELL);
+               ret = regmap_read(map, MAX17042_AvgVCELL, &data);
                if (ret < 0)
                        return ret;
 
-               val->intval = ret * 625 / 8;
+               val->intval = data * 625 / 8;
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_OCV:
-               ret = max17042_read_reg(chip->client, MAX17042_OCVInternal);
+               ret = regmap_read(map, MAX17042_OCVInternal, &data);
                if (ret < 0)
                        return ret;
 
-               val->intval = ret * 625 / 8;
+               val->intval = data * 625 / 8;
                break;
        case POWER_SUPPLY_PROP_CAPACITY:
-               ret = max17042_read_reg(chip->client, MAX17042_RepSOC);
+               ret = regmap_read(map, MAX17042_RepSOC, &data);
                if (ret < 0)
                        return ret;
 
-               val->intval = ret >> 8;
+               val->intval = data >> 8;
                break;
        case POWER_SUPPLY_PROP_CHARGE_FULL:
-               ret = max17042_read_reg(chip->client, MAX17042_FullCAP);
+               ret = regmap_read(map, MAX17042_FullCAP, &data);
                if (ret < 0)
                        return ret;
 
-               val->intval = ret * 1000 / 2;
+               val->intval = data * 1000 / 2;
                break;
        case POWER_SUPPLY_PROP_CHARGE_COUNTER:
-               ret = max17042_read_reg(chip->client, MAX17042_QH);
+               ret = regmap_read(map, MAX17042_QH, &data);
                if (ret < 0)
                        return ret;
 
-               val->intval = ret * 1000 / 2;
+               val->intval = data * 1000 / 2;
                break;
        case POWER_SUPPLY_PROP_TEMP:
-               ret = max17042_read_reg(chip->client, MAX17042_TEMP);
+               ret = regmap_read(map, MAX17042_TEMP, &data);
                if (ret < 0)
                        return ret;
 
-               val->intval = ret;
+               val->intval = data;
                /* The value is signed. */
                if (val->intval & 0x8000) {
                        val->intval = (0x7fff & ~val->intval) + 1;
@@ -226,11 +201,11 @@ static int max17042_get_property(struct power_supply *psy,
                break;
        case POWER_SUPPLY_PROP_CURRENT_NOW:
                if (chip->pdata->enable_current_sense) {
-                       ret = max17042_read_reg(chip->client, MAX17042_Current);
+                       ret = regmap_read(map, MAX17042_Current, &data);
                        if (ret < 0)
                                return ret;
 
-                       val->intval = ret;
+                       val->intval = data;
                        if (val->intval & 0x8000) {
                                /* Negative */
                                val->intval = ~val->intval & 0x7fff;
@@ -244,12 +219,11 @@ static int max17042_get_property(struct power_supply *psy,
                break;
        case POWER_SUPPLY_PROP_CURRENT_AVG:
                if (chip->pdata->enable_current_sense) {
-                       ret = max17042_read_reg(chip->client,
-                                               MAX17042_AvgCurrent);
+                       ret = regmap_read(map, MAX17042_AvgCurrent, &data);
                        if (ret < 0)
                                return ret;
 
-                       val->intval = ret;
+                       val->intval = data;
                        if (val->intval & 0x8000) {
                                /* Negative */
                                val->intval = ~val->intval & 0x7fff;
@@ -267,16 +241,15 @@ static int max17042_get_property(struct power_supply *psy,
        return 0;
 }
 
-static int max17042_write_verify_reg(struct i2c_client *client,
-                               u8 reg, u16 value)
+static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value)
 {
        int retries = 8;
        int ret;
-       u16 read_value;
+       u32 read_value;
 
        do {
-               ret = i2c_smbus_write_word_data(client, reg, value);
-               read_value =  max17042_read_reg(client, reg);
+               ret = regmap_write(map, reg, value);
+               regmap_read(map, reg, &read_value);
                if (read_value != value) {
                        ret = -EIO;
                        retries--;
@@ -284,50 +257,51 @@ static int max17042_write_verify_reg(struct i2c_client *client,
        } while (retries && read_value != value);
 
        if (ret < 0)
-               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+               pr_err("%s: err %d\n", __func__, ret);
 
        return ret;
 }
 
-static inline void max17042_override_por(
-       struct i2c_client *client, u8 reg, u16 value)
+static inline void max17042_override_por(struct regmap *map,
+                                        u8 reg, u16 value)
 {
        if (value)
-               max17042_write_reg(client, reg, value);
+               regmap_write(map, reg, value);
 }
 
 static inline void max10742_unlock_model(struct max17042_chip *chip)
 {
-       struct i2c_client *client = chip->client;
-       max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
-       max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
+       struct regmap *map = chip->regmap;
+       regmap_write(map, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
+       regmap_write(map, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
 }
 
 static inline void max10742_lock_model(struct max17042_chip *chip)
 {
-       struct i2c_client *client = chip->client;
-       max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_LOCK1);
-       max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_LOCK2);
+       struct regmap *map = chip->regmap;
+
+       regmap_write(map, MAX17042_MLOCKReg1, MODEL_LOCK1);
+       regmap_write(map, MAX17042_MLOCKReg2, MODEL_LOCK2);
 }
 
 static inline void max17042_write_model_data(struct max17042_chip *chip,
                                        u8 addr, int size)
 {
-       struct i2c_client *client = chip->client;
+       struct regmap *map = chip->regmap;
        int i;
        for (i = 0; i < size; i++)
-               max17042_write_reg(client, addr + i,
-                               chip->pdata->config_data->cell_char_tbl[i]);
+               regmap_write(map, addr + i,
+                       chip->pdata->config_data->cell_char_tbl[i]);
 }
 
 static inline void max17042_read_model_data(struct max17042_chip *chip,
-                                       u8 addr, u16 *data, int size)
+                                       u8 addr, u32 *data, int size)
 {
-       struct i2c_client *client = chip->client;
+       struct regmap *map = chip->regmap;
        int i;
 
        for (i = 0; i < size; i++)
-               data[i] = max17042_read_reg(client, addr + i);
+               regmap_read(map, addr + i, &data[i]);
 }
 
 static inline int max17042_model_data_compare(struct max17042_chip *chip,
@@ -350,7 +324,7 @@ static int max17042_init_model(struct max17042_chip *chip)
 {
        int ret;
        int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
-       u16 *temp_data;
+       u32 *temp_data;
 
        temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
        if (!temp_data)
@@ -365,7 +339,7 @@ static int max17042_init_model(struct max17042_chip *chip)
        ret = max17042_model_data_compare(
                chip,
                chip->pdata->config_data->cell_char_tbl,
-               temp_data,
+               (u16 *)temp_data,
                table_size);
 
        max10742_lock_model(chip);
@@ -378,7 +352,7 @@ static int max17042_verify_model_lock(struct max17042_chip *chip)
 {
        int i;
        int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
-       u16 *temp_data;
+       u32 *temp_data;
        int ret = 0;
 
        temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
@@ -398,40 +372,38 @@ static int max17042_verify_model_lock(struct max17042_chip *chip)
 static void max17042_write_config_regs(struct max17042_chip *chip)
 {
        struct max17042_config_data *config = chip->pdata->config_data;
+       struct regmap *map = chip->regmap;
 
-       max17042_write_reg(chip->client, MAX17042_CONFIG, config->config);
-       max17042_write_reg(chip->client, MAX17042_LearnCFG, config->learn_cfg);
-       max17042_write_reg(chip->client, MAX17042_FilterCFG,
+       regmap_write(map, MAX17042_CONFIG, config->config);
+       regmap_write(map, MAX17042_LearnCFG, config->learn_cfg);
+       regmap_write(map, MAX17042_FilterCFG,
                        config->filter_cfg);
-       max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg);
+       regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg);
        if (chip->chip_type == MAX17047)
-               max17042_write_reg(chip->client, MAX17047_FullSOCThr,
+               regmap_write(map, MAX17047_FullSOCThr,
                                                config->full_soc_thresh);
 }
 
 static void  max17042_write_custom_regs(struct max17042_chip *chip)
 {
        struct max17042_config_data *config = chip->pdata->config_data;
+       struct regmap *map = chip->regmap;
 
-       max17042_write_verify_reg(chip->client, MAX17042_RCOMP0,
-                               config->rcomp0);
-       max17042_write_verify_reg(chip->client, MAX17042_TempCo,
-                               config->tcompc0);
-       max17042_write_verify_reg(chip->client, MAX17042_ICHGTerm,
-                               config->ichgt_term);
+       max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0);
+       max17042_write_verify_reg(map, MAX17042_TempCo, config->tcompc0);
+       max17042_write_verify_reg(map, MAX17042_ICHGTerm, config->ichgt_term);
        if (chip->chip_type == MAX17042) {
-               max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
-                                       config->empty_tempco);
-               max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
+               regmap_write(map, MAX17042_EmptyTempCo, config->empty_tempco);
+               max17042_write_verify_reg(map, MAX17042_K_empty0,
                                        config->kempty0);
        } else {
-               max17042_write_verify_reg(chip->client, MAX17047_QRTbl00,
+               max17042_write_verify_reg(map, MAX17047_QRTbl00,
                                                config->qrtbl00);
-               max17042_write_verify_reg(chip->client, MAX17047_QRTbl10,
+               max17042_write_verify_reg(map, MAX17047_QRTbl10,
                                                config->qrtbl10);
-               max17042_write_verify_reg(chip->client, MAX17047_QRTbl20,
+               max17042_write_verify_reg(map, MAX17047_QRTbl20,
                                                config->qrtbl20);
-               max17042_write_verify_reg(chip->client, MAX17047_QRTbl30,
+               max17042_write_verify_reg(map, MAX17047_QRTbl30,
                                                config->qrtbl30);
        }
 }
@@ -439,58 +411,60 @@ static void  max17042_write_custom_regs(struct max17042_chip *chip)
 static void max17042_update_capacity_regs(struct max17042_chip *chip)
 {
        struct max17042_config_data *config = chip->pdata->config_data;
+       struct regmap *map = chip->regmap;
 
-       max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
+       max17042_write_verify_reg(map, MAX17042_FullCAP,
                                config->fullcap);
-       max17042_write_reg(chip->client, MAX17042_DesignCap,
-                       config->design_cap);
-       max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
+       regmap_write(map, MAX17042_DesignCap, config->design_cap);
+       max17042_write_verify_reg(map, MAX17042_FullCAPNom,
                                config->fullcapnom);
 }
 
 static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip)
 {
-       u16 vfSoc;
+       unsigned int vfSoc;
+       struct regmap *map = chip->regmap;
 
-       vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
-       max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
-       max17042_write_verify_reg(chip->client, MAX17042_VFSOC0, vfSoc);
-       max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
+       regmap_read(map, MAX17042_VFSOC, &vfSoc);
+       regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
+       max17042_write_verify_reg(map, MAX17042_VFSOC0, vfSoc);
+       regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
 }
 
 static void max17042_load_new_capacity_params(struct max17042_chip *chip)
 {
-       u16 full_cap0, rep_cap, dq_acc, vfSoc;
+       u32 full_cap0, rep_cap, dq_acc, vfSoc;
        u32 rem_cap;
 
        struct max17042_config_data *config = chip->pdata->config_data;
+       struct regmap *map = chip->regmap;
 
-       full_cap0 = max17042_read_reg(chip->client, MAX17042_FullCAP0);
-       vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
+       regmap_read(map, MAX17042_FullCAP0, &full_cap0);
+       regmap_read(map, MAX17042_VFSOC, &vfSoc);
 
        /* fg_vfSoc needs to shifted by 8 bits to get the
         * perc in 1% accuracy, to get the right rem_cap multiply
         * full_cap0, fg_vfSoc and devide by 100
         */
        rem_cap = ((vfSoc >> 8) * full_cap0) / 100;
-       max17042_write_verify_reg(chip->client, MAX17042_RemCap, (u16)rem_cap);
+       max17042_write_verify_reg(map, MAX17042_RemCap, rem_cap);
 
-       rep_cap = (u16)rem_cap;
-       max17042_write_verify_reg(chip->client, MAX17042_RepCap, rep_cap);
+       rep_cap = rem_cap;
+       max17042_write_verify_reg(map, MAX17042_RepCap, rep_cap);
 
        /* Write dQ_acc to 200% of Capacity and dP_acc to 200% */
        dq_acc = config->fullcap / dQ_ACC_DIV;
-       max17042_write_verify_reg(chip->client, MAX17042_dQacc, dq_acc);
-       max17042_write_verify_reg(chip->client, MAX17042_dPacc, dP_ACC_200);
+       max17042_write_verify_reg(map, MAX17042_dQacc, dq_acc);
+       max17042_write_verify_reg(map, MAX17042_dPacc, dP_ACC_200);
 
-       max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
+       max17042_write_verify_reg(map, MAX17042_FullCAP,
                        config->fullcap);
-       max17042_write_reg(chip->client, MAX17042_DesignCap,
+       regmap_write(map, MAX17042_DesignCap,
                        config->design_cap);
-       max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
+       max17042_write_verify_reg(map, MAX17042_FullCAPNom,
                        config->fullcapnom);
        /* Update SOC register with new SOC */
-       max17042_write_reg(chip->client, MAX17042_RepSOC, vfSoc);
+       regmap_write(map, MAX17042_RepSOC, vfSoc);
 }
 
 /*
@@ -500,59 +474,60 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip)
  */
 static inline void max17042_override_por_values(struct max17042_chip *chip)
 {
-       struct i2c_client *client = chip->client;
+       struct regmap *map = chip->regmap;
        struct max17042_config_data *config = chip->pdata->config_data;
 
-       max17042_override_por(client, MAX17042_TGAIN, config->tgain);
-       max17042_override_por(client, MAx17042_TOFF, config->toff);
-       max17042_override_por(client, MAX17042_CGAIN, config->cgain);
-       max17042_override_por(client, MAX17042_COFF, config->coff);
-
-       max17042_override_por(client, MAX17042_VALRT_Th, config->valrt_thresh);
-       max17042_override_por(client, MAX17042_TALRT_Th, config->talrt_thresh);
-       max17042_override_por(client, MAX17042_SALRT_Th,
-                       config->soc_alrt_thresh);
-       max17042_override_por(client, MAX17042_CONFIG, config->config);
-       max17042_override_por(client, MAX17042_SHDNTIMER, config->shdntimer);
-
-       max17042_override_por(client, MAX17042_DesignCap, config->design_cap);
-       max17042_override_por(client, MAX17042_ICHGTerm, config->ichgt_term);
-
-       max17042_override_por(client, MAX17042_AtRate, config->at_rate);
-       max17042_override_por(client, MAX17042_LearnCFG, config->learn_cfg);
-       max17042_override_por(client, MAX17042_FilterCFG, config->filter_cfg);
-       max17042_override_por(client, MAX17042_RelaxCFG, config->relax_cfg);
-       max17042_override_por(client, MAX17042_MiscCFG, config->misc_cfg);
-       max17042_override_por(client, MAX17042_MaskSOC, config->masksoc);
-
-       max17042_override_por(client, MAX17042_FullCAP, config->fullcap);
-       max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom);
+       max17042_override_por(map, MAX17042_TGAIN, config->tgain);
+       max17042_override_por(map, MAx17042_TOFF, config->toff);
+       max17042_override_por(map, MAX17042_CGAIN, config->cgain);
+       max17042_override_por(map, MAX17042_COFF, config->coff);
+
+       max17042_override_por(map, MAX17042_VALRT_Th, config->valrt_thresh);
+       max17042_override_por(map, MAX17042_TALRT_Th, config->talrt_thresh);
+       max17042_override_por(map, MAX17042_SALRT_Th,
+                                               config->soc_alrt_thresh);
+       max17042_override_por(map, MAX17042_CONFIG, config->config);
+       max17042_override_por(map, MAX17042_SHDNTIMER, config->shdntimer);
+
+       max17042_override_por(map, MAX17042_DesignCap, config->design_cap);
+       max17042_override_por(map, MAX17042_ICHGTerm, config->ichgt_term);
+
+       max17042_override_por(map, MAX17042_AtRate, config->at_rate);
+       max17042_override_por(map, MAX17042_LearnCFG, config->learn_cfg);
+       max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg);
+       max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg);
+       max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg);
+       max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
+
+       max17042_override_por(map, MAX17042_FullCAP, config->fullcap);
+       max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom);
        if (chip->chip_type == MAX17042)
-               max17042_override_por(client, MAX17042_SOC_empty,
+               max17042_override_por(map, MAX17042_SOC_empty,
                                                config->socempty);
-       max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty);
-       max17042_override_por(client, MAX17042_dQacc, config->dqacc);
-       max17042_override_por(client, MAX17042_dPacc, config->dpacc);
+       max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
+       max17042_override_por(map, MAX17042_dQacc, config->dqacc);
+       max17042_override_por(map, MAX17042_dPacc, config->dpacc);
 
        if (chip->chip_type == MAX17042)
-               max17042_override_por(client, MAX17042_V_empty, config->vempty);
+               max17042_override_por(map, MAX17042_V_empty, config->vempty);
        else
-               max17042_override_por(client, MAX17047_V_empty, config->vempty);
-       max17042_override_por(client, MAX17042_TempNom, config->temp_nom);
-       max17042_override_por(client, MAX17042_TempLim, config->temp_lim);
-       max17042_override_por(client, MAX17042_FCTC, config->fctc);
-       max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0);
-       max17042_override_por(client, MAX17042_TempCo, config->tcompc0);
+               max17042_override_por(map, MAX17047_V_empty, config->vempty);
+       max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
+       max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
+       max17042_override_por(map, MAX17042_FCTC, config->fctc);
+       max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0);
+       max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
        if (chip->chip_type) {
-               max17042_override_por(client, MAX17042_EmptyTempCo,
-                                       config->empty_tempco);
-               max17042_override_por(client, MAX17042_K_empty0,
-                                       config->kempty0);
+               max17042_override_por(map, MAX17042_EmptyTempCo,
+                                               config->empty_tempco);
+               max17042_override_por(map, MAX17042_K_empty0,
+                                               config->kempty0);
        }
 }
 
 static int max17042_init_chip(struct max17042_chip *chip)
 {
+       struct regmap *map = chip->regmap;
        int ret;
        int val;
 
@@ -597,31 +572,32 @@ static int max17042_init_chip(struct max17042_chip *chip)
        max17042_load_new_capacity_params(chip);
 
        /* Init complete, Clear the POR bit */
-       val = max17042_read_reg(chip->client, MAX17042_STATUS);
-       max17042_write_reg(chip->client, MAX17042_STATUS,
-                       val & (~STATUS_POR_BIT));
+       regmap_read(map, MAX17042_STATUS, &val);
+       regmap_write(map, MAX17042_STATUS, val & (~STATUS_POR_BIT));
        return 0;
 }
 
 static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
 {
-       u16 soc, soc_tr;
+       struct regmap *map = chip->regmap;
+       u32 soc, soc_tr;
 
        /* program interrupt thesholds such that we should
         * get interrupt for every 'off' perc change in the soc
         */
-       soc = max17042_read_reg(chip->client, MAX17042_RepSOC) >> 8;
+       regmap_read(map, MAX17042_RepSOC, &soc);
+       soc >>= 8;
        soc_tr = (soc + off) << 8;
        soc_tr |= (soc - off);
-       max17042_write_reg(chip->client, MAX17042_SALRT_Th, soc_tr);
+       regmap_write(map, MAX17042_SALRT_Th, soc_tr);
 }
 
 static irqreturn_t max17042_thread_handler(int id, void *dev)
 {
        struct max17042_chip *chip = dev;
-       u16 val;
+       u32 val;
 
-       val = max17042_read_reg(chip->client, MAX17042_STATUS);
+       regmap_read(chip->regmap, MAX17042_STATUS, &val);
        if ((val & STATUS_INTR_SOCMIN_BIT) ||
                (val & STATUS_INTR_SOCMAX_BIT)) {
                dev_info(&chip->client->dev, "SOC threshold INTR\n");
@@ -682,13 +658,20 @@ max17042_get_pdata(struct device *dev)
 }
 #endif
 
+static struct regmap_config max17042_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 16,
+       .val_format_endian = REGMAP_ENDIAN_NATIVE,
+};
+
 static int max17042_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
        struct max17042_chip *chip;
        int ret;
-       int reg;
+       int i;
+       u32 val;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
                return -EIO;
@@ -698,6 +681,12 @@ static int max17042_probe(struct i2c_client *client,
                return -ENOMEM;
 
        chip->client = client;
+       chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config);
+       if (IS_ERR(chip->regmap)) {
+               dev_err(&client->dev, "Failed to initialize regmap\n");
+               return -EINVAL;
+       }
+
        chip->pdata = max17042_get_pdata(&client->dev);
        if (!chip->pdata) {
                dev_err(&client->dev, "no platform data provided\n");
@@ -706,15 +695,15 @@ static int max17042_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, chip);
 
-       ret = max17042_read_reg(chip->client, MAX17042_DevName);
-       if (ret == MAX17042_IC_VERSION) {
+       regmap_read(chip->regmap, MAX17042_DevName, &val);
+       if (val == MAX17042_IC_VERSION) {
                dev_dbg(&client->dev, "chip type max17042 detected\n");
                chip->chip_type = MAX17042;
-       } else if (ret == MAX17047_IC_VERSION) {
+       } else if (val == MAX17047_IC_VERSION) {
                dev_dbg(&client->dev, "chip type max17047/50 detected\n");
                chip->chip_type = MAX17047;
        } else {
-               dev_err(&client->dev, "device version mismatch: %x\n", ret);
+               dev_err(&client->dev, "device version mismatch: %x\n", val);
                return -EIO;
        }
 
@@ -733,13 +722,15 @@ static int max17042_probe(struct i2c_client *client,
                chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
 
        if (chip->pdata->init_data)
-               max17042_set_reg(client, chip->pdata->init_data,
-                               chip->pdata->num_init_data);
+               for (i = 0; i < chip->pdata->num_init_data; i++)
+                       regmap_write(chip->regmap,
+                                       chip->pdata->init_data[i].addr,
+                                       chip->pdata->init_data[i].data);
 
        if (!chip->pdata->enable_current_sense) {
-               max17042_write_reg(client, MAX17042_CGAIN, 0x0000);
-               max17042_write_reg(client, MAX17042_MiscCFG, 0x0003);
-               max17042_write_reg(client, MAX17042_LearnCFG, 0x0007);
+               regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000);
+               regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003);
+               regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
        }
 
        ret = power_supply_register(&client->dev, &chip->battery);
@@ -754,9 +745,9 @@ static int max17042_probe(struct i2c_client *client,
                                                IRQF_TRIGGER_FALLING,
                                                chip->battery.name, chip);
                if (!ret) {
-                       reg =  max17042_read_reg(client, MAX17042_CONFIG);
-                       reg |= CONFIG_ALRT_BIT_ENBL;
-                       max17042_write_reg(client, MAX17042_CONFIG, reg);
+                       regmap_read(chip->regmap, MAX17042_CONFIG, &val);
+                       val |= CONFIG_ALRT_BIT_ENBL;
+                       regmap_write(chip->regmap, MAX17042_CONFIG, val);
                        max17042_set_soc_threshold(chip, 1);
                } else {
                        client->irq = 0;
@@ -765,8 +756,8 @@ static int max17042_probe(struct i2c_client *client,
                }
        }
 
-       reg = max17042_read_reg(chip->client, MAX17042_STATUS);
-       if (reg & STATUS_POR_BIT) {
+       regmap_read(chip->regmap, MAX17042_STATUS, &val);
+       if (val & STATUS_POR_BIT) {
                INIT_WORK(&chip->work, max17042_init_worker);
                schedule_work(&chip->work);
        } else {
@@ -786,7 +777,7 @@ static int max17042_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int max17042_suspend(struct device *dev)
 {
        struct max17042_chip *chip = dev_get_drvdata(dev);
@@ -816,17 +807,11 @@ static int max17042_resume(struct device *dev)
 
        return 0;
 }
-
-static const struct dev_pm_ops max17042_pm_ops = {
-       .suspend        = max17042_suspend,
-       .resume         = max17042_resume,
-};
-
-#define MAX17042_PM_OPS (&max17042_pm_ops)
-#else
-#define MAX17042_PM_OPS NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend,
+                       max17042_resume);
+
 #ifdef CONFIG_OF
 static const struct of_device_id max17042_dt_match[] = {
        { .compatible = "maxim,max17042" },
@@ -849,7 +834,7 @@ static struct i2c_driver max17042_i2c_driver = {
        .driver = {
                .name   = "max17042",
                .of_match_table = of_match_ptr(max17042_dt_match),
-               .pm     = MAX17042_PM_OPS,
+               .pm     = &max17042_pm_ops,
        },
        .probe          = max17042_probe,
        .remove         = max17042_remove,
index ffa10ed..62c15af 100644 (file)
@@ -205,7 +205,7 @@ static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val)
 }
 
 
-int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
+static int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
 {
        queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
 
@@ -722,8 +722,12 @@ static int pm2xxx_charger_ac_en(struct ux500_charger *charger,
 
                dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset);
                if (!pm2->vddadc_en_ac) {
-                       regulator_enable(pm2->regu);
-                       pm2->vddadc_en_ac = true;
+                       ret = regulator_enable(pm2->regu);
+                       if (ret)
+                               dev_warn(pm2->dev,
+                                       "Failed to enable vddadc regulator\n");
+                       else
+                               pm2->vddadc_en_ac = true;
                }
 
                ret = pm2xxx_charging_init(pm2);
@@ -953,37 +957,24 @@ static int  pm2xxx_runtime_suspend(struct device *dev)
 {
        struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
        struct pm2xxx_charger *pm2;
-       int ret = 0;
 
        pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
-       if (!pm2) {
-               dev_err(pm2->dev, "no pm2xxx_charger data supplied\n");
-               ret = -EINVAL;
-               return ret;
-       }
-
        clear_lpn_pin(pm2);
 
-       return ret;
+       return 0;
 }
 
 static int  pm2xxx_runtime_resume(struct device *dev)
 {
        struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
        struct pm2xxx_charger *pm2;
-       int ret = 0;
 
        pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
-       if (!pm2) {
-               dev_err(pm2->dev, "no pm2xxx_charger data supplied\n");
-               ret = -EINVAL;
-               return ret;
-       }
 
        if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0)
                set_lpn_pin(pm2);
 
-       return ret;
+       return 0;
 }
 
 #endif
index bdd7b9b..8fc9d6d 100644 (file)
  * 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/delay.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/power_supply.h>
+#include <linux/slab.h>
+
 #include <linux/mfd/tps65090.h>
 
 #define TPS65090_REG_INTR_STS  0x00
@@ -185,10 +187,6 @@ static irqreturn_t tps65090_charger_isr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-#if defined(CONFIG_OF)
-
-#include <linux/of_device.h>
-
 static struct tps65090_platform_data *
                tps65090_parse_dt_charger_data(struct platform_device *pdev)
 {
@@ -210,13 +208,6 @@ static struct tps65090_platform_data *
        return pdata;
 
 }
-#else
-static struct tps65090_platform_data *
-               tps65090_parse_dt_charger_data(struct platform_device *pdev)
-{
-       return NULL;
-}
-#endif
 
 static int tps65090_charger_probe(struct platform_device *pdev)
 {
@@ -228,7 +219,7 @@ static int tps65090_charger_probe(struct platform_device *pdev)
 
        pdata = dev_get_platdata(pdev->dev.parent);
 
-       if (!pdata && pdev->dev.of_node)
+       if (IS_ENABLED(CONFIG_OF) && !pdata && pdev->dev.of_node)
                pdata = tps65090_parse_dt_charger_data(pdev);
 
        if (!pdata) {
@@ -277,13 +268,13 @@ static int tps65090_charger_probe(struct platform_device *pdev)
        if (ret) {
                dev_err(cdata->dev, "Unable to register irq %d err %d\n", irq,
                        ret);
-               goto fail_free_irq;
+               goto fail_unregister_supply;
        }
 
        ret = tps65090_config_charger(cdata);
        if (ret < 0) {
                dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
-               goto fail_free_irq;
+               goto fail_unregister_supply;
        }
 
        /* Check for charger presence */
@@ -292,14 +283,14 @@ static int tps65090_charger_probe(struct platform_device *pdev)
        if (ret < 0) {
                dev_err(cdata->dev, "%s(): Error in reading reg 0x%x", __func__,
                        TPS65090_REG_CG_STATUS1);
-               goto fail_free_irq;
+               goto fail_unregister_supply;
        }
 
        if (status1 != 0) {
                ret = tps65090_enable_charging(cdata);
                if (ret < 0) {
                        dev_err(cdata->dev, "error enabling charger\n");
-                       goto fail_free_irq;
+                       goto fail_unregister_supply;
                }
                cdata->ac_online = 1;
                power_supply_changed(&cdata->ac);
@@ -307,8 +298,6 @@ static int tps65090_charger_probe(struct platform_device *pdev)
 
        return 0;
 
-fail_free_irq:
-       devm_free_irq(cdata->dev, irq, cdata);
 fail_unregister_supply:
        power_supply_unregister(&cdata->ac);
 
@@ -319,7 +308,6 @@ static int tps65090_charger_remove(struct platform_device *pdev)
 {
        struct tps65090_charger *cdata = platform_get_drvdata(pdev);
 
-       devm_free_irq(cdata->dev, cdata->irq, cdata);
        power_supply_unregister(&cdata->ac);
 
        return 0;
index d98abe9..f141088 100644 (file)
@@ -495,10 +495,38 @@ static enum power_supply_property twl4030_charger_props[] = {
        POWER_SUPPLY_PROP_CURRENT_NOW,
 };
 
+#ifdef CONFIG_OF
+static const struct twl4030_bci_platform_data *
+twl4030_bci_parse_dt(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct twl4030_bci_platform_data *pdata;
+       u32 num;
+
+       if (!np)
+               return NULL;
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return pdata;
+
+       if (of_property_read_u32(np, "ti,bb-uvolt", &num) == 0)
+               pdata->bb_uvolt = num;
+       if (of_property_read_u32(np, "ti,bb-uamp", &num) == 0)
+               pdata->bb_uamp = num;
+       return pdata;
+}
+#else
+static inline const struct twl4030_bci_platform_data *
+twl4030_bci_parse_dt(struct device *dev)
+{
+       return NULL;
+}
+#endif
+
 static int __init twl4030_bci_probe(struct platform_device *pdev)
 {
        struct twl4030_bci *bci;
-       struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
+       const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
        int ret;
        u32 reg;
 
@@ -506,6 +534,9 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
        if (bci == NULL)
                return -ENOMEM;
 
+       if (!pdata)
+               pdata = twl4030_bci_parse_dt(&pdev->dev);
+
        bci->dev = &pdev->dev;
        bci->irq_chg = platform_get_irq(pdev, 0);
        bci->irq_bci = platform_get_irq(pdev, 1);
@@ -581,8 +612,11 @@ 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);
+       if (pdata)
+               twl4030_charger_enable_backup(pdata->bb_uvolt,
+                                             pdata->bb_uamp);
+       else
+               twl4030_charger_enable_backup(0, 0);
 
        return 0;
 
@@ -631,10 +665,17 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id twl_bci_of_match[] = {
+       {.compatible = "ti,twl4030-bci", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, twl_bci_of_match);
+
 static struct platform_driver twl4030_bci_driver = {
        .driver = {
                .name   = "twl4030_bci",
                .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(twl_bci_of_match),
        },
        .remove = __exit_p(twl4030_bci_remove),
 };
index 75840b5..eece329 100644 (file)
@@ -62,6 +62,15 @@ config PWM_BFIN
          To compile this driver as a module, choose M here: the module
          will be called pwm-bfin.
 
+config PWM_EP93XX
+       tristate "Cirrus Logic EP93xx PWM support"
+       depends on ARCH_EP93XX
+       help
+         Generic PWM framework driver for Cirrus Logic EP93xx.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-ep93xx.
+
 config PWM_IMX
        tristate "i.MX PWM support"
        depends on ARCH_MXC
index 77a8c18..8b754e4 100644 (file)
@@ -3,6 +3,7 @@ obj-$(CONFIG_PWM_SYSFS)         += sysfs.o
 obj-$(CONFIG_PWM_AB8500)       += pwm-ab8500.o
 obj-$(CONFIG_PWM_ATMEL_TCB)    += pwm-atmel-tcb.o
 obj-$(CONFIG_PWM_BFIN)         += pwm-bfin.o
+obj-$(CONFIG_PWM_EP93XX)       += pwm-ep93xx.o
 obj-$(CONFIG_PWM_IMX)          += pwm-imx.o
 obj-$(CONFIG_PWM_JZ4740)       += pwm-jz4740.o
 obj-$(CONFIG_PWM_LPC32XX)      += pwm-lpc32xx.o
index ba6ce01..f3dcd02 100644 (file)
@@ -249,6 +249,8 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
                }
        }
 
+       cmr |= (tcbpwm->div & ATMEL_TC_TCCLKS);
+
        __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR));
 
        if (index == 0)
@@ -305,7 +307,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                i = slowclk;
                rate = 32768;
                min = div_u64(NSEC_PER_SEC, rate);
-               max = min << 16;
+               max = min << tc->tcb_config->counter_width;
 
                /* If period is too big return ERANGE error */
                if (max < period_ns)
diff --git a/drivers/pwm/pwm-ep93xx.c b/drivers/pwm/pwm-ep93xx.c
new file mode 100644 (file)
index 0000000..33aa446
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * PWM framework driver for Cirrus Logic EP93xx
+ *
+ * Copyright (c) 2009        Matthieu Crapet <mcrapet@gmail.com>
+ * Copyright (c) 2009, 2013  H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * EP9301/02 have only one channel:
+ *   platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14)
+ *
+ * EP9307 has only one channel:
+ *   platform device ep93xx-pwm.0 - PWMOUT
+ *
+ * EP9312/15 have two channels:
+ *   platform device ep93xx-pwm.0 - PWMOUT
+ *   platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#include <mach/platform.h>     /* for ep93xx_pwm_{acquire,release}_gpio() */
+
+#define EP93XX_PWMx_TERM_COUNT 0x00
+#define EP93XX_PWMx_DUTY_CYCLE 0x04
+#define EP93XX_PWMx_ENABLE     0x08
+#define EP93XX_PWMx_INVERT     0x0c
+
+struct ep93xx_pwm {
+       void __iomem *base;
+       struct clk *clk;
+       struct pwm_chip chip;
+};
+
+static inline struct ep93xx_pwm *to_ep93xx_pwm(struct pwm_chip *chip)
+{
+       return container_of(chip, struct ep93xx_pwm, chip);
+}
+
+static int ep93xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct platform_device *pdev = to_platform_device(chip->dev);
+
+       return ep93xx_pwm_acquire_gpio(pdev);
+}
+
+static void ep93xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct platform_device *pdev = to_platform_device(chip->dev);
+
+       ep93xx_pwm_release_gpio(pdev);
+}
+
+static int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+                            int duty_ns, int period_ns)
+{
+       struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
+       void __iomem *base = ep93xx_pwm->base;
+       unsigned long long c;
+       unsigned long period_cycles;
+       unsigned long duty_cycles;
+       unsigned long term;
+       int ret = 0;
+
+       /*
+        * The clock needs to be enabled to access the PWM registers.
+        * Configuration can be changed at any time.
+        */
+       if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+               ret = clk_enable(ep93xx_pwm->clk);
+               if (ret)
+                       return ret;
+       }
+
+       c = clk_get_rate(ep93xx_pwm->clk);
+       c *= period_ns;
+       do_div(c, 1000000000);
+       period_cycles = c;
+
+       c = period_cycles;
+       c *= duty_ns;
+       do_div(c, period_ns);
+       duty_cycles = c;
+
+       if (period_cycles < 0x10000 && duty_cycles < 0x10000) {
+               term = readw(base + EP93XX_PWMx_TERM_COUNT);
+
+               /* Order is important if PWM is running */
+               if (period_cycles > term) {
+                       writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT);
+                       writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE);
+               } else {
+                       writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE);
+                       writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT);
+               }
+       } else {
+               ret = -EINVAL;
+       }
+
+       if (!test_bit(PWMF_ENABLED, &pwm->flags))
+               clk_disable(ep93xx_pwm->clk);
+
+       return ret;
+}
+
+static int ep93xx_pwm_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+                              enum pwm_polarity polarity)
+{
+       struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
+       int ret;
+
+       /*
+        * The clock needs to be enabled to access the PWM registers.
+        * Polarity can only be changed when the PWM is disabled.
+        */
+       ret = clk_enable(ep93xx_pwm->clk);
+       if (ret)
+               return ret;
+
+       if (polarity == PWM_POLARITY_INVERSED)
+               writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT);
+       else
+               writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT);
+
+       clk_disable(ep93xx_pwm->clk);
+
+       return 0;
+}
+
+static int ep93xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
+       int ret;
+
+       ret = clk_enable(ep93xx_pwm->clk);
+       if (ret)
+               return ret;
+
+       writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE);
+
+       return 0;
+}
+
+static void ep93xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
+
+       writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE);
+       clk_disable(ep93xx_pwm->clk);
+}
+
+static const struct pwm_ops ep93xx_pwm_ops = {
+       .request = ep93xx_pwm_request,
+       .free = ep93xx_pwm_free,
+       .config = ep93xx_pwm_config,
+       .set_polarity = ep93xx_pwm_polarity,
+       .enable = ep93xx_pwm_enable,
+       .disable = ep93xx_pwm_disable,
+       .owner = THIS_MODULE,
+};
+
+static int ep93xx_pwm_probe(struct platform_device *pdev)
+{
+       struct ep93xx_pwm *ep93xx_pwm;
+       struct resource *res;
+       int ret;
+
+       ep93xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_pwm), GFP_KERNEL);
+       if (!ep93xx_pwm)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ep93xx_pwm->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ep93xx_pwm->base))
+               return PTR_ERR(ep93xx_pwm->base);
+
+       ep93xx_pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk");
+       if (IS_ERR(ep93xx_pwm->clk))
+               return PTR_ERR(ep93xx_pwm->clk);
+
+       ep93xx_pwm->chip.dev = &pdev->dev;
+       ep93xx_pwm->chip.ops = &ep93xx_pwm_ops;
+       ep93xx_pwm->chip.base = -1;
+       ep93xx_pwm->chip.npwm = 1;
+
+       ret = pwmchip_add(&ep93xx_pwm->chip);
+       if (ret < 0)
+               return ret;
+
+       platform_set_drvdata(pdev, ep93xx_pwm);
+       return 0;
+}
+
+static int ep93xx_pwm_remove(struct platform_device *pdev)
+{
+       struct ep93xx_pwm *ep93xx_pwm = platform_get_drvdata(pdev);
+
+       return pwmchip_remove(&ep93xx_pwm->chip);
+}
+
+static struct platform_driver ep93xx_pwm_driver = {
+       .driver = {
+               .name = "ep93xx-pwm",
+       },
+       .probe = ep93xx_pwm_probe,
+       .remove = ep93xx_pwm_remove,
+};
+module_platform_driver(ep93xx_pwm_driver);
+
+MODULE_DESCRIPTION("Cirrus Logic EP93xx PWM driver");
+MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, "
+             "H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_ALIAS("platform:ep93xx-pwm");
+MODULE_LICENSE("GPL");
index 2b7c4f8..cc47733 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/pwm.h>
+#include <linux/of.h>
 #include <linux/of_device.h>
 
 /* i.MX1 and i.MX21 share the same PWM function block: */
@@ -296,7 +297,7 @@ static struct platform_driver imx_pwm_driver = {
        .driver         = {
                .name   = "imx-pwm",
                .owner = THIS_MODULE,
-               .of_match_table = of_match_ptr(imx_pwm_dt_ids),
+               .of_match_table = imx_pwm_dt_ids,
        },
        .probe          = imx_pwm_probe,
        .remove         = imx_pwm_remove,
index efac99e..9dc0f9d 100644 (file)
@@ -169,7 +169,7 @@ static struct platform_driver lpc32xx_pwm_driver = {
        .driver = {
                .name = "lpc32xx-pwm",
                .owner = THIS_MODULE,
-               .of_match_table = of_match_ptr(lpc32xx_pwm_dt_ids),
+               .of_match_table = lpc32xx_pwm_dt_ids,
        },
        .probe = lpc32xx_pwm_probe,
        .remove = lpc32xx_pwm_remove,
index c2c5a4f..9475bc7 100644 (file)
@@ -189,7 +189,7 @@ static struct platform_driver mxs_pwm_driver = {
        .driver = {
                .name = "mxs-pwm",
                .owner = THIS_MODULE,
-               .of_match_table = of_match_ptr(mxs_pwm_dt_ids),
+               .of_match_table = mxs_pwm_dt_ids,
        },
        .probe = mxs_pwm_probe,
        .remove = mxs_pwm_remove,
index fcc8b9a..b59639e 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/slab.h>
@@ -224,8 +225,8 @@ static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm)
 
 static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-       pwm_set_chip_data(pwm, NULL);
        devm_kfree(chip->dev, pwm_get_chip_data(pwm));
+       pwm_set_chip_data(pwm, NULL);
 }
 
 static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm)
index c2e2e58..4e5c3d1 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/pwm.h>
 #include <linux/of_device.h>
-#include <linux/pinctrl/consumer.h>
 
 #include "pwm-tipwmss.h"
 
@@ -208,11 +207,6 @@ static int ecap_pwm_probe(struct platform_device *pdev)
        struct clk *clk;
        struct ecap_pwm_chip *pc;
        u16 status;
-       struct pinctrl *pinctrl;
-
-       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-       if (IS_ERR(pinctrl))
-               dev_warn(&pdev->dev, "unable to select pin group\n");
 
        pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
        if (!pc) {
index 084f552..a4d8f51 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/clk.h>
 #include <linux/pm_runtime.h>
 #include <linux/of_device.h>
-#include <linux/pinctrl/consumer.h>
 
 #include "pwm-tipwmss.h"
 
@@ -439,11 +438,6 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev)
        struct clk *clk;
        struct ehrpwm_pwm_chip *pc;
        u16 status;
-       struct pinctrl *pinctrl;
-
-       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-       if (IS_ERR(pinctrl))
-               dev_warn(&pdev->dev, "unable to select pin group\n");
 
        pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
        if (!pc) {
index 29d1bba..b964470 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/i2c/twl.h>
index eef9105..b99a50e 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/i2c/twl.h>
index 23f8d1c..a00132e 100644 (file)
@@ -906,7 +906,7 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
                }
                ret = tps65910_reg_write(pmic->mfd, sr_reg_add, 0);
                if (ret < 0) {
-                       dev_err(mfd->dev, "Error in settting sr register\n");
+                       dev_err(mfd->dev, "Error in setting sr register\n");
                        return ret;
                }
        }
index b09c75c..a34b506 100644 (file)
@@ -30,7 +30,7 @@
 #include "remoteproc_internal.h"
 
 /* kick the remote processor, and let it know which virtqueue to poke at */
-static void rproc_virtio_notify(struct virtqueue *vq)
+static bool rproc_virtio_notify(struct virtqueue *vq)
 {
        struct rproc_vring *rvring = vq->priv;
        struct rproc *rproc = rvring->rvdev->rproc;
@@ -39,6 +39,7 @@ static void rproc_virtio_notify(struct virtqueue *vq)
        dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid);
 
        rproc->ops->kick(rproc, notifyid);
+       return true;
 }
 
 /**
index 15f166a..0077302 100644 (file)
@@ -626,7 +626,7 @@ comment "Platform RTC drivers"
 
 config RTC_DRV_CMOS
        tristate "PC-style 'CMOS'"
-       depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS || SPARC64
+       depends on X86 || ARM || M32R || ATARI || PPC || MIPS || SPARC64
        default y if X86
        help
          Say "yes" here to get direct support for the real time clock
@@ -643,6 +643,14 @@ config RTC_DRV_CMOS
          This driver can also be built as a module. If so, the module
          will be called rtc-cmos.
 
+config RTC_DRV_ALPHA
+       bool "Alpha PC-style CMOS"
+       depends on ALPHA
+       default y
+       help
+         Direct support for the real-time clock found on every Alpha
+         system, specifically MC146818 compatibles.  If in doubt, say Y.
+
 config RTC_DRV_VRTC
        tristate "Virtual RTC for Intel MID platforms"
        depends on X86_INTEL_MID
index 8b2cd8a..c0da95e 100644 (file)
@@ -428,6 +428,14 @@ static int __exit at91_rtc_remove(struct platform_device *pdev)
        return 0;
 }
 
+static void at91_rtc_shutdown(struct platform_device *pdev)
+{
+       /* Disable all interrupts */
+       at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM |
+                                       AT91_RTC_SECEV | AT91_RTC_TIMEV |
+                                       AT91_RTC_CALEV);
+}
+
 #ifdef CONFIG_PM_SLEEP
 
 /* AT91RM9200 RTC Power management control */
@@ -466,6 +474,7 @@ static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
 
 static struct platform_driver at91_rtc_driver = {
        .remove         = __exit_p(at91_rtc_remove),
+       .shutdown       = at91_rtc_shutdown,
        .driver         = {
                .name   = "at91_rtc",
                .owner  = THIS_MODULE,
index 45560ff..965a9da 100644 (file)
@@ -209,7 +209,7 @@ static int hid_rtc_read_time(struct device *dev, struct rtc_time *tm)
                platform_get_drvdata(to_platform_device(dev));
        int ret;
 
-       INIT_COMPLETION(time_state->comp_last_time);
+       reinit_completion(&time_state->comp_last_time);
        /* get a report with all values through requesting one value */
        sensor_hub_input_attr_get_raw_value(time_state->common_attributes.hsdev,
                        HID_USAGE_SENSOR_TIME, hid_time_addresses[0],
@@ -236,7 +236,7 @@ static const struct rtc_class_ops hid_time_rtc_ops = {
 static int hid_time_probe(struct platform_device *pdev)
 {
        int ret = 0;
-       struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+       struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
        struct hid_time_state *time_state = devm_kzalloc(&pdev->dev,
                sizeof(struct hid_time_state), GFP_KERNEL);
 
@@ -281,11 +281,18 @@ static int hid_time_probe(struct platform_device *pdev)
                goto err_open;
        }
 
+       /*
+        * Enable HID input processing early in order to be able to read the
+        * clock already in devm_rtc_device_register().
+        */
+       hid_device_io_start(hsdev->hdev);
+
        time_state->rtc = devm_rtc_device_register(&pdev->dev,
                                        "hid-sensor-time", &hid_time_rtc_ops,
                                        THIS_MODULE);
 
        if (IS_ERR_OR_NULL(time_state->rtc)) {
+               hid_device_io_stop(hsdev->hdev);
                ret = time_state->rtc ? PTR_ERR(time_state->rtc) : -ENODEV;
                time_state->rtc = NULL;
                dev_err(&pdev->dev, "rtc device register failed!\n");
@@ -303,7 +310,7 @@ err_open:
 
 static int hid_time_remove(struct platform_device *pdev)
 {
-       struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+       struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
 
        sensor_hub_device_close(hsdev);
        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TIME);
index 548209a..d0ab501 100644 (file)
@@ -118,22 +118,6 @@ static void scm_request_done(struct scm_request *scmrq)
        spin_unlock_irqrestore(&list_lock, flags);
 }
 
-static int scm_open(struct block_device *blkdev, fmode_t mode)
-{
-       return scm_get_ref();
-}
-
-static void scm_release(struct gendisk *gendisk, fmode_t mode)
-{
-       scm_put_ref();
-}
-
-static const struct block_device_operations scm_blk_devops = {
-       .owner = THIS_MODULE,
-       .open = scm_open,
-       .release = scm_release,
-};
-
 static bool scm_permit_request(struct scm_blk_dev *bdev, struct request *req)
 {
        return rq_data_dir(req) != WRITE || bdev->state != SCM_WR_PROHIBIT;
@@ -256,7 +240,7 @@ static void scm_blk_request(struct request_queue *rq)
                atomic_inc(&bdev->queued_reqs);
                blk_start_request(req);
 
-               ret = scm_start_aob(scmrq->aob);
+               ret = eadm_start_aob(scmrq->aob);
                if (ret) {
                        SCM_LOG(5, "no subchannel");
                        scm_request_requeue(scmrq);
@@ -320,7 +304,7 @@ static void scm_blk_handle_error(struct scm_request *scmrq)
        }
 
 restart:
-       if (!scm_start_aob(scmrq->aob))
+       if (!eadm_start_aob(scmrq->aob))
                return;
 
 requeue:
@@ -363,6 +347,10 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev)
        blk_run_queue(bdev->rq);
 }
 
+static const struct block_device_operations scm_blk_devops = {
+       .owner = THIS_MODULE,
+};
+
 int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)
 {
        struct request_queue *rq;
index c0d102e..27f930c 100644 (file)
@@ -187,7 +187,7 @@ bool scm_need_cluster_request(struct scm_request *scmrq)
 void scm_initiate_cluster_request(struct scm_request *scmrq)
 {
        scm_prepare_cluster_request(scmrq);
-       if (scm_start_aob(scmrq->aob))
+       if (eadm_start_aob(scmrq->aob))
                scm_request_requeue(scmrq);
 }
 
index 17821a0..b69ab17 100644 (file)
@@ -3,7 +3,8 @@
 #
 
 obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
-        sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o
+        sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \
+        sclp_early.o
 
 obj-$(CONFIG_TN3270) += raw3270.o
 obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
index f93cc32..71e9747 100644 (file)
@@ -564,6 +564,7 @@ static void __exit
 fs3270_exit(void)
 {
        raw3270_unregister_notifier(&fs3270_notifier);
+       device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, 0));
        __unregister_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270");
 }
 
index 40d1406..6fbe096 100644 (file)
@@ -99,6 +99,7 @@ struct init_sccb {
 } __attribute__((packed));
 
 extern u64 sclp_facilities;
+
 #define SCLP_HAS_CHP_INFO      (sclp_facilities & 0x8000000000000000ULL)
 #define SCLP_HAS_CHP_RECONFIG  (sclp_facilities & 0x2000000000000000ULL)
 #define SCLP_HAS_CPU_INFO      (sclp_facilities & 0x0800000000000000ULL)
@@ -179,6 +180,10 @@ void sclp_sdias_exit(void);
 extern int sclp_console_pages;
 extern int sclp_console_drop;
 extern unsigned long sclp_console_full;
+extern u8 sclp_fac84;
+extern unsigned long long sclp_rzm;
+extern unsigned long long sclp_rnmax;
+extern __initdata int sclp_early_read_info_sccb_valid;
 
 /* useful inlines */
 
index 77df9cb..eaa21d5 100644 (file)
 
 #include "sclp.h"
 
-#define SCLP_CMDW_READ_SCP_INFO                0x00020001
-#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
-
-struct read_info_sccb {
-       struct  sccb_header header;     /* 0-7 */
-       u16     rnmax;                  /* 8-9 */
-       u8      rnsize;                 /* 10 */
-       u8      _reserved0[24 - 11];    /* 11-15 */
-       u8      loadparm[8];            /* 24-31 */
-       u8      _reserved1[48 - 32];    /* 32-47 */
-       u64     facilities;             /* 48-55 */
-       u8      _reserved2[84 - 56];    /* 56-83 */
-       u8      fac84;                  /* 84 */
-       u8      fac85;                  /* 85 */
-       u8      _reserved3[91 - 86];    /* 86-90 */
-       u8      flags;                  /* 91 */
-       u8      _reserved4[100 - 92];   /* 92-99 */
-       u32     rnsize2;                /* 100-103 */
-       u64     rnmax2;                 /* 104-111 */
-       u8      _reserved5[4096 - 112]; /* 112-4095 */
-} __attribute__((packed, aligned(PAGE_SIZE)));
-
-static struct init_sccb __initdata early_event_mask_sccb __aligned(PAGE_SIZE);
-static struct read_info_sccb __initdata early_read_info_sccb;
-static int __initdata early_read_info_sccb_valid;
-
-u64 sclp_facilities;
-static u8 sclp_fac84;
-static unsigned long long rzm;
-static unsigned long long rnmax;
-
-static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
-{
-       int rc;
-
-       __ctl_set_bit(0, 9);
-       rc = sclp_service_call(cmd, sccb);
-       if (rc)
-               goto out;
-       __load_psw_mask(PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_EA |
-                       PSW_MASK_BA | PSW_MASK_EXT | PSW_MASK_WAIT);
-       local_irq_disable();
-out:
-       /* Contents of the sccb might have changed. */
-       barrier();
-       __ctl_clear_bit(0, 9);
-       return rc;
-}
-
-static void __init sclp_read_info_early(void)
-{
-       int rc;
-       int i;
-       struct read_info_sccb *sccb;
-       sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
-                                 SCLP_CMDW_READ_SCP_INFO};
-
-       sccb = &early_read_info_sccb;
-       for (i = 0; i < ARRAY_SIZE(commands); i++) {
-               do {
-                       memset(sccb, 0, sizeof(*sccb));
-                       sccb->header.length = sizeof(*sccb);
-                       sccb->header.function_code = 0x80;
-                       sccb->header.control_mask[2] = 0x80;
-                       rc = sclp_cmd_sync_early(commands[i], sccb);
-               } while (rc == -EBUSY);
-
-               if (rc)
-                       break;
-               if (sccb->header.response_code == 0x10) {
-                       early_read_info_sccb_valid = 1;
-                       break;
-               }
-               if (sccb->header.response_code != 0x1f0)
-                       break;
-       }
-}
-
-static void __init sclp_event_mask_early(void)
-{
-       struct init_sccb *sccb = &early_event_mask_sccb;
-       int rc;
-
-       do {
-               memset(sccb, 0, sizeof(*sccb));
-               sccb->header.length = sizeof(*sccb);
-               sccb->mask_length = sizeof(sccb_mask_t);
-               rc = sclp_cmd_sync_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb);
-       } while (rc == -EBUSY);
-}
-
-void __init sclp_facilities_detect(void)
-{
-       struct read_info_sccb *sccb;
-
-       sclp_read_info_early();
-       if (!early_read_info_sccb_valid)
-               return;
-
-       sccb = &early_read_info_sccb;
-       sclp_facilities = sccb->facilities;
-       sclp_fac84 = sccb->fac84;
-       if (sccb->fac85 & 0x02)
-               S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP;
-       rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
-       rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
-       rzm <<= 20;
-
-       sclp_event_mask_early();
-}
-
-bool __init sclp_has_linemode(void)
-{
-       struct init_sccb *sccb = &early_event_mask_sccb;
-
-       if (sccb->header.response_code != 0x20)
-               return 0;
-       if (!(sccb->sclp_send_mask & (EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK)))
-               return 0;
-       if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
-               return 0;
-       return 1;
-}
-
-bool __init sclp_has_vt220(void)
-{
-       struct init_sccb *sccb = &early_event_mask_sccb;
-
-       if (sccb->header.response_code != 0x20)
-               return 0;
-       if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK)
-               return 1;
-       return 0;
-}
-
-unsigned long long sclp_get_rnmax(void)
-{
-       return rnmax;
-}
-
-unsigned long long sclp_get_rzm(void)
-{
-       return rzm;
-}
-
-/*
- * This function will be called after sclp_facilities_detect(), which gets
- * called from early.c code. Therefore the sccb should have valid contents.
- */
-void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
-{
-       struct read_info_sccb *sccb;
-
-       if (!early_read_info_sccb_valid)
-               return;
-       sccb = &early_read_info_sccb;
-       info->is_valid = 1;
-       if (sccb->flags & 0x2)
-               info->has_dump = 1;
-       memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN);
-}
-
 static void sclp_sync_callback(struct sclp_req *req, void *data)
 {
        struct completion *completion = data;
@@ -356,14 +194,14 @@ struct assign_storage_sccb {
 
 int arch_get_memory_phys_device(unsigned long start_pfn)
 {
-       if (!rzm)
+       if (!sclp_rzm)
                return 0;
-       return PFN_PHYS(start_pfn) >> ilog2(rzm);
+       return PFN_PHYS(start_pfn) >> ilog2(sclp_rzm);
 }
 
 static unsigned long long rn2addr(u16 rn)
 {
-       return (unsigned long long) (rn - 1) * rzm;
+       return (unsigned long long) (rn - 1) * sclp_rzm;
 }
 
 static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
@@ -404,7 +242,7 @@ static int sclp_assign_storage(u16 rn)
        if (rc)
                return rc;
        start = rn2addr(rn);
-       storage_key_init_range(start, start + rzm);
+       storage_key_init_range(start, start + sclp_rzm);
        return 0;
 }
 
@@ -462,7 +300,7 @@ static int sclp_mem_change_state(unsigned long start, unsigned long size,
                istart = rn2addr(incr->rn);
                if (start + size - 1 < istart)
                        break;
-               if (start > istart + rzm - 1)
+               if (start > istart + sclp_rzm - 1)
                        continue;
                if (online)
                        rc |= sclp_assign_storage(incr->rn);
@@ -526,7 +364,7 @@ static void __init add_memory_merged(u16 rn)
        if (!first_rn)
                goto skip_add;
        start = rn2addr(first_rn);
-       size = (unsigned long long ) num * rzm;
+       size = (unsigned long long) num * sclp_rzm;
        if (start >= VMEM_MAX_PHYS)
                goto skip_add;
        if (start + size > VMEM_MAX_PHYS)
@@ -574,7 +412,7 @@ static void __init insert_increment(u16 rn, int standby, int assigned)
        }
        if (!assigned)
                new_incr->rn = last_rn + 1;
-       if (new_incr->rn > rnmax) {
+       if (new_incr->rn > sclp_rnmax) {
                kfree(new_incr);
                return;
        }
@@ -617,7 +455,7 @@ static int __init sclp_detect_standby_memory(void)
 
        if (OLDMEM_BASE) /* No standby memory in kdump mode */
                return 0;
-       if (!early_read_info_sccb_valid)
+       if (!sclp_early_read_info_sccb_valid)
                return 0;
        if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
                return 0;
@@ -661,7 +499,7 @@ static int __init sclp_detect_standby_memory(void)
        }
        if (rc || list_empty(&sclp_mem_list))
                goto out;
-       for (i = 1; i <= rnmax - assigned; i++)
+       for (i = 1; i <= sclp_rnmax - assigned; i++)
                insert_increment(0, 1, 0);
        rc = register_memory_notifier(&sclp_mem_nb);
        if (rc)
diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c
new file mode 100644 (file)
index 0000000..f7aa080
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * SCLP early driver
+ *
+ * Copyright IBM Corp. 2013
+ */
+
+#define KMSG_COMPONENT "sclp_early"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <asm/ctl_reg.h>
+#include <asm/sclp.h>
+#include <asm/ipl.h>
+#include "sclp_sdias.h"
+#include "sclp.h"
+
+#define SCLP_CMDW_READ_SCP_INFO                0x00020001
+#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
+
+struct read_info_sccb {
+       struct  sccb_header header;     /* 0-7 */
+       u16     rnmax;                  /* 8-9 */
+       u8      rnsize;                 /* 10 */
+       u8      _reserved0[24 - 11];    /* 11-15 */
+       u8      loadparm[8];            /* 24-31 */
+       u8      _reserved1[48 - 32];    /* 32-47 */
+       u64     facilities;             /* 48-55 */
+       u8      _reserved2[84 - 56];    /* 56-83 */
+       u8      fac84;                  /* 84 */
+       u8      fac85;                  /* 85 */
+       u8      _reserved3[91 - 86];    /* 86-90 */
+       u8      flags;                  /* 91 */
+       u8      _reserved4[100 - 92];   /* 92-99 */
+       u32     rnsize2;                /* 100-103 */
+       u64     rnmax2;                 /* 104-111 */
+       u8      _reserved5[4096 - 112]; /* 112-4095 */
+} __packed __aligned(PAGE_SIZE);
+
+static __initdata struct init_sccb early_event_mask_sccb __aligned(PAGE_SIZE);
+static __initdata struct read_info_sccb early_read_info_sccb;
+static __initdata char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE);
+static unsigned long sclp_hsa_size;
+
+__initdata int sclp_early_read_info_sccb_valid;
+u64 sclp_facilities;
+u8 sclp_fac84;
+unsigned long long sclp_rzm;
+unsigned long long sclp_rnmax;
+
+static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
+{
+       int rc;
+
+       __ctl_set_bit(0, 9);
+       rc = sclp_service_call(cmd, sccb);
+       if (rc)
+               goto out;
+       __load_psw_mask(PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_EA |
+                       PSW_MASK_BA | PSW_MASK_EXT | PSW_MASK_WAIT);
+       local_irq_disable();
+out:
+       /* Contents of the sccb might have changed. */
+       barrier();
+       __ctl_clear_bit(0, 9);
+       return rc;
+}
+
+static void __init sclp_read_info_early(void)
+{
+       int rc;
+       int i;
+       struct read_info_sccb *sccb;
+       sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
+                                 SCLP_CMDW_READ_SCP_INFO};
+
+       sccb = &early_read_info_sccb;
+       for (i = 0; i < ARRAY_SIZE(commands); i++) {
+               do {
+                       memset(sccb, 0, sizeof(*sccb));
+                       sccb->header.length = sizeof(*sccb);
+                       sccb->header.function_code = 0x80;
+                       sccb->header.control_mask[2] = 0x80;
+                       rc = sclp_cmd_sync_early(commands[i], sccb);
+               } while (rc == -EBUSY);
+
+               if (rc)
+                       break;
+               if (sccb->header.response_code == 0x10) {
+                       sclp_early_read_info_sccb_valid = 1;
+                       break;
+               }
+               if (sccb->header.response_code != 0x1f0)
+                       break;
+       }
+}
+
+static void __init sclp_facilities_detect(void)
+{
+       struct read_info_sccb *sccb;
+
+       sclp_read_info_early();
+       if (!sclp_early_read_info_sccb_valid)
+               return;
+
+       sccb = &early_read_info_sccb;
+       sclp_facilities = sccb->facilities;
+       sclp_fac84 = sccb->fac84;
+       if (sccb->fac85 & 0x02)
+               S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP;
+       sclp_rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
+       sclp_rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
+       sclp_rzm <<= 20;
+}
+
+bool __init sclp_has_linemode(void)
+{
+       struct init_sccb *sccb = &early_event_mask_sccb;
+
+       if (sccb->header.response_code != 0x20)
+               return 0;
+       if (!(sccb->sclp_send_mask & (EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK)))
+               return 0;
+       if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
+               return 0;
+       return 1;
+}
+
+bool __init sclp_has_vt220(void)
+{
+       struct init_sccb *sccb = &early_event_mask_sccb;
+
+       if (sccb->header.response_code != 0x20)
+               return 0;
+       if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK)
+               return 1;
+       return 0;
+}
+
+unsigned long long sclp_get_rnmax(void)
+{
+       return sclp_rnmax;
+}
+
+unsigned long long sclp_get_rzm(void)
+{
+       return sclp_rzm;
+}
+
+/*
+ * This function will be called after sclp_facilities_detect(), which gets
+ * called from early.c code. Therefore the sccb should have valid contents.
+ */
+void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
+{
+       struct read_info_sccb *sccb;
+
+       if (!sclp_early_read_info_sccb_valid)
+               return;
+       sccb = &early_read_info_sccb;
+       info->is_valid = 1;
+       if (sccb->flags & 0x2)
+               info->has_dump = 1;
+       memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN);
+}
+
+static int __init sclp_cmd_early(sclp_cmdw_t cmd, void *sccb)
+{
+       int rc;
+
+       do {
+               rc = sclp_cmd_sync_early(cmd, sccb);
+       } while (rc == -EBUSY);
+
+       if (rc)
+               return -EIO;
+       if (((struct sccb_header *) sccb)->response_code != 0x0020)
+               return -EIO;
+       return 0;
+}
+
+static void __init sccb_init_eq_size(struct sdias_sccb *sccb)
+{
+       memset(sccb, 0, sizeof(*sccb));
+
+       sccb->hdr.length = sizeof(*sccb);
+       sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
+       sccb->evbuf.hdr.type = EVTYP_SDIAS;
+       sccb->evbuf.event_qual = SDIAS_EQ_SIZE;
+       sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
+       sccb->evbuf.event_id = 4712;
+       sccb->evbuf.dbs = 1;
+}
+
+static int __init sclp_set_event_mask(unsigned long receive_mask,
+                                     unsigned long send_mask)
+{
+       struct init_sccb *sccb = (void *) &sccb_early;
+
+       memset(sccb, 0, sizeof(*sccb));
+       sccb->header.length = sizeof(*sccb);
+       sccb->mask_length = sizeof(sccb_mask_t);
+       sccb->receive_mask = receive_mask;
+       sccb->send_mask = send_mask;
+       return sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb);
+}
+
+static long __init sclp_hsa_size_init(void)
+{
+       struct sdias_sccb *sccb = (void *) &sccb_early;
+
+       sccb_init_eq_size(sccb);
+       if (sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_DATA, sccb))
+               return -EIO;
+       if (sccb->evbuf.blk_cnt != 0)
+               return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE;
+       return 0;
+}
+
+static long __init sclp_hsa_copy_wait(void)
+{
+       struct sccb_header *sccb = (void *) &sccb_early;
+
+       memset(sccb, 0, PAGE_SIZE);
+       sccb->length = PAGE_SIZE;
+       if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb))
+               return -EIO;
+       return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE;
+}
+
+unsigned long sclp_get_hsa_size(void)
+{
+       return sclp_hsa_size;
+}
+
+static void __init sclp_hsa_size_detect(void)
+{
+       long size;
+
+       /* First try synchronous interface (LPAR) */
+       if (sclp_set_event_mask(0, 0x40000010))
+               return;
+       size = sclp_hsa_size_init();
+       if (size < 0)
+               return;
+       if (size != 0)
+               goto out;
+       /* Then try asynchronous interface (z/VM) */
+       if (sclp_set_event_mask(0x00000010, 0x40000010))
+               return;
+       size = sclp_hsa_size_init();
+       if (size < 0)
+               return;
+       size = sclp_hsa_copy_wait();
+       if (size < 0)
+               return;
+out:
+       sclp_hsa_size = size;
+}
+
+void __init sclp_early_detect(void)
+{
+       sclp_facilities_detect();
+       sclp_hsa_size_detect();
+       sclp_set_event_mask(0, 0);
+}
index b103293..561a041 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Sclp "store data in absolut storage"
+ * SCLP "store data in absolute storage"
  *
- * Copyright IBM Corp. 2003, 2007
+ * Copyright IBM Corp. 2003, 2013
  * Author(s): Michael Holzheu
  */
 
@@ -14,6 +14,7 @@
 #include <asm/debug.h>
 #include <asm/ipl.h>
 
+#include "sclp_sdias.h"
 #include "sclp.h"
 #include "sclp_rw.h"
 
 #define SDIAS_RETRIES 300
 #define SDIAS_SLEEP_TICKS 50
 
-#define EQ_STORE_DATA  0x0
-#define EQ_SIZE                0x1
-#define DI_FCP_DUMP    0x0
-#define ASA_SIZE_32    0x0
-#define ASA_SIZE_64    0x1
-#define EVSTATE_ALL_STORED     0x0
-#define EVSTATE_NO_DATA                0x3
-#define EVSTATE_PART_STORED    0x10
-
 static struct debug_info *sdias_dbf;
 
 static struct sclp_register sclp_sdias_register = {
        .send_mask = EVTYP_SDIAS_MASK,
 };
 
-struct sdias_evbuf {
-       struct  evbuf_header hdr;
-       u8      event_qual;
-       u8      data_id;
-       u64     reserved2;
-       u32     event_id;
-       u16     reserved3;
-       u8      asa_size;
-       u8      event_status;
-       u32     reserved4;
-       u32     blk_cnt;
-       u64     asa;
-       u32     reserved5;
-       u32     fbn;
-       u32     reserved6;
-       u32     lbn;
-       u16     reserved7;
-       u16     dbs;
-} __attribute__((packed));
-
-struct sdias_sccb {
-       struct sccb_header  hdr;
-       struct sdias_evbuf  evbuf;
-} __attribute__((packed));
-
 static struct sdias_sccb sccb __attribute__((aligned(4096)));
 static struct sdias_evbuf sdias_evbuf;
 
@@ -148,8 +115,8 @@ int sclp_sdias_blk_count(void)
        sccb.hdr.length = sizeof(sccb);
        sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
        sccb.evbuf.hdr.type = EVTYP_SDIAS;
-       sccb.evbuf.event_qual = EQ_SIZE;
-       sccb.evbuf.data_id = DI_FCP_DUMP;
+       sccb.evbuf.event_qual = SDIAS_EQ_SIZE;
+       sccb.evbuf.data_id = SDIAS_DI_FCP_DUMP;
        sccb.evbuf.event_id = 4712;
        sccb.evbuf.dbs = 1;
 
@@ -208,13 +175,13 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
        sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
        sccb.evbuf.hdr.type = EVTYP_SDIAS;
        sccb.evbuf.hdr.flags = 0;
-       sccb.evbuf.event_qual = EQ_STORE_DATA;
-       sccb.evbuf.data_id = DI_FCP_DUMP;
+       sccb.evbuf.event_qual = SDIAS_EQ_STORE_DATA;
+       sccb.evbuf.data_id = SDIAS_DI_FCP_DUMP;
        sccb.evbuf.event_id = 4712;
 #ifdef CONFIG_64BIT
-       sccb.evbuf.asa_size = ASA_SIZE_64;
+       sccb.evbuf.asa_size = SDIAS_ASA_SIZE_64;
 #else
-       sccb.evbuf.asa_size = ASA_SIZE_32;
+       sccb.evbuf.asa_size = SDIAS_ASA_SIZE_32;
 #endif
        sccb.evbuf.event_status = 0;
        sccb.evbuf.blk_cnt = nr_blks;
@@ -240,20 +207,19 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
        }
 
        switch (sdias_evbuf.event_status) {
-               case EVSTATE_ALL_STORED:
-                       TRACE("all stored\n");
-                       break;
-               case EVSTATE_PART_STORED:
-                       TRACE("part stored: %i\n", sdias_evbuf.blk_cnt);
-                       break;
-               case EVSTATE_NO_DATA:
-                       TRACE("no data\n");
-                       /* fall through */
-               default:
-                       pr_err("Error from SCLP while copying hsa. "
-                              "Event status = %x\n",
-                              sdias_evbuf.event_status);
-                       rc = -EIO;
+       case SDIAS_EVSTATE_ALL_STORED:
+               TRACE("all stored\n");
+               break;
+       case SDIAS_EVSTATE_PART_STORED:
+               TRACE("part stored: %i\n", sdias_evbuf.blk_cnt);
+               break;
+       case SDIAS_EVSTATE_NO_DATA:
+               TRACE("no data\n");
+               /* fall through */
+       default:
+               pr_err("Error from SCLP while copying hsa. Event status = %x\n",
+                      sdias_evbuf.event_status);
+               rc = -EIO;
        }
 out:
        mutex_unlock(&sdias_mutex);
diff --git a/drivers/s390/char/sclp_sdias.h b/drivers/s390/char/sclp_sdias.h
new file mode 100644 (file)
index 0000000..f2431c4
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SCLP "store data in absolute storage"
+ *
+ * Copyright IBM Corp. 2003, 2013
+ */
+
+#ifndef SCLP_SDIAS_H
+#define SCLP_SDIAS_H
+
+#include "sclp.h"
+
+#define SDIAS_EQ_STORE_DATA            0x0
+#define SDIAS_EQ_SIZE                  0x1
+#define SDIAS_DI_FCP_DUMP              0x0
+#define SDIAS_ASA_SIZE_32              0x0
+#define SDIAS_ASA_SIZE_64              0x1
+#define SDIAS_EVSTATE_ALL_STORED       0x0
+#define SDIAS_EVSTATE_NO_DATA          0x3
+#define SDIAS_EVSTATE_PART_STORED      0x10
+
+struct sdias_evbuf {
+       struct  evbuf_header hdr;
+       u8      event_qual;
+       u8      data_id;
+       u64     reserved2;
+       u32     event_id;
+       u16     reserved3;
+       u8      asa_size;
+       u8      event_status;
+       u32     reserved4;
+       u32     blk_cnt;
+       u64     asa;
+       u32     reserved5;
+       u32     fbn;
+       u32     reserved6;
+       u32     lbn;
+       u16     reserved7;
+       u16     dbs;
+} __packed;
+
+struct sdias_sccb {
+       struct sccb_header      hdr;
+       struct sdias_evbuf      evbuf;
+} __packed;
+
+#endif /* SCLP_SDIAS_H */
index ffb1fcf..3d8e4d6 100644 (file)
@@ -328,9 +328,9 @@ static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
        mem_offs = 0;
 
        /* Copy from HSA data */
-       if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) {
-               size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE
-                          - mem_start));
+       if (*ppos < sclp_get_hsa_size() + HEADER_SIZE) {
+               size = min((count - hdr_count),
+                          (size_t) (sclp_get_hsa_size() - mem_start));
                rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);
                if (rc)
                        goto fail;
@@ -490,7 +490,7 @@ static ssize_t zcore_hsa_read(struct file *filp, char __user *buf,
        static char str[18];
 
        if (hsa_available)
-               snprintf(str, sizeof(str), "%lx\n", ZFCPDUMP_HSA_SIZE);
+               snprintf(str, sizeof(str), "%lx\n", sclp_get_hsa_size());
        else
                snprintf(str, sizeof(str), "0\n");
        return simple_read_from_buffer(buf, count, ppos, str, strlen(str));
@@ -584,17 +584,9 @@ static int __init sys_info_init(enum arch_id arch, unsigned long mem_end)
 
 static int __init check_sdias(void)
 {
-       int rc, act_hsa_size;
-
-       rc = sclp_sdias_blk_count();
-       if (rc < 0) {
+       if (!sclp_get_hsa_size()) {
                TRACE("Could not determine HSA size\n");
-               return rc;
-       }
-       act_hsa_size = (rc - 1) * PAGE_SIZE;
-       if (act_hsa_size < ZFCPDUMP_HSA_SIZE) {
-               TRACE("HSA size too small: %i\n", act_hsa_size);
-               return -EINVAL;
+               return -ENODEV;
        }
        return 0;
 }
@@ -662,7 +654,7 @@ static int __init zcore_reipl_init(void)
        ipl_block = (void *) __get_free_page(GFP_KERNEL);
        if (!ipl_block)
                return -ENOMEM;
-       if (ipib_info.ipib < ZFCPDUMP_HSA_SIZE)
+       if (ipib_info.ipib < sclp_get_hsa_size())
                rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE);
        else
                rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE);
index aca7bfc..3a2ee4a 100644 (file)
@@ -190,7 +190,7 @@ static struct subchannel *eadm_get_idle_sch(void)
        return NULL;
 }
 
-static int eadm_start_aob(struct aob *aob)
+int eadm_start_aob(struct aob *aob)
 {
        struct eadm_private *private;
        struct subchannel *sch;
@@ -218,6 +218,7 @@ out_unlock:
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(eadm_start_aob);
 
 static int eadm_subchannel_probe(struct subchannel *sch)
 {
@@ -380,11 +381,6 @@ static struct css_driver eadm_subchannel_driver = {
        .restore = eadm_subchannel_restore,
 };
 
-static struct eadm_ops eadm_ops = {
-       .eadm_start = eadm_start_aob,
-       .owner = THIS_MODULE,
-};
-
 static int __init eadm_sch_init(void)
 {
        int ret;
@@ -404,7 +400,6 @@ static int __init eadm_sch_init(void)
        if (ret)
                goto cleanup;
 
-       register_eadm_ops(&eadm_ops);
        return ret;
 
 cleanup:
@@ -415,7 +410,6 @@ cleanup:
 
 static void __exit eadm_sch_exit(void)
 {
-       unregister_eadm_ops(&eadm_ops);
        css_driver_unregister(&eadm_subchannel_driver);
        isc_unregister(EADM_SCH_ISC);
        debug_unregister(eadm_debug);
index 46ec256..15268ed 100644 (file)
@@ -15,8 +15,6 @@
 #include "chsc.h"
 
 static struct device *scm_root;
-static struct eadm_ops *eadm_ops;
-static DEFINE_MUTEX(eadm_ops_mutex);
 
 #define to_scm_dev(n) container_of(n, struct scm_device, dev)
 #define        to_scm_drv(d) container_of(d, struct scm_driver, drv)
@@ -73,49 +71,6 @@ void scm_driver_unregister(struct scm_driver *scmdrv)
 }
 EXPORT_SYMBOL_GPL(scm_driver_unregister);
 
-int scm_get_ref(void)
-{
-       int ret = 0;
-
-       mutex_lock(&eadm_ops_mutex);
-       if (!eadm_ops || !try_module_get(eadm_ops->owner))
-               ret = -ENOENT;
-       mutex_unlock(&eadm_ops_mutex);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(scm_get_ref);
-
-void scm_put_ref(void)
-{
-       mutex_lock(&eadm_ops_mutex);
-       module_put(eadm_ops->owner);
-       mutex_unlock(&eadm_ops_mutex);
-}
-EXPORT_SYMBOL_GPL(scm_put_ref);
-
-void register_eadm_ops(struct eadm_ops *ops)
-{
-       mutex_lock(&eadm_ops_mutex);
-       eadm_ops = ops;
-       mutex_unlock(&eadm_ops_mutex);
-}
-EXPORT_SYMBOL_GPL(register_eadm_ops);
-
-void unregister_eadm_ops(struct eadm_ops *ops)
-{
-       mutex_lock(&eadm_ops_mutex);
-       eadm_ops = NULL;
-       mutex_unlock(&eadm_ops_mutex);
-}
-EXPORT_SYMBOL_GPL(unregister_eadm_ops);
-
-int scm_start_aob(struct aob *aob)
-{
-       return eadm_ops->eadm_start(aob);
-}
-EXPORT_SYMBOL_GPL(scm_start_aob);
-
 void scm_irq_handler(struct aob *aob, int error)
 {
        struct aob_rq_header *aobrq = (void *) aob->request.data;
index af2166f..1abd0db 100644 (file)
@@ -166,11 +166,15 @@ static void kvm_reset(struct virtio_device *vdev)
  * make a hypercall.  We hand the address  of the virtqueue so the Host
  * knows which virtqueue we're talking about.
  */
-static void kvm_notify(struct virtqueue *vq)
+static bool kvm_notify(struct virtqueue *vq)
 {
+       long rc;
        struct kvm_vqconfig *config = vq->priv;
 
-       kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, config->address);
+       rc = kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, config->address);
+       if (rc < 0)
+               return false;
+       return true;
 }
 
 /*
index 779dc51..d629717 100644 (file)
@@ -162,7 +162,7 @@ static inline long do_kvm_notify(struct subchannel_id schid,
        return __rc;
 }
 
-static void virtio_ccw_kvm_notify(struct virtqueue *vq)
+static bool virtio_ccw_kvm_notify(struct virtqueue *vq)
 {
        struct virtio_ccw_vq_info *info = vq->priv;
        struct virtio_ccw_device *vcdev;
@@ -171,6 +171,9 @@ static void virtio_ccw_kvm_notify(struct virtqueue *vq)
        vcdev = to_vc_device(info->vq->vdev);
        ccw_device_get_schid(vcdev->cdev, &schid);
        info->cookie = do_kvm_notify(schid, vq->index, info->cookie);
+       if (info->cookie < 0)
+               return false;
+       return true;
 }
 
 static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev,
index 132a905..0ca6448 100644 (file)
@@ -344,7 +344,7 @@ void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len,
 
 /**
  * zfcp_dbf_san_req - trace event for issued SAN request
- * @tag: indentifier for event
+ * @tag: identifier for event
  * @fsf_req: request containing issued CT data
  * d_id: destination ID
  */
@@ -361,7 +361,7 @@ void zfcp_dbf_san_req(char *tag, struct zfcp_fsf_req *fsf, u32 d_id)
 
 /**
  * zfcp_dbf_san_res - trace event for received SAN request
- * @tag: indentifier for event
+ * @tag: identifier for event
  * @fsf_req: request containing issued CT data
  */
 void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf)
@@ -377,7 +377,7 @@ void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf)
 
 /**
  * zfcp_dbf_san_in_els - trace event for incoming ELS
- * @tag: indentifier for event
+ * @tag: identifier for event
  * @fsf_req: request containing issued CT data
  */
 void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf)
index d85ac1a..fbcd48d 100644 (file)
@@ -511,7 +511,8 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
                goto cleanup;
        }
 
-       if (fibsize > (dev->max_fib_size - sizeof(struct aac_fibhdr))) {
+       if ((fibsize < (sizeof(struct user_aac_srb) - sizeof(struct user_sgentry))) ||
+           (fibsize > (dev->max_fib_size - sizeof(struct aac_fibhdr)))) {
                rcode = -EINVAL;
                goto cleanup;
        }
index 33c52bc..97fd450 100644 (file)
@@ -1035,7 +1035,6 @@ static void arcmsr_remove(struct pci_dev *pdev)
        pci_release_regions(pdev);
        scsi_host_put(host);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static void arcmsr_shutdown(struct pci_dev *pdev)
index 15a629d..a795d81 100644 (file)
@@ -3144,8 +3144,6 @@ static void atp870u_remove (struct pci_dev *pdev)
        atp870u_free_tables(pshost);
        printk(KERN_INFO "scsi_host_put : %p\n",pshost);
        scsi_host_put(pshost);
-       printk(KERN_INFO "pci_set_drvdata : %p\n",pdev);
-       pci_set_drvdata(pdev, NULL);    
 }
 MODULE_LICENSE("GPL");
 
index 7591fa4..fc80a32 100644 (file)
@@ -804,7 +804,6 @@ bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad)
        /* Disable PCIE Advanced Error Recovery (AER) */
        pci_disable_pcie_error_reporting(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 bfa_status_t
index 5be718c..e4cf23d 100644 (file)
@@ -126,7 +126,7 @@ static void bnx2i_iscsi_license_error(struct bnx2i_hba *hba, u32 error_code)
 
 /**
  * bnx2i_arm_cq_event_coalescing - arms CQ to enable EQ notification
- * @ep:                endpoint (transport indentifier) structure
+ * @ep:                endpoint (transport identifier) structure
  * @action:    action, ARM or DISARM. For now only ARM_CQE is used
  *
  * Arm'ing CQ will enable chip to generate global EQ events inorder to interrupt
@@ -756,7 +756,7 @@ void bnx2i_send_cmd_cleanup_req(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd)
 /**
  * bnx2i_send_conn_destroy - initiates iscsi connection teardown process
  * @hba:       adapter structure pointer
- * @ep:                endpoint (transport indentifier) structure
+ * @ep:                endpoint (transport identifier) structure
  *
  * this routine prepares and posts CONN_OFLD_REQ1/2 KWQE to initiate
  *     iscsi connection context clean-up process
@@ -791,7 +791,7 @@ int bnx2i_send_conn_destroy(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
 /**
  * bnx2i_570x_send_conn_ofld_req - initiates iscsi conn context setup process
  * @hba:               adapter structure pointer
- * @ep:                endpoint (transport indentifier) structure
+ * @ep:                endpoint (transport identifier) structure
  *
  * 5706/5708/5709 specific - prepares and posts CONN_OFLD_REQ1/2 KWQE
  */
@@ -851,7 +851,7 @@ static int bnx2i_570x_send_conn_ofld_req(struct bnx2i_hba *hba,
 /**
  * bnx2i_5771x_send_conn_ofld_req - initiates iscsi connection context creation
  * @hba:               adapter structure pointer
- * @ep:                endpoint (transport indentifier) structure
+ * @ep:                endpoint (transport identifier) structure
  *
  * 57710 specific - prepares and posts CONN_OFLD_REQ1/2 KWQE
  */
@@ -920,7 +920,7 @@ static int bnx2i_5771x_send_conn_ofld_req(struct bnx2i_hba *hba,
  * bnx2i_send_conn_ofld_req - initiates iscsi connection context setup process
  *
  * @hba:               adapter structure pointer
- * @ep:                endpoint (transport indentifier) structure
+ * @ep:                endpoint (transport identifier) structure
  *
  * this routine prepares and posts CONN_OFLD_REQ1/2 KWQE
  */
@@ -939,7 +939,7 @@ int bnx2i_send_conn_ofld_req(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
 
 /**
  * setup_qp_page_tables - iscsi QP page table setup function
- * @ep:                endpoint (transport indentifier) structure
+ * @ep:                endpoint (transport identifier) structure
  *
  * Sets up page tables for SQ/RQ/CQ, 1G/sec (5706/5708/5709) devices requires
  *     64-bit address in big endian format. Whereas 10G/sec (57710) requires
@@ -1046,7 +1046,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
 /**
  * bnx2i_alloc_qp_resc - allocates required resources for QP.
  * @hba:       adapter structure pointer
- * @ep:                endpoint (transport indentifier) structure
+ * @ep:                endpoint (transport identifier) structure
  *
  * Allocate QP (transport layer for iSCSI connection) resources, DMA'able
  *     memory for SQ/RQ/CQ and page tables. EP structure elements such
@@ -1191,7 +1191,7 @@ mem_alloc_err:
 /**
  * bnx2i_free_qp_resc - free memory resources held by QP
  * @hba:       adapter structure pointer
- * @ep:        endpoint (transport indentifier) structure
+ * @ep:        endpoint (transport identifier) structure
  *
  * Free QP resources - SQ/RQ/CQ memory and page tables.
  */
index fabeb88..854dad7 100644 (file)
@@ -596,7 +596,7 @@ void bnx2i_drop_session(struct iscsi_cls_session *cls_session)
 /**
  * bnx2i_ep_destroy_list_add - add an entry to EP destroy list
  * @hba:       pointer to adapter instance
- * @ep:                pointer to endpoint (transport indentifier) structure
+ * @ep:                pointer to endpoint (transport identifier) structure
  *
  * EP destroy queue manager
  */
@@ -613,7 +613,7 @@ static int bnx2i_ep_destroy_list_add(struct bnx2i_hba *hba,
  * bnx2i_ep_destroy_list_del - add an entry to EP destroy list
  *
  * @hba:               pointer to adapter instance
- * @ep:                pointer to endpoint (transport indentifier) structure
+ * @ep:                pointer to endpoint (transport identifier) structure
  *
  * EP destroy queue manager
  */
@@ -630,7 +630,7 @@ static int bnx2i_ep_destroy_list_del(struct bnx2i_hba *hba,
 /**
  * bnx2i_ep_ofld_list_add - add an entry to ep offload pending list
  * @hba:       pointer to adapter instance
- * @ep:                pointer to endpoint (transport indentifier) structure
+ * @ep:                pointer to endpoint (transport identifier) structure
  *
  * pending conn offload completion queue manager
  */
@@ -646,7 +646,7 @@ static int bnx2i_ep_ofld_list_add(struct bnx2i_hba *hba,
 /**
  * bnx2i_ep_ofld_list_del - add an entry to ep offload pending list
  * @hba:               pointer to adapter instance
- * @ep:                pointer to endpoint (transport indentifier) structure
+ * @ep:                pointer to endpoint (transport identifier) structure
  *
  * pending conn offload completion queue manager
  */
@@ -721,7 +721,7 @@ bnx2i_find_ep_in_destroy_list(struct bnx2i_hba *hba, u32 iscsi_cid)
 /**
  * bnx2i_ep_active_list_add - add an entry to ep active list
  * @hba:       pointer to adapter instance
- * @ep:                pointer to endpoint (transport indentifier) structure
+ * @ep:                pointer to endpoint (transport identifier) structure
  *
  * current active conn queue manager
  */
@@ -737,7 +737,7 @@ static void bnx2i_ep_active_list_add(struct bnx2i_hba *hba,
 /**
  * bnx2i_ep_active_list_del - deletes an entry to ep active list
  * @hba:       pointer to adapter instance
- * @ep:                pointer to endpoint (transport indentifier) structure
+ * @ep:                pointer to endpoint (transport identifier) structure
  *
  * current active conn queue manager
  */
@@ -1695,7 +1695,7 @@ no_nx2_route:
 /**
  * bnx2i_tear_down_conn - tear down iscsi/tcp connection and free resources
  * @hba:       pointer to adapter instance
- * @ep:                endpoint (transport indentifier) structure
+ * @ep:                endpoint (transport identifier) structure
  *
  * destroys cm_sock structure and on chip iscsi context
  */
index 00346fe..1aafc33 100644 (file)
@@ -1010,7 +1010,6 @@ err_lnode_exit:
        csio_hw_stop(hw);
        spin_unlock_irq(&hw->lock);
        csio_lnodes_unblock_request(hw);
-       pci_set_drvdata(hw->pdev, NULL);
        csio_lnodes_exit(hw, 0);
        csio_hw_free(hw);
 err_pci_exit:
@@ -1044,7 +1043,6 @@ static void csio_remove_one(struct pci_dev *pdev)
 
        csio_lnodes_exit(hw, 0);
        csio_hw_free(hw);
-       pci_set_drvdata(pdev, NULL);
        csio_pci_exit(pdev, &bars);
 }
 
index 42e8624..83d9bf6 100644 (file)
@@ -4861,7 +4861,6 @@ static void dc395x_remove_one(struct pci_dev *dev)
        adapter_uninit(acb);
        pci_disable_device(dev);
        scsi_host_put(scsi_host);
-       pci_set_drvdata(dev, NULL);
 }
 
 
index be09b10..33e4ec2 100644 (file)
@@ -1005,7 +1005,6 @@ static void fnic_remove(struct pci_dev *pdev)
        fnic_iounmap(fnic);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        scsi_host_put(lp->host);
 }
 
index 6d55b4e..ee4fa40 100644 (file)
@@ -594,8 +594,6 @@ static void gdth_pci_remove_one(struct pci_dev *pdev)
 {
        gdth_ha_str *ha = pci_get_drvdata(pdev);
 
-       pci_set_drvdata(pdev, NULL);
-
        list_del(&ha->list);
        gdth_remove_one(ha);
 
index fb5a898..22f6432 100644 (file)
@@ -5017,7 +5017,6 @@ static void hpsa_remove_one(struct pci_dev *pdev)
        kfree(h->hba_inquiry_data);
        pci_disable_device(pdev);
        pci_release_regions(pdev);
-       pci_set_drvdata(pdev, NULL);
        kfree(h);
 }
 
index ca6bf2a..68c94cc 100644 (file)
@@ -4581,8 +4581,6 @@ lpfc_disable_pci_dev(struct lpfc_hba *phba)
        /* Release PCI resource and disable PCI device */
        pci_release_selected_regions(pdev, bars);
        pci_disable_device(pdev);
-       /* Null out PCI private reference to driver */
-       pci_set_drvdata(pdev, NULL);
 
        return;
 }
@@ -9429,7 +9427,6 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev)
        /* Disable interrupt */
        lpfc_sli_disable_intr(phba);
 
-       pci_set_drvdata(pdev, NULL);
        scsi_host_put(shost);
 
        /*
index 515c962..d1a4b82 100644 (file)
@@ -534,7 +534,6 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        return 0;
 
 out_cmm_unreg:
-       pci_set_drvdata(pdev, NULL);
        megaraid_cmm_unregister(adapter);
 out_fini_mbox:
        megaraid_fini_mbox(adapter);
@@ -594,11 +593,6 @@ megaraid_detach_one(struct pci_dev *pdev)
        // detach from the IO sub-system
        megaraid_io_detach(adapter);
 
-       // reset the device state in the PCI structure. We check this
-       // condition when we enter here. If the device state is NULL,
-       // that would mean the device has already been removed
-       pci_set_drvdata(pdev, NULL);
-
        // Unregister from common management module
        //
        // FIXME: this must return success or failure for conditions if there
index 2cf9470..0a743a5 100644 (file)
@@ -4451,7 +4451,6 @@ retry_irq_register:
        megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL;
        megasas_mgmt_info.max_index--;
 
-       pci_set_drvdata(pdev, NULL);
        instance->instancet->disable_intr(instance);
        if (instance->msix_vectors)
                for (i = 0 ; i < instance->msix_vectors; i++)
@@ -4807,8 +4806,6 @@ static void megasas_detach_one(struct pci_dev *pdev)
                }
        }
 
-       pci_set_drvdata(instance->pdev, NULL);
-
        instance->instancet->disable_intr(instance);
 
        if (instance->msix_vectors)
@@ -4850,8 +4847,6 @@ static void megasas_detach_one(struct pci_dev *pdev)
                                instance->evt_detail, instance->evt_detail_h);
        scsi_host_put(host);
 
-       pci_set_drvdata(pdev, NULL);
-
        pci_disable_device(pdev);
 
        return;
index 7b7381d..5ff978b 100644 (file)
@@ -657,7 +657,6 @@ static void mvs_pci_remove(struct pci_dev *pdev)
        tasklet_kill(&((struct mvs_prv_info *)sha->lldd_ha)->mv_tasklet);
 #endif
 
-       pci_set_drvdata(pdev, NULL);
        sas_unregister_ha(sha);
        sas_remove_host(mvi->shost);
        scsi_remove_host(mvi->shost);
index 6b1b4e9..6c1f223 100644 (file)
@@ -1411,7 +1411,7 @@ static int mvs_exec_internal_tmf_task(struct domain_device *dev,
 
                if (res) {
                        del_timer(&task->slow_task->timer);
-                       mv_printk("executing internel task failed:%d\n", res);
+                       mv_printk("executing internal task failed:%d\n", res);
                        goto ex_err;
                }
 
index c3601b5..edbee8d 100644 (file)
@@ -2583,7 +2583,6 @@ static int mvumi_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        return 0;
 
 fail_io_attach:
-       pci_set_drvdata(pdev, NULL);
        mhba->instancet->disable_intr(mhba);
        free_irq(mhba->pdev->irq, mhba);
 fail_init_irq:
@@ -2618,7 +2617,6 @@ static void mvumi_detach_one(struct pci_dev *pdev)
        free_irq(mhba->pdev->irq, mhba);
        mvumi_release_fw(mhba);
        scsi_host_put(host);
-       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
        dev_dbg(&pdev->dev, "driver is removed!\n");
 }
index 5982a58..7d014b1 100644 (file)
@@ -1615,7 +1615,7 @@ struct ncb {
        spinlock_t      smp_lock;       /* Lock for SMP threading       */
 
        /*----------------------------------------------------------------
-       **      Chip and controller indentification.
+       **      Chip and controller identification.
        **----------------------------------------------------------------
        */
        int             unit;           /* Unit number                  */
index 662bf13..34f5f5f 100644 (file)
@@ -909,7 +909,6 @@ static void pm8001_pci_remove(struct pci_dev *pdev)
        struct pm8001_hba_info *pm8001_ha;
        int i;
        pm8001_ha = sha->lldd_ha;
-       pci_set_drvdata(pdev, NULL);
        sas_unregister_ha(sha);
        sas_remove_host(pm8001_ha->shost);
        list_del(&pm8001_ha->list);
index 1eb7b02..bd6f743 100644 (file)
@@ -1512,7 +1512,8 @@ static int pmcraid_notify_aen(
        }
 
        result =
-               genlmsg_multicast(skb, 0, pmcraid_event_family.id, GFP_ATOMIC);
+               genlmsg_multicast(&pmcraid_event_family, skb, 0,
+                                 pmcraid_event_family.id, GFP_ATOMIC);
 
        /* If there are no listeners, genlmsg_multicast may return non-zero
         * value.
@@ -6049,7 +6050,6 @@ out_release_regions:
 
 out_disable_device:
        atomic_dec(&pmcraid_adapter_count);
-       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
        return -ENODEV;
 }
index bcd57f6..52be35e 100644 (file)
@@ -3179,7 +3179,6 @@ qla2x00_remove_one(struct pci_dev *pdev)
        pci_disable_pcie_error_reporting(pdev);
 
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static void
index f85b9e5..7eb19be 100644 (file)
@@ -330,7 +330,7 @@ static int tcm_qla2xxx_check_demo_mode(struct se_portal_group *se_tpg)
        struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
                                struct tcm_qla2xxx_tpg, se_tpg);
 
-       return QLA_TPG_ATTRIB(tpg)->generate_node_acls;
+       return tpg->tpg_attrib.generate_node_acls;
 }
 
 static int tcm_qla2xxx_check_demo_mode_cache(struct se_portal_group *se_tpg)
@@ -338,7 +338,7 @@ static int tcm_qla2xxx_check_demo_mode_cache(struct se_portal_group *se_tpg)
        struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
                                struct tcm_qla2xxx_tpg, se_tpg);
 
-       return QLA_TPG_ATTRIB(tpg)->cache_dynamic_acls;
+       return tpg->tpg_attrib.cache_dynamic_acls;
 }
 
 static int tcm_qla2xxx_check_demo_write_protect(struct se_portal_group *se_tpg)
@@ -346,7 +346,7 @@ static int tcm_qla2xxx_check_demo_write_protect(struct se_portal_group *se_tpg)
        struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
                                struct tcm_qla2xxx_tpg, se_tpg);
 
-       return QLA_TPG_ATTRIB(tpg)->demo_mode_write_protect;
+       return tpg->tpg_attrib.demo_mode_write_protect;
 }
 
 static int tcm_qla2xxx_check_prod_write_protect(struct se_portal_group *se_tpg)
@@ -354,7 +354,7 @@ static int tcm_qla2xxx_check_prod_write_protect(struct se_portal_group *se_tpg)
        struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
                                struct tcm_qla2xxx_tpg, se_tpg);
 
-       return QLA_TPG_ATTRIB(tpg)->prod_mode_write_protect;
+       return tpg->tpg_attrib.prod_mode_write_protect;
 }
 
 static int tcm_qla2xxx_check_demo_mode_login_only(struct se_portal_group *se_tpg)
@@ -362,7 +362,7 @@ static int tcm_qla2xxx_check_demo_mode_login_only(struct se_portal_group *se_tpg
        struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
                                struct tcm_qla2xxx_tpg, se_tpg);
 
-       return QLA_TPG_ATTRIB(tpg)->demo_mode_login_only;
+       return tpg->tpg_attrib.demo_mode_login_only;
 }
 
 static struct se_node_acl *tcm_qla2xxx_alloc_fabric_acl(
@@ -847,7 +847,7 @@ static ssize_t tcm_qla2xxx_tpg_attrib_show_##name(                  \
        struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,              \
                        struct tcm_qla2xxx_tpg, se_tpg);                \
                                                                        \
-       return sprintf(page, "%u\n", QLA_TPG_ATTRIB(tpg)->name);        \
+       return sprintf(page, "%u\n", tpg->tpg_attrib.name);     \
 }                                                                      \
                                                                        \
 static ssize_t tcm_qla2xxx_tpg_attrib_store_##name(                    \
@@ -1027,10 +1027,10 @@ static struct se_portal_group *tcm_qla2xxx_make_tpg(
         * By default allow READ-ONLY TPG demo-mode access w/ cached dynamic
         * NodeACLs
         */
-       QLA_TPG_ATTRIB(tpg)->generate_node_acls = 1;
-       QLA_TPG_ATTRIB(tpg)->demo_mode_write_protect = 1;
-       QLA_TPG_ATTRIB(tpg)->cache_dynamic_acls = 1;
-       QLA_TPG_ATTRIB(tpg)->demo_mode_login_only = 1;
+       tpg->tpg_attrib.generate_node_acls = 1;
+       tpg->tpg_attrib.demo_mode_write_protect = 1;
+       tpg->tpg_attrib.cache_dynamic_acls = 1;
+       tpg->tpg_attrib.demo_mode_login_only = 1;
 
        ret = core_tpg_register(&tcm_qla2xxx_fabric_configfs->tf_ops, wwn,
                                &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL);
@@ -1830,16 +1830,16 @@ static int tcm_qla2xxx_register_configfs(void)
        /*
         * Setup default attribute lists for various fabric->tf_cit_tmpl
         */
-       TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_qla2xxx_tpg_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs =
+       fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = tcm_qla2xxx_tpg_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs =
                                                tcm_qla2xxx_tpg_attrib_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;
        /*
         * Register the fabric for use within TCM
         */
@@ -1870,15 +1870,15 @@ static int tcm_qla2xxx_register_configfs(void)
        /*
         * Setup default attribute lists for various npiv_fabric->tf_cit_tmpl
         */
-       TF_CIT_TMPL(npiv_fabric)->tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs;
-       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       npiv_fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs;
+       npiv_fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = NULL;
+       npiv_fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL;
+       npiv_fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;
+       npiv_fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
+       npiv_fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+       npiv_fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       npiv_fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       npiv_fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;
        /*
         * Register the npiv_fabric for use within TCM
         */
index 3293275..771f7b8 100644 (file)
@@ -45,8 +45,6 @@ struct tcm_qla2xxx_tpg {
        struct se_portal_group se_tpg;
 };
 
-#define QLA_TPG_ATTRIB(tpg)    (&(tpg)->tpg_attrib)
-
 struct tcm_qla2xxx_fc_loopid {
        struct se_node_acl *se_nacl;
 };
index 6dc3e99..a28d5e6 100644 (file)
@@ -7827,7 +7827,6 @@ static void qla4xxx_remove_adapter(struct pci_dev *pdev)
 
        pci_disable_pcie_error_reporting(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 /**
index f379c7f..2700a5a 100644 (file)
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/delay.h>
 
 #include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_srp.h>
+#include "scsi_priv.h"
 #include "scsi_transport_srp_internal.h"
 
 struct srp_host_attrs {
@@ -38,7 +41,7 @@ struct srp_host_attrs {
 #define to_srp_host_attrs(host)        ((struct srp_host_attrs *)(host)->shost_data)
 
 #define SRP_HOST_ATTRS 0
-#define SRP_RPORT_ATTRS 3
+#define SRP_RPORT_ATTRS 8
 
 struct srp_internal {
        struct scsi_transport_template t;
@@ -54,6 +57,36 @@ struct srp_internal {
 
 #define        dev_to_rport(d) container_of(d, struct srp_rport, dev)
 #define transport_class_to_srp_rport(dev) dev_to_rport((dev)->parent)
+static inline struct Scsi_Host *rport_to_shost(struct srp_rport *r)
+{
+       return dev_to_shost(r->dev.parent);
+}
+
+/**
+ * srp_tmo_valid() - check timeout combination validity
+ *
+ * The combination of the timeout parameters must be such that SCSI commands
+ * are finished in a reasonable time. Hence do not allow the fast I/O fail
+ * timeout to exceed SCSI_DEVICE_BLOCK_MAX_TIMEOUT. Furthermore, these
+ * parameters must be such that multipath can detect failed paths timely.
+ * Hence do not allow all three parameters to be disabled simultaneously.
+ */
+int srp_tmo_valid(int reconnect_delay, int fast_io_fail_tmo, int dev_loss_tmo)
+{
+       if (reconnect_delay < 0 && fast_io_fail_tmo < 0 && dev_loss_tmo < 0)
+               return -EINVAL;
+       if (reconnect_delay == 0)
+               return -EINVAL;
+       if (fast_io_fail_tmo > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
+               return -EINVAL;
+       if (dev_loss_tmo >= LONG_MAX / HZ)
+               return -EINVAL;
+       if (fast_io_fail_tmo >= 0 && dev_loss_tmo >= 0 &&
+           fast_io_fail_tmo >= dev_loss_tmo)
+               return -EINVAL;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(srp_tmo_valid);
 
 static int srp_host_setup(struct transport_container *tc, struct device *dev,
                          struct device *cdev)
@@ -134,10 +167,465 @@ static ssize_t store_srp_rport_delete(struct device *dev,
 
 static DEVICE_ATTR(delete, S_IWUSR, NULL, store_srp_rport_delete);
 
+static ssize_t show_srp_rport_state(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       static const char *const state_name[] = {
+               [SRP_RPORT_RUNNING]     = "running",
+               [SRP_RPORT_BLOCKED]     = "blocked",
+               [SRP_RPORT_FAIL_FAST]   = "fail-fast",
+               [SRP_RPORT_LOST]        = "lost",
+       };
+       struct srp_rport *rport = transport_class_to_srp_rport(dev);
+       enum srp_rport_state state = rport->state;
+
+       return sprintf(buf, "%s\n",
+                      (unsigned)state < ARRAY_SIZE(state_name) ?
+                      state_name[state] : "???");
+}
+
+static DEVICE_ATTR(state, S_IRUGO, show_srp_rport_state, NULL);
+
+static ssize_t srp_show_tmo(char *buf, int tmo)
+{
+       return tmo >= 0 ? sprintf(buf, "%d\n", tmo) : sprintf(buf, "off\n");
+}
+
+static int srp_parse_tmo(int *tmo, const char *buf)
+{
+       int res = 0;
+
+       if (strncmp(buf, "off", 3) != 0)
+               res = kstrtoint(buf, 0, tmo);
+       else
+               *tmo = -1;
+
+       return res;
+}
+
+static ssize_t show_reconnect_delay(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct srp_rport *rport = transport_class_to_srp_rport(dev);
+
+       return srp_show_tmo(buf, rport->reconnect_delay);
+}
+
+static ssize_t store_reconnect_delay(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, const size_t count)
+{
+       struct srp_rport *rport = transport_class_to_srp_rport(dev);
+       int res, delay;
+
+       res = srp_parse_tmo(&delay, buf);
+       if (res)
+               goto out;
+       res = srp_tmo_valid(delay, rport->fast_io_fail_tmo,
+                           rport->dev_loss_tmo);
+       if (res)
+               goto out;
+
+       if (rport->reconnect_delay <= 0 && delay > 0 &&
+           rport->state != SRP_RPORT_RUNNING) {
+               queue_delayed_work(system_long_wq, &rport->reconnect_work,
+                                  delay * HZ);
+       } else if (delay <= 0) {
+               cancel_delayed_work(&rport->reconnect_work);
+       }
+       rport->reconnect_delay = delay;
+       res = count;
+
+out:
+       return res;
+}
+
+static DEVICE_ATTR(reconnect_delay, S_IRUGO | S_IWUSR, show_reconnect_delay,
+                  store_reconnect_delay);
+
+static ssize_t show_failed_reconnects(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct srp_rport *rport = transport_class_to_srp_rport(dev);
+
+       return sprintf(buf, "%d\n", rport->failed_reconnects);
+}
+
+static DEVICE_ATTR(failed_reconnects, S_IRUGO, show_failed_reconnects, NULL);
+
+static ssize_t show_srp_rport_fast_io_fail_tmo(struct device *dev,
+                                              struct device_attribute *attr,
+                                              char *buf)
+{
+       struct srp_rport *rport = transport_class_to_srp_rport(dev);
+
+       return srp_show_tmo(buf, rport->fast_io_fail_tmo);
+}
+
+static ssize_t store_srp_rport_fast_io_fail_tmo(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t count)
+{
+       struct srp_rport *rport = transport_class_to_srp_rport(dev);
+       int res;
+       int fast_io_fail_tmo;
+
+       res = srp_parse_tmo(&fast_io_fail_tmo, buf);
+       if (res)
+               goto out;
+       res = srp_tmo_valid(rport->reconnect_delay, fast_io_fail_tmo,
+                           rport->dev_loss_tmo);
+       if (res)
+               goto out;
+       rport->fast_io_fail_tmo = fast_io_fail_tmo;
+       res = count;
+
+out:
+       return res;
+}
+
+static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR,
+                  show_srp_rport_fast_io_fail_tmo,
+                  store_srp_rport_fast_io_fail_tmo);
+
+static ssize_t show_srp_rport_dev_loss_tmo(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       struct srp_rport *rport = transport_class_to_srp_rport(dev);
+
+       return srp_show_tmo(buf, rport->dev_loss_tmo);
+}
+
+static ssize_t store_srp_rport_dev_loss_tmo(struct device *dev,
+                                           struct device_attribute *attr,
+                                           const char *buf, size_t count)
+{
+       struct srp_rport *rport = transport_class_to_srp_rport(dev);
+       int res;
+       int dev_loss_tmo;
+
+       res = srp_parse_tmo(&dev_loss_tmo, buf);
+       if (res)
+               goto out;
+       res = srp_tmo_valid(rport->reconnect_delay, rport->fast_io_fail_tmo,
+                           dev_loss_tmo);
+       if (res)
+               goto out;
+       rport->dev_loss_tmo = dev_loss_tmo;
+       res = count;
+
+out:
+       return res;
+}
+
+static DEVICE_ATTR(dev_loss_tmo, S_IRUGO | S_IWUSR,
+                  show_srp_rport_dev_loss_tmo,
+                  store_srp_rport_dev_loss_tmo);
+
+static int srp_rport_set_state(struct srp_rport *rport,
+                              enum srp_rport_state new_state)
+{
+       enum srp_rport_state old_state = rport->state;
+
+       lockdep_assert_held(&rport->mutex);
+
+       switch (new_state) {
+       case SRP_RPORT_RUNNING:
+               switch (old_state) {
+               case SRP_RPORT_LOST:
+                       goto invalid;
+               default:
+                       break;
+               }
+               break;
+       case SRP_RPORT_BLOCKED:
+               switch (old_state) {
+               case SRP_RPORT_RUNNING:
+                       break;
+               default:
+                       goto invalid;
+               }
+               break;
+       case SRP_RPORT_FAIL_FAST:
+               switch (old_state) {
+               case SRP_RPORT_LOST:
+                       goto invalid;
+               default:
+                       break;
+               }
+               break;
+       case SRP_RPORT_LOST:
+               break;
+       }
+       rport->state = new_state;
+       return 0;
+
+invalid:
+       return -EINVAL;
+}
+
+/**
+ * srp_reconnect_work() - reconnect and schedule a new attempt if necessary
+ */
+static void srp_reconnect_work(struct work_struct *work)
+{
+       struct srp_rport *rport = container_of(to_delayed_work(work),
+                                       struct srp_rport, reconnect_work);
+       struct Scsi_Host *shost = rport_to_shost(rport);
+       int delay, res;
+
+       res = srp_reconnect_rport(rport);
+       if (res != 0) {
+               shost_printk(KERN_ERR, shost,
+                            "reconnect attempt %d failed (%d)\n",
+                            ++rport->failed_reconnects, res);
+               delay = rport->reconnect_delay *
+                       min(100, max(1, rport->failed_reconnects - 10));
+               if (delay > 0)
+                       queue_delayed_work(system_long_wq,
+                                          &rport->reconnect_work, delay * HZ);
+       }
+}
+
+static void __rport_fail_io_fast(struct srp_rport *rport)
+{
+       struct Scsi_Host *shost = rport_to_shost(rport);
+       struct srp_internal *i;
+
+       lockdep_assert_held(&rport->mutex);
+
+       if (srp_rport_set_state(rport, SRP_RPORT_FAIL_FAST))
+               return;
+       scsi_target_unblock(rport->dev.parent, SDEV_TRANSPORT_OFFLINE);
+
+       /* Involve the LLD if possible to terminate all I/O on the rport. */
+       i = to_srp_internal(shost->transportt);
+       if (i->f->terminate_rport_io)
+               i->f->terminate_rport_io(rport);
+}
+
+/**
+ * rport_fast_io_fail_timedout() - fast I/O failure timeout handler
+ */
+static void rport_fast_io_fail_timedout(struct work_struct *work)
+{
+       struct srp_rport *rport = container_of(to_delayed_work(work),
+                                       struct srp_rport, fast_io_fail_work);
+       struct Scsi_Host *shost = rport_to_shost(rport);
+
+       pr_info("fast_io_fail_tmo expired for SRP %s / %s.\n",
+               dev_name(&rport->dev), dev_name(&shost->shost_gendev));
+
+       mutex_lock(&rport->mutex);
+       if (rport->state == SRP_RPORT_BLOCKED)
+               __rport_fail_io_fast(rport);
+       mutex_unlock(&rport->mutex);
+}
+
+/**
+ * rport_dev_loss_timedout() - device loss timeout handler
+ */
+static void rport_dev_loss_timedout(struct work_struct *work)
+{
+       struct srp_rport *rport = container_of(to_delayed_work(work),
+                                       struct srp_rport, dev_loss_work);
+       struct Scsi_Host *shost = rport_to_shost(rport);
+       struct srp_internal *i = to_srp_internal(shost->transportt);
+
+       pr_info("dev_loss_tmo expired for SRP %s / %s.\n",
+               dev_name(&rport->dev), dev_name(&shost->shost_gendev));
+
+       mutex_lock(&rport->mutex);
+       WARN_ON(srp_rport_set_state(rport, SRP_RPORT_LOST) != 0);
+       scsi_target_unblock(rport->dev.parent, SDEV_TRANSPORT_OFFLINE);
+       mutex_unlock(&rport->mutex);
+
+       i->f->rport_delete(rport);
+}
+
+static void __srp_start_tl_fail_timers(struct srp_rport *rport)
+{
+       struct Scsi_Host *shost = rport_to_shost(rport);
+       int delay, fast_io_fail_tmo, dev_loss_tmo;
+
+       lockdep_assert_held(&rport->mutex);
+
+       if (!rport->deleted) {
+               delay = rport->reconnect_delay;
+               fast_io_fail_tmo = rport->fast_io_fail_tmo;
+               dev_loss_tmo = rport->dev_loss_tmo;
+               pr_debug("%s current state: %d\n",
+                        dev_name(&shost->shost_gendev), rport->state);
+
+               if (delay > 0)
+                       queue_delayed_work(system_long_wq,
+                                          &rport->reconnect_work,
+                                          1UL * delay * HZ);
+               if (fast_io_fail_tmo >= 0 &&
+                   srp_rport_set_state(rport, SRP_RPORT_BLOCKED) == 0) {
+                       pr_debug("%s new state: %d\n",
+                                dev_name(&shost->shost_gendev),
+                                rport->state);
+                       scsi_target_block(&shost->shost_gendev);
+                       queue_delayed_work(system_long_wq,
+                                          &rport->fast_io_fail_work,
+                                          1UL * fast_io_fail_tmo * HZ);
+               }
+               if (dev_loss_tmo >= 0)
+                       queue_delayed_work(system_long_wq,
+                                          &rport->dev_loss_work,
+                                          1UL * dev_loss_tmo * HZ);
+       } else {
+               pr_debug("%s has already been deleted\n",
+                        dev_name(&shost->shost_gendev));
+               srp_rport_set_state(rport, SRP_RPORT_FAIL_FAST);
+               scsi_target_unblock(&shost->shost_gendev,
+                                   SDEV_TRANSPORT_OFFLINE);
+       }
+}
+
+/**
+ * srp_start_tl_fail_timers() - start the transport layer failure timers
+ *
+ * Start the transport layer fast I/O failure and device loss timers. Do not
+ * modify a timer that was already started.
+ */
+void srp_start_tl_fail_timers(struct srp_rport *rport)
+{
+       mutex_lock(&rport->mutex);
+       __srp_start_tl_fail_timers(rport);
+       mutex_unlock(&rport->mutex);
+}
+EXPORT_SYMBOL(srp_start_tl_fail_timers);
+
+/**
+ * scsi_request_fn_active() - number of kernel threads inside scsi_request_fn()
+ */
+static int scsi_request_fn_active(struct Scsi_Host *shost)
+{
+       struct scsi_device *sdev;
+       struct request_queue *q;
+       int request_fn_active = 0;
+
+       shost_for_each_device(sdev, shost) {
+               q = sdev->request_queue;
+
+               spin_lock_irq(q->queue_lock);
+               request_fn_active += q->request_fn_active;
+               spin_unlock_irq(q->queue_lock);
+       }
+
+       return request_fn_active;
+}
+
+/**
+ * srp_reconnect_rport() - reconnect to an SRP target port
+ *
+ * Blocks SCSI command queueing before invoking reconnect() such that
+ * queuecommand() won't be invoked concurrently with reconnect() from outside
+ * the SCSI EH. This is important since a reconnect() implementation may
+ * reallocate resources needed by queuecommand().
+ *
+ * Notes:
+ * - This function neither waits until outstanding requests have finished nor
+ *   tries to abort these. It is the responsibility of the reconnect()
+ *   function to finish outstanding commands before reconnecting to the target
+ *   port.
+ * - It is the responsibility of the caller to ensure that the resources
+ *   reallocated by the reconnect() function won't be used while this function
+ *   is in progress. One possible strategy is to invoke this function from
+ *   the context of the SCSI EH thread only. Another possible strategy is to
+ *   lock the rport mutex inside each SCSI LLD callback that can be invoked by
+ *   the SCSI EH (the scsi_host_template.eh_*() functions and also the
+ *   scsi_host_template.queuecommand() function).
+ */
+int srp_reconnect_rport(struct srp_rport *rport)
+{
+       struct Scsi_Host *shost = rport_to_shost(rport);
+       struct srp_internal *i = to_srp_internal(shost->transportt);
+       struct scsi_device *sdev;
+       int res;
+
+       pr_debug("SCSI host %s\n", dev_name(&shost->shost_gendev));
+
+       res = mutex_lock_interruptible(&rport->mutex);
+       if (res)
+               goto out;
+       scsi_target_block(&shost->shost_gendev);
+       while (scsi_request_fn_active(shost))
+               msleep(20);
+       res = i->f->reconnect(rport);
+       pr_debug("%s (state %d): transport.reconnect() returned %d\n",
+                dev_name(&shost->shost_gendev), rport->state, res);
+       if (res == 0) {
+               cancel_delayed_work(&rport->fast_io_fail_work);
+               cancel_delayed_work(&rport->dev_loss_work);
+
+               rport->failed_reconnects = 0;
+               srp_rport_set_state(rport, SRP_RPORT_RUNNING);
+               scsi_target_unblock(&shost->shost_gendev, SDEV_RUNNING);
+               /*
+                * If the SCSI error handler has offlined one or more devices,
+                * invoking scsi_target_unblock() won't change the state of
+                * these devices into running so do that explicitly.
+                */
+               spin_lock_irq(shost->host_lock);
+               __shost_for_each_device(sdev, shost)
+                       if (sdev->sdev_state == SDEV_OFFLINE)
+                               sdev->sdev_state = SDEV_RUNNING;
+               spin_unlock_irq(shost->host_lock);
+       } else if (rport->state == SRP_RPORT_RUNNING) {
+               /*
+                * srp_reconnect_rport() was invoked with fast_io_fail
+                * off. Mark the port as failed and start the TL failure
+                * timers if these had not yet been started.
+                */
+               __rport_fail_io_fast(rport);
+               scsi_target_unblock(&shost->shost_gendev,
+                                   SDEV_TRANSPORT_OFFLINE);
+               __srp_start_tl_fail_timers(rport);
+       } else if (rport->state != SRP_RPORT_BLOCKED) {
+               scsi_target_unblock(&shost->shost_gendev,
+                                   SDEV_TRANSPORT_OFFLINE);
+       }
+       mutex_unlock(&rport->mutex);
+
+out:
+       return res;
+}
+EXPORT_SYMBOL(srp_reconnect_rport);
+
+/**
+ * srp_timed_out() - SRP transport intercept of the SCSI timeout EH
+ *
+ * If a timeout occurs while an rport is in the blocked state, ask the SCSI
+ * EH to continue waiting (BLK_EH_RESET_TIMER). Otherwise let the SCSI core
+ * handle the timeout (BLK_EH_NOT_HANDLED).
+ *
+ * Note: This function is called from soft-IRQ context and with the request
+ * queue lock held.
+ */
+static enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd)
+{
+       struct scsi_device *sdev = scmd->device;
+       struct Scsi_Host *shost = sdev->host;
+       struct srp_internal *i = to_srp_internal(shost->transportt);
+
+       pr_debug("timeout for sdev %s\n", dev_name(&sdev->sdev_gendev));
+       return i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ?
+               BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED;
+}
+
 static void srp_rport_release(struct device *dev)
 {
        struct srp_rport *rport = dev_to_rport(dev);
 
+       cancel_delayed_work_sync(&rport->reconnect_work);
+       cancel_delayed_work_sync(&rport->fast_io_fail_work);
+       cancel_delayed_work_sync(&rport->dev_loss_work);
+
        put_device(dev->parent);
        kfree(rport);
 }
@@ -185,6 +673,24 @@ static int srp_host_match(struct attribute_container *cont, struct device *dev)
 }
 
 /**
+ * srp_rport_get() - increment rport reference count
+ */
+void srp_rport_get(struct srp_rport *rport)
+{
+       get_device(&rport->dev);
+}
+EXPORT_SYMBOL(srp_rport_get);
+
+/**
+ * srp_rport_put() - decrement rport reference count
+ */
+void srp_rport_put(struct srp_rport *rport)
+{
+       put_device(&rport->dev);
+}
+EXPORT_SYMBOL(srp_rport_put);
+
+/**
  * srp_rport_add - add a SRP remote port to the device hierarchy
  * @shost:     scsi host the remote port is connected to.
  * @ids:       The port id for the remote port.
@@ -196,12 +702,15 @@ struct srp_rport *srp_rport_add(struct Scsi_Host *shost,
 {
        struct srp_rport *rport;
        struct device *parent = &shost->shost_gendev;
+       struct srp_internal *i = to_srp_internal(shost->transportt);
        int id, ret;
 
        rport = kzalloc(sizeof(*rport), GFP_KERNEL);
        if (!rport)
                return ERR_PTR(-ENOMEM);
 
+       mutex_init(&rport->mutex);
+
        device_initialize(&rport->dev);
 
        rport->dev.parent = get_device(parent);
@@ -210,6 +719,17 @@ struct srp_rport *srp_rport_add(struct Scsi_Host *shost,
        memcpy(rport->port_id, ids->port_id, sizeof(rport->port_id));
        rport->roles = ids->roles;
 
+       if (i->f->reconnect)
+               rport->reconnect_delay = i->f->reconnect_delay ?
+                       *i->f->reconnect_delay : 10;
+       INIT_DELAYED_WORK(&rport->reconnect_work, srp_reconnect_work);
+       rport->fast_io_fail_tmo = i->f->fast_io_fail_tmo ?
+               *i->f->fast_io_fail_tmo : 15;
+       rport->dev_loss_tmo = i->f->dev_loss_tmo ? *i->f->dev_loss_tmo : 60;
+       INIT_DELAYED_WORK(&rport->fast_io_fail_work,
+                         rport_fast_io_fail_timedout);
+       INIT_DELAYED_WORK(&rport->dev_loss_work, rport_dev_loss_timedout);
+
        id = atomic_inc_return(&to_srp_host_attrs(shost)->next_port_id);
        dev_set_name(&rport->dev, "port-%d:%d", shost->host_no, id);
 
@@ -259,6 +779,13 @@ void srp_rport_del(struct srp_rport *rport)
        transport_remove_device(dev);
        device_del(dev);
        transport_destroy_device(dev);
+
+       mutex_lock(&rport->mutex);
+       if (rport->state == SRP_RPORT_BLOCKED)
+               __rport_fail_io_fast(rport);
+       rport->deleted = true;
+       mutex_unlock(&rport->mutex);
+
        put_device(dev);
 }
 EXPORT_SYMBOL_GPL(srp_rport_del);
@@ -310,6 +837,8 @@ srp_attach_transport(struct srp_function_template *ft)
        if (!i)
                return NULL;
 
+       i->t.eh_timed_out = srp_timed_out;
+
        i->t.tsk_mgmt_response = srp_tsk_mgmt_response;
        i->t.it_nexus_response = srp_it_nexus_response;
 
@@ -327,6 +856,15 @@ srp_attach_transport(struct srp_function_template *ft)
        count = 0;
        i->rport_attrs[count++] = &dev_attr_port_id;
        i->rport_attrs[count++] = &dev_attr_roles;
+       if (ft->has_rport_state) {
+               i->rport_attrs[count++] = &dev_attr_state;
+               i->rport_attrs[count++] = &dev_attr_fast_io_fail_tmo;
+               i->rport_attrs[count++] = &dev_attr_dev_loss_tmo;
+       }
+       if (ft->reconnect) {
+               i->rport_attrs[count++] = &dev_attr_reconnect_delay;
+               i->rport_attrs[count++] = &dev_attr_failed_reconnects;
+       }
        if (ft->rport_delete)
                i->rport_attrs[count++] = &dev_attr_delete;
        i->rport_attrs[count++] = NULL;
index 325c31c..1aa4bef 100644 (file)
@@ -1790,8 +1790,6 @@ static void stex_remove(struct pci_dev *pdev)
 
        scsi_remove_host(hba->host);
 
-       pci_set_drvdata(pdev, NULL);
-
        stex_hba_stop(hba);
 
        stex_hba_free(hba);
index b80bf70..8053695 100644 (file)
@@ -174,7 +174,7 @@ struct sym_slcb {
  */
 struct sym_shcb {
        /*
-        *  Chip and controller indentification.
+        *  Chip and controller identification.
         */
        int             unit;
        char            inst_name[16];
index 1142361..b006cf7 100644 (file)
@@ -2553,7 +2553,6 @@ static void dc390_remove_one(struct pci_dev *dev)
 
        pci_disable_device(dev);
        scsi_host_put(scsi_host);
-       pci_set_drvdata(dev, NULL);
 }
 
 static struct pci_device_id tmscsim_pci_tbl[] = {
index a823cf4..8b95312 100644 (file)
@@ -132,7 +132,6 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
        pm_runtime_forbid(&pdev->dev);
        pm_runtime_get_noresume(&pdev->dev);
        ufshcd_remove(hba);
-       pci_set_drvdata(pdev, NULL);
 }
 
 /**
index 74b88ef..c3173dc 100644 (file)
@@ -224,6 +224,9 @@ static void virtscsi_vq_done(struct virtio_scsi *vscsi,
                virtqueue_disable_cb(vq);
                while ((buf = virtqueue_get_buf(vq, &len)) != NULL)
                        fn(vscsi, buf);
+
+               if (unlikely(virtqueue_is_broken(vq)))
+                       break;
        } while (!virtqueue_enable_cb(vq));
        spin_unlock_irqrestore(&virtscsi_vq->vq_lock, flags);
 }
@@ -710,19 +713,15 @@ static struct scsi_host_template virtscsi_host_template_multi = {
 #define virtscsi_config_get(vdev, fld) \
        ({ \
                typeof(((struct virtio_scsi_config *)0)->fld) __val; \
-               vdev->config->get(vdev, \
-                                 offsetof(struct virtio_scsi_config, fld), \
-                                 &__val, sizeof(__val)); \
+               virtio_cread(vdev, struct virtio_scsi_config, fld, &__val); \
                __val; \
        })
 
 #define virtscsi_config_set(vdev, fld, val) \
-       (void)({ \
+       do { \
                typeof(((struct virtio_scsi_config *)0)->fld) __val = (val); \
-               vdev->config->set(vdev, \
-                                 offsetof(struct virtio_scsi_config, fld), \
-                                 &__val, sizeof(__val)); \
-       })
+               virtio_cwrite(vdev, struct virtio_scsi_config, fld, &__val); \
+       } while(0)
 
 static void __virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity)
 {
@@ -954,7 +953,7 @@ static void virtscsi_remove(struct virtio_device *vdev)
        scsi_host_put(shost);
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int virtscsi_freeze(struct virtio_device *vdev)
 {
        virtscsi_remove_vqs(vdev);
@@ -988,7 +987,7 @@ static struct virtio_driver virtio_scsi_driver = {
        .id_table = id_table,
        .probe = virtscsi_probe,
        .scan = virtscsi_scan,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
        .freeze = virtscsi_freeze,
        .restore = virtscsi_restore,
 #endif
index 3bfaa66..b9755ec 100644 (file)
@@ -1405,7 +1405,6 @@ out_release_resources:
 out_free_host:
        scsi_host_put(host);
 out_disable_device:
-       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
 
        return error;
@@ -1445,7 +1444,6 @@ static void pvscsi_remove(struct pci_dev *pdev)
 
        scsi_host_put(host);
 
-       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
 }
 
index 4c33214..3ed666f 100644 (file)
@@ -217,7 +217,7 @@ static int bcm2835_spi_start_transfer(struct spi_device *spi,
                cs |= spi->chip_select;
        }
 
-       INIT_COMPLETION(bs->done);
+       reinit_completion(&bs->done);
        bs->tx_buf = tfr->tx_buf;
        bs->rx_buf = tfr->rx_buf;
        bs->len = tfr->len;
index e2a5a42..6f03d7e 100644 (file)
@@ -105,7 +105,7 @@ static int spi_clps711x_transfer_one_message(struct spi_master *master,
 
                gpio_set_value(cs, !!(msg->spi->mode & SPI_CS_HIGH));
 
-               INIT_COMPLETION(hw->done);
+               reinit_completion(&hw->done);
 
                hw->count = 0;
                hw->len = xfer->len;
index dd72445..50b2d88 100644 (file)
@@ -554,7 +554,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
        clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK);
        set_io_bits(dspi->base + SPIGCR1, SPIGCR1_SPIENA_MASK);
 
-       INIT_COMPLETION(dspi->done);
+       reinit_completion(&dspi->done);
 
        if (spicfg->io_type == SPI_IO_TYPE_INTR)
                set_io_bits(dspi->base + SPIINT, SPIINT_MASKINT);
index b9f0192..6d207af 100644 (file)
@@ -150,7 +150,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
                                &dws->tx_sgl,
                                1,
                                DMA_MEM_TO_DEV,
-                               DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP);
+                               DMA_PREP_INTERRUPT);
        txdesc->callback = dw_spi_dma_done;
        txdesc->callback_param = dws;
 
@@ -173,7 +173,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
                                &dws->rx_sgl,
                                1,
                                DMA_DEV_TO_MEM,
-                               DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP);
+                               DMA_PREP_INTERRUPT);
        rxdesc->callback = dw_spi_dma_done;
        rxdesc->callback_param = dws;
 
index 32200d4..80d8f40 100644 (file)
@@ -232,7 +232,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t)
        mpc8xxx_spi->tx = t->tx_buf;
        mpc8xxx_spi->rx = t->rx_buf;
 
-       INIT_COMPLETION(mpc8xxx_spi->done);
+       reinit_completion(&mpc8xxx_spi->done);
 
        /* Set SPCOM[CS] and SPCOM[TRANLEN] field */
        if ((t->len - 1) > SPCOM_TRANLEN_MAX) {
index 2129fcd..119f7af 100644 (file)
@@ -339,7 +339,7 @@ static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
        mpc8xxx_spi->tx = t->tx_buf;
        mpc8xxx_spi->rx = t->rx_buf;
 
-       INIT_COMPLETION(mpc8xxx_spi->done);
+       reinit_completion(&mpc8xxx_spi->done);
 
        if (mpc8xxx_spi->flags & SPI_CPM_MODE)
                ret = fsl_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
index 58d5ee0..9602bbd 100644 (file)
@@ -167,7 +167,7 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi,
                        }
 
                        /* have the ISR trigger when the TX FIFO is empty */
-                       INIT_COMPLETION(mps->txisrdone);
+                       reinit_completion(&mps->txisrdone);
                        out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY);
                        out_be32(&fifo->tximr, MPC512x_PSC_FIFO_EMPTY);
                        wait_for_completion(&mps->txisrdone);
index de33305..73afb56 100644 (file)
@@ -202,7 +202,7 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi,
        if (!dma_xfer)
                return -ENOMEM;
 
-       INIT_COMPLETION(spi->c);
+       reinit_completion(&spi->c);
 
        /* Chip select was already programmed into CTRL0 */
        ctrl0 = readl(ssp->base + HW_SSP_CTRL0);
index 9e2020d..4c4b0a1 100644 (file)
@@ -890,7 +890,7 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
        unsigned long flags;
        int use_dma;
 
-       INIT_COMPLETION(sdd->xfer_completion);
+               reinit_completion(&sdd->xfer_completion);
 
        /* Only BPW and Speed may change across transfers */
        bpw = xfer->bits_per_word;
index 2a95435..c74298c 100644 (file)
@@ -465,7 +465,7 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
        ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TXE);
 
        /* start by setting frame bit */
-       INIT_COMPLETION(p->done);
+       reinit_completion(&p->done);
        ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
        if (ret) {
                dev_err(&p->pdev->dev, "failed to start hardware\n");
index 592b4af..ed5e501 100644 (file)
@@ -305,8 +305,8 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
        sspi->tx = t->tx_buf ? t->tx_buf : sspi->dummypage;
        sspi->rx = t->rx_buf ? t->rx_buf : sspi->dummypage;
        sspi->left_tx_word = sspi->left_rx_word = t->len / sspi->word_width;
-       INIT_COMPLETION(sspi->rx_done);
-       INIT_COMPLETION(sspi->tx_done);
+       reinit_completion(&sspi->rx_done);
+       reinit_completion(&sspi->tx_done);
 
        writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS);
 
index 9146bb3..aaecfb3 100644 (file)
@@ -451,7 +451,7 @@ static void tegra_spi_dma_complete(void *args)
 
 static int tegra_spi_start_tx_dma(struct tegra_spi_data *tspi, int len)
 {
-       INIT_COMPLETION(tspi->tx_dma_complete);
+       reinit_completion(&tspi->tx_dma_complete);
        tspi->tx_dma_desc = dmaengine_prep_slave_single(tspi->tx_dma_chan,
                                tspi->tx_dma_phys, len, DMA_MEM_TO_DEV,
                                DMA_PREP_INTERRUPT |  DMA_CTRL_ACK);
@@ -470,7 +470,7 @@ static int tegra_spi_start_tx_dma(struct tegra_spi_data *tspi, int len)
 
 static int tegra_spi_start_rx_dma(struct tegra_spi_data *tspi, int len)
 {
-       INIT_COMPLETION(tspi->rx_dma_complete);
+       reinit_completion(&tspi->rx_dma_complete);
        tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma_chan,
                                tspi->rx_dma_phys, len, DMA_DEV_TO_MEM,
                                DMA_PREP_INTERRUPT |  DMA_CTRL_ACK);
@@ -844,7 +844,7 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
                unsigned long cmd1;
 
-               INIT_COMPLETION(tspi->xfer_completion);
+               reinit_completion(&tspi->xfer_completion);
 
                cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg);
 
index 79be8ce..4dc8e81 100644 (file)
@@ -339,7 +339,7 @@ static int tegra_sflash_transfer_one_message(struct spi_master *master,
        msg->actual_length = 0;
        single_xfer = list_is_singular(&msg->transfers);
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
-               INIT_COMPLETION(tsd->xfer_completion);
+               reinit_completion(&tsd->xfer_completion);
                ret = tegra_sflash_start_transfer_one(spi, xfer,
                                        is_first_msg, single_xfer);
                if (ret < 0) {
index af0a678..e66715b 100644 (file)
@@ -462,7 +462,7 @@ static void tegra_slink_dma_complete(void *args)
 
 static int tegra_slink_start_tx_dma(struct tegra_slink_data *tspi, int len)
 {
-       INIT_COMPLETION(tspi->tx_dma_complete);
+       reinit_completion(&tspi->tx_dma_complete);
        tspi->tx_dma_desc = dmaengine_prep_slave_single(tspi->tx_dma_chan,
                                tspi->tx_dma_phys, len, DMA_MEM_TO_DEV,
                                DMA_PREP_INTERRUPT |  DMA_CTRL_ACK);
@@ -481,7 +481,7 @@ static int tegra_slink_start_tx_dma(struct tegra_slink_data *tspi, int len)
 
 static int tegra_slink_start_rx_dma(struct tegra_slink_data *tspi, int len)
 {
-       INIT_COMPLETION(tspi->rx_dma_complete);
+       reinit_completion(&tspi->rx_dma_complete);
        tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma_chan,
                                tspi->rx_dma_phys, len, DMA_DEV_TO_MEM,
                                DMA_PREP_INTERRUPT |  DMA_CTRL_ACK);
@@ -836,7 +836,7 @@ static int tegra_slink_transfer_one(struct spi_master *master,
        struct tegra_slink_data *tspi = spi_master_get_devdata(master);
        int ret;
 
-       INIT_COMPLETION(tspi->xfer_completion);
+       reinit_completion(&tspi->xfer_completion);
        ret = tegra_slink_start_transfer_one(spi, xfer);
        if (ret < 0) {
                dev_err(tspi->dev,
index ec3a83f..6d4ce46 100644 (file)
@@ -258,7 +258,7 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
        xspi->tx_ptr = t->tx_buf;
        xspi->rx_ptr = t->rx_buf;
        xspi->remaining_bytes = t->len;
-       INIT_COMPLETION(xspi->done);
+       reinit_completion(&xspi->done);
 
 
        /* Enable the transmit empty interrupt, which we use to determine
index 927998a..18cc625 100644 (file)
@@ -357,6 +357,19 @@ struct spi_device *spi_alloc_device(struct spi_master *master)
 }
 EXPORT_SYMBOL_GPL(spi_alloc_device);
 
+static void spi_dev_set_name(struct spi_device *spi)
+{
+       struct acpi_device *adev = ACPI_COMPANION(&spi->dev);
+
+       if (adev) {
+               dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev));
+               return;
+       }
+
+       dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
+                    spi->chip_select);
+}
+
 /**
  * spi_add_device - Add spi_device allocated with spi_alloc_device
  * @spi: spi_device to register
@@ -383,9 +396,7 @@ int spi_add_device(struct spi_device *spi)
        }
 
        /* Set the bus ID string */
-       dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
-                       spi->chip_select);
-
+       spi_dev_set_name(spi);
 
        /* We need to make sure there's no other device with this
         * chipselect **BEFORE** we call setup(), else we'll trash
@@ -571,7 +582,7 @@ static int spi_transfer_one_message(struct spi_master *master,
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
                trace_spi_transfer_start(msg, xfer);
 
-               INIT_COMPLETION(master->xfer_completion);
+               reinit_completion(&master->xfer_completion);
 
                ret = master->transfer_one(master, msg->spi, xfer);
                if (ret < 0) {
@@ -1144,7 +1155,7 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
                return AE_NO_MEMORY;
        }
 
-       ACPI_HANDLE_SET(&spi->dev, handle);
+       ACPI_COMPANION_SET(&spi->dev, adev);
        spi->irq = -1;
 
        INIT_LIST_HEAD(&resource_list);
index 9221a74..93c7299 100644 (file)
@@ -42,7 +42,7 @@ struct ad7606_platform_data {
 
 /**
  * struct ad7606_chip_info - chip specifc information
- * @name:              indentification string for chip
+ * @name:              identification string for chip
  * @int_vref_mv:       the internal reference voltage
  * @channels:          channel specification
  * @num_channels:      number of channels
index aeae76b..e2dd783 100644 (file)
@@ -783,7 +783,7 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
        if (!ret)
                return -EBUSY;
 
-       INIT_COMPLETION(lradc->completion);
+       reinit_completion(&lradc->completion);
 
        /*
         * No buffered operation in progress, map the channel and trigger it.
index 394254f..5032ff7 100644 (file)
@@ -1,6 +1,7 @@
 config DRM_IMX
        tristate "DRM Support for Freescale i.MX"
        select DRM_KMS_HELPER
+       select DRM_KMS_FB_HELPER
        select VIDEOMODE_HELPERS
        select DRM_GEM_CMA_HELPER
        select DRM_KMS_CMA_HELPER
index 3d3a824..51aa977 100644 (file)
@@ -407,14 +407,14 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
 
        /*
         * enable drm irq mode.
-        * - with irq_enabled = 1, we can use the vblank feature.
+        * - with irq_enabled = true, we can use the vblank feature.
         *
         * P.S. note that we wouldn't use drm irq handler but
         *      just specific driver own one instead because
         *      drm framework supports only one irq handler and
         *      drivers can well take care of their interrupts
         */
-       drm->irq_enabled = 1;
+       drm->irq_enabled = true;
 
        drm_mode_config_init(drm);
        imx_drm_mode_config_init(drm);
@@ -434,11 +434,11 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
                goto err_init;
 
        /*
-        * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+        * with vblank_disable_allowed = true, vblank interrupt will be disabled
         * by drm timer once a current process gives up ownership of
         * vblank event.(after drm_vblank_put function is called)
         */
-       imxdrm->drm->vblank_disable_allowed = 1;
+       imxdrm->drm->vblank_disable_allowed = true;
 
        if (!imx_drm_device_get())
                ret = -EINVAL;
index b6cb593..cbea5d8 100644 (file)
@@ -2,6 +2,11 @@
   (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an
   example of a previously completed port).
 
+- lirc_bt829 uses registers on a Mach64 VT, which has a separate kernel
+  framebuffer driver (atyfb) and userland X driver (mach64).  It can't
+  simply be converted to a normal PCI driver, but ideally it should be
+  coordinated with the other drivers.
+
 Please send patches to:
 Jarod Wilson <jarod@wilsonet.com>
 Greg Kroah-Hartman <greg@kroah.com>
index fbbdce4..30edc74 100644 (file)
@@ -63,7 +63,7 @@ static bool debug;
        } while (0)
 
 static int atir_minor;
-static unsigned long pci_addr_phys;
+static phys_addr_t pci_addr_phys;
 static unsigned char *pci_addr_lin;
 
 static struct lirc_driver atir_driver;
@@ -78,11 +78,11 @@ static struct pci_dev *do_pci_probe(void)
                pci_addr_phys = 0;
                if (my_dev->resource[0].flags & IORESOURCE_MEM) {
                        pci_addr_phys = my_dev->resource[0].start;
-                       pr_info("memory at 0x%08X\n",
-                              (unsigned int)pci_addr_phys);
+                       pr_info("memory at %pa\n", &pci_addr_phys);
                }
                if (pci_addr_phys == 0) {
                        pr_err("no memory resource ?\n");
+                       pci_dev_put(my_dev);
                        return NULL;
                }
        } else {
@@ -120,13 +120,20 @@ static void atir_set_use_dec(void *data)
 int init_module(void)
 {
        struct pci_dev *pdev;
+       int rc;
 
        pdev = do_pci_probe();
        if (pdev == NULL)
                return -ENODEV;
 
-       if (!atir_init_start())
-               return -ENODEV;
+       rc = pci_enable_device(pdev);
+       if (rc)
+               goto err_put_dev;
+
+       if (!atir_init_start()) {
+               rc = -ENODEV;
+               goto err_disable;
+       }
 
        strcpy(atir_driver.name, "ATIR");
        atir_driver.minor       = -1;
@@ -142,17 +149,31 @@ int init_module(void)
        atir_minor = lirc_register_driver(&atir_driver);
        if (atir_minor < 0) {
                pr_err("failed to register driver!\n");
-               return atir_minor;
+               rc = atir_minor;
+               goto err_unmap;
        }
        dprintk("driver is registered on minor %d\n", atir_minor);
 
        return 0;
+
+err_unmap:
+       iounmap(pci_addr_lin);
+err_disable:
+       pci_disable_device(pdev);
+err_put_dev:
+       pci_dev_put(pdev);
+       return rc;
 }
 
 
 void cleanup_module(void)
 {
+       struct pci_dev *pdev = to_pci_dev(atir_driver.dev);
+
        lirc_unregister_driver(atir_minor);
+       iounmap(pci_addr_lin);
+       pci_disable_device(pdev);
+       pci_dev_put(pdev);
 }
 
 
index f6bc4c9..2e3a985 100644 (file)
@@ -707,7 +707,8 @@ static irqreturn_t irq_handler(int i, void *blah)
                                pr_warn("ignoring spike: %d %d %lx %lx %lx %lx\n",
                                        dcd, sense,
                                        tv.tv_sec, lasttv.tv_sec,
-                                       tv.tv_usec, lasttv.tv_usec);
+                                       (unsigned long)tv.tv_usec,
+                                       (unsigned long)lasttv.tv_usec);
                                continue;
                        }
 
@@ -719,7 +720,8 @@ static irqreturn_t irq_handler(int i, void *blah)
                                pr_warn("%d %d %lx %lx %lx %lx\n",
                                        dcd, sense,
                                        tv.tv_sec, lasttv.tv_sec,
-                                       tv.tv_usec, lasttv.tv_usec);
+                                       (unsigned long)tv.tv_usec,
+                                       (unsigned long)lasttv.tv_usec);
                                data = PULSE_MASK;
                        } else if (deltv > 15) {
                                data = PULSE_MASK; /* really long time */
@@ -728,7 +730,8 @@ static irqreturn_t irq_handler(int i, void *blah)
                                        pr_warn("AIEEEE: %d %d %lx %lx %lx %lx\n",
                                                dcd, sense,
                                                tv.tv_sec, lasttv.tv_sec,
-                                               tv.tv_usec, lasttv.tv_usec);
+                                               (unsigned long)tv.tv_usec,
+                                               (unsigned long)lasttv.tv_usec);
                                        /*
                                         * detecting pulse while this
                                         * MUST be a space!
index 11d5338..0feeaad 100644 (file)
@@ -61,6 +61,9 @@
 #include <media/lirc_dev.h>
 #include <media/lirc.h>
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 struct IR;
 
 struct IR_rx {
@@ -941,7 +944,14 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
                        schedule();
                        set_current_state(TASK_INTERRUPTIBLE);
                } else {
-                       unsigned char buf[rbuf->chunk_size];
+                       unsigned char buf[MAX_XFER_SIZE];
+
+                       if (rbuf->chunk_size > sizeof(buf)) {
+                               zilog_error("chunk_size is too big (%d)!\n",
+                                           rbuf->chunk_size);
+                               ret = -EINVAL;
+                               break;
+                       }
                        m = lirc_buffer_read(rbuf, buf);
                        if (m == rbuf->chunk_size) {
                                ret = copy_to_user((void *)outbuf+written, buf,
index 76d5bbd..0c349c8 100644 (file)
@@ -1,4 +1,5 @@
 config USB_MSI3101
        tristate "Mirics MSi3101 SDR Dongle"
        depends on USB && VIDEO_DEV && VIDEO_V4L2
-        select VIDEOBUF2_VMALLOC
+       select VIDEOBUF2_CORE
+       select VIDEOBUF2_VMALLOC
index 32d9953..145295a 100644 (file)
@@ -176,18 +176,27 @@ static void solo_vout_config(struct solo_dev *solo_dev)
 static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off,
                               u16 val, int reg_size)
 {
-       u16 buf[64];
-       int i;
-       int ret = 0;
+       u16 *buf;
+       const int n = 64, size = n * sizeof(*buf);
+       int i, ret = 0;
+
+       buf = kmalloc(size, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
 
-       for (i = 0; i < sizeof(buf) >> 1; i++)
+       for (i = 0; i < n; i++)
                buf[i] = cpu_to_le16(val);
 
-       for (i = 0; i < reg_size; i += sizeof(buf))
-               ret |= solo_p2m_dma(solo_dev, 1, buf,
-                                   SOLO_MOTION_EXT_ADDR(solo_dev) + off + i,
-                                   sizeof(buf), 0, 0);
+       for (i = 0; i < reg_size; i += size) {
+               ret = solo_p2m_dma(solo_dev, 1, buf,
+                                  SOLO_MOTION_EXT_ADDR(solo_dev) + off + i,
+                                  size, 0, 0);
+
+               if (ret)
+                       break;
+       }
 
+       kfree(buf);
        return ret;
 }
 
index 3335941..7f2f247 100644 (file)
@@ -87,7 +87,7 @@ int solo_p2m_dma_desc(struct solo_dev *solo_dev,
        if (mutex_lock_interruptible(&p2m_dev->mutex))
                return -EINTR;
 
-       INIT_COMPLETION(p2m_dev->completion);
+       reinit_completion(&p2m_dev->completion);
        p2m_dev->error = 0;
 
        if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && desc_mode) {
index a4c5896..d582c5b 100644 (file)
@@ -95,38 +95,11 @@ static unsigned char vop_6110_pal_cif[] = {
        0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00,
 };
 
-struct vop_header {
-       /* VE_STATUS0 */
-       u32 mpeg_size:20, sad_motion_flag:1, video_motion_flag:1, vop_type:2,
-               channel:5, source_fl:1, interlace:1, progressive:1;
-
-       /* VE_STATUS1 */
-       u32 vsize:8, hsize:8, last_queue:4, nop0:8, scale:4;
-
-       /* VE_STATUS2 */
-       u32 mpeg_off;
-
-       /* VE_STATUS3 */
-       u32 jpeg_off;
-
-       /* VE_STATUS4 */
-       u32 jpeg_size:20, interval:10, nop1:2;
-
-       /* VE_STATUS5/6 */
-       u32 sec, usec;
-
-       /* VE_STATUS7/8/9 */
-       u32 nop2[3];
-
-       /* VE_STATUS10 */
-       u32 mpeg_size_alt:20, nop3:12;
-
-       u32 end_nops[5];
-} __packed;
+typedef __le32 vop_header[16];
 
 struct solo_enc_buf {
        enum solo_enc_types     type;
-       struct vop_header       *vh;
+       const vop_header        *vh;
        int                     motion;
 };
 
@@ -346,7 +319,7 @@ static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma,
 /* Build a descriptor queue out of an SG list and send it to the P2M for
  * processing. */
 static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip,
-                         struct vb2_dma_sg_desc *vbuf, int off, int size,
+                         struct sg_table *vbuf, int off, int size,
                          unsigned int base, unsigned int base_size)
 {
        struct solo_dev *solo_dev = solo_enc->solo_dev;
@@ -359,7 +332,7 @@ static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip,
 
        solo_enc->desc_count = 1;
 
-       for_each_sg(vbuf->sglist, sg, vbuf->num_pages, i) {
+       for_each_sg(vbuf->sgl, sg, vbuf->nents, i) {
                struct solo_p2m_desc *desc;
                dma_addr_t dma;
                int len;
@@ -430,84 +403,145 @@ static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip,
                                 solo_enc->desc_count - 1);
 }
 
+/* Extract values from VOP header - VE_STATUSxx */
+static inline int vop_interlaced(const vop_header *vh)
+{
+       return (__le32_to_cpu((*vh)[0]) >> 30) & 1;
+}
+
+static inline u8 vop_channel(const vop_header *vh)
+{
+       return (__le32_to_cpu((*vh)[0]) >> 24) & 0x1F;
+}
+
+static inline u8 vop_type(const vop_header *vh)
+{
+       return (__le32_to_cpu((*vh)[0]) >> 22) & 3;
+}
+
+static inline u32 vop_mpeg_size(const vop_header *vh)
+{
+       return __le32_to_cpu((*vh)[0]) & 0xFFFFF;
+}
+
+static inline u8 vop_hsize(const vop_header *vh)
+{
+       return (__le32_to_cpu((*vh)[1]) >> 8) & 0xFF;
+}
+
+static inline u8 vop_vsize(const vop_header *vh)
+{
+       return __le32_to_cpu((*vh)[1]) & 0xFF;
+}
+
+static inline u32 vop_mpeg_offset(const vop_header *vh)
+{
+       return __le32_to_cpu((*vh)[2]);
+}
+
+static inline u32 vop_jpeg_offset(const vop_header *vh)
+{
+       return __le32_to_cpu((*vh)[3]);
+}
+
+static inline u32 vop_jpeg_size(const vop_header *vh)
+{
+       return __le32_to_cpu((*vh)[4]) & 0xFFFFF;
+}
+
+static inline u32 vop_sec(const vop_header *vh)
+{
+       return __le32_to_cpu((*vh)[5]);
+}
+
+static inline u32 vop_usec(const vop_header *vh)
+{
+       return __le32_to_cpu((*vh)[6]);
+}
+
 static int solo_fill_jpeg(struct solo_enc_dev *solo_enc,
-               struct vb2_buffer *vb, struct vop_header *vh)
+                         struct vb2_buffer *vb, const vop_header *vh)
 {
        struct solo_dev *solo_dev = solo_enc->solo_dev;
-       struct vb2_dma_sg_desc *vbuf = vb2_dma_sg_plane_desc(vb, 0);
+       struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0);
        int frame_size;
        int ret;
 
        vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
 
-       if (vb2_plane_size(vb, 0) < vh->jpeg_size + solo_enc->jpeg_len)
+       if (vb2_plane_size(vb, 0) < vop_jpeg_size(vh) + solo_enc->jpeg_len)
                return -EIO;
 
-       sg_copy_from_buffer(vbuf->sglist, vbuf->num_pages,
-                       solo_enc->jpeg_header,
-                       solo_enc->jpeg_len);
-
-       frame_size = (vh->jpeg_size + solo_enc->jpeg_len + (DMA_ALIGN - 1))
+       frame_size = (vop_jpeg_size(vh) + solo_enc->jpeg_len + (DMA_ALIGN - 1))
                & ~(DMA_ALIGN - 1);
-       vb2_set_plane_payload(vb, 0, vh->jpeg_size + solo_enc->jpeg_len);
+       vb2_set_plane_payload(vb, 0, vop_jpeg_size(vh) + solo_enc->jpeg_len);
 
-       dma_map_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages,
+       /* may discard all previous data in vbuf->sgl */
+       dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents,
                        DMA_FROM_DEVICE);
-       ret = solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, vh->jpeg_off,
-                       frame_size, SOLO_JPEG_EXT_ADDR(solo_dev),
-                       SOLO_JPEG_EXT_SIZE(solo_dev));
-       dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages,
+       ret = solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf,
+                            vop_jpeg_offset(vh) - SOLO_JPEG_EXT_ADDR(solo_dev),
+                            frame_size, SOLO_JPEG_EXT_ADDR(solo_dev),
+                            SOLO_JPEG_EXT_SIZE(solo_dev));
+       dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents,
                        DMA_FROM_DEVICE);
+
+       /* add the header only after dma_unmap_sg() */
+       sg_copy_from_buffer(vbuf->sgl, vbuf->nents,
+                           solo_enc->jpeg_header, solo_enc->jpeg_len);
+
        return ret;
 }
 
 static int solo_fill_mpeg(struct solo_enc_dev *solo_enc,
-               struct vb2_buffer *vb, struct vop_header *vh)
+               struct vb2_buffer *vb, const vop_header *vh)
 {
        struct solo_dev *solo_dev = solo_enc->solo_dev;
-       struct vb2_dma_sg_desc *vbuf = vb2_dma_sg_plane_desc(vb, 0);
+       struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0);
        int frame_off, frame_size;
        int skip = 0;
        int ret;
 
-       if (vb2_plane_size(vb, 0) < vh->mpeg_size)
+       if (vb2_plane_size(vb, 0) < vop_mpeg_size(vh))
                return -EIO;
 
        /* If this is a key frame, add extra header */
-       if (!vh->vop_type) {
-               sg_copy_from_buffer(vbuf->sglist, vbuf->num_pages,
-                               solo_enc->vop,
-                               solo_enc->vop_len);
-
+       vb->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME);
+       if (!vop_type(vh)) {
                skip = solo_enc->vop_len;
-
                vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
-               vb2_set_plane_payload(vb, 0, vh->mpeg_size + solo_enc->vop_len);
+               vb2_set_plane_payload(vb, 0, vop_mpeg_size(vh) + solo_enc->vop_len);
        } else {
                vb->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
-               vb2_set_plane_payload(vb, 0, vh->mpeg_size);
+               vb2_set_plane_payload(vb, 0, vop_mpeg_size(vh));
        }
 
        /* Now get the actual mpeg payload */
-       frame_off = (vh->mpeg_off + sizeof(*vh))
+       frame_off = (vop_mpeg_offset(vh) - SOLO_MP4E_EXT_ADDR(solo_dev) + sizeof(*vh))
                % SOLO_MP4E_EXT_SIZE(solo_dev);
-       frame_size = (vh->mpeg_size + skip + (DMA_ALIGN - 1))
+       frame_size = (vop_mpeg_size(vh) + skip + (DMA_ALIGN - 1))
                & ~(DMA_ALIGN - 1);
 
-       dma_map_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages,
+       /* may discard all previous data in vbuf->sgl */
+       dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents,
                        DMA_FROM_DEVICE);
        ret = solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size,
                        SOLO_MP4E_EXT_ADDR(solo_dev),
                        SOLO_MP4E_EXT_SIZE(solo_dev));
-       dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages,
+       dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents,
                        DMA_FROM_DEVICE);
+
+       /* add the header only after dma_unmap_sg() */
+       if (!vop_type(vh))
+               sg_copy_from_buffer(vbuf->sgl, vbuf->nents,
+                                   solo_enc->vop, solo_enc->vop_len);
        return ret;
 }
 
 static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc,
                            struct vb2_buffer *vb, struct solo_enc_buf *enc_buf)
 {
-       struct vop_header *vh = enc_buf->vh;
+       const vop_header *vh = enc_buf->vh;
        int ret;
 
        /* Check for motion flags */
@@ -531,8 +565,8 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc,
 
        if (!ret) {
                vb->v4l2_buf.sequence = solo_enc->sequence++;
-               vb->v4l2_buf.timestamp.tv_sec = vh->sec;
-               vb->v4l2_buf.timestamp.tv_usec = vh->usec;
+               vb->v4l2_buf.timestamp.tv_sec = vop_sec(vh);
+               vb->v4l2_buf.timestamp.tv_usec = vop_usec(vh);
        }
 
        vb2_buffer_done(vb, ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
@@ -605,15 +639,13 @@ static void solo_handle_ring(struct solo_dev *solo_dev)
 
                /* FAIL... */
                if (enc_get_mpeg_dma(solo_dev, solo_dev->vh_dma, off,
-                                    sizeof(struct vop_header)))
+                                    sizeof(vop_header)))
                        continue;
 
-               enc_buf.vh = (struct vop_header *)solo_dev->vh_buf;
-               enc_buf.vh->mpeg_off -= SOLO_MP4E_EXT_ADDR(solo_dev);
-               enc_buf.vh->jpeg_off -= SOLO_JPEG_EXT_ADDR(solo_dev);
+               enc_buf.vh = solo_dev->vh_buf;
 
                /* Sanity check */
-               if (enc_buf.vh->mpeg_off != off)
+               if (vop_mpeg_offset(enc_buf.vh) != SOLO_MP4E_EXT_ADDR(solo_dev) + off)
                        continue;
 
                if (solo_motion_detected(solo_enc))
@@ -1329,7 +1361,7 @@ int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
 
        init_waitqueue_head(&solo_dev->ring_thread_wait);
 
-       solo_dev->vh_size = sizeof(struct vop_header);
+       solo_dev->vh_size = sizeof(vop_header);
        solo_dev->vh_buf = pci_alloc_consistent(solo_dev->pdev,
                                                solo_dev->vh_size,
                                                &solo_dev->vh_dma);
index 6f91d2e..f1bbb8c 100644 (file)
@@ -94,7 +94,6 @@
 #define SOLO_ENC_MODE_HD1              1
 #define SOLO_ENC_MODE_D1               9
 
-#define SOLO_DEFAULT_GOP               30
 #define SOLO_DEFAULT_QP                        3
 
 #ifndef V4L2_BUF_FLAG_MOTION_ON
index 7bb550a..743ff09 100644 (file)
@@ -72,7 +72,7 @@ int sync_wait_on_multiple_events(struct sync_object **events,
        spin_lock_bh(&sync_lock);
        for (i = 0; i < count; i++) {
                if (completion_done(&events[i]->comp)) {
-                       INIT_COMPLETION(events[i]->comp);
+                       reinit_completion(&events[i]->comp);
                        *index = i;
                        spin_unlock_bh(&sync_lock);
                        status = 0;
@@ -92,7 +92,7 @@ int sync_wait_on_multiple_events(struct sync_object **events,
        spin_lock_bh(&sync_lock);
        for (i = 0; i < count; i++) {
                if (completion_done(&events[i]->comp)) {
-                       INIT_COMPLETION(events[i]->comp);
+                       reinit_completion(&events[i]->comp);
                        *index = i;
                        status = 0;
                }
index 58a0d5c..fc19b97 100644 (file)
@@ -59,7 +59,7 @@ static inline void sync_init_event(struct sync_object *event)
 
 static inline void sync_reset_event(struct sync_object *event)
 {
-       INIT_COMPLETION(event->comp);
+       reinit_completion(&event->comp);
        event->multi_comp = NULL;
 }
 
index 6d04eb4..1aa4a3f 100644 (file)
@@ -332,7 +332,7 @@ static void bridge_recover(struct work_struct *work)
        struct dev_object *dev;
        struct cfg_devnode *dev_node;
        if (atomic_read(&bridge_cref)) {
-               INIT_COMPLETION(bridge_comp);
+               reinit_completion(&bridge_comp);
                while (!wait_for_completion_timeout(&bridge_comp,
                                                msecs_to_jiffies(REC_TIMEOUT)))
                        pr_info("%s:%d handle(s) still opened\n",
@@ -348,7 +348,7 @@ static void bridge_recover(struct work_struct *work)
 
 void bridge_recover_schedule(void)
 {
-       INIT_COMPLETION(bridge_open_comp);
+       reinit_completion(&bridge_open_comp);
        recover = true;
        queue_work(bridge_rec_queue, &bridge_recovery_work);
 }
@@ -389,7 +389,7 @@ static int omap3_bridge_startup(struct platform_device *pdev)
 #ifdef CONFIG_TIDSPBRIDGE_RECOVERY
        bridge_rec_queue = create_workqueue("bridge_rec_queue");
        INIT_WORK(&bridge_recovery_work, bridge_recover);
-       INIT_COMPLETION(bridge_comp);
+       reinit_completion(&bridge_comp);
 #endif
 
 #ifdef CONFIG_PM
index 38e44b9..d70e911 100644 (file)
@@ -805,14 +805,7 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
        int iscsi_task_attr;
        int sam_task_attr;
 
-       spin_lock_bh(&conn->sess->session_stats_lock);
-       conn->sess->cmd_pdus++;
-       if (conn->sess->se_sess->se_node_acl) {
-               spin_lock(&conn->sess->se_sess->se_node_acl->stats_lock);
-               conn->sess->se_sess->se_node_acl->num_cmds++;
-               spin_unlock(&conn->sess->se_sess->se_node_acl->stats_lock);
-       }
-       spin_unlock_bh(&conn->sess->session_stats_lock);
+       atomic_long_inc(&conn->sess->cmd_pdus);
 
        hdr                     = (struct iscsi_scsi_req *) buf;
        payload_length          = ntoh24(hdr->dlength);
@@ -1254,20 +1247,12 @@ iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf,
        int rc;
 
        if (!payload_length) {
-               pr_err("DataOUT payload is ZERO, protocol error.\n");
-               return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
-                                        buf);
+               pr_warn("DataOUT payload is ZERO, ignoring.\n");
+               return 0;
        }
 
        /* iSCSI write */
-       spin_lock_bh(&conn->sess->session_stats_lock);
-       conn->sess->rx_data_octets += payload_length;
-       if (conn->sess->se_sess->se_node_acl) {
-               spin_lock(&conn->sess->se_sess->se_node_acl->stats_lock);
-               conn->sess->se_sess->se_node_acl->write_bytes += payload_length;
-               spin_unlock(&conn->sess->se_sess->se_node_acl->stats_lock);
-       }
-       spin_unlock_bh(&conn->sess->session_stats_lock);
+       atomic_long_add(payload_length, &conn->sess->rx_data_octets);
 
        if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
                pr_err("DataSegmentLength: %u is greater than"
@@ -1486,7 +1471,7 @@ EXPORT_SYMBOL(iscsit_check_dataout_payload);
 
 static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf)
 {
-       struct iscsi_cmd *cmd;
+       struct iscsi_cmd *cmd = NULL;
        struct iscsi_data *hdr = (struct iscsi_data *)buf;
        int rc;
        bool data_crc_failed = false;
@@ -1954,6 +1939,13 @@ iscsit_setup_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
                                         (unsigned char *)hdr);
        }
 
+       if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL) ||
+            (hdr->flags & ISCSI_FLAG_TEXT_CONTINUE)) {
+               pr_err("Multi sequence text commands currently not supported\n");
+               return iscsit_reject_cmd(cmd, ISCSI_REASON_CMD_NOT_SUPPORTED,
+                                       (unsigned char *)hdr);
+       }
+
        pr_debug("Got Text Request: ITT: 0x%08x, CmdSN: 0x%08x,"
                " ExpStatSN: 0x%08x, Length: %u\n", hdr->itt, hdr->cmdsn,
                hdr->exp_statsn, payload_length);
@@ -2630,14 +2622,7 @@ static int iscsit_send_datain(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
                return -1;
        }
 
-       spin_lock_bh(&conn->sess->session_stats_lock);
-       conn->sess->tx_data_octets += datain.length;
-       if (conn->sess->se_sess->se_node_acl) {
-               spin_lock(&conn->sess->se_sess->se_node_acl->stats_lock);
-               conn->sess->se_sess->se_node_acl->read_bytes += datain.length;
-               spin_unlock(&conn->sess->se_sess->se_node_acl->stats_lock);
-       }
-       spin_unlock_bh(&conn->sess->session_stats_lock);
+       atomic_long_add(datain.length, &conn->sess->tx_data_octets);
        /*
         * Special case for successfully execution w/ both DATAIN
         * and Sense Data.
@@ -3162,9 +3147,7 @@ void iscsit_build_rsp_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
        if (inc_stat_sn)
                cmd->stat_sn = conn->stat_sn++;
 
-       spin_lock_bh(&conn->sess->session_stats_lock);
-       conn->sess->rsp_pdus++;
-       spin_unlock_bh(&conn->sess->session_stats_lock);
+       atomic_long_inc(&conn->sess->rsp_pdus);
 
        memset(hdr, 0, ISCSI_HDR_LEN);
        hdr->opcode             = ISCSI_OP_SCSI_CMD_RSP;
@@ -3374,6 +3357,7 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
        struct iscsi_tiqn *tiqn;
        struct iscsi_tpg_np *tpg_np;
        int buffer_len, end_of_buf = 0, len = 0, payload_len = 0;
+       int target_name_printed;
        unsigned char buf[ISCSI_IQN_LEN+12]; /* iqn + "TargetName=" + \0 */
        unsigned char *text_in = cmd->text_in_ptr, *text_ptr = NULL;
 
@@ -3411,19 +3395,23 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
                        continue;
                }
 
-               len = sprintf(buf, "TargetName=%s", tiqn->tiqn);
-               len += 1;
-
-               if ((len + payload_len) > buffer_len) {
-                       end_of_buf = 1;
-                       goto eob;
-               }
-               memcpy(payload + payload_len, buf, len);
-               payload_len += len;
+               target_name_printed = 0;
 
                spin_lock(&tiqn->tiqn_tpg_lock);
                list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) {
 
+                       /* If demo_mode_discovery=0 and generate_node_acls=0
+                        * (demo mode dislabed) do not return
+                        * TargetName+TargetAddress unless a NodeACL exists.
+                        */
+
+                       if ((tpg->tpg_attrib.generate_node_acls == 0) &&
+                           (tpg->tpg_attrib.demo_mode_discovery == 0) &&
+                           (!core_tpg_get_initiator_node_acl(&tpg->tpg_se_tpg,
+                               cmd->conn->sess->sess_ops->InitiatorName))) {
+                               continue;
+                       }
+
                        spin_lock(&tpg->tpg_state_lock);
                        if ((tpg->tpg_state == TPG_STATE_FREE) ||
                            (tpg->tpg_state == TPG_STATE_INACTIVE)) {
@@ -3438,6 +3426,22 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
                                struct iscsi_np *np = tpg_np->tpg_np;
                                bool inaddr_any = iscsit_check_inaddr_any(np);
 
+                               if (!target_name_printed) {
+                                       len = sprintf(buf, "TargetName=%s",
+                                                     tiqn->tiqn);
+                                       len += 1;
+
+                                       if ((len + payload_len) > buffer_len) {
+                                               spin_unlock(&tpg->tpg_np_lock);
+                                               spin_unlock(&tiqn->tiqn_tpg_lock);
+                                               end_of_buf = 1;
+                                               goto eob;
+                                       }
+                                       memcpy(payload + payload_len, buf, len);
+                                       payload_len += len;
+                                       target_name_printed = 1;
+                               }
+
                                len = sprintf(buf, "TargetAddress="
                                        "%s:%hu,%hu",
                                        (inaddr_any == false) ?
@@ -4092,9 +4096,7 @@ restart:
                                 * hit default in the switch below.
                                 */
                                memset(buffer, 0xff, ISCSI_HDR_LEN);
-                               spin_lock_bh(&conn->sess->session_stats_lock);
-                               conn->sess->conn_digest_errors++;
-                               spin_unlock_bh(&conn->sess->session_stats_lock);
+                               atomic_long_inc(&conn->sess->conn_digest_errors);
                        } else {
                                pr_debug("Got HeaderDigest CRC32C"
                                                " 0x%08x\n", checksum);
@@ -4381,7 +4383,7 @@ int iscsit_close_connection(
 
 int iscsit_close_session(struct iscsi_session *sess)
 {
-       struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess);
+       struct iscsi_portal_group *tpg = sess->tpg;
        struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
 
        if (atomic_read(&sess->nconn)) {
index 7505fdd..de77d9a 100644 (file)
@@ -111,7 +111,7 @@ static struct iscsi_chap *chap_server_open(
        /*
         * Set Identifier.
         */
-       chap->id = ISCSI_TPG_C(conn)->tpg_chap_id++;
+       chap->id = conn->tpg->tpg_chap_id++;
        *aic_len += sprintf(aic_str + *aic_len, "CHAP_I=%d", chap->id);
        *aic_len += 1;
        pr_debug("[server] Sending CHAP_I=%d\n", chap->id);
@@ -146,6 +146,7 @@ static int chap_server_compute_md5(
        unsigned char client_digest[MD5_SIGNATURE_SIZE];
        unsigned char server_digest[MD5_SIGNATURE_SIZE];
        unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH];
+       size_t compare_len;
        struct iscsi_chap *chap = conn->auth_protocol;
        struct crypto_hash *tfm;
        struct hash_desc desc;
@@ -184,7 +185,9 @@ static int chap_server_compute_md5(
                goto out;
        }
 
-       if (memcmp(chap_n, auth->userid, strlen(auth->userid)) != 0) {
+       /* Include the terminating NULL in the compare */
+       compare_len = strlen(auth->userid) + 1;
+       if (strncmp(chap_n, auth->userid, compare_len) != 0) {
                pr_err("CHAP_N values do not match!\n");
                goto out;
        }
index fd14525..e3318ed 100644 (file)
@@ -372,7 +372,7 @@ static ssize_t iscsi_nacl_attrib_show_##name(                               \
        struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \
                                        se_node_acl);                   \
                                                                        \
-       return sprintf(page, "%u\n", ISCSI_NODE_ATTRIB(nacl)->name);    \
+       return sprintf(page, "%u\n", nacl->node_attrib.name);           \
 }                                                                      \
                                                                        \
 static ssize_t iscsi_nacl_attrib_store_##name(                         \
@@ -897,7 +897,7 @@ static struct se_node_acl *lio_target_make_nodeacl(
        if (!se_nacl_new)
                return ERR_PTR(-ENOMEM);
 
-       cmdsn_depth = ISCSI_TPG_ATTRIB(tpg)->default_cmdsn_depth;
+       cmdsn_depth = tpg->tpg_attrib.default_cmdsn_depth;
        /*
         * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
         * when converting a NdoeACL from demo mode -> explict
@@ -920,9 +920,9 @@ static struct se_node_acl *lio_target_make_nodeacl(
                return ERR_PTR(-ENOMEM);
        }
 
-       stats_cg->default_groups[0] = &NODE_STAT_GRPS(acl)->iscsi_sess_stats_group;
+       stats_cg->default_groups[0] = &acl->node_stat_grps.iscsi_sess_stats_group;
        stats_cg->default_groups[1] = NULL;
-       config_group_init_type_name(&NODE_STAT_GRPS(acl)->iscsi_sess_stats_group,
+       config_group_init_type_name(&acl->node_stat_grps.iscsi_sess_stats_group,
                        "iscsi_sess_stats", &iscsi_stat_sess_cit);
 
        return se_nacl;
@@ -967,7 +967,7 @@ static ssize_t iscsi_tpg_attrib_show_##name(                                \
        if (iscsit_get_tpg(tpg) < 0)                                    \
                return -EINVAL;                                         \
                                                                        \
-       rb = sprintf(page, "%u\n", ISCSI_TPG_ATTRIB(tpg)->name);        \
+       rb = sprintf(page, "%u\n", tpg->tpg_attrib.name);               \
        iscsit_put_tpg(tpg);                                            \
        return rb;                                                      \
 }                                                                      \
@@ -1041,6 +1041,16 @@ TPG_ATTR(demo_mode_write_protect, S_IRUGO | S_IWUSR);
  */
 DEF_TPG_ATTRIB(prod_mode_write_protect);
 TPG_ATTR(prod_mode_write_protect, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_demo_mode_discovery,
+ */
+DEF_TPG_ATTRIB(demo_mode_discovery);
+TPG_ATTR(demo_mode_discovery, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_default_erl
+ */
+DEF_TPG_ATTRIB(default_erl);
+TPG_ATTR(default_erl, S_IRUGO | S_IWUSR);
 
 static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = {
        &iscsi_tpg_attrib_authentication.attr,
@@ -1051,6 +1061,8 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = {
        &iscsi_tpg_attrib_cache_dynamic_acls.attr,
        &iscsi_tpg_attrib_demo_mode_write_protect.attr,
        &iscsi_tpg_attrib_prod_mode_write_protect.attr,
+       &iscsi_tpg_attrib_demo_mode_discovery.attr,
+       &iscsi_tpg_attrib_default_erl.attr,
        NULL,
 };
 
@@ -1514,21 +1526,21 @@ static struct se_wwn *lio_target_call_coreaddtiqn(
                return ERR_PTR(-ENOMEM);
        }
 
-       stats_cg->default_groups[0] = &WWN_STAT_GRPS(tiqn)->iscsi_instance_group;
-       stats_cg->default_groups[1] = &WWN_STAT_GRPS(tiqn)->iscsi_sess_err_group;
-       stats_cg->default_groups[2] = &WWN_STAT_GRPS(tiqn)->iscsi_tgt_attr_group;
-       stats_cg->default_groups[3] = &WWN_STAT_GRPS(tiqn)->iscsi_login_stats_group;
-       stats_cg->default_groups[4] = &WWN_STAT_GRPS(tiqn)->iscsi_logout_stats_group;
+       stats_cg->default_groups[0] = &tiqn->tiqn_stat_grps.iscsi_instance_group;
+       stats_cg->default_groups[1] = &tiqn->tiqn_stat_grps.iscsi_sess_err_group;
+       stats_cg->default_groups[2] = &tiqn->tiqn_stat_grps.iscsi_tgt_attr_group;
+       stats_cg->default_groups[3] = &tiqn->tiqn_stat_grps.iscsi_login_stats_group;
+       stats_cg->default_groups[4] = &tiqn->tiqn_stat_grps.iscsi_logout_stats_group;
        stats_cg->default_groups[5] = NULL;
-       config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_instance_group,
+       config_group_init_type_name(&tiqn->tiqn_stat_grps.iscsi_instance_group,
                        "iscsi_instance", &iscsi_stat_instance_cit);
-       config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_sess_err_group,
+       config_group_init_type_name(&tiqn->tiqn_stat_grps.iscsi_sess_err_group,
                        "iscsi_sess_err", &iscsi_stat_sess_err_cit);
-       config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_tgt_attr_group,
+       config_group_init_type_name(&tiqn->tiqn_stat_grps.iscsi_tgt_attr_group,
                        "iscsi_tgt_attr", &iscsi_stat_tgt_attr_cit);
-       config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_login_stats_group,
+       config_group_init_type_name(&tiqn->tiqn_stat_grps.iscsi_login_stats_group,
                        "iscsi_login_stats", &iscsi_stat_login_cit);
-       config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_logout_stats_group,
+       config_group_init_type_name(&tiqn->tiqn_stat_grps.iscsi_logout_stats_group,
                        "iscsi_logout_stats", &iscsi_stat_logout_cit);
 
        pr_debug("LIO_Target_ConfigFS: REGISTER -> %s\n", tiqn->tiqn);
@@ -1784,6 +1796,11 @@ static int lio_queue_status(struct se_cmd *se_cmd)
        struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
 
        cmd->i_state = ISTATE_SEND_STATUS;
+
+       if (cmd->se_cmd.scsi_status || cmd->sense_reason) {
+               iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
+               return 0;
+       }
        cmd->conn->conn_transport->iscsit_queue_status(cmd->conn, cmd);
 
        return 0;
@@ -1815,21 +1832,21 @@ static u32 lio_tpg_get_default_depth(struct se_portal_group *se_tpg)
 {
        struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
 
-       return ISCSI_TPG_ATTRIB(tpg)->default_cmdsn_depth;
+       return tpg->tpg_attrib.default_cmdsn_depth;
 }
 
 static int lio_tpg_check_demo_mode(struct se_portal_group *se_tpg)
 {
        struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
 
-       return ISCSI_TPG_ATTRIB(tpg)->generate_node_acls;
+       return tpg->tpg_attrib.generate_node_acls;
 }
 
 static int lio_tpg_check_demo_mode_cache(struct se_portal_group *se_tpg)
 {
        struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
 
-       return ISCSI_TPG_ATTRIB(tpg)->cache_dynamic_acls;
+       return tpg->tpg_attrib.cache_dynamic_acls;
 }
 
 static int lio_tpg_check_demo_mode_write_protect(
@@ -1837,7 +1854,7 @@ static int lio_tpg_check_demo_mode_write_protect(
 {
        struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
 
-       return ISCSI_TPG_ATTRIB(tpg)->demo_mode_write_protect;
+       return tpg->tpg_attrib.demo_mode_write_protect;
 }
 
 static int lio_tpg_check_prod_mode_write_protect(
@@ -1845,7 +1862,7 @@ static int lio_tpg_check_prod_mode_write_protect(
 {
        struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
 
-       return ISCSI_TPG_ATTRIB(tpg)->prod_mode_write_protect;
+       return tpg->tpg_attrib.prod_mode_write_protect;
 }
 
 static void lio_tpg_release_fabric_acl(
@@ -1908,9 +1925,12 @@ static void lio_set_default_node_attributes(struct se_node_acl *se_acl)
 {
        struct iscsi_node_acl *acl = container_of(se_acl, struct iscsi_node_acl,
                                se_node_acl);
+       struct se_portal_group *se_tpg = se_acl->se_tpg;
+       struct iscsi_portal_group *tpg = container_of(se_tpg,
+                               struct iscsi_portal_group, tpg_se_tpg);
 
-       ISCSI_NODE_ATTRIB(acl)->nacl = acl;
-       iscsit_set_default_node_attribues(acl);
+       acl->node_attrib.nacl = acl;
+       iscsit_set_default_node_attribues(acl, tpg);
 }
 
 static int lio_check_stop_free(struct se_cmd *se_cmd)
@@ -1995,17 +2015,17 @@ int iscsi_target_register_configfs(void)
         * Setup default attribute lists for various fabric->tf_cit_tmpl
         * sturct config_item_type's
         */
-       TF_CIT_TMPL(fabric)->tfc_discovery_cit.ct_attrs = lio_target_discovery_auth_attrs;
-       TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = lio_target_wwn_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = lio_target_tpg_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = lio_target_tpg_attrib_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_auth_cit.ct_attrs = lio_target_tpg_auth_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = lio_target_tpg_param_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = lio_target_portal_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = lio_target_initiator_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = lio_target_nacl_attrib_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = lio_target_nacl_auth_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = lio_target_nacl_param_attrs;
+       fabric->tf_cit_tmpl.tfc_discovery_cit.ct_attrs = lio_target_discovery_auth_attrs;
+       fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = lio_target_wwn_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = lio_target_tpg_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = lio_target_tpg_attrib_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_auth_cit.ct_attrs = lio_target_tpg_auth_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = lio_target_tpg_param_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = lio_target_portal_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = lio_target_initiator_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = lio_target_nacl_attrib_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = lio_target_nacl_auth_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = lio_target_nacl_param_attrs;
 
        ret = target_fabric_configfs_register(fabric);
        if (ret < 0) {
index 9a5721b..48f7b3b 100644 (file)
@@ -37,9 +37,6 @@
 #define NA_RANDOM_DATAIN_PDU_OFFSETS   0
 #define NA_RANDOM_DATAIN_SEQ_OFFSETS   0
 #define NA_RANDOM_R2T_OFFSETS          0
-#define NA_DEFAULT_ERL                 0
-#define NA_DEFAULT_ERL_MAX             2
-#define NA_DEFAULT_ERL_MIN             0
 
 /* struct iscsi_tpg_attrib sanity values */
 #define TA_AUTHENTICATION              1
@@ -58,6 +55,8 @@
 #define TA_DEMO_MODE_WRITE_PROTECT     1
 /* Disabled by default in production mode w/ explict ACLs */
 #define TA_PROD_MODE_WRITE_PROTECT     0
+#define TA_DEMO_MODE_DISCOVERY         1
+#define TA_DEFAULT_ERL                 0
 #define TA_CACHE_CORE_NPS              0
 
 
@@ -192,6 +191,7 @@ enum recover_cmdsn_ret_table {
        CMDSN_NORMAL_OPERATION          = 0,
        CMDSN_LOWER_THAN_EXP            = 1,
        CMDSN_HIGHER_THAN_EXP           = 2,
+       CMDSN_MAXCMDSN_OVERRUN          = 3,
 };
 
 /* Used for iscsi_handle_immediate_data() return values */
@@ -650,14 +650,13 @@ struct iscsi_session {
        /* Used for session reference counting */
        int                     session_usage_count;
        int                     session_waiting_on_uc;
-       u32                     cmd_pdus;
-       u32                     rsp_pdus;
-       u64                     tx_data_octets;
-       u64                     rx_data_octets;
-       u32                     conn_digest_errors;
-       u32                     conn_timeout_errors;
+       atomic_long_t           cmd_pdus;
+       atomic_long_t           rsp_pdus;
+       atomic_long_t           tx_data_octets;
+       atomic_long_t           rx_data_octets;
+       atomic_long_t           conn_digest_errors;
+       atomic_long_t           conn_timeout_errors;
        u64                     creation_time;
-       spinlock_t              session_stats_lock;
        /* Number of active connections */
        atomic_t                nconn;
        atomic_t                session_continuation;
@@ -755,11 +754,6 @@ struct iscsi_node_acl {
        struct se_node_acl      se_node_acl;
 };
 
-#define NODE_STAT_GRPS(nacl)   (&(nacl)->node_stat_grps)
-
-#define ISCSI_NODE_ATTRIB(t)   (&(t)->node_attrib)
-#define ISCSI_NODE_AUTH(t)     (&(t)->node_auth)
-
 struct iscsi_tpg_attrib {
        u32                     authentication;
        u32                     login_timeout;
@@ -769,6 +763,8 @@ struct iscsi_tpg_attrib {
        u32                     default_cmdsn_depth;
        u32                     demo_mode_write_protect;
        u32                     prod_mode_write_protect;
+       u32                     demo_mode_discovery;
+       u32                     default_erl;
        struct iscsi_portal_group *tpg;
 };
 
@@ -835,12 +831,6 @@ struct iscsi_portal_group {
        struct list_head        tpg_list;
 } ____cacheline_aligned;
 
-#define ISCSI_TPG_C(c)         ((struct iscsi_portal_group *)(c)->tpg)
-#define ISCSI_TPG_LUN(c, l)  ((iscsi_tpg_list_t *)(c)->tpg->tpg_lun_list_t[l])
-#define ISCSI_TPG_S(s)         ((struct iscsi_portal_group *)(s)->tpg)
-#define ISCSI_TPG_ATTRIB(t)    (&(t)->tpg_attrib)
-#define SE_TPG(tpg)            (&(tpg)->tpg_se_tpg)
-
 struct iscsi_wwn_stat_grps {
        struct config_group     iscsi_stat_group;
        struct config_group     iscsi_instance_group;
@@ -871,8 +861,6 @@ struct iscsi_tiqn {
        struct iscsi_logout_stats    logout_stats;
 } ____cacheline_aligned;
 
-#define WWN_STAT_GRPS(tiqn)    (&(tiqn)->tiqn_stat_grps)
-
 struct iscsit_global {
        /* In core shutdown */
        u32                     in_shutdown;
index 6c7a510..7087c73 100644 (file)
@@ -58,11 +58,7 @@ void iscsit_increment_maxcmdsn(struct iscsi_cmd *cmd, struct iscsi_session *sess
 
        cmd->maxcmdsn_inc = 1;
 
-       if (!mutex_trylock(&sess->cmdsn_mutex)) {
-               sess->max_cmd_sn += 1;
-               pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn);
-               return;
-       }
+       mutex_lock(&sess->cmdsn_mutex);
        sess->max_cmd_sn += 1;
        pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn);
        mutex_unlock(&sess->cmdsn_mutex);
index 41052e5..0d1e6ee 100644 (file)
@@ -757,7 +757,7 @@ int iscsit_check_post_dataout(
 static void iscsit_handle_time2retain_timeout(unsigned long data)
 {
        struct iscsi_session *sess = (struct iscsi_session *) data;
-       struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess);
+       struct iscsi_portal_group *tpg = sess->tpg;
        struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
 
        spin_lock_bh(&se_tpg->session_lock);
@@ -785,7 +785,7 @@ static void iscsit_handle_time2retain_timeout(unsigned long data)
                tiqn->sess_err_stats.last_sess_failure_type =
                                ISCSI_SESS_ERR_CXN_TIMEOUT;
                tiqn->sess_err_stats.cxn_timeout_errors++;
-               sess->conn_timeout_errors++;
+               atomic_long_inc(&sess->conn_timeout_errors);
                spin_unlock(&tiqn->sess_err_stats.lock);
        }
        }
@@ -801,9 +801,9 @@ void iscsit_start_time2retain_handler(struct iscsi_session *sess)
         * Only start Time2Retain timer when the associated TPG is still in
         * an ACTIVE (eg: not disabled or shutdown) state.
         */
-       spin_lock(&ISCSI_TPG_S(sess)->tpg_state_lock);
-       tpg_active = (ISCSI_TPG_S(sess)->tpg_state == TPG_STATE_ACTIVE);
-       spin_unlock(&ISCSI_TPG_S(sess)->tpg_state_lock);
+       spin_lock(&sess->tpg->tpg_state_lock);
+       tpg_active = (sess->tpg->tpg_state == TPG_STATE_ACTIVE);
+       spin_unlock(&sess->tpg->tpg_state_lock);
 
        if (!tpg_active)
                return;
@@ -829,7 +829,7 @@ void iscsit_start_time2retain_handler(struct iscsi_session *sess)
  */
 int iscsit_stop_time2retain_timer(struct iscsi_session *sess)
 {
-       struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess);
+       struct iscsi_portal_group *tpg = sess->tpg;
        struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
 
        if (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)
index 1794c75..4eb93b2 100644 (file)
@@ -305,7 +305,6 @@ static int iscsi_login_zero_tsih_s1(
        }
 
        sess->creation_time = get_jiffies_64();
-       spin_lock_init(&sess->session_stats_lock);
        /*
         * The FFP CmdSN window values will be allocated from the TPG's
         * Initiator Node's ACL once the login has been successfully completed.
@@ -347,15 +346,15 @@ static int iscsi_login_zero_tsih_s2(
         * Assign a new TPG Session Handle.  Note this is protected with
         * struct iscsi_portal_group->np_login_sem from iscsit_access_np().
         */
-       sess->tsih = ++ISCSI_TPG_S(sess)->ntsih;
+       sess->tsih = ++sess->tpg->ntsih;
        if (!sess->tsih)
-               sess->tsih = ++ISCSI_TPG_S(sess)->ntsih;
+               sess->tsih = ++sess->tpg->ntsih;
 
        /*
         * Create the default params from user defined values..
         */
        if (iscsi_copy_param_list(&conn->param_list,
-                               ISCSI_TPG_C(conn)->param_list, 1) < 0) {
+                               conn->tpg->param_list, 1) < 0) {
                iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
                                ISCSI_LOGIN_STATUS_NO_RESOURCES);
                return -1;
@@ -380,7 +379,7 @@ static int iscsi_login_zero_tsih_s2(
         * In our case, we have already located the struct iscsi_tiqn at this point.
         */
        memset(buf, 0, 32);
-       sprintf(buf, "TargetPortalGroupTag=%hu", ISCSI_TPG_S(sess)->tpgt);
+       sprintf(buf, "TargetPortalGroupTag=%hu", sess->tpg->tpgt);
        if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) {
                iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
                                ISCSI_LOGIN_STATUS_NO_RESOURCES);
@@ -575,7 +574,7 @@ static int iscsi_login_non_zero_tsih_s2(
        iscsi_login_set_conn_values(sess, conn, pdu->cid);
 
        if (iscsi_copy_param_list(&conn->param_list,
-                       ISCSI_TPG_C(conn)->param_list, 0) < 0) {
+                       conn->tpg->param_list, 0) < 0) {
                iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
                                ISCSI_LOGIN_STATUS_NO_RESOURCES);
                return -1;
@@ -593,7 +592,7 @@ static int iscsi_login_non_zero_tsih_s2(
         * In our case, we have already located the struct iscsi_tiqn at this point.
         */
        memset(buf, 0, 32);
-       sprintf(buf, "TargetPortalGroupTag=%hu", ISCSI_TPG_S(sess)->tpgt);
+       sprintf(buf, "TargetPortalGroupTag=%hu", sess->tpg->tpgt);
        if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) {
                iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
                                ISCSI_LOGIN_STATUS_NO_RESOURCES);
@@ -691,7 +690,7 @@ int iscsi_post_login_handler(
        int stop_timer = 0;
        struct iscsi_session *sess = conn->sess;
        struct se_session *se_sess = sess->se_sess;
-       struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess);
+       struct iscsi_portal_group *tpg = sess->tpg;
        struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
        struct iscsi_thread_set *ts;
 
@@ -1154,7 +1153,7 @@ old_sess_out:
                spin_lock_bh(&conn->sess->conn_lock);
                if (conn->sess->session_state == TARG_SESS_STATE_FAILED) {
                        struct se_portal_group *se_tpg =
-                                       &ISCSI_TPG_C(conn)->tpg_se_tpg;
+                                       &conn->tpg->tpg_se_tpg;
 
                        atomic_set(&conn->sess->session_continuation, 0);
                        spin_unlock_bh(&conn->sess->conn_lock);
index ef6d836..83c965c 100644 (file)
@@ -88,7 +88,7 @@ int extract_param(
        if (len < 0)
                return -1;
 
-       if (len > max_length) {
+       if (len >= max_length) {
                pr_err("Length of input: %d exceeds max_length:"
                        " %d\n", len, max_length);
                return -1;
@@ -140,7 +140,7 @@ static u32 iscsi_handle_authentication(
                        iscsi_nacl = container_of(se_nacl, struct iscsi_node_acl,
                                                  se_node_acl);
 
-                       auth = ISCSI_NODE_AUTH(iscsi_nacl);
+                       auth = &iscsi_nacl->node_auth;
                }
        } else {
                /*
@@ -789,7 +789,7 @@ static int iscsi_target_handle_csg_zero(
                return -1;
 
        if (!iscsi_check_negotiated_keys(conn->param_list)) {
-               if (ISCSI_TPG_ATTRIB(ISCSI_TPG_C(conn))->authentication &&
+               if (conn->tpg->tpg_attrib.authentication &&
                    !strncmp(param->value, NONE, 4)) {
                        pr_err("Initiator sent AuthMethod=None but"
                                " Target is enforcing iSCSI Authentication,"
@@ -799,7 +799,7 @@ static int iscsi_target_handle_csg_zero(
                        return -1;
                }
 
-               if (ISCSI_TPG_ATTRIB(ISCSI_TPG_C(conn))->authentication &&
+               if (conn->tpg->tpg_attrib.authentication &&
                    !login->auth_complete)
                        return 0;
 
@@ -862,7 +862,7 @@ static int iscsi_target_handle_csg_one(struct iscsi_conn *conn, struct iscsi_log
        }
 
        if (!login->auth_complete &&
-            ISCSI_TPG_ATTRIB(ISCSI_TPG_C(conn))->authentication) {
+            conn->tpg->tpg_attrib.authentication) {
                pr_err("Initiator is requesting CSG: 1, has not been"
                         " successfully authenticated, and the Target is"
                        " enforcing iSCSI Authentication, login failed.\n");
index 93bdc47..16454a9 100644 (file)
@@ -33,7 +33,8 @@ static inline char *iscsit_na_get_initiatorname(
 }
 
 void iscsit_set_default_node_attribues(
-       struct iscsi_node_acl *acl)
+       struct iscsi_node_acl *acl,
+       struct iscsi_portal_group *tpg)
 {
        struct iscsi_node_attrib *a = &acl->node_attrib;
 
@@ -44,7 +45,7 @@ void iscsit_set_default_node_attribues(
        a->random_datain_pdu_offsets = NA_RANDOM_DATAIN_PDU_OFFSETS;
        a->random_datain_seq_offsets = NA_RANDOM_DATAIN_SEQ_OFFSETS;
        a->random_r2t_offsets = NA_RANDOM_R2T_OFFSETS;
-       a->default_erl = NA_DEFAULT_ERL;
+       a->default_erl = tpg->tpg_attrib.default_erl;
 }
 
 int iscsit_na_dataout_timeout(
index c970b32..0c69a46 100644 (file)
@@ -1,7 +1,8 @@
 #ifndef ISCSI_TARGET_NODEATTRIB_H
 #define ISCSI_TARGET_NODEATTRIB_H
 
-extern void iscsit_set_default_node_attribues(struct iscsi_node_acl *);
+extern void iscsit_set_default_node_attribues(struct iscsi_node_acl *,
+                                             struct iscsi_portal_group *);
 extern int iscsit_na_dataout_timeout(struct iscsi_node_acl *, u32);
 extern int iscsit_na_dataout_timeout_retries(struct iscsi_node_acl *, u32);
 extern int iscsit_na_nopin_timeout(struct iscsi_node_acl *, u32);
index f788e8b..1033955 100644 (file)
@@ -792,7 +792,8 @@ static ssize_t iscsi_stat_sess_show_attr_cmd_pdus(
        if (se_sess) {
                sess = se_sess->fabric_sess_ptr;
                if (sess)
-                       ret = snprintf(page, PAGE_SIZE, "%u\n", sess->cmd_pdus);
+                       ret = snprintf(page, PAGE_SIZE, "%lu\n",
+                                      atomic_long_read(&sess->cmd_pdus));
        }
        spin_unlock_bh(&se_nacl->nacl_sess_lock);
 
@@ -815,7 +816,8 @@ static ssize_t iscsi_stat_sess_show_attr_rsp_pdus(
        if (se_sess) {
                sess = se_sess->fabric_sess_ptr;
                if (sess)
-                       ret = snprintf(page, PAGE_SIZE, "%u\n", sess->rsp_pdus);
+                       ret = snprintf(page, PAGE_SIZE, "%lu\n",
+                                      atomic_long_read(&sess->rsp_pdus));
        }
        spin_unlock_bh(&se_nacl->nacl_sess_lock);
 
@@ -838,8 +840,8 @@ static ssize_t iscsi_stat_sess_show_attr_txdata_octs(
        if (se_sess) {
                sess = se_sess->fabric_sess_ptr;
                if (sess)
-                       ret = snprintf(page, PAGE_SIZE, "%llu\n",
-                               (unsigned long long)sess->tx_data_octets);
+                       ret = snprintf(page, PAGE_SIZE, "%lu\n",
+                                      atomic_long_read(&sess->tx_data_octets));
        }
        spin_unlock_bh(&se_nacl->nacl_sess_lock);
 
@@ -862,8 +864,8 @@ static ssize_t iscsi_stat_sess_show_attr_rxdata_octs(
        if (se_sess) {
                sess = se_sess->fabric_sess_ptr;
                if (sess)
-                       ret = snprintf(page, PAGE_SIZE, "%llu\n",
-                               (unsigned long long)sess->rx_data_octets);
+                       ret = snprintf(page, PAGE_SIZE, "%lu\n",
+                                      atomic_long_read(&sess->rx_data_octets));
        }
        spin_unlock_bh(&se_nacl->nacl_sess_lock);
 
@@ -886,8 +888,8 @@ static ssize_t iscsi_stat_sess_show_attr_conn_digest_errors(
        if (se_sess) {
                sess = se_sess->fabric_sess_ptr;
                if (sess)
-                       ret = snprintf(page, PAGE_SIZE, "%u\n",
-                                       sess->conn_digest_errors);
+                       ret = snprintf(page, PAGE_SIZE, "%lu\n",
+                                      atomic_long_read(&sess->conn_digest_errors));
        }
        spin_unlock_bh(&se_nacl->nacl_sess_lock);
 
@@ -910,8 +912,8 @@ static ssize_t iscsi_stat_sess_show_attr_conn_timeout_errors(
        if (se_sess) {
                sess = se_sess->fabric_sess_ptr;
                if (sess)
-                       ret = snprintf(page, PAGE_SIZE, "%u\n",
-                                       sess->conn_timeout_errors);
+                       ret = snprintf(page, PAGE_SIZE, "%lu\n",
+                                      atomic_long_read(&sess->conn_timeout_errors));
        }
        spin_unlock_bh(&se_nacl->nacl_sess_lock);
 
index 4faeb47..3976183 100644 (file)
@@ -223,6 +223,8 @@ static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg)
        a->cache_dynamic_acls = TA_CACHE_DYNAMIC_ACLS;
        a->demo_mode_write_protect = TA_DEMO_MODE_WRITE_PROTECT;
        a->prod_mode_write_protect = TA_PROD_MODE_WRITE_PROTECT;
+       a->demo_mode_discovery = TA_DEMO_MODE_DISCOVERY;
+       a->default_erl = TA_DEFAULT_ERL;
 }
 
 int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg)
@@ -237,7 +239,7 @@ int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_gro
        if (iscsi_create_default_params(&tpg->param_list) < 0)
                goto err_out;
 
-       ISCSI_TPG_ATTRIB(tpg)->tpg = tpg;
+       tpg->tpg_attrib.tpg = tpg;
 
        spin_lock(&tpg->tpg_state_lock);
        tpg->tpg_state  = TPG_STATE_INACTIVE;
@@ -330,7 +332,7 @@ int iscsit_tpg_enable_portal_group(struct iscsi_portal_group *tpg)
                return -EINVAL;
        }
 
-       if (ISCSI_TPG_ATTRIB(tpg)->authentication) {
+       if (tpg->tpg_attrib.authentication) {
                if (!strcmp(param->value, NONE)) {
                        ret = iscsi_update_param_value(param, CHAP);
                        if (ret)
@@ -820,3 +822,39 @@ int iscsit_ta_prod_mode_write_protect(
 
        return 0;
 }
+
+int iscsit_ta_demo_mode_discovery(
+       struct iscsi_portal_group *tpg,
+       u32 flag)
+{
+       struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+       if ((flag != 0) && (flag != 1)) {
+               pr_err("Illegal value %d\n", flag);
+               return -EINVAL;
+       }
+
+       a->demo_mode_discovery = flag;
+       pr_debug("iSCSI_TPG[%hu] - Demo Mode Discovery bit:"
+               " %s\n", tpg->tpgt, (a->demo_mode_discovery) ?
+               "ON" : "OFF");
+
+       return 0;
+}
+
+int iscsit_ta_default_erl(
+       struct iscsi_portal_group *tpg,
+       u32 default_erl)
+{
+       struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+       if ((default_erl != 0) && (default_erl != 1) && (default_erl != 2)) {
+               pr_err("Illegal value for default_erl: %u\n", default_erl);
+               return -EINVAL;
+       }
+
+       a->default_erl = default_erl;
+       pr_debug("iSCSI_TPG[%hu] - DefaultERL: %u\n", tpg->tpgt, a->default_erl);
+
+       return 0;
+}
index b77693e..213c0fc 100644 (file)
@@ -37,5 +37,7 @@ extern int iscsit_ta_default_cmdsn_depth(struct iscsi_portal_group *, u32);
 extern int iscsit_ta_cache_dynamic_acls(struct iscsi_portal_group *, u32);
 extern int iscsit_ta_demo_mode_write_protect(struct iscsi_portal_group *, u32);
 extern int iscsit_ta_prod_mode_write_protect(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_demo_mode_discovery(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_default_erl(struct iscsi_portal_group *, u32);
 
 #endif /* ISCSI_TARGET_TPG_H */
index b0cac0c..0819e68 100644 (file)
@@ -242,9 +242,9 @@ static inline int iscsit_check_received_cmdsn(struct iscsi_session *sess, u32 cm
         */
        if (iscsi_sna_gt(cmdsn, sess->max_cmd_sn)) {
                pr_err("Received CmdSN: 0x%08x is greater than"
-                      " MaxCmdSN: 0x%08x, protocol error.\n", cmdsn,
+                      " MaxCmdSN: 0x%08x, ignoring.\n", cmdsn,
                       sess->max_cmd_sn);
-               ret = CMDSN_ERROR_CANNOT_RECOVER;
+               ret = CMDSN_MAXCMDSN_OVERRUN;
 
        } else if (cmdsn == sess->exp_cmd_sn) {
                sess->exp_cmd_sn++;
@@ -303,14 +303,16 @@ int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
                ret = CMDSN_HIGHER_THAN_EXP;
                break;
        case CMDSN_LOWER_THAN_EXP:
+       case CMDSN_MAXCMDSN_OVERRUN:
+       default:
                cmd->i_state = ISTATE_REMOVE;
                iscsit_add_cmd_to_immediate_queue(cmd, conn, cmd->i_state);
-               ret = cmdsn_ret;
-               break;
-       default:
-               reason = ISCSI_REASON_PROTOCOL_ERROR;
-               reject = true;
-               ret = cmdsn_ret;
+               /*
+                * Existing callers for iscsit_sequence_cmd() will silently
+                * ignore commands with CMDSN_LOWER_THAN_EXP, so force this
+                * return for CMDSN_MAXCMDSN_OVERRUN as well..
+                */
+               ret = CMDSN_LOWER_THAN_EXP;
                break;
        }
        mutex_unlock(&conn->sess->cmdsn_mutex);
@@ -980,7 +982,7 @@ static void iscsit_handle_nopin_response_timeout(unsigned long data)
                tiqn->sess_err_stats.last_sess_failure_type =
                                ISCSI_SESS_ERR_CXN_TIMEOUT;
                tiqn->sess_err_stats.cxn_timeout_errors++;
-               conn->sess->conn_timeout_errors++;
+               atomic_long_inc(&conn->sess->conn_timeout_errors);
                spin_unlock_bh(&tiqn->sess_err_stats.lock);
        }
        }
index 0f6d69d..1b41e67 100644 (file)
@@ -135,6 +135,21 @@ static int tcm_loop_change_queue_depth(
        return sdev->queue_depth;
 }
 
+static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag)
+{
+       if (sdev->tagged_supported) {
+               scsi_set_tag_type(sdev, tag);
+
+               if (tag)
+                       scsi_activate_tcq(sdev, sdev->queue_depth);
+               else
+                       scsi_deactivate_tcq(sdev, sdev->queue_depth);
+       } else
+               tag = 0;
+
+       return tag;
+}
+
 /*
  * Locate the SAM Task Attr from struct scsi_cmnd *
  */
@@ -178,7 +193,10 @@ static void tcm_loop_submission_work(struct work_struct *work)
                set_host_byte(sc, DID_NO_CONNECT);
                goto out_done;
        }
-
+       if (tl_tpg->tl_transport_status == TCM_TRANSPORT_OFFLINE) {
+               set_host_byte(sc, DID_TRANSPORT_DISRUPTED);
+               goto out_done;
+       }
        tl_nexus = tl_hba->tl_nexus;
        if (!tl_nexus) {
                scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus"
@@ -233,6 +251,7 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
        }
 
        tl_cmd->sc = sc;
+       tl_cmd->sc_cmd_tag = sc->tag;
        INIT_WORK(&tl_cmd->work, tcm_loop_submission_work);
        queue_work(tcm_loop_workqueue, &tl_cmd->work);
        return 0;
@@ -242,41 +261,21 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
  * Called from SCSI EH process context to issue a LUN_RESET TMR
  * to struct scsi_device
  */
-static int tcm_loop_device_reset(struct scsi_cmnd *sc)
+static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg,
+                             struct tcm_loop_nexus *tl_nexus,
+                             int lun, int task, enum tcm_tmreq_table tmr)
 {
        struct se_cmd *se_cmd = NULL;
-       struct se_portal_group *se_tpg;
        struct se_session *se_sess;
+       struct se_portal_group *se_tpg;
        struct tcm_loop_cmd *tl_cmd = NULL;
-       struct tcm_loop_hba *tl_hba;
-       struct tcm_loop_nexus *tl_nexus;
        struct tcm_loop_tmr *tl_tmr = NULL;
-       struct tcm_loop_tpg *tl_tpg;
-       int ret = FAILED, rc;
-       /*
-        * Locate the tcm_loop_hba_t pointer
-        */
-       tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
-       /*
-        * Locate the tl_nexus and se_sess pointers
-        */
-       tl_nexus = tl_hba->tl_nexus;
-       if (!tl_nexus) {
-               pr_err("Unable to perform device reset without"
-                               " active I_T Nexus\n");
-               return FAILED;
-       }
-       se_sess = tl_nexus->se_sess;
-       /*
-        * Locate the tl_tpg and se_tpg pointers from TargetID in sc->device->id
-        */
-       tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
-       se_tpg = &tl_tpg->tl_se_tpg;
+       int ret = TMR_FUNCTION_FAILED, rc;
 
        tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL);
        if (!tl_cmd) {
                pr_err("Unable to allocate memory for tl_cmd\n");
-               return FAILED;
+               return ret;
        }
 
        tl_tmr = kzalloc(sizeof(struct tcm_loop_tmr), GFP_KERNEL);
@@ -287,6 +286,8 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)
        init_waitqueue_head(&tl_tmr->tl_tmr_wait);
 
        se_cmd = &tl_cmd->tl_se_cmd;
+       se_tpg = &tl_tpg->tl_se_tpg;
+       se_sess = tl_nexus->se_sess;
        /*
         * Initialize struct se_cmd descriptor from target_core_mod infrastructure
         */
@@ -294,17 +295,23 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)
                                DMA_NONE, MSG_SIMPLE_TAG,
                                &tl_cmd->tl_sense_buf[0]);
 
-       rc = core_tmr_alloc_req(se_cmd, tl_tmr, TMR_LUN_RESET, GFP_KERNEL);
+       rc = core_tmr_alloc_req(se_cmd, tl_tmr, tmr, GFP_KERNEL);
        if (rc < 0)
                goto release;
+
+       if (tmr == TMR_ABORT_TASK)
+               se_cmd->se_tmr_req->ref_task_tag = task;
+
        /*
-        * Locate the underlying TCM struct se_lun from sc->device->lun
+        * Locate the underlying TCM struct se_lun
         */
-       if (transport_lookup_tmr_lun(se_cmd, sc->device->lun) < 0)
+       if (transport_lookup_tmr_lun(se_cmd, lun) < 0) {
+               ret = TMR_LUN_DOES_NOT_EXIST;
                goto release;
+       }
        /*
-        * Queue the TMR to TCM Core and sleep waiting for tcm_loop_queue_tm_rsp()
-        * to wake us up.
+        * Queue the TMR to TCM Core and sleep waiting for
+        * tcm_loop_queue_tm_rsp() to wake us up.
         */
        transport_generic_handle_tmr(se_cmd);
        wait_event(tl_tmr->tl_tmr_wait, atomic_read(&tl_tmr->tmr_complete));
@@ -312,8 +319,7 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)
         * The TMR LUN_RESET has completed, check the response status and
         * then release allocations.
         */
-       ret = (se_cmd->se_tmr_req->response == TMR_FUNCTION_COMPLETE) ?
-               SUCCESS : FAILED;
+       ret = se_cmd->se_tmr_req->response;
 release:
        if (se_cmd)
                transport_generic_free_cmd(se_cmd, 1);
@@ -323,6 +329,94 @@ release:
        return ret;
 }
 
+static int tcm_loop_abort_task(struct scsi_cmnd *sc)
+{
+       struct tcm_loop_hba *tl_hba;
+       struct tcm_loop_nexus *tl_nexus;
+       struct tcm_loop_tpg *tl_tpg;
+       int ret = FAILED;
+
+       /*
+        * Locate the tcm_loop_hba_t pointer
+        */
+       tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
+       /*
+        * Locate the tl_nexus and se_sess pointers
+        */
+       tl_nexus = tl_hba->tl_nexus;
+       if (!tl_nexus) {
+               pr_err("Unable to perform device reset without"
+                               " active I_T Nexus\n");
+               return FAILED;
+       }
+
+       /*
+        * Locate the tl_tpg pointer from TargetID in sc->device->id
+        */
+       tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
+       ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun,
+                                sc->tag, TMR_ABORT_TASK);
+       return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;
+}
+
+/*
+ * Called from SCSI EH process context to issue a LUN_RESET TMR
+ * to struct scsi_device
+ */
+static int tcm_loop_device_reset(struct scsi_cmnd *sc)
+{
+       struct tcm_loop_hba *tl_hba;
+       struct tcm_loop_nexus *tl_nexus;
+       struct tcm_loop_tpg *tl_tpg;
+       int ret = FAILED;
+
+       /*
+        * Locate the tcm_loop_hba_t pointer
+        */
+       tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
+       /*
+        * Locate the tl_nexus and se_sess pointers
+        */
+       tl_nexus = tl_hba->tl_nexus;
+       if (!tl_nexus) {
+               pr_err("Unable to perform device reset without"
+                               " active I_T Nexus\n");
+               return FAILED;
+       }
+       /*
+        * Locate the tl_tpg pointer from TargetID in sc->device->id
+        */
+       tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
+       ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun,
+                                0, TMR_LUN_RESET);
+       return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;
+}
+
+static int tcm_loop_target_reset(struct scsi_cmnd *sc)
+{
+       struct tcm_loop_hba *tl_hba;
+       struct tcm_loop_tpg *tl_tpg;
+
+       /*
+        * Locate the tcm_loop_hba_t pointer
+        */
+       tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
+       if (!tl_hba) {
+               pr_err("Unable to perform device reset without"
+                               " active I_T Nexus\n");
+               return FAILED;
+       }
+       /*
+        * Locate the tl_tpg pointer from TargetID in sc->device->id
+        */
+       tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
+       if (tl_tpg) {
+               tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE;
+               return SUCCESS;
+       }
+       return FAILED;
+}
+
 static int tcm_loop_slave_alloc(struct scsi_device *sd)
 {
        set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags);
@@ -331,6 +425,15 @@ static int tcm_loop_slave_alloc(struct scsi_device *sd)
 
 static int tcm_loop_slave_configure(struct scsi_device *sd)
 {
+       if (sd->tagged_supported) {
+               scsi_activate_tcq(sd, sd->queue_depth);
+               scsi_adjust_queue_depth(sd, MSG_SIMPLE_TAG,
+                                       sd->host->cmd_per_lun);
+       } else {
+               scsi_adjust_queue_depth(sd, 0,
+                                       sd->host->cmd_per_lun);
+       }
+
        return 0;
 }
 
@@ -340,7 +443,10 @@ static struct scsi_host_template tcm_loop_driver_template = {
        .name                   = "TCM_Loopback",
        .queuecommand           = tcm_loop_queuecommand,
        .change_queue_depth     = tcm_loop_change_queue_depth,
+       .change_queue_type      = tcm_loop_change_queue_type,
+       .eh_abort_handler = tcm_loop_abort_task,
        .eh_device_reset_handler = tcm_loop_device_reset,
+       .eh_target_reset_handler = tcm_loop_target_reset,
        .can_queue              = 1024,
        .this_id                = -1,
        .sg_tablesize           = 256,
@@ -699,7 +805,10 @@ static void tcm_loop_set_default_node_attributes(struct se_node_acl *se_acl)
 
 static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd)
 {
-       return 1;
+       struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+                       struct tcm_loop_cmd, tl_se_cmd);
+
+       return tl_cmd->sc_cmd_tag;
 }
 
 static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd)
@@ -932,7 +1041,10 @@ static int tcm_loop_drop_nexus(
        struct tcm_loop_nexus *tl_nexus;
        struct tcm_loop_hba *tl_hba = tpg->tl_hba;
 
-       tl_nexus = tpg->tl_hba->tl_nexus;
+       if (!tl_hba)
+               return -ENODEV;
+
+       tl_nexus = tl_hba->tl_nexus;
        if (!tl_nexus)
                return -ENODEV;
 
@@ -1061,8 +1173,56 @@ check_newline:
 
 TF_TPG_BASE_ATTR(tcm_loop, nexus, S_IRUGO | S_IWUSR);
 
+static ssize_t tcm_loop_tpg_show_transport_status(
+       struct se_portal_group *se_tpg,
+       char *page)
+{
+       struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+                       struct tcm_loop_tpg, tl_se_tpg);
+       const char *status = NULL;
+       ssize_t ret = -EINVAL;
+
+       switch (tl_tpg->tl_transport_status) {
+       case TCM_TRANSPORT_ONLINE:
+               status = "online";
+               break;
+       case TCM_TRANSPORT_OFFLINE:
+               status = "offline";
+               break;
+       default:
+               break;
+       }
+
+       if (status)
+               ret = snprintf(page, PAGE_SIZE, "%s\n", status);
+
+       return ret;
+}
+
+static ssize_t tcm_loop_tpg_store_transport_status(
+       struct se_portal_group *se_tpg,
+       const char *page,
+       size_t count)
+{
+       struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+                       struct tcm_loop_tpg, tl_se_tpg);
+
+       if (!strncmp(page, "online", 6)) {
+               tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE;
+               return count;
+       }
+       if (!strncmp(page, "offline", 7)) {
+               tl_tpg->tl_transport_status = TCM_TRANSPORT_OFFLINE;
+               return count;
+       }
+       return -EINVAL;
+}
+
+TF_TPG_BASE_ATTR(tcm_loop, transport_status, S_IRUGO | S_IWUSR);
+
 static struct configfs_attribute *tcm_loop_tpg_attrs[] = {
        &tcm_loop_tpg_nexus.attr,
+       &tcm_loop_tpg_transport_status.attr,
        NULL,
 };
 
@@ -1334,11 +1494,11 @@ static int tcm_loop_register_configfs(void)
        /*
         * Setup default attribute lists for various fabric->tf_cit_tmpl
         */
-       TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
        /*
         * Once fabric->tf_ops has been setup, now register the fabric for
         * use within TCM
index dd7a84e..54c59d0 100644 (file)
@@ -10,6 +10,8 @@
 struct tcm_loop_cmd {
        /* State of Linux/SCSI CDB+Data descriptor */
        u32 sc_cmd_state;
+       /* Tagged command queueing */
+       u32 sc_cmd_tag;
        /* Pointer to the CDB+Data descriptor from Linux/SCSI subsystem */
        struct scsi_cmnd *sc;
        /* The TCM I/O descriptor that is accessed via container_of() */
@@ -40,8 +42,12 @@ struct tcm_loop_nacl {
        struct se_node_acl se_node_acl;
 };
 
+#define TCM_TRANSPORT_ONLINE 0
+#define TCM_TRANSPORT_OFFLINE 1
+
 struct tcm_loop_tpg {
        unsigned short tl_tpgt;
+       unsigned short tl_transport_status;
        atomic_t tl_tpg_port_count;
        struct se_portal_group tl_se_tpg;
        struct tcm_loop_hba *tl_hba;
index e51b09a..24884ca 100644 (file)
@@ -2556,15 +2556,15 @@ static int sbp_register_configfs(void)
        /*
         * Setup default attribute lists for various fabric->tf_cit_tmpl
         */
-       TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = sbp_wwn_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = sbp_wwn_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;
 
        ret = target_fabric_configfs_register(fabric);
        if (ret < 0) {
index 4724410..fdcee32 100644 (file)
@@ -44,7 +44,7 @@
 static sense_reason_t core_alua_check_transition(int state, int *primary);
 static int core_alua_set_tg_pt_secondary_state(
                struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem,
-               struct se_port *port, int explict, int offline);
+               struct se_port *port, int explicit, int offline);
 
 static u16 alua_lu_gps_counter;
 static u32 alua_lu_gps_count;
@@ -117,12 +117,7 @@ target_emulate_report_target_port_groups(struct se_cmd *cmd)
                /*
                 * Set supported ASYMMETRIC ACCESS State bits
                 */
-               buf[off] = 0x80; /* T_SUP */
-               buf[off] |= 0x40; /* O_SUP */
-               buf[off] |= 0x8; /* U_SUP */
-               buf[off] |= 0x4; /* S_SUP */
-               buf[off] |= 0x2; /* AN_SUP */
-               buf[off++] |= 0x1; /* AO_SUP */
+               buf[off++] |= tg_pt_gp->tg_pt_gp_alua_supported_states;
                /*
                 * TARGET PORT GROUP
                 */
@@ -175,7 +170,7 @@ target_emulate_report_target_port_groups(struct se_cmd *cmd)
        if (ext_hdr != 0) {
                buf[4] = 0x10;
                /*
-                * Set the implict transition time (in seconds) for the application
+                * Set the implicit transition time (in seconds) for the application
                 * client to use as a base for it's transition timeout value.
                 *
                 * Use the current tg_pt_gp_mem -> tg_pt_gp membership from the LUN
@@ -188,7 +183,7 @@ target_emulate_report_target_port_groups(struct se_cmd *cmd)
                        spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
                        tg_pt_gp = tg_pt_gp_mem->tg_pt_gp;
                        if (tg_pt_gp)
-                               buf[5] = tg_pt_gp->tg_pt_gp_implict_trans_secs;
+                               buf[5] = tg_pt_gp->tg_pt_gp_implicit_trans_secs;
                        spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
                }
        }
@@ -199,7 +194,7 @@ target_emulate_report_target_port_groups(struct se_cmd *cmd)
 }
 
 /*
- * SET_TARGET_PORT_GROUPS for explict ALUA operation.
+ * SET_TARGET_PORT_GROUPS for explicit ALUA operation.
  *
  * See spc4r17 section 6.35
  */
@@ -232,7 +227,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)
                return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
 
        /*
-        * Determine if explict ALUA via SET_TARGET_PORT_GROUPS is allowed
+        * Determine if explicit ALUA via SET_TARGET_PORT_GROUPS is allowed
         * for the local tg_pt_gp.
         */
        l_tg_pt_gp_mem = l_port->sep_alua_tg_pt_gp_mem;
@@ -251,9 +246,9 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)
        }
        spin_unlock(&l_tg_pt_gp_mem->tg_pt_gp_mem_lock);
 
-       if (!(l_tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICT_ALUA)) {
+       if (!(l_tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA)) {
                pr_debug("Unable to process SET_TARGET_PORT_GROUPS"
-                               " while TPGS_EXPLICT_ALUA is disabled\n");
+                               " while TPGS_EXPLICIT_ALUA is disabled\n");
                rc = TCM_UNSUPPORTED_SCSI_OPCODE;
                goto out;
        }
@@ -330,7 +325,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)
                        spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
                } else {
                        /*
-                        * Extact the RELATIVE TARGET PORT IDENTIFIER to identify
+                        * Extract the RELATIVE TARGET PORT IDENTIFIER to identify
                         * the Target Port in question for the the incoming
                         * SET_TARGET_PORT_GROUPS op.
                         */
@@ -487,7 +482,7 @@ static inline int core_alua_state_transition(
        u8 *alua_ascq)
 {
        /*
-        * Allowed CDBs for ALUA_ACCESS_STATE_TRANSITIO as defined by
+        * Allowed CDBs for ALUA_ACCESS_STATE_TRANSITION as defined by
         * spc4r17 section 5.9.2.5
         */
        switch (cdb[0]) {
@@ -515,9 +510,9 @@ static inline int core_alua_state_transition(
 }
 
 /*
- * return 1: Is used to signal LUN not accecsable, and check condition/not ready
+ * return 1: Is used to signal LUN not accessible, and check condition/not ready
  * return 0: Used to signal success
- * reutrn -1: Used to signal failure, and invalid cdb field
+ * return -1: Used to signal failure, and invalid cdb field
  */
 sense_reason_t
 target_alua_state_check(struct se_cmd *cmd)
@@ -566,12 +561,12 @@ target_alua_state_check(struct se_cmd *cmd)
        nonop_delay_msecs = tg_pt_gp->tg_pt_gp_nonop_delay_msecs;
        spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
        /*
-        * Process ALUA_ACCESS_STATE_ACTIVE_OPTMIZED in a separate conditional
+        * Process ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED in a separate conditional
         * statement so the compiler knows explicitly to check this case first.
         * For the Optimized ALUA access state case, we want to process the
         * incoming fabric cmd ASAP..
         */
-       if (out_alua_state == ALUA_ACCESS_STATE_ACTIVE_OPTMIZED)
+       if (out_alua_state == ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED)
                return 0;
 
        switch (out_alua_state) {
@@ -620,13 +615,13 @@ out:
 }
 
 /*
- * Check implict and explict ALUA state change request.
+ * Check implicit and explicit ALUA state change request.
  */
 static sense_reason_t
 core_alua_check_transition(int state, int *primary)
 {
        switch (state) {
-       case ALUA_ACCESS_STATE_ACTIVE_OPTMIZED:
+       case ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED:
        case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
        case ALUA_ACCESS_STATE_STANDBY:
        case ALUA_ACCESS_STATE_UNAVAILABLE:
@@ -654,7 +649,7 @@ core_alua_check_transition(int state, int *primary)
 static char *core_alua_dump_state(int state)
 {
        switch (state) {
-       case ALUA_ACCESS_STATE_ACTIVE_OPTMIZED:
+       case ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED:
                return "Active/Optimized";
        case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
                return "Active/NonOptimized";
@@ -676,10 +671,10 @@ char *core_alua_dump_status(int status)
        switch (status) {
        case ALUA_STATUS_NONE:
                return "None";
-       case ALUA_STATUS_ALTERED_BY_EXPLICT_STPG:
-               return "Altered by Explict STPG";
-       case ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA:
-               return "Altered by Implict ALUA";
+       case ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG:
+               return "Altered by Explicit STPG";
+       case ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA:
+               return "Altered by Implicit ALUA";
        default:
                return "Unknown";
        }
@@ -770,7 +765,7 @@ static int core_alua_do_transition_tg_pt(
        struct se_node_acl *nacl,
        unsigned char *md_buf,
        int new_state,
-       int explict)
+       int explicit)
 {
        struct se_dev_entry *se_deve;
        struct se_lun_acl *lacl;
@@ -784,9 +779,9 @@ static int core_alua_do_transition_tg_pt(
        old_state = atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state);
        atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state,
                        ALUA_ACCESS_STATE_TRANSITION);
-       tg_pt_gp->tg_pt_gp_alua_access_status = (explict) ?
-                               ALUA_STATUS_ALTERED_BY_EXPLICT_STPG :
-                               ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA;
+       tg_pt_gp->tg_pt_gp_alua_access_status = (explicit) ?
+                               ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG :
+                               ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA;
        /*
         * Check for the optional ALUA primary state transition delay
         */
@@ -802,7 +797,7 @@ static int core_alua_do_transition_tg_pt(
                 * change, a device server shall establish a unit attention
                 * condition for the initiator port associated with every I_T
                 * nexus with the additional sense code set to ASYMMETRIC
-                * ACCESS STATE CHAGED.
+                * ACCESS STATE CHANGED.
                 *
                 * After an explicit target port asymmetric access state
                 * change, a device server shall establish a unit attention
@@ -821,12 +816,12 @@ static int core_alua_do_transition_tg_pt(
                        lacl = se_deve->se_lun_acl;
                        /*
                         * se_deve->se_lun_acl pointer may be NULL for a
-                        * entry created without explict Node+MappedLUN ACLs
+                        * entry created without explicit Node+MappedLUN ACLs
                         */
                        if (!lacl)
                                continue;
 
-                       if (explict &&
+                       if (explicit &&
                           (nacl != NULL) && (nacl == lacl->se_lun_nacl) &&
                           (l_port != NULL) && (l_port == port))
                                continue;
@@ -866,8 +861,8 @@ static int core_alua_do_transition_tg_pt(
        atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state, new_state);
 
        pr_debug("Successful %s ALUA transition TG PT Group: %s ID: %hu"
-               " from primary access state %s to %s\n", (explict) ? "explict" :
-               "implict", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item),
+               " from primary access state %s to %s\n", (explicit) ? "explicit" :
+               "implicit", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item),
                tg_pt_gp->tg_pt_gp_id, core_alua_dump_state(old_state),
                core_alua_dump_state(new_state));
 
@@ -880,7 +875,7 @@ int core_alua_do_port_transition(
        struct se_port *l_port,
        struct se_node_acl *l_nacl,
        int new_state,
-       int explict)
+       int explicit)
 {
        struct se_device *dev;
        struct se_port *port;
@@ -917,7 +912,7 @@ int core_alua_do_port_transition(
                 * success.
                 */
                core_alua_do_transition_tg_pt(l_tg_pt_gp, l_port, l_nacl,
-                                       md_buf, new_state, explict);
+                                       md_buf, new_state, explicit);
                atomic_dec(&lu_gp->lu_gp_ref_cnt);
                smp_mb__after_atomic_dec();
                kfree(md_buf);
@@ -946,7 +941,7 @@ int core_alua_do_port_transition(
                                continue;
                        /*
                         * If the target behavior port asymmetric access state
-                        * is changed for any target port group accessiable via
+                        * is changed for any target port group accessible via
                         * a logical unit within a LU group, the target port
                         * behavior group asymmetric access states for the same
                         * target port group accessible via other logical units
@@ -970,7 +965,7 @@ int core_alua_do_port_transition(
                         * success.
                         */
                        core_alua_do_transition_tg_pt(tg_pt_gp, port,
-                                       nacl, md_buf, new_state, explict);
+                                       nacl, md_buf, new_state, explicit);
 
                        spin_lock(&dev->t10_alua.tg_pt_gps_lock);
                        atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt);
@@ -987,7 +982,7 @@ int core_alua_do_port_transition(
        pr_debug("Successfully processed LU Group: %s all ALUA TG PT"
                " Group IDs: %hu %s transition to primary state: %s\n",
                config_item_name(&lu_gp->lu_gp_group.cg_item),
-               l_tg_pt_gp->tg_pt_gp_id, (explict) ? "explict" : "implict",
+               l_tg_pt_gp->tg_pt_gp_id, (explicit) ? "explicit" : "implicit",
                core_alua_dump_state(new_state));
 
        atomic_dec(&lu_gp->lu_gp_ref_cnt);
@@ -1034,7 +1029,7 @@ static int core_alua_update_tpg_secondary_metadata(
 static int core_alua_set_tg_pt_secondary_state(
        struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem,
        struct se_port *port,
-       int explict,
+       int explicit,
        int offline)
 {
        struct t10_alua_tg_pt_gp *tg_pt_gp;
@@ -1061,13 +1056,13 @@ static int core_alua_set_tg_pt_secondary_state(
                atomic_set(&port->sep_tg_pt_secondary_offline, 0);
 
        md_buf_len = tg_pt_gp->tg_pt_gp_md_buf_len;
-       port->sep_tg_pt_secondary_stat = (explict) ?
-                       ALUA_STATUS_ALTERED_BY_EXPLICT_STPG :
-                       ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA;
+       port->sep_tg_pt_secondary_stat = (explicit) ?
+                       ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG :
+                       ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA;
 
        pr_debug("Successful %s ALUA transition TG PT Group: %s ID: %hu"
-               " to secondary access state: %s\n", (explict) ? "explict" :
-               "implict", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item),
+               " to secondary access state: %s\n", (explicit) ? "explicit" :
+               "implicit", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item),
                tg_pt_gp->tg_pt_gp_id, (offline) ? "OFFLINE" : "ONLINE");
 
        spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
@@ -1232,7 +1227,7 @@ void core_alua_free_lu_gp(struct t10_alua_lu_gp *lu_gp)
                 * struct se_device is released via core_alua_free_lu_gp_mem().
                 *
                 * If the passed lu_gp does NOT match the default_lu_gp, assume
-                * we want to re-assocate a given lu_gp_mem with default_lu_gp.
+                * we want to re-associate a given lu_gp_mem with default_lu_gp.
                 */
                spin_lock(&lu_gp_mem->lu_gp_mem_lock);
                if (lu_gp != default_lu_gp)
@@ -1354,18 +1349,25 @@ struct t10_alua_tg_pt_gp *core_alua_allocate_tg_pt_gp(struct se_device *dev,
        tg_pt_gp->tg_pt_gp_dev = dev;
        tg_pt_gp->tg_pt_gp_md_buf_len = ALUA_MD_BUF_LEN;
        atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state,
-               ALUA_ACCESS_STATE_ACTIVE_OPTMIZED);
+               ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED);
        /*
-        * Enable both explict and implict ALUA support by default
+        * Enable both explicit and implicit ALUA support by default
         */
        tg_pt_gp->tg_pt_gp_alua_access_type =
-                       TPGS_EXPLICT_ALUA | TPGS_IMPLICT_ALUA;
+                       TPGS_EXPLICIT_ALUA | TPGS_IMPLICIT_ALUA;
        /*
         * Set the default Active/NonOptimized Delay in milliseconds
         */
        tg_pt_gp->tg_pt_gp_nonop_delay_msecs = ALUA_DEFAULT_NONOP_DELAY_MSECS;
        tg_pt_gp->tg_pt_gp_trans_delay_msecs = ALUA_DEFAULT_TRANS_DELAY_MSECS;
-       tg_pt_gp->tg_pt_gp_implict_trans_secs = ALUA_DEFAULT_IMPLICT_TRANS_SECS;
+       tg_pt_gp->tg_pt_gp_implicit_trans_secs = ALUA_DEFAULT_IMPLICIT_TRANS_SECS;
+
+       /*
+        * Enable all supported states
+        */
+       tg_pt_gp->tg_pt_gp_alua_supported_states =
+           ALUA_T_SUP | ALUA_O_SUP |
+           ALUA_U_SUP | ALUA_S_SUP | ALUA_AN_SUP | ALUA_AO_SUP;
 
        if (def_group) {
                spin_lock(&dev->t10_alua.tg_pt_gps_lock);
@@ -1465,7 +1467,7 @@ void core_alua_free_tg_pt_gp(
         * been called from target_core_alua_drop_tg_pt_gp().
         *
         * Here we remove *tg_pt_gp from the global list so that
-        * no assications *OR* explict ALUA via SET_TARGET_PORT_GROUPS
+        * no associations *OR* explicit ALUA via SET_TARGET_PORT_GROUPS
         * can be made while we are releasing struct t10_alua_tg_pt_gp.
         */
        spin_lock(&dev->t10_alua.tg_pt_gps_lock);
@@ -1501,7 +1503,7 @@ void core_alua_free_tg_pt_gp(
                 * core_alua_free_tg_pt_gp_mem().
                 *
                 * If the passed tg_pt_gp does NOT match the default_tg_pt_gp,
-                * assume we want to re-assocate a given tg_pt_gp_mem with
+                * assume we want to re-associate a given tg_pt_gp_mem with
                 * default_tg_pt_gp.
                 */
                spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
@@ -1740,13 +1742,13 @@ ssize_t core_alua_show_access_type(
        struct t10_alua_tg_pt_gp *tg_pt_gp,
        char *page)
 {
-       if ((tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICT_ALUA) &&
-           (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICT_ALUA))
-               return sprintf(page, "Implict and Explict\n");
-       else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICT_ALUA)
-               return sprintf(page, "Implict\n");
-       else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICT_ALUA)
-               return sprintf(page, "Explict\n");
+       if ((tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA) &&
+           (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICIT_ALUA))
+               return sprintf(page, "Implicit and Explicit\n");
+       else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICIT_ALUA)
+               return sprintf(page, "Implicit\n");
+       else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA)
+               return sprintf(page, "Explicit\n");
        else
                return sprintf(page, "None\n");
 }
@@ -1771,11 +1773,11 @@ ssize_t core_alua_store_access_type(
        }
        if (tmp == 3)
                tg_pt_gp->tg_pt_gp_alua_access_type =
-                       TPGS_IMPLICT_ALUA | TPGS_EXPLICT_ALUA;
+                       TPGS_IMPLICIT_ALUA | TPGS_EXPLICIT_ALUA;
        else if (tmp == 2)
-               tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_EXPLICT_ALUA;
+               tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_EXPLICIT_ALUA;
        else if (tmp == 1)
-               tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_IMPLICT_ALUA;
+               tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_IMPLICIT_ALUA;
        else
                tg_pt_gp->tg_pt_gp_alua_access_type = 0;
 
@@ -1844,14 +1846,14 @@ ssize_t core_alua_store_trans_delay_msecs(
        return count;
 }
 
-ssize_t core_alua_show_implict_trans_secs(
+ssize_t core_alua_show_implicit_trans_secs(
        struct t10_alua_tg_pt_gp *tg_pt_gp,
        char *page)
 {
-       return sprintf(page, "%d\n", tg_pt_gp->tg_pt_gp_implict_trans_secs);
+       return sprintf(page, "%d\n", tg_pt_gp->tg_pt_gp_implicit_trans_secs);
 }
 
-ssize_t core_alua_store_implict_trans_secs(
+ssize_t core_alua_store_implicit_trans_secs(
        struct t10_alua_tg_pt_gp *tg_pt_gp,
        const char *page,
        size_t count)
@@ -1861,16 +1863,16 @@ ssize_t core_alua_store_implict_trans_secs(
 
        ret = kstrtoul(page, 0, &tmp);
        if (ret < 0) {
-               pr_err("Unable to extract implict_trans_secs\n");
+               pr_err("Unable to extract implicit_trans_secs\n");
                return ret;
        }
-       if (tmp > ALUA_MAX_IMPLICT_TRANS_SECS) {
-               pr_err("Passed implict_trans_secs: %lu, exceeds"
-                       " ALUA_MAX_IMPLICT_TRANS_SECS: %d\n", tmp,
-                       ALUA_MAX_IMPLICT_TRANS_SECS);
+       if (tmp > ALUA_MAX_IMPLICIT_TRANS_SECS) {
+               pr_err("Passed implicit_trans_secs: %lu, exceeds"
+                       " ALUA_MAX_IMPLICIT_TRANS_SECS: %d\n", tmp,
+                       ALUA_MAX_IMPLICIT_TRANS_SECS);
                return  -EINVAL;
        }
-       tg_pt_gp->tg_pt_gp_implict_trans_secs = (int)tmp;
+       tg_pt_gp->tg_pt_gp_implicit_trans_secs = (int)tmp;
 
        return count;
 }
@@ -1970,8 +1972,8 @@ ssize_t core_alua_store_secondary_status(
                return ret;
        }
        if ((tmp != ALUA_STATUS_NONE) &&
-           (tmp != ALUA_STATUS_ALTERED_BY_EXPLICT_STPG) &&
-           (tmp != ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA)) {
+           (tmp != ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG) &&
+           (tmp != ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA)) {
                pr_err("Illegal value for alua_tg_pt_status: %lu\n",
                                tmp);
                return -EINVAL;
index e539c3e..88e2e83 100644 (file)
@@ -7,15 +7,15 @@
  * from spc4r17 section 6.4.2 Table 135
  */
 #define TPGS_NO_ALUA                           0x00
-#define TPGS_IMPLICT_ALUA                      0x10
-#define TPGS_EXPLICT_ALUA                      0x20
+#define TPGS_IMPLICIT_ALUA                     0x10
+#define TPGS_EXPLICIT_ALUA                     0x20
 
 /*
  * ASYMMETRIC ACCESS STATE field
  *
  * from spc4r17 section 6.27 Table 245
  */
-#define ALUA_ACCESS_STATE_ACTIVE_OPTMIZED      0x0
+#define ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED     0x0
 #define ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED 0x1
 #define ALUA_ACCESS_STATE_STANDBY              0x2
 #define ALUA_ACCESS_STATE_UNAVAILABLE          0x3
 #define ALUA_ACCESS_STATE_TRANSITION           0xf
 
 /*
+ * from spc4r36j section 6.37 Table 306
+ */
+#define ALUA_T_SUP             0x80
+#define ALUA_O_SUP             0x40
+#define ALUA_LBD_SUP           0x10
+#define ALUA_U_SUP             0x08
+#define ALUA_S_SUP             0x04
+#define ALUA_AN_SUP            0x02
+#define ALUA_AO_SUP            0x01
+
+/*
  * REPORT_TARGET_PORT_GROUP STATUS CODE
  *
  * from spc4r17 section 6.27 Table 246
  */
 #define ALUA_STATUS_NONE                               0x00
-#define ALUA_STATUS_ALTERED_BY_EXPLICT_STPG            0x01
-#define ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA            0x02
+#define ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG           0x01
+#define ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA           0x02
 
 /*
  * From spc4r17, Table D.1: ASC and ASCQ Assignement
 #define ALUA_DEFAULT_NONOP_DELAY_MSECS                 100
 #define ALUA_MAX_NONOP_DELAY_MSECS                     10000 /* 10 seconds */
 /*
- * Used for implict and explict ALUA transitional delay, that is disabled
+ * Used for implicit and explicit ALUA transitional delay, that is disabled
  * by default, and is intended to be used for debugging client side ALUA code.
  */
 #define ALUA_DEFAULT_TRANS_DELAY_MSECS                 0
 #define ALUA_MAX_TRANS_DELAY_MSECS                     30000 /* 30 seconds */
 /*
- * Used for the recommended application client implict transition timeout
+ * Used for the recommended application client implicit transition timeout
  * in seconds, returned by the REPORT_TARGET_PORT_GROUPS w/ extended header.
  */
-#define ALUA_DEFAULT_IMPLICT_TRANS_SECS                        0
-#define ALUA_MAX_IMPLICT_TRANS_SECS                    255
+#define ALUA_DEFAULT_IMPLICIT_TRANS_SECS                       0
+#define ALUA_MAX_IMPLICIT_TRANS_SECS                   255
 /*
  * Used by core_alua_update_tpg_primary_metadata() and
  * core_alua_update_tpg_secondary_metadata()
@@ -113,9 +124,9 @@ extern ssize_t core_alua_show_trans_delay_msecs(struct t10_alua_tg_pt_gp *,
                                        char *);
 extern ssize_t core_alua_store_trans_delay_msecs(struct t10_alua_tg_pt_gp *,
                                        const char *, size_t);
-extern ssize_t core_alua_show_implict_trans_secs(struct t10_alua_tg_pt_gp *,
+extern ssize_t core_alua_show_implicit_trans_secs(struct t10_alua_tg_pt_gp *,
                                        char *);
-extern ssize_t core_alua_store_implict_trans_secs(struct t10_alua_tg_pt_gp *,
+extern ssize_t core_alua_store_implicit_trans_secs(struct t10_alua_tg_pt_gp *,
                                        const char *, size_t);
 extern ssize_t core_alua_show_preferred_bit(struct t10_alua_tg_pt_gp *,
                                        char *);
index 82e81c5..272755d 100644 (file)
@@ -177,16 +177,16 @@ static struct config_group *target_core_register_fabric(
         * struct target_fabric_configfs *tf will contain a usage reference.
         */
        pr_debug("Target_Core_ConfigFS: REGISTER tfc_wwn_cit -> %p\n",
-                       &TF_CIT_TMPL(tf)->tfc_wwn_cit);
+                       &tf->tf_cit_tmpl.tfc_wwn_cit);
 
        tf->tf_group.default_groups = tf->tf_default_groups;
        tf->tf_group.default_groups[0] = &tf->tf_disc_group;
        tf->tf_group.default_groups[1] = NULL;
 
        config_group_init_type_name(&tf->tf_group, name,
-                       &TF_CIT_TMPL(tf)->tfc_wwn_cit);
+                       &tf->tf_cit_tmpl.tfc_wwn_cit);
        config_group_init_type_name(&tf->tf_disc_group, "discovery_auth",
-                       &TF_CIT_TMPL(tf)->tfc_discovery_cit);
+                       &tf->tf_cit_tmpl.tfc_discovery_cit);
 
        pr_debug("Target_Core_ConfigFS: REGISTER -> Allocated Fabric:"
                        " %s\n", tf->tf_group.cg_item.ci_name);
@@ -2036,7 +2036,7 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_state(
        int new_state, ret;
 
        if (!tg_pt_gp->tg_pt_gp_valid_id) {
-               pr_err("Unable to do implict ALUA on non valid"
+               pr_err("Unable to do implicit ALUA on non valid"
                        " tg_pt_gp ID: %hu\n", tg_pt_gp->tg_pt_gp_valid_id);
                return -EINVAL;
        }
@@ -2049,9 +2049,9 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_state(
        }
        new_state = (int)tmp;
 
-       if (!(tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICT_ALUA)) {
-               pr_err("Unable to process implict configfs ALUA"
-                       " transition while TPGS_IMPLICT_ALUA is disabled\n");
+       if (!(tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICIT_ALUA)) {
+               pr_err("Unable to process implicit configfs ALUA"
+                       " transition while TPGS_IMPLICIT_ALUA is disabled\n");
                return -EINVAL;
        }
 
@@ -2097,8 +2097,8 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_status(
        new_status = (int)tmp;
 
        if ((new_status != ALUA_STATUS_NONE) &&
-           (new_status != ALUA_STATUS_ALTERED_BY_EXPLICT_STPG) &&
-           (new_status != ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA)) {
+           (new_status != ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG) &&
+           (new_status != ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA)) {
                pr_err("Illegal ALUA access status: 0x%02x\n",
                                new_status);
                return -EINVAL;
@@ -2131,6 +2131,90 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_type(
 SE_DEV_ALUA_TG_PT_ATTR(alua_access_type, S_IRUGO | S_IWUSR);
 
 /*
+ * alua_supported_states
+ */
+
+#define SE_DEV_ALUA_SUPPORT_STATE_SHOW(_name, _var, _bit)              \
+static ssize_t target_core_alua_tg_pt_gp_show_attr_alua_support_##_name( \
+       struct t10_alua_tg_pt_gp *t, char *p)                           \
+{                                                                      \
+       return sprintf(p, "%d\n", !!(t->_var & _bit));                  \
+}
+
+#define SE_DEV_ALUA_SUPPORT_STATE_STORE(_name, _var, _bit)             \
+static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_support_##_name(\
+       struct t10_alua_tg_pt_gp *t, const char *p, size_t c)           \
+{                                                                      \
+       unsigned long tmp;                                              \
+       int ret;                                                        \
+                                                                       \
+       if (!t->tg_pt_gp_valid_id) {                                    \
+               pr_err("Unable to do set ##_name ALUA state on non"     \
+                      " valid tg_pt_gp ID: %hu\n",                     \
+                      t->tg_pt_gp_valid_id);                           \
+               return -EINVAL;                                         \
+       }                                                               \
+                                                                       \
+       ret = kstrtoul(p, 0, &tmp);                                     \
+       if (ret < 0) {                                                  \
+               pr_err("Invalid value '%s', must be '0' or '1'\n", p);  \
+               return -EINVAL;                                         \
+       }                                                               \
+       if (tmp > 1) {                                                  \
+               pr_err("Invalid value '%ld', must be '0' or '1'\n", tmp); \
+               return -EINVAL;                                         \
+       }                                                               \
+       if (!tmp)                                                       \
+               t->_var |= _bit;                                        \
+       else                                                            \
+               t->_var &= ~_bit;                                       \
+                                                                       \
+       return c;                                                       \
+}
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(transitioning,
+                              tg_pt_gp_alua_supported_states, ALUA_T_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(transitioning,
+                               tg_pt_gp_alua_supported_states, ALUA_T_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_transitioning, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(offline,
+                              tg_pt_gp_alua_supported_states, ALUA_O_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(offline,
+                               tg_pt_gp_alua_supported_states, ALUA_O_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_offline, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(lba_dependent,
+                              tg_pt_gp_alua_supported_states, ALUA_LBD_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(lba_dependent,
+                               tg_pt_gp_alua_supported_states, ALUA_LBD_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_lba_dependent, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(unavailable,
+                              tg_pt_gp_alua_supported_states, ALUA_U_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(unavailable,
+                               tg_pt_gp_alua_supported_states, ALUA_U_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_unavailable, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(standby,
+                              tg_pt_gp_alua_supported_states, ALUA_S_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(standby,
+                               tg_pt_gp_alua_supported_states, ALUA_S_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_standby, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(active_optimized,
+                              tg_pt_gp_alua_supported_states, ALUA_AO_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(active_optimized,
+                               tg_pt_gp_alua_supported_states, ALUA_AO_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_active_optimized, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(active_nonoptimized,
+                              tg_pt_gp_alua_supported_states, ALUA_AN_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(active_nonoptimized,
+                               tg_pt_gp_alua_supported_states, ALUA_AN_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_active_nonoptimized, S_IRUGO | S_IWUSR);
+
+/*
  * alua_write_metadata
  */
 static ssize_t target_core_alua_tg_pt_gp_show_attr_alua_write_metadata(
@@ -2210,24 +2294,24 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_trans_delay_msecs(
 SE_DEV_ALUA_TG_PT_ATTR(trans_delay_msecs, S_IRUGO | S_IWUSR);
 
 /*
- * implict_trans_secs
+ * implicit_trans_secs
  */
-static ssize_t target_core_alua_tg_pt_gp_show_attr_implict_trans_secs(
+static ssize_t target_core_alua_tg_pt_gp_show_attr_implicit_trans_secs(
        struct t10_alua_tg_pt_gp *tg_pt_gp,
        char *page)
 {
-       return core_alua_show_implict_trans_secs(tg_pt_gp, page);
+       return core_alua_show_implicit_trans_secs(tg_pt_gp, page);
 }
 
-static ssize_t target_core_alua_tg_pt_gp_store_attr_implict_trans_secs(
+static ssize_t target_core_alua_tg_pt_gp_store_attr_implicit_trans_secs(
        struct t10_alua_tg_pt_gp *tg_pt_gp,
        const char *page,
        size_t count)
 {
-       return core_alua_store_implict_trans_secs(tg_pt_gp, page, count);
+       return core_alua_store_implicit_trans_secs(tg_pt_gp, page, count);
 }
 
-SE_DEV_ALUA_TG_PT_ATTR(implict_trans_secs, S_IRUGO | S_IWUSR);
+SE_DEV_ALUA_TG_PT_ATTR(implicit_trans_secs, S_IRUGO | S_IWUSR);
 
 /*
  * preferred
@@ -2350,10 +2434,17 @@ static struct configfs_attribute *target_core_alua_tg_pt_gp_attrs[] = {
        &target_core_alua_tg_pt_gp_alua_access_state.attr,
        &target_core_alua_tg_pt_gp_alua_access_status.attr,
        &target_core_alua_tg_pt_gp_alua_access_type.attr,
+       &target_core_alua_tg_pt_gp_alua_support_transitioning.attr,
+       &target_core_alua_tg_pt_gp_alua_support_offline.attr,
+       &target_core_alua_tg_pt_gp_alua_support_lba_dependent.attr,
+       &target_core_alua_tg_pt_gp_alua_support_unavailable.attr,
+       &target_core_alua_tg_pt_gp_alua_support_standby.attr,
+       &target_core_alua_tg_pt_gp_alua_support_active_nonoptimized.attr,
+       &target_core_alua_tg_pt_gp_alua_support_active_optimized.attr,
        &target_core_alua_tg_pt_gp_alua_write_metadata.attr,
        &target_core_alua_tg_pt_gp_nonop_delay_msecs.attr,
        &target_core_alua_tg_pt_gp_trans_delay_msecs.attr,
-       &target_core_alua_tg_pt_gp_implict_trans_secs.attr,
+       &target_core_alua_tg_pt_gp_implicit_trans_secs.attr,
        &target_core_alua_tg_pt_gp_preferred.attr,
        &target_core_alua_tg_pt_gp_tg_pt_gp_id.attr,
        &target_core_alua_tg_pt_gp_members.attr,
index d90dbb0..207b340 100644 (file)
@@ -92,6 +92,9 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u32 unpacked_lun)
                se_cmd->pr_res_key = deve->pr_res_key;
                se_cmd->orig_fe_lun = unpacked_lun;
                se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;
+
+               percpu_ref_get(&se_lun->lun_ref);
+               se_cmd->lun_ref_active = true;
        }
        spin_unlock_irqrestore(&se_sess->se_node_acl->device_list_lock, flags);
 
@@ -119,24 +122,20 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u32 unpacked_lun)
                se_cmd->se_lun = &se_sess->se_tpg->tpg_virt_lun0;
                se_cmd->orig_fe_lun = 0;
                se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;
+
+               percpu_ref_get(&se_lun->lun_ref);
+               se_cmd->lun_ref_active = true;
        }
 
        /* Directly associate cmd with se_dev */
        se_cmd->se_dev = se_lun->lun_se_dev;
 
-       /* TODO: get rid of this and use atomics for stats */
        dev = se_lun->lun_se_dev;
-       spin_lock_irqsave(&dev->stats_lock, flags);
-       dev->num_cmds++;
+       atomic_long_inc(&dev->num_cmds);
        if (se_cmd->data_direction == DMA_TO_DEVICE)
-               dev->write_bytes += se_cmd->data_length;
+               atomic_long_add(se_cmd->data_length, &dev->write_bytes);
        else if (se_cmd->data_direction == DMA_FROM_DEVICE)
-               dev->read_bytes += se_cmd->data_length;
-       spin_unlock_irqrestore(&dev->stats_lock, flags);
-
-       spin_lock_irqsave(&se_lun->lun_cmd_lock, flags);
-       list_add_tail(&se_cmd->se_lun_node, &se_lun->lun_cmd_list);
-       spin_unlock_irqrestore(&se_lun->lun_cmd_lock, flags);
+               atomic_long_add(se_cmd->data_length, &dev->read_bytes);
 
        return 0;
 }
@@ -314,14 +313,14 @@ int core_enable_device_list_for_node(
        deve = nacl->device_list[mapped_lun];
 
        /*
-        * Check if the call is handling demo mode -> explict LUN ACL
+        * Check if the call is handling demo mode -> explicit LUN ACL
         * transition.  This transition must be for the same struct se_lun
         * + mapped_lun that was setup in demo mode..
         */
        if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {
                if (deve->se_lun_acl != NULL) {
                        pr_err("struct se_dev_entry->se_lun_acl"
-                              " already set for demo mode -> explict"
+                              " already set for demo mode -> explicit"
                               " LUN ACL transition\n");
                        spin_unlock_irq(&nacl->device_list_lock);
                        return -EINVAL;
@@ -329,7 +328,7 @@ int core_enable_device_list_for_node(
                if (deve->se_lun != lun) {
                        pr_err("struct se_dev_entry->se_lun does"
                               " match passed struct se_lun for demo mode"
-                              " -> explict LUN ACL transition\n");
+                              " -> explicit LUN ACL transition\n");
                        spin_unlock_irq(&nacl->device_list_lock);
                        return -EINVAL;
                }
@@ -1407,6 +1406,7 @@ static void scsi_dump_inquiry(struct se_device *dev)
 struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
 {
        struct se_device *dev;
+       struct se_lun *xcopy_lun;
 
        dev = hba->transport->alloc_device(hba, name);
        if (!dev)
@@ -1423,7 +1423,6 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
        INIT_LIST_HEAD(&dev->state_list);
        INIT_LIST_HEAD(&dev->qf_cmd_list);
        INIT_LIST_HEAD(&dev->g_dev_node);
-       spin_lock_init(&dev->stats_lock);
        spin_lock_init(&dev->execute_task_lock);
        spin_lock_init(&dev->delayed_cmd_lock);
        spin_lock_init(&dev->dev_reservation_lock);
@@ -1469,6 +1468,14 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
        dev->dev_attrib.fabric_max_sectors = DA_FABRIC_MAX_SECTORS;
        dev->dev_attrib.optimal_sectors = DA_FABRIC_MAX_SECTORS;
 
+       xcopy_lun = &dev->xcopy_lun;
+       xcopy_lun->lun_se_dev = dev;
+       init_completion(&xcopy_lun->lun_shutdown_comp);
+       INIT_LIST_HEAD(&xcopy_lun->lun_acl_list);
+       spin_lock_init(&xcopy_lun->lun_acl_lock);
+       spin_lock_init(&xcopy_lun->lun_sep_lock);
+       init_completion(&xcopy_lun->lun_ref_comp);
+
        return dev;
 }
 
index 3503996..dae2ad6 100644 (file)
@@ -385,9 +385,9 @@ static struct config_group *target_fabric_make_mappedlun(
        }
 
        config_group_init_type_name(&lacl->se_lun_group, name,
-                       &TF_CIT_TMPL(tf)->tfc_tpg_mappedlun_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_mappedlun_cit);
        config_group_init_type_name(&lacl->ml_stat_grps.stat_group,
-                       "statistics", &TF_CIT_TMPL(tf)->tfc_tpg_mappedlun_stat_cit);
+                       "statistics", &tf->tf_cit_tmpl.tfc_tpg_mappedlun_stat_cit);
        lacl_cg->default_groups[0] = &lacl->ml_stat_grps.stat_group;
        lacl_cg->default_groups[1] = NULL;
 
@@ -504,16 +504,16 @@ static struct config_group *target_fabric_make_nodeacl(
        nacl_cg->default_groups[4] = NULL;
 
        config_group_init_type_name(&se_nacl->acl_group, name,
-                       &TF_CIT_TMPL(tf)->tfc_tpg_nacl_base_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_nacl_base_cit);
        config_group_init_type_name(&se_nacl->acl_attrib_group, "attrib",
-                       &TF_CIT_TMPL(tf)->tfc_tpg_nacl_attrib_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit);
        config_group_init_type_name(&se_nacl->acl_auth_group, "auth",
-                       &TF_CIT_TMPL(tf)->tfc_tpg_nacl_auth_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_nacl_auth_cit);
        config_group_init_type_name(&se_nacl->acl_param_group, "param",
-                       &TF_CIT_TMPL(tf)->tfc_tpg_nacl_param_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_nacl_param_cit);
        config_group_init_type_name(&se_nacl->acl_fabric_stat_group,
                        "fabric_statistics",
-                       &TF_CIT_TMPL(tf)->tfc_tpg_nacl_stat_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_nacl_stat_cit);
 
        return &se_nacl->acl_group;
 }
@@ -595,7 +595,7 @@ static struct config_group *target_fabric_make_np(
 
        se_tpg_np->tpg_np_parent = se_tpg;
        config_group_init_type_name(&se_tpg_np->tpg_np_group, name,
-                       &TF_CIT_TMPL(tf)->tfc_tpg_np_base_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_np_base_cit);
 
        return &se_tpg_np->tpg_np_group;
 }
@@ -899,9 +899,9 @@ static struct config_group *target_fabric_make_lun(
        }
 
        config_group_init_type_name(&lun->lun_group, name,
-                       &TF_CIT_TMPL(tf)->tfc_tpg_port_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_port_cit);
        config_group_init_type_name(&lun->port_stat_grps.stat_group,
-                       "statistics", &TF_CIT_TMPL(tf)->tfc_tpg_port_stat_cit);
+                       "statistics", &tf->tf_cit_tmpl.tfc_tpg_port_stat_cit);
        lun_cg->default_groups[0] = &lun->port_stat_grps.stat_group;
        lun_cg->default_groups[1] = NULL;
 
@@ -1056,19 +1056,19 @@ static struct config_group *target_fabric_make_tpg(
        se_tpg->tpg_group.default_groups[6] = NULL;
 
        config_group_init_type_name(&se_tpg->tpg_group, name,
-                       &TF_CIT_TMPL(tf)->tfc_tpg_base_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_base_cit);
        config_group_init_type_name(&se_tpg->tpg_lun_group, "lun",
-                       &TF_CIT_TMPL(tf)->tfc_tpg_lun_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_lun_cit);
        config_group_init_type_name(&se_tpg->tpg_np_group, "np",
-                       &TF_CIT_TMPL(tf)->tfc_tpg_np_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_np_cit);
        config_group_init_type_name(&se_tpg->tpg_acl_group, "acls",
-                       &TF_CIT_TMPL(tf)->tfc_tpg_nacl_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_nacl_cit);
        config_group_init_type_name(&se_tpg->tpg_attrib_group, "attrib",
-                       &TF_CIT_TMPL(tf)->tfc_tpg_attrib_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_attrib_cit);
        config_group_init_type_name(&se_tpg->tpg_auth_group, "auth",
-                       &TF_CIT_TMPL(tf)->tfc_tpg_auth_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_auth_cit);
        config_group_init_type_name(&se_tpg->tpg_param_group, "param",
-                       &TF_CIT_TMPL(tf)->tfc_tpg_param_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_param_cit);
 
        return &se_tpg->tpg_group;
 }
@@ -1155,9 +1155,9 @@ static struct config_group *target_fabric_make_wwn(
        wwn->wwn_group.default_groups[1] = NULL;
 
        config_group_init_type_name(&wwn->wwn_group, name,
-                       &TF_CIT_TMPL(tf)->tfc_tpg_cit);
+                       &tf->tf_cit_tmpl.tfc_tpg_cit);
        config_group_init_type_name(&wwn->fabric_stat_group, "fabric_statistics",
-                       &TF_CIT_TMPL(tf)->tfc_wwn_fabric_stats_cit);
+                       &tf->tf_cit_tmpl.tfc_wwn_fabric_stats_cit);
 
        return &wwn->wwn_group;
 }
index b662f89..0e34cda 100644 (file)
@@ -562,7 +562,7 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
        } else {
                ret = fd_do_rw(cmd, sgl, sgl_nents, 1);
                /*
-                * Perform implict vfs_fsync_range() for fd_do_writev() ops
+                * Perform implicit vfs_fsync_range() for fd_do_writev() ops
                 * for SCSI WRITEs with Forced Unit Access (FUA) set.
                 * Allow this to happen independent of WCE=0 setting.
                 */
index b9a3394..c87959f 100644 (file)
@@ -710,6 +710,45 @@ static sector_t iblock_get_blocks(struct se_device *dev)
        return iblock_emulate_read_cap_with_block_size(dev, bd, q);
 }
 
+static sector_t iblock_get_alignment_offset_lbas(struct se_device *dev)
+{
+       struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+       struct block_device *bd = ib_dev->ibd_bd;
+       int ret;
+
+       ret = bdev_alignment_offset(bd);
+       if (ret == -1)
+               return 0;
+
+       /* convert offset-bytes to offset-lbas */
+       return ret / bdev_logical_block_size(bd);
+}
+
+static unsigned int iblock_get_lbppbe(struct se_device *dev)
+{
+       struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+       struct block_device *bd = ib_dev->ibd_bd;
+       int logs_per_phys = bdev_physical_block_size(bd) / bdev_logical_block_size(bd);
+
+       return ilog2(logs_per_phys);
+}
+
+static unsigned int iblock_get_io_min(struct se_device *dev)
+{
+       struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+       struct block_device *bd = ib_dev->ibd_bd;
+
+       return bdev_io_min(bd);
+}
+
+static unsigned int iblock_get_io_opt(struct se_device *dev)
+{
+       struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+       struct block_device *bd = ib_dev->ibd_bd;
+
+       return bdev_io_opt(bd);
+}
+
 static struct sbc_ops iblock_sbc_ops = {
        .execute_rw             = iblock_execute_rw,
        .execute_sync_cache     = iblock_execute_sync_cache,
@@ -749,6 +788,10 @@ static struct se_subsystem_api iblock_template = {
        .show_configfs_dev_params = iblock_show_configfs_dev_params,
        .get_device_type        = sbc_get_device_type,
        .get_blocks             = iblock_get_blocks,
+       .get_alignment_offset_lbas = iblock_get_alignment_offset_lbas,
+       .get_lbppbe             = iblock_get_lbppbe,
+       .get_io_min             = iblock_get_io_min,
+       .get_io_opt             = iblock_get_io_opt,
        .get_write_cache        = iblock_get_write_cache,
 };
 
index 579128a..47b63b0 100644 (file)
@@ -75,8 +75,6 @@ extern struct se_device *g_lun0_dev;
 
 struct se_node_acl *__core_tpg_get_initiator_node_acl(struct se_portal_group *tpg,
                const char *);
-struct se_node_acl *core_tpg_get_initiator_node_acl(struct se_portal_group *tpg,
-               unsigned char *);
 void   core_tpg_add_node_to_devs(struct se_node_acl *, struct se_portal_group *);
 void   core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *);
 struct se_lun *core_tpg_pre_addlun(struct se_portal_group *, u32);
@@ -102,7 +100,7 @@ int transport_dump_vpd_assoc(struct t10_vpd *, unsigned char *, int);
 int    transport_dump_vpd_ident_type(struct t10_vpd *, unsigned char *, int);
 int    transport_dump_vpd_ident(struct t10_vpd *, unsigned char *, int);
 bool   target_stop_cmd(struct se_cmd *cmd, unsigned long *flags);
-int    transport_clear_lun_from_sessions(struct se_lun *);
+int    transport_clear_lun_ref(struct se_lun *);
 void   transport_send_task_abort(struct se_cmd *);
 sense_reason_t target_cmd_size_check(struct se_cmd *cmd, unsigned int size);
 void   target_qf_do_work(struct work_struct *work);
index d1ae4c5..2f5d779 100644 (file)
@@ -474,7 +474,7 @@ static int core_scsi3_pr_seq_non_holder(
         * statement.
         */
        if (!ret && !other_cdb) {
-               pr_debug("Allowing explict CDB: 0x%02x for %s"
+               pr_debug("Allowing explicit CDB: 0x%02x for %s"
                        " reservation holder\n", cdb[0],
                        core_scsi3_pr_dump_type(pr_reg_type));
 
@@ -507,7 +507,7 @@ static int core_scsi3_pr_seq_non_holder(
                         */
 
                        if (!registered_nexus) {
-                               pr_debug("Allowing implict CDB: 0x%02x"
+                               pr_debug("Allowing implicit CDB: 0x%02x"
                                        " for %s reservation on unregistered"
                                        " nexus\n", cdb[0],
                                        core_scsi3_pr_dump_type(pr_reg_type));
@@ -522,7 +522,7 @@ static int core_scsi3_pr_seq_non_holder(
                         * allow commands from registered nexuses.
                         */
 
-                       pr_debug("Allowing implict CDB: 0x%02x for %s"
+                       pr_debug("Allowing implicit CDB: 0x%02x for %s"
                                " reservation\n", cdb[0],
                                core_scsi3_pr_dump_type(pr_reg_type));
 
@@ -683,7 +683,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration(
                                        alua_port_list) {
                        /*
                         * This pointer will be NULL for demo mode MappedLUNs
-                        * that have not been make explict via a ConfigFS
+                        * that have not been make explicit via a ConfigFS
                         * MappedLUN group for the SCSI Initiator Node ACL.
                         */
                        if (!deve_tmp->se_lun_acl)
@@ -1158,7 +1158,7 @@ static void core_scsi3_put_pr_reg(struct t10_pr_registration *pr_reg)
        smp_mb__after_atomic_dec();
 }
 
-static int core_scsi3_check_implict_release(
+static int core_scsi3_check_implicit_release(
        struct se_device *dev,
        struct t10_pr_registration *pr_reg)
 {
@@ -1174,7 +1174,7 @@ static int core_scsi3_check_implict_release(
        }
        if (pr_res_holder == pr_reg) {
                /*
-                * Perform an implict RELEASE if the registration that
+                * Perform an implicit RELEASE if the registration that
                 * is being released is holding the reservation.
                 *
                 * From spc4r17, section 5.7.11.1:
@@ -1192,7 +1192,7 @@ static int core_scsi3_check_implict_release(
                 * For 'All Registrants' reservation types, all existing
                 * registrations are still processed as reservation holders
                 * in core_scsi3_pr_seq_non_holder() after the initial
-                * reservation holder is implictly released here.
+                * reservation holder is implicitly released here.
                 */
        } else if (pr_reg->pr_reg_all_tg_pt &&
                  (!strcmp(pr_res_holder->pr_reg_nacl->initiatorname,
@@ -2125,7 +2125,7 @@ core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key,
                /*
                 * sa_res_key=0 Unregister Reservation Key for registered I_T Nexus.
                 */
-               pr_holder = core_scsi3_check_implict_release(
+               pr_holder = core_scsi3_check_implicit_release(
                                cmd->se_dev, pr_reg);
                if (pr_holder < 0) {
                        ret = TCM_RESERVATION_CONFLICT;
@@ -2402,7 +2402,7 @@ static void __core_scsi3_complete_pro_release(
        struct se_device *dev,
        struct se_node_acl *se_nacl,
        struct t10_pr_registration *pr_reg,
-       int explict)
+       int explicit)
 {
        struct target_core_fabric_ops *tfo = se_nacl->se_tpg->se_tpg_tfo;
        char i_buf[PR_REG_ISID_ID_LEN];
@@ -2416,7 +2416,7 @@ static void __core_scsi3_complete_pro_release(
 
        pr_debug("SPC-3 PR [%s] Service Action: %s RELEASE cleared"
                " reservation holder TYPE: %s ALL_TG_PT: %d\n",
-               tfo->get_fabric_name(), (explict) ? "explict" : "implict",
+               tfo->get_fabric_name(), (explicit) ? "explicit" : "implicit",
                core_scsi3_pr_dump_type(pr_reg->pr_res_type),
                (pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
        pr_debug("SPC-3 PR [%s] RELEASE Node: %s%s\n",
@@ -2692,7 +2692,7 @@ static void __core_scsi3_complete_pro_preempt(
        memset(i_buf, 0, PR_REG_ISID_ID_LEN);
        core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
        /*
-        * Do an implict RELEASE of the existing reservation.
+        * Do an implicit RELEASE of the existing reservation.
         */
        if (dev->dev_pr_res_holder)
                __core_scsi3_complete_pro_release(dev, nacl,
@@ -2845,7 +2845,7 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key,
                                 * 5.7.11.4 Preempting, Table 52 and Figure 7.
                                 *
                                 * For a ZERO SA Reservation key, release
-                                * all other registrations and do an implict
+                                * all other registrations and do an implicit
                                 * release of active persistent reservation.
                                 *
                                 * For a non-ZERO SA Reservation key, only
index 131327a..4ffe5f2 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/string.h>
 #include <linux/parser.h>
 #include <linux/timer.h>
-#include <linux/blkdev.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <scsi/scsi.h>
index d9b92b2..52ae54e 100644 (file)
@@ -105,12 +105,22 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd)
        buf[9] = (dev->dev_attrib.block_size >> 16) & 0xff;
        buf[10] = (dev->dev_attrib.block_size >> 8) & 0xff;
        buf[11] = dev->dev_attrib.block_size & 0xff;
+
+       if (dev->transport->get_lbppbe)
+               buf[13] = dev->transport->get_lbppbe(dev) & 0x0f;
+
+       if (dev->transport->get_alignment_offset_lbas) {
+               u16 lalba = dev->transport->get_alignment_offset_lbas(dev);
+               buf[14] = (lalba >> 8) & 0x3f;
+               buf[15] = lalba & 0xff;
+       }
+
        /*
         * Set Thin Provisioning Enable bit following sbc3r22 in section
         * READ CAPACITY (16) byte 14 if emulate_tpu or emulate_tpws is enabled.
         */
        if (dev->dev_attrib.emulate_tpu || dev->dev_attrib.emulate_tpws)
-               buf[14] = 0x80;
+               buf[14] |= 0x80;
 
        rbuf = transport_kmap_data_sg(cmd);
        if (rbuf) {
index 0745395..021c3f4 100644 (file)
@@ -48,7 +48,7 @@ static void spc_fill_alua_data(struct se_port *port, unsigned char *buf)
        buf[5]  = 0x80;
 
        /*
-        * Set TPGS field for explict and/or implict ALUA access type
+        * Set TPGS field for explicit and/or implicit ALUA access type
         * and opteration.
         *
         * See spc4r17 section 6.4.2 Table 135
@@ -452,6 +452,7 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
        struct se_device *dev = cmd->se_dev;
        u32 max_sectors;
        int have_tp = 0;
+       int opt, min;
 
        /*
         * Following spc3r22 section 6.5.3 Block Limits VPD page, when
@@ -475,7 +476,10 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
        /*
         * Set OPTIMAL TRANSFER LENGTH GRANULARITY
         */
-       put_unaligned_be16(1, &buf[6]);
+       if (dev->transport->get_io_min && (min = dev->transport->get_io_min(dev)))
+               put_unaligned_be16(min / dev->dev_attrib.block_size, &buf[6]);
+       else
+               put_unaligned_be16(1, &buf[6]);
 
        /*
         * Set MAXIMUM TRANSFER LENGTH
@@ -487,7 +491,10 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
        /*
         * Set OPTIMAL TRANSFER LENGTH
         */
-       put_unaligned_be32(dev->dev_attrib.optimal_sectors, &buf[12]);
+       if (dev->transport->get_io_opt && (opt = dev->transport->get_io_opt(dev)))
+               put_unaligned_be32(opt / dev->dev_attrib.block_size, &buf[12]);
+       else
+               put_unaligned_be32(dev->dev_attrib.optimal_sectors, &buf[12]);
 
        /*
         * Exit now if we don't support TP.
@@ -1250,7 +1257,7 @@ spc_parse_cdb(struct se_cmd *cmd, unsigned int *size)
                *size = (cdb[3] << 8) + cdb[4];
 
                /*
-                * Do implict HEAD_OF_QUEUE processing for INQUIRY.
+                * Do implicit HEAD_OF_QUEUE processing for INQUIRY.
                 * See spc4r17 section 5.3
                 */
                cmd->sam_task_attr = MSG_HEAD_TAG;
@@ -1284,7 +1291,7 @@ spc_parse_cdb(struct se_cmd *cmd, unsigned int *size)
                cmd->execute_cmd = spc_emulate_report_luns;
                *size = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
                /*
-                * Do implict HEAD_OF_QUEUE processing for REPORT_LUNS
+                * Do implicit HEAD_OF_QUEUE processing for REPORT_LUNS
                 * See spc4r17 section 5.3
                 */
                cmd->sam_task_attr = MSG_HEAD_TAG;
index 9c642e0..0353899 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/utsname.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-#include <linux/blkdev.h>
 #include <linux/configfs.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_device.h>
@@ -214,7 +213,8 @@ static ssize_t target_stat_scsi_tgt_dev_show_attr_resets(
        struct se_device *dev =
                container_of(sgrps, struct se_device, dev_stat_grps);
 
-       return snprintf(page, PAGE_SIZE, "%u\n", dev->num_resets);
+       return snprintf(page, PAGE_SIZE, "%lu\n",
+                       atomic_long_read(&dev->num_resets));
 }
 DEV_STAT_SCSI_TGT_DEV_ATTR_RO(resets);
 
@@ -397,8 +397,8 @@ static ssize_t target_stat_scsi_lu_show_attr_num_cmds(
                container_of(sgrps, struct se_device, dev_stat_grps);
 
        /* scsiLuNumCommands */
-       return snprintf(page, PAGE_SIZE, "%llu\n",
-                       (unsigned long long)dev->num_cmds);
+       return snprintf(page, PAGE_SIZE, "%lu\n",
+                       atomic_long_read(&dev->num_cmds));
 }
 DEV_STAT_SCSI_LU_ATTR_RO(num_cmds);
 
@@ -409,7 +409,8 @@ static ssize_t target_stat_scsi_lu_show_attr_read_mbytes(
                container_of(sgrps, struct se_device, dev_stat_grps);
 
        /* scsiLuReadMegaBytes */
-       return snprintf(page, PAGE_SIZE, "%u\n", (u32)(dev->read_bytes >> 20));
+       return snprintf(page, PAGE_SIZE, "%lu\n",
+                       atomic_long_read(&dev->read_bytes) >> 20);
 }
 DEV_STAT_SCSI_LU_ATTR_RO(read_mbytes);
 
@@ -420,7 +421,8 @@ static ssize_t target_stat_scsi_lu_show_attr_write_mbytes(
                container_of(sgrps, struct se_device, dev_stat_grps);
 
        /* scsiLuWrittenMegaBytes */
-       return snprintf(page, PAGE_SIZE, "%u\n", (u32)(dev->write_bytes >> 20));
+       return snprintf(page, PAGE_SIZE, "%lu\n",
+                       atomic_long_read(&dev->write_bytes) >> 20);
 }
 DEV_STAT_SCSI_LU_ATTR_RO(write_mbytes);
 
@@ -431,7 +433,7 @@ static ssize_t target_stat_scsi_lu_show_attr_resets(
                container_of(sgrps, struct se_device, dev_stat_grps);
 
        /* scsiLuInResets */
-       return snprintf(page, PAGE_SIZE, "%u\n", dev->num_resets);
+       return snprintf(page, PAGE_SIZE, "%lu\n", atomic_long_read(&dev->num_resets));
 }
 DEV_STAT_SCSI_LU_ATTR_RO(resets);
 
index 2500099..70c638f 100644 (file)
@@ -386,9 +386,7 @@ int core_tmr_lun_reset(
                pr_debug("LUN_RESET: SCSI-2 Released reservation\n");
        }
 
-       spin_lock_irq(&dev->stats_lock);
-       dev->num_resets++;
-       spin_unlock_irq(&dev->stats_lock);
+       atomic_long_inc(&dev->num_resets);
 
        pr_debug("LUN_RESET: %s for [%s] Complete\n",
                        (preempt_and_abort_list) ? "Preempt" : "TMR",
index b9a6ec0..f697f8b 100644 (file)
@@ -116,6 +116,7 @@ struct se_node_acl *core_tpg_get_initiator_node_acl(
 
        return acl;
 }
+EXPORT_SYMBOL(core_tpg_get_initiator_node_acl);
 
 /*     core_tpg_add_node_to_devs():
  *
@@ -633,6 +634,13 @@ int core_tpg_set_initiator_node_tag(
 }
 EXPORT_SYMBOL(core_tpg_set_initiator_node_tag);
 
+static void core_tpg_lun_ref_release(struct percpu_ref *ref)
+{
+       struct se_lun *lun = container_of(ref, struct se_lun, lun_ref);
+
+       complete(&lun->lun_ref_comp);
+}
+
 static int core_tpg_setup_virtual_lun0(struct se_portal_group *se_tpg)
 {
        /* Set in core_dev_setup_virtual_lun0() */
@@ -646,15 +654,20 @@ static int core_tpg_setup_virtual_lun0(struct se_portal_group *se_tpg)
        atomic_set(&lun->lun_acl_count, 0);
        init_completion(&lun->lun_shutdown_comp);
        INIT_LIST_HEAD(&lun->lun_acl_list);
-       INIT_LIST_HEAD(&lun->lun_cmd_list);
        spin_lock_init(&lun->lun_acl_lock);
-       spin_lock_init(&lun->lun_cmd_lock);
        spin_lock_init(&lun->lun_sep_lock);
+       init_completion(&lun->lun_ref_comp);
 
-       ret = core_tpg_post_addlun(se_tpg, lun, lun_access, dev);
+       ret = percpu_ref_init(&lun->lun_ref, core_tpg_lun_ref_release);
        if (ret < 0)
                return ret;
 
+       ret = core_tpg_post_addlun(se_tpg, lun, lun_access, dev);
+       if (ret < 0) {
+               percpu_ref_cancel_init(&lun->lun_ref);
+               return ret;
+       }
+
        return 0;
 }
 
@@ -691,10 +704,9 @@ int core_tpg_register(
                atomic_set(&lun->lun_acl_count, 0);
                init_completion(&lun->lun_shutdown_comp);
                INIT_LIST_HEAD(&lun->lun_acl_list);
-               INIT_LIST_HEAD(&lun->lun_cmd_list);
                spin_lock_init(&lun->lun_acl_lock);
-               spin_lock_init(&lun->lun_cmd_lock);
                spin_lock_init(&lun->lun_sep_lock);
+               init_completion(&lun->lun_ref_comp);
        }
 
        se_tpg->se_tpg_type = se_tpg_type;
@@ -815,10 +827,16 @@ int core_tpg_post_addlun(
 {
        int ret;
 
-       ret = core_dev_export(lun_ptr, tpg, lun);
+       ret = percpu_ref_init(&lun->lun_ref, core_tpg_lun_ref_release);
        if (ret < 0)
                return ret;
 
+       ret = core_dev_export(lun_ptr, tpg, lun);
+       if (ret < 0) {
+               percpu_ref_cancel_init(&lun->lun_ref);
+               return ret;
+       }
+
        spin_lock(&tpg->tpg_lun_lock);
        lun->lun_access = lun_access;
        lun->lun_status = TRANSPORT_LUN_STATUS_ACTIVE;
@@ -827,14 +845,6 @@ int core_tpg_post_addlun(
        return 0;
 }
 
-static void core_tpg_shutdown_lun(
-       struct se_portal_group *tpg,
-       struct se_lun *lun)
-{
-       core_clear_lun_from_tpg(lun, tpg);
-       transport_clear_lun_from_sessions(lun);
-}
-
 struct se_lun *core_tpg_pre_dellun(
        struct se_portal_group *tpg,
        u32 unpacked_lun)
@@ -869,7 +879,8 @@ int core_tpg_post_dellun(
        struct se_portal_group *tpg,
        struct se_lun *lun)
 {
-       core_tpg_shutdown_lun(tpg, lun);
+       core_clear_lun_from_tpg(lun, tpg);
+       transport_clear_lun_ref(lun);
 
        core_dev_unexport(lun->lun_se_dev, tpg, lun);
 
index 81e945e..91953da 100644 (file)
@@ -28,7 +28,6 @@
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/slab.h>
-#include <linux/blkdev.h>
 #include <linux/spinlock.h>
 #include <linux/kthread.h>
 #include <linux/in.h>
@@ -473,7 +472,7 @@ void transport_deregister_session(struct se_session *se_sess)
        pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n",
                se_tpg->se_tpg_tfo->get_fabric_name());
        /*
-        * If last kref is dropping now for an explict NodeACL, awake sleeping
+        * If last kref is dropping now for an explicit NodeACL, awake sleeping
         * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group
         * removal context.
         */
@@ -515,23 +514,6 @@ static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists,
        if (write_pending)
                cmd->t_state = TRANSPORT_WRITE_PENDING;
 
-       /*
-        * Determine if IOCTL context caller in requesting the stopping of this
-        * command for LUN shutdown purposes.
-        */
-       if (cmd->transport_state & CMD_T_LUN_STOP) {
-               pr_debug("%s:%d CMD_T_LUN_STOP for ITT: 0x%08x\n",
-                       __func__, __LINE__, cmd->se_tfo->get_task_tag(cmd));
-
-               cmd->transport_state &= ~CMD_T_ACTIVE;
-               if (remove_from_lists)
-                       target_remove_from_state_list(cmd);
-               spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-
-               complete(&cmd->transport_lun_stop_comp);
-               return 1;
-       }
-
        if (remove_from_lists) {
                target_remove_from_state_list(cmd);
 
@@ -585,15 +567,11 @@ static int transport_cmd_check_stop_to_fabric(struct se_cmd *cmd)
 static void transport_lun_remove_cmd(struct se_cmd *cmd)
 {
        struct se_lun *lun = cmd->se_lun;
-       unsigned long flags;
 
-       if (!lun)
+       if (!lun || !cmd->lun_ref_active)
                return;
 
-       spin_lock_irqsave(&lun->lun_cmd_lock, flags);
-       if (!list_empty(&cmd->se_lun_node))
-               list_del_init(&cmd->se_lun_node);
-       spin_unlock_irqrestore(&lun->lun_cmd_lock, flags);
+       percpu_ref_put(&lun->lun_ref);
 }
 
 void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
@@ -668,7 +646,7 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
                cmd->transport_state |= CMD_T_FAILED;
 
        /*
-        * Check for case where an explict ABORT_TASK has been received
+        * Check for case where an explicit ABORT_TASK has been received
         * and transport_wait_for_tasks() will be waiting for completion..
         */
        if (cmd->transport_state & CMD_T_ABORTED &&
@@ -1092,13 +1070,10 @@ void transport_init_se_cmd(
        int task_attr,
        unsigned char *sense_buffer)
 {
-       INIT_LIST_HEAD(&cmd->se_lun_node);
        INIT_LIST_HEAD(&cmd->se_delayed_node);
        INIT_LIST_HEAD(&cmd->se_qf_node);
        INIT_LIST_HEAD(&cmd->se_cmd_list);
        INIT_LIST_HEAD(&cmd->state_list);
-       init_completion(&cmd->transport_lun_fe_stop_comp);
-       init_completion(&cmd->transport_lun_stop_comp);
        init_completion(&cmd->t_transport_stop_comp);
        init_completion(&cmd->cmd_wait_comp);
        init_completion(&cmd->task_stop_comp);
@@ -1719,29 +1694,14 @@ void target_execute_cmd(struct se_cmd *cmd)
        /*
         * If the received CDB has aleady been aborted stop processing it here.
         */
-       if (transport_check_aborted_status(cmd, 1)) {
-               complete(&cmd->transport_lun_stop_comp);
+       if (transport_check_aborted_status(cmd, 1))
                return;
-       }
 
        /*
-        * Determine if IOCTL context caller in requesting the stopping of this
-        * command for LUN shutdown purposes.
-        */
-       spin_lock_irq(&cmd->t_state_lock);
-       if (cmd->transport_state & CMD_T_LUN_STOP) {
-               pr_debug("%s:%d CMD_T_LUN_STOP for ITT: 0x%08x\n",
-                       __func__, __LINE__, cmd->se_tfo->get_task_tag(cmd));
-
-               cmd->transport_state &= ~CMD_T_ACTIVE;
-               spin_unlock_irq(&cmd->t_state_lock);
-               complete(&cmd->transport_lun_stop_comp);
-               return;
-       }
-       /*
         * Determine if frontend context caller is requesting the stopping of
         * this command for frontend exceptions.
         */
+       spin_lock_irq(&cmd->t_state_lock);
        if (cmd->transport_state & CMD_T_STOP) {
                pr_debug("%s:%d CMD_T_STOP for ITT: 0x%08x\n",
                        __func__, __LINE__,
@@ -2404,164 +2364,23 @@ void target_wait_for_sess_cmds(struct se_session *se_sess)
 }
 EXPORT_SYMBOL(target_wait_for_sess_cmds);
 
-/*     transport_lun_wait_for_tasks():
- *
- *     Called from ConfigFS context to stop the passed struct se_cmd to allow
- *     an struct se_lun to be successfully shutdown.
- */
-static int transport_lun_wait_for_tasks(struct se_cmd *cmd, struct se_lun *lun)
-{
-       unsigned long flags;
-       int ret = 0;
-
-       /*
-        * If the frontend has already requested this struct se_cmd to
-        * be stopped, we can safely ignore this struct se_cmd.
-        */
-       spin_lock_irqsave(&cmd->t_state_lock, flags);
-       if (cmd->transport_state & CMD_T_STOP) {
-               cmd->transport_state &= ~CMD_T_LUN_STOP;
-
-               pr_debug("ConfigFS ITT[0x%08x] - CMD_T_STOP, skipping\n",
-                        cmd->se_tfo->get_task_tag(cmd));
-               spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-               transport_cmd_check_stop(cmd, false, false);
-               return -EPERM;
-       }
-       cmd->transport_state |= CMD_T_LUN_FE_STOP;
-       spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-
-       // XXX: audit task_flags checks.
-       spin_lock_irqsave(&cmd->t_state_lock, flags);
-       if ((cmd->transport_state & CMD_T_BUSY) &&
-           (cmd->transport_state & CMD_T_SENT)) {
-               if (!target_stop_cmd(cmd, &flags))
-                       ret++;
-       }
-       spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-
-       pr_debug("ConfigFS: cmd: %p stop tasks ret:"
-                       " %d\n", cmd, ret);
-       if (!ret) {
-               pr_debug("ConfigFS: ITT[0x%08x] - stopping cmd....\n",
-                               cmd->se_tfo->get_task_tag(cmd));
-               wait_for_completion(&cmd->transport_lun_stop_comp);
-               pr_debug("ConfigFS: ITT[0x%08x] - stopped cmd....\n",
-                               cmd->se_tfo->get_task_tag(cmd));
-       }
-
-       return 0;
-}
-
-static void __transport_clear_lun_from_sessions(struct se_lun *lun)
-{
-       struct se_cmd *cmd = NULL;
-       unsigned long lun_flags, cmd_flags;
-       /*
-        * Do exception processing and return CHECK_CONDITION status to the
-        * Initiator Port.
-        */
-       spin_lock_irqsave(&lun->lun_cmd_lock, lun_flags);
-       while (!list_empty(&lun->lun_cmd_list)) {
-               cmd = list_first_entry(&lun->lun_cmd_list,
-                      struct se_cmd, se_lun_node);
-               list_del_init(&cmd->se_lun_node);
-
-               spin_lock(&cmd->t_state_lock);
-               pr_debug("SE_LUN[%d] - Setting cmd->transport"
-                       "_lun_stop for  ITT: 0x%08x\n",
-                       cmd->se_lun->unpacked_lun,
-                       cmd->se_tfo->get_task_tag(cmd));
-               cmd->transport_state |= CMD_T_LUN_STOP;
-               spin_unlock(&cmd->t_state_lock);
-
-               spin_unlock_irqrestore(&lun->lun_cmd_lock, lun_flags);
-
-               if (!cmd->se_lun) {
-                       pr_err("ITT: 0x%08x, [i,t]_state: %u/%u\n",
-                               cmd->se_tfo->get_task_tag(cmd),
-                               cmd->se_tfo->get_cmd_state(cmd), cmd->t_state);
-                       BUG();
-               }
-               /*
-                * If the Storage engine still owns the iscsi_cmd_t, determine
-                * and/or stop its context.
-                */
-               pr_debug("SE_LUN[%d] - ITT: 0x%08x before transport"
-                       "_lun_wait_for_tasks()\n", cmd->se_lun->unpacked_lun,
-                       cmd->se_tfo->get_task_tag(cmd));
-
-               if (transport_lun_wait_for_tasks(cmd, cmd->se_lun) < 0) {
-                       spin_lock_irqsave(&lun->lun_cmd_lock, lun_flags);
-                       continue;
-               }
-
-               pr_debug("SE_LUN[%d] - ITT: 0x%08x after transport_lun"
-                       "_wait_for_tasks(): SUCCESS\n",
-                       cmd->se_lun->unpacked_lun,
-                       cmd->se_tfo->get_task_tag(cmd));
-
-               spin_lock_irqsave(&cmd->t_state_lock, cmd_flags);
-               if (!(cmd->transport_state & CMD_T_DEV_ACTIVE)) {
-                       spin_unlock_irqrestore(&cmd->t_state_lock, cmd_flags);
-                       goto check_cond;
-               }
-               cmd->transport_state &= ~CMD_T_DEV_ACTIVE;
-               target_remove_from_state_list(cmd);
-               spin_unlock_irqrestore(&cmd->t_state_lock, cmd_flags);
-
-               /*
-                * The Storage engine stopped this struct se_cmd before it was
-                * send to the fabric frontend for delivery back to the
-                * Initiator Node.  Return this SCSI CDB back with an
-                * CHECK_CONDITION status.
-                */
-check_cond:
-               transport_send_check_condition_and_sense(cmd,
-                               TCM_NON_EXISTENT_LUN, 0);
-               /*
-                *  If the fabric frontend is waiting for this iscsi_cmd_t to
-                * be released, notify the waiting thread now that LU has
-                * finished accessing it.
-                */
-               spin_lock_irqsave(&cmd->t_state_lock, cmd_flags);
-               if (cmd->transport_state & CMD_T_LUN_FE_STOP) {
-                       pr_debug("SE_LUN[%d] - Detected FE stop for"
-                               " struct se_cmd: %p ITT: 0x%08x\n",
-                               lun->unpacked_lun,
-                               cmd, cmd->se_tfo->get_task_tag(cmd));
-
-                       spin_unlock_irqrestore(&cmd->t_state_lock,
-                                       cmd_flags);
-                       transport_cmd_check_stop(cmd, false, false);
-                       complete(&cmd->transport_lun_fe_stop_comp);
-                       spin_lock_irqsave(&lun->lun_cmd_lock, lun_flags);
-                       continue;
-               }
-               pr_debug("SE_LUN[%d] - ITT: 0x%08x finished processing\n",
-                       lun->unpacked_lun, cmd->se_tfo->get_task_tag(cmd));
-
-               spin_unlock_irqrestore(&cmd->t_state_lock, cmd_flags);
-               spin_lock_irqsave(&lun->lun_cmd_lock, lun_flags);
-       }
-       spin_unlock_irqrestore(&lun->lun_cmd_lock, lun_flags);
-}
-
-static int transport_clear_lun_thread(void *p)
+static int transport_clear_lun_ref_thread(void *p)
 {
        struct se_lun *lun = p;
 
-       __transport_clear_lun_from_sessions(lun);
+       percpu_ref_kill(&lun->lun_ref);
+
+       wait_for_completion(&lun->lun_ref_comp);
        complete(&lun->lun_shutdown_comp);
 
        return 0;
 }
 
-int transport_clear_lun_from_sessions(struct se_lun *lun)
+int transport_clear_lun_ref(struct se_lun *lun)
 {
        struct task_struct *kt;
 
-       kt = kthread_run(transport_clear_lun_thread, lun,
+       kt = kthread_run(transport_clear_lun_ref_thread, lun,
                        "tcm_cl_%u", lun->unpacked_lun);
        if (IS_ERR(kt)) {
                pr_err("Unable to start clear_lun thread\n");
@@ -2595,43 +2414,6 @@ bool transport_wait_for_tasks(struct se_cmd *cmd)
                spin_unlock_irqrestore(&cmd->t_state_lock, flags);
                return false;
        }
-       /*
-        * If we are already stopped due to an external event (ie: LUN shutdown)
-        * sleep until the connection can have the passed struct se_cmd back.
-        * The cmd->transport_lun_stopped_sem will be upped by
-        * transport_clear_lun_from_sessions() once the ConfigFS context caller
-        * has completed its operation on the struct se_cmd.
-        */
-       if (cmd->transport_state & CMD_T_LUN_STOP) {
-               pr_debug("wait_for_tasks: Stopping"
-                       " wait_for_completion(&cmd->t_tasktransport_lun_fe"
-                       "_stop_comp); for ITT: 0x%08x\n",
-                       cmd->se_tfo->get_task_tag(cmd));
-               /*
-                * There is a special case for WRITES where a FE exception +
-                * LUN shutdown means ConfigFS context is still sleeping on
-                * transport_lun_stop_comp in transport_lun_wait_for_tasks().
-                * We go ahead and up transport_lun_stop_comp just to be sure
-                * here.
-                */
-               spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-               complete(&cmd->transport_lun_stop_comp);
-               wait_for_completion(&cmd->transport_lun_fe_stop_comp);
-               spin_lock_irqsave(&cmd->t_state_lock, flags);
-
-               target_remove_from_state_list(cmd);
-               /*
-                * At this point, the frontend who was the originator of this
-                * struct se_cmd, now owns the structure and can be released through
-                * normal means below.
-                */
-               pr_debug("wait_for_tasks: Stopped"
-                       " wait_for_completion(&cmd->t_tasktransport_lun_fe_"
-                       "stop_comp); for ITT: 0x%08x\n",
-                       cmd->se_tfo->get_task_tag(cmd));
-
-               cmd->transport_state &= ~CMD_T_LUN_STOP;
-       }
 
        if (!(cmd->transport_state & CMD_T_ACTIVE)) {
                spin_unlock_irqrestore(&cmd->t_state_lock, flags);
@@ -2910,6 +2692,7 @@ int transport_check_aborted_status(struct se_cmd *cmd, int send_status)
                 cmd->t_task_cdb[0], cmd->se_tfo->get_task_tag(cmd));
 
        cmd->se_cmd_flags |= SCF_SENT_DELAYED_TAS;
+       cmd->scsi_status = SAM_STAT_TASK_ABORTED;
        trace_target_cmd_complete(cmd);
        cmd->se_tfo->queue_status(cmd);
 
@@ -2938,6 +2721,7 @@ void transport_send_task_abort(struct se_cmd *cmd)
                if (cmd->se_tfo->write_pending_status(cmd) != 0) {
                        cmd->transport_state |= CMD_T_ABORTED;
                        smp_mb__after_atomic_inc();
+                       return;
                }
        }
        cmd->scsi_status = SAM_STAT_TASK_ABORTED;
index 0204952..be912b3 100644 (file)
@@ -19,7 +19,7 @@
 #define ASCQ_2AH_RESERVATIONS_RELEASED                         0x04
 #define ASCQ_2AH_REGISTRATIONS_PREEMPTED                       0x05
 #define ASCQ_2AH_ASYMMETRIC_ACCESS_STATE_CHANGED               0x06
-#define ASCQ_2AH_IMPLICT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED 0x07
+#define ASCQ_2AH_IMPLICIT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED 0x07
 #define ASCQ_2AH_PRIORITY_CHANGED                              0x08
 
 #define ASCQ_2CH_PREVIOUS_RESERVATION_CONFLICT_STATUS          0x09
index 474cd44..6b88a99 100644 (file)
@@ -405,9 +405,6 @@ static void xcopy_pt_release_cmd(struct se_cmd *se_cmd)
        struct xcopy_pt_cmd *xpt_cmd = container_of(se_cmd,
                                struct xcopy_pt_cmd, se_cmd);
 
-       if (xpt_cmd->remote_port)
-               kfree(se_cmd->se_lun);
-
        kfree(xpt_cmd);
 }
 
@@ -572,22 +569,10 @@ static int target_xcopy_init_pt_lun(
                return 0;
        }
 
-       pt_cmd->se_lun = kzalloc(sizeof(struct se_lun), GFP_KERNEL);
-       if (!pt_cmd->se_lun) {
-               pr_err("Unable to allocate pt_cmd->se_lun\n");
-               return -ENOMEM;
-       }
-       init_completion(&pt_cmd->se_lun->lun_shutdown_comp);
-       INIT_LIST_HEAD(&pt_cmd->se_lun->lun_cmd_list);
-       INIT_LIST_HEAD(&pt_cmd->se_lun->lun_acl_list);
-       spin_lock_init(&pt_cmd->se_lun->lun_acl_lock);
-       spin_lock_init(&pt_cmd->se_lun->lun_cmd_lock);
-       spin_lock_init(&pt_cmd->se_lun->lun_sep_lock);
-
+       pt_cmd->se_lun = &se_dev->xcopy_lun;
        pt_cmd->se_dev = se_dev;
 
        pr_debug("Setup emulated se_dev: %p from se_dev\n", pt_cmd->se_dev);
-       pt_cmd->se_lun->lun_se_dev = se_dev;
        pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH;
 
        pr_debug("Setup emulated se_dev: %p to pt_cmd->se_lun->lun_se_dev\n",
@@ -658,8 +643,6 @@ static int target_xcopy_setup_pt_cmd(
        return 0;
 
 out:
-       if (remote_port == true)
-               kfree(cmd->se_lun);
        return ret;
 }
 
index 0dd54a4..752863a 100644 (file)
@@ -22,6 +22,7 @@
 #define FT_NAMELEN 32          /* length of ASCII WWPNs including pad */
 #define FT_TPG_NAMELEN 32      /* max length of TPG name */
 #define FT_LUN_NAMELEN 32      /* max length of LUN name */
+#define TCM_FC_DEFAULT_TAGS 512        /* tags used for per-session preallocation */
 
 struct ft_transport_id {
        __u8    format;
index 0e5a1ca..479ec56 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/configfs.h>
 #include <linux/ctype.h>
 #include <linux/hash.h>
+#include <linux/percpu_ida.h>
 #include <asm/unaligned.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
@@ -89,16 +90,18 @@ static void ft_free_cmd(struct ft_cmd *cmd)
 {
        struct fc_frame *fp;
        struct fc_lport *lport;
+       struct se_session *se_sess;
 
        if (!cmd)
                return;
+       se_sess = cmd->sess->se_sess;
        fp = cmd->req_frame;
        lport = fr_dev(fp);
        if (fr_seq(fp))
                lport->tt.seq_release(fr_seq(fp));
        fc_frame_free(fp);
+       percpu_ida_free(&se_sess->sess_tag_pool, cmd->se_cmd.map_tag);
        ft_sess_put(cmd->sess); /* undo get from lookup at recv */
-       kfree(cmd);
 }
 
 void ft_release_cmd(struct se_cmd *se_cmd)
@@ -432,14 +435,21 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)
 {
        struct ft_cmd *cmd;
        struct fc_lport *lport = sess->tport->lport;
+       struct se_session *se_sess = sess->se_sess;
+       int tag;
 
-       cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
-       if (!cmd)
+       tag = percpu_ida_alloc(&se_sess->sess_tag_pool, GFP_ATOMIC);
+       if (tag < 0)
                goto busy;
+
+       cmd = &((struct ft_cmd *)se_sess->sess_cmd_map)[tag];
+       memset(cmd, 0, sizeof(struct ft_cmd));
+
+       cmd->se_cmd.map_tag = tag;
        cmd->sess = sess;
        cmd->seq = lport->tt.seq_assign(lport, fp);
        if (!cmd->seq) {
-               kfree(cmd);
+               percpu_ida_free(&se_sess->sess_tag_pool, tag);
                goto busy;
        }
        cmd->req_frame = fp;            /* hold frame during cmd */
index 4e00508..c6932fb 100644 (file)
@@ -571,16 +571,16 @@ int ft_register_configfs(void)
        /*
         * Setup default attribute lists for various fabric->tf_cit_tmpl
         */
-       TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = ft_wwn_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs =
+       fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = ft_wwn_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs =
                                                    ft_nacl_base_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;
        /*
         * register the fabric for use within TCM
         */
index 4859505..ae52c08 100644 (file)
@@ -210,7 +210,8 @@ static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id,
        if (!sess)
                return NULL;
 
-       sess->se_sess = transport_init_session();
+       sess->se_sess = transport_init_session_tags(TCM_FC_DEFAULT_TAGS,
+                                                   sizeof(struct ft_cmd));
        if (IS_ERR(sess->se_sess)) {
                kfree(sess);
                return NULL;
index 03a5671..f1d511a 100644 (file)
@@ -1608,15 +1608,17 @@ exit:
 EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
 
 #ifdef CONFIG_NET
+static const struct genl_multicast_group thermal_event_mcgrps[] = {
+       { .name = THERMAL_GENL_MCAST_GROUP_NAME, },
+};
+
 static struct genl_family thermal_event_genl_family = {
        .id = GENL_ID_GENERATE,
        .name = THERMAL_GENL_FAMILY_NAME,
        .version = THERMAL_GENL_VERSION,
        .maxattr = THERMAL_GENL_ATTR_MAX,
-};
-
-static struct genl_multicast_group thermal_event_mcgrp = {
-       .name = THERMAL_GENL_MCAST_GROUP_NAME,
+       .mcgrps = thermal_event_mcgrps,
+       .n_mcgrps = ARRAY_SIZE(thermal_event_mcgrps),
 };
 
 int thermal_generate_netlink_event(struct thermal_zone_device *tz,
@@ -1677,7 +1679,8 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz,
                return result;
        }
 
-       result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC);
+       result = genlmsg_multicast(&thermal_event_genl_family, skb, 0,
+                                  0, GFP_ATOMIC);
        if (result)
                dev_err(&tz->device, "Failed to send netlink event:%d", result);
 
@@ -1687,17 +1690,7 @@ EXPORT_SYMBOL_GPL(thermal_generate_netlink_event);
 
 static int genetlink_init(void)
 {
-       int result;
-
-       result = genl_register_family(&thermal_event_genl_family);
-       if (result)
-               return result;
-
-       result = genl_register_mc_group(&thermal_event_genl_family,
-                                       &thermal_event_mcgrp);
-       if (result)
-               genl_unregister_family(&thermal_event_genl_family);
-       return result;
+       return genl_register_family(&thermal_event_genl_family);
 }
 
 static void genetlink_exit(void)
index c193af6..636c9ba 100644 (file)
@@ -183,7 +183,7 @@ static int dom0_write_console(uint32_t vtermno, const char *str, int len)
 {
        int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str);
        if (rc < 0)
-               return 0;
+               return rc;
 
        return len;
 }
@@ -642,7 +642,22 @@ struct console xenboot_console = {
 
 void xen_raw_console_write(const char *str)
 {
-       dom0_write_console(0, str, strlen(str));
+       ssize_t len = strlen(str);
+       int rc = 0;
+
+       if (xen_domain()) {
+               rc = dom0_write_console(0, str, len);
+#ifdef CONFIG_X86
+               if (rc == -ENOSYS && xen_hvm_domain())
+                       goto outb_print;
+
+       } else if (xen_cpuid_base()) {
+               int i;
+outb_print:
+               for (i = 0; i < len; i++)
+                       outb(str[i], 0xe9);
+#endif
+       }
 }
 
 void xen_raw_printk(const char *fmt, ...)
index 0e88862..7332e2c 100644 (file)
@@ -495,7 +495,7 @@ static int dashtty_write(struct tty_struct *tty, const unsigned char *buf,
        count = dport->xmit_cnt;
        /* xmit buffer no longer empty? */
        if (count)
-               INIT_COMPLETION(dport->xmit_empty);
+               reinit_completion(&dport->xmit_empty);
        mutex_unlock(&dport->xmit_lock);
 
        if (total) {
index 5377502..7d8103c 100644 (file)
@@ -1433,7 +1433,7 @@ static void work_fn_rx(struct work_struct *work)
        desc = s->desc_rx[new];
 
        if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) !=
-           DMA_SUCCESS) {
+           DMA_COMPLETE) {
                /* Handle incomplete DMA receive */
                struct dma_chan *chan = s->chan_rx;
                struct shdma_desc *sh_desc = container_of(desc,
index aa49162..892cc96 100644 (file)
@@ -344,7 +344,7 @@ void c67x00_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
                /* it could happen that we reinitialize this completion, while
                 * somebody was waiting for that completion.  The timeout and
                 * while loop handle such cases, but this might be improved */
-               INIT_COMPLETION(c67x00->endpoint_disable);
+               reinit_completion(&c67x00->endpoint_disable);
                c67x00_sched_kick(c67x00);
                wait_for_completion_timeout(&c67x00->endpoint_disable, 1 * HZ);
 
index db535b0..fed7f68 100644 (file)
@@ -28,7 +28,7 @@ config USB_DEFAULT_PERSIST
        bool "Enable USB persist by default"
        default y
        help
-         Say N here if you don't want USB power session persistance
+         Say N here if you don't want USB power session persistence
          enabled by default.  If you say N it will make suspended USB
          devices that lose power get reenumerated as if they had been
          unplugged, causing any mounted filesystems to be lost.  The
index 06cec63..a7c04e2 100644 (file)
@@ -5501,6 +5501,6 @@ acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
        if (!hub)
                return NULL;
 
-       return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
+       return ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
 }
 #endif
index 255c144..4e243c3 100644 (file)
@@ -173,7 +173,7 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
                }
 
                /* root hub's parent is the usb hcd. */
-               parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
+               parent_handle = ACPI_HANDLE(dev->parent);
                *handle = acpi_get_child(parent_handle, udev->portnum);
                if (!*handle)
                        return -ENODEV;
@@ -194,7 +194,7 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
 
                        raw_port_num = usb_hcd_find_raw_port_number(hcd,
                                port_num);
-                       *handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev),
+                       *handle = acpi_get_child(ACPI_HANDLE(&udev->dev),
                                raw_port_num);
                        if (!*handle)
                                return -ENODEV;
index 44cf775..774e8b8 100644 (file)
@@ -373,7 +373,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
        if (req->buf == NULL)
                req->buf = (void *)0xDEADBABE;
 
-       INIT_COMPLETION(ffs->ep0req_completion);
+       reinit_completion(&ffs->ep0req_completion);
 
        ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
        if (unlikely(ret < 0))
index eccea1d..6c3d795 100644 (file)
@@ -1923,15 +1923,15 @@ static int usbg_register_configfs(void)
        }
 
        fabric->tf_ops = usbg_ops;
-       TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = usbg_wwn_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = usbg_base_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = usbg_wwn_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = usbg_base_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;
        ret = target_fabric_configfs_register(fabric);
        if (ret < 0) {
                printk(KERN_ERR "target_fabric_configfs_register() failed"
index 84657e0..439c951 100644 (file)
@@ -455,7 +455,7 @@ static int parport_prologue(struct parport *pp)
                return -1;
        }
        mos_parport->msg_pending = true;   /* synch usb call pending */
-       INIT_COMPLETION(mos_parport->syncmsg_compl);
+       reinit_completion(&mos_parport->syncmsg_compl);
        spin_unlock(&release_lock);
 
        mutex_lock(&mos_parport->serial->disc_mutex);
index e663921..f175629 100644 (file)
@@ -2168,15 +2168,15 @@ static int tcm_vhost_register_configfs(void)
        /*
         * Setup default attribute lists for various fabric->tf_cit_tmpl
         */
-       TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_vhost_wwn_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_vhost_tpg_attrs;
-       TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
-       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_vhost_wwn_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = tcm_vhost_tpg_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;
        /*
         * Register the fabric for use within TCM
         */
index 14317b7..4f2e1b3 100644 (file)
@@ -19,10 +19,10 @@ source "drivers/char/agp/Kconfig"
 
 source "drivers/gpu/vga/Kconfig"
 
-source "drivers/gpu/drm/Kconfig"
-
 source "drivers/gpu/host1x/Kconfig"
 
+source "drivers/gpu/drm/Kconfig"
+
 config VGASTATE
        tristate
        default n
index 36db5d9..fb80d68 100644 (file)
@@ -10,6 +10,8 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -19,6 +21,7 @@
 #include <linux/err.h>
 #include <linux/pwm.h>
 #include <linux/pwm_backlight.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
 struct pwm_bl_data {
@@ -27,6 +30,11 @@ struct pwm_bl_data {
        unsigned int            period;
        unsigned int            lth_brightness;
        unsigned int            *levels;
+       bool                    enabled;
+       struct regulator        *power_supply;
+       int                     enable_gpio;
+       unsigned long           enable_gpio_flags;
+       unsigned int            scale;
        int                     (*notify)(struct device *,
                                          int brightness);
        void                    (*notify_after)(struct device *,
@@ -35,11 +43,65 @@ struct pwm_bl_data {
        void                    (*exit)(struct device *);
 };
 
+static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
+{
+       int err;
+
+       if (pb->enabled)
+               return;
+
+       err = regulator_enable(pb->power_supply);
+       if (err < 0)
+               dev_err(pb->dev, "failed to enable power supply\n");
+
+       if (gpio_is_valid(pb->enable_gpio)) {
+               if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
+                       gpio_set_value(pb->enable_gpio, 0);
+               else
+                       gpio_set_value(pb->enable_gpio, 1);
+       }
+
+       pwm_enable(pb->pwm);
+       pb->enabled = true;
+}
+
+static void pwm_backlight_power_off(struct pwm_bl_data *pb)
+{
+       if (!pb->enabled)
+               return;
+
+       pwm_config(pb->pwm, 0, pb->period);
+       pwm_disable(pb->pwm);
+
+       if (gpio_is_valid(pb->enable_gpio)) {
+               if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
+                       gpio_set_value(pb->enable_gpio, 1);
+               else
+                       gpio_set_value(pb->enable_gpio, 0);
+       }
+
+       regulator_disable(pb->power_supply);
+       pb->enabled = false;
+}
+
+static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
+{
+       unsigned int lth = pb->lth_brightness;
+       int duty_cycle;
+
+       if (pb->levels)
+               duty_cycle = pb->levels[brightness];
+       else
+               duty_cycle = brightness;
+
+       return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
+}
+
 static int pwm_backlight_update_status(struct backlight_device *bl)
 {
        struct pwm_bl_data *pb = bl_get_data(bl);
        int brightness = bl->props.brightness;
-       int max = bl->props.max_brightness;
+       int duty_cycle;
 
        if (bl->props.power != FB_BLANK_UNBLANK ||
            bl->props.fb_blank != FB_BLANK_UNBLANK ||
@@ -49,24 +111,12 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
        if (pb->notify)
                brightness = pb->notify(pb->dev, brightness);
 
-       if (brightness == 0) {
-               pwm_config(pb->pwm, 0, pb->period);
-               pwm_disable(pb->pwm);
-       } else {
-               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);
+       if (brightness > 0) {
+               duty_cycle = compute_duty_cycle(pb, brightness);
                pwm_config(pb->pwm, duty_cycle, pb->period);
-               pwm_enable(pb->pwm);
-       }
+               pwm_backlight_power_on(pb, brightness);
+       } else
+               pwm_backlight_power_off(pb);
 
        if (pb->notify_after)
                pb->notify_after(pb->dev, brightness);
@@ -98,6 +148,7 @@ static int pwm_backlight_parse_dt(struct device *dev,
                                  struct platform_pwm_backlight_data *data)
 {
        struct device_node *node = dev->of_node;
+       enum of_gpio_flags flags;
        struct property *prop;
        int length;
        u32 value;
@@ -138,11 +189,13 @@ static int pwm_backlight_parse_dt(struct device *dev,
                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.
-        */
+       data->enable_gpio = of_get_named_gpio_flags(node, "enable-gpios", 0,
+                                                   &flags);
+       if (data->enable_gpio == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
+       if (gpio_is_valid(data->enable_gpio) && (flags & OF_GPIO_ACTIVE_LOW))
+               data->enable_gpio_flags |= PWM_BACKLIGHT_GPIO_ACTIVE_LOW;
 
        return 0;
 }
@@ -168,7 +221,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
        struct backlight_properties props;
        struct backlight_device *bl;
        struct pwm_bl_data *pb;
-       unsigned int max;
        int ret;
 
        if (!data) {
@@ -195,16 +247,46 @@ static int pwm_backlight_probe(struct platform_device *pdev)
        }
 
        if (data->levels) {
-               max = data->levels[data->max_brightness];
+               unsigned int i;
+
+               for (i = 0; i <= data->max_brightness; i++)
+                       if (data->levels[i] > pb->scale)
+                               pb->scale = data->levels[i];
+
                pb->levels = data->levels;
        } else
-               max = data->max_brightness;
+               pb->scale = data->max_brightness;
 
+       pb->enable_gpio = data->enable_gpio;
+       pb->enable_gpio_flags = data->enable_gpio_flags;
        pb->notify = data->notify;
        pb->notify_after = data->notify_after;
        pb->check_fb = data->check_fb;
        pb->exit = data->exit;
        pb->dev = &pdev->dev;
+       pb->enabled = false;
+
+       if (gpio_is_valid(pb->enable_gpio)) {
+               unsigned long flags;
+
+               if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
+                       flags = GPIOF_OUT_INIT_HIGH;
+               else
+                       flags = GPIOF_OUT_INIT_LOW;
+
+               ret = gpio_request_one(pb->enable_gpio, flags, "enable");
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",
+                               pb->enable_gpio, ret);
+                       goto err_alloc;
+               }
+       }
+
+       pb->power_supply = devm_regulator_get(&pdev->dev, "power");
+       if (IS_ERR(pb->power_supply)) {
+               ret = PTR_ERR(pb->power_supply);
+               goto err_gpio;
+       }
 
        pb->pwm = devm_pwm_get(&pdev->dev, NULL);
        if (IS_ERR(pb->pwm)) {
@@ -214,7 +296,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
                if (IS_ERR(pb->pwm)) {
                        dev_err(&pdev->dev, "unable to request legacy PWM\n");
                        ret = PTR_ERR(pb->pwm);
-                       goto err_alloc;
+                       goto err_gpio;
                }
        }
 
@@ -229,7 +311,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
                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);
+       pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
 
        memset(&props, 0, sizeof(struct backlight_properties));
        props.type = BACKLIGHT_RAW;
@@ -239,7 +321,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
        if (IS_ERR(bl)) {
                dev_err(&pdev->dev, "failed to register backlight\n");
                ret = PTR_ERR(bl);
-               goto err_alloc;
+               goto err_gpio;
        }
 
        if (data->dft_brightness > data->max_brightness) {
@@ -255,6 +337,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, bl);
        return 0;
 
+err_gpio:
+       if (gpio_is_valid(pb->enable_gpio))
+               gpio_free(pb->enable_gpio);
 err_alloc:
        if (data->exit)
                data->exit(&pdev->dev);
@@ -267,10 +352,11 @@ static int pwm_backlight_remove(struct platform_device *pdev)
        struct pwm_bl_data *pb = bl_get_data(bl);
 
        backlight_device_unregister(bl);
-       pwm_config(pb->pwm, 0, pb->period);
-       pwm_disable(pb->pwm);
+       pwm_backlight_power_off(pb);
+
        if (pb->exit)
                pb->exit(&pdev->dev);
+
        return 0;
 }
 
@@ -282,10 +368,12 @@ static int pwm_backlight_suspend(struct device *dev)
 
        if (pb->notify)
                pb->notify(pb->dev, 0);
-       pwm_config(pb->pwm, 0, pb->period);
-       pwm_disable(pb->pwm);
+
+       pwm_backlight_power_off(pb);
+
        if (pb->notify_after)
                pb->notify_after(pb->dev, 0);
+
        return 0;
 }
 
@@ -294,12 +382,19 @@ static int pwm_backlight_resume(struct device *dev)
        struct backlight_device *bl = dev_get_drvdata(dev);
 
        backlight_update_status(bl);
+
        return 0;
 }
 #endif
 
-static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend,
-                        pwm_backlight_resume);
+static const struct dev_pm_ops pwm_backlight_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+       .suspend = pwm_backlight_suspend,
+       .resume = pwm_backlight_resume,
+       .poweroff = pwm_backlight_suspend,
+       .restore = pwm_backlight_resume,
+#endif
+};
 
 static struct platform_driver pwm_backlight_driver = {
        .driver         = {
@@ -317,4 +412,3 @@ module_platform_driver(pwm_backlight_driver);
 MODULE_DESCRIPTION("PWM based Backlight Driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:pwm-backlight");
-
index 00b3a52..cee9602 100644 (file)
@@ -141,7 +141,6 @@ static int exynos_mipi_dsi_early_blank_mode(struct mipi_dsim_device *dsim,
 
 static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
 {
-       struct platform_device *pdev = to_platform_device(dsim->dev);
        struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
        struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
 
index 7eed957..85edabf 100644 (file)
@@ -220,7 +220,7 @@ int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
        case MIPI_DSI_DCS_LONG_WRITE:
        {
                unsigned int size, payload = 0;
-               INIT_COMPLETION(dsim_wr_comp);
+               reinit_completion(&dsim_wr_comp);
 
                size = data_size * 4;
 
@@ -356,7 +356,7 @@ int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id,
        msleep(20);
 
        mutex_lock(&dsim->lock);
-       INIT_COMPLETION(dsim_rd_comp);
+       reinit_completion(&dsim_rd_comp);
        exynos_mipi_dsi_rd_tx_header(dsim,
                MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, req_size);
 
index 798ef20..d5c936c 100644 (file)
@@ -69,7 +69,7 @@ static int tpd_connect(struct omap_dss_device *dssdev,
        dst->src = dssdev;
        dssdev->dst = dst;
 
-       INIT_COMPLETION(ddata->hpd_completion);
+       reinit_completion(&ddata->hpd_completion);
 
        gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
        /* DC-DC converter needs at max 300us to get to 90% of 5V */
index 1f572c0..c444654 100644 (file)
@@ -275,9 +275,8 @@ static inline s64 towards_target(struct virtio_balloon *vb)
        __le32 v;
        s64 target;
 
-       vb->vdev->config->get(vb->vdev,
-                             offsetof(struct virtio_balloon_config, num_pages),
-                             &v, sizeof(v));
+       virtio_cread(vb->vdev, struct virtio_balloon_config, num_pages, &v);
+
        target = le32_to_cpu(v);
        return target - vb->num_pages;
 }
@@ -286,9 +285,8 @@ static void update_balloon_size(struct virtio_balloon *vb)
 {
        __le32 actual = cpu_to_le32(vb->num_pages);
 
-       vb->vdev->config->set(vb->vdev,
-                             offsetof(struct virtio_balloon_config, actual),
-                             &actual, sizeof(actual));
+       virtio_cwrite(vb->vdev, struct virtio_balloon_config, num_pages,
+                     &actual);
 }
 
 static int balloon(void *_vballoon)
@@ -513,7 +511,7 @@ static void virtballoon_remove(struct virtio_device *vdev)
        kfree(vb);
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int virtballoon_freeze(struct virtio_device *vdev)
 {
        struct virtio_balloon *vb = vdev->priv;
@@ -556,7 +554,7 @@ static struct virtio_driver virtio_balloon_driver = {
        .probe =        virtballoon_probe,
        .remove =       virtballoon_remove,
        .config_changed = virtballoon_changed,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
        .freeze =       virtballoon_freeze,
        .restore =      virtballoon_restore,
 #endif
index 1ba0d68..c600ccf 100644 (file)
@@ -219,13 +219,14 @@ static void vm_reset(struct virtio_device *vdev)
 /* Transport interface */
 
 /* the notify function used when creating a virt queue */
-static void vm_notify(struct virtqueue *vq)
+static bool vm_notify(struct virtqueue *vq)
 {
        struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
 
        /* We write the queue's selector into the notification register to
         * signal the other end */
        writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY);
+       return true;
 }
 
 /* Notify all virtqueues on an interrupt. */
@@ -470,7 +471,7 @@ static int virtio_mmio_probe(struct platform_device *pdev)
 
        /* Check magic value */
        magic = readl(vm_dev->base + VIRTIO_MMIO_MAGIC_VALUE);
-       if (memcmp(&magic, "virt", 4) != 0) {
+       if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) {
                dev_warn(&pdev->dev, "Wrong magic value 0x%08lx!\n", magic);
                return -ENODEV;
        }
index 98917fc..a37c699 100644 (file)
@@ -197,13 +197,14 @@ static void vp_reset(struct virtio_device *vdev)
 }
 
 /* the notify function used when creating a virt queue */
-static void vp_notify(struct virtqueue *vq)
+static bool vp_notify(struct virtqueue *vq)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
 
        /* we write the queue's selector into the notification register to
         * signal the other end */
        iowrite16(vq->index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
+       return true;
 }
 
 /* Handle a configuration change: Tell driver if it wants to know. */
index 6b4a4db..28b5338 100644 (file)
@@ -81,7 +81,7 @@ struct vring_virtqueue
        u16 last_used_idx;
 
        /* How to notify other side. FIXME: commonalize hcalls! */
-       void (*notify)(struct virtqueue *vq);
+       bool (*notify)(struct virtqueue *vq);
 
 #ifdef DEBUG
        /* They're supposed to lock for us. */
@@ -173,6 +173,8 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
        head = vq->free_head;
        vq->vring.desc[head].flags = VRING_DESC_F_INDIRECT;
        vq->vring.desc[head].addr = virt_to_phys(desc);
+       /* kmemleak gives a false positive, as it's hidden by virt_to_phys */
+       kmemleak_ignore(desc);
        vq->vring.desc[head].len = i * sizeof(struct vring_desc);
 
        /* Update free pointer */
@@ -428,13 +430,22 @@ EXPORT_SYMBOL_GPL(virtqueue_kick_prepare);
  * @vq: the struct virtqueue
  *
  * This does not need to be serialized.
+ *
+ * Returns false if host notify failed or queue is broken, otherwise true.
  */
-void virtqueue_notify(struct virtqueue *_vq)
+bool virtqueue_notify(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
 
+       if (unlikely(vq->broken))
+               return false;
+
        /* Prod other side to tell it about changes. */
-       vq->notify(_vq);
+       if (!vq->notify(_vq)) {
+               vq->broken = true;
+               return false;
+       }
+       return true;
 }
 EXPORT_SYMBOL_GPL(virtqueue_notify);
 
@@ -447,11 +458,14 @@ EXPORT_SYMBOL_GPL(virtqueue_notify);
  *
  * Caller must ensure we don't call this with other virtqueue
  * operations at the same time (except where noted).
+ *
+ * Returns false if kick failed, otherwise true.
  */
-void virtqueue_kick(struct virtqueue *vq)
+bool virtqueue_kick(struct virtqueue *vq)
 {
        if (virtqueue_kick_prepare(vq))
-               virtqueue_notify(vq);
+               return virtqueue_notify(vq);
+       return true;
 }
 EXPORT_SYMBOL_GPL(virtqueue_kick);
 
@@ -742,7 +756,7 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
                                      struct virtio_device *vdev,
                                      bool weak_barriers,
                                      void *pages,
-                                     void (*notify)(struct virtqueue *),
+                                     bool (*notify)(struct virtqueue *),
                                      void (*callback)(struct virtqueue *),
                                      const char *name)
 {
@@ -837,4 +851,12 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_vring_size);
 
+bool virtqueue_is_broken(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       return vq->broken;
+}
+EXPORT_SYMBOL_GPL(virtqueue_is_broken);
+
 MODULE_LICENSE("GPL");
index 264ad1c..e36b18b 100644 (file)
@@ -56,7 +56,7 @@ MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids);
 
 static int w1_gpio_probe_dt(struct platform_device *pdev)
 {
-       struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
+       struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
        struct device_node *np = pdev->dev.of_node;
        int gpio;
 
@@ -92,7 +92,7 @@ static int w1_gpio_probe(struct platform_device *pdev)
                }
        }
 
-       pdata = pdev->dev.platform_data;
+       pdata = dev_get_platdata(&pdev->dev);
 
        if (!pdata) {
                dev_err(&pdev->dev, "No configuration data\n");
@@ -154,7 +154,7 @@ static int w1_gpio_probe(struct platform_device *pdev)
 static int w1_gpio_remove(struct platform_device *pdev)
 {
        struct w1_bus_master *master = platform_get_drvdata(pdev);
-       struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
+       struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
 
        if (pdata->enable_external_pullup)
                pdata->enable_external_pullup(0);
@@ -171,7 +171,7 @@ static int w1_gpio_remove(struct platform_device *pdev)
 
 static int w1_gpio_suspend(struct platform_device *pdev, pm_message_t state)
 {
-       struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
+       struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
 
        if (pdata->enable_external_pullup)
                pdata->enable_external_pullup(0);
@@ -181,7 +181,7 @@ static int w1_gpio_suspend(struct platform_device *pdev, pm_message_t state)
 
 static int w1_gpio_resume(struct platform_device *pdev)
 {
-       struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
+       struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
 
        if (pdata->enable_external_pullup)
                pdata->enable_external_pullup(1);
index 6df632e..5be6e91 100644 (file)
@@ -392,6 +392,25 @@ config RETU_WATCHDOG
          To compile this driver as a module, choose M here: the
          module will be called retu_wdt.
 
+config MOXART_WDT
+       tristate "MOXART watchdog"
+       depends on ARCH_MOXART
+       help
+         Say Y here to include Watchdog timer support for the watchdog
+         existing on the MOXA ART SoC series platforms.
+
+         To compile this driver as a module, choose M here: the
+         module will be called moxart_wdt.
+
+config SIRFSOC_WATCHDOG
+       tristate "SiRFSOC watchdog"
+       depends on ARCH_SIRF
+       select WATCHDOG_CORE
+       default y
+       help
+         Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
+         the watchdog triggers the system will be reset.
+
 # AVR32 Architecture
 
 config AT32AP700X_WDT
@@ -866,6 +885,7 @@ config VIA_WDT
 config W83627HF_WDT
        tristate "W83627HF/W83627DHG Watchdog Timer"
        depends on X86
+       select WATCHDOG_CORE
        ---help---
          This is the driver for the hardware watchdog on the W83627HF chipset
          as used in Advantech PC-9578 and Tyan S2721-533 motherboards
@@ -1125,6 +1145,13 @@ config LANTIQ_WDT
        help
          Hardware driver for the Lantiq SoC Watchdog Timer.
 
+config RALINK_WDT
+       tristate "Ralink SoC watchdog"
+       select WATCHDOG_CORE
+       depends on RALINK
+       help
+         Hardware driver for the Ralink SoC Watchdog Timer.
+
 # PARISC Architecture
 
 # POWERPC Architecture
index 8c7b8bc..91bd95a 100644 (file)
@@ -55,6 +55,8 @@ obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
 obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
 obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
+obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
+obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
 
 # AVR32 Architecture
 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
@@ -134,6 +136,7 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
 obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
 octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
 obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o
+obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o
 
 # PARISC Architecture
 
index 24a5177..5cf1621 100644 (file)
@@ -60,8 +60,7 @@
 #include <linux/types.h>               /* For standard types (like size_t) */
 #include <linux/errno.h>               /* For the -ENODEV/... values */
 #include <linux/kernel.h>              /* For printk/panic/... */
-#include <linux/miscdevice.h>          /* For MODULE_ALIAS_MISCDEV
-                                                       (WATCHDOG_MINOR) */
+#include <linux/miscdevice.h>          /* For struct miscdevice */
 #include <linux/watchdog.h>            /* For the watchdog specific items */
 #include <linux/fs.h>                  /* For file operations */
 #include <linux/ioport.h>              /* For io-port access */
@@ -337,4 +336,3 @@ module_exit(acq_exit);
 MODULE_AUTHOR("David Woodhouse");
 MODULE_DESCRIPTION("Acquire Inc. Single Board Computer Watchdog Timer driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index cc6702f..a8961ad 100644 (file)
@@ -345,4 +345,3 @@ module_exit(advwdt_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Marek Michalkiewicz <marekm@linux.org.pl>");
 MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 41b8493..fbb7b94 100644 (file)
@@ -452,4 +452,3 @@ module_exit(watchdog_exit);
 MODULE_AUTHOR("Alan Cox");
 MODULE_DESCRIPTION("ALi M1535 PMU Watchdog Timer driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 5eee550..12f0b76 100644 (file)
@@ -425,4 +425,3 @@ MODULE_DEVICE_TABLE(pci, alim7101_pci_tbl);
 MODULE_AUTHOR("Steve Hill");
 MODULE_DESCRIPTION("ALi M7101 PMU Computer Watchdog Timer driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index b3709f9..3a99657 100644 (file)
@@ -46,7 +46,6 @@
 MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
 MODULE_DESCRIPTION(LONGNAME);
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 static int margin = 60;
 module_param(margin, int, 0);
index b178e71..afe7d17 100644 (file)
@@ -434,4 +434,3 @@ module_platform_driver_probe(at32_wdt_driver, at32_wdt_probe);
 MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
 MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 1c75260..dee6cc2 100644 (file)
@@ -269,7 +269,7 @@ static struct platform_driver at91wdt_driver = {
        .driver         = {
                .name   = "at91_wdt",
                .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(at91_wdt_dt_ids),
+               .of_match_table = at91_wdt_dt_ids,
        },
 };
 
@@ -297,5 +297,4 @@ module_exit(at91_wdt_exit);
 MODULE_AUTHOR("Andrew Victor");
 MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:at91_wdt");
index 37cb09b..9fa1f69 100644 (file)
@@ -329,4 +329,3 @@ MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
 MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("platform:" DRIVER_NAME);
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 61566fc..a6a2ceb 100644 (file)
@@ -186,4 +186,3 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
 MODULE_DESCRIPTION("Driver for Broadcom BCM2835 watchdog timer");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index a14a58d..4eb188b 100644 (file)
@@ -317,5 +317,4 @@ MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:bcm63xx-wdt");
index 5d36d6f..a3b6a5b 100644 (file)
@@ -465,7 +465,6 @@ module_exit(bfin_wdt_exit);
 MODULE_AUTHOR("Michele d'Amico, Mike Frysinger <vapier@gentoo.org>");
 MODULE_DESCRIPTION("Blackfin Watchdog Device Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 module_param(timeout, uint, 0);
 MODULE_PARM_DESC(timeout,
index f270bb7..f7ae49e 100644 (file)
@@ -289,7 +289,6 @@ MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
 MODULE_DESCRIPTION("sma cpu5 watchdog driver");
 MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 module_param(port, int, 0);
 MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
index bead774..dd625cc 100644 (file)
@@ -267,5 +267,4 @@ MODULE_PARM_DESC(heartbeat,
                 __MODULE_STRING(DEFAULT_HEARTBEAT));
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:watchdog");
index e621098..a46f5c7 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/of.h>
 #include <linux/pm.h>
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
@@ -203,12 +204,12 @@ static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
        switch (cmd) {
        case WDIOC_GETSUPPORT:
-               return copy_to_user((struct watchdog_info *)arg, &dw_wdt_ident,
+               return copy_to_user((void __user *)arg, &dw_wdt_ident,
                                    sizeof(dw_wdt_ident)) ? -EFAULT : 0;
 
        case WDIOC_GETSTATUS:
        case WDIOC_GETBOOTSTATUS:
-               return put_user(0, (int *)arg);
+               return put_user(0, (int __user *)arg);
 
        case WDIOC_KEEPALIVE:
                dw_wdt_set_next_heartbeat();
@@ -252,17 +253,17 @@ static int dw_wdt_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int dw_wdt_suspend(struct device *dev)
 {
-       clk_disable(dw_wdt.clk);
+       clk_disable_unprepare(dw_wdt.clk);
 
        return 0;
 }
 
 static int dw_wdt_resume(struct device *dev)
 {
-       int err = clk_enable(dw_wdt.clk);
+       int err = clk_prepare_enable(dw_wdt.clk);
 
        if (err)
                return err;
@@ -271,12 +272,9 @@ static int dw_wdt_resume(struct device *dev)
 
        return 0;
 }
+#endif /* CONFIG_PM_SLEEP */
 
-static const struct dev_pm_ops dw_wdt_pm_ops = {
-       .suspend        = dw_wdt_suspend,
-       .resume         = dw_wdt_resume,
-};
-#endif /* CONFIG_PM */
+static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume);
 
 static const struct file_operations wdt_fops = {
        .owner          = THIS_MODULE,
@@ -309,7 +307,7 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
        if (IS_ERR(dw_wdt.clk))
                return PTR_ERR(dw_wdt.clk);
 
-       ret = clk_enable(dw_wdt.clk);
+       ret = clk_prepare_enable(dw_wdt.clk);
        if (ret)
                return ret;
 
@@ -326,7 +324,7 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
        return 0;
 
 out_disable_clk:
-       clk_disable(dw_wdt.clk);
+       clk_disable_unprepare(dw_wdt.clk);
 
        return ret;
 }
@@ -335,20 +333,27 @@ static int dw_wdt_drv_remove(struct platform_device *pdev)
 {
        misc_deregister(&dw_wdt_miscdev);
 
-       clk_disable(dw_wdt.clk);
+       clk_disable_unprepare(dw_wdt.clk);
 
        return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id dw_wdt_of_match[] = {
+       { .compatible = "snps,dw-wdt", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dw_wdt_of_match);
+#endif
+
 static struct platform_driver dw_wdt_driver = {
        .probe          = dw_wdt_drv_probe,
        .remove         = dw_wdt_drv_remove,
        .driver         = {
                .name   = "dw_wdt",
                .owner  = THIS_MODULE,
-#ifdef CONFIG_PM
+               .of_match_table = of_match_ptr(dw_wdt_of_match),
                .pm     = &dw_wdt_pm_ops,
-#endif /* CONFIG_PM */
        },
 };
 
@@ -357,4 +362,3 @@ module_platform_driver(dw_wdt_driver);
 MODULE_AUTHOR("Jamie Iles");
 MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index e057484..833e813 100644 (file)
@@ -179,4 +179,3 @@ MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>,"
 MODULE_DESCRIPTION("EP93xx Watchdog");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(WDT_VERSION);
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index cd31b8a..23ee532 100644 (file)
@@ -477,4 +477,3 @@ module_exit(eurwdt_exit);
 MODULE_AUTHOR("Rodolfo Giometti");
 MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 3755833..25beb30 100644 (file)
@@ -331,5 +331,4 @@ module_exit(gef_wdt_exit);
 MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
 MODULE_DESCRIPTION("GE watchdog driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:gef_wdt");
index fcd599d..4a6ae84 100644 (file)
@@ -297,4 +297,3 @@ module_exit(geodewdt_exit);
 MODULE_AUTHOR("Advanced Micro Devices, Inc");
 MODULE_DESCRIPTION("Geode GX/LX Watchdog Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 19f3c3f..45b979d 100644 (file)
@@ -881,7 +881,6 @@ MODULE_AUTHOR("Tom Mingarelli");
 MODULE_DESCRIPTION("hp watchdog driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(HPWDT_VERSION);
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 module_param(soft_margin, int, 0);
 MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
index 2b2ea13..a72fe93 100644 (file)
@@ -497,4 +497,3 @@ module_pci_driver(esb_driver);
 MODULE_AUTHOR("Ross Biro and David Härdeman");
 MODULE_DESCRIPTION("Watchdog driver for Intel 6300ESB chipsets");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 6130321..04f8af6 100644 (file)
@@ -56,8 +56,6 @@
 #include <linux/types.h>               /* For standard types (like size_t) */
 #include <linux/errno.h>               /* For the -ENODEV/... values */
 #include <linux/kernel.h>              /* For printk/panic/... */
-#include <linux/miscdevice.h>          /* For MODULE_ALIAS_MISCDEV
-                                                       (WATCHDOG_MINOR) */
 #include <linux/watchdog.h>            /* For the watchdog specific items */
 #include <linux/init.h>                        /* For __init/__exit/... */
 #include <linux/fs.h>                  /* For file operations */
@@ -394,7 +392,7 @@ static int iTCO_wdt_probe(struct platform_device *dev)
 {
        int ret = -ENODEV;
        unsigned long val32;
-       struct lpc_ich_info *ich_info = dev->dev.platform_data;
+       struct lpc_ich_info *ich_info = dev_get_platdata(&dev->dev);
 
        if (!ich_info)
                goto out;
@@ -582,5 +580,4 @@ MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
 MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver");
 MODULE_VERSION(DRV_VERSION);
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:" DRV_NAME);
index eb6b5cc..7ae3669 100644 (file)
@@ -382,6 +382,5 @@ module_exit(ibwdt_exit);
 MODULE_AUTHOR("Charles Howes <chowes@vsol.net>");
 MODULE_DESCRIPTION("IB700 SBC watchdog driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 /* end of ib700wdt.c */
index bc3fb8f..db0a344 100644 (file)
@@ -419,4 +419,3 @@ MODULE_PARM_DESC(nowayout,
 MODULE_DESCRIPTION("IBM Automatic Server Restart driver");
 MODULE_AUTHOR("Andrey Panin");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index e24ef6a..70a2402 100644 (file)
@@ -344,5 +344,4 @@ module_exit(ie6xx_wdt_exit);
 MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
 MODULE_DESCRIPTION("Intel Atom E6xx Watchdog Device Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:" DRIVER_NAME);
index 693ac3f..b4786bc 100644 (file)
@@ -322,6 +322,7 @@ static const struct of_device_id imx2_wdt_dt_ids[] = {
        { .compatible = "fsl,imx21-wdt", },
        { /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids);
 
 static struct platform_driver imx2_wdt_driver = {
        .remove         = __exit_p(imx2_wdt_remove),
@@ -338,5 +339,4 @@ module_platform_driver_probe(imx2_wdt_driver, imx2_wdt_probe);
 MODULE_AUTHOR("Wolfram Sang");
 MODULE_DESCRIPTION("Watchdog driver for IMX2 and later");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:" DRIVER_NAME);
index 6d90f7a..1b5c25a 100644 (file)
@@ -214,4 +214,3 @@ module_exit(watchdog_exit);
 MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
 MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 8ced256..e13e65e 100644 (file)
@@ -564,5 +564,4 @@ module_exit(intel_scu_watchdog_exit);
 MODULE_AUTHOR("Intel Corporation");
 MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_VERSION(WDT_VER);
index d964faf..b16013f 100644 (file)
@@ -259,4 +259,3 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
 MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>");
 MODULE_DESCRIPTION("iop watchdog timer driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index f4cce6d..41b3979 100644 (file)
@@ -41,7 +41,6 @@
 MODULE_AUTHOR("Jorge Boncompte - DTI2 <jorge@dti2.net>");
 MODULE_DESCRIPTION("IT8712F Watchdog Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 static int max_units = 255;
 static int margin = 60;                /* in seconds */
index d3dcc69..e2bba68 100644 (file)
@@ -772,4 +772,3 @@ module_exit(it87_wdt_exit);
 MODULE_AUTHOR("Oliver Schuster");
 MODULE_DESCRIPTION("Hardware Watchdog Device Driver for IT87xx EC-LPC I/O");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 5580b4f..f20cc53 100644 (file)
@@ -208,5 +208,3 @@ module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
index d1afdf6..2de486a 100644 (file)
@@ -222,5 +222,4 @@ module_platform_driver(jz4740_wdt_driver);
 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
 MODULE_DESCRIPTION("jz4740 Watchdog Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:jz4740-wdt");
index 5c3d4df..a1a3638 100644 (file)
@@ -67,7 +67,7 @@ enum {
        PRESCALER_12,
 };
 
-const u32 kempld_prescaler[] = {
+static const u32 kempld_prescaler[] = {
        [PRESCALER_21] = (1 << 21) - 1,
        [PRESCALER_17] = (1 << 17) - 1,
        [PRESCALER_12] = (1 << 12) - 1,
@@ -361,7 +361,7 @@ static long kempld_wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd,
                ret = kempld_wdt_keepalive(wdd);
                break;
        case WDIOC_GETPRETIMEOUT:
-               ret = put_user(wdt_data->pretimeout, (int *)arg);
+               ret = put_user(wdt_data->pretimeout, (int __user *)arg);
                break;
        }
 
@@ -578,4 +578,3 @@ module_platform_driver(kempld_wdt_driver);
 MODULE_DESCRIPTION("KEM PLD Watchdog Driver");
 MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index dce9ecf..40ca559 100644 (file)
@@ -323,5 +323,4 @@ module_exit(ks8695_wdt_exit);
 MODULE_AUTHOR("Andrew Victor");
 MODULE_DESCRIPTION("Watchdog driver for KS8695");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:ks8695_wdt");
index 088fd0c..3b3148c 100644 (file)
@@ -249,4 +249,3 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
 MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
 MODULE_DESCRIPTION("Lantiq SoC Watchdog");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 173494a..da6fa2b 100644 (file)
@@ -223,4 +223,3 @@ module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index bf84f78..9826b59 100644 (file)
@@ -92,7 +92,6 @@ static unsigned short zf_readw(unsigned char port)
 MODULE_AUTHOR("Fernando Fuganti <fuganti@conectiva.com.br>");
 MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 static bool nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, bool, 0);
index cc9d328..6d4f399 100644 (file)
@@ -258,4 +258,3 @@ MODULE_PARM_DESC(nodelay,
                 "(max6373/74 only, default=0)");
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 97d62ee..be86ea3 100644 (file)
@@ -315,4 +315,3 @@ MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
 MODULE_DESCRIPTION("MixCom Watchdog driver");
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/moxart_wdt.c b/drivers/watchdog/moxart_wdt.c
new file mode 100644 (file)
index 0000000..4166e4d
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * MOXA ART SoCs watchdog driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <jonas.jensen@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/moduleparam.h>
+
+#define REG_COUNT                      0x4
+#define REG_MODE                       0x8
+#define REG_ENABLE                     0xC
+
+struct moxart_wdt_dev {
+       struct watchdog_device dev;
+       void __iomem *base;
+       unsigned int clock_frequency;
+};
+
+static int heartbeat;
+
+static int moxart_wdt_stop(struct watchdog_device *wdt_dev)
+{
+       struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev);
+
+       writel(0, moxart_wdt->base + REG_ENABLE);
+
+       return 0;
+}
+
+static int moxart_wdt_start(struct watchdog_device *wdt_dev)
+{
+       struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev);
+
+       writel(moxart_wdt->clock_frequency * wdt_dev->timeout,
+              moxart_wdt->base + REG_COUNT);
+       writel(0x5ab9, moxart_wdt->base + REG_MODE);
+       writel(0x03, moxart_wdt->base + REG_ENABLE);
+
+       return 0;
+}
+
+static int moxart_wdt_set_timeout(struct watchdog_device *wdt_dev,
+                                 unsigned int timeout)
+{
+       wdt_dev->timeout = timeout;
+
+       return 0;
+}
+
+static const struct watchdog_info moxart_wdt_info = {
+       .identity       = "moxart-wdt",
+       .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+                         WDIOF_MAGICCLOSE,
+};
+
+static const struct watchdog_ops moxart_wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = moxart_wdt_start,
+       .stop           = moxart_wdt_stop,
+       .set_timeout    = moxart_wdt_set_timeout,
+};
+
+static int moxart_wdt_probe(struct platform_device *pdev)
+{
+       struct moxart_wdt_dev *moxart_wdt;
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct resource *res;
+       struct clk *clk;
+       int err;
+       unsigned int max_timeout;
+       bool nowayout = WATCHDOG_NOWAYOUT;
+
+       moxart_wdt = devm_kzalloc(dev, sizeof(*moxart_wdt), GFP_KERNEL);
+       if (!moxart_wdt)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, moxart_wdt);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       moxart_wdt->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(moxart_wdt->base))
+               return PTR_ERR(moxart_wdt->base);
+
+       clk = of_clk_get(node, 0);
+       if (IS_ERR(clk)) {
+               pr_err("%s: of_clk_get failed\n", __func__);
+               return PTR_ERR(clk);
+       }
+
+       moxart_wdt->clock_frequency = clk_get_rate(clk);
+       if (moxart_wdt->clock_frequency == 0) {
+               pr_err("%s: incorrect clock frequency\n", __func__);
+               return -EINVAL;
+       }
+
+       max_timeout = UINT_MAX / moxart_wdt->clock_frequency;
+
+       moxart_wdt->dev.info = &moxart_wdt_info;
+       moxart_wdt->dev.ops = &moxart_wdt_ops;
+       moxart_wdt->dev.timeout = max_timeout;
+       moxart_wdt->dev.min_timeout = 1;
+       moxart_wdt->dev.max_timeout = max_timeout;
+       moxart_wdt->dev.parent = dev;
+
+       watchdog_init_timeout(&moxart_wdt->dev, heartbeat, dev);
+       watchdog_set_nowayout(&moxart_wdt->dev, nowayout);
+
+       watchdog_set_drvdata(&moxart_wdt->dev, moxart_wdt);
+
+       err = watchdog_register_device(&moxart_wdt->dev);
+       if (err)
+               return err;
+
+       dev_dbg(dev, "Watchdog enabled (heartbeat=%d sec, nowayout=%d)\n",
+               moxart_wdt->dev.timeout, nowayout);
+
+       return 0;
+}
+
+static int moxart_wdt_remove(struct platform_device *pdev)
+{
+       struct moxart_wdt_dev *moxart_wdt = platform_get_drvdata(pdev);
+
+       moxart_wdt_stop(&moxart_wdt->dev);
+       watchdog_unregister_device(&moxart_wdt->dev);
+
+       return 0;
+}
+
+static const struct of_device_id moxart_watchdog_match[] = {
+       { .compatible = "moxa,moxart-watchdog" },
+       { },
+};
+
+static struct platform_driver moxart_wdt_driver = {
+       .probe      = moxart_wdt_probe,
+       .remove     = moxart_wdt_remove,
+       .driver     = {
+               .name           = "moxart-watchdog",
+               .owner          = THIS_MODULE,
+               .of_match_table = moxart_watchdog_match,
+       },
+};
+module_platform_driver(moxart_wdt_driver);
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds");
+
+MODULE_DESCRIPTION("MOXART watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
index d0ebeba..d821520 100644 (file)
@@ -330,4 +330,3 @@ MODULE_AUTHOR("Dave Updegraff, Kumar Gala");
 MODULE_DESCRIPTION("Driver for watchdog timer in MPC8xx/MPC83xx/MPC86xx "
                   "uProcessors");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index b434111..edb31ff 100644 (file)
@@ -257,5 +257,4 @@ module_platform_driver(mtx1_wdt_driver);
 MODULE_AUTHOR("Michael Stickel, Florian Fainelli");
 MODULE_DESCRIPTION("Driver for the MTX-1 watchdog");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:mtx1-wdt");
index e4cf980..f9fa584 100644 (file)
@@ -255,7 +255,7 @@ static struct miscdevice mv64x60_wdt_miscdev = {
 
 static int mv64x60_wdt_probe(struct platform_device *dev)
 {
-       struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;
+       struct mv64x60_wdt_pdata *pdata = dev_get_platdata(&dev->dev);
        struct resource *r;
        int timeout = 10;
 
@@ -323,5 +323,4 @@ module_exit(mv64x60_wdt_exit);
 MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
 MODULE_DESCRIPTION("MV64x60 watchdog driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:" MV64x60_WDT_NAME);
index b15b6ef..a0d893b 100644 (file)
@@ -307,5 +307,4 @@ module_platform_driver(nuc900wdt_driver);
 MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
 MODULE_DESCRIPTION("Watchdog driver for NUC900");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:nuc900-wdt");
index 59cf19e..231e5b9 100644 (file)
@@ -513,4 +513,3 @@ module_exit(nv_tco_cleanup_module);
 MODULE_AUTHOR("Mike Waychison");
 MODULE_DESCRIPTION("TCO timer driver for NV chipsets");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 4dd281f..fb57103 100644 (file)
@@ -405,4 +405,3 @@ module_platform_driver(xwdt_driver);
 MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
 MODULE_DESCRIPTION("Xilinx Watchdog driver");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index af88ffd..09cf013 100644 (file)
@@ -68,14 +68,14 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
        void __iomem    *base = wdev->base;
 
        /* wait for posted write to complete */
-       while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
+       while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x08)
                cpu_relax();
 
        wdev->wdt_trgr_pattern = ~wdev->wdt_trgr_pattern;
-       __raw_writel(wdev->wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
+       writel_relaxed(wdev->wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
 
        /* wait for posted write to complete */
-       while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
+       while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x08)
                cpu_relax();
        /* reloaded WCRR from WLDR */
 }
@@ -85,12 +85,12 @@ static void omap_wdt_enable(struct omap_wdt_dev *wdev)
        void __iomem *base = wdev->base;
 
        /* Sequence to enable the watchdog */
-       __raw_writel(0xBBBB, base + OMAP_WATCHDOG_SPR);
-       while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
+       writel_relaxed(0xBBBB, base + OMAP_WATCHDOG_SPR);
+       while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x10)
                cpu_relax();
 
-       __raw_writel(0x4444, base + OMAP_WATCHDOG_SPR);
-       while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
+       writel_relaxed(0x4444, base + OMAP_WATCHDOG_SPR);
+       while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x10)
                cpu_relax();
 }
 
@@ -99,12 +99,12 @@ static void omap_wdt_disable(struct omap_wdt_dev *wdev)
        void __iomem *base = wdev->base;
 
        /* sequence required to disable watchdog */
-       __raw_writel(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
-       while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
+       writel_relaxed(0xAAAA, base + OMAP_WATCHDOG_SPR);       /* TIMER_MODE */
+       while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x10)
                cpu_relax();
 
-       __raw_writel(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
-       while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
+       writel_relaxed(0x5555, base + OMAP_WATCHDOG_SPR);       /* TIMER_MODE */
+       while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x10)
                cpu_relax();
 }
 
@@ -115,11 +115,11 @@ static void omap_wdt_set_timer(struct omap_wdt_dev *wdev,
        void __iomem *base = wdev->base;
 
        /* just count up at 32 KHz */
-       while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
+       while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x04)
                cpu_relax();
 
-       __raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR);
-       while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
+       writel_relaxed(pre_margin, base + OMAP_WATCHDOG_LDR);
+       while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x04)
                cpu_relax();
 }
 
@@ -135,11 +135,11 @@ static int omap_wdt_start(struct watchdog_device *wdog)
        pm_runtime_get_sync(wdev->dev);
 
        /* initialize prescaler */
-       while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
+       while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01)
                cpu_relax();
 
-       __raw_writel((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL);
-       while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
+       writel_relaxed((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL);
+       while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01)
                cpu_relax();
 
        omap_wdt_set_timer(wdev, wdog->timeout);
@@ -205,7 +205,7 @@ static const struct watchdog_ops omap_wdt_ops = {
 
 static int omap_wdt_probe(struct platform_device *pdev)
 {
-       struct omap_wd_timer_platform_data *pdata = pdev->dev.platform_data;
+       struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev);
        struct watchdog_device *omap_wdt;
        struct resource *res, *mem;
        struct omap_wdt_dev *wdev;
@@ -275,7 +275,7 @@ static int omap_wdt_probe(struct platform_device *pdev)
        }
 
        pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
-               __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
+               readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
                omap_wdt->timeout);
 
        pm_runtime_put_sync(wdev->dev);
index 4ea5fcc..44edca6 100644 (file)
@@ -207,7 +207,7 @@ static struct platform_driver orion_wdt_driver = {
        .driver         = {
                .owner  = THIS_MODULE,
                .name   = "orion_wdt",
-               .of_match_table = of_match_ptr(orion_wdt_of_match_table),
+               .of_match_table = orion_wdt_of_match_table,
        },
 };
 
@@ -225,4 +225,3 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:orion_wdt");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 5afb89b..5211d56 100644 (file)
@@ -580,8 +580,6 @@ MODULE_AUTHOR("Sven Anders <anders@anduras.de>, "
 MODULE_DESCRIPTION("PC87413 WDT driver");
 MODULE_LICENSE("GPL");
 
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
 module_param(io, int, 0);
 MODULE_PARM_DESC(io, MODNAME " I/O port (default: "
                                        __MODULE_STRING(IO_DEFAULT) ").");
index 33e49a7..e936f15 100644 (file)
@@ -61,7 +61,7 @@
 #include <linux/delay.h>       /* For mdelay function */
 #include <linux/timer.h>       /* For timer related operations */
 #include <linux/jiffies.h>     /* For jiffies stuff */
-#include <linux/miscdevice.h>  /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
+#include <linux/miscdevice.h>  /* For struct miscdevice */
 #include <linux/watchdog.h>    /* For the watchdog specific items */
 #include <linux/reboot.h>      /* For kernel_power_off() */
 #include <linux/init.h>                /* For __init/__exit/... */
@@ -1011,5 +1011,3 @@ MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, "
 MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
 MODULE_VERSION(WATCHDOG_VERSION);
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
index 7890f84..b4864f2 100644 (file)
@@ -40,7 +40,7 @@
 #include <linux/errno.h>       /* For the -ENODEV/... values */
 #include <linux/kernel.h>      /* For printk/panic/... */
 #include <linux/delay.h>       /* For mdelay function */
-#include <linux/miscdevice.h>  /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
+#include <linux/miscdevice.h>  /* For struct miscdevice */
 #include <linux/watchdog.h>    /* For the watchdog specific items */
 #include <linux/notifier.h>    /* For notifier support */
 #include <linux/reboot.h>      /* For reboot_notifier stuff */
@@ -820,5 +820,3 @@ module_pci_driver(pcipcwd_driver);
 MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
 MODULE_DESCRIPTION("Berkshire PCI-PC Watchdog driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
index 7b14d18..b731b5d 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/errno.h>       /* For the -ENODEV/... values */
 #include <linux/kernel.h>      /* For printk/panic/... */
 #include <linux/delay.h>       /* For mdelay function */
-#include <linux/miscdevice.h>  /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
+#include <linux/miscdevice.h>  /* For struct miscdevice */
 #include <linux/watchdog.h>    /* For the watchdog specific items */
 #include <linux/notifier.h>    /* For notifier support */
 #include <linux/reboot.h>      /* For reboot_notifier stuff */
@@ -72,8 +72,6 @@ do {                                                  \
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE(DRIVER_LICENSE);
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
 
 /* Module Parameters */
 module_param(debug, int, 0);
@@ -235,13 +233,17 @@ static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd,
                unsigned char cmd, unsigned char *msb, unsigned char *lsb)
 {
        int got_response, count;
-       unsigned char buf[6];
+       unsigned char *buf;
 
        /* We will not send any commands if the USB PCWD device does
         * not exist */
        if ((!usb_pcwd) || (!usb_pcwd->exists))
                return -1;
 
+       buf = kmalloc(6, GFP_KERNEL);
+       if (buf == NULL)
+               return 0;
+
        /* The USB PC Watchdog uses a 6 byte report format.
         * The board currently uses only 3 of the six bytes of the report. */
        buf[0] = cmd;                   /* Byte 0 = CMD */
@@ -256,8 +258,8 @@ static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd,
 
        if (usb_control_msg(usb_pcwd->udev, usb_sndctrlpipe(usb_pcwd->udev, 0),
                        HID_REQ_SET_REPORT, HID_DT_REPORT,
-                       0x0200, usb_pcwd->interface_number, buf, sizeof(buf),
-                       USB_COMMAND_TIMEOUT) != sizeof(buf)) {
+                       0x0200, usb_pcwd->interface_number, buf, 6,
+                       USB_COMMAND_TIMEOUT) != 6) {
                dbg("usb_pcwd_send_command: error in usb_control_msg for "
                                "cmd 0x%x 0x%x 0x%x\n", cmd, *msb, *lsb);
        }
@@ -277,6 +279,8 @@ static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd,
                *lsb = usb_pcwd->cmd_data_lsb;
        }
 
+       kfree(buf);
+
        return got_response;
 }
 
index 329bc60..0cdfee2 100644 (file)
@@ -299,5 +299,3 @@ module_exit(pikawdt_exit);
 MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>");
 MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
index b30bd43..1bdcc31 100644 (file)
@@ -233,5 +233,4 @@ MODULE_PARM_DESC(nowayout,
                 "Set to 1 to keep watchdog running after device release");
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:pnx4008-watchdog");
index 1b62a7d..882fdcb 100644 (file)
@@ -278,4 +278,3 @@ module_exit(watchdog_exit);
 MODULE_AUTHOR("Daniel Laird/Andre McCurdy");
 MODULE_DESCRIPTION("Hardware Watchdog Device for PNX833x");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 9cf6bc7..71e78ef 100644 (file)
@@ -25,8 +25,7 @@
 #include <linux/errno.h>               /* For the -ENODEV/... values */
 #include <linux/kernel.h>              /* For printk/panic/... */
 #include <linux/fs.h>                  /* For file operations */
-#include <linux/miscdevice.h>          /* For MODULE_ALIAS_MISCDEV
-                                                       (WATCHDOG_MINOR) */
+#include <linux/miscdevice.h>          /* For struct miscdevice */
 #include <linux/watchdog.h>            /* For the watchdog specific items */
 #include <linux/init.h>                        /* For __init/__exit/... */
 #include <linux/platform_device.h>     /* For platform_driver framework */
@@ -329,4 +328,3 @@ MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>,"
                "Florian Fainelli <florian@openwrt.org>");
 MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index b0f116c..082d062 100644 (file)
@@ -231,7 +231,7 @@ static int rdc321x_wdt_probe(struct platform_device *pdev)
        struct resource *r;
        struct rdc321x_wdt_pdata *pdata;
 
-       pdata = pdev->dev.platform_data;
+       pdata = dev_get_platdata(&pdev->dev);
        if (!pdata) {
                dev_err(&pdev->dev, "no platform data supplied\n");
                return -ENODEV;
@@ -298,4 +298,3 @@ module_platform_driver(rdc321x_wdt_driver);
 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
 MODULE_DESCRIPTION("RDC321x watchdog driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c
new file mode 100644 (file)
index 0000000..53d37fe
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * This driver was based on: drivers/watchdog/softdog.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/watchdog.h>
+#include <linux/miscdevice.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#define SYSC_RSTSTAT                   0x38
+#define WDT_RST_CAUSE                  BIT(1)
+
+#define RALINK_WDT_TIMEOUT             30
+#define RALINK_WDT_PRESCALE            65536
+
+#define TIMER_REG_TMR1LOAD             0x00
+#define TIMER_REG_TMR1CTL              0x08
+
+#define TMRSTAT_TMR1RST                        BIT(5)
+
+#define TMR1CTL_ENABLE                 BIT(7)
+#define TMR1CTL_MODE_SHIFT             4
+#define TMR1CTL_MODE_MASK              0x3
+#define TMR1CTL_MODE_FREE_RUNNING      0x0
+#define TMR1CTL_MODE_PERIODIC          0x1
+#define TMR1CTL_MODE_TIMEOUT           0x2
+#define TMR1CTL_MODE_WDT               0x3
+#define TMR1CTL_PRESCALE_MASK          0xf
+#define TMR1CTL_PRESCALE_65536         0xf
+
+static struct clk *rt288x_wdt_clk;
+static unsigned long rt288x_wdt_freq;
+static void __iomem *rt288x_wdt_base;
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+               "Watchdog cannot be stopped once started (default="
+               __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static inline void rt_wdt_w32(unsigned reg, u32 val)
+{
+       iowrite32(val, rt288x_wdt_base + reg);
+}
+
+static inline u32 rt_wdt_r32(unsigned reg)
+{
+       return ioread32(rt288x_wdt_base + reg);
+}
+
+static int rt288x_wdt_ping(struct watchdog_device *w)
+{
+       rt_wdt_w32(TIMER_REG_TMR1LOAD, w->timeout * rt288x_wdt_freq);
+
+       return 0;
+}
+
+static int rt288x_wdt_start(struct watchdog_device *w)
+{
+       u32 t;
+
+       t = rt_wdt_r32(TIMER_REG_TMR1CTL);
+       t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT |
+               TMR1CTL_PRESCALE_MASK);
+       t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT |
+               TMR1CTL_PRESCALE_65536);
+       rt_wdt_w32(TIMER_REG_TMR1CTL, t);
+
+       rt288x_wdt_ping(w);
+
+       t = rt_wdt_r32(TIMER_REG_TMR1CTL);
+       t |= TMR1CTL_ENABLE;
+       rt_wdt_w32(TIMER_REG_TMR1CTL, t);
+
+       return 0;
+}
+
+static int rt288x_wdt_stop(struct watchdog_device *w)
+{
+       u32 t;
+
+       rt288x_wdt_ping(w);
+
+       t = rt_wdt_r32(TIMER_REG_TMR1CTL);
+       t &= ~TMR1CTL_ENABLE;
+       rt_wdt_w32(TIMER_REG_TMR1CTL, t);
+
+       return 0;
+}
+
+static int rt288x_wdt_set_timeout(struct watchdog_device *w, unsigned int t)
+{
+       w->timeout = t;
+       rt288x_wdt_ping(w);
+
+       return 0;
+}
+
+static int rt288x_wdt_bootcause(void)
+{
+       if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE)
+               return WDIOF_CARDRESET;
+
+       return 0;
+}
+
+static struct watchdog_info rt288x_wdt_info = {
+       .identity = "Ralink Watchdog",
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+};
+
+static struct watchdog_ops rt288x_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = rt288x_wdt_start,
+       .stop = rt288x_wdt_stop,
+       .ping = rt288x_wdt_ping,
+       .set_timeout = rt288x_wdt_set_timeout,
+};
+
+static struct watchdog_device rt288x_wdt_dev = {
+       .info = &rt288x_wdt_info,
+       .ops = &rt288x_wdt_ops,
+       .min_timeout = 1,
+};
+
+static int rt288x_wdt_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       rt288x_wdt_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(rt288x_wdt_base))
+               return PTR_ERR(rt288x_wdt_base);
+
+       rt288x_wdt_clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(rt288x_wdt_clk))
+               return PTR_ERR(rt288x_wdt_clk);
+
+       device_reset(&pdev->dev);
+
+       rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE;
+
+       rt288x_wdt_dev.dev = &pdev->dev;
+       rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
+
+       rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
+       rt288x_wdt_dev.timeout = rt288x_wdt_dev.max_timeout;
+
+       watchdog_set_nowayout(&rt288x_wdt_dev, nowayout);
+
+       ret = watchdog_register_device(&rt288x_wdt_dev);
+       if (!ret)
+               dev_info(&pdev->dev, "Initialized\n");
+
+       return 0;
+}
+
+static int rt288x_wdt_remove(struct platform_device *pdev)
+{
+       watchdog_unregister_device(&rt288x_wdt_dev);
+
+       return 0;
+}
+
+static void rt288x_wdt_shutdown(struct platform_device *pdev)
+{
+       rt288x_wdt_stop(&rt288x_wdt_dev);
+}
+
+static const struct of_device_id rt288x_wdt_match[] = {
+       { .compatible = "ralink,rt2880-wdt" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rt288x_wdt_match);
+
+static struct platform_driver rt288x_wdt_driver = {
+       .probe          = rt288x_wdt_probe,
+       .remove         = rt288x_wdt_remove,
+       .shutdown       = rt288x_wdt_shutdown,
+       .driver         = {
+               .name           = KBUILD_MODNAME,
+               .owner          = THIS_MODULE,
+               .of_match_table = rt288x_wdt_match,
+       },
+};
+
+module_platform_driver(rt288x_wdt_driver);
+
+MODULE_DESCRIPTION("MediaTek/Ralink RT288x/RT3xxx hardware watchdog driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
+MODULE_LICENSE("GPL v2");
index 23aad7c..7d8fd04 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/moduleparam.h>
 #include <linux/types.h>
 #include <linux/timer.h>
-#include <linux/miscdevice.h> /* for MODULE_ALIAS_MISCDEV */
 #include <linux/watchdog.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
@@ -539,5 +538,4 @@ MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "
              "Dimitry Andric <dimitry.andric@tomtom.com>");
 MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:s3c2410-wdt");
index ccd6b29..e1d39a1 100644 (file)
@@ -193,4 +193,3 @@ module_param(margin, int, 0);
 MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index ea5d84a..3abae50 100644 (file)
@@ -341,7 +341,6 @@ MODULE_PARM_DESC(timeout,
       "Watchdog timeout in microseconds (max/default 8388607 or 8.3ish secs)");
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 /*
  * example code that can be put in a platform code area to utilize the
index 63632ec..2eef58a 100644 (file)
@@ -387,4 +387,3 @@ module_exit(sbc60xxwdt_unload);
 MODULE_AUTHOR("Jakob Oestergaard <jakob@unthought.net>");
 MODULE_DESCRIPTION("60xx Single Board Computer Watchdog Timer driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 719edc8..5f268ad 100644 (file)
@@ -309,5 +309,3 @@ MODULE_AUTHOR("Gilles Gigan");
 MODULE_DESCRIPTION("Watchdog device driver for single board"
                   " computers EPIC Nano 7240 from iEi");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
index d4781e0..da60560 100644 (file)
@@ -404,6 +404,5 @@ MODULE_AUTHOR("Ian E. Morgan <imorgan@webcon.ca>");
 MODULE_DESCRIPTION("SBC8360 watchdog driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("1.01");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 /* end of sbc8360.c */
index 0c3e9f6..a1c502e 100644 (file)
@@ -220,4 +220,3 @@ MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC.  "
        "so only use it if you are *sure* you are running on this specific "
        "SBC system from Winsystems!  It writes to IO ports 0x1ee and 0x1ef!");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 90d5527..a517d8b 100644 (file)
@@ -263,5 +263,3 @@ module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
index 3fb83b0..3b9fff9 100644 (file)
@@ -476,4 +476,3 @@ MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
 MODULE_DESCRIPTION(
        "Driver for National Semiconductor PC87307/PC97307 watchdog component");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 707e027..f353e18 100644 (file)
@@ -433,4 +433,3 @@ MODULE_AUTHOR("Scott and Bill Jennings");
 MODULE_DESCRIPTION(
        "Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index af7b136..b96127e 100644 (file)
@@ -26,8 +26,7 @@
 #include <linux/types.h>               /* For standard types (like size_t) */
 #include <linux/errno.h>               /* For the -ENODEV/... values */
 #include <linux/kernel.h>              /* For printk/... */
-#include <linux/miscdevice.h>          /* For MODULE_ALIAS_MISCDEV
-                                                       (WATCHDOG_MINOR) */
+#include <linux/miscdevice.h>          /* For struct miscdevice */
 #include <linux/watchdog.h>            /* For the watchdog specific items */
 #include <linux/init.h>                        /* For __init/__exit/... */
 #include <linux/fs.h>                  /* For file operations */
@@ -545,5 +544,3 @@ module_exit(sch311x_wdt_exit);
 MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
 MODULE_DESCRIPTION("SMSC SCH311x WatchDog Timer Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
index 8ae7c28..836377c 100644 (file)
@@ -37,7 +37,6 @@
 MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
 MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 static int margin = 60;                /* in seconds */
 module_param(margin, int, 0);
index 5bca794..f9b8e06 100644 (file)
@@ -343,7 +343,6 @@ MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
 MODULE_DESCRIPTION("SuperH watchdog driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:" DRV_NAME);
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 module_param(clock_division_ratio, int, 0);
 MODULE_PARM_DESC(clock_division_ratio,
diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c
new file mode 100644 (file)
index 0000000..ced3edc
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Watchdog driver for CSR SiRFprimaII and SiRFatlasVI
+ *
+ * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#define CLOCK_FREQ     1000000
+
+#define SIRFSOC_TIMER_COUNTER_LO       0x0000
+#define SIRFSOC_TIMER_MATCH_0          0x0008
+#define SIRFSOC_TIMER_INT_EN           0x0024
+#define SIRFSOC_TIMER_WATCHDOG_EN      0x0028
+#define SIRFSOC_TIMER_LATCH            0x0030
+#define SIRFSOC_TIMER_LATCHED_LO       0x0034
+
+#define SIRFSOC_TIMER_WDT_INDEX                5
+
+#define SIRFSOC_WDT_MIN_TIMEOUT                30              /* 30 secs */
+#define SIRFSOC_WDT_MAX_TIMEOUT                (10 * 60)       /* 10 mins */
+#define SIRFSOC_WDT_DEFAULT_TIMEOUT    30              /* 30 secs */
+
+static unsigned int timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
+
+module_param(timeout, uint, 0);
+module_param(nowayout, bool, 0);
+
+MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)");
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+                       __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd)
+{
+       u32 counter, match;
+       void __iomem *wdt_base;
+       int time_left;
+
+       wdt_base = watchdog_get_drvdata(wdd);
+       counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO);
+       match = readl(wdt_base +
+               SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
+
+       time_left = match - counter;
+
+       return time_left / CLOCK_FREQ;
+}
+
+static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd)
+{
+       u32 counter, timeout_ticks;
+       void __iomem *wdt_base;
+
+       timeout_ticks = wdd->timeout * CLOCK_FREQ;
+       wdt_base = watchdog_get_drvdata(wdd);
+
+       /* Enable the latch before reading the LATCH_LO register */
+       writel(1, wdt_base + SIRFSOC_TIMER_LATCH);
+
+       /* Set the TO value */
+       counter = readl(wdt_base + SIRFSOC_TIMER_LATCHED_LO);
+
+       counter += timeout_ticks;
+
+       writel(counter, wdt_base +
+               SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
+
+       return 0;
+}
+
+static int sirfsoc_wdt_enable(struct watchdog_device *wdd)
+{
+       void __iomem *wdt_base = watchdog_get_drvdata(wdd);
+       sirfsoc_wdt_updatetimeout(wdd);
+
+       /*
+        * NOTE: If interrupt is not enabled
+        * then WD-Reset doesn't get generated at all.
+        */
+       writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
+               | (1 << SIRFSOC_TIMER_WDT_INDEX),
+               wdt_base + SIRFSOC_TIMER_INT_EN);
+       writel(1, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
+
+       return 0;
+}
+
+static int sirfsoc_wdt_disable(struct watchdog_device *wdd)
+{
+       void __iomem *wdt_base = watchdog_get_drvdata(wdd);
+
+       writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
+       writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
+               & (~(1 << SIRFSOC_TIMER_WDT_INDEX)),
+               wdt_base + SIRFSOC_TIMER_INT_EN);
+
+       return 0;
+}
+
+static int sirfsoc_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
+{
+       wdd->timeout = to;
+       sirfsoc_wdt_updatetimeout(wdd);
+
+       return 0;
+}
+
+#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
+
+static const struct watchdog_info sirfsoc_wdt_ident = {
+       .options          =     OPTIONS,
+       .firmware_version =     0,
+       .identity         =     "SiRFSOC Watchdog",
+};
+
+static struct watchdog_ops sirfsoc_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = sirfsoc_wdt_enable,
+       .stop = sirfsoc_wdt_disable,
+       .get_timeleft = sirfsoc_wdt_gettimeleft,
+       .ping = sirfsoc_wdt_updatetimeout,
+       .set_timeout = sirfsoc_wdt_settimeout,
+};
+
+static struct watchdog_device sirfsoc_wdd = {
+       .info = &sirfsoc_wdt_ident,
+       .ops = &sirfsoc_wdt_ops,
+       .timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT,
+       .min_timeout = SIRFSOC_WDT_MIN_TIMEOUT,
+       .max_timeout = SIRFSOC_WDT_MAX_TIMEOUT,
+};
+
+static int sirfsoc_wdt_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int ret;
+       void __iomem *base;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       watchdog_set_drvdata(&sirfsoc_wdd, base);
+
+       watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
+       watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
+
+       ret = watchdog_register_device(&sirfsoc_wdd);
+       if (ret)
+               return ret;
+
+       platform_set_drvdata(pdev, &sirfsoc_wdd);
+
+       return 0;
+}
+
+static void sirfsoc_wdt_shutdown(struct platform_device *pdev)
+{
+       struct watchdog_device *wdd = platform_get_drvdata(pdev);
+
+       sirfsoc_wdt_disable(wdd);
+}
+
+static int sirfsoc_wdt_remove(struct platform_device *pdev)
+{
+       sirfsoc_wdt_shutdown(pdev);
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sirfsoc_wdt_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static int sirfsoc_wdt_resume(struct device *dev)
+{
+       struct watchdog_device *wdd = dev_get_drvdata(dev);
+
+       /*
+        * NOTE: Since timer controller registers settings are saved
+        * and restored back by the timer-prima2.c, so we need not
+        * update WD settings except refreshing timeout.
+        */
+       sirfsoc_wdt_updatetimeout(wdd);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(sirfsoc_wdt_pm_ops,
+               sirfsoc_wdt_suspend, sirfsoc_wdt_resume);
+
+static const struct of_device_id sirfsoc_wdt_of_match[] = {
+       { .compatible = "sirf,prima2-tick"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, sirfsoc_wdt_of_match);
+
+static struct platform_driver sirfsoc_wdt_driver = {
+       .driver = {
+               .name = "sirfsoc-wdt",
+               .owner = THIS_MODULE,
+               .pm = &sirfsoc_wdt_pm_ops,
+               .of_match_table = of_match_ptr(sirfsoc_wdt_of_match),
+       },
+       .probe = sirfsoc_wdt_probe,
+       .remove = sirfsoc_wdt_remove,
+       .shutdown = sirfsoc_wdt_shutdown,
+};
+module_platform_driver(sirfsoc_wdt_driver);
+
+MODULE_DESCRIPTION("SiRF SoC watchdog driver");
+MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sirfsoc-wdt");
index 6d665f9..445ea1a 100644 (file)
@@ -603,8 +603,6 @@ MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version "
                                                                VERSION ")");
 MODULE_LICENSE("GPL");
 
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
 #ifdef SMSC_SUPPORT_MINUTES
 module_param(unit, int, 0);
 MODULE_PARM_DESC(unit,
index b68b1e5..ef2638f 100644 (file)
@@ -207,4 +207,3 @@ module_exit(watchdog_exit);
 MODULE_AUTHOR("Alan Cox");
 MODULE_DESCRIPTION("Software Watchdog Device Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 0e9d8c4..ce63a1b 100644 (file)
@@ -580,4 +580,3 @@ module_exit(sp5100_tco_cleanup_module);
 MODULE_AUTHOR("Priyanka Gupta");
 MODULE_DESCRIPTION("TCO timer driver for SP5100/SB800 chipset");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 58df98a..3f786ce 100644 (file)
@@ -268,7 +268,6 @@ static int sp805_wdt_remove(struct amba_device *adev)
        struct sp805_wdt *wdt = amba_get_drvdata(adev);
 
        watchdog_unregister_device(&wdt->wdd);
-       amba_set_drvdata(adev, NULL);
        watchdog_set_drvdata(&wdt->wdd, NULL);
 
        return 0;
index c97e98d..d667f6b 100644 (file)
@@ -30,7 +30,7 @@ MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat period in seconds from 1 to "
 static int wdt_start(struct watchdog_device *wdd)
 {
        struct device *dev = watchdog_get_drvdata(wdd);
-       struct stmp3xxx_wdt_pdata *pdata = dev->platform_data;
+       struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(dev);
 
        pdata->wdt_set_timeout(dev->parent, wdd->timeout * WDOG_TICK_RATE);
        return 0;
@@ -39,7 +39,7 @@ static int wdt_start(struct watchdog_device *wdd)
 static int wdt_stop(struct watchdog_device *wdd)
 {
        struct device *dev = watchdog_get_drvdata(wdd);
-       struct stmp3xxx_wdt_pdata *pdata = dev->platform_data;
+       struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(dev);
 
        pdata->wdt_set_timeout(dev->parent, 0);
        return 0;
@@ -108,4 +108,3 @@ module_platform_driver(stmp3xxx_wdt_driver);
 MODULE_DESCRIPTION("STMP3XXX RTC Watchdog Driver");
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index f6caa77..76332d8 100644 (file)
@@ -217,7 +217,7 @@ static struct platform_driver sunxi_wdt_driver = {
        .driver         = {
                .owner          = THIS_MODULE,
                .name           = DRV_NAME,
-               .of_match_table = of_match_ptr(sunxi_wdt_dt_ids)
+               .of_match_table = sunxi_wdt_dt_ids,
        },
 };
 
index c9b0c62..09d4831 100644 (file)
@@ -192,7 +192,7 @@ static int ts72xx_wdt_open(struct inode *inode, struct file *file)
                dev_err(&wdt->pdev->dev,
                        "failed to convert timeout (%d) to register value\n",
                        timeout);
-               return -EINVAL;
+               return regval;
        }
 
        if (mutex_lock_interruptible(&wdt->lock))
@@ -305,7 +305,8 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
 
        switch (cmd) {
        case WDIOC_GETSUPPORT:
-               error = copy_to_user(argp, &winfo, sizeof(winfo));
+               if (copy_to_user(argp, &winfo, sizeof(winfo)))
+                       error = -EFAULT;
                break;
 
        case WDIOC_GETSTATUS:
@@ -320,10 +321,9 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
        case WDIOC_SETOPTIONS: {
                int options;
 
-               if (get_user(options, p)) {
-                       error = -EFAULT;
+               error = get_user(options, p);
+               if (error)
                        break;
-               }
 
                error = -EINVAL;
 
@@ -341,30 +341,26 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
 
        case WDIOC_SETTIMEOUT: {
                int new_timeout;
+               int regval;
 
-               if (get_user(new_timeout, p)) {
-                       error = -EFAULT;
-               } else {
-                       int regval;
-
-                       regval = timeout_to_regval(new_timeout);
-                       if (regval < 0) {
-                               error = -EINVAL;
-                       } else {
-                               ts72xx_wdt_stop(wdt);
-                               wdt->regval = regval;
-                               ts72xx_wdt_start(wdt);
-                       }
-               }
+               error = get_user(new_timeout, p);
                if (error)
                        break;
 
+               regval = timeout_to_regval(new_timeout);
+               if (regval < 0) {
+                       error = regval;
+                       break;
+               }
+               ts72xx_wdt_stop(wdt);
+               wdt->regval = regval;
+               ts72xx_wdt_start(wdt);
+
                /*FALLTHROUGH*/
        }
 
        case WDIOC_GETTIMEOUT:
-               if (put_user(regval_to_timeout(wdt->regval), p))
-                       error = -EFAULT;
+               error = put_user(regval_to_timeout(wdt->regval), p);
                break;
 
        default:
index 88f23c5..0fd0e8a 100644 (file)
@@ -176,5 +176,4 @@ module_platform_driver_probe(txx9wdt_driver, txx9wdt_probe);
 
 MODULE_DESCRIPTION("TXx9 Watchdog Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:txx9wdt");
index a614d84..e029b57 100644 (file)
@@ -88,7 +88,7 @@ static struct watchdog_device ux500_wdt = {
 static int ux500_wdt_probe(struct platform_device *pdev)
 {
        int ret;
-       struct ux500_wdt_data *pdata = pdev->dev.platform_data;
+       struct ux500_wdt_data *pdata = dev_get_platdata(&pdev->dev);
 
        if (pdata) {
                if (pdata->timeout > 0)
@@ -167,5 +167,4 @@ module_platform_driver(ux500_wdt_driver);
 MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>");
 MODULE_DESCRIPTION("Ux500 Watchdog Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:ux500_wdt");
index 92f1326..e24b210 100644 (file)
@@ -1,6 +1,9 @@
 /*
  *     w83627hf/thf WDT driver
  *
+ *     (c) Copyright 2013 Guenter Roeck
+ *             converted to watchdog infrastructure
+ *
  *     (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
  *             added support for W83627THF.
  *
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/types.h>
-#include <linux/miscdevice.h>
 #include <linux/watchdog.h>
-#include <linux/fs.h>
 #include <linux/ioport.h>
 #include <linux/notifier.h>
 #include <linux/reboot.h>
 #include <linux/init.h>
-#include <linux/spinlock.h>
 #include <linux/io.h>
-#include <linux/uaccess.h>
-
 
 #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
 #define WATCHDOG_TIMEOUT 60            /* 60 sec default timeout */
 
-static unsigned long wdt_is_open;
-static char expect_close;
-static DEFINE_SPINLOCK(io_lock);
-
 /* You must set this - there is no sane way to probe for this board. */
 static int wdt_io = 0x2E;
 module_param(wdt_io, int, 0);
 MODULE_PARM_DESC(wdt_io, "w83627hf/thf WDT io port (default 0x2E)");
 
-static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+static int timeout;                    /* in seconds */
 module_param(timeout, int, 0);
 MODULE_PARM_DESC(timeout,
                "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
@@ -76,236 +70,147 @@ MODULE_PARM_DESC(nowayout,
                                                        (same as EFER) */
 #define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
 
-static void w83627hf_select_wd_register(void)
+#define W83627HF_LD_WDT                0x08
+
+static void superio_outb(int reg, int val)
 {
-       unsigned char c;
+       outb(reg, WDT_EFER);
+       outb(val, WDT_EFDR);
+}
+
+static inline int superio_inb(int reg)
+{
+       outb(reg, WDT_EFER);
+       return inb(WDT_EFDR);
+}
+
+static int superio_enter(void)
+{
+       if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME))
+               return -EBUSY;
+
        outb_p(0x87, WDT_EFER); /* Enter extended function mode */
        outb_p(0x87, WDT_EFER); /* Again according to manual */
 
-       outb(0x20, WDT_EFER);   /* check chip version   */
-       c = inb(WDT_EFDR);
-       if (c == 0x82) {        /* W83627THF            */
-               outb_p(0x2b, WDT_EFER); /* select GPIO3 */
-               c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */
-               outb_p(0x2b, WDT_EFER);
-               outb_p(c, WDT_EFDR);    /* set GPIO3 to WDT0 */
-       } else if (c == 0x88 || c == 0xa0) {    /* W83627EHF / W83627DHG */
-               outb_p(0x2d, WDT_EFER); /* select GPIO5 */
-               c = inb_p(WDT_EFDR) & ~0x01; /* PIN77 -> WDT0# */
-               outb_p(0x2d, WDT_EFER);
-               outb_p(c, WDT_EFDR); /* set GPIO5 to WDT0 */
-       }
+       return 0;
+}
 
-       outb_p(0x07, WDT_EFER); /* point to logical device number reg */
-       outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
-       outb_p(0x30, WDT_EFER); /* select CR30 */
-       outb_p(0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
+static void superio_select(int ld)
+{
+       superio_outb(0x07, ld);
 }
 
-static void w83627hf_unselect_wd_register(void)
+static void superio_exit(void)
 {
        outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
+       release_region(wdt_io, 2);
 }
 
 /* tyan motherboards seem to set F5 to 0x4C ?
  * So explicitly init to appropriate value. */
 
-static void w83627hf_init(void)
+static int w83627hf_init(struct watchdog_device *wdog)
 {
+       int ret;
        unsigned char t;
 
-       w83627hf_select_wd_register();
+       ret = superio_enter();
+       if (ret)
+               return ret;
+
+       superio_select(W83627HF_LD_WDT);
+       t = superio_inb(0x20);  /* check chip version   */
+       if (t == 0x82) {        /* W83627THF            */
+               t = (superio_inb(0x2b) & 0xf7);
+               superio_outb(0x2b, t | 0x04); /* set GPIO3 to WDT0 */
+       } else if (t == 0x88 || t == 0xa0) {    /* W83627EHF / W83627DHG */
+               t = superio_inb(0x2d);
+               superio_outb(0x2d, t & ~0x01);  /* set GPIO5 to WDT0 */
+       }
+
+       /* set CR30 bit 0 to activate GPIO2 */
+       t = superio_inb(0x30);
+       if (!(t & 0x01))
+               superio_outb(0x30, t | 0x01);
 
-       outb_p(0xF6, WDT_EFER); /* Select CRF6 */
-       t = inb_p(WDT_EFDR);      /* read CRF6 */
+       t = superio_inb(0xF6);
        if (t != 0) {
                pr_info("Watchdog already running. Resetting timeout to %d sec\n",
-                       timeout);
-               outb_p(timeout, WDT_EFDR);    /* Write back to CRF6 */
+                       wdog->timeout);
+               superio_outb(0xF6, wdog->timeout);
        }
 
-       outb_p(0xF5, WDT_EFER); /* Select CRF5 */
-       t = inb_p(WDT_EFDR);      /* read CRF5 */
-       t &= ~0x0C;               /* set second mode & disable keyboard
-                                   turning off watchdog */
-       t |= 0x02;                /* enable the WDTO# output low pulse
-                                   to the KBRST# pin (PIN60) */
-       outb_p(t, WDT_EFDR);    /* Write back to CRF5 */
-
-       outb_p(0xF7, WDT_EFER); /* Select CRF7 */
-       t = inb_p(WDT_EFDR);      /* read CRF7 */
-       t &= ~0xC0;               /* disable keyboard & mouse turning off
-                                   watchdog */
-       outb_p(t, WDT_EFDR);    /* Write back to CRF7 */
-
-       w83627hf_unselect_wd_register();
-}
-
-static void wdt_set_time(int timeout)
-{
-       spin_lock(&io_lock);
-
-       w83627hf_select_wd_register();
+       /* set second mode & disable keyboard turning off watchdog */
+       t = superio_inb(0xF5) & ~0x0C;
+       /* enable the WDTO# output low pulse to the KBRST# pin */
+       t |= 0x02;
+       superio_outb(0xF5, t);
 
-       outb_p(0xF6, WDT_EFER);    /* Select CRF6 */
-       outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */
+       /* disable keyboard & mouse turning off watchdog */
+       t = superio_inb(0xF7) & ~0xC0;
+       superio_outb(0xF7, t);
 
-       w83627hf_unselect_wd_register();
+       superio_exit();
 
-       spin_unlock(&io_lock);
-}
-
-static int wdt_ping(void)
-{
-       wdt_set_time(timeout);
        return 0;
 }
 
-static int wdt_disable(void)
+static int wdt_set_time(unsigned int timeout)
 {
-       wdt_set_time(0);
-       return 0;
-}
+       int ret;
+
+       ret = superio_enter();
+       if (ret)
+               return ret;
+
+       superio_select(W83627HF_LD_WDT);
+       superio_outb(0xF6, timeout);
+       superio_exit();
 
-static int wdt_set_heartbeat(int t)
-{
-       if (t < 1 || t > 255)
-               return -EINVAL;
-       timeout = t;
        return 0;
 }
 
-static int wdt_get_time(void)
+static int wdt_start(struct watchdog_device *wdog)
 {
-       int timeleft;
-
-       spin_lock(&io_lock);
-
-       w83627hf_select_wd_register();
-
-       outb_p(0xF6, WDT_EFER);    /* Select CRF6 */
-       timeleft = inb_p(WDT_EFDR); /* Read Timeout counter to CRF6 */
-
-       w83627hf_unselect_wd_register();
-
-       spin_unlock(&io_lock);
-
-       return timeleft;
+       return wdt_set_time(wdog->timeout);
 }
 
-static ssize_t wdt_write(struct file *file, const char __user *buf,
-                                               size_t count, loff_t *ppos)
+static int wdt_stop(struct watchdog_device *wdog)
 {
-       if (count) {
-               if (!nowayout) {
-                       size_t i;
-
-                       expect_close = 0;
-
-                       for (i = 0; i != count; i++) {
-                               char c;
-                               if (get_user(c, buf + i))
-                                       return -EFAULT;
-                               if (c == 'V')
-                                       expect_close = 42;
-                       }
-               }
-               wdt_ping();
-       }
-       return count;
+       return wdt_set_time(0);
 }
 
-static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)
 {
-       void __user *argp = (void __user *)arg;
-       int __user *p = argp;
-       int timeval;
-       static const struct watchdog_info ident = {
-               .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
-                                                       WDIOF_MAGICCLOSE,
-               .firmware_version = 1,
-               .identity = "W83627HF WDT",
-       };
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               if (copy_to_user(argp, &ident, sizeof(ident)))
-                       return -EFAULT;
-               break;
-       case WDIOC_GETSTATUS:
-       case WDIOC_GETBOOTSTATUS:
-               return put_user(0, p);
-       case WDIOC_SETOPTIONS:
-       {
-               int options, retval = -EINVAL;
-
-               if (get_user(options, p))
-                       return -EFAULT;
-               if (options & WDIOS_DISABLECARD) {
-                       wdt_disable();
-                       retval = 0;
-               }
-               if (options & WDIOS_ENABLECARD) {
-                       wdt_ping();
-                       retval = 0;
-               }
-               return retval;
-       }
-       case WDIOC_KEEPALIVE:
-               wdt_ping();
-               break;
-       case WDIOC_SETTIMEOUT:
-               if (get_user(timeval, p))
-                       return -EFAULT;
-               if (wdt_set_heartbeat(timeval))
-                       return -EINVAL;
-               wdt_ping();
-               /* Fall */
-       case WDIOC_GETTIMEOUT:
-               return put_user(timeout, p);
-       case WDIOC_GETTIMELEFT:
-               timeval = wdt_get_time();
-               return put_user(timeval, p);
-       default:
-               return -ENOTTY;
-       }
+       wdog->timeout = timeout;
+
        return 0;
 }
 
-static int wdt_open(struct inode *inode, struct file *file)
+static unsigned int wdt_get_time(struct watchdog_device *wdog)
 {
-       if (test_and_set_bit(0, &wdt_is_open))
-               return -EBUSY;
-       /*
-        *      Activate
-        */
+       unsigned int timeleft;
+       int ret;
 
-       wdt_ping();
-       return nonseekable_open(inode, file);
-}
+       ret = superio_enter();
+       if (ret)
+               return 0;
 
-static int wdt_close(struct inode *inode, struct file *file)
-{
-       if (expect_close == 42)
-               wdt_disable();
-       else {
-               pr_crit("Unexpected close, not stopping watchdog!\n");
-               wdt_ping();
-       }
-       expect_close = 0;
-       clear_bit(0, &wdt_is_open);
-       return 0;
+       superio_select(W83627HF_LD_WDT);
+       timeleft = superio_inb(0xF6);
+       superio_exit();
+
+       return timeleft;
 }
 
 /*
  *     Notifier for system down
  */
-
 static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
        void *unused)
 {
        if (code == SYS_DOWN || code == SYS_HALT)
-               wdt_disable();  /* Turn the WDT off */
+               wdt_set_time(0);        /* Turn the WDT off */
 
        return NOTIFY_DONE;
 }
@@ -314,19 +219,25 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
  *     Kernel Interfaces
  */
 
-static const struct file_operations wdt_fops = {
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .write          = wdt_write,
-       .unlocked_ioctl = wdt_ioctl,
-       .open           = wdt_open,
-       .release        = wdt_close,
+static struct watchdog_info wdt_info = {
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+       .identity = "W83627HF Watchdog",
 };
 
-static struct miscdevice wdt_miscdev = {
-       .minor = WATCHDOG_MINOR,
-       .name = "watchdog",
-       .fops = &wdt_fops,
+static struct watchdog_ops wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = wdt_start,
+       .stop = wdt_stop,
+       .set_timeout = wdt_set_timeout,
+       .get_timeleft = wdt_get_time,
+};
+
+static struct watchdog_device wdt_dev = {
+       .info = &wdt_info,
+       .ops = &wdt_ops,
+       .timeout = WATCHDOG_TIMEOUT,
+       .min_timeout = 1,
+       .max_timeout = 255,
 };
 
 /*
@@ -344,50 +255,39 @@ static int __init wdt_init(void)
 
        pr_info("WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising\n");
 
-       if (wdt_set_heartbeat(timeout)) {
-               wdt_set_heartbeat(WATCHDOG_TIMEOUT);
-               pr_info("timeout value must be 1 <= timeout <= 255, using %d\n",
-                       WATCHDOG_TIMEOUT);
-       }
+       watchdog_init_timeout(&wdt_dev, timeout, NULL);
+       watchdog_set_nowayout(&wdt_dev, nowayout);
 
-       if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
-               pr_err("I/O address 0x%04x already in use\n", wdt_io);
-               ret = -EIO;
-               goto out;
+       ret = w83627hf_init(&wdt_dev);
+       if (ret) {
+               pr_err("failed to initialize watchdog (err=%d)\n", ret);
+               return ret;
        }
 
-       w83627hf_init();
-
        ret = register_reboot_notifier(&wdt_notifier);
        if (ret != 0) {
                pr_err("cannot register reboot notifier (err=%d)\n", ret);
-               goto unreg_regions;
+               return ret;
        }
 
-       ret = misc_register(&wdt_miscdev);
-       if (ret != 0) {
-               pr_err("cannot register miscdev on minor=%d (err=%d)\n",
-                      WATCHDOG_MINOR, ret);
+       ret = watchdog_register_device(&wdt_dev);
+       if (ret)
                goto unreg_reboot;
-       }
 
        pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
-               timeout, nowayout);
+               wdt_dev.timeout, nowayout);
 
-out:
        return ret;
+
 unreg_reboot:
        unregister_reboot_notifier(&wdt_notifier);
-unreg_regions:
-       release_region(wdt_io, 1);
-       goto out;
+       return ret;
 }
 
 static void __exit wdt_exit(void)
 {
-       misc_deregister(&wdt_miscdev);
+       watchdog_unregister_device(&wdt_dev);
        unregister_reboot_notifier(&wdt_notifier);
-       release_region(wdt_io, 1);
 }
 
 module_init(wdt_init);
@@ -396,4 +296,3 @@ module_exit(wdt_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Pádraig  Brady <P@draigBrady.com>");
 MODULE_DESCRIPTION("w83627hf/thf WDT driver");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index cd9f3c1..aaf2995 100644 (file)
@@ -458,4 +458,3 @@ MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Marcus Junker <junker@anduras.de>, "
                "Samuel Tardieu <sam@rfc1149.net>");
 MODULE_DESCRIPTION("w83697hf/hg WDT driver");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 274be0b..ff58cb7 100644 (file)
@@ -395,4 +395,3 @@ module_exit(wdt_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>");
 MODULE_DESCRIPTION("w83697ug/uf WDT driver");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 7874ae0..f0483c7 100644 (file)
@@ -406,4 +406,3 @@ module_exit(w83877f_wdt_unload);
 MODULE_AUTHOR("Scott and Bill Jennings");
 MODULE_DESCRIPTION("Driver for watchdog timer in w83877f chip");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 5d2c902..91bf55a 100644 (file)
@@ -527,4 +527,3 @@ module_exit(w83977f_wdt_exit);
 MODULE_AUTHOR("Jose Goncalves <jose.goncalves@inov.pt>");
 MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 25aba6e..db0da7e 100644 (file)
@@ -322,6 +322,5 @@ module_exit(wafwdt_exit);
 MODULE_AUTHOR("Justin Cormack");
 MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 /* end of wafer5823wdt.c */
index 05d18b4..461336c 100644 (file)
@@ -77,7 +77,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
 
        watchdog_check_min_max_timeout(wdd);
 
-       /* try to get the tiemout module parameter first */
+       /* try to get the timeout module parameter first */
        if (!watchdog_timeout_invalid(wdd, timeout_parm)) {
                wdd->timeout = timeout_parm;
                return ret;
index 3045deb..0240c60 100644 (file)
@@ -48,8 +48,6 @@
 MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
 MODULE_DESCRIPTION("RTAS watchdog driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
 
 static bool wdrtas_nowayout = WATCHDOG_NOWAYOUT;
 static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0);
index ee4333c..e0206b5 100644 (file)
@@ -664,6 +664,4 @@ module_exit(wdt_exit);
 
 MODULE_AUTHOR("Alan Cox");
 MODULE_DESCRIPTION("Driver for ISA ICS watchdog cards (WDT500/501)");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
 MODULE_LICENSE("GPL");
index 5eec740..7355ddd 100644 (file)
@@ -224,7 +224,6 @@ static void __exit footbridge_watchdog_exit(void)
 MODULE_AUTHOR("Phil Blundell <pb@nexus.co.uk>");
 MODULE_DESCRIPTION("Footbridge watchdog driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
 module_param(soft_margin, int, 0);
 MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
index 65a4023..a8e6f87 100644 (file)
@@ -507,4 +507,3 @@ module_exit(wd977_exit);
 MODULE_AUTHOR("Woody Suwalski <woodys@xandros.com>");
 MODULE_DESCRIPTION("W83977AF Watchdog driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 36a54c0..ee89ba4 100644 (file)
@@ -744,5 +744,3 @@ module_pci_driver(wdtpci_driver);
 MODULE_AUTHOR("JP Nollmann, Alan Cox");
 MODULE_DESCRIPTION("Driver for the ICS PCI-WDT500/501 watchdog cards");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
index d4e47ed..e243bd0 100644 (file)
@@ -184,7 +184,7 @@ static const struct watchdog_ops wm831x_wdt_ops = {
 static int wm831x_wdt_probe(struct platform_device *pdev)
 {
        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
-       struct wm831x_pdata *chip_pdata;
+       struct wm831x_pdata *chip_pdata = dev_get_platdata(pdev->dev.parent);
        struct wm831x_watchdog_pdata *pdata;
        struct wm831x_wdt_drvdata *driver_data;
        struct watchdog_device *wm831x_wdt;
@@ -231,12 +231,10 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
                wm831x_wdt->timeout = wm831x_wdt_cfgs[i].time;
 
        /* Apply any configuration */
-       if (pdev->dev.parent->platform_data) {
-               chip_pdata = pdev->dev.parent->platform_data;
+       if (chip_pdata)
                pdata = chip_pdata->watchdog;
-       } else {
+       else
                pdata = NULL;
-       }
 
        if (pdata) {
                reg &= ~(WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK |
index 92ad33d..7a42dff 100644 (file)
@@ -362,4 +362,3 @@ MODULE_AUTHOR("Jan Beulich <jbeulich@novell.com>");
 MODULE_DESCRIPTION("Xen WatchDog Timer Driver");
 MODULE_VERSION(DRV_VERSION);
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 23eae5c..c794ea1 100644 (file)
@@ -140,7 +140,6 @@ config XEN_GRANT_DEV_ALLOC
 
 config SWIOTLB_XEN
        def_bool y
-       depends on PCI && X86
        select SWIOTLB
 
 config XEN_TMEM
index b232908..55ea73f 100644 (file)
@@ -596,7 +596,7 @@ static void __init balloon_add_region(unsigned long start_pfn,
        }
 }
 
-static int __cpuinit balloon_cpu_notify(struct notifier_block *self,
+static int balloon_cpu_notify(struct notifier_block *self,
                                    unsigned long action, void *hcpu)
 {
        int cpu = (long)hcpu;
@@ -616,7 +616,7 @@ static int __cpuinit balloon_cpu_notify(struct notifier_block *self,
        return NOTIFY_OK;
 }
 
-static struct notifier_block balloon_cpu_notifier __cpuinitdata = {
+static struct notifier_block balloon_cpu_notifier = {
        .notifier_call  = balloon_cpu_notify,
 };
 
@@ -641,7 +641,7 @@ static int __init balloon_init(void)
 
        balloon_stats.current_pages = xen_pv_domain()
                ? min(xen_start_info->nr_pages - xen_released_pages, max_pfn)
-               : max_pfn;
+               : get_num_physpages();
        balloon_stats.target_pages  = balloon_stats.current_pages;
        balloon_stats.balloon_low   = 0;
        balloon_stats.balloon_high  = 0;
index 8b3a69a..5de2063 100644 (file)
@@ -305,7 +305,7 @@ static int evtchn_bind_to_user(struct per_user_data *u, int port)
        if (rc < 0)
                goto err;
 
-       rc = bind_evtchn_to_irqhandler(port, evtchn_interrupt, IRQF_DISABLED,
+       rc = bind_evtchn_to_irqhandler(port, evtchn_interrupt, 0,
                                       u->name, evtchn);
        if (rc < 0)
                goto err;
index c4d2298..62ccf54 100644 (file)
@@ -49,6 +49,7 @@
 #include <xen/grant_table.h>
 #include <xen/interface/memory.h>
 #include <xen/hvc-console.h>
+#include <xen/swiotlb-xen.h>
 #include <asm/xen/hypercall.h>
 #include <asm/xen/interface.h>
 
@@ -898,8 +899,16 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
                        gnttab_retry_eagain_gop(GNTTABOP_map_grant_ref, map_ops + i,
                                                &map_ops[i].status, __func__);
 
-       if (xen_feature(XENFEAT_auto_translated_physmap))
+       /* this is basically a nop on x86 */
+       if (xen_feature(XENFEAT_auto_translated_physmap)) {
+               for (i = 0; i < count; i++) {
+                       if (map_ops[i].status)
+                               continue;
+                       set_phys_to_machine(map_ops[i].host_addr >> PAGE_SHIFT,
+                                       map_ops[i].dev_bus_addr >> PAGE_SHIFT);
+               }
                return ret;
+       }
 
        if (!in_interrupt() && paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) {
                arch_enter_lazy_mmu_mode();
@@ -942,8 +951,14 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
        if (ret)
                return ret;
 
-       if (xen_feature(XENFEAT_auto_translated_physmap))
+       /* this is basically a nop on x86 */
+       if (xen_feature(XENFEAT_auto_translated_physmap)) {
+               for (i = 0; i < count; i++) {
+                       set_phys_to_machine(unmap_ops[i].host_addr >> PAGE_SHIFT,
+                                       INVALID_P2M_ENTRY);
+               }
                return ret;
+       }
 
        if (!in_interrupt() && paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) {
                arch_enter_lazy_mmu_mode();
index 18fff88..1888251 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/xen/hypervisor.h>
 #include <asm/xen/hypercall.h>
 #include "../pci/pci.h"
+#include <asm/pci_x86.h>
 
 static bool __read_mostly pci_seg_supported = true;
 
@@ -58,12 +59,12 @@ static int xen_add_device(struct device *dev)
                        add.flags = XEN_PCI_DEV_EXTFN;
 
 #ifdef CONFIG_ACPI
-               handle = DEVICE_ACPI_HANDLE(&pci_dev->dev);
+               handle = ACPI_HANDLE(&pci_dev->dev);
                if (!handle && pci_dev->bus->bridge)
-                       handle = DEVICE_ACPI_HANDLE(pci_dev->bus->bridge);
+                       handle = ACPI_HANDLE(pci_dev->bus->bridge);
 #ifdef CONFIG_PCI_IOV
                if (!handle && pci_dev->is_virtfn)
-                       handle = DEVICE_ACPI_HANDLE(physfn->bus->bridge);
+                       handle = ACPI_HANDLE(physfn->bus->bridge);
 #endif
                if (handle) {
                        acpi_status status;
@@ -192,3 +193,49 @@ static int __init register_xen_pci_notifier(void)
 }
 
 arch_initcall(register_xen_pci_notifier);
+
+#ifdef CONFIG_PCI_MMCONFIG
+static int __init xen_mcfg_late(void)
+{
+       struct pci_mmcfg_region *cfg;
+       int rc;
+
+       if (!xen_initial_domain())
+               return 0;
+
+       if ((pci_probe & PCI_PROBE_MMCONF) == 0)
+               return 0;
+
+       if (list_empty(&pci_mmcfg_list))
+               return 0;
+
+       /* Check whether they are in the right area. */
+       list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+               struct physdev_pci_mmcfg_reserved r;
+
+               r.address = cfg->address;
+               r.segment = cfg->segment;
+               r.start_bus = cfg->start_bus;
+               r.end_bus = cfg->end_bus;
+               r.flags = XEN_PCI_MMCFG_RESERVED;
+
+               rc = HYPERVISOR_physdev_op(PHYSDEVOP_pci_mmcfg_reserved, &r);
+               switch (rc) {
+               case 0:
+               case -ENOSYS:
+                       continue;
+
+               default:
+                       pr_warn("Failed to report MMCONFIG reservation"
+                               " state for %s to hypervisor"
+                               " (%d)\n",
+                               cfg->name, rc);
+               }
+       }
+       return 0;
+}
+/*
+ * Needs to be done after acpi_init which are subsys_initcall.
+ */
+subsys_initcall_sync(xen_mcfg_late);
+#endif
index 99db9e1..2f3528e 100644 (file)
@@ -84,7 +84,7 @@ static irqreturn_t do_hvm_evtchn_intr(int irq, void *dev_id)
 static int xen_allocate_irq(struct pci_dev *pdev)
 {
        return request_irq(pdev->irq, do_hvm_evtchn_intr,
-                       IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TRIGGER_RISING,
+                       IRQF_NOBALANCING | IRQF_TRIGGER_RISING,
                        "xen-platform-pci", pdev);
 }
 
index 1b2277c..a224bc7 100644 (file)
 #include <xen/page.h>
 #include <xen/xen-ops.h>
 #include <xen/hvc-console.h>
+
+#include <asm/dma-mapping.h>
+#include <asm/xen/page-coherent.h>
+
+#include <trace/events/swiotlb.h>
 /*
  * Used to do a quick range check in swiotlb_tbl_unmap_single and
  * swiotlb_tbl_sync_single_*, to see if the memory was in fact allocated by this
  * API.
  */
 
+#ifndef CONFIG_X86
+static unsigned long dma_alloc_coherent_mask(struct device *dev,
+                                           gfp_t gfp)
+{
+       unsigned long dma_mask = 0;
+
+       dma_mask = dev->coherent_dma_mask;
+       if (!dma_mask)
+               dma_mask = (gfp & GFP_DMA) ? DMA_BIT_MASK(24) : DMA_BIT_MASK(32);
+
+       return dma_mask;
+}
+#endif
+
 static char *xen_io_tlb_start, *xen_io_tlb_end;
 static unsigned long xen_io_tlb_nslabs;
 /*
@@ -56,17 +75,17 @@ static unsigned long xen_io_tlb_nslabs;
 
 static u64 start_dma_addr;
 
-static dma_addr_t xen_phys_to_bus(phys_addr_t paddr)
+static inline dma_addr_t xen_phys_to_bus(phys_addr_t paddr)
 {
        return phys_to_machine(XPADDR(paddr)).maddr;
 }
 
-static phys_addr_t xen_bus_to_phys(dma_addr_t baddr)
+static inline phys_addr_t xen_bus_to_phys(dma_addr_t baddr)
 {
        return machine_to_phys(XMADDR(baddr)).paddr;
 }
 
-static dma_addr_t xen_virt_to_bus(void *address)
+static inline dma_addr_t xen_virt_to_bus(void *address)
 {
        return xen_phys_to_bus(virt_to_phys(address));
 }
@@ -89,7 +108,7 @@ static int check_pages_physically_contiguous(unsigned long pfn,
        return 1;
 }
 
-static int range_straddles_page_boundary(phys_addr_t p, size_t size)
+static inline int range_straddles_page_boundary(phys_addr_t p, size_t size)
 {
        unsigned long pfn = PFN_DOWN(p);
        unsigned int offset = p & ~PAGE_MASK;
@@ -126,6 +145,8 @@ xen_swiotlb_fixup(void *buf, size_t size, unsigned long nslabs)
 {
        int i, rc;
        int dma_bits;
+       dma_addr_t dma_handle;
+       phys_addr_t p = virt_to_phys(buf);
 
        dma_bits = get_order(IO_TLB_SEGSIZE << IO_TLB_SHIFT) + PAGE_SHIFT;
 
@@ -135,9 +156,9 @@ xen_swiotlb_fixup(void *buf, size_t size, unsigned long nslabs)
 
                do {
                        rc = xen_create_contiguous_region(
-                               (unsigned long)buf + (i << IO_TLB_SHIFT),
+                               p + (i << IO_TLB_SHIFT),
                                get_order(slabs << IO_TLB_SHIFT),
-                               dma_bits);
+                               dma_bits, &dma_handle);
                } while (rc && dma_bits++ < max_dma_bits);
                if (rc)
                        return rc;
@@ -263,7 +284,6 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
        void *ret;
        int order = get_order(size);
        u64 dma_mask = DMA_BIT_MASK(32);
-       unsigned long vstart;
        phys_addr_t phys;
        dma_addr_t dev_addr;
 
@@ -278,8 +298,12 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
        if (dma_alloc_from_coherent(hwdev, size, dma_handle, &ret))
                return ret;
 
-       vstart = __get_free_pages(flags, order);
-       ret = (void *)vstart;
+       /* On ARM this function returns an ioremap'ped virtual address for
+        * which virt_to_phys doesn't return the corresponding physical
+        * address. In fact on ARM virt_to_phys only works for kernel direct
+        * mapped RAM memory. Also see comment below.
+        */
+       ret = xen_alloc_coherent_pages(hwdev, size, dma_handle, flags, attrs);
 
        if (!ret)
                return ret;
@@ -287,18 +311,21 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
        if (hwdev && hwdev->coherent_dma_mask)
                dma_mask = dma_alloc_coherent_mask(hwdev, flags);
 
-       phys = virt_to_phys(ret);
+       /* At this point dma_handle is the physical address, next we are
+        * going to set it to the machine address.
+        * Do not use virt_to_phys(ret) because on ARM it doesn't correspond
+        * to *dma_handle. */
+       phys = *dma_handle;
        dev_addr = xen_phys_to_bus(phys);
        if (((dev_addr + size - 1 <= dma_mask)) &&
            !range_straddles_page_boundary(phys, size))
                *dma_handle = dev_addr;
        else {
-               if (xen_create_contiguous_region(vstart, order,
-                                                fls64(dma_mask)) != 0) {
-                       free_pages(vstart, order);
+               if (xen_create_contiguous_region(phys, order,
+                                                fls64(dma_mask), dma_handle) != 0) {
+                       xen_free_coherent_pages(hwdev, size, ret, (dma_addr_t)phys, attrs);
                        return NULL;
                }
-               *dma_handle = virt_to_machine(ret).maddr;
        }
        memset(ret, 0, size);
        return ret;
@@ -319,13 +346,15 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr,
        if (hwdev && hwdev->coherent_dma_mask)
                dma_mask = hwdev->coherent_dma_mask;
 
-       phys = virt_to_phys(vaddr);
+       /* do not use virt_to_phys because on ARM it doesn't return you the
+        * physical address */
+       phys = xen_bus_to_phys(dev_addr);
 
        if (((dev_addr + size - 1 > dma_mask)) ||
            range_straddles_page_boundary(phys, size))
-               xen_destroy_contiguous_region((unsigned long)vaddr, order);
+               xen_destroy_contiguous_region(phys, order);
 
-       free_pages((unsigned long)vaddr, order);
+       xen_free_coherent_pages(hwdev, size, vaddr, (dma_addr_t)phys, attrs);
 }
 EXPORT_SYMBOL_GPL(xen_swiotlb_free_coherent);
 
@@ -352,16 +381,25 @@ dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
         * buffering it.
         */
        if (dma_capable(dev, dev_addr, size) &&
-           !range_straddles_page_boundary(phys, size) && !swiotlb_force)
+           !range_straddles_page_boundary(phys, size) && !swiotlb_force) {
+               /* we are not interested in the dma_addr returned by
+                * xen_dma_map_page, only in the potential cache flushes executed
+                * by the function. */
+               xen_dma_map_page(dev, page, offset, size, dir, attrs);
                return dev_addr;
+       }
 
        /*
         * Oh well, have to allocate and map a bounce buffer.
         */
+       trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force);
+
        map = swiotlb_tbl_map_single(dev, start_dma_addr, phys, size, dir);
        if (map == SWIOTLB_MAP_ERROR)
                return DMA_ERROR_CODE;
 
+       xen_dma_map_page(dev, pfn_to_page(map >> PAGE_SHIFT),
+                                       map & ~PAGE_MASK, size, dir, attrs);
        dev_addr = xen_phys_to_bus(map);
 
        /*
@@ -384,12 +422,15 @@ EXPORT_SYMBOL_GPL(xen_swiotlb_map_page);
  * whatever the device wrote there.
  */
 static void xen_unmap_single(struct device *hwdev, dma_addr_t dev_addr,
-                            size_t size, enum dma_data_direction dir)
+                            size_t size, enum dma_data_direction dir,
+                                struct dma_attrs *attrs)
 {
        phys_addr_t paddr = xen_bus_to_phys(dev_addr);
 
        BUG_ON(dir == DMA_NONE);
 
+       xen_dma_unmap_page(hwdev, paddr, size, dir, attrs);
+
        /* NOTE: We use dev_addr here, not paddr! */
        if (is_xen_swiotlb_buffer(dev_addr)) {
                swiotlb_tbl_unmap_single(hwdev, paddr, size, dir);
@@ -412,7 +453,7 @@ void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
                            size_t size, enum dma_data_direction dir,
                            struct dma_attrs *attrs)
 {
-       xen_unmap_single(hwdev, dev_addr, size, dir);
+       xen_unmap_single(hwdev, dev_addr, size, dir, attrs);
 }
 EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_page);
 
@@ -435,11 +476,15 @@ xen_swiotlb_sync_single(struct device *hwdev, dma_addr_t dev_addr,
 
        BUG_ON(dir == DMA_NONE);
 
+       if (target == SYNC_FOR_CPU)
+               xen_dma_sync_single_for_cpu(hwdev, paddr, size, dir);
+
        /* NOTE: We use dev_addr here, not paddr! */
-       if (is_xen_swiotlb_buffer(dev_addr)) {
+       if (is_xen_swiotlb_buffer(dev_addr))
                swiotlb_tbl_sync_single(hwdev, paddr, size, dir, target);
-               return;
-       }
+
+       if (target == SYNC_FOR_DEVICE)
+               xen_dma_sync_single_for_cpu(hwdev, paddr, size, dir);
 
        if (dir != DMA_FROM_DEVICE)
                return;
@@ -502,16 +547,26 @@ xen_swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
                                                                 sg->length,
                                                                 dir);
                        if (map == SWIOTLB_MAP_ERROR) {
+                               dev_warn(hwdev, "swiotlb buffer is full\n");
                                /* Don't panic here, we expect map_sg users
                                   to do proper error handling. */
                                xen_swiotlb_unmap_sg_attrs(hwdev, sgl, i, dir,
                                                           attrs);
                                sg_dma_len(sgl) = 0;
-                               return DMA_ERROR_CODE;
+                               return 0;
                        }
                        sg->dma_address = xen_phys_to_bus(map);
-               } else
+               } else {
+                       /* we are not interested in the dma_addr returned by
+                        * xen_dma_map_page, only in the potential cache flushes executed
+                        * by the function. */
+                       xen_dma_map_page(hwdev, pfn_to_page(paddr >> PAGE_SHIFT),
+                                               paddr & ~PAGE_MASK,
+                                               sg->length,
+                                               dir,
+                                               attrs);
                        sg->dma_address = dev_addr;
+               }
                sg_dma_len(sg) = sg->length;
        }
        return nelems;
@@ -533,7 +588,7 @@ xen_swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
        BUG_ON(dir == DMA_NONE);
 
        for_each_sg(sgl, sg, nelems, i)
-               xen_unmap_single(hwdev, sg->dma_address, sg_dma_len(sg), dir);
+               xen_unmap_single(hwdev, sg->dma_address, sg_dma_len(sg), dir, attrs);
 
 }
 EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_sg_attrs);
@@ -593,3 +648,15 @@ xen_swiotlb_dma_supported(struct device *hwdev, u64 mask)
        return xen_virt_to_bus(xen_io_tlb_end - 1) <= mask;
 }
 EXPORT_SYMBOL_GPL(xen_swiotlb_dma_supported);
+
+int
+xen_swiotlb_set_dma_mask(struct device *dev, u64 dma_mask)
+{
+       if (!dev->dma_mask || !xen_swiotlb_dma_supported(dev, dma_mask))
+               return -EIO;
+
+       *dev->dma_mask = dma_mask;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_set_dma_mask);
index f039b10..b03dd23 100644 (file)
 #include "fid.h"
 
 /**
- * v9fs_dentry_delete - called when dentry refcount equals 0
- * @dentry:  dentry in question
- *
- * By returning 1 here we should remove cacheing of unused
- * dentry components.
- *
- */
-
-static int v9fs_dentry_delete(const struct dentry *dentry)
-{
-       p9_debug(P9_DEBUG_VFS, " dentry: %s (%p)\n",
-                dentry->d_name.name, dentry);
-
-       return 1;
-}
-
-/**
  * v9fs_cached_dentry_delete - called when dentry refcount equals 0
  * @dentry:  dentry in question
  *
@@ -134,6 +117,6 @@ const struct dentry_operations v9fs_cached_dentry_operations = {
 };
 
 const struct dentry_operations v9fs_dentry_operations = {
-       .d_delete = v9fs_dentry_delete,
+       .d_delete = always_delete_dentry,
        .d_release = v9fs_dentry_release,
 };
index 823efcb..08159ed 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -80,6 +80,8 @@ struct kioctx {
        struct percpu_ref       users;
        atomic_t                dead;
 
+       struct percpu_ref       reqs;
+
        unsigned long           user_id;
 
        struct __percpu kioctx_cpu *cpu;
@@ -107,7 +109,6 @@ struct kioctx {
        struct page             **ring_pages;
        long                    nr_pages;
 
-       struct rcu_head         rcu_head;
        struct work_struct      free_work;
 
        struct {
@@ -250,8 +251,10 @@ static void aio_free_ring(struct kioctx *ctx)
 
        put_aio_ring_file(ctx);
 
-       if (ctx->ring_pages && ctx->ring_pages != ctx->internal_pages)
+       if (ctx->ring_pages && ctx->ring_pages != ctx->internal_pages) {
                kfree(ctx->ring_pages);
+               ctx->ring_pages = NULL;
+       }
 }
 
 static int aio_ring_mmap(struct file *file, struct vm_area_struct *vma)
@@ -463,26 +466,34 @@ static int kiocb_cancel(struct kioctx *ctx, struct kiocb *kiocb)
        return cancel(kiocb);
 }
 
-static void free_ioctx_rcu(struct rcu_head *head)
+static void free_ioctx(struct work_struct *work)
 {
-       struct kioctx *ctx = container_of(head, struct kioctx, rcu_head);
+       struct kioctx *ctx = container_of(work, struct kioctx, free_work);
 
+       pr_debug("freeing %p\n", ctx);
+
+       aio_free_ring(ctx);
        free_percpu(ctx->cpu);
        kmem_cache_free(kioctx_cachep, ctx);
 }
 
+static void free_ioctx_reqs(struct percpu_ref *ref)
+{
+       struct kioctx *ctx = container_of(ref, struct kioctx, reqs);
+
+       INIT_WORK(&ctx->free_work, free_ioctx);
+       schedule_work(&ctx->free_work);
+}
+
 /*
  * When this function runs, the kioctx has been removed from the "hash table"
  * and ctx->users has dropped to 0, so we know no more kiocbs can be submitted -
  * now it's safe to cancel any that need to be.
  */
-static void free_ioctx(struct work_struct *work)
+static void free_ioctx_users(struct percpu_ref *ref)
 {
-       struct kioctx *ctx = container_of(work, struct kioctx, free_work);
-       struct aio_ring *ring;
+       struct kioctx *ctx = container_of(ref, struct kioctx, users);
        struct kiocb *req;
-       unsigned cpu, avail;
-       DEFINE_WAIT(wait);
 
        spin_lock_irq(&ctx->ctx_lock);
 
@@ -496,54 +507,8 @@ static void free_ioctx(struct work_struct *work)
 
        spin_unlock_irq(&ctx->ctx_lock);
 
-       for_each_possible_cpu(cpu) {
-               struct kioctx_cpu *kcpu = per_cpu_ptr(ctx->cpu, cpu);
-
-               atomic_add(kcpu->reqs_available, &ctx->reqs_available);
-               kcpu->reqs_available = 0;
-       }
-
-       while (1) {
-               prepare_to_wait(&ctx->wait, &wait, TASK_UNINTERRUPTIBLE);
-
-               ring = kmap_atomic(ctx->ring_pages[0]);
-               avail = (ring->head <= ring->tail)
-                        ? ring->tail - ring->head
-                        : ctx->nr_events - ring->head + ring->tail;
-
-               atomic_add(avail, &ctx->reqs_available);
-               ring->head = ring->tail;
-               kunmap_atomic(ring);
-
-               if (atomic_read(&ctx->reqs_available) >= ctx->nr_events - 1)
-                       break;
-
-               schedule();
-       }
-       finish_wait(&ctx->wait, &wait);
-
-       WARN_ON(atomic_read(&ctx->reqs_available) > ctx->nr_events - 1);
-
-       aio_free_ring(ctx);
-
-       pr_debug("freeing %p\n", ctx);
-
-       /*
-        * Here the call_rcu() is between the wait_event() for reqs_active to
-        * hit 0, and freeing the ioctx.
-        *
-        * aio_complete() decrements reqs_active, but it has to touch the ioctx
-        * after to issue a wakeup so we use rcu.
-        */
-       call_rcu(&ctx->rcu_head, free_ioctx_rcu);
-}
-
-static void free_ioctx_ref(struct percpu_ref *ref)
-{
-       struct kioctx *ctx = container_of(ref, struct kioctx, users);
-
-       INIT_WORK(&ctx->free_work, free_ioctx);
-       schedule_work(&ctx->free_work);
+       percpu_ref_kill(&ctx->reqs);
+       percpu_ref_put(&ctx->reqs);
 }
 
 static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm)
@@ -602,6 +567,16 @@ static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm)
        }
 }
 
+static void aio_nr_sub(unsigned nr)
+{
+       spin_lock(&aio_nr_lock);
+       if (WARN_ON(aio_nr - nr > aio_nr))
+               aio_nr = 0;
+       else
+               aio_nr -= nr;
+       spin_unlock(&aio_nr_lock);
+}
+
 /* ioctx_alloc
  *     Allocates and initializes an ioctx.  Returns an ERR_PTR if it failed.
  */
@@ -639,8 +614,11 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
 
        ctx->max_reqs = nr_events;
 
-       if (percpu_ref_init(&ctx->users, free_ioctx_ref))
-               goto out_freectx;
+       if (percpu_ref_init(&ctx->users, free_ioctx_users))
+               goto err;
+
+       if (percpu_ref_init(&ctx->reqs, free_ioctx_reqs))
+               goto err;
 
        spin_lock_init(&ctx->ctx_lock);
        spin_lock_init(&ctx->completion_lock);
@@ -651,10 +629,10 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
 
        ctx->cpu = alloc_percpu(struct kioctx_cpu);
        if (!ctx->cpu)
-               goto out_freeref;
+               goto err;
 
        if (aio_setup_ring(ctx) < 0)
-               goto out_freepcpu;
+               goto err;
 
        atomic_set(&ctx->reqs_available, ctx->nr_events - 1);
        ctx->req_batch = (ctx->nr_events - 1) / (num_possible_cpus() * 4);
@@ -666,7 +644,8 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
        if (aio_nr + nr_events > (aio_max_nr * 2UL) ||
            aio_nr + nr_events < aio_nr) {
                spin_unlock(&aio_nr_lock);
-               goto out_cleanup;
+               err = -EAGAIN;
+               goto err;
        }
        aio_nr += ctx->max_reqs;
        spin_unlock(&aio_nr_lock);
@@ -675,23 +654,18 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
 
        err = ioctx_add_table(ctx, mm);
        if (err)
-               goto out_cleanup_put;
+               goto err_cleanup;
 
        pr_debug("allocated ioctx %p[%ld]: mm=%p mask=0x%x\n",
                 ctx, ctx->user_id, mm, ctx->nr_events);
        return ctx;
 
-out_cleanup_put:
-       percpu_ref_put(&ctx->users);
-out_cleanup:
-       err = -EAGAIN;
-       aio_free_ring(ctx);
-out_freepcpu:
+err_cleanup:
+       aio_nr_sub(ctx->max_reqs);
+err:
        free_percpu(ctx->cpu);
-out_freeref:
+       free_percpu(ctx->reqs.pcpu_count);
        free_percpu(ctx->users.pcpu_count);
-out_freectx:
-       put_aio_ring_file(ctx);
        kmem_cache_free(kioctx_cachep, ctx);
        pr_debug("error allocating ioctx %d\n", err);
        return ERR_PTR(err);
@@ -726,10 +700,7 @@ static void kill_ioctx(struct mm_struct *mm, struct kioctx *ctx)
                 * -EAGAIN with no ioctxs actually in use (as far as userspace
                 *  could tell).
                 */
-               spin_lock(&aio_nr_lock);
-               BUG_ON(aio_nr - ctx->max_reqs > aio_nr);
-               aio_nr -= ctx->max_reqs;
-               spin_unlock(&aio_nr_lock);
+               aio_nr_sub(ctx->max_reqs);
 
                if (ctx->mmap_size)
                        vm_munmap(ctx->mmap_base, ctx->mmap_size);
@@ -861,6 +832,8 @@ static inline struct kiocb *aio_get_req(struct kioctx *ctx)
        if (unlikely(!req))
                goto out_put;
 
+       percpu_ref_get(&ctx->reqs);
+
        req->ki_ctx = ctx;
        return req;
 out_put:
@@ -930,12 +903,6 @@ void aio_complete(struct kiocb *iocb, long res, long res2)
                return;
        }
 
-       /*
-        * Take rcu_read_lock() in case the kioctx is being destroyed, as we
-        * need to issue a wakeup after incrementing reqs_available.
-        */
-       rcu_read_lock();
-
        if (iocb->ki_list.next) {
                unsigned long flags;
 
@@ -1010,7 +977,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2)
        if (waitqueue_active(&ctx->wait))
                wake_up(&ctx->wait);
 
-       rcu_read_unlock();
+       percpu_ref_put(&ctx->reqs);
 }
 EXPORT_SYMBOL(aio_complete);
 
@@ -1421,6 +1388,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
        return 0;
 out_put_req:
        put_reqs_available(ctx, 1);
+       percpu_ref_put(&ctx->reqs);
        kiocb_free(req);
        return ret;
 }
index 2bdb4e2..33d79a4 100644 (file)
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -601,7 +601,7 @@ EXPORT_SYMBOL(bio_get_nr_vecs);
 
 static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page
                          *page, unsigned int len, unsigned int offset,
-                         unsigned short max_sectors)
+                         unsigned int max_sectors)
 {
        int retried_segments = 0;
        struct bio_vec *bvec;
index 398cbd5..aa976ec 100644 (file)
@@ -9,12 +9,17 @@ config BTRFS_FS
        select XOR_BLOCKS
 
        help
-         Btrfs is a new filesystem with extents, writable snapshotting,
-         support for multiple devices and many more features.
+         Btrfs is a general purpose copy-on-write filesystem with extents,
+         writable snapshotting, support for multiple devices and many more
+         features focused on fault tolerance, repair and easy administration.
 
-         Btrfs is highly experimental, and THE DISK FORMAT IS NOT YET
-         FINALIZED.  You should say N here unless you are interested in
-         testing Btrfs with non-critical data.
+         The filesystem disk format is no longer unstable, and it's not
+         expected to change unless there are strong reasons to do so. If there
+         is a format change, file systems with a unchanged format will
+         continue to be mountable and usable by newer kernels.
+
+         For more information, please see the web pages at
+         http://btrfs.wiki.kernel.org.
 
          To compile this file system support as a module, choose M here. The
          module will be called btrfs.
@@ -59,7 +64,8 @@ config BTRFS_FS_RUN_SANITY_TESTS
        help
          This will run some basic sanity tests on the free space cache
          code to make sure it is acting as it should.  These are mostly
-         regression tests and are only really interesting to btrfs devlopers.
+         regression tests and are only really interesting to btrfs
+         developers.
 
          If unsure, say N.
 
index a91a6a3..1a44e42 100644 (file)
@@ -14,4 +14,6 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
 btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
 btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
 
-btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o
+btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \
+       tests/extent-buffer-tests.o tests/btrfs-tests.o \
+       tests/extent-io-tests.o tests/inode-tests.o
index e15d2b0..0890c83 100644 (file)
@@ -229,7 +229,7 @@ int btrfs_init_acl(struct btrfs_trans_handle *trans,
                if (ret > 0) {
                        /* we need an acl */
                        ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_ACCESS);
-               } else {
+               } else if (ret < 0) {
                        cache_no_acl(inode);
                }
        } else {
index 08cc08f..c1e0b0c 100644 (file)
@@ -262,7 +262,7 @@ static struct btrfs_work *get_next_work(struct btrfs_worker_thread *worker,
        struct btrfs_work *work = NULL;
        struct list_head *cur = NULL;
 
-       if(!list_empty(prio_head))
+       if (!list_empty(prio_head))
                cur = prio_head->next;
 
        smp_mb();
@@ -495,6 +495,7 @@ static int __btrfs_start_workers(struct btrfs_workers *workers)
        spin_lock_irq(&workers->lock);
        if (workers->stopping) {
                spin_unlock_irq(&workers->lock);
+               ret = -EINVAL;
                goto fail_kthread;
        }
        list_add_tail(&worker->worker_list, &workers->idle_list);
index 0552a59..3775947 100644 (file)
@@ -185,6 +185,9 @@ static int __add_prelim_ref(struct list_head *head, u64 root_id,
 {
        struct __prelim_ref *ref;
 
+       if (root_id == BTRFS_DATA_RELOC_TREE_OBJECTID)
+               return 0;
+
        ref = kmem_cache_alloc(btrfs_prelim_ref_cache, gfp_mask);
        if (!ref)
                return -ENOMEM;
@@ -323,8 +326,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
 
        eb = path->nodes[level];
        while (!eb) {
-               if (!level) {
-                       WARN_ON(1);
+               if (WARN_ON(!level)) {
                        ret = 1;
                        goto out;
                }
@@ -1619,7 +1621,7 @@ static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root,
                btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
                btrfs_release_path(path);
 
-               item = btrfs_item_nr(eb, slot);
+               item = btrfs_item_nr(slot);
                iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref);
 
                for (cur = 0; cur < btrfs_item_size(eb, item); cur += len) {
index 71f074e..ac0b39d 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef __BTRFS_I__
 #define __BTRFS_I__
 
+#include <linux/hash.h>
 #include "extent_map.h"
 #include "extent_io.h"
 #include "ordered-data.h"
@@ -179,6 +180,25 @@ static inline struct btrfs_inode *BTRFS_I(struct inode *inode)
        return container_of(inode, struct btrfs_inode, vfs_inode);
 }
 
+static inline unsigned long btrfs_inode_hash(u64 objectid,
+                                            const struct btrfs_root *root)
+{
+       u64 h = objectid ^ (root->objectid * GOLDEN_RATIO_PRIME);
+
+#if BITS_PER_LONG == 32
+       h = (h >> 32) ^ (h & 0xffffffff);
+#endif
+
+       return (unsigned long)h;
+}
+
+static inline void btrfs_insert_inode_hash(struct inode *inode)
+{
+       unsigned long h = btrfs_inode_hash(inode->i_ino, BTRFS_I(inode)->root);
+
+       __insert_inode_hash(inode, h);
+}
+
 static inline u64 btrfs_ino(struct inode *inode)
 {
        u64 ino = BTRFS_I(inode)->location.objectid;
index 1c47be1..b50764b 100644 (file)
  * the integrity of (super)-block write requests, do not
  * enable the config option BTRFS_FS_CHECK_INTEGRITY to
  * include and compile the integrity check tool.
+ *
+ * Expect millions of lines of information in the kernel log with an
+ * enabled check_int_print_mask. Therefore set LOG_BUF_SHIFT in the
+ * kernel config to at least 26 (which is 64MB). Usually the value is
+ * limited to 21 (which is 2MB) in init/Kconfig. The file needs to be
+ * changed like this before LOG_BUF_SHIFT can be set to a high value:
+ * config LOG_BUF_SHIFT
+ *       int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
+ *       range 12 30
  */
 
 #include <linux/sched.h>
 #define BTRFSIC_PRINT_MASK_INITIAL_DATABASE                    0x00000400
 #define BTRFSIC_PRINT_MASK_NUM_COPIES                          0x00000800
 #define BTRFSIC_PRINT_MASK_TREE_WITH_ALL_MIRRORS               0x00001000
+#define BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH_VERBOSE               0x00002000
 
 struct btrfsic_dev_state;
 struct btrfsic_state;
@@ -1038,7 +1048,7 @@ leaf_item_out_of_bounce_error:
                                                     disk_item_offset,
                                                     sizeof(struct btrfs_item));
                        item_offset = btrfs_stack_item_offset(&disk_item);
-                       item_size = btrfs_stack_item_offset(&disk_item);
+                       item_size = btrfs_stack_item_size(&disk_item);
                        disk_key = &disk_item.key;
                        type = btrfs_disk_key_type(disk_key);
 
@@ -1900,7 +1910,9 @@ again:
                                                               dev_state,
                                                               dev_bytenr);
                        }
-                       if (block->logical_bytenr != bytenr) {
+                       if (block->logical_bytenr != bytenr &&
+                           !(!block->is_metadata &&
+                             block->logical_bytenr == 0))
                                printk(KERN_INFO
                                       "Written block @%llu (%s/%llu/%d)"
                                       " found in hash table, %c,"
@@ -1910,15 +1922,14 @@ again:
                                       block->mirror_num,
                                       btrfsic_get_block_type(state, block),
                                       block->logical_bytenr);
-                               block->logical_bytenr = bytenr;
-                       } else if (state->print_mask &
-                                  BTRFSIC_PRINT_MASK_VERBOSE)
+                       else if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE)
                                printk(KERN_INFO
                                       "Written block @%llu (%s/%llu/%d)"
                                       " found in hash table, %c.\n",
                                       bytenr, dev_state->name, dev_bytenr,
                                       block->mirror_num,
                                       btrfsic_get_block_type(state, block));
+                       block->logical_bytenr = bytenr;
                } else {
                        if (num_pages * PAGE_CACHE_SIZE <
                            state->datablock_size) {
@@ -2463,10 +2474,8 @@ static int btrfsic_process_written_superblock(
                }
        }
 
-       if (-1 == btrfsic_check_all_ref_blocks(state, superblock, 0)) {
-               WARN_ON(1);
+       if (WARN_ON(-1 == btrfsic_check_all_ref_blocks(state, superblock, 0)))
                btrfsic_dump_tree(state);
-       }
 
        return 0;
 }
@@ -2906,7 +2915,7 @@ static void btrfsic_cmp_log_and_dev_bytenr(struct btrfsic_state *state,
                btrfsic_release_block_ctx(&block_ctx);
        }
 
-       if (!match) {
+       if (WARN_ON(!match)) {
                printk(KERN_INFO "btrfs: attempt to write M-block which contains logical bytenr that doesn't map to dev+physical bytenr of submit_bio,"
                       " buffer->log_bytenr=%llu, submit_bio(bdev=%s,"
                       " phys_bytenr=%llu)!\n",
@@ -2923,7 +2932,6 @@ static void btrfsic_cmp_log_and_dev_bytenr(struct btrfsic_state *state,
                               bytenr, block_ctx.dev->name,
                               block_ctx.dev_bytenr, mirror_num);
                }
-               WARN_ON(1);
        }
 }
 
@@ -3017,6 +3025,7 @@ void btrfsic_submit_bio(int rw, struct bio *bio)
            (rw & WRITE) && NULL != bio->bi_io_vec) {
                unsigned int i;
                u64 dev_bytenr;
+               u64 cur_bytenr;
                int bio_is_patched;
                char **mapped_datav;
 
@@ -3035,6 +3044,7 @@ void btrfsic_submit_bio(int rw, struct bio *bio)
                                       GFP_NOFS);
                if (!mapped_datav)
                        goto leave;
+               cur_bytenr = dev_bytenr;
                for (i = 0; i < bio->bi_vcnt; i++) {
                        BUG_ON(bio->bi_io_vec[i].bv_len != PAGE_CACHE_SIZE);
                        mapped_datav[i] = kmap(bio->bi_io_vec[i].bv_page);
@@ -3046,16 +3056,13 @@ void btrfsic_submit_bio(int rw, struct bio *bio)
                                kfree(mapped_datav);
                                goto leave;
                        }
-                       if ((BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH |
-                            BTRFSIC_PRINT_MASK_VERBOSE) ==
-                           (dev_state->state->print_mask &
-                            (BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH |
-                             BTRFSIC_PRINT_MASK_VERBOSE)))
+                       if (dev_state->state->print_mask &
+                           BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH_VERBOSE)
                                printk(KERN_INFO
-                                      "#%u: page=%p, len=%u, offset=%u\n",
-                                      i, bio->bi_io_vec[i].bv_page,
-                                      bio->bi_io_vec[i].bv_len,
+                                      "#%u: bytenr=%llu, len=%u, offset=%u\n",
+                                      i, cur_bytenr, bio->bi_io_vec[i].bv_len,
                                       bio->bi_io_vec[i].bv_offset);
+                       cur_bytenr += bio->bi_io_vec[i].bv_len;
                }
                btrfsic_process_written_block(dev_state, dev_bytenr,
                                              mapped_datav, bio->bi_vcnt,
diff --git a/fs/btrfs/compat.h b/fs/btrfs/compat.h
deleted file mode 100644 (file)
index 7c4503e..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _COMPAT_H_
-#define _COMPAT_H_
-
-#define btrfs_drop_nlink(inode) drop_nlink(inode)
-#define btrfs_inc_nlink(inode) inc_nlink(inode)
-
-#endif /* _COMPAT_H_ */
index 6aad98c..1499b27 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/writeback.h>
 #include <linux/bit_spinlock.h>
 #include <linux/slab.h>
-#include "compat.h"
 #include "ctree.h"
 #include "disk-io.h"
 #include "transaction.h"
@@ -360,7 +359,7 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
        bdev = BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev;
 
        bio = compressed_bio_alloc(bdev, first_byte, GFP_NOFS);
-       if(!bio) {
+       if (!bio) {
                kfree(cb);
                return -ENOMEM;
        }
index 61b5bcd..316136b 100644 (file)
@@ -274,7 +274,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
        else
                btrfs_set_header_owner(cow, new_root_objectid);
 
-       write_extent_buffer(cow, root->fs_info->fsid, btrfs_header_fsid(cow),
+       write_extent_buffer(cow, root->fs_info->fsid, btrfs_header_fsid(),
                            BTRFS_FSID_SIZE);
 
        WARN_ON(btrfs_header_generation(buf) > trans->transid);
@@ -996,7 +996,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
        else
                btrfs_set_header_owner(cow, root->root_key.objectid);
 
-       write_extent_buffer(cow, root->fs_info->fsid, btrfs_header_fsid(cow),
+       write_extent_buffer(cow, root->fs_info->fsid, btrfs_header_fsid(),
                            BTRFS_FSID_SIZE);
 
        ret = update_ref_for_cow(trans, root, buf, cow, &last_ref);
@@ -1285,11 +1285,10 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
                free_extent_buffer(eb_root);
                blocksize = btrfs_level_size(root, old_root->level);
                old = read_tree_block(root, logical, blocksize, 0);
-               if (!old || !extent_buffer_uptodate(old)) {
+               if (WARN_ON(!old || !extent_buffer_uptodate(old))) {
                        free_extent_buffer(old);
                        pr_warn("btrfs: failed to read tree block %llu from get_old_root\n",
                                logical);
-                       WARN_ON(1);
                } else {
                        eb = btrfs_clone_extent_buffer(old);
                        free_extent_buffer(old);
@@ -2758,7 +2757,7 @@ int btrfs_search_old_slot(struct btrfs_root *root, struct btrfs_key *key,
        int level;
        int lowest_unlock = 1;
        u8 lowest_level = 0;
-       int prev_cmp;
+       int prev_cmp = -1;
 
        lowest_level = p->lowest_level;
        WARN_ON(p->nodes[0] != NULL);
@@ -2769,7 +2768,6 @@ int btrfs_search_old_slot(struct btrfs_root *root, struct btrfs_key *key,
        }
 
 again:
-       prev_cmp = -1;
        b = get_old_root(root, time_seq);
        level = btrfs_header_level(b);
        p->locks[level] = BTRFS_READ_LOCK;
@@ -2787,6 +2785,11 @@ again:
                 */
                btrfs_unlock_up_safe(p, level + 1);
 
+               /*
+                * Since we can unwind eb's we want to do a real search every
+                * time.
+                */
+               prev_cmp = -1;
                ret = key_search(b, key, level, &prev_cmp, &slot);
 
                if (level != 0) {
@@ -3148,7 +3151,7 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
        btrfs_set_header_backref_rev(c, BTRFS_MIXED_BACKREF_REV);
        btrfs_set_header_owner(c, root->root_key.objectid);
 
-       write_extent_buffer(c, root->fs_info->fsid, btrfs_header_fsid(c),
+       write_extent_buffer(c, root->fs_info->fsid, btrfs_header_fsid(),
                            BTRFS_FSID_SIZE);
 
        write_extent_buffer(c, root->fs_info->chunk_tree_uuid,
@@ -3287,7 +3290,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
        btrfs_set_header_backref_rev(split, BTRFS_MIXED_BACKREF_REV);
        btrfs_set_header_owner(split, root->root_key.objectid);
        write_extent_buffer(split, root->fs_info->fsid,
-                           btrfs_header_fsid(split), BTRFS_FSID_SIZE);
+                           btrfs_header_fsid(), BTRFS_FSID_SIZE);
        write_extent_buffer(split, root->fs_info->chunk_tree_uuid,
                            btrfs_header_chunk_tree_uuid(split),
                            BTRFS_UUID_SIZE);
@@ -3337,8 +3340,8 @@ static int leaf_space_used(struct extent_buffer *l, int start, int nr)
        if (!nr)
                return 0;
        btrfs_init_map_token(&token);
-       start_item = btrfs_item_nr(l, start);
-       end_item = btrfs_item_nr(l, end);
+       start_item = btrfs_item_nr(start);
+       end_item = btrfs_item_nr(end);
        data_len = btrfs_token_item_offset(l, start_item, &token) +
                btrfs_token_item_size(l, start_item, &token);
        data_len = data_len - btrfs_token_item_offset(l, end_item, &token);
@@ -3406,7 +3409,7 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
        slot = path->slots[1];
        i = left_nritems - 1;
        while (i >= nr) {
-               item = btrfs_item_nr(left, i);
+               item = btrfs_item_nr(i);
 
                if (!empty && push_items > 0) {
                        if (path->slots[0] > i)
@@ -3470,7 +3473,7 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
        btrfs_set_header_nritems(right, right_nritems);
        push_space = BTRFS_LEAF_DATA_SIZE(root);
        for (i = 0; i < right_nritems; i++) {
-               item = btrfs_item_nr(right, i);
+               item = btrfs_item_nr(i);
                push_space -= btrfs_token_item_size(right, item, &token);
                btrfs_set_token_item_offset(right, item, push_space, &token);
        }
@@ -3612,7 +3615,7 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
                nr = min(right_nritems - 1, max_slot);
 
        for (i = 0; i < nr; i++) {
-               item = btrfs_item_nr(right, i);
+               item = btrfs_item_nr(i);
 
                if (!empty && push_items > 0) {
                        if (path->slots[0] < i)
@@ -3639,8 +3642,7 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
                ret = 1;
                goto out;
        }
-       if (!empty && push_items == btrfs_header_nritems(right))
-               WARN_ON(1);
+       WARN_ON(!empty && push_items == btrfs_header_nritems(right));
 
        /* push data from right to left */
        copy_extent_buffer(left, right,
@@ -3663,7 +3665,7 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
        for (i = old_left_nritems; i < old_left_nritems + push_items; i++) {
                u32 ioff;
 
-               item = btrfs_item_nr(left, i);
+               item = btrfs_item_nr(i);
 
                ioff = btrfs_token_item_offset(left, item, &token);
                btrfs_set_token_item_offset(left, item,
@@ -3694,7 +3696,7 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
        btrfs_set_header_nritems(right, right_nritems);
        push_space = BTRFS_LEAF_DATA_SIZE(root);
        for (i = 0; i < right_nritems; i++) {
-               item = btrfs_item_nr(right, i);
+               item = btrfs_item_nr(i);
 
                push_space = push_space - btrfs_token_item_size(right,
                                                                item, &token);
@@ -3835,7 +3837,7 @@ static noinline void copy_for_split(struct btrfs_trans_handle *trans,
                      btrfs_item_end_nr(l, mid);
 
        for (i = 0; i < nritems; i++) {
-               struct btrfs_item *item = btrfs_item_nr(right, i);
+               struct btrfs_item *item = btrfs_item_nr(i);
                u32 ioff;
 
                ioff = btrfs_token_item_offset(right, item, &token);
@@ -4016,7 +4018,7 @@ again:
                                    data_size > BTRFS_LEAF_DATA_SIZE(root)) {
                                        if (data_size && !tried_avoid_double)
                                                goto push_for_double;
-                                       split = 2 ;
+                                       split = 2;
                                }
                        }
                }
@@ -4042,7 +4044,7 @@ again:
        btrfs_set_header_owner(right, root->root_key.objectid);
        btrfs_set_header_level(right, 0);
        write_extent_buffer(right, root->fs_info->fsid,
-                           btrfs_header_fsid(right), BTRFS_FSID_SIZE);
+                           btrfs_header_fsid(), BTRFS_FSID_SIZE);
 
        write_extent_buffer(right, root->fs_info->chunk_tree_uuid,
                            btrfs_header_chunk_tree_uuid(right),
@@ -4177,7 +4179,7 @@ static noinline int split_item(struct btrfs_trans_handle *trans,
 
        btrfs_set_path_blocking(path);
 
-       item = btrfs_item_nr(leaf, path->slots[0]);
+       item = btrfs_item_nr(path->slots[0]);
        orig_offset = btrfs_item_offset(leaf, item);
        item_size = btrfs_item_size(leaf, item);
 
@@ -4200,7 +4202,7 @@ static noinline int split_item(struct btrfs_trans_handle *trans,
        btrfs_cpu_key_to_disk(&disk_key, new_key);
        btrfs_set_item_key(leaf, &disk_key, slot);
 
-       new_item = btrfs_item_nr(leaf, slot);
+       new_item = btrfs_item_nr(slot);
 
        btrfs_set_item_offset(leaf, new_item, orig_offset);
        btrfs_set_item_size(leaf, new_item, item_size - split_offset);
@@ -4339,7 +4341,7 @@ void btrfs_truncate_item(struct btrfs_root *root, struct btrfs_path *path,
        /* first correct the data pointers */
        for (i = slot; i < nritems; i++) {
                u32 ioff;
-               item = btrfs_item_nr(leaf, i);
+               item = btrfs_item_nr(i);
 
                ioff = btrfs_token_item_offset(leaf, item, &token);
                btrfs_set_token_item_offset(leaf, item,
@@ -4387,7 +4389,7 @@ void btrfs_truncate_item(struct btrfs_root *root, struct btrfs_path *path,
                        fixup_low_keys(root, path, &disk_key, 1);
        }
 
-       item = btrfs_item_nr(leaf, slot);
+       item = btrfs_item_nr(slot);
        btrfs_set_item_size(leaf, item, new_size);
        btrfs_mark_buffer_dirty(leaf);
 
@@ -4441,7 +4443,7 @@ void btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path,
        /* first correct the data pointers */
        for (i = slot; i < nritems; i++) {
                u32 ioff;
-               item = btrfs_item_nr(leaf, i);
+               item = btrfs_item_nr(i);
 
                ioff = btrfs_token_item_offset(leaf, item, &token);
                btrfs_set_token_item_offset(leaf, item,
@@ -4455,7 +4457,7 @@ void btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path,
 
        data_end = old_data;
        old_size = btrfs_item_size_nr(leaf, slot);
-       item = btrfs_item_nr(leaf, slot);
+       item = btrfs_item_nr(slot);
        btrfs_set_item_size(leaf, item, old_size + data_size);
        btrfs_mark_buffer_dirty(leaf);
 
@@ -4514,7 +4516,7 @@ void setup_items_for_insert(struct btrfs_root *root, struct btrfs_path *path,
                for (i = slot; i < nritems; i++) {
                        u32 ioff;
 
-                       item = btrfs_item_nr(leaf, i);
+                       item = btrfs_item_nr( i);
                        ioff = btrfs_token_item_offset(leaf, item, &token);
                        btrfs_set_token_item_offset(leaf, item,
                                                    ioff - total_data, &token);
@@ -4535,7 +4537,7 @@ void setup_items_for_insert(struct btrfs_root *root, struct btrfs_path *path,
        for (i = 0; i < nr; i++) {
                btrfs_cpu_key_to_disk(&disk_key, cpu_key + i);
                btrfs_set_item_key(leaf, &disk_key, slot + i);
-               item = btrfs_item_nr(leaf, slot + i);
+               item = btrfs_item_nr(slot + i);
                btrfs_set_token_item_offset(leaf, item,
                                            data_end - data_size[i], &token);
                data_end -= data_size[i];
@@ -4730,7 +4732,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                for (i = slot + nr; i < nritems; i++) {
                        u32 ioff;
 
-                       item = btrfs_item_nr(leaf, i);
+                       item = btrfs_item_nr(i);
                        ioff = btrfs_token_item_offset(leaf, item, &token);
                        btrfs_set_token_item_offset(leaf, item,
                                                    ioff + dsize, &token);
@@ -4823,14 +4825,18 @@ static int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
 
        btrfs_item_key_to_cpu(path->nodes[0], &key, 0);
 
-       if (key.offset > 0)
+       if (key.offset > 0) {
                key.offset--;
-       else if (key.type > 0)
+       } else if (key.type > 0) {
                key.type--;
-       else if (key.objectid > 0)
+               key.offset = (u64)-1;
+       } else if (key.objectid > 0) {
                key.objectid--;
-       else
+               key.type = (u8)-1;
+               key.offset = (u64)-1;
+       } else {
                return 1;
+       }
 
        btrfs_release_path(path);
        ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
@@ -4866,7 +4872,6 @@ static int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
  * was nothing in the tree that matched the search criteria.
  */
 int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
-                        struct btrfs_key *max_key,
                         struct btrfs_path *path,
                         u64 min_trans)
 {
@@ -4911,10 +4916,8 @@ again:
                 * If it is too old, old, skip to the next one.
                 */
                while (slot < nritems) {
-                       u64 blockptr;
                        u64 gen;
 
-                       blockptr = btrfs_node_blockptr(cur, slot);
                        gen = btrfs_node_ptr_generation(cur, slot);
                        if (gen < min_trans) {
                                slot++;
index 0506f40..54ab861 100644 (file)
@@ -47,6 +47,12 @@ extern struct kmem_cache *btrfs_path_cachep;
 extern struct kmem_cache *btrfs_free_space_cachep;
 struct btrfs_ordered_sum;
 
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+#define STATIC noinline
+#else
+#define STATIC static noinline
+#endif
+
 #define BTRFS_MAGIC 0x4D5F53665248425FULL /* ascii _BHRfS_M, no null */
 
 #define BTRFS_MAX_MIRRORS 3
@@ -1580,7 +1586,6 @@ struct btrfs_fs_info {
        atomic_t scrubs_paused;
        atomic_t scrub_cancel_req;
        wait_queue_head_t scrub_pause_wait;
-       struct rw_semaphore scrub_super_lock;
        int scrub_workers_refcnt;
        struct btrfs_workers scrub_workers;
        struct btrfs_workers scrub_wr_completion_workers;
@@ -1724,7 +1729,9 @@ struct btrfs_root {
        int ref_cows;
        int track_dirty;
        int in_radix;
-
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+       int dummy_root;
+#endif
        u64 defrag_trans_start;
        struct btrfs_key defrag_progress;
        struct btrfs_key defrag_max;
@@ -2461,8 +2468,7 @@ static inline unsigned long btrfs_item_nr_offset(int nr)
                sizeof(struct btrfs_item) * nr;
 }
 
-static inline struct btrfs_item *btrfs_item_nr(struct extent_buffer *eb,
-                                              int nr)
+static inline struct btrfs_item *btrfs_item_nr(int nr)
 {
        return (struct btrfs_item *)btrfs_item_nr_offset(nr);
 }
@@ -2475,30 +2481,30 @@ static inline u32 btrfs_item_end(struct extent_buffer *eb,
 
 static inline u32 btrfs_item_end_nr(struct extent_buffer *eb, int nr)
 {
-       return btrfs_item_end(eb, btrfs_item_nr(eb, nr));
+       return btrfs_item_end(eb, btrfs_item_nr(nr));
 }
 
 static inline u32 btrfs_item_offset_nr(struct extent_buffer *eb, int nr)
 {
-       return btrfs_item_offset(eb, btrfs_item_nr(eb, nr));
+       return btrfs_item_offset(eb, btrfs_item_nr(nr));
 }
 
 static inline u32 btrfs_item_size_nr(struct extent_buffer *eb, int nr)
 {
-       return btrfs_item_size(eb, btrfs_item_nr(eb, nr));
+       return btrfs_item_size(eb, btrfs_item_nr(nr));
 }
 
 static inline void btrfs_item_key(struct extent_buffer *eb,
                           struct btrfs_disk_key *disk_key, int nr)
 {
-       struct btrfs_item *item = btrfs_item_nr(eb, nr);
+       struct btrfs_item *item = btrfs_item_nr(nr);
        read_eb_member(eb, item, struct btrfs_item, key, disk_key);
 }
 
 static inline void btrfs_set_item_key(struct extent_buffer *eb,
                               struct btrfs_disk_key *disk_key, int nr)
 {
-       struct btrfs_item *item = btrfs_item_nr(eb, nr);
+       struct btrfs_item *item = btrfs_item_nr(nr);
        write_eb_member(eb, item, struct btrfs_item, key, disk_key);
 }
 
@@ -2666,7 +2672,7 @@ static inline void btrfs_set_header_backref_rev(struct extent_buffer *eb,
        btrfs_set_header_flags(eb, flags);
 }
 
-static inline unsigned long btrfs_header_fsid(struct extent_buffer *eb)
+static inline unsigned long btrfs_header_fsid(void)
 {
        return offsetof(struct btrfs_header, fsid);
 }
@@ -3105,11 +3111,6 @@ static inline u32 btrfs_level_size(struct btrfs_root *root, int level)
        ((unsigned long)(btrfs_leaf_data(leaf) + \
        btrfs_item_offset_nr(leaf, slot)))
 
-static inline struct dentry *fdentry(struct file *file)
-{
-       return file->f_path.dentry;
-}
-
 static inline bool btrfs_mixed_space_info(struct btrfs_space_info *space_info)
 {
        return ((space_info->flags & BTRFS_BLOCK_GROUP_METADATA) &&
@@ -3308,7 +3309,6 @@ int btrfs_find_next_key(struct btrfs_root *root, struct btrfs_path *path,
                        struct btrfs_key *key, int lowest_level,
                        u64 min_trans);
 int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
-                        struct btrfs_key *max_key,
                         struct btrfs_path *path,
                         u64 min_trans);
 enum btrfs_compare_tree_result {
@@ -3613,9 +3613,6 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
                           struct btrfs_ordered_sum *sums);
 int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
                       struct bio *bio, u64 file_start, int contig);
-int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
-                       struct btrfs_root *root, struct btrfs_path *path,
-                       u64 isize);
 int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
                             struct list_head *list, int search_commit);
 /* inode.c */
@@ -3675,8 +3672,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
                               u32 min_type);
 
 int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput);
-int btrfs_start_all_delalloc_inodes(struct btrfs_fs_info *fs_info,
-                                   int delay_iput);
+int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput);
 int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
                              struct extent_state **cached_state);
 int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
@@ -3745,9 +3741,6 @@ void btrfs_cleanup_defrag_inodes(struct btrfs_fs_info *fs_info);
 int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync);
 void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
                             int skip_pinned);
-int btrfs_replace_extent_cache(struct inode *inode, struct extent_map *replace,
-                              u64 start, u64 end, int skip_pinned,
-                              int modified);
 extern const struct file_operations btrfs_file_operations;
 int __btrfs_drop_extents(struct btrfs_trans_handle *trans,
                         struct btrfs_root *root, struct inode *inode,
@@ -3944,9 +3937,7 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
                    u64 end, struct btrfs_scrub_progress *progress,
                    int readonly, int is_dev_replace);
 void btrfs_scrub_pause(struct btrfs_root *root);
-void btrfs_scrub_pause_super(struct btrfs_root *root);
 void btrfs_scrub_continue(struct btrfs_root *root);
-void btrfs_scrub_continue_super(struct btrfs_root *root);
 int btrfs_scrub_cancel(struct btrfs_fs_info *info);
 int btrfs_scrub_cancel_dev(struct btrfs_fs_info *info,
                           struct btrfs_device *dev);
@@ -4028,5 +4019,9 @@ static inline int btrfs_defrag_cancelled(struct btrfs_fs_info *fs_info)
        return signal_pending(current);
 }
 
+/* Sanity test specific functions */
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+void btrfs_test_destroy_inode(struct inode *inode);
+#endif
 
 #endif
index cbd9523..8d292fb 100644 (file)
@@ -108,8 +108,8 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(struct inode *inode)
                        return node;
                }
                btrfs_inode->delayed_node = node;
-               atomic_inc(&node->refs);        /* can be accessed */
-               atomic_inc(&node->refs);        /* cached in the inode */
+               /* can be accessed and cached in the inode */
+               atomic_add(2, &node->refs);
                spin_unlock(&root->inode_lock);
                return node;
        }
@@ -138,8 +138,8 @@ again:
                return ERR_PTR(-ENOMEM);
        btrfs_init_delayed_node(node, root, ino);
 
-       atomic_inc(&node->refs);        /* cached in the btrfs inode */
-       atomic_inc(&node->refs);        /* can be accessed */
+       /* cached in the btrfs inode and can be accessed */
+       atomic_add(2, &node->refs);
 
        ret = radix_tree_preload(GFP_NOFS & ~__GFP_HIGHMEM);
        if (ret) {
@@ -649,14 +649,13 @@ static int btrfs_delayed_inode_reserve_metadata(
                        goto out;
 
                ret = btrfs_block_rsv_migrate(src_rsv, dst_rsv, num_bytes);
-               if (!ret)
+               if (!WARN_ON(ret))
                        goto out;
 
                /*
                 * Ok this is a problem, let's just steal from the global rsv
                 * since this really shouldn't happen that often.
                 */
-               WARN_ON(1);
                ret = btrfs_block_rsv_migrate(&root->fs_info->global_block_rsv,
                                              dst_rsv, num_bytes);
                goto out;
@@ -771,13 +770,13 @@ static int btrfs_batch_insert_items(struct btrfs_root *root,
         */
        btrfs_set_path_blocking(path);
 
-       keys = kmalloc(sizeof(struct btrfs_key) * nitems, GFP_NOFS);
+       keys = kmalloc_array(nitems, sizeof(struct btrfs_key), GFP_NOFS);
        if (!keys) {
                ret = -ENOMEM;
                goto out;
        }
 
-       data_size = kmalloc(sizeof(u32) * nitems, GFP_NOFS);
+       data_size = kmalloc_array(nitems, sizeof(u32), GFP_NOFS);
        if (!data_size) {
                ret = -ENOMEM;
                goto error;
@@ -1174,8 +1173,10 @@ int btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
        mutex_unlock(&delayed_node->mutex);
 
        path = btrfs_alloc_path();
-       if (!path)
+       if (!path) {
+               btrfs_release_delayed_node(delayed_node);
                return -ENOMEM;
+       }
        path->leave_spinning = 1;
 
        block_rsv = trans->block_rsv;
index 9efb94e..2cfc3df 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/kthread.h>
 #include <linux/math64.h>
 #include <asm/div64.h>
-#include "compat.h"
 #include "ctree.h"
 #include "extent_map.h"
 #include "disk-io.h"
@@ -38,7 +37,6 @@
 #include "rcu-string.h"
 #include "dev-replace.h"
 
-static u64 btrfs_get_seconds_since_1970(void);
 static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
                                       int scrub_ret);
 static void btrfs_dev_replace_update_device_in_mapping_tree(
@@ -296,13 +294,6 @@ void btrfs_after_dev_replace_commit(struct btrfs_fs_info *fs_info)
                dev_replace->cursor_left_last_write_of_item;
 }
 
-static u64 btrfs_get_seconds_since_1970(void)
-{
-       struct timespec t = CURRENT_TIME_SEC;
-
-       return t.tv_sec;
-}
-
 int btrfs_dev_replace_start(struct btrfs_root *root,
                            struct btrfs_ioctl_dev_replace_args *args)
 {
@@ -375,7 +366,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
        dev_replace->tgtdev = tgt_device;
 
        printk_in_rcu(KERN_INFO
-                     "btrfs: dev_replace from %s (devid %llu) to %s) started\n",
+                     "btrfs: dev_replace from %s (devid %llu) to %s started\n",
                      src_device->missing ? "<missing disk>" :
                        rcu_str_deref(src_device->name),
                      src_device->devid,
@@ -390,7 +381,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
         * go to the tgtdev as well (refer to btrfs_map_block()).
         */
        dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED;
-       dev_replace->time_started = btrfs_get_seconds_since_1970();
+       dev_replace->time_started = get_seconds();
        dev_replace->cursor_left = 0;
        dev_replace->committed_cursor_left = 0;
        dev_replace->cursor_left_last_write_of_item = 0;
@@ -400,7 +391,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
        args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR;
        btrfs_dev_replace_unlock(dev_replace);
 
-       btrfs_wait_all_ordered_extents(root->fs_info);
+       btrfs_wait_ordered_roots(root->fs_info, -1);
 
        /* force writing the updated state information to disk */
        trans = btrfs_start_transaction(root, 0);
@@ -470,12 +461,12 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
         * flush all outstanding I/O and inode extent mappings before the
         * copy operation is declared as being finished
         */
-       ret = btrfs_start_all_delalloc_inodes(root->fs_info, 0);
+       ret = btrfs_start_delalloc_roots(root->fs_info, 0);
        if (ret) {
                mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
                return ret;
        }
-       btrfs_wait_all_ordered_extents(root->fs_info);
+       btrfs_wait_ordered_roots(root->fs_info, -1);
 
        trans = btrfs_start_transaction(root, 0);
        if (IS_ERR(trans)) {
@@ -493,7 +484,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
                          : BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED;
        dev_replace->tgtdev = NULL;
        dev_replace->srcdev = NULL;
-       dev_replace->time_stopped = btrfs_get_seconds_since_1970();
+       dev_replace->time_stopped = get_seconds();
        dev_replace->item_needs_writeback = 1;
 
        if (scrub_ret) {
@@ -650,6 +641,9 @@ static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info)
        u64 result;
        int ret;
 
+       if (fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
        mutex_lock(&dev_replace->lock_finishing_cancel_unmount);
        btrfs_dev_replace_lock(dev_replace);
        switch (dev_replace->replace_state) {
@@ -668,7 +662,7 @@ static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info)
                break;
        }
        dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED;
-       dev_replace->time_stopped = btrfs_get_seconds_since_1970();
+       dev_replace->time_stopped = get_seconds();
        dev_replace->item_needs_writeback = 1;
        btrfs_dev_replace_unlock(dev_replace);
        btrfs_scrub_cancel(fs_info);
@@ -703,7 +697,7 @@ void btrfs_dev_replace_suspend_for_unmount(struct btrfs_fs_info *fs_info)
        case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
                dev_replace->replace_state =
                        BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED;
-               dev_replace->time_stopped = btrfs_get_seconds_since_1970();
+               dev_replace->time_stopped = get_seconds();
                dev_replace->item_needs_writeback = 1;
                pr_info("btrfs: suspending dev_replace for unmount\n");
                break;
index 79e594e..c031ea3 100644 (file)
@@ -58,7 +58,7 @@ static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
                return ERR_PTR(ret);
        WARN_ON(ret > 0);
        leaf = path->nodes[0];
-       item = btrfs_item_nr(leaf, path->slots[0]);
+       item = btrfs_item_nr(path->slots[0]);
        ptr = btrfs_item_ptr(leaf, path->slots[0], char);
        BUG_ON(data_size > btrfs_item_size(leaf, item));
        ptr += btrfs_item_size(leaf, item) - data_size;
@@ -474,8 +474,10 @@ int verify_dir_item(struct btrfs_root *root,
        }
 
        /* BTRFS_MAX_XATTR_SIZE is the same for all dir items */
-       if (btrfs_dir_data_len(leaf, dir_item) > BTRFS_MAX_XATTR_SIZE(root)) {
-               printk(KERN_CRIT "btrfs: invalid dir item data len: %u\n",
+       if ((btrfs_dir_data_len(leaf, dir_item) +
+            btrfs_dir_name_len(leaf, dir_item)) > BTRFS_MAX_XATTR_SIZE(root)) {
+               printk(KERN_CRIT "btrfs: invalid dir item name + data len: %u + %u\n",
+                      (unsigned)btrfs_dir_name_len(leaf, dir_item),
                       (unsigned)btrfs_dir_data_len(leaf, dir_item));
                return 1;
        }
index 62176ad..8072cfa 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/uuid.h>
 #include <linux/semaphore.h>
 #include <asm/unaligned.h>
-#include "compat.h"
 #include "ctree.h"
 #include "disk-io.h"
 #include "transaction.h"
@@ -64,7 +63,6 @@ static void btrfs_destroy_ordered_operations(struct btrfs_transaction *t,
 static void btrfs_destroy_ordered_extents(struct btrfs_root *root);
 static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
                                      struct btrfs_root *root);
-static void btrfs_evict_pending_snapshots(struct btrfs_transaction *t);
 static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root);
 static int btrfs_destroy_marked_extents(struct btrfs_root *root,
                                        struct extent_io_tree *dirty_pages,
@@ -477,14 +475,8 @@ static int csum_dirty_buffer(struct btrfs_root *root, struct page *page)
        if (page != eb->pages[0])
                return 0;
        found_start = btrfs_header_bytenr(eb);
-       if (found_start != start) {
-               WARN_ON(1);
+       if (WARN_ON(found_start != start || !PageUptodate(page)))
                return 0;
-       }
-       if (!PageUptodate(page)) {
-               WARN_ON(1);
-               return 0;
-       }
        csum_tree_block(root, eb, 0);
        return 0;
 }
@@ -496,7 +488,7 @@ static int check_tree_block_fsid(struct btrfs_root *root,
        u8 fsid[BTRFS_UUID_SIZE];
        int ret = 1;
 
-       read_extent_buffer(eb, fsid, btrfs_header_fsid(eb), BTRFS_FSID_SIZE);
+       read_extent_buffer(eb, fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE);
        while (fs_devices) {
                if (!memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE)) {
                        ret = 0;
@@ -1105,8 +1097,7 @@ struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
 {
        struct inode *btree_inode = root->fs_info->btree_inode;
        struct extent_buffer *eb;
-       eb = find_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
-                               bytenr, blocksize);
+       eb = find_extent_buffer(&BTRFS_I(btree_inode)->io_tree, bytenr);
        return eb;
 }
 
@@ -1229,14 +1220,18 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
        atomic_set(&root->refs, 1);
        root->log_transid = 0;
        root->last_log_commit = 0;
-       extent_io_tree_init(&root->dirty_log_pages,
-                            fs_info->btree_inode->i_mapping);
+       if (fs_info)
+               extent_io_tree_init(&root->dirty_log_pages,
+                                    fs_info->btree_inode->i_mapping);
 
        memset(&root->root_key, 0, sizeof(root->root_key));
        memset(&root->root_item, 0, sizeof(root->root_item));
        memset(&root->defrag_progress, 0, sizeof(root->defrag_progress));
        memset(&root->root_kobj, 0, sizeof(root->root_kobj));
-       root->defrag_trans_start = fs_info->generation;
+       if (fs_info)
+               root->defrag_trans_start = fs_info->generation;
+       else
+               root->defrag_trans_start = 0;
        init_completion(&root->kobj_unregister);
        root->defrag_running = 0;
        root->root_key.objectid = objectid;
@@ -1253,6 +1248,22 @@ static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info)
        return root;
 }
 
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+/* Should only be used by the testing infrastructure */
+struct btrfs_root *btrfs_alloc_dummy_root(void)
+{
+       struct btrfs_root *root;
+
+       root = btrfs_alloc_root(NULL);
+       if (!root)
+               return ERR_PTR(-ENOMEM);
+       __setup_root(4096, 4096, 4096, 4096, root, NULL, 1);
+       root->dummy_root = 1;
+
+       return root;
+}
+#endif
+
 struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
                                     struct btrfs_fs_info *fs_info,
                                     u64 objectid)
@@ -1292,7 +1303,7 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
        btrfs_set_header_owner(leaf, objectid);
        root->node = leaf;
 
-       write_extent_buffer(leaf, fs_info->fsid, btrfs_header_fsid(leaf),
+       write_extent_buffer(leaf, fs_info->fsid, btrfs_header_fsid(),
                            BTRFS_FSID_SIZE);
        write_extent_buffer(leaf, fs_info->chunk_tree_uuid,
                            btrfs_header_chunk_tree_uuid(leaf),
@@ -1379,7 +1390,7 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,
        root->node = leaf;
 
        write_extent_buffer(root->node, root->fs_info->fsid,
-                           btrfs_header_fsid(root->node), BTRFS_FSID_SIZE);
+                           btrfs_header_fsid(), BTRFS_FSID_SIZE);
        btrfs_mark_buffer_dirty(root->node);
        btrfs_tree_unlock(root->node);
        return root;
@@ -1780,6 +1791,9 @@ sleep:
                wake_up_process(root->fs_info->cleaner_kthread);
                mutex_unlock(&root->fs_info->transaction_kthread_mutex);
 
+               if (unlikely(test_bit(BTRFS_FS_STATE_ERROR,
+                                     &root->fs_info->fs_state)))
+                       btrfs_cleanup_transaction(root);
                if (!try_to_freeze()) {
                        set_current_state(TASK_INTERRUPTIBLE);
                        if (!kthread_should_stop() &&
@@ -2013,50 +2027,28 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
        btrfs_stop_workers(&fs_info->qgroup_rescan_workers);
 }
 
+static void free_root_extent_buffers(struct btrfs_root *root)
+{
+       if (root) {
+               free_extent_buffer(root->node);
+               free_extent_buffer(root->commit_root);
+               root->node = NULL;
+               root->commit_root = NULL;
+       }
+}
+
 /* helper to cleanup tree roots */
 static void free_root_pointers(struct btrfs_fs_info *info, int chunk_root)
 {
-       free_extent_buffer(info->tree_root->node);
-       free_extent_buffer(info->tree_root->commit_root);
-       info->tree_root->node = NULL;
-       info->tree_root->commit_root = NULL;
-
-       if (info->dev_root) {
-               free_extent_buffer(info->dev_root->node);
-               free_extent_buffer(info->dev_root->commit_root);
-               info->dev_root->node = NULL;
-               info->dev_root->commit_root = NULL;
-       }
-       if (info->extent_root) {
-               free_extent_buffer(info->extent_root->node);
-               free_extent_buffer(info->extent_root->commit_root);
-               info->extent_root->node = NULL;
-               info->extent_root->commit_root = NULL;
-       }
-       if (info->csum_root) {
-               free_extent_buffer(info->csum_root->node);
-               free_extent_buffer(info->csum_root->commit_root);
-               info->csum_root->node = NULL;
-               info->csum_root->commit_root = NULL;
-       }
-       if (info->quota_root) {
-               free_extent_buffer(info->quota_root->node);
-               free_extent_buffer(info->quota_root->commit_root);
-               info->quota_root->node = NULL;
-               info->quota_root->commit_root = NULL;
-       }
-       if (info->uuid_root) {
-               free_extent_buffer(info->uuid_root->node);
-               free_extent_buffer(info->uuid_root->commit_root);
-               info->uuid_root->node = NULL;
-               info->uuid_root->commit_root = NULL;
-       }
-       if (chunk_root) {
-               free_extent_buffer(info->chunk_root->node);
-               free_extent_buffer(info->chunk_root->commit_root);
-               info->chunk_root->node = NULL;
-               info->chunk_root->commit_root = NULL;
-       }
+       free_root_extent_buffers(info->tree_root);
+
+       free_root_extent_buffers(info->dev_root);
+       free_root_extent_buffers(info->extent_root);
+       free_root_extent_buffers(info->csum_root);
+       free_root_extent_buffers(info->quota_root);
+       free_root_extent_buffers(info->uuid_root);
+       if (chunk_root)
+               free_root_extent_buffers(info->chunk_root);
 }
 
 static void del_fs_roots(struct btrfs_fs_info *fs_info)
@@ -2230,7 +2222,6 @@ int open_ctree(struct super_block *sb,
        atomic_set(&fs_info->scrubs_paused, 0);
        atomic_set(&fs_info->scrub_cancel_req, 0);
        init_waitqueue_head(&fs_info->scrub_pause_wait);
-       init_rwsem(&fs_info->scrub_super_lock);
        fs_info->scrub_workers_refcnt = 0;
 #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
        fs_info->check_integrity_print_mask = 0;
@@ -2272,7 +2263,7 @@ int open_ctree(struct super_block *sb,
               sizeof(struct btrfs_key));
        set_bit(BTRFS_INODE_DUMMY,
                &BTRFS_I(fs_info->btree_inode)->runtime_flags);
-       insert_inode_hash(fs_info->btree_inode);
+       btrfs_insert_inode_hash(fs_info->btree_inode);
 
        spin_lock_init(&fs_info->block_group_cache_lock);
        fs_info->block_group_cache_tree = RB_ROOT;
@@ -2670,6 +2661,7 @@ retry_root_backup:
 
        btrfs_set_root_node(&tree_root->root_item, tree_root->node);
        tree_root->commit_root = btrfs_root_node(tree_root);
+       btrfs_set_root_refs(&tree_root->root_item, 1);
 
        location.objectid = BTRFS_EXTENT_TREE_OBJECTID;
        location.type = BTRFS_ROOT_ITEM_KEY;
@@ -3448,10 +3440,7 @@ static int write_all_supers(struct btrfs_root *root, int max_mirrors)
 int write_ctree_super(struct btrfs_trans_handle *trans,
                      struct btrfs_root *root, int max_mirrors)
 {
-       int ret;
-
-       ret = write_all_supers(root, max_mirrors);
-       return ret;
+       return write_all_supers(root, max_mirrors);
 }
 
 /* Drop a fs root from the radix tree and free it. */
@@ -3528,7 +3517,6 @@ int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info)
 int btrfs_commit_super(struct btrfs_root *root)
 {
        struct btrfs_trans_handle *trans;
-       int ret;
 
        mutex_lock(&root->fs_info->cleaner_mutex);
        btrfs_run_delayed_iputs(root);
@@ -3542,25 +3530,7 @@ int btrfs_commit_super(struct btrfs_root *root)
        trans = btrfs_join_transaction(root);
        if (IS_ERR(trans))
                return PTR_ERR(trans);
-       ret = btrfs_commit_transaction(trans, root);
-       if (ret)
-               return ret;
-       /* run commit again to drop the original snapshot */
-       trans = btrfs_join_transaction(root);
-       if (IS_ERR(trans))
-               return PTR_ERR(trans);
-       ret = btrfs_commit_transaction(trans, root);
-       if (ret)
-               return ret;
-       ret = btrfs_write_and_wait_transaction(NULL, root);
-       if (ret) {
-               btrfs_error(root->fs_info, ret,
-                           "Failed to sync btree inode to disk.");
-               return ret;
-       }
-
-       ret = write_ctree_super(NULL, root, 0);
-       return ret;
+       return btrfs_commit_transaction(trans, root);
 }
 
 int close_ctree(struct btrfs_root *root)
@@ -3614,12 +3584,12 @@ int close_ctree(struct btrfs_root *root)
                       percpu_counter_sum(&fs_info->delalloc_bytes));
        }
 
+       del_fs_roots(fs_info);
+
        btrfs_free_block_groups(fs_info);
 
        btrfs_stop_all_workers(fs_info);
 
-       del_fs_roots(fs_info);
-
        free_root_pointers(fs_info, 1);
 
        iput(fs_info->btree_inode);
@@ -3669,10 +3639,20 @@ int btrfs_set_buffer_uptodate(struct extent_buffer *buf)
 
 void btrfs_mark_buffer_dirty(struct extent_buffer *buf)
 {
-       struct btrfs_root *root = BTRFS_I(buf->pages[0]->mapping->host)->root;
+       struct btrfs_root *root;
        u64 transid = btrfs_header_generation(buf);
        int was_dirty;
 
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+       /*
+        * This is a fast path so only do this check if we have sanity tests
+        * enabled.  Normal people shouldn't be marking dummy buffers as dirty
+        * outside of the sanity tests.
+        */
+       if (unlikely(test_bit(EXTENT_BUFFER_DUMMY, &buf->bflags)))
+               return;
+#endif
+       root = BTRFS_I(buf->pages[0]->mapping->host)->root;
        btrfs_assert_tree_locked(buf);
        if (transid != root->fs_info->generation)
                WARN(1, KERN_CRIT "btrfs transid mismatch buffer %llu, "
@@ -3802,7 +3782,8 @@ static void btrfs_destroy_all_ordered_extents(struct btrfs_fs_info *fs_info)
        while (!list_empty(&splice)) {
                root = list_first_entry(&splice, struct btrfs_root,
                                        ordered_root);
-               list_del_init(&root->ordered_root);
+               list_move_tail(&root->ordered_root,
+                              &fs_info->ordered_roots);
 
                btrfs_destroy_ordered_extents(root);
 
@@ -3880,24 +3861,6 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
        return ret;
 }
 
-static void btrfs_evict_pending_snapshots(struct btrfs_transaction *t)
-{
-       struct btrfs_pending_snapshot *snapshot;
-       struct list_head splice;
-
-       INIT_LIST_HEAD(&splice);
-
-       list_splice_init(&t->pending_snapshots, &splice);
-
-       while (!list_empty(&splice)) {
-               snapshot = list_entry(splice.next,
-                                     struct btrfs_pending_snapshot,
-                                     list);
-               snapshot->error = -ECANCELED;
-               list_del_init(&snapshot->list);
-       }
-}
-
 static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root)
 {
        struct btrfs_inode *btrfs_inode;
@@ -4027,15 +3990,13 @@ again:
 void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
                                   struct btrfs_root *root)
 {
+       btrfs_destroy_ordered_operations(cur_trans, root);
+
        btrfs_destroy_delayed_refs(cur_trans, root);
-       btrfs_block_rsv_release(root, &root->fs_info->trans_block_rsv,
-                               cur_trans->dirty_pages.dirty_bytes);
 
        cur_trans->state = TRANS_STATE_COMMIT_START;
        wake_up(&root->fs_info->transaction_blocked_wait);
 
-       btrfs_evict_pending_snapshots(cur_trans);
-
        cur_trans->state = TRANS_STATE_UNBLOCKED;
        wake_up(&root->fs_info->transaction_wait);
 
@@ -4059,63 +4020,51 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
 static int btrfs_cleanup_transaction(struct btrfs_root *root)
 {
        struct btrfs_transaction *t;
-       LIST_HEAD(list);
 
        mutex_lock(&root->fs_info->transaction_kthread_mutex);
 
        spin_lock(&root->fs_info->trans_lock);
-       list_splice_init(&root->fs_info->trans_list, &list);
-       root->fs_info->running_transaction = NULL;
-       spin_unlock(&root->fs_info->trans_lock);
-
-       while (!list_empty(&list)) {
-               t = list_entry(list.next, struct btrfs_transaction, list);
-
-               btrfs_destroy_ordered_operations(t, root);
-
-               btrfs_destroy_all_ordered_extents(root->fs_info);
-
-               btrfs_destroy_delayed_refs(t, root);
-
-               /*
-                *  FIXME: cleanup wait for commit
-                *  We needn't acquire the lock here, because we are during
-                *  the umount, there is no other task which will change it.
-                */
-               t->state = TRANS_STATE_COMMIT_START;
-               smp_mb();
-               if (waitqueue_active(&root->fs_info->transaction_blocked_wait))
-                       wake_up(&root->fs_info->transaction_blocked_wait);
-
-               btrfs_evict_pending_snapshots(t);
-
-               t->state = TRANS_STATE_UNBLOCKED;
-               smp_mb();
-               if (waitqueue_active(&root->fs_info->transaction_wait))
-                       wake_up(&root->fs_info->transaction_wait);
-
-               btrfs_destroy_delayed_inodes(root);
-               btrfs_assert_delayed_root_empty(root);
-
-               btrfs_destroy_all_delalloc_inodes(root->fs_info);
-
-               btrfs_destroy_marked_extents(root, &t->dirty_pages,
-                                            EXTENT_DIRTY);
-
-               btrfs_destroy_pinned_extent(root,
-                                           root->fs_info->pinned_extents);
-
-               t->state = TRANS_STATE_COMPLETED;
-               smp_mb();
-               if (waitqueue_active(&t->commit_wait))
-                       wake_up(&t->commit_wait);
+       while (!list_empty(&root->fs_info->trans_list)) {
+               t = list_first_entry(&root->fs_info->trans_list,
+                                    struct btrfs_transaction, list);
+               if (t->state >= TRANS_STATE_COMMIT_START) {
+                       atomic_inc(&t->use_count);
+                       spin_unlock(&root->fs_info->trans_lock);
+                       btrfs_wait_for_commit(root, t->transid);
+                       btrfs_put_transaction(t);
+                       spin_lock(&root->fs_info->trans_lock);
+                       continue;
+               }
+               if (t == root->fs_info->running_transaction) {
+                       t->state = TRANS_STATE_COMMIT_DOING;
+                       spin_unlock(&root->fs_info->trans_lock);
+                       /*
+                        * We wait for 0 num_writers since we don't hold a trans
+                        * handle open currently for this transaction.
+                        */
+                       wait_event(t->writer_wait,
+                                  atomic_read(&t->num_writers) == 0);
+               } else {
+                       spin_unlock(&root->fs_info->trans_lock);
+               }
+               btrfs_cleanup_one_transaction(t, root);
 
-               atomic_set(&t->use_count, 0);
+               spin_lock(&root->fs_info->trans_lock);
+               if (t == root->fs_info->running_transaction)
+                       root->fs_info->running_transaction = NULL;
                list_del_init(&t->list);
-               memset(t, 0, sizeof(*t));
-               kmem_cache_free(btrfs_transaction_cachep, t);
-       }
+               spin_unlock(&root->fs_info->trans_lock);
 
+               btrfs_put_transaction(t);
+               trace_btrfs_transaction_commit(root);
+               spin_lock(&root->fs_info->trans_lock);
+       }
+       spin_unlock(&root->fs_info->trans_lock);
+       btrfs_destroy_all_ordered_extents(root->fs_info);
+       btrfs_destroy_delayed_inodes(root);
+       btrfs_assert_delayed_root_empty(root);
+       btrfs_destroy_pinned_extent(root, root->fs_info->pinned_extents);
+       btrfs_destroy_all_delalloc_inodes(root->fs_info);
        mutex_unlock(&root->fs_info->transaction_kthread_mutex);
 
        return 0;
index 5ce2a7d..53059df 100644 (file)
@@ -86,6 +86,10 @@ void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info,
                                 struct btrfs_root *root);
 void btrfs_free_fs_root(struct btrfs_root *root);
 
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+struct btrfs_root *btrfs_alloc_dummy_root(void);
+#endif
+
 /*
  * This function is used to grab the root, and avoid it is freed when we
  * access it. But it doesn't ensure that the tree is not dropped.
index 4b86916..41422a3 100644 (file)
@@ -5,7 +5,6 @@
 #include "btrfs_inode.h"
 #include "print-tree.h"
 #include "export.h"
-#include "compat.h"
 
 #define BTRFS_FID_SIZE_NON_CONNECTABLE (offsetof(struct btrfs_fid, \
                                                 parent_objectid) / 4)
index d58bef1..45d98d0 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/slab.h>
 #include <linux/ratelimit.h>
 #include <linux/percpu_counter.h>
-#include "compat.h"
 #include "hash.h"
 #include "ctree.h"
 #include "disk-io.h"
@@ -1551,9 +1550,8 @@ again:
        if (ret && !insert) {
                err = -ENOENT;
                goto out;
-       } else if (ret) {
+       } else if (WARN_ON(ret)) {
                err = -EIO;
-               WARN_ON(1);
                goto out;
        }
 
@@ -1979,7 +1977,6 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
        struct btrfs_extent_item *item;
        u64 refs;
        int ret;
-       int err = 0;
 
        path = btrfs_alloc_path();
        if (!path)
@@ -1992,14 +1989,9 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
                                           path, bytenr, num_bytes, parent,
                                           root_objectid, owner, offset,
                                           refs_to_add, extent_op);
-       if (ret == 0)
+       if (ret != -EAGAIN)
                goto out;
 
-       if (ret != -EAGAIN) {
-               err = ret;
-               goto out;
-       }
-
        leaf = path->nodes[0];
        item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
        refs = btrfs_extent_refs(leaf, item);
@@ -2021,7 +2013,7 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
                btrfs_abort_transaction(trans, root, ret);
 out:
        btrfs_free_path(path);
-       return err;
+       return ret;
 }
 
 static int run_delayed_data_ref(struct btrfs_trans_handle *trans,
@@ -2137,15 +2129,28 @@ again:
        }
        if (ret > 0) {
                if (metadata) {
-                       btrfs_release_path(path);
-                       metadata = 0;
+                       if (path->slots[0] > 0) {
+                               path->slots[0]--;
+                               btrfs_item_key_to_cpu(path->nodes[0], &key,
+                                                     path->slots[0]);
+                               if (key.objectid == node->bytenr &&
+                                   key.type == BTRFS_EXTENT_ITEM_KEY &&
+                                   key.offset == node->num_bytes)
+                                       ret = 0;
+                       }
+                       if (ret > 0) {
+                               btrfs_release_path(path);
+                               metadata = 0;
 
-                       key.offset = node->num_bytes;
-                       key.type = BTRFS_EXTENT_ITEM_KEY;
-                       goto again;
+                               key.objectid = node->bytenr;
+                               key.offset = node->num_bytes;
+                               key.type = BTRFS_EXTENT_ITEM_KEY;
+                               goto again;
+                       }
+               } else {
+                       err = -EIO;
+                       goto out;
                }
-               err = -EIO;
-               goto out;
        }
 
        leaf = path->nodes[0];
@@ -2234,8 +2239,12 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
 {
        int ret = 0;
 
-       if (trans->aborted)
+       if (trans->aborted) {
+               if (insert_reserved)
+                       btrfs_pin_extent(root, node->bytenr,
+                                        node->num_bytes, 1);
                return 0;
+       }
 
        if (btrfs_delayed_ref_is_head(node)) {
                struct btrfs_delayed_ref_head *head;
@@ -2411,6 +2420,14 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
                                btrfs_free_delayed_extent_op(extent_op);
 
                                if (ret) {
+                                       /*
+                                        * Need to reset must_insert_reserved if
+                                        * there was an error so the abort stuff
+                                        * can cleanup the reserved space
+                                        * properly.
+                                        */
+                                       if (must_insert_reserved)
+                                               locked_ref->must_insert_reserved = 1;
                                        btrfs_debug(fs_info, "run_delayed_extent_op returned %d", ret);
                                        spin_lock(&delayed_refs->lock);
                                        btrfs_delayed_ref_unlock(locked_ref);
@@ -3197,8 +3214,7 @@ again:
                if (ret)
                        goto out_put;
 
-               ret = btrfs_truncate_free_space_cache(root, trans, path,
-                                                     inode);
+               ret = btrfs_truncate_free_space_cache(root, trans, inode);
                if (ret)
                        goto out_put;
        }
@@ -3318,10 +3334,9 @@ again:
                last = cache->key.objectid + cache->key.offset;
 
                err = write_one_cache_group(trans, root, path, cache);
+               btrfs_put_block_group(cache);
                if (err) /* File system offline */
                        goto out;
-
-               btrfs_put_block_group(cache);
        }
 
        while (1) {
@@ -3605,10 +3620,9 @@ int btrfs_check_data_free_space(struct inode *inode, u64 bytes)
        /* make sure bytes are sectorsize aligned */
        bytes = ALIGN(bytes, root->sectorsize);
 
-       if (root == root->fs_info->tree_root ||
-           BTRFS_I(inode)->location.objectid == BTRFS_FREE_INO_OBJECTID) {
-               alloc_chunk = 0;
+       if (btrfs_is_free_space_inode(inode)) {
                committed = 1;
+               ASSERT(current->journal_info);
        }
 
        data_sinfo = fs_info->data_sinfo;
@@ -3636,6 +3650,16 @@ again:
                        spin_unlock(&data_sinfo->lock);
 alloc:
                        alloc_target = btrfs_get_alloc_profile(root, 1);
+                       /*
+                        * It is ugly that we don't call nolock join
+                        * transaction for the free space inode case here.
+                        * But it is safe because we only do the data space
+                        * reservation for the free space cache in the
+                        * transaction context, the common join transaction
+                        * just increase the counter of the current transaction
+                        * handler, doesn't try to acquire the trans_lock of
+                        * the fs.
+                        */
                        trans = btrfs_join_transaction(root);
                        if (IS_ERR(trans))
                                return PTR_ERR(trans);
@@ -3681,6 +3705,9 @@ commit_trans:
                        goto again;
                }
 
+               trace_btrfs_space_reservation(root->fs_info,
+                                             "space_info:enospc",
+                                             data_sinfo->flags, bytes, 1);
                return -ENOSPC;
        }
        data_sinfo->bytes_may_use += bytes;
@@ -3989,12 +4016,26 @@ static void btrfs_writeback_inodes_sb_nr(struct btrfs_root *root,
                 * the filesystem is readonly(all dirty pages are written to
                 * the disk).
                 */
-               btrfs_start_all_delalloc_inodes(root->fs_info, 0);
+               btrfs_start_delalloc_roots(root->fs_info, 0);
                if (!current->journal_info)
-                       btrfs_wait_all_ordered_extents(root->fs_info);
+                       btrfs_wait_ordered_roots(root->fs_info, -1);
        }
 }
 
+static inline int calc_reclaim_items_nr(struct btrfs_root *root, u64 to_reclaim)
+{
+       u64 bytes;
+       int nr;
+
+       bytes = btrfs_calc_trans_metadata_size(root, 1);
+       nr = (int)div64_u64(to_reclaim, bytes);
+       if (!nr)
+               nr = 1;
+       return nr;
+}
+
+#define EXTENT_SIZE_PER_ITEM   (256 * 1024)
+
 /*
  * shrink metadata reservation for delalloc
  */
@@ -4007,24 +4048,30 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig,
        u64 delalloc_bytes;
        u64 max_reclaim;
        long time_left;
-       unsigned long nr_pages = (2 * 1024 * 1024) >> PAGE_CACHE_SHIFT;
-       int loops = 0;
+       unsigned long nr_pages;
+       int loops;
+       int items;
        enum btrfs_reserve_flush_enum flush;
 
+       /* Calc the number of the pages we need flush for space reservation */
+       items = calc_reclaim_items_nr(root, to_reclaim);
+       to_reclaim = items * EXTENT_SIZE_PER_ITEM;
+
        trans = (struct btrfs_trans_handle *)current->journal_info;
        block_rsv = &root->fs_info->delalloc_block_rsv;
        space_info = block_rsv->space_info;
 
-       smp_mb();
        delalloc_bytes = percpu_counter_sum_positive(
                                                &root->fs_info->delalloc_bytes);
        if (delalloc_bytes == 0) {
                if (trans)
                        return;
-               btrfs_wait_all_ordered_extents(root->fs_info);
+               if (wait_ordered)
+                       btrfs_wait_ordered_roots(root->fs_info, items);
                return;
        }
 
+       loops = 0;
        while (delalloc_bytes && loops < 3) {
                max_reclaim = min(delalloc_bytes, to_reclaim);
                nr_pages = max_reclaim >> PAGE_CACHE_SHIFT;
@@ -4033,9 +4080,19 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig,
                 * We need to wait for the async pages to actually start before
                 * we do anything.
                 */
-               wait_event(root->fs_info->async_submit_wait,
-                          !atomic_read(&root->fs_info->async_delalloc_pages));
+               max_reclaim = atomic_read(&root->fs_info->async_delalloc_pages);
+               if (!max_reclaim)
+                       goto skip_async;
+
+               if (max_reclaim <= nr_pages)
+                       max_reclaim = 0;
+               else
+                       max_reclaim -= nr_pages;
 
+               wait_event(root->fs_info->async_submit_wait,
+                          atomic_read(&root->fs_info->async_delalloc_pages) <=
+                          (int)max_reclaim);
+skip_async:
                if (!trans)
                        flush = BTRFS_RESERVE_FLUSH_ALL;
                else
@@ -4049,13 +4106,12 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig,
 
                loops++;
                if (wait_ordered && !trans) {
-                       btrfs_wait_all_ordered_extents(root->fs_info);
+                       btrfs_wait_ordered_roots(root->fs_info, items);
                } else {
                        time_left = schedule_timeout_killable(1);
                        if (time_left)
                                break;
                }
-               smp_mb();
                delalloc_bytes = percpu_counter_sum_positive(
                                                &root->fs_info->delalloc_bytes);
        }
@@ -4140,16 +4196,11 @@ static int flush_space(struct btrfs_root *root,
        switch (state) {
        case FLUSH_DELAYED_ITEMS_NR:
        case FLUSH_DELAYED_ITEMS:
-               if (state == FLUSH_DELAYED_ITEMS_NR) {
-                       u64 bytes = btrfs_calc_trans_metadata_size(root, 1);
-
-                       nr = (int)div64_u64(num_bytes, bytes);
-                       if (!nr)
-                               nr = 1;
-                       nr *= 2;
-               } else {
+               if (state == FLUSH_DELAYED_ITEMS_NR)
+                       nr = calc_reclaim_items_nr(root, num_bytes) * 2;
+               else
                        nr = -1;
-               }
+
                trans = btrfs_join_transaction(root);
                if (IS_ERR(trans)) {
                        ret = PTR_ERR(trans);
@@ -4332,6 +4383,10 @@ out:
                    !block_rsv_use_bytes(global_rsv, orig_bytes))
                        ret = 0;
        }
+       if (ret == -ENOSPC)
+               trace_btrfs_space_reservation(root->fs_info,
+                                             "space_info:enospc",
+                                             space_info->flags, orig_bytes, 1);
        if (flushing) {
                spin_lock(&space_info->lock);
                space_info->flush = 0;
@@ -4986,7 +5041,7 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
                mutex_unlock(&BTRFS_I(inode)->delalloc_mutex);
 
        if (to_reserve)
-               trace_btrfs_space_reservation(root->fs_info,"delalloc",
+               trace_btrfs_space_reservation(root->fs_info, "delalloc",
                                              btrfs_ino(inode), to_reserve, 1);
        block_rsv_add_bytes(block_rsv, to_reserve, 1);
 
@@ -5264,6 +5319,8 @@ static int pin_down_extent(struct btrfs_root *root,
 
        set_extent_dirty(root->fs_info->pinned_extents, bytenr,
                         bytenr + num_bytes - 1, GFP_NOFS | __GFP_NOFAIL);
+       if (reserved)
+               trace_btrfs_reserved_extent_free(root, bytenr, num_bytes);
        return 0;
 }
 
@@ -5718,9 +5775,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                        }
                        extent_slot = path->slots[0];
                }
-       } else if (ret == -ENOENT) {
+       } else if (WARN_ON(ret == -ENOENT)) {
                btrfs_print_leaf(extent_root, path->nodes[0]);
-               WARN_ON(1);
                btrfs_err(info,
                        "unable to find ref byte nr %llu parent %llu root %llu  owner %llu offset %llu",
                        bytenr, parent, root_objectid, owner_objectid,
@@ -5967,6 +6023,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
 
                btrfs_add_free_space(cache, buf->start, buf->len);
                btrfs_update_reserved_bytes(cache, buf->len, RESERVE_FREE);
+               trace_btrfs_reserved_extent_free(root, buf->start, buf->len);
                pin = 0;
        }
 out:
@@ -6594,8 +6651,6 @@ again:
                }
        }
 
-       trace_btrfs_reserved_extent_alloc(root, ins->objectid, ins->offset);
-
        return ret;
 }
 
@@ -6707,6 +6762,7 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
                        ins->objectid, ins->offset);
                BUG();
        }
+       trace_btrfs_reserved_extent_alloc(root, ins->objectid, ins->offset);
        return ret;
 }
 
@@ -6731,13 +6787,18 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
                size += sizeof(*block_info);
 
        path = btrfs_alloc_path();
-       if (!path)
+       if (!path) {
+               btrfs_free_and_pin_reserved_extent(root, ins->objectid,
+                                                  root->leafsize);
                return -ENOMEM;
+       }
 
        path->leave_spinning = 1;
        ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path,
                                      ins, size);
        if (ret) {
+               btrfs_free_and_pin_reserved_extent(root, ins->objectid,
+                                                  root->leafsize);
                btrfs_free_path(path);
                return ret;
        }
@@ -6779,6 +6840,8 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
                        ins->objectid, ins->offset);
                BUG();
        }
+
+       trace_btrfs_reserved_extent_alloc(root, ins->objectid, root->leafsize);
        return ret;
 }
 
@@ -7983,7 +8046,7 @@ u64 btrfs_account_ro_block_groups_free_space(struct btrfs_space_info *sinfo)
 
        spin_lock(&sinfo->lock);
 
-       for(i = 0; i < BTRFS_NR_RAID_TYPES; i++)
+       for (i = 0; i < BTRFS_NR_RAID_TYPES; i++)
                if (!list_empty(&sinfo->block_groups[i]))
                        free_bytes += __btrfs_get_ro_block_group_free_space(
                                                &sinfo->block_groups[i]);
@@ -8271,15 +8334,14 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
 
        release_global_block_rsv(info);
 
-       while(!list_empty(&info->space_info)) {
+       while (!list_empty(&info->space_info)) {
                space_info = list_entry(info->space_info.next,
                                        struct btrfs_space_info,
                                        list);
                if (btrfs_test_opt(info->tree_root, ENOSPC_DEBUG)) {
-                       if (space_info->bytes_pinned > 0 ||
+                       if (WARN_ON(space_info->bytes_pinned > 0 ||
                            space_info->bytes_reserved > 0 ||
-                           space_info->bytes_may_use > 0) {
-                               WARN_ON(1);
+                           space_info->bytes_may_use > 0)) {
                                dump_space_info(space_info, 0, 0);
                        }
                }
index 51731b7..8e457fc 100644 (file)
 #include <linux/cleancache.h>
 #include "extent_io.h"
 #include "extent_map.h"
-#include "compat.h"
 #include "ctree.h"
 #include "btrfs_inode.h"
 #include "volumes.h"
 #include "check-integrity.h"
 #include "locking.h"
 #include "rcu-string.h"
+#include "backref.h"
 
 static struct kmem_cache *extent_state_cache;
 static struct kmem_cache *extent_buffer_cache;
@@ -1597,11 +1597,10 @@ done:
  *
  * 1 is returned if we find something, 0 if nothing was in the tree
  */
-static noinline u64 find_lock_delalloc_range(struct inode *inode,
-                                            struct extent_io_tree *tree,
-                                            struct page *locked_page,
-                                            u64 *start, u64 *end,
-                                            u64 max_bytes)
+STATIC u64 find_lock_delalloc_range(struct inode *inode,
+                                   struct extent_io_tree *tree,
+                                   struct page *locked_page, u64 *start,
+                                   u64 *end, u64 max_bytes)
 {
        u64 delalloc_start;
        u64 delalloc_end;
@@ -1740,10 +1739,8 @@ u64 count_range_bits(struct extent_io_tree *tree,
        u64 last = 0;
        int found = 0;
 
-       if (search_end <= cur_start) {
-               WARN_ON(1);
+       if (WARN_ON(search_end <= cur_start))
                return 0;
-       }
 
        spin_lock(&tree->lock);
        if (cur_start == 0 && bits == EXTENT_DIRTY) {
@@ -1983,6 +1980,7 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start,
        struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
        int ret;
 
+       ASSERT(!(fs_info->sb->s_flags & MS_RDONLY));
        BUG_ON(!mirror_num);
 
        /* we can't repair anything in raid56 yet */
@@ -2039,6 +2037,9 @@ int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
        unsigned long i, num_pages = num_extent_pages(eb->start, eb->len);
        int ret = 0;
 
+       if (root->fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
        for (i = 0; i < num_pages; i++) {
                struct page *p = extent_buffer_page(eb, i);
                ret = repair_io_failure(root->fs_info, start, PAGE_CACHE_SIZE,
@@ -2060,12 +2061,12 @@ static int clean_io_failure(u64 start, struct page *page)
        u64 private;
        u64 private_failure;
        struct io_failure_record *failrec;
-       struct btrfs_fs_info *fs_info;
+       struct inode *inode = page->mapping->host;
+       struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
        struct extent_state *state;
        int num_copies;
        int did_repair = 0;
        int ret;
-       struct inode *inode = page->mapping->host;
 
        private = 0;
        ret = count_range_bits(&BTRFS_I(inode)->io_failure_tree, &private,
@@ -2088,6 +2089,8 @@ static int clean_io_failure(u64 start, struct page *page)
                did_repair = 1;
                goto out;
        }
+       if (fs_info->sb->s_flags & MS_RDONLY)
+               goto out;
 
        spin_lock(&BTRFS_I(inode)->io_tree.lock);
        state = find_first_extent_bit_state(&BTRFS_I(inode)->io_tree,
@@ -2097,7 +2100,6 @@ static int clean_io_failure(u64 start, struct page *page)
 
        if (state && state->start <= failrec->start &&
            state->end >= failrec->start + failrec->len - 1) {
-               fs_info = BTRFS_I(inode)->root->fs_info;
                num_copies = btrfs_num_copies(fs_info, failrec->logical,
                                              failrec->len);
                if (num_copies > 1)  {
@@ -3569,9 +3571,8 @@ retry:
                         * but no sense in crashing the users box for something
                         * we can survive anyway.
                         */
-                       if (!eb) {
+                       if (WARN_ON(!eb)) {
                                spin_unlock(&mapping->private_lock);
-                               WARN_ON(1);
                                continue;
                        }
 
@@ -4038,7 +4039,7 @@ static struct extent_map *get_extent_skip_holes(struct inode *inode,
        if (offset >= last)
                return NULL;
 
-       while(1) {
+       while (1) {
                len = last - offset;
                if (len == 0)
                        break;
@@ -4062,6 +4063,19 @@ static struct extent_map *get_extent_skip_holes(struct inode *inode,
        return NULL;
 }
 
+static noinline int count_ext_ref(u64 inum, u64 offset, u64 root_id, void *ctx)
+{
+       unsigned long cnt = *((unsigned long *)ctx);
+
+       cnt++;
+       *((unsigned long *)ctx) = cnt;
+
+       /* Now we're sure that the extent is shared. */
+       if (cnt > 1)
+               return 1;
+       return 0;
+}
+
 int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                __u64 start, __u64 len, get_extent_t *get_extent)
 {
@@ -4128,7 +4142,7 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                last = found_key.offset;
                last_for_get_extent = last + 1;
        }
-       btrfs_free_path(path);
+       btrfs_release_path(path);
 
        /*
         * we might have some extents allocated but more delalloc past those
@@ -4198,7 +4212,24 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                        flags |= (FIEMAP_EXTENT_DELALLOC |
                                  FIEMAP_EXTENT_UNKNOWN);
                } else {
+                       unsigned long ref_cnt = 0;
+
                        disko = em->block_start + offset_in_extent;
+
+                       /*
+                        * As btrfs supports shared space, this information
+                        * can be exported to userspace tools via
+                        * flag FIEMAP_EXTENT_SHARED.
+                        */
+                       ret = iterate_inodes_from_logical(
+                                       em->block_start,
+                                       BTRFS_I(inode)->root->fs_info,
+                                       path, count_ext_ref, &ref_cnt);
+                       if (ret < 0 && ret != -ENOENT)
+                               goto out_free;
+
+                       if (ref_cnt > 1)
+                               flags |= FIEMAP_EXTENT_SHARED;
                }
                if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags))
                        flags |= FIEMAP_EXTENT_ENCODED;
@@ -4230,6 +4261,7 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
 out_free:
        free_extent_map(em);
 out:
+       btrfs_free_path(path);
        unlock_extent_cached(&BTRFS_I(inode)->io_tree, start, start + len - 1,
                             &cached_state, GFP_NOFS);
        return ret;
@@ -4455,6 +4487,23 @@ static void mark_extent_buffer_accessed(struct extent_buffer *eb)
        }
 }
 
+struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
+                                                       u64 start)
+{
+       struct extent_buffer *eb;
+
+       rcu_read_lock();
+       eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT);
+       if (eb && atomic_inc_not_zero(&eb->refs)) {
+               rcu_read_unlock();
+               mark_extent_buffer_accessed(eb);
+               return eb;
+       }
+       rcu_read_unlock();
+
+       return NULL;
+}
+
 struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
                                          u64 start, unsigned long len)
 {
@@ -4468,14 +4517,10 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
        int uptodate = 1;
        int ret;
 
-       rcu_read_lock();
-       eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT);
-       if (eb && atomic_inc_not_zero(&eb->refs)) {
-               rcu_read_unlock();
-               mark_extent_buffer_accessed(eb);
+
+       eb = find_extent_buffer(tree, start);
+       if (eb)
                return eb;
-       }
-       rcu_read_unlock();
 
        eb = __alloc_extent_buffer(tree, start, len, GFP_NOFS);
        if (!eb)
@@ -4534,24 +4579,17 @@ again:
 
        spin_lock(&tree->buffer_lock);
        ret = radix_tree_insert(&tree->buffer, start >> PAGE_CACHE_SHIFT, eb);
+       spin_unlock(&tree->buffer_lock);
+       radix_tree_preload_end();
        if (ret == -EEXIST) {
-               exists = radix_tree_lookup(&tree->buffer,
-                                               start >> PAGE_CACHE_SHIFT);
-               if (!atomic_inc_not_zero(&exists->refs)) {
-                       spin_unlock(&tree->buffer_lock);
-                       radix_tree_preload_end();
-                       exists = NULL;
+               exists = find_extent_buffer(tree, start);
+               if (exists)
+                       goto free_eb;
+               else
                        goto again;
-               }
-               spin_unlock(&tree->buffer_lock);
-               radix_tree_preload_end();
-               mark_extent_buffer_accessed(exists);
-               goto free_eb;
        }
        /* add one reference for the tree */
        check_buffer_tree_ref(eb);
-       spin_unlock(&tree->buffer_lock);
-       radix_tree_preload_end();
 
        /*
         * there is a race where release page may have
@@ -4582,23 +4620,6 @@ free_eb:
        return exists;
 }
 
-struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
-                                        u64 start, unsigned long len)
-{
-       struct extent_buffer *eb;
-
-       rcu_read_lock();
-       eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT);
-       if (eb && atomic_inc_not_zero(&eb->refs)) {
-               rcu_read_unlock();
-               mark_extent_buffer_accessed(eb);
-               return eb;
-       }
-       rcu_read_unlock();
-
-       return NULL;
-}
-
 static inline void btrfs_release_extent_buffer_rcu(struct rcu_head *head)
 {
        struct extent_buffer *eb =
@@ -5062,23 +5083,6 @@ void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
        }
 }
 
-static void move_pages(struct page *dst_page, struct page *src_page,
-                      unsigned long dst_off, unsigned long src_off,
-                      unsigned long len)
-{
-       char *dst_kaddr = page_address(dst_page);
-       if (dst_page == src_page) {
-               memmove(dst_kaddr + dst_off, dst_kaddr + src_off, len);
-       } else {
-               char *src_kaddr = page_address(src_page);
-               char *p = dst_kaddr + dst_off + len;
-               char *s = src_kaddr + src_off + len;
-
-               while (len--)
-                       *--p = *--s;
-       }
-}
-
 static inline bool areas_overlap(unsigned long src, unsigned long dst, unsigned long len)
 {
        unsigned long distance = (src > dst) ? src - dst : dst - src;
@@ -5189,7 +5193,7 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
 
                cur = min_t(unsigned long, len, src_off_in_page + 1);
                cur = min(cur, dst_off_in_page + 1);
-               move_pages(extent_buffer_page(dst, dst_i),
+               copy_pages(extent_buffer_page(dst, dst_i),
                           extent_buffer_page(dst, src_i),
                           dst_off_in_page - cur + 1,
                           src_off_in_page - cur + 1, cur);
index 6dbc645..19620c5 100644 (file)
@@ -271,7 +271,7 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
 struct extent_buffer *alloc_dummy_extent_buffer(u64 start, unsigned long len);
 struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src);
 struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
-                                        u64 start, unsigned long len);
+                                        u64 start);
 void free_extent_buffer(struct extent_buffer *eb);
 void free_extent_buffer_stale(struct extent_buffer *eb);
 #define WAIT_NONE      0
@@ -345,4 +345,10 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start,
 int end_extent_writepage(struct page *page, int err, u64 start, u64 end);
 int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
                         int mirror_num);
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+noinline u64 find_lock_delalloc_range(struct inode *inode,
+                                     struct extent_io_tree *tree,
+                                     struct page *locked_page, u64 *start,
+                                     u64 *end, u64 max_bytes);
+#endif
 #endif
index 61adc44..93fba71 100644 (file)
@@ -3,10 +3,10 @@
 
 #include <linux/rbtree.h>
 
-#define EXTENT_MAP_LAST_BYTE (u64)-4
-#define EXTENT_MAP_HOLE (u64)-3
-#define EXTENT_MAP_INLINE (u64)-2
-#define EXTENT_MAP_DELALLOC (u64)-1
+#define EXTENT_MAP_LAST_BYTE ((u64)-4)
+#define EXTENT_MAP_HOLE ((u64)-3)
+#define EXTENT_MAP_INLINE ((u64)-2)
+#define EXTENT_MAP_DELALLOC ((u64)-1)
 
 /* bits for the flags field */
 #define EXTENT_FLAG_PINNED 0 /* this entry not yet on disk, don't free it */
index 4f53159..6f38488 100644 (file)
@@ -329,6 +329,9 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
        u64 csum_end;
        u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
 
+       ASSERT(start == ALIGN(start, root->sectorsize) &&
+              (end + 1) == ALIGN(end + 1, root->sectorsize));
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -846,10 +849,8 @@ insert:
        path->leave_spinning = 0;
        if (ret < 0)
                goto fail_unlock;
-       if (ret != 0) {
-               WARN_ON(1);
+       if (WARN_ON(ret != 0))
                goto fail_unlock;
-       }
        leaf = path->nodes[0];
 csum:
        item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
index 72da4df..82d0342 100644 (file)
@@ -39,7 +39,6 @@
 #include "print-tree.h"
 #include "tree-log.h"
 #include "locking.h"
-#include "compat.h"
 #include "volumes.h"
 
 static struct kmem_cache *btrfs_inode_defrag_cachep;
@@ -370,7 +369,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
        u64 root_objectid = 0;
 
        atomic_inc(&fs_info->defrag_running);
-       while(1) {
+       while (1) {
                /* Pause the auto defragger. */
                if (test_bit(BTRFS_FS_STATE_REMOUNTING,
                             &fs_info->fs_state))
@@ -1281,6 +1280,7 @@ again:
                }
                wait_on_page_writeback(pages[i]);
        }
+       faili = num_pages - 1;
        err = 0;
        if (start_pos < inode->i_size) {
                struct btrfs_ordered_extent *ordered;
@@ -1299,8 +1299,10 @@ again:
                                unlock_page(pages[i]);
                                page_cache_release(pages[i]);
                        }
-                       btrfs_wait_ordered_range(inode, start_pos,
-                                                last_pos - start_pos);
+                       err = btrfs_wait_ordered_range(inode, start_pos,
+                                                      last_pos - start_pos);
+                       if (err)
+                               goto fail;
                        goto again;
                }
                if (ordered)
@@ -1809,8 +1811,13 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
        atomic_inc(&root->log_batch);
        full_sync = test_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
                             &BTRFS_I(inode)->runtime_flags);
-       if (full_sync)
-               btrfs_wait_ordered_range(inode, start, end - start + 1);
+       if (full_sync) {
+               ret = btrfs_wait_ordered_range(inode, start, end - start + 1);
+               if (ret) {
+                       mutex_unlock(&inode->i_mutex);
+                       goto out;
+               }
+       }
        atomic_inc(&root->log_batch);
 
        /*
@@ -1876,27 +1883,20 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
        mutex_unlock(&inode->i_mutex);
 
        if (ret != BTRFS_NO_LOG_SYNC) {
-               if (ret > 0) {
-                       /*
-                        * If we didn't already wait for ordered extents we need
-                        * to do that now.
-                        */
-                       if (!full_sync)
-                               btrfs_wait_ordered_range(inode, start,
-                                                        end - start + 1);
-                       ret = btrfs_commit_transaction(trans, root);
-               } else {
+               if (!ret) {
                        ret = btrfs_sync_log(trans, root);
-                       if (ret == 0) {
+                       if (!ret) {
                                ret = btrfs_end_transaction(trans, root);
-                       } else {
-                               if (!full_sync)
-                                       btrfs_wait_ordered_range(inode, start,
-                                                                end -
-                                                                start + 1);
-                               ret = btrfs_commit_transaction(trans, root);
+                               goto out;
                        }
                }
+               if (!full_sync) {
+                       ret = btrfs_wait_ordered_range(inode, start,
+                                                      end - start + 1);
+                       if (ret)
+                               goto out;
+               }
+               ret = btrfs_commit_transaction(trans, root);
        } else {
                ret = btrfs_end_transaction(trans, root);
        }
@@ -2067,7 +2067,9 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
        bool same_page = ((offset >> PAGE_CACHE_SHIFT) ==
                          ((offset + len - 1) >> PAGE_CACHE_SHIFT));
 
-       btrfs_wait_ordered_range(inode, offset, len);
+       ret = btrfs_wait_ordered_range(inode, offset, len);
+       if (ret)
+               return ret;
 
        mutex_lock(&inode->i_mutex);
        /*
@@ -2136,8 +2138,12 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
                        btrfs_put_ordered_extent(ordered);
                unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart,
                                     lockend, &cached_state, GFP_NOFS);
-               btrfs_wait_ordered_range(inode, lockstart,
-                                        lockend - lockstart + 1);
+               ret = btrfs_wait_ordered_range(inode, lockstart,
+                                              lockend - lockstart + 1);
+               if (ret) {
+                       mutex_unlock(&inode->i_mutex);
+                       return ret;
+               }
        }
 
        path = btrfs_alloc_path();
@@ -2308,7 +2314,10 @@ static long btrfs_fallocate(struct file *file, int mode,
         * wait for ordered IO before we have any locks.  We'll loop again
         * below with the locks held.
         */
-       btrfs_wait_ordered_range(inode, alloc_start, alloc_end - alloc_start);
+       ret = btrfs_wait_ordered_range(inode, alloc_start,
+                                      alloc_end - alloc_start);
+       if (ret)
+               goto out;
 
        locked_end = alloc_end - 1;
        while (1) {
@@ -2332,8 +2341,10 @@ static long btrfs_fallocate(struct file *file, int mode,
                         * we can't wait on the range with the transaction
                         * running or with the extent lock held
                         */
-                       btrfs_wait_ordered_range(inode, alloc_start,
-                                                alloc_end - alloc_start);
+                       ret = btrfs_wait_ordered_range(inode, alloc_start,
+                                                      alloc_end - alloc_start);
+                       if (ret)
+                               goto out;
                } else {
                        if (ordered)
                                btrfs_put_ordered_extent(ordered);
@@ -2405,14 +2416,12 @@ out_reserve_fail:
 static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
-       struct extent_map *em;
+       struct extent_map *em = NULL;
        struct extent_state *cached_state = NULL;
        u64 lockstart = *offset;
        u64 lockend = i_size_read(inode);
        u64 start = *offset;
-       u64 orig_start = *offset;
        u64 len = i_size_read(inode);
-       u64 last_end = 0;
        int ret = 0;
 
        lockend = max_t(u64, root->sectorsize, lockend);
@@ -2429,89 +2438,35 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
        lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, 0,
                         &cached_state);
 
-       /*
-        * Delalloc is such a pain.  If we have a hole and we have pending
-        * delalloc for a portion of the hole we will get back a hole that
-        * exists for the entire range since it hasn't been actually written
-        * yet.  So to take care of this case we need to look for an extent just
-        * before the position we want in case there is outstanding delalloc
-        * going on here.
-        */
-       if (whence == SEEK_HOLE && start != 0) {
-               if (start <= root->sectorsize)
-                       em = btrfs_get_extent_fiemap(inode, NULL, 0, 0,
-                                                    root->sectorsize, 0);
-               else
-                       em = btrfs_get_extent_fiemap(inode, NULL, 0,
-                                                    start - root->sectorsize,
-                                                    root->sectorsize, 0);
-               if (IS_ERR(em)) {
-                       ret = PTR_ERR(em);
-                       goto out;
-               }
-               last_end = em->start + em->len;
-               if (em->block_start == EXTENT_MAP_DELALLOC)
-                       last_end = min_t(u64, last_end, inode->i_size);
-               free_extent_map(em);
-       }
-
-       while (1) {
+       while (start < inode->i_size) {
                em = btrfs_get_extent_fiemap(inode, NULL, 0, start, len, 0);
                if (IS_ERR(em)) {
                        ret = PTR_ERR(em);
+                       em = NULL;
                        break;
                }
 
-               if (em->block_start == EXTENT_MAP_HOLE) {
-                       if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
-                               if (last_end <= orig_start) {
-                                       free_extent_map(em);
-                                       ret = -ENXIO;
-                                       break;
-                               }
-                       }
-
-                       if (whence == SEEK_HOLE) {
-                               *offset = start;
-                               free_extent_map(em);
-                               break;
-                       }
-               } else {
-                       if (whence == SEEK_DATA) {
-                               if (em->block_start == EXTENT_MAP_DELALLOC) {
-                                       if (start >= inode->i_size) {
-                                               free_extent_map(em);
-                                               ret = -ENXIO;
-                                               break;
-                                       }
-                               }
-
-                               if (!test_bit(EXTENT_FLAG_PREALLOC,
-                                             &em->flags)) {
-                                       *offset = start;
-                                       free_extent_map(em);
-                                       break;
-                               }
-                       }
-               }
+               if (whence == SEEK_HOLE &&
+                   (em->block_start == EXTENT_MAP_HOLE ||
+                    test_bit(EXTENT_FLAG_PREALLOC, &em->flags)))
+                       break;
+               else if (whence == SEEK_DATA &&
+                          (em->block_start != EXTENT_MAP_HOLE &&
+                           !test_bit(EXTENT_FLAG_PREALLOC, &em->flags)))
+                       break;
 
                start = em->start + em->len;
-               last_end = em->start + em->len;
-
-               if (em->block_start == EXTENT_MAP_DELALLOC)
-                       last_end = min_t(u64, last_end, inode->i_size);
-
-               if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
-                       free_extent_map(em);
-                       ret = -ENXIO;
-                       break;
-               }
                free_extent_map(em);
+               em = NULL;
                cond_resched();
        }
-       if (!ret)
-               *offset = min(*offset, inode->i_size);
-out:
+       free_extent_map(em);
+       if (!ret) {
+               if (whence == SEEK_DATA && start >= inode->i_size)
+                       ret = -ENXIO;
+               else
+                       *offset = min_t(loff_t, start, inode->i_size);
+       }
        unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
                             &cached_state, GFP_NOFS);
        return ret;
index b4f9904..057be95 100644 (file)
@@ -218,7 +218,6 @@ int btrfs_check_trunc_cache_free_space(struct btrfs_root *root,
 
 int btrfs_truncate_free_space_cache(struct btrfs_root *root,
                                    struct btrfs_trans_handle *trans,
-                                   struct btrfs_path *path,
                                    struct inode *inode)
 {
        int ret = 0;
@@ -1009,8 +1008,13 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
        if (ret)
                goto out;
 
-
-       btrfs_wait_ordered_range(inode, 0, (u64)-1);
+       ret = btrfs_wait_ordered_range(inode, 0, (u64)-1);
+       if (ret) {
+               clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, inode->i_size - 1,
+                                EXTENT_DIRTY | EXTENT_DELALLOC, 0, 0, NULL,
+                                GFP_NOFS);
+               goto out;
+       }
 
        key.objectid = BTRFS_FREE_SPACE_OBJECTID;
        key.offset = offset;
@@ -2276,7 +2280,7 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group,
                goto out;
 
        entry = rb_entry(node, struct btrfs_free_space, offset_index);
-       while(1) {
+       while (1) {
                if (entry->bytes < bytes && entry->bytes > *max_extent_size)
                        *max_extent_size = entry->bytes;
 
@@ -2967,19 +2971,15 @@ out:
 
 int btrfs_write_out_ino_cache(struct btrfs_root *root,
                              struct btrfs_trans_handle *trans,
-                             struct btrfs_path *path)
+                             struct btrfs_path *path,
+                             struct inode *inode)
 {
        struct btrfs_free_space_ctl *ctl = root->free_ino_ctl;
-       struct inode *inode;
        int ret;
 
        if (!btrfs_test_opt(root, INODE_MAP_CACHE))
                return 0;
 
-       inode = lookup_free_ino_inode(root, path);
-       if (IS_ERR(inode))
-               return 0;
-
        ret = __btrfs_write_out_cache(root, inode, ctl, NULL, trans, path, 0);
        if (ret) {
                btrfs_delalloc_release_metadata(inode, inode->i_size);
@@ -2990,7 +2990,6 @@ int btrfs_write_out_ino_cache(struct btrfs_root *root,
 #endif
        }
 
-       iput(inode);
        return ret;
 }
 
index e737f92..0cf4977 100644 (file)
@@ -58,7 +58,6 @@ int btrfs_check_trunc_cache_free_space(struct btrfs_root *root,
                                       struct btrfs_block_rsv *rsv);
 int btrfs_truncate_free_space_cache(struct btrfs_root *root,
                                    struct btrfs_trans_handle *trans,
-                                   struct btrfs_path *path,
                                    struct inode *inode);
 int load_free_space_cache(struct btrfs_fs_info *fs_info,
                          struct btrfs_block_group_cache *block_group);
@@ -76,7 +75,8 @@ int load_free_ino_cache(struct btrfs_fs_info *fs_info,
                        struct btrfs_root *root);
 int btrfs_write_out_ino_cache(struct btrfs_root *root,
                              struct btrfs_trans_handle *trans,
-                             struct btrfs_path *path);
+                             struct btrfs_path *path,
+                             struct inode *inode);
 
 void btrfs_init_free_space_ctl(struct btrfs_block_group_cache *block_group);
 int __btrfs_add_free_space(struct btrfs_free_space_ctl *ctl,
index e0b7034..ec82fae 100644 (file)
@@ -369,7 +369,7 @@ static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
                goto out;
 
        leaf = path->nodes[0];
-       item = btrfs_item_nr(leaf, path->slots[0]);
+       item = btrfs_item_nr(path->slots[0]);
        ptr = (unsigned long)btrfs_item_ptr(leaf, path->slots[0], char);
        ptr += btrfs_item_size(leaf, item) - ins_len;
        extref = (struct btrfs_inode_extref *)ptr;
index 2c66ddb..ab485e5 100644 (file)
@@ -78,10 +78,8 @@ again:
                            btrfs_transaction_in_commit(fs_info)) {
                                leaf = path->nodes[0];
 
-                               if (btrfs_header_nritems(leaf) == 0) {
-                                       WARN_ON(1);
+                               if (WARN_ON(btrfs_header_nritems(leaf) == 0))
                                        break;
-                               }
 
                                /*
                                 * Save the key so we can advances forward
@@ -237,7 +235,7 @@ again:
                start_caching(root);
 
                if (objectid <= root->cache_progress ||
-                   objectid > root->highest_objectid)
+                   objectid >= root->highest_objectid)
                        __btrfs_add_free_space(ctl, objectid, 1);
                else
                        __btrfs_add_free_space(pinned, objectid, 1);
@@ -412,8 +410,7 @@ int btrfs_save_ino_cache(struct btrfs_root *root,
                return 0;
 
        /* Don't save inode cache if we are deleting this root */
-       if (btrfs_root_refs(&root->root_item) == 0 &&
-           root != root->fs_info->tree_root)
+       if (btrfs_root_refs(&root->root_item) == 0)
                return 0;
 
        if (!btrfs_test_opt(root, INODE_MAP_CACHE))
@@ -467,7 +464,7 @@ again:
        }
 
        if (i_size_read(inode) > 0) {
-               ret = btrfs_truncate_free_space_cache(root, trans, path, inode);
+               ret = btrfs_truncate_free_space_cache(root, trans, inode);
                if (ret) {
                        if (ret != -ENOSPC)
                                btrfs_abort_transaction(trans, root, ret);
@@ -504,7 +501,7 @@ again:
        }
        btrfs_free_reserved_data_space(inode, prealloc);
 
-       ret = btrfs_write_out_ino_cache(root, trans, path);
+       ret = btrfs_write_out_ino_cache(root, trans, path, inode);
 out_put:
        iput(inode);
 out_release:
index 51e3afa..f1a7744 100644 (file)
@@ -43,7 +43,6 @@
 #include <linux/btrfs.h>
 #include <linux/blkdev.h>
 #include <linux/posix_acl_xattr.h>
-#include "compat.h"
 #include "ctree.h"
 #include "disk-io.h"
 #include "transaction.h"
@@ -844,7 +843,10 @@ static noinline int cow_file_range(struct inode *inode,
        struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
        int ret = 0;
 
-       BUG_ON(btrfs_is_free_space_inode(inode));
+       if (btrfs_is_free_space_inode(inode)) {
+               WARN_ON_ONCE(1);
+               return -EINVAL;
+       }
 
        num_bytes = ALIGN(end - start + 1, blocksize);
        num_bytes = max(blocksize,  num_bytes);
@@ -1178,10 +1180,8 @@ static noinline int run_delalloc_nocow(struct inode *inode,
        while (1) {
                ret = btrfs_lookup_file_extent(trans, root, path, ino,
                                               cur_offset, 0);
-               if (ret < 0) {
-                       btrfs_abort_transaction(trans, root, ret);
+               if (ret < 0)
                        goto error;
-               }
                if (ret > 0 && path->slots[0] > 0 && check_prev) {
                        leaf = path->nodes[0];
                        btrfs_item_key_to_cpu(leaf, &found_key,
@@ -1195,10 +1195,8 @@ next_slot:
                leaf = path->nodes[0];
                if (path->slots[0] >= btrfs_header_nritems(leaf)) {
                        ret = btrfs_next_leaf(root, path);
-                       if (ret < 0) {
-                               btrfs_abort_transaction(trans, root, ret);
+                       if (ret < 0)
                                goto error;
-                       }
                        if (ret > 0)
                                break;
                        leaf = path->nodes[0];
@@ -1289,10 +1287,8 @@ out_check:
                        ret = cow_file_range(inode, locked_page,
                                             cow_start, found_key.offset - 1,
                                             page_started, nr_written, 1);
-                       if (ret) {
-                               btrfs_abort_transaction(trans, root, ret);
+                       if (ret)
                                goto error;
-                       }
                        cow_start = (u64)-1;
                }
 
@@ -1339,10 +1335,8 @@ out_check:
                    BTRFS_DATA_RELOC_TREE_OBJECTID) {
                        ret = btrfs_reloc_clone_csums(inode, cur_offset,
                                                      num_bytes);
-                       if (ret) {
-                               btrfs_abort_transaction(trans, root, ret);
+                       if (ret)
                                goto error;
-                       }
                }
 
                extent_clear_unlock_delalloc(inode, cur_offset,
@@ -1364,10 +1358,8 @@ out_check:
        if (cow_start != (u64)-1) {
                ret = cow_file_range(inode, locked_page, cow_start, end,
                                     page_started, nr_written, 1);
-               if (ret) {
-                       btrfs_abort_transaction(trans, root, ret);
+               if (ret)
                        goto error;
-               }
        }
 
 error:
@@ -1551,7 +1543,13 @@ static void btrfs_clear_bit_hook(struct inode *inode,
                        spin_unlock(&BTRFS_I(inode)->lock);
                }
 
-               if (*bits & EXTENT_DO_ACCOUNTING)
+               /*
+                * We don't reserve metadata space for space cache inodes so we
+                * don't need to call dellalloc_release_metadata if there is an
+                * error.
+                */
+               if (*bits & EXTENT_DO_ACCOUNTING &&
+                   root != root->fs_info->tree_root)
                        btrfs_delalloc_release_metadata(inode, len);
 
                if (root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID
@@ -2041,10 +2039,8 @@ static noinline int record_one_backref(u64 inum, u64 offset, u64 root_id,
                key.offset = offset;
 
        ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
-       if (ret < 0) {
-               WARN_ON(1);
+       if (WARN_ON(ret < 0))
                return ret;
-       }
        ret = 0;
 
        while (1) {
@@ -2133,7 +2129,8 @@ static noinline bool record_extent_backrefs(struct btrfs_path *path,
                                                  old->extent_offset, fs_info,
                                                  path, record_one_backref,
                                                  old);
-               BUG_ON(ret < 0 && ret != -ENOENT);
+               if (ret < 0 && ret != -ENOENT)
+                       return false;
 
                /* no backref to be processed for this extent */
                if (!old->count) {
@@ -2367,10 +2364,23 @@ out_unlock:
        return ret;
 }
 
+static void free_sa_defrag_extent(struct new_sa_defrag_extent *new)
+{
+       struct old_sa_defrag_extent *old, *tmp;
+
+       if (!new)
+               return;
+
+       list_for_each_entry_safe(old, tmp, &new->head, list) {
+               list_del(&old->list);
+               kfree(old);
+       }
+       kfree(new);
+}
+
 static void relink_file_extents(struct new_sa_defrag_extent *new)
 {
        struct btrfs_path *path;
-       struct old_sa_defrag_extent *old, *tmp;
        struct sa_defrag_extent_backref *backref;
        struct sa_defrag_extent_backref *prev = NULL;
        struct inode *inode;
@@ -2413,16 +2423,11 @@ static void relink_file_extents(struct new_sa_defrag_extent *new)
        kfree(prev);
 
        btrfs_free_path(path);
-
-       list_for_each_entry_safe(old, tmp, &new->head, list) {
-               list_del(&old->list);
-               kfree(old);
-       }
 out:
+       free_sa_defrag_extent(new);
+
        atomic_dec(&root->fs_info->defrag_running);
        wake_up(&root->fs_info->transaction_wait);
-
-       kfree(new);
 }
 
 static struct new_sa_defrag_extent *
@@ -2432,7 +2437,7 @@ record_old_file_extents(struct inode *inode,
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_path *path;
        struct btrfs_key key;
-       struct old_sa_defrag_extent *old, *tmp;
+       struct old_sa_defrag_extent *old;
        struct new_sa_defrag_extent *new;
        int ret;
 
@@ -2480,7 +2485,7 @@ record_old_file_extents(struct inode *inode,
                if (slot >= btrfs_header_nritems(l)) {
                        ret = btrfs_next_leaf(root, path);
                        if (ret < 0)
-                               goto out_free_list;
+                               goto out_free_path;
                        else if (ret > 0)
                                break;
                        continue;
@@ -2509,7 +2514,7 @@ record_old_file_extents(struct inode *inode,
 
                old = kmalloc(sizeof(*old), GFP_NOFS);
                if (!old)
-                       goto out_free_list;
+                       goto out_free_path;
 
                offset = max(new->file_pos, key.offset);
                end = min(new->file_pos + new->len, key.offset + num_bytes);
@@ -2531,15 +2536,10 @@ next:
 
        return new;
 
-out_free_list:
-       list_for_each_entry_safe(old, tmp, &new->head, list) {
-               list_del(&old->list);
-               kfree(old);
-       }
 out_free_path:
        btrfs_free_path(path);
 out_kfree:
-       kfree(new);
+       free_sa_defrag_extent(new);
        return NULL;
 }
 
@@ -2710,8 +2710,14 @@ out:
        btrfs_remove_ordered_extent(inode, ordered_extent);
 
        /* for snapshot-aware defrag */
-       if (new)
-               relink_file_extents(new);
+       if (new) {
+               if (ret) {
+                       free_sa_defrag_extent(new);
+                       atomic_dec(&root->fs_info->defrag_running);
+               } else {
+                       relink_file_extents(new);
+               }
+       }
 
        /* once for us */
        btrfs_put_ordered_extent(ordered_extent);
@@ -2969,6 +2975,7 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode)
        if (insert >= 1) {
                ret = btrfs_insert_orphan_item(trans, root, btrfs_ino(inode));
                if (ret) {
+                       atomic_dec(&root->orphan_inodes);
                        if (reserve) {
                                clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED,
                                          &BTRFS_I(inode)->runtime_flags);
@@ -3018,14 +3025,16 @@ static int btrfs_orphan_del(struct btrfs_trans_handle *trans,
                release_rsv = 1;
        spin_unlock(&root->orphan_lock);
 
-       if (trans && delete_item)
-               ret = btrfs_del_orphan_item(trans, root, btrfs_ino(inode));
-
-       if (release_rsv) {
-               btrfs_orphan_release_metadata(inode);
+       if (delete_item) {
                atomic_dec(&root->orphan_inodes);
+               if (trans)
+                       ret = btrfs_del_orphan_item(trans, root,
+                                                   btrfs_ino(inode));
        }
 
+       if (release_rsv)
+               btrfs_orphan_release_metadata(inode);
+
        return ret;
 }
 
@@ -3172,8 +3181,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
 
                /* if we have links, this was a truncate, lets do that */
                if (inode->i_nlink) {
-                       if (!S_ISREG(inode->i_mode)) {
-                               WARN_ON(1);
+                       if (WARN_ON(!S_ISREG(inode->i_mode))) {
                                iput(inode);
                                continue;
                        }
@@ -3636,7 +3644,7 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
        int ret;
        ret = __btrfs_unlink_inode(trans, root, dir, inode, name, name_len);
        if (!ret) {
-               btrfs_drop_nlink(inode);
+               drop_nlink(inode);
                ret = btrfs_update_inode(trans, root, inode);
        }
        return ret;
@@ -4230,15 +4238,16 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
 
        while (1) {
                struct btrfs_ordered_extent *ordered;
-               btrfs_wait_ordered_range(inode, hole_start,
-                                        block_end - hole_start);
+
                lock_extent_bits(io_tree, hole_start, block_end - 1, 0,
                                 &cached_state);
-               ordered = btrfs_lookup_ordered_extent(inode, hole_start);
+               ordered = btrfs_lookup_ordered_range(inode, hole_start,
+                                                    block_end - hole_start);
                if (!ordered)
                        break;
                unlock_extent_cached(io_tree, hole_start, block_end - 1,
                                     &cached_state, GFP_NOFS);
+               btrfs_start_ordered_extent(inode, ordered, 1);
                btrfs_put_ordered_extent(ordered);
        }
 
@@ -4472,8 +4481,10 @@ void btrfs_evict_inode(struct inode *inode)
        trace_btrfs_inode_evict(inode);
 
        truncate_inode_pages(&inode->i_data, 0);
-       if (inode->i_nlink && (btrfs_root_refs(&root->root_item) != 0 ||
-                              btrfs_is_free_space_inode(inode)))
+       if (inode->i_nlink &&
+           ((btrfs_root_refs(&root->root_item) != 0 &&
+             root->root_key.objectid != BTRFS_ROOT_TREE_OBJECTID) ||
+            btrfs_is_free_space_inode(inode)))
                goto no_delete;
 
        if (is_bad_inode(inode)) {
@@ -4490,7 +4501,8 @@ void btrfs_evict_inode(struct inode *inode)
        }
 
        if (inode->i_nlink > 0) {
-               BUG_ON(btrfs_root_refs(&root->root_item) != 0);
+               BUG_ON(btrfs_root_refs(&root->root_item) != 0 &&
+                      root->root_key.objectid != BTRFS_ROOT_TREE_OBJECTID);
                goto no_delete;
        }
 
@@ -4731,14 +4743,7 @@ static void inode_tree_del(struct inode *inode)
        }
        spin_unlock(&root->inode_lock);
 
-       /*
-        * Free space cache has inodes in the tree root, but the tree root has a
-        * root_refs of 0, so this could end up dropping the tree root as a
-        * snapshot, so we need the extra !root->fs_info->tree_root check to
-        * make sure we don't drop it.
-        */
-       if (empty && btrfs_root_refs(&root->root_item) == 0 &&
-           root != root->fs_info->tree_root) {
+       if (empty && btrfs_root_refs(&root->root_item) == 0) {
                synchronize_srcu(&root->fs_info->subvol_srcu);
                spin_lock(&root->inode_lock);
                empty = RB_EMPTY_ROOT(&root->inode_tree);
@@ -4831,10 +4836,12 @@ static struct inode *btrfs_iget_locked(struct super_block *s,
 {
        struct inode *inode;
        struct btrfs_iget_args args;
+       unsigned long hashval = btrfs_inode_hash(objectid, root);
+
        args.ino = objectid;
        args.root = root;
 
-       inode = iget5_locked(s, objectid, btrfs_find_actor,
+       inode = iget5_locked(s, hashval, btrfs_find_actor,
                             btrfs_init_locked_inode,
                             (void *)&args);
        return inode;
@@ -5048,7 +5055,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
                        continue;
                }
 
-               item = btrfs_item_nr(leaf, slot);
+               item = btrfs_item_nr(slot);
                btrfs_item_key_to_cpu(leaf, &found_key, slot);
 
                if (found_key.objectid != key.objectid)
@@ -5454,7 +5461,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
                                BTRFS_INODE_NODATASUM;
        }
 
-       insert_inode_hash(inode);
+       btrfs_insert_inode_hash(inode);
        inode_tree_add(inode);
 
        trace_btrfs_inode_new(inode);
@@ -5730,7 +5737,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
                goto fail;
        }
 
-       btrfs_inc_nlink(inode);
+       inc_nlink(inode);
        inode_inc_iversion(inode);
        inode->i_ctime = CURRENT_TIME;
        ihold(inode);
@@ -5860,7 +5867,7 @@ static noinline int uncompress_inline(struct btrfs_path *path,
        compress_type = btrfs_file_extent_compression(leaf, item);
        max_size = btrfs_file_extent_ram_bytes(leaf, item);
        inline_size = btrfs_file_extent_inline_item_len(leaf,
-                                       btrfs_item_nr(leaf, path->slots[0]));
+                                       btrfs_item_nr(path->slots[0]));
        tmp = kmalloc(inline_size, GFP_NOFS);
        if (!tmp)
                return -ENOMEM;
@@ -5974,7 +5981,14 @@ again:
        found_type = btrfs_key_type(&found_key);
        if (found_key.objectid != objectid ||
            found_type != BTRFS_EXTENT_DATA_KEY) {
-               goto not_found;
+               /*
+                * If we backup past the first extent we want to move forward
+                * and see if there is an extent in front of us, otherwise we'll
+                * say there is a hole for our whole search range which can
+                * cause problems.
+                */
+               extent_end = start;
+               goto next;
        }
 
        found_type = btrfs_file_extent_type(leaf, item);
@@ -5989,7 +6003,7 @@ again:
                size = btrfs_file_extent_inline_len(leaf, item);
                extent_end = ALIGN(extent_start + size, root->sectorsize);
        }
-
+next:
        if (start >= extent_end) {
                path->slots[0]++;
                if (path->slots[0] >= btrfs_header_nritems(leaf)) {
@@ -6173,8 +6187,7 @@ insert:
        write_unlock(&em_tree->lock);
 out:
 
-       if (em)
-               trace_btrfs_get_extent(root, em);
+       trace_btrfs_get_extent(root, em);
 
        if (path)
                btrfs_free_path(path);
@@ -6249,7 +6262,7 @@ struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *pag
        /* adjust the range_start to make sure it doesn't
         * go backwards from the start they passed in
         */
-       range_start = max(start,range_start);
+       range_start = max(start, range_start);
        found = found_end - range_start;
 
        if (found > 0) {
@@ -7053,7 +7066,7 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
                        }
                } else {
                        submit_len += bvec->bv_len;
-                       nr_pages ++;
+                       nr_pages++;
                        bvec++;
                }
        }
@@ -7222,7 +7235,9 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
         * outstanding dirty pages are on disk.
         */
        count = iov_length(iov, nr_segs);
-       btrfs_wait_ordered_range(inode, offset, count);
+       ret = btrfs_wait_ordered_range(inode, offset, count);
+       if (ret)
+               return ret;
 
        if (rw & WRITE) {
                /*
@@ -7563,7 +7578,10 @@ static int btrfs_truncate(struct inode *inode)
        u64 mask = root->sectorsize - 1;
        u64 min_size = btrfs_calc_trunc_metadata_size(root, 1);
 
-       btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
+       ret = btrfs_wait_ordered_range(inode, inode->i_size & (~mask),
+                                      (u64)-1);
+       if (ret)
+               return ret;
 
        /*
         * Yes ladies and gentelment, this is indeed ugly.  The fact is we have
@@ -7787,6 +7805,14 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
        return inode;
 }
 
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+void btrfs_test_destroy_inode(struct inode *inode)
+{
+       btrfs_drop_extent_cache(inode, 0, (u64)-1, 0);
+       kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
+}
+#endif
+
 static void btrfs_i_callback(struct rcu_head *head)
 {
        struct inode *inode = container_of(head, struct inode, i_rcu);
@@ -7857,8 +7883,7 @@ int btrfs_drop_inode(struct inode *inode)
                return 1;
 
        /* the snap/subvol tree is on deleting */
-       if (btrfs_root_refs(&root->root_item) == 0 &&
-           root != root->fs_info->tree_root)
+       if (btrfs_root_refs(&root->root_item) == 0)
                return 1;
        else
                return generic_drop_inode(inode);
@@ -7995,8 +8020,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                if (ret == -EEXIST) {
                        /* we shouldn't get
                         * eexist without a new_inode */
-                       if (!new_inode) {
-                               WARN_ON(1);
+                       if (WARN_ON(!new_inode)) {
                                return ret;
                        }
                } else {
@@ -8144,18 +8168,24 @@ out_notrans:
 static void btrfs_run_delalloc_work(struct btrfs_work *work)
 {
        struct btrfs_delalloc_work *delalloc_work;
+       struct inode *inode;
 
        delalloc_work = container_of(work, struct btrfs_delalloc_work,
                                     work);
-       if (delalloc_work->wait)
-               btrfs_wait_ordered_range(delalloc_work->inode, 0, (u64)-1);
-       else
-               filemap_flush(delalloc_work->inode->i_mapping);
+       inode = delalloc_work->inode;
+       if (delalloc_work->wait) {
+               btrfs_wait_ordered_range(inode, 0, (u64)-1);
+       } else {
+               filemap_flush(inode->i_mapping);
+               if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
+                            &BTRFS_I(inode)->runtime_flags))
+                       filemap_flush(inode->i_mapping);
+       }
 
        if (delalloc_work->delay_iput)
-               btrfs_add_delayed_iput(delalloc_work->inode);
+               btrfs_add_delayed_iput(inode);
        else
-               iput(delalloc_work->inode);
+               iput(inode);
        complete(&delalloc_work->completion);
 }
 
@@ -8276,8 +8306,7 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
        return ret;
 }
 
-int btrfs_start_all_delalloc_inodes(struct btrfs_fs_info *fs_info,
-                                   int delay_iput)
+int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput)
 {
        struct btrfs_root *root;
        struct list_head splice;
@@ -8337,14 +8366,14 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
        int err;
        int drop_inode = 0;
        u64 objectid;
-       u64 index = 0 ;
+       u64 index = 0;
        int name_len;
        int datasize;
        unsigned long ptr;
        struct btrfs_file_extent_item *ei;
        struct extent_buffer *leaf;
 
-       name_len = strlen(symname) + 1;
+       name_len = strlen(symname);
        if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(root))
                return -ENAMETOOLONG;
 
@@ -8432,7 +8461,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
        inode->i_mapping->a_ops = &btrfs_symlink_aops;
        inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
        inode_set_bytes(inode, name_len);
-       btrfs_i_size_write(inode, name_len - 1);
+       btrfs_i_size_write(inode, name_len);
        err = btrfs_update_inode(trans, root, inode);
        if (err)
                drop_inode = 1;
@@ -8491,6 +8520,8 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
                                                  ins.offset, 0, 0, 0,
                                                  BTRFS_FILE_EXTENT_PREALLOC);
                if (ret) {
+                       btrfs_free_reserved_extent(root, ins.objectid,
+                                                  ins.offset);
                        btrfs_abort_transaction(trans, root, ret);
                        if (own_trans)
                                btrfs_end_transaction(trans, root);
index 9d46f60..a111622 100644 (file)
@@ -44,7 +44,6 @@
 #include <linux/uuid.h>
 #include <linux/btrfs.h>
 #include <linux/uaccess.h>
-#include "compat.h"
 #include "ctree.h"
 #include "disk-io.h"
 #include "transaction.h"
@@ -321,7 +320,7 @@ static int btrfs_ioctl_getversion(struct file *file, int __user *arg)
 
 static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
 {
-       struct btrfs_fs_info *fs_info = btrfs_sb(fdentry(file)->d_sb);
+       struct btrfs_fs_info *fs_info = btrfs_sb(file_inode(file)->i_sb);
        struct btrfs_device *device;
        struct request_queue *q;
        struct fstrim_range range;
@@ -369,9 +368,13 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
 
 int btrfs_is_empty_uuid(u8 *uuid)
 {
-       static char empty_uuid[BTRFS_UUID_SIZE] = {0};
+       int i;
 
-       return !memcmp(uuid, empty_uuid, BTRFS_UUID_SIZE);
+       for (i = 0; i < BTRFS_UUID_SIZE; i++) {
+               if (uuid[i])
+                       return 0;
+       }
+       return 1;
 }
 
 static noinline int create_subvol(struct inode *dir,
@@ -436,7 +439,7 @@ static noinline int create_subvol(struct inode *dir,
        btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV);
        btrfs_set_header_owner(leaf, objectid);
 
-       write_extent_buffer(leaf, root->fs_info->fsid, btrfs_header_fsid(leaf),
+       write_extent_buffer(leaf, root->fs_info->fsid, btrfs_header_fsid(),
                            BTRFS_FSID_SIZE);
        write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid,
                            btrfs_header_chunk_tree_uuid(leaf),
@@ -574,7 +577,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
        if (ret)
                return ret;
 
-       btrfs_wait_ordered_extents(root);
+       btrfs_wait_ordered_extents(root, -1);
 
        pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_NOFS);
        if (!pending_snapshot)
@@ -688,7 +691,7 @@ static inline int btrfs_check_sticky(struct inode *dir, struct inode *inode)
  *     nfs_async_unlink().
  */
 
-static int btrfs_may_delete(struct inode *dir,struct dentry *victim,int isdir)
+static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
 {
        int error;
 
@@ -842,7 +845,6 @@ static int find_new_extents(struct btrfs_root *root,
 {
        struct btrfs_path *path;
        struct btrfs_key min_key;
-       struct btrfs_key max_key;
        struct extent_buffer *leaf;
        struct btrfs_file_extent_item *extent;
        int type;
@@ -857,15 +859,10 @@ static int find_new_extents(struct btrfs_root *root,
        min_key.type = BTRFS_EXTENT_DATA_KEY;
        min_key.offset = *off;
 
-       max_key.objectid = ino;
-       max_key.type = (u8)-1;
-       max_key.offset = (u64)-1;
-
        path->keep_locks = 1;
 
-       while(1) {
-               ret = btrfs_search_forward(root, &min_key, &max_key,
-                                          path, newer_than);
+       while (1) {
+               ret = btrfs_search_forward(root, &min_key, path, newer_than);
                if (ret != 0)
                        goto none;
                if (min_key.objectid != ino)
@@ -1206,7 +1203,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
                ra = &file->f_ra;
        }
 
-       pages = kmalloc(sizeof(struct page *) * max_cluster,
+       pages = kmalloc_array(max_cluster, sizeof(struct page *),
                        GFP_NOFS);
        if (!pages) {
                ret = -ENOMEM;
@@ -1893,7 +1890,6 @@ static noinline int search_ioctl(struct inode *inode,
 {
        struct btrfs_root *root;
        struct btrfs_key key;
-       struct btrfs_key max_key;
        struct btrfs_path *path;
        struct btrfs_ioctl_search_key *sk = &args->key;
        struct btrfs_fs_info *info = BTRFS_I(inode)->root->fs_info;
@@ -1925,15 +1921,10 @@ static noinline int search_ioctl(struct inode *inode,
        key.type = sk->min_type;
        key.offset = sk->min_offset;
 
-       max_key.objectid = sk->max_objectid;
-       max_key.type = sk->max_type;
-       max_key.offset = sk->max_offset;
-
        path->keep_locks = 1;
 
-       while(1) {
-               ret = btrfs_search_forward(root, &key, &max_key, path,
-                                          sk->min_transid);
+       while (1) {
+               ret = btrfs_search_forward(root, &key, path, sk->min_transid);
                if (ret != 0) {
                        if (ret > 0)
                                ret = 0;
@@ -2018,7 +2009,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
        key.type = BTRFS_INODE_REF_KEY;
        key.offset = (u64)-1;
 
-       while(1) {
+       while (1) {
                ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
                if (ret < 0)
                        goto out;
@@ -2047,7 +2038,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
                }
 
                *(ptr + len) = '/';
-               read_extent_buffer(l, ptr,(unsigned long)(iref + 1), len);
+               read_extent_buffer(l, ptr, (unsigned long)(iref + 1), len);
 
                if (key.offset == BTRFS_FIRST_FREE_OBJECTID)
                        break;
@@ -2058,7 +2049,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
                dirid = key.objectid;
        }
        memmove(name, ptr, total_len);
-       name[total_len]='\0';
+       name[total_len] = '\0';
        ret = 0;
 out:
        btrfs_free_path(path);
@@ -2098,7 +2089,7 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
 static noinline int btrfs_ioctl_snap_destroy(struct file *file,
                                             void __user *arg)
 {
-       struct dentry *parent = fdentry(file);
+       struct dentry *parent = file->f_path.dentry;
        struct dentry *dentry;
        struct inode *dir = parent->d_inode;
        struct inode *inode;
@@ -2144,7 +2135,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 
        inode = dentry->d_inode;
        dest = BTRFS_I(inode)->root;
-       if (!capable(CAP_SYS_ADMIN)){
+       if (!capable(CAP_SYS_ADMIN)) {
                /*
                 * Regular user.  Only allow this with a special mount
                 * option, when the user has write+exec access to the
@@ -2727,15 +2718,10 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
        size = sizeof(tmp) +
                tmp.dest_count * sizeof(struct btrfs_ioctl_same_extent_info);
 
-       same = kmalloc(size, GFP_NOFS);
-       if (!same) {
-               ret = -EFAULT;
-               goto out;
-       }
+       same = memdup_user((struct btrfs_ioctl_same_args __user *)argp, size);
 
-       if (copy_from_user(same,
-                          (struct btrfs_ioctl_same_args __user *)argp, size)) {
-               ret = -EFAULT;
+       if (IS_ERR(same)) {
+               ret = PTR_ERR(same);
                goto out;
        }
 
@@ -3119,7 +3105,7 @@ out:
 static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                                       u64 off, u64 olen, u64 destoff)
 {
-       struct inode *inode = fdentry(file)->d_inode;
+       struct inode *inode = file_inode(file);
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct fd src_file;
        struct inode *src;
@@ -3679,9 +3665,10 @@ static long btrfs_ioctl_dev_replace(struct btrfs_root *root, void __user *arg)
 
        switch (p->cmd) {
        case BTRFS_IOCTL_DEV_REPLACE_CMD_START:
-               if (root->fs_info->sb->s_flags & MS_RDONLY)
-                       return -EROFS;
-
+               if (root->fs_info->sb->s_flags & MS_RDONLY) {
+                       ret = -EROFS;
+                       goto out;
+               }
                if (atomic_xchg(
                        &root->fs_info->mutually_exclusive_operation_running,
                        1)) {
@@ -3707,7 +3694,7 @@ static long btrfs_ioctl_dev_replace(struct btrfs_root *root, void __user *arg)
 
        if (copy_to_user(arg, p, sizeof(*p)))
                ret = -EFAULT;
-
+out:
        kfree(p);
        return ret;
 }
@@ -4317,7 +4304,7 @@ static long btrfs_ioctl_quota_rescan_status(struct file *file, void __user *arg)
 
 static long btrfs_ioctl_quota_rescan_wait(struct file *file, void __user *arg)
 {
-       struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
+       struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
@@ -4557,9 +4544,15 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_logical_to_ino(root, argp);
        case BTRFS_IOC_SPACE_INFO:
                return btrfs_ioctl_space_info(root, argp);
-       case BTRFS_IOC_SYNC:
-               btrfs_sync_fs(file->f_dentry->d_sb, 1);
-               return 0;
+       case BTRFS_IOC_SYNC: {
+               int ret;
+
+               ret = btrfs_start_delalloc_roots(root->fs_info, 0);
+               if (ret)
+                       return ret;
+               ret = btrfs_sync_fs(file->f_dentry->d_sb, 1);
+               return ret;
+       }
        case BTRFS_IOC_START_SYNC:
                return btrfs_ioctl_start_sync(root, argp);
        case BTRFS_IOC_WAIT_SYNC:
index c702cb6..69582d5 100644 (file)
@@ -537,7 +537,9 @@ void btrfs_remove_ordered_extent(struct inode *inode,
         */
        if (RB_EMPTY_ROOT(&tree->tree) &&
            !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_DIRTY)) {
+               spin_lock(&root->fs_info->ordered_root_lock);
                list_del_init(&BTRFS_I(inode)->ordered_operations);
+               spin_unlock(&root->fs_info->ordered_root_lock);
        }
 
        if (!root->nr_ordered_extents) {
@@ -563,10 +565,11 @@ static void btrfs_run_ordered_extent_work(struct btrfs_work *work)
  * wait for all the ordered extents in a root.  This is done when balancing
  * space between drives.
  */
-void btrfs_wait_ordered_extents(struct btrfs_root *root)
+int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr)
 {
        struct list_head splice, works;
        struct btrfs_ordered_extent *ordered, *next;
+       int count = 0;
 
        INIT_LIST_HEAD(&splice);
        INIT_LIST_HEAD(&works);
@@ -574,7 +577,7 @@ void btrfs_wait_ordered_extents(struct btrfs_root *root)
        mutex_lock(&root->fs_info->ordered_operations_mutex);
        spin_lock(&root->ordered_extent_lock);
        list_splice_init(&root->ordered_extents, &splice);
-       while (!list_empty(&splice)) {
+       while (!list_empty(&splice) && nr) {
                ordered = list_first_entry(&splice, struct btrfs_ordered_extent,
                                           root_extent_list);
                list_move_tail(&ordered->root_extent_list,
@@ -589,7 +592,11 @@ void btrfs_wait_ordered_extents(struct btrfs_root *root)
 
                cond_resched();
                spin_lock(&root->ordered_extent_lock);
+               if (nr != -1)
+                       nr--;
+               count++;
        }
+       list_splice_tail(&splice, &root->ordered_extents);
        spin_unlock(&root->ordered_extent_lock);
 
        list_for_each_entry_safe(ordered, next, &works, work_list) {
@@ -599,18 +606,21 @@ void btrfs_wait_ordered_extents(struct btrfs_root *root)
                cond_resched();
        }
        mutex_unlock(&root->fs_info->ordered_operations_mutex);
+
+       return count;
 }
 
-void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info)
+void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr)
 {
        struct btrfs_root *root;
        struct list_head splice;
+       int done;
 
        INIT_LIST_HEAD(&splice);
 
        spin_lock(&fs_info->ordered_root_lock);
        list_splice_init(&fs_info->ordered_roots, &splice);
-       while (!list_empty(&splice)) {
+       while (!list_empty(&splice) && nr) {
                root = list_first_entry(&splice, struct btrfs_root,
                                        ordered_root);
                root = btrfs_grab_fs_root(root);
@@ -619,11 +629,16 @@ void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info)
                               &fs_info->ordered_roots);
                spin_unlock(&fs_info->ordered_root_lock);
 
-               btrfs_wait_ordered_extents(root);
+               done = btrfs_wait_ordered_extents(root, nr);
                btrfs_put_fs_root(root);
 
                spin_lock(&fs_info->ordered_root_lock);
+               if (nr != -1) {
+                       nr -= done;
+                       WARN_ON(nr < 0);
+               }
        }
+       list_splice_tail(&splice, &fs_info->ordered_roots);
        spin_unlock(&fs_info->ordered_root_lock);
 }
 
@@ -734,8 +749,9 @@ void btrfs_start_ordered_extent(struct inode *inode,
 /*
  * Used to wait on ordered extents across a large range of bytes.
  */
-void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)
+int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)
 {
+       int ret = 0;
        u64 end;
        u64 orig_end;
        struct btrfs_ordered_extent *ordered;
@@ -751,8 +767,9 @@ void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)
        /* start IO across the range first to instantiate any delalloc
         * extents
         */
-       filemap_fdatawrite_range(inode->i_mapping, start, orig_end);
-
+       ret = filemap_fdatawrite_range(inode->i_mapping, start, orig_end);
+       if (ret)
+               return ret;
        /*
         * So with compression we will find and lock a dirty page and clear the
         * first one as dirty, setup an async extent, and immediately return
@@ -768,10 +785,15 @@ void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)
         * right and you are wrong.
         */
        if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
-                    &BTRFS_I(inode)->runtime_flags))
-               filemap_fdatawrite_range(inode->i_mapping, start, orig_end);
-
-       filemap_fdatawait_range(inode->i_mapping, start, orig_end);
+                    &BTRFS_I(inode)->runtime_flags)) {
+               ret = filemap_fdatawrite_range(inode->i_mapping, start,
+                                              orig_end);
+               if (ret)
+                       return ret;
+       }
+       ret = filemap_fdatawait_range(inode->i_mapping, start, orig_end);
+       if (ret)
+               return ret;
 
        end = orig_end;
        while (1) {
@@ -782,17 +804,20 @@ void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)
                        btrfs_put_ordered_extent(ordered);
                        break;
                }
-               if (ordered->file_offset + ordered->len < start) {
+               if (ordered->file_offset + ordered->len <= start) {
                        btrfs_put_ordered_extent(ordered);
                        break;
                }
                btrfs_start_ordered_extent(inode, ordered, 1);
                end = ordered->file_offset;
+               if (test_bit(BTRFS_ORDERED_IOERR, &ordered->flags))
+                       ret = -EIO;
                btrfs_put_ordered_extent(ordered);
-               if (end == 0 || end == start)
+               if (ret || end == 0 || end == start)
                        break;
                end--;
        }
+       return ret;
 }
 
 /*
@@ -1076,7 +1101,7 @@ void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
         * if this file hasn't been changed since the last transaction
         * commit, we can safely return without doing anything
         */
-       if (last_mod < root->fs_info->last_trans_committed)
+       if (last_mod <= root->fs_info->last_trans_committed)
                return;
 
        spin_lock(&root->fs_info->ordered_root_lock);
index 0c0b356..9b0450f 100644 (file)
@@ -180,7 +180,7 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct inode *inode,
                                                         u64 file_offset);
 void btrfs_start_ordered_extent(struct inode *inode,
                                struct btrfs_ordered_extent *entry, int wait);
-void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len);
+int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len);
 struct btrfs_ordered_extent *
 btrfs_lookup_first_ordered_extent(struct inode * inode, u64 file_offset);
 struct btrfs_ordered_extent *btrfs_lookup_ordered_range(struct inode *inode,
@@ -195,8 +195,8 @@ int btrfs_run_ordered_operations(struct btrfs_trans_handle *trans,
 void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
                                 struct btrfs_root *root,
                                 struct inode *inode);
-void btrfs_wait_ordered_extents(struct btrfs_root *root);
-void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info);
+int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr);
+void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr);
 void btrfs_get_logged_extents(struct btrfs_root *log, struct inode *inode);
 void btrfs_wait_logged_extents(struct btrfs_root *log, u64 transid);
 void btrfs_free_logged_extents(struct btrfs_root *log, u64 transid);
index 0088bed..417053b 100644 (file)
@@ -193,7 +193,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
        btrfs_info(root->fs_info, "leaf %llu total ptrs %d free space %d",
                   btrfs_header_bytenr(l), nr, btrfs_leaf_free_space(root, l));
        for (i = 0 ; i < nr ; i++) {
-               item = btrfs_item_nr(l, i);
+               item = btrfs_item_nr(i);
                btrfs_item_key_to_cpu(l, &key, i);
                type = btrfs_key_type(&key);
                printk(KERN_INFO "\titem %d key (%llu %u %llu) itemoff %d "
index d0ecfbd..24ac218 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/raid/xor.h>
 #include <linux/vmalloc.h>
 #include <asm/div64.h>
-#include "compat.h"
 #include "ctree.h"
 #include "extent_map.h"
 #include "disk-io.h"
index 4a35572..ce459a7 100644 (file)
@@ -1383,6 +1383,7 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
 {
        struct btrfs_root *reloc_root;
        struct reloc_control *rc = root->fs_info->reloc_ctl;
+       struct btrfs_block_rsv *rsv;
        int clear_rsv = 0;
        int ret;
 
@@ -1396,13 +1397,14 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
            root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
                return 0;
 
-       if (!trans->block_rsv) {
+       if (!trans->reloc_reserved) {
+               rsv = trans->block_rsv;
                trans->block_rsv = rc->block_rsv;
                clear_rsv = 1;
        }
        reloc_root = create_reloc_root(trans, root, root->root_key.objectid);
        if (clear_rsv)
-               trans->block_rsv = NULL;
+               trans->block_rsv = rsv;
 
        ret = __add_reloc_root(reloc_root);
        BUG_ON(ret < 0);
@@ -1775,8 +1777,7 @@ again:
                        new_ptr_gen = 0;
                }
 
-               if (new_bytenr > 0 && new_bytenr == old_bytenr) {
-                       WARN_ON(1);
+               if (WARN_ON(new_bytenr > 0 && new_bytenr == old_bytenr)) {
                        ret = level;
                        break;
                }
@@ -2058,7 +2059,7 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
        LIST_HEAD(inode_list);
        struct btrfs_key key;
        struct btrfs_key next_key;
-       struct btrfs_trans_handle *trans;
+       struct btrfs_trans_handle *trans = NULL;
        struct btrfs_root *reloc_root;
        struct btrfs_root_item *root_item;
        struct btrfs_path *path;
@@ -2107,18 +2108,19 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
        memset(&next_key, 0, sizeof(next_key));
 
        while (1) {
-               trans = btrfs_start_transaction(root, 0);
-               BUG_ON(IS_ERR(trans));
-               trans->block_rsv = rc->block_rsv;
-
                ret = btrfs_block_rsv_refill(root, rc->block_rsv, min_reserved,
                                             BTRFS_RESERVE_FLUSH_ALL);
                if (ret) {
-                       BUG_ON(ret != -EAGAIN);
-                       ret = btrfs_commit_transaction(trans, root);
-                       BUG_ON(ret);
-                       continue;
+                       err = ret;
+                       goto out;
                }
+               trans = btrfs_start_transaction(root, 0);
+               if (IS_ERR(trans)) {
+                       err = PTR_ERR(trans);
+                       trans = NULL;
+                       goto out;
+               }
+               trans->block_rsv = rc->block_rsv;
 
                replaced = 0;
                max_level = level;
@@ -2164,6 +2166,7 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
                root_item->drop_level = level;
 
                btrfs_end_transaction_throttle(trans, root);
+               trans = NULL;
 
                btrfs_btree_balance_dirty(root);
 
@@ -2192,7 +2195,8 @@ out:
                btrfs_update_reloc_root(trans, root);
        }
 
-       btrfs_end_transaction_throttle(trans, root);
+       if (trans)
+               btrfs_end_transaction_throttle(trans, root);
 
        btrfs_btree_balance_dirty(root);
 
@@ -3258,7 +3262,7 @@ static int add_tree_block(struct reloc_control *rc,
        struct rb_node *rb_node;
        u32 item_size;
        int level = -1;
-       int generation;
+       u64 generation;
 
        eb =  path->nodes[0];
        item_size = btrfs_item_size_nr(eb, path->slots[0]);
@@ -3407,7 +3411,6 @@ static int delete_block_group_cache(struct btrfs_fs_info *fs_info,
                                    struct inode *inode, u64 ino)
 {
        struct btrfs_key key;
-       struct btrfs_path *path;
        struct btrfs_root *root = fs_info->tree_root;
        struct btrfs_trans_handle *trans;
        int ret = 0;
@@ -3432,22 +3435,14 @@ truncate:
        if (ret)
                goto out;
 
-       path = btrfs_alloc_path();
-       if (!path) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
        trans = btrfs_join_transaction(root);
        if (IS_ERR(trans)) {
-               btrfs_free_path(path);
                ret = PTR_ERR(trans);
                goto out;
        }
 
-       ret = btrfs_truncate_free_space_cache(root, trans, path, inode);
+       ret = btrfs_truncate_free_space_cache(root, trans, inode);
 
-       btrfs_free_path(path);
        btrfs_end_transaction(trans, root);
        btrfs_btree_balance_dirty(root);
 out:
@@ -3549,10 +3544,8 @@ static int find_data_references(struct reloc_control *rc,
                                err = ret;
                                goto out;
                        }
-                       if (ret > 0) {
-                               WARN_ON(1);
+                       if (WARN_ON(ret > 0))
                                goto out;
-                       }
 
                        leaf = path->nodes[0];
                        nritems = btrfs_header_nritems(leaf);
@@ -3572,11 +3565,9 @@ static int find_data_references(struct reloc_control *rc,
                }
 
                btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
-               if (key.objectid != ref_objectid ||
-                   key.type != BTRFS_EXTENT_DATA_KEY) {
-                       WARN_ON(1);
+               if (WARN_ON(key.objectid != ref_objectid ||
+                   key.type != BTRFS_EXTENT_DATA_KEY))
                        break;
-               }
 
                fi = btrfs_item_ptr(leaf, path->slots[0],
                                    struct btrfs_file_extent_item);
@@ -4001,16 +3992,6 @@ restart:
                        }
                }
 
-               ret = btrfs_block_rsv_check(rc->extent_root, rc->block_rsv, 5);
-               if (ret < 0) {
-                       if (ret != -ENOSPC) {
-                               err = ret;
-                               WARN_ON(1);
-                               break;
-                       }
-                       rc->commit_transaction = 1;
-               }
-
                if (rc->commit_transaction) {
                        rc->commit_transaction = 0;
                        ret = btrfs_commit_transaction(trans, rc->extent_root);
@@ -4241,12 +4222,12 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
        printk(KERN_INFO "btrfs: relocating block group %llu flags %llu\n",
               rc->block_group->key.objectid, rc->block_group->flags);
 
-       ret = btrfs_start_all_delalloc_inodes(fs_info, 0);
+       ret = btrfs_start_delalloc_roots(fs_info, 0);
        if (ret < 0) {
                err = ret;
                goto out;
        }
-       btrfs_wait_all_ordered_extents(fs_info);
+       btrfs_wait_ordered_roots(fs_info, -1);
 
        while (1) {
                mutex_lock(&fs_info->cleaner_mutex);
@@ -4264,7 +4245,12 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
                        rc->extents_found);
 
                if (rc->stage == MOVE_DATA_EXTENTS && rc->found_file_extent) {
-                       btrfs_wait_ordered_range(rc->data_inode, 0, (u64)-1);
+                       ret = btrfs_wait_ordered_range(rc->data_inode, 0,
+                                                      (u64)-1);
+                       if (ret) {
+                               err = ret;
+                               goto out;
+                       }
                        invalidate_mapping_pages(rc->data_inode->i_mapping,
                                                 0, -1);
                        rc->stage = UPDATE_DATA_PTRS;
@@ -4481,6 +4467,7 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
        struct btrfs_root *root = BTRFS_I(inode)->root;
        int ret;
        u64 disk_bytenr;
+       u64 new_bytenr;
        LIST_HEAD(list);
 
        ordered = btrfs_lookup_ordered_extent(inode, file_pos);
@@ -4492,13 +4479,24 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
        if (ret)
                goto out;
 
-       disk_bytenr = ordered->start;
        while (!list_empty(&list)) {
                sums = list_entry(list.next, struct btrfs_ordered_sum, list);
                list_del_init(&sums->list);
 
-               sums->bytenr = disk_bytenr;
-               disk_bytenr += sums->len;
+               /*
+                * We need to offset the new_bytenr based on where the csum is.
+                * We need to do this because we will read in entire prealloc
+                * extents but we may have written to say the middle of the
+                * prealloc extent, so we need to make sure the csum goes with
+                * the right disk offset.
+                *
+                * We can do this because the data reloc inode refers strictly
+                * to the on disk bytes, so we don't have to worry about
+                * disk_len vs real len like with real inodes since it's all
+                * disk length.
+                */
+               new_bytenr = ordered->start + (sums->bytenr - disk_bytenr);
+               sums->bytenr = new_bytenr;
 
                btrfs_add_ordered_sum(inode, ordered, sums);
        }
index a18e0e2..561e2f1 100644 (file)
@@ -938,8 +938,10 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check)
                                BTRFS_DEV_STAT_CORRUPTION_ERRS);
        }
 
-       if (sctx->readonly && !sctx->is_dev_replace)
-               goto did_not_correct_error;
+       if (sctx->readonly) {
+               ASSERT(!sctx->is_dev_replace);
+               goto out;
+       }
 
        if (!is_metadata && !have_csum) {
                struct scrub_fixup_nodatasum *fixup_nodatasum;
@@ -2717,8 +2719,6 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
                mutex_unlock(&fs_info->scrub_lock);
                wake_up(&fs_info->scrub_pause_wait);
 
-               dev_replace->cursor_left = dev_replace->cursor_right;
-               dev_replace->item_needs_writeback = 1;
                btrfs_put_block_group(cache);
                if (ret)
                        break;
@@ -2732,6 +2732,9 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
                        break;
                }
 
+               dev_replace->cursor_left = dev_replace->cursor_right;
+               dev_replace->item_needs_writeback = 1;
+
                key.offset = found_key.offset + length;
                btrfs_release_path(path);
        }
@@ -2783,7 +2786,6 @@ static noinline_for_stack int scrub_workers_get(struct btrfs_fs_info *fs_info,
 {
        int ret = 0;
 
-       mutex_lock(&fs_info->scrub_lock);
        if (fs_info->scrub_workers_refcnt == 0) {
                if (is_dev_replace)
                        btrfs_init_workers(&fs_info->scrub_workers, "scrub", 1,
@@ -2813,21 +2815,17 @@ static noinline_for_stack int scrub_workers_get(struct btrfs_fs_info *fs_info,
        }
        ++fs_info->scrub_workers_refcnt;
 out:
-       mutex_unlock(&fs_info->scrub_lock);
-
        return ret;
 }
 
 static noinline_for_stack void scrub_workers_put(struct btrfs_fs_info *fs_info)
 {
-       mutex_lock(&fs_info->scrub_lock);
        if (--fs_info->scrub_workers_refcnt == 0) {
                btrfs_stop_workers(&fs_info->scrub_workers);
                btrfs_stop_workers(&fs_info->scrub_wr_completion_workers);
                btrfs_stop_workers(&fs_info->scrub_nocow_workers);
        }
        WARN_ON(fs_info->scrub_workers_refcnt < 0);
-       mutex_unlock(&fs_info->scrub_lock);
 }
 
 int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
@@ -2888,23 +2886,18 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
                return -EINVAL;
        }
 
-       ret = scrub_workers_get(fs_info, is_dev_replace);
-       if (ret)
-               return ret;
 
        mutex_lock(&fs_info->fs_devices->device_list_mutex);
        dev = btrfs_find_device(fs_info, devid, NULL, NULL);
        if (!dev || (dev->missing && !is_dev_replace)) {
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
-               scrub_workers_put(fs_info);
                return -ENODEV;
        }
-       mutex_lock(&fs_info->scrub_lock);
 
+       mutex_lock(&fs_info->scrub_lock);
        if (!dev->in_fs_metadata || dev->is_tgtdev_for_dev_replace) {
                mutex_unlock(&fs_info->scrub_lock);
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
-               scrub_workers_put(fs_info);
                return -EIO;
        }
 
@@ -2915,10 +2908,17 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
                btrfs_dev_replace_unlock(&fs_info->dev_replace);
                mutex_unlock(&fs_info->scrub_lock);
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
-               scrub_workers_put(fs_info);
                return -EINPROGRESS;
        }
        btrfs_dev_replace_unlock(&fs_info->dev_replace);
+
+       ret = scrub_workers_get(fs_info, is_dev_replace);
+       if (ret) {
+               mutex_unlock(&fs_info->scrub_lock);
+               mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+               return ret;
+       }
+
        sctx = scrub_setup_ctx(dev, is_dev_replace);
        if (IS_ERR(sctx)) {
                mutex_unlock(&fs_info->scrub_lock);
@@ -2931,13 +2931,15 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
 
        atomic_inc(&fs_info->scrubs_running);
        mutex_unlock(&fs_info->scrub_lock);
-       mutex_unlock(&fs_info->fs_devices->device_list_mutex);
 
        if (!is_dev_replace) {
-               down_read(&fs_info->scrub_super_lock);
+               /*
+                * by holding device list mutex, we can
+                * kick off writing super in log tree sync.
+                */
                ret = scrub_supers(sctx, dev);
-               up_read(&fs_info->scrub_super_lock);
        }
+       mutex_unlock(&fs_info->fs_devices->device_list_mutex);
 
        if (!ret)
                ret = scrub_enumerate_chunks(sctx, dev, start, end,
@@ -2954,10 +2956,10 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
 
        mutex_lock(&fs_info->scrub_lock);
        dev->scrub_device = NULL;
+       scrub_workers_put(fs_info);
        mutex_unlock(&fs_info->scrub_lock);
 
        scrub_free_ctx(sctx);
-       scrub_workers_put(fs_info);
 
        return ret;
 }
@@ -2987,16 +2989,6 @@ void btrfs_scrub_continue(struct btrfs_root *root)
        wake_up(&fs_info->scrub_pause_wait);
 }
 
-void btrfs_scrub_pause_super(struct btrfs_root *root)
-{
-       down_write(&root->fs_info->scrub_super_lock);
-}
-
-void btrfs_scrub_continue_super(struct btrfs_root *root)
-{
-       up_write(&root->fs_info->scrub_super_lock);
-}
-
 int btrfs_scrub_cancel(struct btrfs_fs_info *fs_info)
 {
        mutex_lock(&fs_info->scrub_lock);
index e46e0ed..6837fe8 100644 (file)
@@ -121,7 +121,6 @@ struct send_ctx {
        struct list_head name_cache_list;
        int name_cache_size;
 
-       struct file *cur_inode_filp;
        char *read_buf;
 };
 
@@ -565,10 +564,8 @@ static int begin_cmd(struct send_ctx *sctx, int cmd)
 {
        struct btrfs_cmd_header *hdr;
 
-       if (!sctx->send_buf) {
-               WARN_ON(1);
+       if (WARN_ON(!sctx->send_buf))
                return -EINVAL;
-       }
 
        BUG_ON(sctx->send_size);
 
@@ -791,7 +788,7 @@ static int iterate_inode_ref(struct btrfs_root *root, struct btrfs_path *path,
        if (found_key->type == BTRFS_INODE_REF_KEY) {
                ptr = (unsigned long)btrfs_item_ptr(eb, slot,
                                                    struct btrfs_inode_ref);
-               item = btrfs_item_nr(eb, slot);
+               item = btrfs_item_nr(slot);
                total = btrfs_item_size(eb, item);
                elem_size = sizeof(*iref);
        } else {
@@ -905,7 +902,7 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
 
        eb = path->nodes[0];
        slot = path->slots[0];
-       item = btrfs_item_nr(eb, slot);
+       item = btrfs_item_nr(slot);
        di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
        cur = 0;
        len = 0;
@@ -2120,77 +2117,6 @@ out:
 }
 
 /*
- * Called for regular files when sending extents data. Opens a struct file
- * to read from the file.
- */
-static int open_cur_inode_file(struct send_ctx *sctx)
-{
-       int ret = 0;
-       struct btrfs_key key;
-       struct path path;
-       struct inode *inode;
-       struct dentry *dentry;
-       struct file *filp;
-       int new = 0;
-
-       if (sctx->cur_inode_filp)
-               goto out;
-
-       key.objectid = sctx->cur_ino;
-       key.type = BTRFS_INODE_ITEM_KEY;
-       key.offset = 0;
-
-       inode = btrfs_iget(sctx->send_root->fs_info->sb, &key, sctx->send_root,
-                       &new);
-       if (IS_ERR(inode)) {
-               ret = PTR_ERR(inode);
-               goto out;
-       }
-
-       dentry = d_obtain_alias(inode);
-       inode = NULL;
-       if (IS_ERR(dentry)) {
-               ret = PTR_ERR(dentry);
-               goto out;
-       }
-
-       path.mnt = sctx->mnt;
-       path.dentry = dentry;
-       filp = dentry_open(&path, O_RDONLY | O_LARGEFILE, current_cred());
-       dput(dentry);
-       dentry = NULL;
-       if (IS_ERR(filp)) {
-               ret = PTR_ERR(filp);
-               goto out;
-       }
-       sctx->cur_inode_filp = filp;
-
-out:
-       /*
-        * no xxxput required here as every vfs op
-        * does it by itself on failure
-        */
-       return ret;
-}
-
-/*
- * Closes the struct file that was created in open_cur_inode_file
- */
-static int close_cur_inode_file(struct send_ctx *sctx)
-{
-       int ret = 0;
-
-       if (!sctx->cur_inode_filp)
-               goto out;
-
-       ret = filp_close(sctx->cur_inode_filp, NULL);
-       sctx->cur_inode_filp = NULL;
-
-out:
-       return ret;
-}
-
-/*
  * Sends a BTRFS_SEND_C_SUBVOL command/item to userspace
  */
 static int send_subvol_begin(struct send_ctx *sctx)
@@ -3622,6 +3548,72 @@ out:
        return ret;
 }
 
+static ssize_t fill_read_buf(struct send_ctx *sctx, u64 offset, u32 len)
+{
+       struct btrfs_root *root = sctx->send_root;
+       struct btrfs_fs_info *fs_info = root->fs_info;
+       struct inode *inode;
+       struct page *page;
+       char *addr;
+       struct btrfs_key key;
+       pgoff_t index = offset >> PAGE_CACHE_SHIFT;
+       pgoff_t last_index;
+       unsigned pg_offset = offset & ~PAGE_CACHE_MASK;
+       ssize_t ret = 0;
+
+       key.objectid = sctx->cur_ino;
+       key.type = BTRFS_INODE_ITEM_KEY;
+       key.offset = 0;
+
+       inode = btrfs_iget(fs_info->sb, &key, root, NULL);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+
+       if (offset + len > i_size_read(inode)) {
+               if (offset > i_size_read(inode))
+                       len = 0;
+               else
+                       len = offset - i_size_read(inode);
+       }
+       if (len == 0)
+               goto out;
+
+       last_index = (offset + len - 1) >> PAGE_CACHE_SHIFT;
+       while (index <= last_index) {
+               unsigned cur_len = min_t(unsigned, len,
+                                        PAGE_CACHE_SIZE - pg_offset);
+               page = find_or_create_page(inode->i_mapping, index, GFP_NOFS);
+               if (!page) {
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               if (!PageUptodate(page)) {
+                       btrfs_readpage(NULL, page);
+                       lock_page(page);
+                       if (!PageUptodate(page)) {
+                               unlock_page(page);
+                               page_cache_release(page);
+                               ret = -EIO;
+                               break;
+                       }
+               }
+
+               addr = kmap(page);
+               memcpy(sctx->read_buf + ret, addr + pg_offset, cur_len);
+               kunmap(page);
+               unlock_page(page);
+               page_cache_release(page);
+               index++;
+               pg_offset = 0;
+               len -= cur_len;
+               ret += cur_len;
+       }
+out:
+       iput(inode);
+       return ret;
+}
+
 /*
  * Read some bytes from the current inode/file and send a write command to
  * user space.
@@ -3630,35 +3622,20 @@ static int send_write(struct send_ctx *sctx, u64 offset, u32 len)
 {
        int ret = 0;
        struct fs_path *p;
-       loff_t pos = offset;
-       int num_read = 0;
-       mm_segment_t old_fs;
+       ssize_t num_read = 0;
 
        p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
-       /*
-        * vfs normally only accepts user space buffers for security reasons.
-        * we only read from the file and also only provide the read_buf buffer
-        * to vfs. As this buffer does not come from a user space call, it's
-        * ok to temporary allow kernel space buffers.
-        */
-       old_fs = get_fs();
-       set_fs(KERNEL_DS);
-
 verbose_printk("btrfs: send_write offset=%llu, len=%d\n", offset, len);
 
-       ret = open_cur_inode_file(sctx);
-       if (ret < 0)
-               goto out;
-
-       ret = vfs_read(sctx->cur_inode_filp, sctx->read_buf, len, &pos);
-       if (ret < 0)
-               goto out;
-       num_read = ret;
-       if (!num_read)
+       num_read = fill_read_buf(sctx, offset, len);
+       if (num_read <= 0) {
+               if (num_read < 0)
+                       ret = num_read;
                goto out;
+       }
 
        ret = begin_cmd(sctx, BTRFS_SEND_C_WRITE);
        if (ret < 0)
@@ -3677,7 +3654,6 @@ verbose_printk("btrfs: send_write offset=%llu, len=%d\n", offset, len);
 tlv_put_failure:
 out:
        fs_path_free(p);
-       set_fs(old_fs);
        if (ret < 0)
                return ret;
        return num_read;
@@ -3926,16 +3902,16 @@ static int is_extent_unchanged(struct send_ctx *sctx,
        while (key.offset < ekey->offset + left_len) {
                ei = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
                right_type = btrfs_file_extent_type(eb, ei);
-               right_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
-               right_len = btrfs_file_extent_num_bytes(eb, ei);
-               right_offset = btrfs_file_extent_offset(eb, ei);
-               right_gen = btrfs_file_extent_generation(eb, ei);
-
                if (right_type != BTRFS_FILE_EXTENT_REG) {
                        ret = 0;
                        goto out;
                }
 
+               right_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
+               right_len = btrfs_file_extent_num_bytes(eb, ei);
+               right_offset = btrfs_file_extent_offset(eb, ei);
+               right_gen = btrfs_file_extent_generation(eb, ei);
+
                /*
                 * Are we at extent 8? If yes, we know the extent is changed.
                 * This may only happen on the first iteration.
@@ -4222,10 +4198,6 @@ static int changed_inode(struct send_ctx *sctx,
        u64 left_gen = 0;
        u64 right_gen = 0;
 
-       ret = close_cur_inode_file(sctx);
-       if (ret < 0)
-               goto out;
-
        sctx->cur_ino = key->objectid;
        sctx->cur_inode_new_gen = 0;
 
@@ -4686,11 +4658,6 @@ static int send_subvol(struct send_ctx *sctx)
        }
 
 out:
-       if (!ret)
-               ret = close_cur_inode_file(sctx);
-       else
-               close_cur_inode_file(sctx);
-
        free_recorded_refs(sctx);
        return ret;
 }
index e913328..2d8ac1b 100644 (file)
@@ -42,7 +42,6 @@
 #include <linux/cleancache.h>
 #include <linux/ratelimit.h>
 #include <linux/btrfs.h>
-#include "compat.h"
 #include "delayed-inode.h"
 #include "ctree.h"
 #include "disk-io.h"
@@ -921,7 +920,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait)
                return 0;
        }
 
-       btrfs_wait_all_ordered_extents(fs_info);
+       btrfs_wait_ordered_roots(fs_info, -1);
 
        trans = btrfs_attach_transaction_barrier(root);
        if (IS_ERR(trans)) {
@@ -1330,6 +1329,12 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
                 * this also happens on 'umount -rf' or on shutdown, when
                 * the filesystem is busy.
                 */
+
+               /* wait for the uuid_scan task to finish */
+               down(&fs_info->uuid_tree_rescan_sem);
+               /* avoid complains from lockdep et al. */
+               up(&fs_info->uuid_tree_rescan_sem);
+
                sb->s_flags |= MS_RDONLY;
 
                btrfs_dev_replace_suspend_for_unmount(fs_info);
@@ -1465,7 +1470,7 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes)
        nr_devices = fs_info->fs_devices->open_devices;
        BUG_ON(!nr_devices);
 
-       devices_info = kmalloc(sizeof(*devices_info) * nr_devices,
+       devices_info = kmalloc_array(nr_devices, sizeof(*devices_info),
                               GFP_NOFS);
        if (!devices_info)
                return -ENOMEM;
@@ -1789,7 +1794,25 @@ static void btrfs_print_info(void)
 
 static int btrfs_run_sanity_tests(void)
 {
-       return btrfs_test_free_space_cache();
+       int ret;
+
+       ret = btrfs_init_test_fs();
+       if (ret)
+               return ret;
+
+       ret = btrfs_test_free_space_cache();
+       if (ret)
+               goto out;
+       ret = btrfs_test_extent_buffer_operations();
+       if (ret)
+               goto out;
+       ret = btrfs_test_extent_io();
+       if (ret)
+               goto out;
+       ret = btrfs_test_inodes();
+out:
+       btrfs_destroy_test_fs();
+       return ret;
 }
 
 static int __init init_btrfs_fs(void)
diff --git a/fs/btrfs/tests/btrfs-tests.c b/fs/btrfs/tests/btrfs-tests.c
new file mode 100644 (file)
index 0000000..757ef00
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 Fusion IO.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/magic.h>
+#include "btrfs-tests.h"
+#include "../ctree.h"
+
+static struct vfsmount *test_mnt = NULL;
+
+static const struct super_operations btrfs_test_super_ops = {
+       .alloc_inode    = btrfs_alloc_inode,
+       .destroy_inode  = btrfs_test_destroy_inode,
+};
+
+static struct dentry *btrfs_test_mount(struct file_system_type *fs_type,
+                                      int flags, const char *dev_name,
+                                      void *data)
+{
+       return mount_pseudo(fs_type, "btrfs_test:", &btrfs_test_super_ops,
+                           NULL, BTRFS_TEST_MAGIC);
+}
+
+static struct file_system_type test_type = {
+       .name           = "btrfs_test_fs",
+       .mount          = btrfs_test_mount,
+       .kill_sb        = kill_anon_super,
+};
+
+struct inode *btrfs_new_test_inode(void)
+{
+       return new_inode(test_mnt->mnt_sb);
+}
+
+int btrfs_init_test_fs(void)
+{
+       int ret;
+
+       ret = register_filesystem(&test_type);
+       if (ret) {
+               printk(KERN_ERR "btrfs: cannot register test file system\n");
+               return ret;
+       }
+
+       test_mnt = kern_mount(&test_type);
+       if (IS_ERR(test_mnt)) {
+               printk(KERN_ERR "btrfs: cannot mount test file system\n");
+               unregister_filesystem(&test_type);
+               return ret;
+       }
+       return 0;
+}
+
+void btrfs_destroy_test_fs(void)
+{
+       kern_unmount(test_mnt);
+       unregister_filesystem(&test_type);
+}
index 5808776..b353bc8 100644 (file)
 #define test_msg(fmt, ...) pr_info("btrfs: selftest: " fmt, ##__VA_ARGS__)
 
 int btrfs_test_free_space_cache(void);
+int btrfs_test_extent_buffer_operations(void);
+int btrfs_test_extent_io(void);
+int btrfs_test_inodes(void);
+int btrfs_init_test_fs(void);
+void btrfs_destroy_test_fs(void);
+struct inode *btrfs_new_test_inode(void);
 #else
 static inline int btrfs_test_free_space_cache(void)
 {
        return 0;
 }
+static inline int btrfs_test_extent_buffer_operations(void)
+{
+       return 0;
+}
+static inline int btrfs_init_test_fs(void)
+{
+       return 0;
+}
+static inline void btrfs_destroy_test_fs(void)
+{
+}
+static inline int btrfs_test_extent_io(void)
+{
+       return 0;
+}
+static inline int btrfs_test_inodes(void)
+{
+       return 0;
+}
 #endif
 
 #endif
diff --git a/fs/btrfs/tests/extent-buffer-tests.c b/fs/btrfs/tests/extent-buffer-tests.c
new file mode 100644 (file)
index 0000000..cc286ce
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2013 Fusion IO.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <linux/slab.h>
+#include "btrfs-tests.h"
+#include "../ctree.h"
+#include "../extent_io.h"
+#include "../disk-io.h"
+
+static int test_btrfs_split_item(void)
+{
+       struct btrfs_path *path;
+       struct btrfs_root *root;
+       struct extent_buffer *eb;
+       struct btrfs_item *item;
+       char *value = "mary had a little lamb";
+       char *split1 = "mary had a little";
+       char *split2 = " lamb";
+       char *split3 = "mary";
+       char *split4 = " had a little";
+       char buf[32];
+       struct btrfs_key key;
+       u32 value_len = strlen(value);
+       int ret = 0;
+
+       test_msg("Running btrfs_split_item tests\n");
+
+       root = btrfs_alloc_dummy_root();
+       if (IS_ERR(root)) {
+               test_msg("Could not allocate root\n");
+               return PTR_ERR(root);
+       }
+
+       path = btrfs_alloc_path();
+       if (!path) {
+               test_msg("Could not allocate path\n");
+               kfree(root);
+               return -ENOMEM;
+       }
+
+       path->nodes[0] = eb = alloc_dummy_extent_buffer(0, 4096);
+       if (!eb) {
+               test_msg("Could not allocate dummy buffer\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+       path->slots[0] = 0;
+
+       key.objectid = 0;
+       key.type = BTRFS_EXTENT_CSUM_KEY;
+       key.offset = 0;
+
+       setup_items_for_insert(root, path, &key, &value_len, value_len,
+                              value_len + sizeof(struct btrfs_item), 1);
+       item = btrfs_item_nr(0);
+       write_extent_buffer(eb, value, btrfs_item_ptr_offset(eb, 0),
+                           value_len);
+
+       key.offset = 3;
+
+       /*
+        * Passing NULL trans here should be safe because we have plenty of
+        * space in this leaf to split the item without having to split the
+        * leaf.
+        */
+       ret = btrfs_split_item(NULL, root, path, &key, 17);
+       if (ret) {
+               test_msg("Split item failed %d\n", ret);
+               goto out;
+       }
+
+       /*
+        * Read the first slot, it should have the original key and contain only
+        * 'mary had a little'
+        */
+       btrfs_item_key_to_cpu(eb, &key, 0);
+       if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
+           key.offset != 0) {
+               test_msg("Invalid key at slot 0\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       item = btrfs_item_nr(0);
+       if (btrfs_item_size(eb, item) != strlen(split1)) {
+               test_msg("Invalid len in the first split\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0),
+                          strlen(split1));
+       if (memcmp(buf, split1, strlen(split1))) {
+               test_msg("Data in the buffer doesn't match what it should "
+                        "in the first split have='%.*s' want '%s'\n",
+                        (int)strlen(split1), buf, split1);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       btrfs_item_key_to_cpu(eb, &key, 1);
+       if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
+           key.offset != 3) {
+               test_msg("Invalid key at slot 1\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       item = btrfs_item_nr(1);
+       if (btrfs_item_size(eb, item) != strlen(split2)) {
+               test_msg("Invalid len in the second split\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1),
+                          strlen(split2));
+       if (memcmp(buf, split2, strlen(split2))) {
+               test_msg("Data in the buffer doesn't match what it should "
+                        "in the second split\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       key.offset = 1;
+       /* Do it again so we test memmoving the other items in the leaf */
+       ret = btrfs_split_item(NULL, root, path, &key, 4);
+       if (ret) {
+               test_msg("Second split item failed %d\n", ret);
+               goto out;
+       }
+
+       btrfs_item_key_to_cpu(eb, &key, 0);
+       if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
+           key.offset != 0) {
+               test_msg("Invalid key at slot 0\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       item = btrfs_item_nr(0);
+       if (btrfs_item_size(eb, item) != strlen(split3)) {
+               test_msg("Invalid len in the first split\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0),
+                          strlen(split3));
+       if (memcmp(buf, split3, strlen(split3))) {
+               test_msg("Data in the buffer doesn't match what it should "
+                        "in the third split");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       btrfs_item_key_to_cpu(eb, &key, 1);
+       if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
+           key.offset != 1) {
+               test_msg("Invalid key at slot 1\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       item = btrfs_item_nr(1);
+       if (btrfs_item_size(eb, item) != strlen(split4)) {
+               test_msg("Invalid len in the second split\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1),
+                          strlen(split4));
+       if (memcmp(buf, split4, strlen(split4))) {
+               test_msg("Data in the buffer doesn't match what it should "
+                        "in the fourth split\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       btrfs_item_key_to_cpu(eb, &key, 2);
+       if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
+           key.offset != 3) {
+               test_msg("Invalid key at slot 2\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       item = btrfs_item_nr(2);
+       if (btrfs_item_size(eb, item) != strlen(split2)) {
+               test_msg("Invalid len in the second split\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 2),
+                          strlen(split2));
+       if (memcmp(buf, split2, strlen(split2))) {
+               test_msg("Data in the buffer doesn't match what it should "
+                        "in the last chunk\n");
+               ret = -EINVAL;
+               goto out;
+       }
+out:
+       btrfs_free_path(path);
+       kfree(root);
+       return ret;
+}
+
+int btrfs_test_extent_buffer_operations(void)
+{
+       test_msg("Running extent buffer operation tests");
+       return test_btrfs_split_item();
+}
diff --git a/fs/btrfs/tests/extent-io-tests.c b/fs/btrfs/tests/extent-io-tests.c
new file mode 100644 (file)
index 0000000..7e99c2f
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2013 Fusion IO.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include "btrfs-tests.h"
+#include "../extent_io.h"
+
+#define PROCESS_UNLOCK         (1 << 0)
+#define PROCESS_RELEASE                (1 << 1)
+#define PROCESS_TEST_LOCKED    (1 << 2)
+
+static noinline int process_page_range(struct inode *inode, u64 start, u64 end,
+                                      unsigned long flags)
+{
+       int ret;
+       struct page *pages[16];
+       unsigned long index = start >> PAGE_CACHE_SHIFT;
+       unsigned long end_index = end >> PAGE_CACHE_SHIFT;
+       unsigned long nr_pages = end_index - index + 1;
+       int i;
+       int count = 0;
+       int loops = 0;
+
+       while (nr_pages > 0) {
+               ret = find_get_pages_contig(inode->i_mapping, index,
+                                    min_t(unsigned long, nr_pages,
+                                    ARRAY_SIZE(pages)), pages);
+               for (i = 0; i < ret; i++) {
+                       if (flags & PROCESS_TEST_LOCKED &&
+                           !PageLocked(pages[i]))
+                               count++;
+                       if (flags & PROCESS_UNLOCK && PageLocked(pages[i]))
+                               unlock_page(pages[i]);
+                       page_cache_release(pages[i]);
+                       if (flags & PROCESS_RELEASE)
+                               page_cache_release(pages[i]);
+               }
+               nr_pages -= ret;
+               index += ret;
+               cond_resched();
+               loops++;
+               if (loops > 100000) {
+                       printk(KERN_ERR "stuck in a loop, start %Lu, end %Lu, nr_pages %lu, ret %d\n", start, end, nr_pages, ret);
+                       break;
+               }
+       }
+       return count;
+}
+
+static int test_find_delalloc(void)
+{
+       struct inode *inode;
+       struct extent_io_tree tmp;
+       struct page *page;
+       struct page *locked_page = NULL;
+       unsigned long index = 0;
+       u64 total_dirty = 256 * 1024 * 1024;
+       u64 max_bytes = 128 * 1024 * 1024;
+       u64 start, end, test_start;
+       u64 found;
+       int ret = -EINVAL;
+
+       inode = btrfs_new_test_inode();
+       if (!inode) {
+               test_msg("Failed to allocate test inode\n");
+               return -ENOMEM;
+       }
+
+       extent_io_tree_init(&tmp, &inode->i_data);
+
+       /*
+        * First go through and create and mark all of our pages dirty, we pin
+        * everything to make sure our pages don't get evicted and screw up our
+        * test.
+        */
+       for (index = 0; index < (total_dirty >> PAGE_CACHE_SHIFT); index++) {
+               page = find_or_create_page(inode->i_mapping, index, GFP_NOFS);
+               if (!page) {
+                       test_msg("Failed to allocate test page\n");
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               SetPageDirty(page);
+               if (index) {
+                       unlock_page(page);
+               } else {
+                       page_cache_get(page);
+                       locked_page = page;
+               }
+       }
+
+       /* Test this scenario
+        * |--- delalloc ---|
+        * |---  search  ---|
+        */
+       set_extent_delalloc(&tmp, 0, 4095, NULL, GFP_NOFS);
+       start = 0;
+       end = 0;
+       found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
+                                        &end, max_bytes);
+       if (!found) {
+               test_msg("Should have found at least one delalloc\n");
+               goto out_bits;
+       }
+       if (start != 0 || end != 4095) {
+               test_msg("Expected start 0 end 4095, got start %Lu end %Lu\n",
+                        start, end);
+               goto out_bits;
+       }
+       unlock_extent(&tmp, start, end);
+       unlock_page(locked_page);
+       page_cache_release(locked_page);
+
+       /*
+        * Test this scenario
+        *
+        * |--- delalloc ---|
+        *           |--- search ---|
+        */
+       test_start = 64 * 1024 * 1024;
+       locked_page = find_lock_page(inode->i_mapping,
+                                    test_start >> PAGE_CACHE_SHIFT);
+       if (!locked_page) {
+               test_msg("Couldn't find the locked page\n");
+               goto out_bits;
+       }
+       set_extent_delalloc(&tmp, 4096, max_bytes - 1, NULL, GFP_NOFS);
+       start = test_start;
+       end = 0;
+       found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
+                                        &end, max_bytes);
+       if (!found) {
+               test_msg("Couldn't find delalloc in our range\n");
+               goto out_bits;
+       }
+       if (start != test_start || end != max_bytes - 1) {
+               test_msg("Expected start %Lu end %Lu, got start %Lu, end "
+                        "%Lu\n", test_start, max_bytes - 1, start, end);
+               goto out_bits;
+       }
+       if (process_page_range(inode, start, end,
+                              PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) {
+               test_msg("There were unlocked pages in the range\n");
+               goto out_bits;
+       }
+       unlock_extent(&tmp, start, end);
+       /* locked_page was unlocked above */
+       page_cache_release(locked_page);
+
+       /*
+        * Test this scenario
+        * |--- delalloc ---|
+        *                    |--- search ---|
+        */
+       test_start = max_bytes + 4096;
+       locked_page = find_lock_page(inode->i_mapping, test_start >>
+                                    PAGE_CACHE_SHIFT);
+       if (!locked_page) {
+               test_msg("Could'nt find the locked page\n");
+               goto out_bits;
+       }
+       start = test_start;
+       end = 0;
+       found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
+                                        &end, max_bytes);
+       if (found) {
+               test_msg("Found range when we shouldn't have\n");
+               goto out_bits;
+       }
+       if (end != (u64)-1) {
+               test_msg("Did not return the proper end offset\n");
+               goto out_bits;
+       }
+
+       /*
+        * Test this scenario
+        * [------- delalloc -------|
+        * [max_bytes]|-- search--|
+        *
+        * We are re-using our test_start from above since it works out well.
+        */
+       set_extent_delalloc(&tmp, max_bytes, total_dirty - 1, NULL, GFP_NOFS);
+       start = test_start;
+       end = 0;
+       found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
+                                        &end, max_bytes);
+       if (!found) {
+               test_msg("Didn't find our range\n");
+               goto out_bits;
+       }
+       if (start != test_start || end != total_dirty - 1) {
+               test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n",
+                        test_start, total_dirty - 1, start, end);
+               goto out_bits;
+       }
+       if (process_page_range(inode, start, end,
+                              PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) {
+               test_msg("Pages in range were not all locked\n");
+               goto out_bits;
+       }
+       unlock_extent(&tmp, start, end);
+
+       /*
+        * Now to test where we run into a page that is no longer dirty in the
+        * range we want to find.
+        */
+       page = find_get_page(inode->i_mapping, (max_bytes + (1 * 1024 * 1024))
+                            >> PAGE_CACHE_SHIFT);
+       if (!page) {
+               test_msg("Couldn't find our page\n");
+               goto out_bits;
+       }
+       ClearPageDirty(page);
+       page_cache_release(page);
+
+       /* We unlocked it in the previous test */
+       lock_page(locked_page);
+       start = test_start;
+       end = 0;
+       /*
+        * Currently if we fail to find dirty pages in the delalloc range we
+        * will adjust max_bytes down to PAGE_CACHE_SIZE and then re-search.  If
+        * this changes at any point in the future we will need to fix this
+        * tests expected behavior.
+        */
+       found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
+                                        &end, max_bytes);
+       if (!found) {
+               test_msg("Didn't find our range\n");
+               goto out_bits;
+       }
+       if (start != test_start && end != test_start + PAGE_CACHE_SIZE - 1) {
+               test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n",
+                        test_start, test_start + PAGE_CACHE_SIZE - 1, start,
+                        end);
+               goto out_bits;
+       }
+       if (process_page_range(inode, start, end, PROCESS_TEST_LOCKED |
+                              PROCESS_UNLOCK)) {
+               test_msg("Pages in range were not all locked\n");
+               goto out_bits;
+       }
+       ret = 0;
+out_bits:
+       clear_extent_bits(&tmp, 0, total_dirty - 1,
+                         (unsigned long)-1, GFP_NOFS);
+out:
+       if (locked_page)
+               page_cache_release(locked_page);
+       process_page_range(inode, 0, total_dirty - 1,
+                          PROCESS_UNLOCK | PROCESS_RELEASE);
+       iput(inode);
+       return ret;
+}
+
+int btrfs_test_extent_io(void)
+{
+       test_msg("Running find delalloc tests\n");
+       return test_find_delalloc();
+}
diff --git a/fs/btrfs/tests/inode-tests.c b/fs/btrfs/tests/inode-tests.c
new file mode 100644 (file)
index 0000000..397d1f9
--- /dev/null
@@ -0,0 +1,955 @@
+/*
+ * Copyright (C) 2013 Fusion IO.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include "btrfs-tests.h"
+#include "../ctree.h"
+#include "../btrfs_inode.h"
+#include "../disk-io.h"
+#include "../extent_io.h"
+#include "../volumes.h"
+
+static struct btrfs_fs_info *alloc_dummy_fs_info(void)
+{
+       struct btrfs_fs_info *fs_info = kzalloc(sizeof(struct btrfs_fs_info),
+                                               GFP_NOFS);
+       if (!fs_info)
+               return fs_info;
+       fs_info->fs_devices = kzalloc(sizeof(struct btrfs_fs_devices),
+                                     GFP_NOFS);
+       if (!fs_info->fs_devices) {
+               kfree(fs_info);
+               return NULL;
+       }
+       return fs_info;
+}
+static void free_dummy_root(struct btrfs_root *root)
+{
+       if (!root)
+               return;
+       if (root->fs_info) {
+               kfree(root->fs_info->fs_devices);
+               kfree(root->fs_info);
+       }
+       if (root->node)
+               free_extent_buffer(root->node);
+       kfree(root);
+}
+
+static void insert_extent(struct btrfs_root *root, u64 start, u64 len,
+                         u64 ram_bytes, u64 offset, u64 disk_bytenr,
+                         u64 disk_len, u32 type, u8 compression, int slot)
+{
+       struct btrfs_path path;
+       struct btrfs_file_extent_item *fi;
+       struct extent_buffer *leaf = root->node;
+       struct btrfs_key key;
+       u32 value_len = sizeof(struct btrfs_file_extent_item);
+
+       if (type == BTRFS_FILE_EXTENT_INLINE)
+               value_len += len;
+       memset(&path, 0, sizeof(path));
+
+       path.nodes[0] = leaf;
+       path.slots[0] = slot;
+
+       key.objectid = BTRFS_FIRST_FREE_OBJECTID;
+       key.type = BTRFS_EXTENT_DATA_KEY;
+       key.offset = start;
+
+       setup_items_for_insert(root, &path, &key, &value_len, value_len,
+                              value_len + sizeof(struct btrfs_item), 1);
+       fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+       btrfs_set_file_extent_generation(leaf, fi, 1);
+       btrfs_set_file_extent_type(leaf, fi, type);
+       btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr);
+       btrfs_set_file_extent_disk_num_bytes(leaf, fi, disk_len);
+       btrfs_set_file_extent_offset(leaf, fi, offset);
+       btrfs_set_file_extent_num_bytes(leaf, fi, len);
+       btrfs_set_file_extent_ram_bytes(leaf, fi, ram_bytes);
+       btrfs_set_file_extent_compression(leaf, fi, compression);
+       btrfs_set_file_extent_encryption(leaf, fi, 0);
+       btrfs_set_file_extent_other_encoding(leaf, fi, 0);
+}
+
+static void insert_inode_item_key(struct btrfs_root *root)
+{
+       struct btrfs_path path;
+       struct extent_buffer *leaf = root->node;
+       struct btrfs_key key;
+       u32 value_len = 0;
+
+       memset(&path, 0, sizeof(path));
+
+       path.nodes[0] = leaf;
+       path.slots[0] = 0;
+
+       key.objectid = BTRFS_INODE_ITEM_KEY;
+       key.type = BTRFS_INODE_ITEM_KEY;
+       key.offset = 0;
+
+       setup_items_for_insert(root, &path, &key, &value_len, value_len,
+                              value_len + sizeof(struct btrfs_item), 1);
+}
+
+/*
+ * Build the most complicated map of extents the earth has ever seen.  We want
+ * this so we can test all of the corner cases of btrfs_get_extent.  Here is a
+ * diagram of how the extents will look though this may not be possible we still
+ * want to make sure everything acts normally (the last number is not inclusive)
+ *
+ * [0 - 5][5 -  6][6 - 10][10 - 4096][  4096 - 8192 ][8192 - 12288]
+ * [hole ][inline][ hole ][ regular ][regular1 split][    hole    ]
+ *
+ * [ 12288 - 20480][20480 - 24576][  24576 - 28672  ][28672 - 36864][36864 - 45056]
+ * [regular1 split][   prealloc1 ][prealloc1 written][   prealloc1 ][ compressed  ]
+ *
+ * [45056 - 49152][49152-53248][53248-61440][61440-65536][     65536+81920   ]
+ * [ compressed1 ][  regular  ][compressed1][  regular  ][ hole but no extent]
+ *
+ * [81920-86016]
+ * [  regular  ]
+ */
+static void setup_file_extents(struct btrfs_root *root)
+{
+       int slot = 0;
+       u64 disk_bytenr = 1 * 1024 * 1024;
+       u64 offset = 0;
+
+       /* First we want a hole */
+       insert_extent(root, offset, 5, 5, 0, 0, 0, BTRFS_FILE_EXTENT_REG, 0,
+                     slot);
+       slot++;
+       offset += 5;
+
+       /*
+        * Now we want an inline extent, I don't think this is possible but hey
+        * why not?  Also keep in mind if we have an inline extent it counts as
+        * the whole first page.  If we were to expand it we would have to cow
+        * and we wouldn't have an inline extent anymore.
+        */
+       insert_extent(root, offset, 1, 1, 0, 0, 0, BTRFS_FILE_EXTENT_INLINE, 0,
+                     slot);
+       slot++;
+       offset = 4096;
+
+       /* Now another hole */
+       insert_extent(root, offset, 4, 4, 0, 0, 0, BTRFS_FILE_EXTENT_REG, 0,
+                     slot);
+       slot++;
+       offset += 4;
+
+       /* Now for a regular extent */
+       insert_extent(root, offset, 4095, 4095, 0, disk_bytenr, 4096,
+                     BTRFS_FILE_EXTENT_REG, 0, slot);
+       slot++;
+       disk_bytenr += 4096;
+       offset += 4095;
+
+       /*
+        * Now for 3 extents that were split from a hole punch so we test
+        * offsets properly.
+        */
+       insert_extent(root, offset, 4096, 16384, 0, disk_bytenr, 16384,
+                     BTRFS_FILE_EXTENT_REG, 0, slot);
+       slot++;
+       offset += 4096;
+       insert_extent(root, offset, 4096, 4096, 0, 0, 0, BTRFS_FILE_EXTENT_REG,
+                     0, slot);
+       slot++;
+       offset += 4096;
+       insert_extent(root, offset, 8192, 16384, 8192, disk_bytenr, 16384,
+                     BTRFS_FILE_EXTENT_REG, 0, slot);
+       slot++;
+       offset += 8192;
+       disk_bytenr += 16384;
+
+       /* Now for a unwritten prealloc extent */
+       insert_extent(root, offset, 4096, 4096, 0, disk_bytenr, 4096,
+                     BTRFS_FILE_EXTENT_PREALLOC, 0, slot);
+       slot++;
+       offset += 4096;
+
+       /*
+        * We want to jack up disk_bytenr a little more so the em stuff doesn't
+        * merge our records.
+        */
+       disk_bytenr += 8192;
+
+       /*
+        * Now for a partially written prealloc extent, basically the same as
+        * the hole punch example above.  Ram_bytes never changes when you mark
+        * extents written btw.
+        */
+       insert_extent(root, offset, 4096, 16384, 0, disk_bytenr, 16384,
+                     BTRFS_FILE_EXTENT_PREALLOC, 0, slot);
+       slot++;
+       offset += 4096;
+       insert_extent(root, offset, 4096, 16384, 4096, disk_bytenr, 16384,
+                     BTRFS_FILE_EXTENT_REG, 0, slot);
+       slot++;
+       offset += 4096;
+       insert_extent(root, offset, 8192, 16384, 8192, disk_bytenr, 16384,
+                     BTRFS_FILE_EXTENT_PREALLOC, 0, slot);
+       slot++;
+       offset += 8192;
+       disk_bytenr += 16384;
+
+       /* Now a normal compressed extent */
+       insert_extent(root, offset, 8192, 8192, 0, disk_bytenr, 4096,
+                     BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot);
+       slot++;
+       offset += 8192;
+       /* No merges */
+       disk_bytenr += 8192;
+
+       /* Now a split compressed extent */
+       insert_extent(root, offset, 4096, 16384, 0, disk_bytenr, 4096,
+                     BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot);
+       slot++;
+       offset += 4096;
+       insert_extent(root, offset, 4096, 4096, 0, disk_bytenr + 4096, 4096,
+                     BTRFS_FILE_EXTENT_REG, 0, slot);
+       slot++;
+       offset += 4096;
+       insert_extent(root, offset, 8192, 16384, 8192, disk_bytenr, 4096,
+                     BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot);
+       slot++;
+       offset += 8192;
+       disk_bytenr += 8192;
+
+       /* Now extents that have a hole but no hole extent */
+       insert_extent(root, offset, 4096, 4096, 0, disk_bytenr, 4096,
+                     BTRFS_FILE_EXTENT_REG, 0, slot);
+       slot++;
+       offset += 16384;
+       disk_bytenr += 4096;
+       insert_extent(root, offset, 4096, 4096, 0, disk_bytenr, 4096,
+                     BTRFS_FILE_EXTENT_REG, 0, slot);
+}
+
+static unsigned long prealloc_only = 0;
+static unsigned long compressed_only = 0;
+static unsigned long vacancy_only = 0;
+
+static noinline int test_btrfs_get_extent(void)
+{
+       struct inode *inode = NULL;
+       struct btrfs_root *root = NULL;
+       struct extent_map *em = NULL;
+       u64 orig_start;
+       u64 disk_bytenr;
+       u64 offset;
+       int ret = -ENOMEM;
+
+       inode = btrfs_new_test_inode();
+       if (!inode) {
+               test_msg("Couldn't allocate inode\n");
+               return ret;
+       }
+
+       BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
+       BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID;
+       BTRFS_I(inode)->location.offset = 0;
+
+       root = btrfs_alloc_dummy_root();
+       if (IS_ERR(root)) {
+               test_msg("Couldn't allocate root\n");
+               goto out;
+       }
+
+       /*
+        * We do this since btrfs_get_extent wants to assign em->bdev to
+        * root->fs_info->fs_devices->latest_bdev.
+        */
+       root->fs_info = alloc_dummy_fs_info();
+       if (!root->fs_info) {
+               test_msg("Couldn't allocate dummy fs info\n");
+               goto out;
+       }
+
+       root->node = alloc_dummy_extent_buffer(0, 4096);
+       if (!root->node) {
+               test_msg("Couldn't allocate dummy buffer\n");
+               goto out;
+       }
+
+       /*
+        * We will just free a dummy node if it's ref count is 2 so we need an
+        * extra ref so our searches don't accidently release our page.
+        */
+       extent_buffer_get(root->node);
+       btrfs_set_header_nritems(root->node, 0);
+       btrfs_set_header_level(root->node, 0);
+       ret = -EINVAL;
+
+       /* First with no extents */
+       BTRFS_I(inode)->root = root;
+       em = btrfs_get_extent(inode, NULL, 0, 0, 4096, 0);
+       if (IS_ERR(em)) {
+               em = NULL;
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start != EXTENT_MAP_HOLE) {
+               test_msg("Expected a hole, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (!test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
+               test_msg("Vacancy flag wasn't set properly\n");
+               goto out;
+       }
+       free_extent_map(em);
+       btrfs_drop_extent_cache(inode, 0, (u64)-1, 0);
+
+       /*
+        * All of the magic numbers are based on the mapping setup in
+        * setup_file_extents, so if you change anything there you need to
+        * update the comment and update the expected values below.
+        */
+       setup_file_extents(root);
+
+       em = btrfs_get_extent(inode, NULL, 0, 0, (u64)-1, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start != EXTENT_MAP_HOLE) {
+               test_msg("Expected a hole, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != 0 || em->len != 5) {
+               test_msg("Unexpected extent wanted start 0 len 5, got start "
+                        "%llu len %llu\n", em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start != EXTENT_MAP_INLINE) {
+               test_msg("Expected an inline, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4091) {
+               test_msg("Unexpected extent wanted start %llu len 1, got start "
+                        "%llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       /*
+        * We don't test anything else for inline since it doesn't get set
+        * unless we have a page for it to write into.  Maybe we should change
+        * this?
+        */
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start != EXTENT_MAP_HOLE) {
+               test_msg("Expected a hole, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4) {
+               test_msg("Unexpected extent wanted start %llu len 4, got start "
+                        "%llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       /* Regular extent */
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4095) {
+               test_msg("Unexpected extent wanted start %llu len 4095, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       if (em->orig_start != em->start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n", em->start,
+                        em->orig_start);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       /* The next 3 are split extents */
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4096) {
+               test_msg("Unexpected extent wanted start %llu len 4096, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       if (em->orig_start != em->start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n", em->start,
+                        em->orig_start);
+               goto out;
+       }
+       disk_bytenr = em->block_start;
+       orig_start = em->start;
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start != EXTENT_MAP_HOLE) {
+               test_msg("Expected a hole, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4096) {
+               test_msg("Unexpected extent wanted start %llu len 4096, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 8192) {
+               test_msg("Unexpected extent wanted start %llu len 8192, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       if (em->orig_start != orig_start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n",
+                        orig_start, em->orig_start);
+               goto out;
+       }
+       disk_bytenr += (em->start - orig_start);
+       if (em->block_start != disk_bytenr) {
+               test_msg("Wrong block start, want %llu, have %llu\n",
+                        disk_bytenr, em->block_start);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       /* Prealloc extent */
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4096) {
+               test_msg("Unexpected extent wanted start %llu len 4096, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != prealloc_only) {
+               test_msg("Unexpected flags set, want %lu have %lu\n",
+                        prealloc_only, em->flags);
+               goto out;
+       }
+       if (em->orig_start != em->start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n", em->start,
+                        em->orig_start);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       /* The next 3 are a half written prealloc extent */
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4096) {
+               test_msg("Unexpected extent wanted start %llu len 4096, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != prealloc_only) {
+               test_msg("Unexpected flags set, want %lu have %lu\n",
+                        prealloc_only, em->flags);
+               goto out;
+       }
+       if (em->orig_start != em->start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n", em->start,
+                        em->orig_start);
+               goto out;
+       }
+       disk_bytenr = em->block_start;
+       orig_start = em->start;
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_HOLE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4096) {
+               test_msg("Unexpected extent wanted start %llu len 4096, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       if (em->orig_start != orig_start) {
+               test_msg("Unexpected orig offset, wanted %llu, have %llu\n",
+                        orig_start, em->orig_start);
+               goto out;
+       }
+       if (em->block_start != (disk_bytenr + (em->start - em->orig_start))) {
+               test_msg("Unexpected block start, wanted %llu, have %llu\n",
+                        disk_bytenr + (em->start - em->orig_start),
+                        em->block_start);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 8192) {
+               test_msg("Unexpected extent wanted start %llu len 8192, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != prealloc_only) {
+               test_msg("Unexpected flags set, want %lu have %lu\n",
+                        prealloc_only, em->flags);
+               goto out;
+       }
+       if (em->orig_start != orig_start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n", orig_start,
+                        em->orig_start);
+               goto out;
+       }
+       if (em->block_start != (disk_bytenr + (em->start - em->orig_start))) {
+               test_msg("Unexpected block start, wanted %llu, have %llu\n",
+                        disk_bytenr + (em->start - em->orig_start),
+                        em->block_start);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       /* Now for the compressed extent */
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 8192) {
+               test_msg("Unexpected extent wanted start %llu len 8192, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != compressed_only) {
+               test_msg("Unexpected flags set, want %lu have %lu\n",
+                        compressed_only, em->flags);
+               goto out;
+       }
+       if (em->orig_start != em->start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n",
+                        em->start, em->orig_start);
+               goto out;
+       }
+       if (em->compress_type != BTRFS_COMPRESS_ZLIB) {
+               test_msg("Unexpected compress type, wanted %d, got %d\n",
+                        BTRFS_COMPRESS_ZLIB, em->compress_type);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       /* Split compressed extent */
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4096) {
+               test_msg("Unexpected extent wanted start %llu len 4096, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != compressed_only) {
+               test_msg("Unexpected flags set, want %lu have %lu\n",
+                        compressed_only, em->flags);
+               goto out;
+       }
+       if (em->orig_start != em->start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n",
+                        em->start, em->orig_start);
+               goto out;
+       }
+       if (em->compress_type != BTRFS_COMPRESS_ZLIB) {
+               test_msg("Unexpected compress type, wanted %d, got %d\n",
+                        BTRFS_COMPRESS_ZLIB, em->compress_type);
+               goto out;
+       }
+       disk_bytenr = em->block_start;
+       orig_start = em->start;
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4096) {
+               test_msg("Unexpected extent wanted start %llu len 4096, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       if (em->orig_start != em->start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n", em->start,
+                        em->orig_start);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start != disk_bytenr) {
+               test_msg("Block start does not match, want %llu got %llu\n",
+                        disk_bytenr, em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 8192) {
+               test_msg("Unexpected extent wanted start %llu len 8192, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != compressed_only) {
+               test_msg("Unexpected flags set, want %lu have %lu\n",
+                        compressed_only, em->flags);
+               goto out;
+       }
+       if (em->orig_start != orig_start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n",
+                        em->start, orig_start);
+               goto out;
+       }
+       if (em->compress_type != BTRFS_COMPRESS_ZLIB) {
+               test_msg("Unexpected compress type, wanted %d, got %d\n",
+                        BTRFS_COMPRESS_ZLIB, em->compress_type);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       /* A hole between regular extents but no hole extent */
+       em = btrfs_get_extent(inode, NULL, 0, offset + 6, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4096) {
+               test_msg("Unexpected extent wanted start %llu len 4096, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       if (em->orig_start != em->start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n", em->start,
+                        em->orig_start);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096 * 1024, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start != EXTENT_MAP_HOLE) {
+               test_msg("Expected a hole extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       /*
+        * Currently we just return a length that we requested rather than the
+        * length of the actual hole, if this changes we'll have to change this
+        * test.
+        */
+       if (em->start != offset || em->len != 12288) {
+               test_msg("Unexpected extent wanted start %llu len 12288, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != vacancy_only) {
+               test_msg("Unexpected flags set, want %lu have %lu\n",
+                        vacancy_only, em->flags);
+               goto out;
+       }
+       if (em->orig_start != em->start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n", em->start,
+                        em->orig_start);
+               goto out;
+       }
+       offset = em->start + em->len;
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != offset || em->len != 4096) {
+               test_msg("Unexpected extent wanted start %llu len 4096, got "
+                        "start %llu len %llu\n", offset, em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, want 0 have %lu\n", em->flags);
+               goto out;
+       }
+       if (em->orig_start != em->start) {
+               test_msg("Wrong orig offset, want %llu, have %llu\n", em->start,
+                        em->orig_start);
+               goto out;
+       }
+       ret = 0;
+out:
+       if (!IS_ERR(em))
+               free_extent_map(em);
+       iput(inode);
+       free_dummy_root(root);
+       return ret;
+}
+
+static int test_hole_first(void)
+{
+       struct inode *inode = NULL;
+       struct btrfs_root *root = NULL;
+       struct extent_map *em = NULL;
+       int ret = -ENOMEM;
+
+       inode = btrfs_new_test_inode();
+       if (!inode) {
+               test_msg("Couldn't allocate inode\n");
+               return ret;
+       }
+
+       BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
+       BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID;
+       BTRFS_I(inode)->location.offset = 0;
+
+       root = btrfs_alloc_dummy_root();
+       if (IS_ERR(root)) {
+               test_msg("Couldn't allocate root\n");
+               goto out;
+       }
+
+       root->fs_info = alloc_dummy_fs_info();
+       if (!root->fs_info) {
+               test_msg("Couldn't allocate dummy fs info\n");
+               goto out;
+       }
+
+       root->node = alloc_dummy_extent_buffer(0, 4096);
+       if (!root->node) {
+               test_msg("Couldn't allocate dummy buffer\n");
+               goto out;
+       }
+
+       extent_buffer_get(root->node);
+       btrfs_set_header_nritems(root->node, 0);
+       btrfs_set_header_level(root->node, 0);
+       BTRFS_I(inode)->root = root;
+       ret = -EINVAL;
+
+       /*
+        * Need a blank inode item here just so we don't confuse
+        * btrfs_get_extent.
+        */
+       insert_inode_item_key(root);
+       insert_extent(root, 4096, 4096, 4096, 0, 4096, 4096,
+                     BTRFS_FILE_EXTENT_REG, 0, 1);
+       em = btrfs_get_extent(inode, NULL, 0, 0, 8192, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start != EXTENT_MAP_HOLE) {
+               test_msg("Expected a hole, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != 0 || em->len != 4096) {
+               test_msg("Unexpected extent wanted start 0 len 4096, got start "
+                        "%llu len %llu\n", em->start, em->len);
+               goto out;
+       }
+       if (em->flags != vacancy_only) {
+               test_msg("Wrong flags, wanted %lu, have %lu\n", vacancy_only,
+                        em->flags);
+               goto out;
+       }
+       free_extent_map(em);
+
+       em = btrfs_get_extent(inode, NULL, 0, 4096, 8192, 0);
+       if (IS_ERR(em)) {
+               test_msg("Got an error when we shouldn't have\n");
+               goto out;
+       }
+       if (em->block_start != 4096) {
+               test_msg("Expected a real extent, got %llu\n", em->block_start);
+               goto out;
+       }
+       if (em->start != 4096 || em->len != 4096) {
+               test_msg("Unexpected extent wanted start 4096 len 4096, got "
+                        "start %llu len %llu\n", em->start, em->len);
+               goto out;
+       }
+       if (em->flags != 0) {
+               test_msg("Unexpected flags set, wanted 0 got %lu\n",
+                        em->flags);
+               goto out;
+       }
+       ret = 0;
+out:
+       if (!IS_ERR(em))
+               free_extent_map(em);
+       iput(inode);
+       free_dummy_root(root);
+       return ret;
+}
+
+int btrfs_test_inodes(void)
+{
+       int ret;
+
+       set_bit(EXTENT_FLAG_COMPRESSED, &compressed_only);
+       set_bit(EXTENT_FLAG_VACANCY, &vacancy_only);
+       set_bit(EXTENT_FLAG_PREALLOC, &prealloc_only);
+
+       test_msg("Running btrfs_get_extent tests\n");
+       ret = test_btrfs_get_extent();
+       if (ret)
+               return ret;
+       test_msg("Running hole first btrfs_get_extent test\n");
+       return test_hole_first();
+}
index 8c81bdc..c6a872a 100644 (file)
@@ -57,7 +57,7 @@ static unsigned int btrfs_blocked_trans_types[TRANS_STATE_MAX] = {
                                           __TRANS_JOIN_NOLOCK),
 };
 
-static void put_transaction(struct btrfs_transaction *transaction)
+void btrfs_put_transaction(struct btrfs_transaction *transaction)
 {
        WARN_ON(atomic_read(&transaction->use_count) == 0);
        if (atomic_dec_and_test(&transaction->use_count)) {
@@ -332,7 +332,7 @@ static void wait_current_trans(struct btrfs_root *root)
                wait_event(root->fs_info->transaction_wait,
                           cur_trans->state >= TRANS_STATE_UNBLOCKED ||
                           cur_trans->aborted);
-               put_transaction(cur_trans);
+               btrfs_put_transaction(cur_trans);
        } else {
                spin_unlock(&root->fs_info->trans_lock);
        }
@@ -353,6 +353,17 @@ static int may_wait_transaction(struct btrfs_root *root, int type)
        return 0;
 }
 
+static inline bool need_reserve_reloc_root(struct btrfs_root *root)
+{
+       if (!root->fs_info->reloc_ctl ||
+           !root->ref_cows ||
+           root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID ||
+           root->reloc_root)
+               return false;
+
+       return true;
+}
+
 static struct btrfs_trans_handle *
 start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type,
                  enum btrfs_reserve_flush_enum flush)
@@ -360,8 +371,9 @@ start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type,
        struct btrfs_trans_handle *h;
        struct btrfs_transaction *cur_trans;
        u64 num_bytes = 0;
-       int ret;
        u64 qgroup_reserved = 0;
+       bool reloc_reserved = false;
+       int ret;
 
        if (test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state))
                return ERR_PTR(-EROFS);
@@ -390,6 +402,14 @@ start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type,
                }
 
                num_bytes = btrfs_calc_trans_metadata_size(root, num_items);
+               /*
+                * Do the reservation for the relocation root creation
+                */
+               if (unlikely(need_reserve_reloc_root(root))) {
+                       num_bytes += root->nodesize;
+                       reloc_reserved = true;
+               }
+
                ret = btrfs_block_rsv_add(root,
                                          &root->fs_info->trans_block_rsv,
                                          num_bytes, flush);
@@ -451,6 +471,7 @@ again:
        h->delayed_ref_elem.seq = 0;
        h->type = type;
        h->allocating_chunk = false;
+       h->reloc_reserved = false;
        INIT_LIST_HEAD(&h->qgroup_ref_list);
        INIT_LIST_HEAD(&h->new_bgs);
 
@@ -466,6 +487,7 @@ again:
                                              h->transid, num_bytes, 1);
                h->block_rsv = &root->fs_info->trans_block_rsv;
                h->bytes_reserved = num_bytes;
+               h->reloc_reserved = reloc_reserved;
        }
        h->qgroup_reserved = qgroup_reserved;
 
@@ -610,7 +632,7 @@ int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid)
        }
 
        wait_for_commit(root, cur_trans);
-       put_transaction(cur_trans);
+       btrfs_put_transaction(cur_trans);
 out:
        return ret;
 }
@@ -735,7 +757,7 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
        smp_mb();
        if (waitqueue_active(&cur_trans->writer_wait))
                wake_up(&cur_trans->writer_wait);
-       put_transaction(cur_trans);
+       btrfs_put_transaction(cur_trans);
 
        if (current->journal_info == trans)
                current->journal_info = NULL;
@@ -744,8 +766,10 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
                btrfs_run_delayed_iputs(root);
 
        if (trans->aborted ||
-           test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state))
+           test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state)) {
+               wake_up_process(info->transaction_kthread);
                err = -EIO;
+       }
        assert_qgroups_uptodate(trans);
 
        kmem_cache_free(btrfs_trans_handle_cachep, trans);
@@ -948,16 +972,19 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
                return ret;
 
        ret = btrfs_run_dev_stats(trans, root->fs_info);
-       WARN_ON(ret);
+       if (ret)
+               return ret;
        ret = btrfs_run_dev_replace(trans, root->fs_info);
-       WARN_ON(ret);
-
+       if (ret)
+               return ret;
        ret = btrfs_run_qgroups(trans, root->fs_info);
-       BUG_ON(ret);
+       if (ret)
+               return ret;
 
        /* run_qgroups might have added some more refs */
        ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
-       BUG_ON(ret);
+       if (ret)
+               return ret;
 
        while (!list_empty(&fs_info->dirty_cowonly_roots)) {
                next = fs_info->dirty_cowonly_roots.next;
@@ -1453,7 +1480,7 @@ static void do_async_commit(struct work_struct *work)
         * We've got freeze protection passed with the transaction.
         * Tell lockdep about it.
         */
-       if (ac->newtrans->type < TRANS_JOIN_NOLOCK)
+       if (ac->newtrans->type & __TRANS_FREEZABLE)
                rwsem_acquire_read(
                     &ac->root->fs_info->sb->s_writers.lock_map[SB_FREEZE_FS-1],
                     0, 1, _THIS_IP_);
@@ -1494,7 +1521,7 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,
         * Tell lockdep we've released the freeze rwsem, since the
         * async commit thread will be the one to unlock it.
         */
-       if (trans->type < TRANS_JOIN_NOLOCK)
+       if (ac->newtrans->type & __TRANS_FREEZABLE)
                rwsem_release(
                        &root->fs_info->sb->s_writers.lock_map[SB_FREEZE_FS-1],
                        1, _THIS_IP_);
@@ -1510,7 +1537,7 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,
        if (current->journal_info == trans)
                current->journal_info = NULL;
 
-       put_transaction(cur_trans);
+       btrfs_put_transaction(cur_trans);
        return 0;
 }
 
@@ -1552,8 +1579,10 @@ static void cleanup_transaction(struct btrfs_trans_handle *trans,
                root->fs_info->running_transaction = NULL;
        spin_unlock(&root->fs_info->trans_lock);
 
-       put_transaction(cur_trans);
-       put_transaction(cur_trans);
+       if (trans->type & __TRANS_FREEZABLE)
+               sb_end_intwrite(root->fs_info->sb);
+       btrfs_put_transaction(cur_trans);
+       btrfs_put_transaction(cur_trans);
 
        trace_btrfs_transaction_commit(root);
 
@@ -1571,15 +1600,19 @@ static int btrfs_flush_all_pending_stuffs(struct btrfs_trans_handle *trans,
        int ret;
 
        ret = btrfs_run_delayed_items(trans, root);
-       if (ret)
-               return ret;
-
        /*
         * running the delayed items may have added new refs. account
         * them now so that they hinder processing of more delayed refs
         * as little as possible.
         */
-       btrfs_delayed_refs_qgroup_accounting(trans, root->fs_info);
+       if (ret) {
+               btrfs_delayed_refs_qgroup_accounting(trans, root->fs_info);
+               return ret;
+       }
+
+       ret = btrfs_delayed_refs_qgroup_accounting(trans, root->fs_info);
+       if (ret)
+               return ret;
 
        /*
         * rename don't use btrfs_join_transaction, so, once we
@@ -1596,14 +1629,14 @@ static int btrfs_flush_all_pending_stuffs(struct btrfs_trans_handle *trans,
 static inline int btrfs_start_delalloc_flush(struct btrfs_fs_info *fs_info)
 {
        if (btrfs_test_opt(fs_info->tree_root, FLUSHONCOMMIT))
-               return btrfs_start_all_delalloc_inodes(fs_info, 1);
+               return btrfs_start_delalloc_roots(fs_info, 1);
        return 0;
 }
 
 static inline void btrfs_wait_delalloc_flush(struct btrfs_fs_info *fs_info)
 {
        if (btrfs_test_opt(fs_info->tree_root, FLUSHONCOMMIT))
-               btrfs_wait_all_ordered_extents(fs_info);
+               btrfs_wait_ordered_roots(fs_info, -1);
 }
 
 int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
@@ -1669,7 +1702,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
 
                wait_for_commit(root, cur_trans);
 
-               put_transaction(cur_trans);
+               btrfs_put_transaction(cur_trans);
 
                return ret;
        }
@@ -1686,7 +1719,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
 
                        wait_for_commit(root, prev_trans);
 
-                       put_transaction(prev_trans);
+                       btrfs_put_transaction(prev_trans);
                } else {
                        spin_unlock(&root->fs_info->trans_lock);
                }
@@ -1885,8 +1918,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        list_del_init(&cur_trans->list);
        spin_unlock(&root->fs_info->trans_lock);
 
-       put_transaction(cur_trans);
-       put_transaction(cur_trans);
+       btrfs_put_transaction(cur_trans);
+       btrfs_put_transaction(cur_trans);
 
        if (trans->type & __TRANS_FREEZABLE)
                sb_end_intwrite(root->fs_info->sb);
index 5c2af84..7657d11 100644 (file)
@@ -92,6 +92,7 @@ struct btrfs_trans_handle {
        short aborted;
        short adding_csums;
        bool allocating_chunk;
+       bool reloc_reserved;
        unsigned int type;
        /*
         * this root is only needed to validate that the root passed to
@@ -166,4 +167,5 @@ int btrfs_wait_marked_extents(struct btrfs_root *root,
                                struct extent_io_tree *dirty_pages, int mark);
 int btrfs_transaction_blocked(struct btrfs_fs_info *info);
 int btrfs_transaction_in_commit(struct btrfs_fs_info *info);
+void btrfs_put_transaction(struct btrfs_transaction *transaction);
 #endif
index 94e05c1..76928ca 100644 (file)
@@ -37,7 +37,6 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
        int ret = 0;
        int wret;
        int level;
-       int is_extent = 0;
        int next_key_ret = 0;
        u64 last_ret = 0;
        u64 min_trans = 0;
@@ -50,7 +49,7 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
                goto out;
        }
 
-       if (root->ref_cows == 0 && !is_extent)
+       if (root->ref_cows == 0)
                goto out;
 
        if (btrfs_test_opt(root, SSD))
@@ -85,7 +84,7 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
 
        path->keep_locks = 1;
 
-       ret = btrfs_search_forward(root, &key, NULL, path, min_trans);
+       ret = btrfs_search_forward(root, &key, path, min_trans);
        if (ret < 0)
                goto out;
        if (ret > 0) {
index 79f057c..9f7fc51 100644 (file)
@@ -26,7 +26,6 @@
 #include "locking.h"
 #include "print-tree.h"
 #include "backref.h"
-#include "compat.h"
 #include "tree-log.h"
 #include "hash.h"
 
@@ -936,7 +935,7 @@ again:
                                            parent_objectid,
                                            victim_name,
                                            victim_name_len)) {
-                               btrfs_inc_nlink(inode);
+                               inc_nlink(inode);
                                btrfs_release_path(path);
 
                                ret = btrfs_unlink_inode(trans, root, dir,
@@ -1006,7 +1005,7 @@ again:
                                victim_parent = read_one_inode(root,
                                                               parent_objectid);
                                if (victim_parent) {
-                                       btrfs_inc_nlink(inode);
+                                       inc_nlink(inode);
                                        btrfs_release_path(path);
 
                                        ret = btrfs_unlink_inode(trans, root,
@@ -1113,11 +1112,11 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
                                  struct extent_buffer *eb, int slot,
                                  struct btrfs_key *key)
 {
-       struct inode *dir;
-       struct inode *inode;
+       struct inode *dir = NULL;
+       struct inode *inode = NULL;
        unsigned long ref_ptr;
        unsigned long ref_end;
-       char *name;
+       char *name = NULL;
        int namelen;
        int ret;
        int search_done = 0;
@@ -1150,13 +1149,15 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
         * care of the rest
         */
        dir = read_one_inode(root, parent_objectid);
-       if (!dir)
-               return -ENOENT;
+       if (!dir) {
+               ret = -ENOENT;
+               goto out;
+       }
 
        inode = read_one_inode(root, inode_objectid);
        if (!inode) {
-               iput(dir);
-               return -EIO;
+               ret = -EIO;
+               goto out;
        }
 
        while (ref_ptr < ref_end) {
@@ -1169,14 +1170,16 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
                         */
                        if (!dir)
                                dir = read_one_inode(root, parent_objectid);
-                       if (!dir)
-                               return -ENOENT;
+                       if (!dir) {
+                               ret = -ENOENT;
+                               goto out;
+                       }
                } else {
                        ret = ref_get_fields(eb, ref_ptr, &namelen, &name,
                                             &ref_index);
                }
                if (ret)
-                       return ret;
+                       goto out;
 
                /* if we already have a perfect match, we're done */
                if (!inode_in_dir(root, path, btrfs_ino(dir), btrfs_ino(inode),
@@ -1196,12 +1199,11 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
                                                      parent_objectid,
                                                      ref_index, name, namelen,
                                                      &search_done);
-                               if (ret == 1) {
-                                       ret = 0;
+                               if (ret) {
+                                       if (ret == 1)
+                                               ret = 0;
                                        goto out;
                                }
-                               if (ret)
-                                       goto out;
                        }
 
                        /* insert our name */
@@ -1215,6 +1217,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
 
                ref_ptr = (unsigned long)(ref_ptr + ref_struct_size) + namelen;
                kfree(name);
+               name = NULL;
                if (log_ref_ver) {
                        iput(dir);
                        dir = NULL;
@@ -1225,6 +1228,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
        ret = overwrite_item(trans, root, path, eb, slot, key);
 out:
        btrfs_release_path(path);
+       kfree(name);
        iput(dir);
        iput(inode);
        return ret;
@@ -1307,6 +1311,7 @@ static int count_inode_refs(struct btrfs_root *root,
                                break;
                        path->slots[0]--;
                }
+process_slot:
                btrfs_item_key_to_cpu(path->nodes[0], &key,
                                      path->slots[0]);
                if (key.objectid != ino ||
@@ -1327,6 +1332,10 @@ static int count_inode_refs(struct btrfs_root *root,
 
                if (key.offset == 0)
                        break;
+               if (path->slots[0] > 0) {
+                       path->slots[0]--;
+                       goto process_slot;
+               }
                key.offset--;
                btrfs_release_path(path);
        }
@@ -1480,7 +1489,7 @@ static noinline int link_to_fixup_dir(struct btrfs_trans_handle *trans,
                if (!inode->i_nlink)
                        set_nlink(inode, 1);
                else
-                       btrfs_inc_nlink(inode);
+                       inc_nlink(inode);
                ret = btrfs_update_inode(trans, root, inode);
        } else if (ret == -EEXIST) {
                ret = 0;
@@ -1823,7 +1832,7 @@ again:
                                                     dir_key->offset,
                                                     name, name_len, 0);
                }
-               if (IS_ERR_OR_NULL(log_di)) {
+               if (!log_di || (IS_ERR(log_di) && PTR_ERR(log_di) == -ENOENT)) {
                        btrfs_dir_item_key_to_cpu(eb, di, &location);
                        btrfs_release_path(path);
                        btrfs_release_path(log_path);
@@ -1841,7 +1850,7 @@ again:
                                goto out;
                        }
 
-                       btrfs_inc_nlink(inode);
+                       inc_nlink(inode);
                        ret = btrfs_unlink_inode(trans, root, dir, inode,
                                                 name, name_len);
                        if (!ret)
@@ -1860,6 +1869,9 @@ again:
                                goto again;
                        ret = 0;
                        goto out;
+               } else if (IS_ERR(log_di)) {
+                       kfree(name);
+                       return PTR_ERR(log_di);
                }
                btrfs_release_path(log_path);
                kfree(name);
@@ -2118,8 +2130,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                WARN_ON(*level >= BTRFS_MAX_LEVEL);
                cur = path->nodes[*level];
 
-               if (btrfs_header_level(cur) != *level)
-                       WARN_ON(1);
+               WARN_ON(btrfs_header_level(cur) != *level);
 
                if (path->slots[*level] >=
                    btrfs_header_nritems(cur))
@@ -2151,11 +2162,13 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                                        return ret;
                                }
 
-                               btrfs_tree_lock(next);
-                               btrfs_set_lock_blocking(next);
-                               clean_tree_block(trans, root, next);
-                               btrfs_wait_tree_block_writeback(next);
-                               btrfs_tree_unlock(next);
+                               if (trans) {
+                                       btrfs_tree_lock(next);
+                                       btrfs_set_lock_blocking(next);
+                                       clean_tree_block(trans, root, next);
+                                       btrfs_wait_tree_block_writeback(next);
+                                       btrfs_tree_unlock(next);
+                               }
 
                                WARN_ON(root_owner !=
                                        BTRFS_TREE_LOG_OBJECTID);
@@ -2227,11 +2240,13 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
 
                                next = path->nodes[*level];
 
-                               btrfs_tree_lock(next);
-                               btrfs_set_lock_blocking(next);
-                               clean_tree_block(trans, root, next);
-                               btrfs_wait_tree_block_writeback(next);
-                               btrfs_tree_unlock(next);
+                               if (trans) {
+                                       btrfs_tree_lock(next);
+                                       btrfs_set_lock_blocking(next);
+                                       clean_tree_block(trans, root, next);
+                                       btrfs_wait_tree_block_writeback(next);
+                                       btrfs_tree_unlock(next);
+                               }
 
                                WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);
                                ret = btrfs_free_and_pin_reserved_extent(root,
@@ -2301,11 +2316,13 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
 
                        next = path->nodes[orig_level];
 
-                       btrfs_tree_lock(next);
-                       btrfs_set_lock_blocking(next);
-                       clean_tree_block(trans, log, next);
-                       btrfs_wait_tree_block_writeback(next);
-                       btrfs_tree_unlock(next);
+                       if (trans) {
+                               btrfs_tree_lock(next);
+                               btrfs_set_lock_blocking(next);
+                               clean_tree_block(trans, log, next);
+                               btrfs_wait_tree_block_writeback(next);
+                               btrfs_tree_unlock(next);
+                       }
 
                        WARN_ON(log->root_key.objectid !=
                                BTRFS_TREE_LOG_OBJECTID);
@@ -2571,9 +2588,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
         * the running transaction open, so a full commit can't hop
         * in and cause problems either.
         */
-       btrfs_scrub_pause_super(root);
        ret = write_ctree_super(trans, root->fs_info->tree_root, 1);
-       btrfs_scrub_continue_super(root);
        if (ret) {
                btrfs_abort_transaction(trans, root, ret);
                goto out_wake_log_root;
@@ -2608,13 +2623,10 @@ static void free_log_tree(struct btrfs_trans_handle *trans,
                .process_func = process_one_buffer
        };
 
-       if (trans) {
-               ret = walk_log_tree(trans, log, &wc);
-
-               /* I don't think this can happen but just in case */
-               if (ret)
-                       btrfs_abort_transaction(trans, log, ret);
-       }
+       ret = walk_log_tree(trans, log, &wc);
+       /* I don't think this can happen but just in case */
+       if (ret)
+               btrfs_abort_transaction(trans, log, ret);
 
        while (1) {
                ret = find_first_extent_bit(&log->dirty_log_pages,
@@ -2867,7 +2879,6 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
                          u64 min_offset, u64 *last_offset_ret)
 {
        struct btrfs_key min_key;
-       struct btrfs_key max_key;
        struct btrfs_root *log = root->log_root;
        struct extent_buffer *src;
        int err = 0;
@@ -2879,9 +2890,6 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
        u64 ino = btrfs_ino(inode);
 
        log = root->log_root;
-       max_key.objectid = ino;
-       max_key.offset = (u64)-1;
-       max_key.type = key_type;
 
        min_key.objectid = ino;
        min_key.type = key_type;
@@ -2889,8 +2897,7 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
 
        path->keep_locks = 1;
 
-       ret = btrfs_search_forward(root, &min_key, &max_key,
-                                  path, trans->transid);
+       ret = btrfs_search_forward(root, &min_key, path, trans->transid);
 
        /*
         * we didn't find anything from this transaction, see if there
@@ -2943,10 +2950,8 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
 
        /* find the first key from this transaction again */
        ret = btrfs_search_slot(NULL, root, &min_key, path, 0, 0);
-       if (ret != 0) {
-               WARN_ON(1);
+       if (WARN_ON(ret != 0))
                goto done;
-       }
 
        /*
         * we have a block from this transaction, log every item in it
@@ -3172,11 +3177,10 @@ static int log_inode_item(struct btrfs_trans_handle *trans,
                          struct inode *inode)
 {
        struct btrfs_inode_item *inode_item;
-       struct btrfs_key key;
        int ret;
 
-       memcpy(&key, &BTRFS_I(inode)->location, sizeof(key));
-       ret = btrfs_insert_empty_item(trans, log, path, &key,
+       ret = btrfs_insert_empty_item(trans, log, path,
+                                     &BTRFS_I(inode)->location,
                                      sizeof(*inode_item));
        if (ret && ret != -EEXIST)
                return ret;
@@ -3375,7 +3379,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
                btrfs_set_token_file_extent_type(leaf, fi,
                                                 BTRFS_FILE_EXTENT_REG,
                                                 &token);
-               if (em->block_start == 0)
+               if (em->block_start == EXTENT_MAP_HOLE)
                        skip_csum = true;
        }
 
@@ -3417,11 +3421,6 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
        if (skip_csum)
                return 0;
 
-       if (em->compress_type) {
-               csum_offset = 0;
-               csum_len = block_len;
-       }
-
        /*
         * First check and see if our csums are on our outstanding ordered
         * extents.
@@ -3505,8 +3504,13 @@ unlocked:
        if (!mod_len || ret)
                return ret;
 
-       csum_offset = mod_start - em->start;
-       csum_len = mod_len;
+       if (em->compress_type) {
+               csum_offset = 0;
+               csum_len = block_len;
+       } else {
+               csum_offset = mod_start - em->start;
+               csum_len = mod_len;
+       }
 
        /* block start is already adjusted for the file extent offset. */
        ret = btrfs_lookup_csums_range(log->fs_info->csum_root,
@@ -3693,7 +3697,8 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
                        ret = btrfs_truncate_inode_items(trans, log,
                                                         inode, 0, 0);
                } else if (test_and_clear_bit(BTRFS_INODE_COPY_EVERYTHING,
-                                             &BTRFS_I(inode)->runtime_flags)) {
+                                             &BTRFS_I(inode)->runtime_flags) ||
+                          inode_only == LOG_INODE_EXISTS) {
                        if (inode_only == LOG_INODE_ALL)
                                fast_search = true;
                        max_key.type = BTRFS_XATTR_ITEM_KEY;
@@ -3719,7 +3724,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
 
        while (1) {
                ins_nr = 0;
-               ret = btrfs_search_forward(root, &min_key, &max_key,
+               ret = btrfs_search_forward(root, &min_key,
                                           path, trans->transid);
                if (ret != 0)
                        break;
@@ -3769,14 +3774,14 @@ next_slot:
                }
                btrfs_release_path(path);
 
-               if (min_key.offset < (u64)-1)
+               if (min_key.offset < (u64)-1) {
                        min_key.offset++;
-               else if (min_key.type < (u8)-1)
+               } else if (min_key.type < max_key.type) {
                        min_key.type++;
-               else if (min_key.objectid < (u64)-1)
-                       min_key.objectid++;
-               else
+                       min_key.offset = 0;
+               } else {
                        break;
+               }
        }
        if (ins_nr) {
                ret = copy_items(trans, inode, dst_path, src, ins_start_slot,
@@ -3797,7 +3802,7 @@ log_extents:
                        err = ret;
                        goto out_unlock;
                }
-       } else {
+       } else if (inode_only == LOG_INODE_ALL) {
                struct extent_map_tree *tree = &BTRFS_I(inode)->extent_tree;
                struct extent_map *em, *n;
 
index dd0dea3..fbda900 100644 (file)
@@ -260,7 +260,6 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info,
 {
        struct btrfs_root *root = fs_info->uuid_root;
        struct btrfs_key key;
-       struct btrfs_key max_key;
        struct btrfs_path *path;
        int ret = 0;
        struct extent_buffer *leaf;
@@ -277,13 +276,10 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info,
        key.objectid = 0;
        key.type = 0;
        key.offset = 0;
-       max_key.objectid = (u64)-1;
-       max_key.type = (u8)-1;
-       max_key.offset = (u64)-1;
 
 again_search_slot:
        path->keep_locks = 1;
-       ret = btrfs_search_forward(root, &key, &max_key, path, 0);
+       ret = btrfs_search_forward(root, &key, path, 0);
        if (ret) {
                if (ret > 0)
                        ret = 0;
index 043b215..92303f4 100644 (file)
@@ -28,7 +28,6 @@
 #include <linux/raid/pq.h>
 #include <linux/semaphore.h>
 #include <asm/div64.h>
-#include "compat.h"
 #include "ctree.h"
 #include "extent_map.h"
 #include "disk-io.h"
@@ -666,7 +665,8 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
                if (device->bdev)
                        fs_devices->open_devices--;
 
-               if (device->writeable && !device->is_tgtdev_for_dev_replace) {
+               if (device->writeable &&
+                   device->devid != BTRFS_DEV_REPLACE_DEVID) {
                        list_del_init(&device->dev_alloc_list);
                        fs_devices->rw_devices--;
                }
@@ -2041,6 +2041,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        device->in_fs_metadata = 1;
        device->is_tgtdev_for_dev_replace = 0;
        device->mode = FMODE_EXCL;
+       device->dev_stats_valid = 1;
        set_blocksize(device->bdev, 4096);
 
        if (seeding_dev) {
@@ -2208,6 +2209,7 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
        device->in_fs_metadata = 1;
        device->is_tgtdev_for_dev_replace = 1;
        device->mode = FMODE_EXCL;
+       device->dev_stats_valid = 1;
        set_blocksize(device->bdev, 4096);
        device->fs_devices = fs_info->fs_devices;
        list_add(&device->dev_list, &fs_info->fs_devices->devices);
@@ -2550,8 +2552,7 @@ again:
                failed = 0;
                retried = true;
                goto again;
-       } else if (failed && retried) {
-               WARN_ON(1);
+       } else if (WARN_ON(failed && retried)) {
                ret = -ENOSPC;
        }
 error:
@@ -3423,6 +3424,9 @@ int btrfs_pause_balance(struct btrfs_fs_info *fs_info)
 
 int btrfs_cancel_balance(struct btrfs_fs_info *fs_info)
 {
+       if (fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
        mutex_lock(&fs_info->balance_mutex);
        if (!fs_info->balance_ctl) {
                mutex_unlock(&fs_info->balance_mutex);
@@ -3488,7 +3492,7 @@ static int btrfs_uuid_scan_kthread(void *data)
        path->keep_locks = 1;
 
        while (1) {
-               ret = btrfs_search_forward(root, &key, &max_key, path, 0);
+               ret = btrfs_search_forward(root, &key, path, 0);
                if (ret) {
                        if (ret > 0)
                                ret = 0;
@@ -4488,6 +4492,7 @@ int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len)
                btrfs_crit(fs_info, "Invalid mapping for %Lu-%Lu, got "
                            "%Lu-%Lu\n", logical, logical+len, em->start,
                            em->start + em->len);
+               free_extent_map(em);
                return 1;
        }
 
@@ -4668,6 +4673,7 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
                btrfs_crit(fs_info, "found a bad mapping, wanted %Lu, "
                           "found %Lu-%Lu\n", logical, em->start,
                           em->start + em->len);
+               free_extent_map(em);
                return -EINVAL;
        }
 
@@ -4895,7 +4901,7 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
                        num_stripes = map->num_stripes;
                        max_errors = nr_parity_stripes(map);
 
-                       raid_map = kmalloc(sizeof(u64) * num_stripes,
+                       raid_map = kmalloc_array(num_stripes, sizeof(u64),
                                           GFP_NOFS);
                        if (!raid_map) {
                                ret = -ENOMEM;
@@ -5388,17 +5394,15 @@ static int bio_size_ok(struct block_device *bdev, struct bio *bio,
 {
        struct bio_vec *prev;
        struct request_queue *q = bdev_get_queue(bdev);
-       unsigned short max_sectors = queue_max_sectors(q);
+       unsigned int max_sectors = queue_max_sectors(q);
        struct bvec_merge_data bvm = {
                .bi_bdev = bdev,
                .bi_sector = sector,
                .bi_rw = bio->bi_rw,
        };
 
-       if (bio->bi_vcnt == 0) {
-               WARN_ON(1);
+       if (WARN_ON(bio->bi_vcnt == 0))
                return 1;
-       }
 
        prev = &bio->bi_io_vec[bio->bi_vcnt - 1];
        if (bio_sectors(bio) > max_sectors)
@@ -5631,10 +5635,8 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info,
        struct btrfs_device *dev;
        u64 tmp;
 
-       if (!devid && !fs_info) {
-               WARN_ON(1);
+       if (WARN_ON(!devid && !fs_info))
                return ERR_PTR(-EINVAL);
-       }
 
        dev = __alloc_device();
        if (IS_ERR(dev))
index b72f540..8b3cd14 100644 (file)
@@ -43,9 +43,8 @@ struct btrfs_device {
        /* WRITE_SYNC bios */
        struct btrfs_pending_bios pending_sync_bios;
 
-       int running_pending;
        u64 generation;
-
+       int running_pending;
        int writeable;
        int in_fs_metadata;
        int missing;
@@ -53,11 +52,11 @@ struct btrfs_device {
        int is_tgtdev_for_dev_replace;
 
        spinlock_t io_lock;
+       /* the mode sent to blkdev_get */
+       fmode_t mode;
 
        struct block_device *bdev;
 
-       /* the mode sent to blkdev_get */
-       fmode_t mode;
 
        struct rcu_string *name;
 
@@ -78,16 +77,21 @@ struct btrfs_device {
 
        /* optimal io width for this device */
        u32 io_width;
+       /* type and info about this device */
+       u64 type;
 
        /* minimal io size for this device */
        u32 sector_size;
 
-       /* type and info about this device */
-       u64 type;
 
        /* physical drive uuid (or lvm uuid) */
        u8 uuid[BTRFS_UUID_SIZE];
 
+       /* for sending down flush barriers */
+       int nobarriers;
+       struct bio *flush_bio;
+       struct completion flush_wait;
+
        /* per-device scrub information */
        struct scrub_ctx *scrub_device;
 
@@ -103,10 +107,6 @@ struct btrfs_device {
        struct radix_tree_root reada_zones;
        struct radix_tree_root reada_extents;
 
-       /* for sending down flush barriers */
-       struct bio *flush_bio;
-       struct completion flush_wait;
-       int nobarriers;
 
        /* disk I/O failure stats. For detailed description refer to
         * enum btrfs_dev_stat_values in ioctl.h */
@@ -132,7 +132,9 @@ struct btrfs_fs_devices {
 
        /* all of the devices in the FS, protected by a mutex
         * so we can safely walk it to write out the supers without
-        * worrying about add/remove by the multi-device code
+        * worrying about add/remove by the multi-device code.
+        * Scrubbing super can kick off supers writing by holding
+        * this mutex lock.
         */
        struct mutex device_list_mutex;
        struct list_head devices;
index fc6f4f3..4934347 100644 (file)
@@ -548,7 +548,13 @@ static int
 CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
 {
        int rc;
-       unsigned int offset = CIFS_SESS_KEY_SIZE + 8;
+       struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *)
+           (ses->auth_key.response + CIFS_SESS_KEY_SIZE);
+       unsigned int hash_len;
+
+       /* The MD5 hash starts at challenge_key.key */
+       hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE +
+               offsetof(struct ntlmv2_resp, challenge.key[0]));
 
        if (!ses->server->secmech.sdeschmacmd5) {
                cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__);
@@ -556,7 +562,7 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
        }
 
        rc = crypto_shash_setkey(ses->server->secmech.hmacmd5,
-                               ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
+                                ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
        if (rc) {
                cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",
                         __func__);
@@ -570,20 +576,21 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
        }
 
        if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED)
-               memcpy(ses->auth_key.response + offset,
-                       ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
+               memcpy(ntlmv2->challenge.key,
+                      ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
        else
-               memcpy(ses->auth_key.response + offset,
-                       ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
+               memcpy(ntlmv2->challenge.key,
+                      ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
        rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
-               ses->auth_key.response + offset, ses->auth_key.len - offset);
+                                ntlmv2->challenge.key, hash_len);
        if (rc) {
                cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
                return rc;
        }
 
+       /* Note that the MD5 digest over writes anon.challenge_key.key */
        rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
-               ses->auth_key.response + CIFS_SESS_KEY_SIZE);
+                               ntlmv2->ntlmv2_hash);
        if (rc)
                cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
 
@@ -627,7 +634,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
        int rc;
        int baselen;
        unsigned int tilen;
-       struct ntlmv2_resp *buf;
+       struct ntlmv2_resp *ntlmv2;
        char ntlmv2_hash[16];
        unsigned char *tiblob = NULL; /* target info blob */
 
@@ -660,13 +667,14 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
        }
        ses->auth_key.len += baselen;
 
-       buf = (struct ntlmv2_resp *)
+       ntlmv2 = (struct ntlmv2_resp *)
                        (ses->auth_key.response + CIFS_SESS_KEY_SIZE);
-       buf->blob_signature = cpu_to_le32(0x00000101);
-       buf->reserved = 0;
-       buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
-       get_random_bytes(&buf->client_chal, sizeof(buf->client_chal));
-       buf->reserved2 = 0;
+       ntlmv2->blob_signature = cpu_to_le32(0x00000101);
+       ntlmv2->reserved = 0;
+       /* Must be within 5 minutes of the server */
+       ntlmv2->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+       get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal));
+       ntlmv2->reserved2 = 0;
 
        memcpy(ses->auth_key.response + baselen, tiblob, tilen);
 
@@ -706,7 +714,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
        }
 
        rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
-               ses->auth_key.response + CIFS_SESS_KEY_SIZE,
+               ntlmv2->ntlmv2_hash,
                CIFS_HMAC_MD5_HASH_SIZE);
        if (rc) {
                cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
index 26b1c1d..d9ea7ad 100644 (file)
@@ -261,7 +261,7 @@ struct smb_version_operations {
        /* query path data from the server */
        int (*query_path_info)(const unsigned int, struct cifs_tcon *,
                               struct cifs_sb_info *, const char *,
-                              FILE_ALL_INFO *, bool *);
+                              FILE_ALL_INFO *, bool *, bool *);
        /* query file data from the server */
        int (*query_file_info)(const unsigned int, struct cifs_tcon *,
                               struct cifs_fid *, FILE_ALL_INFO *);
@@ -381,6 +381,9 @@ struct smb_version_operations {
        char * (*create_lease_buf)(u8 *, u8);
        /* parse lease context buffer and return oplock/epoch info */
        __u8 (*parse_lease_buf)(void *, unsigned int *);
+       int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file,
+                       struct cifsFileInfo *target_file, u64 src_off, u64 len,
+                       u64 dest_off);
 };
 
 struct smb_version_values {
@@ -855,6 +858,9 @@ struct cifs_tcon {
        __le64 vol_create_time;
        __u32 ss_flags;         /* sector size flags */
        __u32 perf_sector_size; /* best sector size for perf */
+       __u32 max_chunks;
+       __u32 max_bytes_chunk;
+       __u32 max_bytes_copy;
 #endif /* CONFIG_CIFS_SMB2 */
 #ifdef CONFIG_CIFS_FSCACHE
        u64 resource_id;                /* server resource id */
index 9e5ee34..33df36e 100644 (file)
@@ -697,7 +697,13 @@ struct ntlmssp2_name {
 } __attribute__((packed));
 
 struct ntlmv2_resp {
-       char ntlmv2_hash[CIFS_ENCPWD_SIZE];
+       union {
+           char ntlmv2_hash[CIFS_ENCPWD_SIZE];
+           struct {
+               __u8 reserved[8];
+               __u8 key[CIFS_SERVER_CHALLENGE_SIZE];
+           } __attribute__((packed)) challenge;
+       } __attribute__((packed));
        __le32 blob_signature;
        __u32  reserved;
        __le64  time;
index 93b2947..124aa02 100644 (file)
@@ -3369,11 +3369,13 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
                return 0;
        }
        cifs_acl->version = cpu_to_le16(1);
-       if (acl_type == ACL_TYPE_ACCESS)
+       if (acl_type == ACL_TYPE_ACCESS) {
                cifs_acl->access_entry_count = cpu_to_le16(count);
-       else if (acl_type == ACL_TYPE_DEFAULT)
+               cifs_acl->default_entry_count = __constant_cpu_to_le16(0xFFFF);
+       } else if (acl_type == ACL_TYPE_DEFAULT) {
                cifs_acl->default_entry_count = cpu_to_le16(count);
-       else {
+               cifs_acl->access_entry_count = __constant_cpu_to_le16(0xFFFF);
+       } else {
                cifs_dbg(FYI, "unknown ACL type %d\n", acl_type);
                return 0;
        }
index 5384c2a..11ff5f1 100644 (file)
@@ -756,7 +756,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
        /*      if it was once a directory (but how can we tell?) we could do
                shrink_dcache_parent(direntry); */
        } else if (rc != -EACCES) {
-               cifs_dbg(VFS, "Unexpected lookup error %d\n", rc);
+               cifs_dbg(FYI, "Unexpected lookup error %d\n", rc);
                /* We special case check for Access Denied - since that
                is a common return code */
        }
index 7ddddf2..5a5a872 100644 (file)
@@ -3663,6 +3663,27 @@ void cifs_oplock_break(struct work_struct *work)
        }
 }
 
+/*
+ * The presence of cifs_direct_io() in the address space ops vector
+ * allowes open() O_DIRECT flags which would have failed otherwise.
+ *
+ * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests
+ * so this method should never be called.
+ *
+ * Direct IO is not yet supported in the cached mode. 
+ */
+static ssize_t
+cifs_direct_io(int rw, struct kiocb *iocb, const struct iovec *iov,
+               loff_t pos, unsigned long nr_segs)
+{
+        /*
+         * FIXME
+         * Eventually need to support direct IO for non forcedirectio mounts
+         */
+        return -EINVAL;
+}
+
+
 const struct address_space_operations cifs_addr_ops = {
        .readpage = cifs_readpage,
        .readpages = cifs_readpages,
@@ -3672,6 +3693,7 @@ const struct address_space_operations cifs_addr_ops = {
        .write_end = cifs_write_end,
        .set_page_dirty = __set_page_dirty_nobuffers,
        .releasepage = cifs_release_page,
+       .direct_IO = cifs_direct_io,
        .invalidatepage = cifs_invalidate_page,
        .launder_page = cifs_launder_page,
 };
index 867b7cd..36f9ebb 100644 (file)
@@ -542,7 +542,8 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
 /* Fill a cifs_fattr struct with info from FILE_ALL_INFO */
 static void
 cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
-                      struct cifs_sb_info *cifs_sb, bool adjust_tz)
+                      struct cifs_sb_info *cifs_sb, bool adjust_tz,
+                      bool symlink)
 {
        struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
 
@@ -569,7 +570,11 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
        fattr->cf_createtime = le64_to_cpu(info->CreationTime);
 
        fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
-       if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
+
+       if (symlink) {
+               fattr->cf_mode = S_IFLNK;
+               fattr->cf_dtype = DT_LNK;
+       } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
                fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
                fattr->cf_dtype = DT_DIR;
                /*
@@ -578,10 +583,6 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
                 */
                if (!tcon->unix_ext)
                        fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
-       } else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
-               fattr->cf_mode = S_IFLNK;
-               fattr->cf_dtype = DT_LNK;
-               fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
        } else {
                fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
                fattr->cf_dtype = DT_REG;
@@ -626,7 +627,8 @@ cifs_get_file_info(struct file *filp)
        rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data);
        switch (rc) {
        case 0:
-               cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false);
+               cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false,
+                                      false);
                break;
        case -EREMOTE:
                cifs_create_dfs_fattr(&fattr, inode->i_sb);
@@ -673,6 +675,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
        bool adjust_tz = false;
        struct cifs_fattr fattr;
        struct cifs_search_info *srchinf = NULL;
+       bool symlink = false;
 
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink))
@@ -702,12 +705,12 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
                }
                data = (FILE_ALL_INFO *)buf;
                rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path,
-                                                 data, &adjust_tz);
+                                                 data, &adjust_tz, &symlink);
        }
 
        if (!rc) {
-               cifs_all_info_to_fattr(&fattr, (FILE_ALL_INFO *)data, cifs_sb,
-                                      adjust_tz);
+               cifs_all_info_to_fattr(&fattr, data, cifs_sb, adjust_tz,
+                                      symlink);
        } else if (rc == -EREMOTE) {
                cifs_create_dfs_fattr(&fattr, sb);
                rc = 0;
index ba54bf6..409b45e 100644 (file)
  */
 
 #include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/mount.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/btrfs.h>
 #include "cifspdu.h"
 #include "cifsglob.h"
 #include "cifsproto.h"
 #include "cifs_debug.h"
 #include "cifsfs.h"
 
+static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
+                       unsigned long srcfd, u64 off, u64 len, u64 destoff)
+{
+       int rc;
+       struct cifsFileInfo *smb_file_target = dst_file->private_data;
+       struct inode *target_inode = file_inode(dst_file);
+       struct cifs_tcon *target_tcon;
+       struct fd src_file;
+       struct cifsFileInfo *smb_file_src;
+       struct inode *src_inode;
+       struct cifs_tcon *src_tcon;
+
+       cifs_dbg(FYI, "ioctl clone range\n");
+       /* the destination must be opened for writing */
+       if (!(dst_file->f_mode & FMODE_WRITE)) {
+               cifs_dbg(FYI, "file target not open for write\n");
+               return -EINVAL;
+       }
+
+       /* check if target volume is readonly and take reference */
+       rc = mnt_want_write_file(dst_file);
+       if (rc) {
+               cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
+               return rc;
+       }
+
+       src_file = fdget(srcfd);
+       if (!src_file.file) {
+               rc = -EBADF;
+               goto out_drop_write;
+       }
+
+       if ((!src_file.file->private_data) || (!dst_file->private_data)) {
+               rc = -EBADF;
+               cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
+               goto out_fput;
+       }
+
+       rc = -EXDEV;
+       smb_file_target = dst_file->private_data;
+       smb_file_src = src_file.file->private_data;
+       src_tcon = tlink_tcon(smb_file_src->tlink);
+       target_tcon = tlink_tcon(smb_file_target->tlink);
+
+       /* check if source and target are on same tree connection */
+       if (src_tcon != target_tcon) {
+               cifs_dbg(VFS, "file copy src and target on different volume\n");
+               goto out_fput;
+       }
+
+       src_inode = src_file.file->f_dentry->d_inode;
+
+       /*
+        * Note: cifs case is easier than btrfs since server responsible for
+        * checks for proper open modes and file type and if it wants
+        * server could even support copy of range where source = target
+        */
+
+       /* so we do not deadlock racing two ioctls on same files */
+       if (target_inode < src_inode) {
+               mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_PARENT);
+               mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD);
+       } else {
+               mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT);
+               mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_CHILD);
+       }
+
+       /* determine range to clone */
+       rc = -EINVAL;
+       if (off + len > src_inode->i_size || off + len < off)
+               goto out_unlock;
+       if (len == 0)
+               len = src_inode->i_size - off;
+
+       cifs_dbg(FYI, "about to flush pages\n");
+       /* should we flush first and last page first */
+       truncate_inode_pages_range(&target_inode->i_data, destoff,
+                                  PAGE_CACHE_ALIGN(destoff + len)-1);
+
+       if (target_tcon->ses->server->ops->clone_range)
+               rc = target_tcon->ses->server->ops->clone_range(xid,
+                       smb_file_src, smb_file_target, off, len, destoff);
+
+       /* force revalidate of size and timestamps of target file now
+          that target is updated on the server */
+       CIFS_I(target_inode)->time = 0;
+out_unlock:
+       /* although unlocking in the reverse order from locking is not
+          strictly necessary here it is a little cleaner to be consistent */
+       if (target_inode < src_inode) {
+               mutex_unlock(&src_inode->i_mutex);
+               mutex_unlock(&target_inode->i_mutex);
+       } else {
+               mutex_unlock(&target_inode->i_mutex);
+               mutex_unlock(&src_inode->i_mutex);
+       }
+out_fput:
+       fdput(src_file);
+out_drop_write:
+       mnt_drop_write_file(dst_file);
+       return rc;
+}
+
 long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 {
        struct inode *inode = file_inode(filep);
@@ -105,6 +213,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
                                cifs_dbg(FYI, "set compress flag rc %d\n", rc);
                        }
                        break;
+               case BTRFS_IOC_CLONE:
+                       rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0);
+                       break;
                default:
                        cifs_dbg(FYI, "unsupported ioctl\n");
                        break;
index 651a527..0498845 100644 (file)
@@ -51,7 +51,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
        {ERRnoaccess, -EACCES},
        {ERRbadfid, -EBADF},
        {ERRbadmcb, -EIO},
-       {ERRnomem, -ENOMEM},
+       {ERRnomem, -EREMOTEIO},
        {ERRbadmem, -EFAULT},
        {ERRbadenv, -EFAULT},
        {ERRbadformat, -EINVAL},
index 53a75f3..5940eca 100644 (file)
@@ -134,22 +134,6 @@ out:
        dput(dentry);
 }
 
-/*
- * Is it possible that this directory might turn out to be a DFS referral
- * once we go to try and use it?
- */
-static bool
-cifs_dfs_is_possible(struct cifs_sb_info *cifs_sb)
-{
-#ifdef CONFIG_CIFS_DFS_UPCALL
-       struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
-
-       if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
-               return true;
-#endif
-       return false;
-}
-
 static void
 cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
 {
@@ -159,27 +143,19 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
        if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
                fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
                fattr->cf_dtype = DT_DIR;
-               /*
-                * Windows CIFS servers generally make DFS referrals look
-                * like directories in FIND_* responses with the reparse
-                * attribute flag also set (since DFS junctions are
-                * reparse points). We must revalidate at least these
-                * directory inodes before trying to use them (if
-                * they are DFS we will get PATH_NOT_COVERED back
-                * when queried directly and can then try to connect
-                * to the DFS target)
-                */
-               if (cifs_dfs_is_possible(cifs_sb) &&
-                   (fattr->cf_cifsattrs & ATTR_REPARSE))
-                       fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
-       } else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
-               fattr->cf_mode = S_IFLNK;
-               fattr->cf_dtype = DT_LNK;
        } else {
                fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
                fattr->cf_dtype = DT_REG;
        }
 
+       /*
+        * We need to revalidate it further to make a decision about whether it
+        * is a symbolic link, DFS referral or a reparse point with a direct
+        * access like junctions, deduplicated files, NFS symlinks.
+        */
+       if (fattr->cf_cifsattrs & ATTR_REPARSE)
+               fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
+
        /* non-unix readdir doesn't provide nlink */
        fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
 
index 384cffe..5f5ba0d 100644 (file)
@@ -534,10 +534,12 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 static int
 cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
                     struct cifs_sb_info *cifs_sb, const char *full_path,
-                    FILE_ALL_INFO *data, bool *adjustTZ)
+                    FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink)
 {
        int rc;
 
+       *symlink = false;
+
        /* could do find first instead but this returns more info */
        rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */,
                              cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
@@ -554,6 +556,23 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
                                                CIFS_MOUNT_MAP_SPECIAL_CHR);
                *adjustTZ = true;
        }
+
+       if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) {
+               int tmprc;
+               int oplock = 0;
+               __u16 netfid;
+
+               /* Need to check if this is a symbolic link or not */
+               tmprc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
+                                   FILE_READ_ATTRIBUTES, 0, &netfid, &oplock,
+                                   NULL, cifs_sb->local_nls,
+                       cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+               if (tmprc == -EOPNOTSUPP)
+                       *symlink = true;
+               else
+                       CIFSSMBClose(xid, tcon, netfid);
+       }
+
        return rc;
 }
 
index 78ff88c..84c012a 100644 (file)
@@ -123,12 +123,13 @@ move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
 int
 smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
                     struct cifs_sb_info *cifs_sb, const char *full_path,
-                    FILE_ALL_INFO *data, bool *adjust_tz)
+                    FILE_ALL_INFO *data, bool *adjust_tz, bool *symlink)
 {
        int rc;
        struct smb2_file_all_info *smb2_data;
 
        *adjust_tz = false;
+       *symlink = false;
 
        smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2,
                            GFP_KERNEL);
@@ -136,9 +137,16 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
                return -ENOMEM;
 
        rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path,
-                               FILE_READ_ATTRIBUTES, FILE_OPEN,
-                               OPEN_REPARSE_POINT, smb2_data,
-                               SMB2_OP_QUERY_INFO);
+                               FILE_READ_ATTRIBUTES, FILE_OPEN, 0,
+                               smb2_data, SMB2_OP_QUERY_INFO);
+       if (rc == -EOPNOTSUPP) {
+               *symlink = true;
+               /* Failed on a symbolic link - query a reparse point info */
+               rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path,
+                                       FILE_READ_ATTRIBUTES, FILE_OPEN,
+                                       OPEN_REPARSE_POINT, smb2_data,
+                                       SMB2_OP_QUERY_INFO);
+       }
        if (rc)
                goto out;
 
index 7c2f45c..94bd4fb 100644 (file)
@@ -306,7 +306,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
        {STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"},
        {STATUS_MORE_PROCESSING_REQUIRED, -EIO,
        "STATUS_MORE_PROCESSING_REQUIRED"},
-       {STATUS_NO_MEMORY, -ENOMEM, "STATUS_NO_MEMORY"},
+       {STATUS_NO_MEMORY, -EREMOTEIO, "STATUS_NO_MEMORY"},
        {STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE,
        "STATUS_CONFLICTING_ADDRESSES"},
        {STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"},
index c571be8..11dde4b 100644 (file)
@@ -494,6 +494,85 @@ smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 static int
+SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
+                    u64 persistent_fid, u64 volatile_fid,
+                    struct copychunk_ioctl *pcchunk)
+{
+       int rc;
+       unsigned int ret_data_len;
+       struct resume_key_req *res_key;
+
+       rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
+                       FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */,
+                       NULL, 0 /* no input */,
+                       (char **)&res_key, &ret_data_len);
+
+       if (rc) {
+               cifs_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc);
+               goto req_res_key_exit;
+       }
+       if (ret_data_len < sizeof(struct resume_key_req)) {
+               cifs_dbg(VFS, "Invalid refcopy resume key length\n");
+               rc = -EINVAL;
+               goto req_res_key_exit;
+       }
+       memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE);
+
+req_res_key_exit:
+       kfree(res_key);
+       return rc;
+}
+
+static int
+smb2_clone_range(const unsigned int xid,
+                       struct cifsFileInfo *srcfile,
+                       struct cifsFileInfo *trgtfile, u64 src_off,
+                       u64 len, u64 dest_off)
+{
+       int rc;
+       unsigned int ret_data_len;
+       struct copychunk_ioctl *pcchunk;
+       char *retbuf = NULL;
+
+       pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
+
+       if (pcchunk == NULL)
+               return -ENOMEM;
+
+       cifs_dbg(FYI, "in smb2_clone_range - about to call request res key\n");
+       /* Request a key from the server to identify the source of the copy */
+       rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink),
+                               srcfile->fid.persistent_fid,
+                               srcfile->fid.volatile_fid, pcchunk);
+
+       /* Note: request_res_key sets res_key null only if rc !=0 */
+       if (rc)
+               return rc;
+
+       /* For now array only one chunk long, will make more flexible later */
+       pcchunk->ChunkCount = __constant_cpu_to_le32(1);
+       pcchunk->Reserved = 0;
+       pcchunk->SourceOffset = cpu_to_le64(src_off);
+       pcchunk->TargetOffset = cpu_to_le64(dest_off);
+       pcchunk->Length = cpu_to_le32(len);
+       pcchunk->Reserved2 = 0;
+
+       /* Request that server copy to target from src file identified by key */
+       rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink),
+                       trgtfile->fid.persistent_fid,
+                       trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
+                       true /* is_fsctl */, (char *)pcchunk,
+                       sizeof(struct copychunk_ioctl), &retbuf, &ret_data_len);
+
+       /* BB need to special case rc = EINVAL to alter chunk size */
+
+       cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len);
+
+       kfree(pcchunk);
+       return rc;
+}
+
+static int
 smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
                struct cifs_fid *fid)
 {
@@ -1017,6 +1096,7 @@ struct smb_version_operations smb20_operations = {
        .set_oplock_level = smb2_set_oplock_level,
        .create_lease_buf = smb2_create_lease_buf,
        .parse_lease_buf = smb2_parse_lease_buf,
+       .clone_range = smb2_clone_range,
 };
 
 struct smb_version_operations smb21_operations = {
@@ -1090,6 +1170,7 @@ struct smb_version_operations smb21_operations = {
        .set_oplock_level = smb21_set_oplock_level,
        .create_lease_buf = smb2_create_lease_buf,
        .parse_lease_buf = smb2_parse_lease_buf,
+       .clone_range = smb2_clone_range,
 };
 
 struct smb_version_operations smb30_operations = {
@@ -1165,6 +1246,7 @@ struct smb_version_operations smb30_operations = {
        .set_oplock_level = smb3_set_oplock_level,
        .create_lease_buf = smb3_create_lease_buf,
        .parse_lease_buf = smb3_parse_lease_buf,
+       .clone_range = smb2_clone_range,
 };
 
 struct smb_version_values smb20_values = {
index 8ab05b0..d65270c 100644 (file)
@@ -630,6 +630,8 @@ ssetup_ntlmssp_authenticate:
                goto ssetup_exit;
 
        ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+       if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
+               cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
 ssetup_exit:
        free_rsp_buf(resp_buftype, rsp);
 
@@ -717,6 +719,14 @@ static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code)
 
 #define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */)
 
+/* These are similar values to what Windows uses */
+static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon)
+{
+       tcon->max_chunks = 256;
+       tcon->max_bytes_chunk = 1048576;
+       tcon->max_bytes_copy = 16777216;
+}
+
 int
 SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
          struct cifs_tcon *tcon, const struct nls_table *cp)
@@ -818,7 +828,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
        if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&
            ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
                cifs_dbg(VFS, "DFS capability contradicts DFS flag\n");
-
+       init_copy_chunk_defaults(tcon);
 tcon_exit:
        free_rsp_buf(resp_buftype, rsp);
        kfree(unc_path);
index 6183b1b..f88320b 100644 (file)
@@ -122,6 +122,23 @@ struct smb2_pdu {
        __le16 StructureSize2; /* size of wct area (varies, request specific) */
 } __packed;
 
+struct smb2_transform_hdr {
+       __be32 smb2_buf_length; /* big endian on wire */
+                               /* length is only two or three bytes - with
+                                one or two byte type preceding it that MBZ */
+       __u8   ProtocolId[4];   /* 0xFD 'S' 'M' 'B' */
+       __u8   Signature[16];
+       __u8   Nonce[11];
+       __u8   Reserved[5];
+       __le32 OriginalMessageSize;
+       __u16  Reserved1;
+       __le16 EncryptionAlgorithm;
+       __u64  SessionId;
+} __packed;
+
+/* Encryption Algorithms */
+#define SMB2_ENCRYPTION_AES128_CCM     __constant_cpu_to_le16(0x0001)
+
 /*
  *     SMB2 flag definitions
  */
@@ -237,6 +254,7 @@ struct smb2_sess_setup_req {
 /* Currently defined SessionFlags */
 #define SMB2_SESSION_FLAG_IS_GUEST     0x0001
 #define SMB2_SESSION_FLAG_IS_NULL      0x0002
+#define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004
 struct smb2_sess_setup_rsp {
        struct smb2_hdr hdr;
        __le16 StructureSize; /* Must be 9 */
@@ -534,9 +552,16 @@ struct create_durable {
        } Data;
 } __packed;
 
+#define COPY_CHUNK_RES_KEY_SIZE        24
+struct resume_key_req {
+       char ResumeKey[COPY_CHUNK_RES_KEY_SIZE];
+       __le32  ContextLength;  /* MBZ */
+       char    Context[0];     /* ignored, Windows sets to 4 bytes of zero */
+} __packed;
+
 /* this goes in the ioctl buffer when doing a copychunk request */
 struct copychunk_ioctl {
-       char SourceKey[24];
+       char SourceKey[COPY_CHUNK_RES_KEY_SIZE];
        __le32 ChunkCount; /* we are only sending 1 */
        __le32 Reserved;
        /* array will only be one chunk long for us */
@@ -546,6 +571,12 @@ struct copychunk_ioctl {
        __u32 Reserved2;
 } __packed;
 
+struct copychunk_ioctl_rsp {
+       __le32 ChunksWritten;
+       __le32 ChunkBytesWritten;
+       __le32 TotalBytesWritten;
+} __packed;
+
 /* Response and Request are the same format */
 struct validate_negotiate_info {
        __le32 Capabilities;
index 313813e..b4eea10 100644 (file)
@@ -61,7 +61,7 @@ extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
 extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
                                struct cifs_sb_info *cifs_sb,
                                const char *full_path, FILE_ALL_INFO *data,
-                               bool *adjust_tz);
+                               bool *adjust_tz, bool *symlink);
 extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
                              const char *full_path, __u64 size,
                              struct cifs_sb_info *cifs_sb, bool set_alloc);
index 277bd1b..e081acb 100644 (file)
@@ -56,29 +56,28 @@ static void configfs_d_iput(struct dentry * dentry,
        struct configfs_dirent *sd = dentry->d_fsdata;
 
        if (sd) {
-               BUG_ON(sd->s_dentry != dentry);
                /* Coordinate with configfs_readdir */
                spin_lock(&configfs_dirent_lock);
-               sd->s_dentry = NULL;
+               /* Coordinate with configfs_attach_attr where will increase
+                * sd->s_count and update sd->s_dentry to new allocated one.
+                * Only set sd->dentry to null when this dentry is the only
+                * sd owner.
+                * If not do so, configfs_d_iput may run just after
+                * configfs_attach_attr and set sd->s_dentry to null
+                * even it's still in use.
+                */
+               if (atomic_read(&sd->s_count) <= 2)
+                       sd->s_dentry = NULL;
+
                spin_unlock(&configfs_dirent_lock);
                configfs_put(sd);
        }
        iput(inode);
 }
 
-/*
- * We _must_ delete our dentries on last dput, as the chain-to-parent
- * behavior is required to clear the parents of default_groups.
- */
-static int configfs_d_delete(const struct dentry *dentry)
-{
-       return 1;
-}
-
 const struct dentry_operations configfs_dentry_ops = {
        .d_iput         = configfs_d_iput,
-       /* simple_delete_dentry() isn't exported */
-       .d_delete       = configfs_d_delete,
+       .d_delete       = always_delete_dentry,
 };
 
 #ifdef CONFIG_LOCKDEP
@@ -426,8 +425,11 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den
        struct configfs_attribute * attr = sd->s_element;
        int error;
 
+       spin_lock(&configfs_dirent_lock);
        dentry->d_fsdata = configfs_get(sd);
        sd->s_dentry = dentry;
+       spin_unlock(&configfs_dirent_lock);
+
        error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG,
                                configfs_init_file);
        if (error) {
index 62406b6..bc3fbcd 100644 (file)
@@ -695,7 +695,7 @@ int dump_emit(struct coredump_params *cprm, const void *addr, int nr)
        while (nr) {
                if (dump_interrupted())
                        return 0;
-               n = vfs_write(file, addr, nr, &pos);
+               n = __kernel_write(file, addr, nr, &pos);
                if (n <= 0)
                        return 0;
                file->f_pos = pos;
@@ -733,7 +733,7 @@ int dump_align(struct coredump_params *cprm, int align)
 {
        unsigned mod = cprm->written & (align - 1);
        if (align & (align - 1))
-               return -EINVAL;
-       return mod ? dump_skip(cprm, align - mod) : 0;
+               return 0;
+       return mod ? dump_skip(cprm, align - mod) : 1;
 }
 EXPORT_SYMBOL(dump_align);
index 0a38ef8..4bdb300 100644 (file)
@@ -88,35 +88,6 @@ EXPORT_SYMBOL(rename_lock);
 
 static struct kmem_cache *dentry_cache __read_mostly;
 
-/**
- * read_seqbegin_or_lock - begin a sequence number check or locking block
- * @lock: sequence lock
- * @seq : sequence number to be checked
- *
- * First try it once optimistically without taking the lock. If that fails,
- * take the lock. The sequence number is also used as a marker for deciding
- * whether to be a reader (even) or writer (odd).
- * N.B. seq must be initialized to an even number to begin with.
- */
-static inline void read_seqbegin_or_lock(seqlock_t *lock, int *seq)
-{
-       if (!(*seq & 1))        /* Even */
-               *seq = read_seqbegin(lock);
-       else                    /* Odd */
-               read_seqlock_excl(lock);
-}
-
-static inline int need_seqretry(seqlock_t *lock, int seq)
-{
-       return !(seq & 1) && read_seqretry(lock, seq);
-}
-
-static inline void done_seqretry(seqlock_t *lock, int seq)
-{
-       if (seq & 1)
-               read_sequnlock_excl(lock);
-}
-
 /*
  * This is the single most critical data structure when it comes
  * to the dcache: the hashtable for lookups. Somebody should try
@@ -125,8 +96,6 @@ static inline void done_seqretry(seqlock_t *lock, int seq)
  * This hash-function tries to avoid losing too many bits of hash
  * information, yet avoid using a prime hash-size or similar.
  */
-#define D_HASHBITS     d_hash_shift
-#define D_HASHMASK     d_hash_mask
 
 static unsigned int d_hash_mask __read_mostly;
 static unsigned int d_hash_shift __read_mostly;
@@ -137,8 +106,8 @@ static inline struct hlist_bl_head *d_hash(const struct dentry *parent,
                                        unsigned int hash)
 {
        hash += (unsigned long) parent / L1_CACHE_BYTES;
-       hash = hash + (hash >> D_HASHBITS);
-       return dentry_hashtable + (hash & D_HASHMASK);
+       hash = hash + (hash >> d_hash_shift);
+       return dentry_hashtable + (hash & d_hash_mask);
 }
 
 /* Statistics gathering. */
@@ -469,7 +438,7 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)
 {
        list_del(&dentry->d_u.d_child);
        /*
-        * Inform try_to_ascend() that we are no longer attached to the
+        * Inform d_walk() that we are no longer attached to the
         * dentry tree
         */
        dentry->d_flags |= DCACHE_DENTRY_KILLED;
@@ -1069,34 +1038,6 @@ void shrink_dcache_sb(struct super_block *sb)
 }
 EXPORT_SYMBOL(shrink_dcache_sb);
 
-/*
- * This tries to ascend one level of parenthood, but
- * we can race with renaming, so we need to re-check
- * the parenthood after dropping the lock and check
- * that the sequence number still matches.
- */
-static struct dentry *try_to_ascend(struct dentry *old, unsigned seq)
-{
-       struct dentry *new = old->d_parent;
-
-       rcu_read_lock();
-       spin_unlock(&old->d_lock);
-       spin_lock(&new->d_lock);
-
-       /*
-        * might go back up the wrong parent if we have had a rename
-        * or deletion
-        */
-       if (new != old->d_parent ||
-                (old->d_flags & DCACHE_DENTRY_KILLED) ||
-                need_seqretry(&rename_lock, seq)) {
-               spin_unlock(&new->d_lock);
-               new = NULL;
-       }
-       rcu_read_unlock();
-       return new;
-}
-
 /**
  * enum d_walk_ret - action to talke during tree walk
  * @D_WALK_CONTINUE:   contrinue walk
@@ -1185,9 +1126,24 @@ resume:
         */
        if (this_parent != parent) {
                struct dentry *child = this_parent;
-               this_parent = try_to_ascend(this_parent, seq);
-               if (!this_parent)
+               this_parent = child->d_parent;
+
+               rcu_read_lock();
+               spin_unlock(&child->d_lock);
+               spin_lock(&this_parent->d_lock);
+
+               /*
+                * might go back up the wrong parent if we have had a rename
+                * or deletion
+                */
+               if (this_parent != child->d_parent ||
+                        (child->d_flags & DCACHE_DENTRY_KILLED) ||
+                        need_seqretry(&rename_lock, seq)) {
+                       spin_unlock(&this_parent->d_lock);
+                       rcu_read_unlock();
                        goto rename_retry;
+               }
+               rcu_read_unlock();
                next = child->d_u.d_child.next;
                goto resume;
        }
index 60a3278..e7cfbaf 100644 (file)
@@ -74,14 +74,16 @@ static int user_cmd(struct sk_buff *skb, struct genl_info *info)
        return 0;
 }
 
-static struct genl_ops dlm_nl_ops = {
-       .cmd            = DLM_CMD_HELLO,
-       .doit           = user_cmd,
+static struct genl_ops dlm_nl_ops[] = {
+       {
+               .cmd    = DLM_CMD_HELLO,
+               .doit   = user_cmd,
+       },
 };
 
 int __init dlm_netlink_init(void)
 {
-       return genl_register_family_with_ops(&family, &dlm_nl_ops, 1);
+       return genl_register_family_with_ops(&family, dlm_nl_ops);
 }
 
 void dlm_netlink_exit(void)
index 000eae2..2f6735d 100644 (file)
@@ -392,7 +392,7 @@ static int crypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
 
                wait_for_completion(&ecr->completion);
                rc = ecr->rc;
-               INIT_COMPLETION(ecr->completion);
+               reinit_completion(&ecr->completion);
        }
 out:
        ablkcipher_request_free(req);
index a8766b8..becc725 100644 (file)
@@ -83,19 +83,10 @@ static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr)
        return 0;
 }
 
-/*
- * Retaining negative dentries for an in-memory filesystem just wastes
- * memory and lookup time: arrange for them to be deleted immediately.
- */
-static int efivarfs_delete_dentry(const struct dentry *dentry)
-{
-       return 1;
-}
-
 static struct dentry_operations efivarfs_d_ops = {
        .d_compare = efivarfs_d_compare,
        .d_hash = efivarfs_d_hash,
-       .d_delete = efivarfs_delete_dentry,
+       .d_delete = always_delete_dentry,
 };
 
 static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
index 977319f..7ea097f 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1380,10 +1380,6 @@ int search_binary_handler(struct linux_binprm *bprm)
        if (retval)
                return retval;
 
-       retval = audit_bprm(bprm);
-       if (retval)
-               return retval;
-
        retval = -ENOENT;
  retry:
        read_lock(&binfmt_lock);
@@ -1431,6 +1427,7 @@ static int exec_binprm(struct linux_binprm *bprm)
 
        ret = search_binary_handler(bprm);
        if (ret >= 0) {
+               audit_bprm(bprm);
                trace_sched_process_exec(current, old_pid, bprm);
                ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
                current->did_exec = 1;
index e66a800..c8420f7 100644 (file)
@@ -1899,7 +1899,8 @@ static int gfs2_glock_iter_next(struct gfs2_glock_iter *gi)
                        gi->nhash = 0;
                }
        /* Skip entries for other sb and dead entries */
-       } while (gi->sdp != gi->gl->gl_sbd || __lockref_is_dead(&gl->gl_lockref));
+       } while (gi->sdp != gi->gl->gl_sbd ||
+                __lockref_is_dead(&gi->gl->gl_lockref));
 
        return 0;
 }
index 1615df1..7119504 100644 (file)
@@ -1171,8 +1171,11 @@ static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry,
        if (d != NULL)
                dentry = d;
        if (dentry->d_inode) {
-               if (!(*opened & FILE_OPENED))
+               if (!(*opened & FILE_OPENED)) {
+                       if (d == NULL)
+                               dget(dentry);
                        return finish_no_open(file, dentry);
+               }
                dput(d);
                return 0;
        }
index c8423d6..2a6ba06 100644 (file)
@@ -466,19 +466,19 @@ static void gdlm_cancel(struct gfs2_glock *gl)
 static void control_lvb_read(struct lm_lockstruct *ls, uint32_t *lvb_gen,
                             char *lvb_bits)
 {
-       uint32_t gen;
+       __le32 gen;
        memcpy(lvb_bits, ls->ls_control_lvb, GDLM_LVB_SIZE);
-       memcpy(&gen, lvb_bits, sizeof(uint32_t));
+       memcpy(&gen, lvb_bits, sizeof(__le32));
        *lvb_gen = le32_to_cpu(gen);
 }
 
 static void control_lvb_write(struct lm_lockstruct *ls, uint32_t lvb_gen,
                              char *lvb_bits)
 {
-       uint32_t gen;
+       __le32 gen;
        memcpy(ls->ls_control_lvb, lvb_bits, GDLM_LVB_SIZE);
        gen = cpu_to_le32(lvb_gen);
-       memcpy(ls->ls_control_lvb, &gen, sizeof(uint32_t));
+       memcpy(ls->ls_control_lvb, &gen, sizeof(__le32));
 }
 
 static int all_jid_bits_clear(char *lvb)
index 453b50e..98236d0 100644 (file)
@@ -667,7 +667,7 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
        struct buffer_head *bh;
        struct page *page;
        void *kaddr, *ptr;
-       struct gfs2_quota q, *qp;
+       struct gfs2_quota q;
        int err, nbytes;
        u64 size;
 
@@ -683,28 +683,25 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
                return err;
 
        err = -EIO;
-       qp = &q;
-       qp->qu_value = be64_to_cpu(qp->qu_value);
-       qp->qu_value += change;
-       qp->qu_value = cpu_to_be64(qp->qu_value);
-       qd->qd_qb.qb_value = qp->qu_value;
+       be64_add_cpu(&q.qu_value, change);
+       qd->qd_qb.qb_value = q.qu_value;
        if (fdq) {
                if (fdq->d_fieldmask & FS_DQ_BSOFT) {
-                       qp->qu_warn = cpu_to_be64(fdq->d_blk_softlimit >> sdp->sd_fsb2bb_shift);
-                       qd->qd_qb.qb_warn = qp->qu_warn;
+                       q.qu_warn = cpu_to_be64(fdq->d_blk_softlimit >> sdp->sd_fsb2bb_shift);
+                       qd->qd_qb.qb_warn = q.qu_warn;
                }
                if (fdq->d_fieldmask & FS_DQ_BHARD) {
-                       qp->qu_limit = cpu_to_be64(fdq->d_blk_hardlimit >> sdp->sd_fsb2bb_shift);
-                       qd->qd_qb.qb_limit = qp->qu_limit;
+                       q.qu_limit = cpu_to_be64(fdq->d_blk_hardlimit >> sdp->sd_fsb2bb_shift);
+                       qd->qd_qb.qb_limit = q.qu_limit;
                }
                if (fdq->d_fieldmask & FS_DQ_BCOUNT) {
-                       qp->qu_value = cpu_to_be64(fdq->d_bcount >> sdp->sd_fsb2bb_shift);
-                       qd->qd_qb.qb_value = qp->qu_value;
+                       q.qu_value = cpu_to_be64(fdq->d_bcount >> sdp->sd_fsb2bb_shift);
+                       qd->qd_qb.qb_value = q.qu_value;
                }
        }
 
        /* Write the quota into the quota file on disk */
-       ptr = qp;
+       ptr = &q;
        nbytes = sizeof(struct gfs2_quota);
 get_a_page:
        page = find_or_create_page(mapping, index, GFP_NOFS);
index 4d83abd..c8d6161 100644 (file)
@@ -1127,7 +1127,7 @@ int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd)
                rgd->rd_flags |= (GFS2_RDF_UPTODATE | GFS2_RDF_CHECK);
                rgd->rd_free_clone = rgd->rd_free;
        }
-       if (be32_to_cpu(GFS2_MAGIC) != rgd->rd_rgl->rl_magic) {
+       if (cpu_to_be32(GFS2_MAGIC) != rgd->rd_rgl->rl_magic) {
                rgd->rd_rgl->rl_unlinked = cpu_to_be32(count_unlinked(rgd));
                gfs2_rgrp_ondisk2lvb(rgd->rd_rgl,
                                     rgd->rd_bits[0].bi_bh->b_data);
@@ -1161,7 +1161,7 @@ int update_rgrp_lvb(struct gfs2_rgrpd *rgd)
        if (rgd->rd_flags & GFS2_RDF_UPTODATE)
                return 0;
 
-       if (be32_to_cpu(GFS2_MAGIC) != rgd->rd_rgl->rl_magic)
+       if (cpu_to_be32(GFS2_MAGIC) != rgd->rd_rgl->rl_magic)
                return gfs2_rgrp_bh_get(rgd);
 
        rl_flags = be32_to_cpu(rgd->rd_rgl->rl_flags);
index efc85b1..3c6136f 100644 (file)
@@ -129,7 +129,7 @@ static int can_set_xattr(struct inode *inode, const char *name,
 
 static void hfsplus_init_header_node(struct inode *attr_file,
                                        u32 clump_size,
-                                       char *buf, size_t node_size)
+                                       char *buf, u16 node_size)
 {
        struct hfs_bnode_desc *desc;
        struct hfs_btree_header_rec *head;
@@ -139,8 +139,9 @@ static void hfsplus_init_header_node(struct inode *attr_file,
        char *bmp;
        u32 used_nodes;
        u32 used_bmp_bytes;
+       loff_t tmp;
 
-       hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %zu\n",
+       hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %u\n",
                                clump_size, node_size);
 
        /* The end of the node contains list of record offsets */
@@ -154,7 +155,9 @@ static void hfsplus_init_header_node(struct inode *attr_file,
 
        head = (struct hfs_btree_header_rec *)(buf + offset);
        head->node_size = cpu_to_be16(node_size);
-       head->node_count = cpu_to_be32(i_size_read(attr_file) / node_size);
+       tmp = i_size_read(attr_file);
+       do_div(tmp, node_size);
+       head->node_count = cpu_to_be32(tmp);
        head->free_nodes = cpu_to_be32(be32_to_cpu(head->node_count) - 1);
        head->clump_size = cpu_to_be32(clump_size);
        head->attributes |= cpu_to_be32(HFS_TREE_BIGKEYS | HFS_TREE_VARIDXKEYS);
index 2543728..db23ce1 100644 (file)
@@ -33,15 +33,6 @@ static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
 
 #define FILE_HOSTFS_I(file) HOSTFS_I(file_inode(file))
 
-static int hostfs_d_delete(const struct dentry *dentry)
-{
-       return 1;
-}
-
-static const struct dentry_operations hostfs_dentry_ops = {
-       .d_delete               = hostfs_d_delete,
-};
-
 /* Changed in hostfs_args before the kernel starts running */
 static char *root_ino = "";
 static int append = 0;
@@ -925,7 +916,7 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
        sb->s_blocksize_bits = 10;
        sb->s_magic = HOSTFS_SUPER_MAGIC;
        sb->s_op = &hostfs_sbops;
-       sb->s_d_op = &hostfs_dentry_ops;
+       sb->s_d_op = &simple_dentry_operations;
        sb->s_maxbytes = MAX_LFS_FILESIZE;
 
        /* NULL is printed as <NULL> by sprintf: avoid that. */
index 5de0694..a184424 100644 (file)
@@ -47,10 +47,16 @@ EXPORT_SYMBOL(simple_statfs);
  * Retaining negative dentries for an in-memory filesystem just wastes
  * memory and lookup time: arrange for them to be deleted immediately.
  */
-static int simple_delete_dentry(const struct dentry *dentry)
+int always_delete_dentry(const struct dentry *dentry)
 {
        return 1;
 }
+EXPORT_SYMBOL(always_delete_dentry);
+
+const struct dentry_operations simple_dentry_operations = {
+       .d_delete = always_delete_dentry,
+};
+EXPORT_SYMBOL(simple_dentry_operations);
 
 /*
  * Lookup the data. This is trivial - if the dentry didn't already
@@ -58,10 +64,6 @@ static int simple_delete_dentry(const struct dentry *dentry)
  */
 struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
 {
-       static const struct dentry_operations simple_dentry_operations = {
-               .d_delete = simple_delete_dentry,
-       };
-
        if (dentry->d_name.len > NAME_MAX)
                return ERR_PTR(-ENAMETOOLONG);
        if (!dentry->d_sb->s_d_op)
index e029a4c..8f77a8c 100644 (file)
@@ -2435,6 +2435,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
  */
 static inline int may_create(struct inode *dir, struct dentry *child)
 {
+       audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
        if (child->d_inode)
                return -EEXIST;
        if (IS_DEADDIR(dir))
index 38c1768..3dece03 100644 (file)
@@ -116,17 +116,17 @@ config NFS_V4_2
 config PNFS_FILE_LAYOUT
        tristate
        depends on NFS_V4_1
-       default m
+       default NFS_V4
 
 config PNFS_BLOCK
        tristate
        depends on NFS_V4_1 && BLK_DEV_DM
-       default m
+       default NFS_V4
 
 config PNFS_OBJLAYOUT
        tristate
        depends on NFS_V4_1 && SCSI_OSD_ULD
-       default m
+       default NFS_V4
 
 config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN
        string "NFSv4.1 Implementation ID Domain"
index c8e729d..059c01b 100644 (file)
@@ -244,7 +244,7 @@ static int nfs4_drain_slot_tbl(struct nfs4_slot_table *tbl)
        set_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state);
        spin_lock(&tbl->slot_tbl_lock);
        if (tbl->highest_used_slotid != NFS4_NO_SLOT) {
-               INIT_COMPLETION(tbl->complete);
+               reinit_completion(&tbl->complete);
                spin_unlock(&tbl->slot_tbl_lock);
                return wait_for_completion_interruptible(&tbl->complete);
        }
@@ -2093,10 +2093,15 @@ again:
                        nfs4_root_machine_cred(clp);
                        goto again;
                }
-               if (i > 2)
+               if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX)
                        break;
        case -NFS4ERR_CLID_INUSE:
        case -NFS4ERR_WRONGSEC:
+               /* No point in retrying if we already used RPC_AUTH_UNIX */
+               if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX) {
+                       status = -EPERM;
+                       break;
+               }
                clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX);
                if (IS_ERR(clnt)) {
                        status = PTR_ERR(clnt);
index 317d6fc..910ed90 100644 (file)
@@ -1614,7 +1614,7 @@ static int nfs_parse_mount_options(char *raw,
                goto out_minorversion_mismatch;
 
        if (mnt->options & NFS_OPTION_MIGRATION &&
-           mnt->version != 4 && mnt->minorversion != 0)
+           (mnt->version != 4 || mnt->minorversion != 0))
                goto out_migration_misuse;
 
        /*
index dc8f1ef..f994e75 100644 (file)
@@ -95,7 +95,7 @@ config NFSD_V4_SECURITY_LABEL
        Smack policies on NFSv4 files, say N.
 
        WARNING: there is still a chance of backwards-incompatible protocol changes.
-       For now we recommend "Y" only for developers and testers."
+       For now we recommend "Y" only for developers and testers.
 
 config NFSD_FAULT_INJECTION
        bool "NFS server manual fault injection"
index 5f38ea3..8513c59 100644 (file)
@@ -536,16 +536,12 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
                if (err)
                        goto out3;
                exp.ex_anon_uid= make_kuid(&init_user_ns, an_int);
-               if (!uid_valid(exp.ex_anon_uid))
-                       goto out3;
 
                /* anon gid */
                err = get_int(&mesg, &an_int);
                if (err)
                        goto out3;
                exp.ex_anon_gid= make_kgid(&init_user_ns, an_int);
-               if (!gid_valid(exp.ex_anon_gid))
-                       goto out3;
 
                /* fsid */
                err = get_int(&mesg, &an_int);
@@ -583,6 +579,26 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
                                   exp.ex_uuid);
                if (err)
                        goto out4;
+               /*
+                * No point caching this if it would immediately expire.
+                * Also, this protects exportfs's dummy export from the
+                * anon_uid/anon_gid checks:
+                */
+               if (exp.h.expiry_time < seconds_since_boot())
+                       goto out4;
+               /*
+                * For some reason exportfs has been passing down an
+                * invalid (-1) uid & gid on the "dummy" export which it
+                * uses to test export support.  To make sure exportfs
+                * sees errors from check_export we therefore need to
+                * delay these checks till after check_export:
+                */
+               err = -EINVAL;
+               if (!uid_valid(exp.ex_anon_uid))
+                       goto out4;
+               if (!gid_valid(exp.ex_anon_gid))
+                       goto out4;
+               err = 0;
        }
 
        expp = svc_export_lookup(&exp);
index f36a30a..105d6fa 100644 (file)
@@ -402,11 +402,16 @@ static void remove_stid(struct nfs4_stid *s)
        idr_remove(stateids, s->sc_stateid.si_opaque.so_id);
 }
 
+static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
+{
+       kmem_cache_free(slab, s);
+}
+
 void
 nfs4_put_delegation(struct nfs4_delegation *dp)
 {
        if (atomic_dec_and_test(&dp->dl_count)) {
-               kmem_cache_free(deleg_slab, dp);
+               nfs4_free_stid(deleg_slab, &dp->dl_stid);
                num_delegations--;
        }
 }
@@ -610,7 +615,7 @@ static void close_generic_stateid(struct nfs4_ol_stateid *stp)
 static void free_generic_stateid(struct nfs4_ol_stateid *stp)
 {
        remove_stid(&stp->st_stid);
-       kmem_cache_free(stateid_slab, stp);
+       nfs4_free_stid(stateid_slab, &stp->st_stid);
 }
 
 static void release_lock_stateid(struct nfs4_ol_stateid *stp)
@@ -668,7 +673,6 @@ static void unhash_open_stateid(struct nfs4_ol_stateid *stp)
 static void release_open_stateid(struct nfs4_ol_stateid *stp)
 {
        unhash_open_stateid(stp);
-       unhash_stid(&stp->st_stid);
        free_generic_stateid(stp);
 }
 
@@ -690,7 +694,6 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo)
        struct nfs4_ol_stateid *s = oo->oo_last_closed_stid;
 
        if (s) {
-               unhash_stid(&s->st_stid);
                free_generic_stateid(s);
                oo->oo_last_closed_stid = NULL;
        }
@@ -1127,6 +1130,11 @@ destroy_client(struct nfs4_client *clp)
                dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
                destroy_delegation(dp);
        }
+       list_splice_init(&clp->cl_revoked, &reaplist);
+       while (!list_empty(&reaplist)) {
+               dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
+               destroy_revoked_delegation(dp);
+       }
        while (!list_empty(&clp->cl_openowners)) {
                oo = list_entry(clp->cl_openowners.next, struct nfs4_openowner, oo_perclient);
                release_openowner(oo);
@@ -3154,7 +3162,7 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
        open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
        return;
 out_free:
-       unhash_stid(&dp->dl_stid);
+       remove_stid(&dp->dl_stid);
        nfs4_put_delegation(dp);
 out_no_deleg:
        open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE;
@@ -3995,10 +4003,9 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        nfsd4_close_open_stateid(stp);
 
-       if (cstate->minorversion) {
-               unhash_stid(&stp->st_stid);
+       if (cstate->minorversion)
                free_generic_stateid(stp);
-       else
+       else
                oo->oo_last_closed_stid = stp;
 
        if (list_empty(&oo->oo_owner.so_stateids)) {
@@ -5119,7 +5126,6 @@ out_recovery:
        return ret;
 }
 
-/* should be called with the state lock held */
 void
 nfs4_state_shutdown_net(struct net *net)
 {
@@ -5130,6 +5136,7 @@ nfs4_state_shutdown_net(struct net *net)
        cancel_delayed_work_sync(&nn->laundromat_work);
        locks_end_grace(&nn->nfsd4_manager);
 
+       nfs4_lock_state();
        INIT_LIST_HEAD(&reaplist);
        spin_lock(&recall_lock);
        list_for_each_safe(pos, next, &nn->del_recall_lru) {
@@ -5144,6 +5151,7 @@ nfs4_state_shutdown_net(struct net *net)
 
        nfsd4_client_tracking_exit(net);
        nfs4_state_destroy_net(net);
+       nfs4_unlock_state();
 }
 
 void
index d9454fe..ee7237f 100644 (file)
@@ -141,8 +141,8 @@ xdr_error:                                  \
 
 static void next_decode_page(struct nfsd4_compoundargs *argp)
 {
-       argp->pagelist++;
        argp->p = page_address(argp->pagelist[0]);
+       argp->pagelist++;
        if (argp->pagelen < PAGE_SIZE) {
                argp->end = argp->p + (argp->pagelen>>2);
                argp->pagelen = 0;
@@ -411,6 +411,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
                label->data = kzalloc(dummy32 + 1, GFP_KERNEL);
                if (!label->data)
                        return nfserr_jukebox;
+               label->len = dummy32;
                defer_free(argp, kfree, label->data);
                memcpy(label->data, buf, dummy32);
        }
@@ -945,13 +946,16 @@ static __be32
 nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf)
 {
        DECODE_HEAD;
-                   
+
+       if (argp->minorversion >= 1)
+               return nfserr_notsupp;
+
        status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
        if (status)
                return status;
        READ_BUF(4);
        READ32(open_conf->oc_seqid);
-                                                       
+
        DECODE_TAIL;
 }
 
@@ -991,6 +995,14 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
 }
 
 static __be32
+nfsd4_decode_putpubfh(struct nfsd4_compoundargs *argp, void *p)
+{
+       if (argp->minorversion == 0)
+               return nfs_ok;
+       return nfserr_notsupp;
+}
+
+static __be32
 nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
 {
        DECODE_HEAD;
@@ -1061,6 +1073,9 @@ nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
 {
        DECODE_HEAD;
 
+       if (argp->minorversion >= 1)
+               return nfserr_notsupp;
+
        READ_BUF(sizeof(clientid_t));
        COPYMEM(clientid, sizeof(clientid_t));
 
@@ -1111,6 +1126,9 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient
 {
        DECODE_HEAD;
 
+       if (argp->minorversion >= 1)
+               return nfserr_notsupp;
+
        READ_BUF(NFS4_VERIFIER_SIZE);
        COPYMEM(setclientid->se_verf.data, NFS4_VERIFIER_SIZE);
 
@@ -1137,6 +1155,9 @@ nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_s
 {
        DECODE_HEAD;
 
+       if (argp->minorversion >= 1)
+               return nfserr_notsupp;
+
        READ_BUF(8 + NFS4_VERIFIER_SIZE);
        COPYMEM(&scd_c->sc_clientid, 8);
        COPYMEM(&scd_c->sc_confirm, NFS4_VERIFIER_SIZE);
@@ -1208,6 +1229,7 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
                len -= pages * PAGE_SIZE;
 
                argp->p = (__be32 *)page_address(argp->pagelist[0]);
+               argp->pagelist++;
                argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
        }
        argp->p += XDR_QUADLEN(len);
@@ -1220,6 +1242,9 @@ nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_rel
 {
        DECODE_HEAD;
 
+       if (argp->minorversion >= 1)
+               return nfserr_notsupp;
+
        READ_BUF(12);
        COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
        READ32(rlockowner->rl_owner.len);
@@ -1519,7 +1544,7 @@ static nfsd4_dec nfsd4_dec_ops[] = {
        [OP_OPEN_CONFIRM]       = (nfsd4_dec)nfsd4_decode_open_confirm,
        [OP_OPEN_DOWNGRADE]     = (nfsd4_dec)nfsd4_decode_open_downgrade,
        [OP_PUTFH]              = (nfsd4_dec)nfsd4_decode_putfh,
-       [OP_PUTPUBFH]           = (nfsd4_dec)nfsd4_decode_noop,
+       [OP_PUTPUBFH]           = (nfsd4_dec)nfsd4_decode_putpubfh,
        [OP_PUTROOTFH]          = (nfsd4_dec)nfsd4_decode_noop,
        [OP_READ]               = (nfsd4_dec)nfsd4_decode_read,
        [OP_READDIR]            = (nfsd4_dec)nfsd4_decode_readdir,
@@ -1536,46 +1561,6 @@ static nfsd4_dec nfsd4_dec_ops[] = {
        [OP_VERIFY]             = (nfsd4_dec)nfsd4_decode_verify,
        [OP_WRITE]              = (nfsd4_dec)nfsd4_decode_write,
        [OP_RELEASE_LOCKOWNER]  = (nfsd4_dec)nfsd4_decode_release_lockowner,
-};
-
-static nfsd4_dec nfsd41_dec_ops[] = {
-       [OP_ACCESS]             = (nfsd4_dec)nfsd4_decode_access,
-       [OP_CLOSE]              = (nfsd4_dec)nfsd4_decode_close,
-       [OP_COMMIT]             = (nfsd4_dec)nfsd4_decode_commit,
-       [OP_CREATE]             = (nfsd4_dec)nfsd4_decode_create,
-       [OP_DELEGPURGE]         = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_DELEGRETURN]        = (nfsd4_dec)nfsd4_decode_delegreturn,
-       [OP_GETATTR]            = (nfsd4_dec)nfsd4_decode_getattr,
-       [OP_GETFH]              = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_LINK]               = (nfsd4_dec)nfsd4_decode_link,
-       [OP_LOCK]               = (nfsd4_dec)nfsd4_decode_lock,
-       [OP_LOCKT]              = (nfsd4_dec)nfsd4_decode_lockt,
-       [OP_LOCKU]              = (nfsd4_dec)nfsd4_decode_locku,
-       [OP_LOOKUP]             = (nfsd4_dec)nfsd4_decode_lookup,
-       [OP_LOOKUPP]            = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_NVERIFY]            = (nfsd4_dec)nfsd4_decode_verify,
-       [OP_OPEN]               = (nfsd4_dec)nfsd4_decode_open,
-       [OP_OPENATTR]           = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_OPEN_CONFIRM]       = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_OPEN_DOWNGRADE]     = (nfsd4_dec)nfsd4_decode_open_downgrade,
-       [OP_PUTFH]              = (nfsd4_dec)nfsd4_decode_putfh,
-       [OP_PUTPUBFH]           = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_PUTROOTFH]          = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_READ]               = (nfsd4_dec)nfsd4_decode_read,
-       [OP_READDIR]            = (nfsd4_dec)nfsd4_decode_readdir,
-       [OP_READLINK]           = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_REMOVE]             = (nfsd4_dec)nfsd4_decode_remove,
-       [OP_RENAME]             = (nfsd4_dec)nfsd4_decode_rename,
-       [OP_RENEW]              = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_RESTOREFH]          = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_SAVEFH]             = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_SECINFO]            = (nfsd4_dec)nfsd4_decode_secinfo,
-       [OP_SETATTR]            = (nfsd4_dec)nfsd4_decode_setattr,
-       [OP_SETCLIENTID]        = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_SETCLIENTID_CONFIRM]= (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_VERIFY]             = (nfsd4_dec)nfsd4_decode_verify,
-       [OP_WRITE]              = (nfsd4_dec)nfsd4_decode_write,
-       [OP_RELEASE_LOCKOWNER]  = (nfsd4_dec)nfsd4_decode_notsupp,
 
        /* new operations for NFSv4.1 */
        [OP_BACKCHANNEL_CTL]    = (nfsd4_dec)nfsd4_decode_backchannel_ctl,
@@ -1599,24 +1584,53 @@ static nfsd4_dec nfsd41_dec_ops[] = {
        [OP_RECLAIM_COMPLETE]   = (nfsd4_dec)nfsd4_decode_reclaim_complete,
 };
 
-struct nfsd4_minorversion_ops {
-       nfsd4_dec *decoders;
-       int nops;
-};
+static inline bool
+nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op)
+{
+       if (op->opnum < FIRST_NFS4_OP)
+               return false;
+       else if (argp->minorversion == 0 && op->opnum > LAST_NFS40_OP)
+               return false;
+       else if (argp->minorversion == 1 && op->opnum > LAST_NFS41_OP)
+               return false;
+       else if (argp->minorversion == 2 && op->opnum > LAST_NFS42_OP)
+               return false;
+       return true;
+}
 
-static struct nfsd4_minorversion_ops nfsd4_minorversion[] = {
-       [0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) },
-       [1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
-       [2] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
-};
+/*
+ * Return a rough estimate of the maximum possible reply size.  Note the
+ * estimate includes rpc headers so is meant to be passed to
+ * svc_reserve, not svc_reserve_auth.
+ *
+ * Also note the current compound encoding permits only one operation to
+ * use pages beyond the first one, so the maximum possible length is the
+ * maximum over these values, not the sum.
+ */
+static int nfsd4_max_reply(u32 opnum)
+{
+       switch (opnum) {
+       case OP_READLINK:
+       case OP_READDIR:
+               /*
+                * Both of these ops take a single page for data and put
+                * the head and tail in another page:
+                */
+               return 2 * PAGE_SIZE;
+       case OP_READ:
+               return INT_MAX;
+       default:
+               return PAGE_SIZE;
+       }
+}
 
 static __be32
 nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
 {
        DECODE_HEAD;
        struct nfsd4_op *op;
-       struct nfsd4_minorversion_ops *ops;
        bool cachethis = false;
+       int max_reply = PAGE_SIZE;
        int i;
 
        READ_BUF(4);
@@ -1640,10 +1654,9 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                }
        }
 
-       if (argp->minorversion >= ARRAY_SIZE(nfsd4_minorversion))
+       if (argp->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
                argp->opcnt = 0;
 
-       ops = &nfsd4_minorversion[argp->minorversion];
        for (i = 0; i < argp->opcnt; i++) {
                op = &argp->ops[i];
                op->replay = NULL;
@@ -1651,8 +1664,8 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                READ_BUF(4);
                READ32(op->opnum);
 
-               if (op->opnum >= FIRST_NFS4_OP && op->opnum <= LAST_NFS4_OP)
-                       op->status = ops->decoders[op->opnum](argp, &op->u);
+               if (nfsd4_opnum_in_range(argp, op))
+                       op->status = nfsd4_dec_ops[op->opnum](argp, &op->u);
                else {
                        op->opnum = OP_ILLEGAL;
                        op->status = nfserr_op_illegal;
@@ -1667,10 +1680,14 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                 * op in the compound wants to be cached:
                 */
                cachethis |= nfsd4_cache_this_op(op);
+
+               max_reply = max(max_reply, nfsd4_max_reply(op->opnum));
        }
        /* Sessions make the DRC unnecessary: */
        if (argp->minorversion)
                cachethis = false;
+       if (max_reply != INT_MAX)
+               svc_reserve(argp->rqstp, max_reply);
        argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
 
        DECODE_TAIL;
@@ -2375,7 +2392,7 @@ out_acl:
        if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
                if ((buflen -= 8) < 0)
                        goto out_resource;
-               WRITE64(~(u64)0);
+               WRITE64(exp->ex_path.mnt->mnt_sb->s_maxbytes);
        }
        if (bmval0 & FATTR4_WORD0_MAXLINK) {
                if ((buflen -= 4) < 0)
index 3d0e15a..3c37b16 100644 (file)
@@ -598,22 +598,20 @@ fh_update(struct svc_fh *fhp)
                _fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle);
        } else {
                if (fhp->fh_handle.fh_fileid_type != FILEID_ROOT)
-                       goto out;
+                       return 0;
 
                _fh_update(fhp, fhp->fh_export, dentry);
                if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID)
                        return nfserr_opnotsupp;
        }
-out:
        return 0;
-
 out_bad:
        printk(KERN_ERR "fh_update: fh not verified!\n");
-       goto out;
+       return nfserr_serverfault;
 out_negative:
        printk(KERN_ERR "fh_update: %pd2 still negative!\n",
                dentry);
-       goto out;
+       return nfserr_serverfault;
 }
 
 /*
index 94b5f5d..7eea63c 100644 (file)
@@ -298,41 +298,12 @@ commit_metadata(struct svc_fh *fhp)
 }
 
 /*
- * Set various file attributes.
- * N.B. After this call fhp needs an fh_put
+ * Go over the attributes and take care of the small differences between
+ * NFS semantics and what Linux expects.
  */
-__be32
-nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
-            int check_guard, time_t guardtime)
+static void
+nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
 {
-       struct dentry   *dentry;
-       struct inode    *inode;
-       int             accmode = NFSD_MAY_SATTR;
-       umode_t         ftype = 0;
-       __be32          err;
-       int             host_err;
-       int             size_change = 0;
-
-       if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
-               accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
-       if (iap->ia_valid & ATTR_SIZE)
-               ftype = S_IFREG;
-
-       /* Get inode */
-       err = fh_verify(rqstp, fhp, ftype, accmode);
-       if (err)
-               goto out;
-
-       dentry = fhp->fh_dentry;
-       inode = dentry->d_inode;
-
-       /* Ignore any mode updates on symlinks */
-       if (S_ISLNK(inode->i_mode))
-               iap->ia_valid &= ~ATTR_MODE;
-
-       if (!iap->ia_valid)
-               goto out;
-
        /*
         * NFSv2 does not differentiate between "set-[ac]time-to-now"
         * which only requires access, and "set-[ac]time-to-X" which
@@ -342,8 +313,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
         * convert to "set to now" instead of "set to explicit time"
         *
         * We only call inode_change_ok as the last test as technically
-        * it is not an interface that we should be using.  It is only
-        * valid if the filesystem does not define it's own i_op->setattr.
+        * it is not an interface that we should be using.
         */
 #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
 #define        MAX_TOUCH_TIME_ERROR (30*60)
@@ -369,30 +339,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
                        iap->ia_valid &= ~BOTH_TIME_SET;
                }
        }
-           
-       /*
-        * The size case is special.
-        * It changes the file as well as the attributes.
-        */
-       if (iap->ia_valid & ATTR_SIZE) {
-               if (iap->ia_size < inode->i_size) {
-                       err = nfsd_permission(rqstp, fhp->fh_export, dentry,
-                                       NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE);
-                       if (err)
-                               goto out;
-               }
-
-               host_err = get_write_access(inode);
-               if (host_err)
-                       goto out_nfserr;
-
-               size_change = 1;
-               host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
-               if (host_err) {
-                       put_write_access(inode);
-                       goto out_nfserr;
-               }
-       }
 
        /* sanitize the mode change */
        if (iap->ia_valid & ATTR_MODE) {
@@ -415,32 +361,111 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
                        iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID);
                }
        }
+}
 
-       /* Change the attributes. */
+static __be32
+nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
+               struct iattr *iap)
+{
+       struct inode *inode = fhp->fh_dentry->d_inode;
+       int host_err;
 
-       iap->ia_valid |= ATTR_CTIME;
+       if (iap->ia_size < inode->i_size) {
+               __be32 err;
 
-       err = nfserr_notsync;
-       if (!check_guard || guardtime == inode->i_ctime.tv_sec) {
-               host_err = nfsd_break_lease(inode);
-               if (host_err)
-                       goto out_nfserr;
-               fh_lock(fhp);
+               err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
+                               NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
+               if (err)
+                       return err;
+       }
 
-               host_err = notify_change(dentry, iap, NULL);
-               err = nfserrno(host_err);
-               fh_unlock(fhp);
+       host_err = get_write_access(inode);
+       if (host_err)
+               goto out_nfserrno;
+
+       host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
+       if (host_err)
+               goto out_put_write_access;
+       return 0;
+
+out_put_write_access:
+       put_write_access(inode);
+out_nfserrno:
+       return nfserrno(host_err);
+}
+
+/*
+ * Set various file attributes.  After this call fhp needs an fh_put.
+ */
+__be32
+nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
+            int check_guard, time_t guardtime)
+{
+       struct dentry   *dentry;
+       struct inode    *inode;
+       int             accmode = NFSD_MAY_SATTR;
+       umode_t         ftype = 0;
+       __be32          err;
+       int             host_err;
+       int             size_change = 0;
+
+       if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
+               accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
+       if (iap->ia_valid & ATTR_SIZE)
+               ftype = S_IFREG;
+
+       /* Get inode */
+       err = fh_verify(rqstp, fhp, ftype, accmode);
+       if (err)
+               goto out;
+
+       dentry = fhp->fh_dentry;
+       inode = dentry->d_inode;
+
+       /* Ignore any mode updates on symlinks */
+       if (S_ISLNK(inode->i_mode))
+               iap->ia_valid &= ~ATTR_MODE;
+
+       if (!iap->ia_valid)
+               goto out;
+
+       nfsd_sanitize_attrs(inode, iap);
+
+       /*
+        * The size case is special, it changes the file in addition to the
+        * attributes.
+        */
+       if (iap->ia_valid & ATTR_SIZE) {
+               err = nfsd_get_write_access(rqstp, fhp, iap);
+               if (err)
+                       goto out;
+               size_change = 1;
        }
+
+       iap->ia_valid |= ATTR_CTIME;
+
+       if (check_guard && guardtime != inode->i_ctime.tv_sec) {
+               err = nfserr_notsync;
+               goto out_put_write_access;
+       }
+
+       host_err = nfsd_break_lease(inode);
+       if (host_err)
+               goto out_put_write_access_nfserror;
+
+       fh_lock(fhp);
+       host_err = notify_change(dentry, iap, NULL);
+       fh_unlock(fhp);
+
+out_put_write_access_nfserror:
+       err = nfserrno(host_err);
+out_put_write_access:
        if (size_change)
                put_write_access(inode);
        if (!err)
                commit_metadata(fhp);
 out:
        return err;
-
-out_nfserr:
-       err = nfserrno(host_err);
-       goto out;
 }
 
 #if defined(CONFIG_NFSD_V2_ACL) || \
index 3a44a64..3407b2c 100644 (file)
@@ -1304,7 +1304,7 @@ static int ocfs2_wait_for_mask(struct ocfs2_mask_waiter *mw)
 {
        wait_for_completion(&mw->mw_complete);
        /* Re-arm the completion in case we want to wait on it again */
-       INIT_COMPLETION(mw->mw_complete);
+       reinit_completion(&mw->mw_complete);
        return mw->mw_status;
 }
 
@@ -1355,7 +1355,7 @@ static int ocfs2_wait_for_mask_interruptible(struct ocfs2_mask_waiter *mw,
        else
                ret = mw->mw_status;
        /* Re-arm the completion in case we want to wait on it again */
-       INIT_COMPLETION(mw->mw_complete);
+       reinit_completion(&mw->mw_complete);
        return ret;
 }
 
index 1485e38..03c8d74 100644 (file)
@@ -1151,10 +1151,16 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
                goto out_free_page;
 
        }
-       kloginuid = make_kuid(file->f_cred->user_ns, loginuid);
-       if (!uid_valid(kloginuid)) {
-               length = -EINVAL;
-               goto out_free_page;
+
+       /* is userspace tring to explicitly UNSET the loginuid? */
+       if (loginuid == AUDIT_UID_UNSET) {
+               kloginuid = INVALID_UID;
+       } else {
+               kloginuid = make_kuid(file->f_cred->user_ns, loginuid);
+               if (!uid_valid(kloginuid)) {
+                       length = -EINVAL;
+                       goto out_free_page;
+               }
        }
 
        length = audit_set_loginuid(kloginuid);
index b701eaa..51942d5 100644 (file)
@@ -29,7 +29,6 @@ static int show_console_dev(struct seq_file *m, void *v)
        char flags[ARRAY_SIZE(con_flags) + 1];
        struct console *con = v;
        unsigned int a;
-       int len;
        dev_t dev = 0;
 
        if (con->device) {
@@ -47,11 +46,10 @@ static int show_console_dev(struct seq_file *m, void *v)
                        con_flags[a].name : ' ';
        flags[a] = 0;
 
-       seq_printf(m, "%s%d%n", con->name, con->index, &len);
-       len = 21 - len;
-       if (len < 1)
-               len = 1;
-       seq_printf(m, "%*c%c%c%c (%s)", len, ' ', con->read ? 'R' : '-',
+       seq_setwidth(m, 21 - 1);
+       seq_printf(m, "%s%d", con->name, con->index);
+       seq_pad(m, ' ');
+       seq_printf(m, "%c%c%c (%s)", con->read ? 'R' : '-',
                        con->write ? 'W' : '-', con->unblank ? 'U' : '-',
                        flags);
        if (dev)
index 737e156..cca93b6 100644 (file)
@@ -175,22 +175,6 @@ static const struct inode_operations proc_link_inode_operations = {
 };
 
 /*
- * As some entries in /proc are volatile, we want to 
- * get rid of unused dentries.  This could be made 
- * smarter: we could keep a "volatile" flag in the 
- * inode to indicate which ones to keep.
- */
-static int proc_delete_dentry(const struct dentry * dentry)
-{
-       return 1;
-}
-
-static const struct dentry_operations proc_dentry_operations =
-{
-       .d_delete       = proc_delete_dentry,
-};
-
-/*
  * Don't create negative dentries here, return -ENOENT by hand
  * instead.
  */
@@ -209,7 +193,7 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
                        inode = proc_get_inode(dir->i_sb, de);
                        if (!inode)
                                return ERR_PTR(-ENOMEM);
-                       d_set_d_op(dentry, &proc_dentry_operations);
+                       d_set_d_op(dentry, &simple_dentry_operations);
                        d_add(dentry, inode);
                        return NULL;
                }
index c805d5b..a77d2b2 100644 (file)
@@ -1,8 +1,8 @@
 #include <linux/fs.h>
-#include <linux/hugetlb.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/hugetlb.h>
 #include <linux/mman.h>
 #include <linux/mmzone.h>
 #include <linux/proc_fs.h>
index 49a7fff..9ae46b8 100644 (file)
@@ -42,12 +42,6 @@ static const struct inode_operations ns_inode_operations = {
        .setattr        = proc_setattr,
 };
 
-static int ns_delete_dentry(const struct dentry *dentry)
-{
-       /* Don't cache namespace inodes when not in use */
-       return 1;
-}
-
 static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
 {
        struct inode *inode = dentry->d_inode;
@@ -59,7 +53,7 @@ static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
 
 const struct dentry_operations ns_dentry_operations =
 {
-       .d_delete       = ns_delete_dentry,
+       .d_delete       = always_delete_dentry,
        .d_dname        = ns_dname,
 };
 
index ccfd99b..5f9bc8a 100644 (file)
@@ -39,7 +39,7 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region)
        unsigned long ino = 0;
        struct file *file;
        dev_t dev = 0;
-       int flags, len;
+       int flags;
 
        flags = region->vm_flags;
        file = region->vm_file;
@@ -50,8 +50,9 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region)
                ino = inode->i_ino;
        }
 
+       seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
        seq_printf(m,
-                  "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n",
+                  "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
                   region->vm_start,
                   region->vm_end,
                   flags & VM_READ ? 'r' : '-',
@@ -59,13 +60,10 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region)
                   flags & VM_EXEC ? 'x' : '-',
                   flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p',
                   ((loff_t)region->vm_pgoff) << PAGE_SHIFT,
-                  MAJOR(dev), MINOR(dev), ino, &len);
+                  MAJOR(dev), MINOR(dev), ino);
 
        if (file) {
-               len = 25 + sizeof(void *) * 6 - len;
-               if (len < 1)
-                       len = 1;
-               seq_printf(m, "%*c", len, ' ');
+               seq_pad(m, ' ');
                seq_path(m, &file->f_path, "");
        }
 
index abbe825..fb52b54 100644 (file)
@@ -62,7 +62,8 @@ void task_mem(struct seq_file *m, struct mm_struct *mm)
                total_rss << (PAGE_SHIFT-10),
                data << (PAGE_SHIFT-10),
                mm->stack_vm << (PAGE_SHIFT-10), text, lib,
-               (PTRS_PER_PTE*sizeof(pte_t)*mm->nr_ptes) >> 10,
+               (PTRS_PER_PTE * sizeof(pte_t) *
+                atomic_long_read(&mm->nr_ptes)) >> 10,
                swap << (PAGE_SHIFT-10));
 }
 
@@ -83,14 +84,6 @@ unsigned long task_statm(struct mm_struct *mm,
        return mm->total_vm;
 }
 
-static void pad_len_spaces(struct seq_file *m, int len)
-{
-       len = 25 + sizeof(void*) * 6 - len;
-       if (len < 1)
-               len = 1;
-       seq_printf(m, "%*c", len, ' ');
-}
-
 #ifdef CONFIG_NUMA
 /*
  * These functions are for numa_maps but called in generic **maps seq_file
@@ -268,7 +261,6 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
        unsigned long long pgoff = 0;
        unsigned long start, end;
        dev_t dev = 0;
-       int len;
        const char *name = NULL;
 
        if (file) {
@@ -286,7 +278,8 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
        if (stack_guard_page_end(vma, end))
                end -= PAGE_SIZE;
 
-       seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n",
+       seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
+       seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
                        start,
                        end,
                        flags & VM_READ ? 'r' : '-',
@@ -294,14 +287,14 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
                        flags & VM_EXEC ? 'x' : '-',
                        flags & VM_MAYSHARE ? 's' : 'p',
                        pgoff,
-                       MAJOR(dev), MINOR(dev), ino, &len);
+                       MAJOR(dev), MINOR(dev), ino);
 
        /*
         * Print the dentry name for named mappings, and a
         * special [heap] marker for the heap:
         */
        if (file) {
-               pad_len_spaces(m, len);
+               seq_pad(m, ' ');
                seq_path(m, &file->f_path, "\n");
                goto done;
        }
@@ -333,7 +326,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
                                name = "[stack]";
                        } else {
                                /* Thread stack in /proc/PID/maps */
-                               pad_len_spaces(m, len);
+                               seq_pad(m, ' ');
                                seq_printf(m, "[stack:%d]", tid);
                        }
                }
@@ -341,7 +334,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
 
 done:
        if (name) {
-               pad_len_spaces(m, len);
+               seq_pad(m, ' ');
                seq_puts(m, name);
        }
        seq_putc(m, '\n');
@@ -505,9 +498,9 @@ static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
        pte_t *pte;
        spinlock_t *ptl;
 
-       if (pmd_trans_huge_lock(pmd, vma) == 1) {
+       if (pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
                smaps_pte_entry(*(pte_t *)pmd, addr, HPAGE_PMD_SIZE, walk);
-               spin_unlock(&walk->mm->page_table_lock);
+               spin_unlock(ptl);
                mss->anonymous_thp += HPAGE_PMD_SIZE;
                return 0;
        }
@@ -998,13 +991,14 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 {
        struct vm_area_struct *vma;
        struct pagemapread *pm = walk->private;
+       spinlock_t *ptl;
        pte_t *pte;
        int err = 0;
        pagemap_entry_t pme = make_pme(PM_NOT_PRESENT(pm->v2));
 
        /* find the first VMA at or above 'addr' */
        vma = find_vma(walk->mm, addr);
-       if (vma && pmd_trans_huge_lock(pmd, vma) == 1) {
+       if (vma && pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
                int pmd_flags2;
 
                if ((vma->vm_flags & VM_SOFTDIRTY) || pmd_soft_dirty(*pmd))
@@ -1022,7 +1016,7 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
                        if (err)
                                break;
                }
-               spin_unlock(&walk->mm->page_table_lock);
+               spin_unlock(ptl);
                return err;
        }
 
@@ -1324,7 +1318,7 @@ static int gather_pte_stats(pmd_t *pmd, unsigned long addr,
 
        md = walk->private;
 
-       if (pmd_trans_huge_lock(pmd, md->vma) == 1) {
+       if (pmd_trans_huge_lock(pmd, md->vma, &ptl) == 1) {
                pte_t huge_pte = *(pte_t *)pmd;
                struct page *page;
 
@@ -1332,7 +1326,7 @@ static int gather_pte_stats(pmd_t *pmd, unsigned long addr,
                if (page)
                        gather_stats(page, md, pte_dirty(huge_pte),
                                     HPAGE_PMD_SIZE/PAGE_SIZE);
-               spin_unlock(&walk->mm->page_table_lock);
+               spin_unlock(ptl);
                return 0;
        }
 
index 56123a6..678455d 100644 (file)
@@ -123,14 +123,6 @@ unsigned long task_statm(struct mm_struct *mm,
        return size;
 }
 
-static void pad_len_spaces(struct seq_file *m, int len)
-{
-       len = 25 + sizeof(void*) * 6 - len;
-       if (len < 1)
-               len = 1;
-       seq_printf(m, "%*c", len, ' ');
-}
-
 /*
  * display a single VMA to a sequenced file
  */
@@ -142,7 +134,7 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
        unsigned long ino = 0;
        struct file *file;
        dev_t dev = 0;
-       int flags, len;
+       int flags;
        unsigned long long pgoff = 0;
 
        flags = vma->vm_flags;
@@ -155,8 +147,9 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
                pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT;
        }
 
+       seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
        seq_printf(m,
-                  "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n",
+                  "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
                   vma->vm_start,
                   vma->vm_end,
                   flags & VM_READ ? 'r' : '-',
@@ -164,16 +157,16 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
                   flags & VM_EXEC ? 'x' : '-',
                   flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p',
                   pgoff,
-                  MAJOR(dev), MINOR(dev), ino, &len);
+                  MAJOR(dev), MINOR(dev), ino);
 
        if (file) {
-               pad_len_spaces(m, len);
+               seq_pad(m, ' ');
                seq_path(m, &file->f_path, "");
        } else if (mm) {
                pid_t tid = vm_is_stack(priv->task, vma, is_pid);
 
                if (tid != 0) {
-                       pad_len_spaces(m, len);
+                       seq_pad(m, ' ');
                        /*
                         * Thread stack in /proc/PID/task/TID/maps or
                         * the main process stack.
index 16e8abb..72d2917 100644 (file)
@@ -9,13 +9,25 @@
 #include <net/netlink.h>
 #include <net/genetlink.h>
 
+static const struct genl_multicast_group quota_mcgrps[] = {
+       { .name = "events", },
+};
+
 /* Netlink family structure for quota */
 static struct genl_family quota_genl_family = {
-       .id = GENL_ID_GENERATE,
+       /*
+        * Needed due to multicast group ID abuse - old code assumed
+        * the family ID was also a valid multicast group ID (which
+        * isn't true) and userspace might thus rely on it. Assign a
+        * static ID for this group to make dealing with that easier.
+        */
+       .id = GENL_ID_VFS_DQUOT,
        .hdrsize = 0,
        .name = "VFS_DQUOT",
        .version = 1,
        .maxattr = QUOTA_NL_A_MAX,
+       .mcgrps = quota_mcgrps,
+       .n_mcgrps = ARRAY_SIZE(quota_mcgrps),
 };
 
 /**
@@ -78,7 +90,7 @@ void quota_send_warning(struct kqid qid, dev_t dev,
                goto attr_err_out;
        genlmsg_end(skb, msg_head);
 
-       genlmsg_multicast(skb, 0, quota_genl_family.id, GFP_NOFS);
+       genlmsg_multicast(&quota_genl_family, skb, 0, 0, GFP_NOFS);
        return;
 attr_err_out:
        printk(KERN_ERR "VFS: Not enough space to compose quota message!\n");
index a290157..1d641bb 100644 (file)
@@ -136,6 +136,7 @@ static int traverse(struct seq_file *m, loff_t offset)
 Eoverflow:
        m->op->stop(m, p);
        kfree(m->buf);
+       m->count = 0;
        m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
        return !m->buf ? -ENOMEM : -EAGAIN;
 }
@@ -232,10 +233,10 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
                        goto Fill;
                m->op->stop(m, p);
                kfree(m->buf);
+               m->count = 0;
                m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
                if (!m->buf)
                        goto Enomem;
-               m->count = 0;
                m->version = 0;
                pos = m->index;
                p = m->op->start(m, &pos);
@@ -766,6 +767,21 @@ int seq_write(struct seq_file *seq, const void *data, size_t len)
 }
 EXPORT_SYMBOL(seq_write);
 
+/**
+ * seq_pad - write padding spaces to buffer
+ * @m: seq_file identifying the buffer to which data should be written
+ * @c: the byte to append after padding if non-zero
+ */
+void seq_pad(struct seq_file *m, char c)
+{
+       int size = m->pad_until - m->count;
+       if (size > 0)
+               seq_printf(m, "%*s", size, "");
+       if (c)
+               seq_putc(m, c);
+}
+EXPORT_SYMBOL(seq_pad);
+
 struct list_head *seq_list_start(struct list_head *head, loff_t pos)
 {
        struct list_head *lh;
index c70111e..b6fa865 100644 (file)
@@ -25,6 +25,78 @@ config SQUASHFS
 
          If unsure, say N.
 
+choice
+       prompt "File decompression options"
+       depends on SQUASHFS
+       help
+         Squashfs now supports two options for decompressing file
+         data.  Traditionally Squashfs has decompressed into an
+         intermediate buffer and then memcopied it into the page cache.
+         Squashfs now supports the ability to decompress directly into
+         the page cache.
+
+         If unsure, select "Decompress file data into an intermediate buffer"
+
+config SQUASHFS_FILE_CACHE
+       bool "Decompress file data into an intermediate buffer"
+       help
+         Decompress file data into an intermediate buffer and then
+         memcopy it into the page cache.
+
+config SQUASHFS_FILE_DIRECT
+       bool "Decompress files directly into the page cache"
+       help
+         Directly decompress file data into the page cache.
+         Doing so can significantly improve performance because
+         it eliminates a memcpy and it also removes the lock contention
+         on the single buffer.
+
+endchoice
+
+choice
+       prompt "Decompressor parallelisation options"
+       depends on SQUASHFS
+       help
+         Squashfs now supports three parallelisation options for
+         decompression.  Each one exhibits various trade-offs between
+         decompression performance and CPU and memory usage.
+
+         If in doubt, select "Single threaded compression"
+
+config SQUASHFS_DECOMP_SINGLE
+       bool "Single threaded compression"
+       help
+         Traditionally Squashfs has used single-threaded decompression.
+         Only one block (data or metadata) can be decompressed at any
+         one time.  This limits CPU and memory usage to a minimum.
+
+config SQUASHFS_DECOMP_MULTI
+       bool "Use multiple decompressors for parallel I/O"
+       help
+         By default Squashfs uses a single decompressor but it gives
+         poor performance on parallel I/O workloads when using multiple CPU
+         machines due to waiting on decompressor availability.
+
+         If you have a parallel I/O workload and your system has enough memory,
+         using this option may improve overall I/O performance.
+
+         This decompressor implementation uses up to two parallel
+         decompressors per core.  It dynamically allocates decompressors
+         on a demand basis.
+
+config SQUASHFS_DECOMP_MULTI_PERCPU
+       bool "Use percpu multiple decompressors for parallel I/O"
+       help
+         By default Squashfs uses a single decompressor but it gives
+         poor performance on parallel I/O workloads when using multiple CPU
+         machines due to waiting on decompressor availability.
+
+         This decompressor implementation uses a maximum of one
+         decompressor per core.  It uses percpu variables to ensure
+         decompression is load-balanced across the cores.
+
+endchoice
+
 config SQUASHFS_XATTR
        bool "Squashfs XATTR support"
        depends on SQUASHFS
index 110b047..4132520 100644 (file)
@@ -5,6 +5,11 @@
 obj-$(CONFIG_SQUASHFS) += squashfs.o
 squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
 squashfs-y += namei.o super.o symlink.o decompressor.o
+squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o
+squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o
+squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o
+squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o
+squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o
 squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o
 squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o
 squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o
index 41d108e..0cea9b9 100644 (file)
@@ -36,6 +36,7 @@
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "page_actor.h"
 
 /*
  * Read the metadata block length, this is stored in the first two
@@ -86,16 +87,16 @@ static struct buffer_head *get_block_length(struct super_block *sb,
  * generated a larger block - this does occasionally happen with compression
  * algorithms).
  */
-int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
-                       int length, u64 *next_index, int srclength, int pages)
+int squashfs_read_data(struct super_block *sb, u64 index, int length,
+               u64 *next_index, struct squashfs_page_actor *output)
 {
        struct squashfs_sb_info *msblk = sb->s_fs_info;
        struct buffer_head **bh;
        int offset = index & ((1 << msblk->devblksize_log2) - 1);
        u64 cur_index = index >> msblk->devblksize_log2;
-       int bytes, compressed, b = 0, k = 0, page = 0, avail;
+       int bytes, compressed, b = 0, k = 0, avail, i;
 
-       bh = kcalloc(((srclength + msblk->devblksize - 1)
+       bh = kcalloc(((output->length + msblk->devblksize - 1)
                >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
        if (bh == NULL)
                return -ENOMEM;
@@ -111,9 +112,9 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
                        *next_index = index + length;
 
                TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
-                       index, compressed ? "" : "un", length, srclength);
+                       index, compressed ? "" : "un", length, output->length);
 
-               if (length < 0 || length > srclength ||
+               if (length < 0 || length > output->length ||
                                (index + length) > msblk->bytes_used)
                        goto read_failure;
 
@@ -145,7 +146,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
                TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
                                compressed ? "" : "un", length);
 
-               if (length < 0 || length > srclength ||
+               if (length < 0 || length > output->length ||
                                        (index + length) > msblk->bytes_used)
                        goto block_release;
 
@@ -158,9 +159,15 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
                ll_rw_block(READ, b - 1, bh + 1);
        }
 
+       for (i = 0; i < b; i++) {
+               wait_on_buffer(bh[i]);
+               if (!buffer_uptodate(bh[i]))
+                       goto block_release;
+       }
+
        if (compressed) {
-               length = squashfs_decompress(msblk, buffer, bh, b, offset,
-                        length, srclength, pages);
+               length = squashfs_decompress(msblk, bh, b, offset, length,
+                       output);
                if (length < 0)
                        goto read_failure;
        } else {
@@ -168,22 +175,20 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
                 * Block is uncompressed.
                 */
                int in, pg_offset = 0;
+               void *data = squashfs_first_page(output);
 
                for (bytes = length; k < b; k++) {
                        in = min(bytes, msblk->devblksize - offset);
                        bytes -= in;
-                       wait_on_buffer(bh[k]);
-                       if (!buffer_uptodate(bh[k]))
-                               goto block_release;
                        while (in) {
                                if (pg_offset == PAGE_CACHE_SIZE) {
-                                       page++;
+                                       data = squashfs_next_page(output);
                                        pg_offset = 0;
                                }
                                avail = min_t(int, in, PAGE_CACHE_SIZE -
                                                pg_offset);
-                               memcpy(buffer[page] + pg_offset,
-                                               bh[k]->b_data + offset, avail);
+                               memcpy(data + pg_offset, bh[k]->b_data + offset,
+                                               avail);
                                in -= avail;
                                pg_offset += avail;
                                offset += avail;
@@ -191,6 +196,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
                        offset = 0;
                        put_bh(bh[k]);
                }
+               squashfs_finish_page(output);
        }
 
        kfree(bh);
index af0b738..1cb70a0 100644 (file)
@@ -56,6 +56,7 @@
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
+#include "page_actor.h"
 
 /*
  * Look-up block in cache, and increment usage count.  If not in cache, read
@@ -119,9 +120,8 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,
                        entry->error = 0;
                        spin_unlock(&cache->lock);
 
-                       entry->length = squashfs_read_data(sb, entry->data,
-                               block, length, &entry->next_index,
-                               cache->block_size, cache->pages);
+                       entry->length = squashfs_read_data(sb, block, length,
+                               &entry->next_index, entry->actor);
 
                        spin_lock(&cache->lock);
 
@@ -220,6 +220,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache)
                                kfree(cache->entry[i].data[j]);
                        kfree(cache->entry[i].data);
                }
+               kfree(cache->entry[i].actor);
        }
 
        kfree(cache->entry);
@@ -280,6 +281,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries,
                                goto cleanup;
                        }
                }
+
+               entry->actor = squashfs_page_actor_init(entry->data,
+                                               cache->pages, 0);
+               if (entry->actor == NULL) {
+                       ERROR("Failed to allocate %s cache entry\n", name);
+                       goto cleanup;
+               }
        }
 
        return cache;
@@ -410,6 +418,7 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length)
        int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
        int i, res;
        void *table, *buffer, **data;
+       struct squashfs_page_actor *actor;
 
        table = buffer = kmalloc(length, GFP_KERNEL);
        if (table == NULL)
@@ -421,19 +430,28 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length)
                goto failed;
        }
 
+       actor = squashfs_page_actor_init(data, pages, length);
+       if (actor == NULL) {
+               res = -ENOMEM;
+               goto failed2;
+       }
+
        for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
                data[i] = buffer;
 
-       res = squashfs_read_data(sb, data, block, length |
-               SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages);
+       res = squashfs_read_data(sb, block, length |
+               SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
 
        kfree(data);
+       kfree(actor);
 
        if (res < 0)
                goto failed;
 
        return table;
 
+failed2:
+       kfree(data);
 failed:
        kfree(table);
        return ERR_PTR(res);
index 3f6271d..ac22fe7 100644 (file)
@@ -30,6 +30,7 @@
 #include "squashfs_fs_sb.h"
 #include "decompressor.h"
 #include "squashfs.h"
+#include "page_actor.h"
 
 /*
  * This file (and decompressor.h) implements a decompressor framework for
  */
 
 static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = {
-       NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
+       NULL, NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
 };
 
 #ifndef CONFIG_SQUASHFS_LZO
 static const struct squashfs_decompressor squashfs_lzo_comp_ops = {
-       NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
+       NULL, NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
 };
 #endif
 
 #ifndef CONFIG_SQUASHFS_XZ
 static const struct squashfs_decompressor squashfs_xz_comp_ops = {
-       NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
+       NULL, NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
 };
 #endif
 
 #ifndef CONFIG_SQUASHFS_ZLIB
 static const struct squashfs_decompressor squashfs_zlib_comp_ops = {
-       NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
+       NULL, NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
 };
 #endif
 
 static const struct squashfs_decompressor squashfs_unknown_comp_ops = {
-       NULL, NULL, NULL, 0, "unknown", 0
+       NULL, NULL, NULL, NULL, 0, "unknown", 0
 };
 
 static const struct squashfs_decompressor *decompressor[] = {
@@ -83,10 +84,11 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
 }
 
 
-void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
+static void *get_comp_opts(struct super_block *sb, unsigned short flags)
 {
        struct squashfs_sb_info *msblk = sb->s_fs_info;
-       void *strm, *buffer = NULL;
+       void *buffer = NULL, *comp_opts;
+       struct squashfs_page_actor *actor = NULL;
        int length = 0;
 
        /*
@@ -94,23 +96,46 @@ void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
         */
        if (SQUASHFS_COMP_OPTS(flags)) {
                buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
-               if (buffer == NULL)
-                       return ERR_PTR(-ENOMEM);
+               if (buffer == NULL) {
+                       comp_opts = ERR_PTR(-ENOMEM);
+                       goto out;
+               }
+
+               actor = squashfs_page_actor_init(&buffer, 1, 0);
+               if (actor == NULL) {
+                       comp_opts = ERR_PTR(-ENOMEM);
+                       goto out;
+               }
 
-               length = squashfs_read_data(sb, &buffer,
-                       sizeof(struct squashfs_super_block), 0, NULL,
-                       PAGE_CACHE_SIZE, 1);
+               length = squashfs_read_data(sb,
+                       sizeof(struct squashfs_super_block), 0, NULL, actor);
 
                if (length < 0) {
-                       strm = ERR_PTR(length);
-                       goto finished;
+                       comp_opts = ERR_PTR(length);
+                       goto out;
                }
        }
 
-       strm = msblk->decompressor->init(msblk, buffer, length);
+       comp_opts = squashfs_comp_opts(msblk, buffer, length);
 
-finished:
+out:
+       kfree(actor);
        kfree(buffer);
+       return comp_opts;
+}
+
+
+void *squashfs_decompressor_setup(struct super_block *sb, unsigned short flags)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       void *stream, *comp_opts = get_comp_opts(sb, flags);
+
+       if (IS_ERR(comp_opts))
+               return comp_opts;
+
+       stream = squashfs_decompressor_create(msblk, comp_opts);
+       if (IS_ERR(stream))
+               kfree(comp_opts);
 
-       return strm;
+       return stream;
 }
index 330073e..af09853 100644 (file)
  */
 
 struct squashfs_decompressor {
-       void    *(*init)(struct squashfs_sb_info *, void *, int);
+       void    *(*init)(struct squashfs_sb_info *, void *);
+       void    *(*comp_opts)(struct squashfs_sb_info *, void *, int);
        void    (*free)(void *);
-       int     (*decompress)(struct squashfs_sb_info *, void **,
-               struct buffer_head **, int, int, int, int, int);
+       int     (*decompress)(struct squashfs_sb_info *, void *,
+               struct buffer_head **, int, int, int,
+               struct squashfs_page_actor *);
        int     id;
        char    *name;
        int     supported;
 };
 
-static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
-       void *s)
+static inline void *squashfs_comp_opts(struct squashfs_sb_info *msblk,
+                                                       void *buff, int length)
 {
-       if (msblk->decompressor)
-               msblk->decompressor->free(s);
-}
-
-static inline int squashfs_decompress(struct squashfs_sb_info *msblk,
-       void **buffer, struct buffer_head **bh, int b, int offset, int length,
-       int srclength, int pages)
-{
-       return msblk->decompressor->decompress(msblk, buffer, bh, b, offset,
-               length, srclength, pages);
+       return msblk->decompressor->comp_opts ?
+               msblk->decompressor->comp_opts(msblk, buff, length) : NULL;
 }
 
 #ifdef CONFIG_SQUASHFS_XZ
diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c
new file mode 100644 (file)
index 0000000..d6008a6
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ *  Copyright (c) 2013
+ *  Minchan Kim <minchan@kernel.org>
+ *
+ *  This work is licensed under the terms of the GNU GPL, version 2. See
+ *  the COPYING file in the top-level directory.
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/cpumask.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "decompressor.h"
+#include "squashfs.h"
+
+/*
+ * This file implements multi-threaded decompression in the
+ * decompressor framework
+ */
+
+
+/*
+ * The reason that multiply two is that a CPU can request new I/O
+ * while it is waiting previous request.
+ */
+#define MAX_DECOMPRESSOR       (num_online_cpus() * 2)
+
+
+int squashfs_max_decompressors(void)
+{
+       return MAX_DECOMPRESSOR;
+}
+
+
+struct squashfs_stream {
+       void                    *comp_opts;
+       struct list_head        strm_list;
+       struct mutex            mutex;
+       int                     avail_decomp;
+       wait_queue_head_t       wait;
+};
+
+
+struct decomp_stream {
+       void *stream;
+       struct list_head list;
+};
+
+
+static void put_decomp_stream(struct decomp_stream *decomp_strm,
+                               struct squashfs_stream *stream)
+{
+       mutex_lock(&stream->mutex);
+       list_add(&decomp_strm->list, &stream->strm_list);
+       mutex_unlock(&stream->mutex);
+       wake_up(&stream->wait);
+}
+
+void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
+                               void *comp_opts)
+{
+       struct squashfs_stream *stream;
+       struct decomp_stream *decomp_strm = NULL;
+       int err = -ENOMEM;
+
+       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+       if (!stream)
+               goto out;
+
+       stream->comp_opts = comp_opts;
+       mutex_init(&stream->mutex);
+       INIT_LIST_HEAD(&stream->strm_list);
+       init_waitqueue_head(&stream->wait);
+
+       /*
+        * We should have a decompressor at least as default
+        * so if we fail to allocate new decompressor dynamically,
+        * we could always fall back to default decompressor and
+        * file system works.
+        */
+       decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL);
+       if (!decomp_strm)
+               goto out;
+
+       decomp_strm->stream = msblk->decompressor->init(msblk,
+                                               stream->comp_opts);
+       if (IS_ERR(decomp_strm->stream)) {
+               err = PTR_ERR(decomp_strm->stream);
+               goto out;
+       }
+
+       list_add(&decomp_strm->list, &stream->strm_list);
+       stream->avail_decomp = 1;
+       return stream;
+
+out:
+       kfree(decomp_strm);
+       kfree(stream);
+       return ERR_PTR(err);
+}
+
+
+void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
+{
+       struct squashfs_stream *stream = msblk->stream;
+       if (stream) {
+               struct decomp_stream *decomp_strm;
+
+               while (!list_empty(&stream->strm_list)) {
+                       decomp_strm = list_entry(stream->strm_list.prev,
+                                               struct decomp_stream, list);
+                       list_del(&decomp_strm->list);
+                       msblk->decompressor->free(decomp_strm->stream);
+                       kfree(decomp_strm);
+                       stream->avail_decomp--;
+               }
+               WARN_ON(stream->avail_decomp);
+               kfree(stream->comp_opts);
+               kfree(stream);
+       }
+}
+
+
+static struct decomp_stream *get_decomp_stream(struct squashfs_sb_info *msblk,
+                                       struct squashfs_stream *stream)
+{
+       struct decomp_stream *decomp_strm;
+
+       while (1) {
+               mutex_lock(&stream->mutex);
+
+               /* There is available decomp_stream */
+               if (!list_empty(&stream->strm_list)) {
+                       decomp_strm = list_entry(stream->strm_list.prev,
+                               struct decomp_stream, list);
+                       list_del(&decomp_strm->list);
+                       mutex_unlock(&stream->mutex);
+                       break;
+               }
+
+               /*
+                * If there is no available decomp and already full,
+                * let's wait for releasing decomp from other users.
+                */
+               if (stream->avail_decomp >= MAX_DECOMPRESSOR)
+                       goto wait;
+
+               /* Let's allocate new decomp */
+               decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL);
+               if (!decomp_strm)
+                       goto wait;
+
+               decomp_strm->stream = msblk->decompressor->init(msblk,
+                                               stream->comp_opts);
+               if (IS_ERR(decomp_strm->stream)) {
+                       kfree(decomp_strm);
+                       goto wait;
+               }
+
+               stream->avail_decomp++;
+               WARN_ON(stream->avail_decomp > MAX_DECOMPRESSOR);
+
+               mutex_unlock(&stream->mutex);
+               break;
+wait:
+               /*
+                * If system memory is tough, let's for other's
+                * releasing instead of hurting VM because it could
+                * make page cache thrashing.
+                */
+               mutex_unlock(&stream->mutex);
+               wait_event(stream->wait,
+                       !list_empty(&stream->strm_list));
+       }
+
+       return decomp_strm;
+}
+
+
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
+       int b, int offset, int length, struct squashfs_page_actor *output)
+{
+       int res;
+       struct squashfs_stream *stream = msblk->stream;
+       struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream);
+       res = msblk->decompressor->decompress(msblk, decomp_stream->stream,
+               bh, b, offset, length, output);
+       put_decomp_stream(decomp_stream, stream);
+       if (res < 0)
+               ERROR("%s decompression failed, data probably corrupt\n",
+                       msblk->decompressor->name);
+       return res;
+}
diff --git a/fs/squashfs/decompressor_multi_percpu.c b/fs/squashfs/decompressor_multi_percpu.c
new file mode 100644 (file)
index 0000000..23a9c28
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/percpu.h>
+#include <linux/buffer_head.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "decompressor.h"
+#include "squashfs.h"
+
+/*
+ * This file implements multi-threaded decompression using percpu
+ * variables, one thread per cpu core.
+ */
+
+struct squashfs_stream {
+       void            *stream;
+};
+
+void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
+                                               void *comp_opts)
+{
+       struct squashfs_stream *stream;
+       struct squashfs_stream __percpu *percpu;
+       int err, cpu;
+
+       percpu = alloc_percpu(struct squashfs_stream);
+       if (percpu == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       for_each_possible_cpu(cpu) {
+               stream = per_cpu_ptr(percpu, cpu);
+               stream->stream = msblk->decompressor->init(msblk, comp_opts);
+               if (IS_ERR(stream->stream)) {
+                       err = PTR_ERR(stream->stream);
+                       goto out;
+               }
+       }
+
+       kfree(comp_opts);
+       return (__force void *) percpu;
+
+out:
+       for_each_possible_cpu(cpu) {
+               stream = per_cpu_ptr(percpu, cpu);
+               if (!IS_ERR_OR_NULL(stream->stream))
+                       msblk->decompressor->free(stream->stream);
+       }
+       free_percpu(percpu);
+       return ERR_PTR(err);
+}
+
+void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
+{
+       struct squashfs_stream __percpu *percpu =
+                       (struct squashfs_stream __percpu *) msblk->stream;
+       struct squashfs_stream *stream;
+       int cpu;
+
+       if (msblk->stream) {
+               for_each_possible_cpu(cpu) {
+                       stream = per_cpu_ptr(percpu, cpu);
+                       msblk->decompressor->free(stream->stream);
+               }
+               free_percpu(percpu);
+       }
+}
+
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
+       int b, int offset, int length, struct squashfs_page_actor *output)
+{
+       struct squashfs_stream __percpu *percpu =
+                       (struct squashfs_stream __percpu *) msblk->stream;
+       struct squashfs_stream *stream = get_cpu_ptr(percpu);
+       int res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
+               offset, length, output);
+       put_cpu_ptr(stream);
+
+       if (res < 0)
+               ERROR("%s decompression failed, data probably corrupt\n",
+                       msblk->decompressor->name);
+
+       return res;
+}
+
+int squashfs_max_decompressors(void)
+{
+       return num_possible_cpus();
+}
diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c
new file mode 100644 (file)
index 0000000..a6c7592
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "decompressor.h"
+#include "squashfs.h"
+
+/*
+ * This file implements single-threaded decompression in the
+ * decompressor framework
+ */
+
+struct squashfs_stream {
+       void            *stream;
+       struct mutex    mutex;
+};
+
+void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
+                                               void *comp_opts)
+{
+       struct squashfs_stream *stream;
+       int err = -ENOMEM;
+
+       stream = kmalloc(sizeof(*stream), GFP_KERNEL);
+       if (stream == NULL)
+               goto out;
+
+       stream->stream = msblk->decompressor->init(msblk, comp_opts);
+       if (IS_ERR(stream->stream)) {
+               err = PTR_ERR(stream->stream);
+               goto out;
+       }
+
+       kfree(comp_opts);
+       mutex_init(&stream->mutex);
+       return stream;
+
+out:
+       kfree(stream);
+       return ERR_PTR(err);
+}
+
+void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
+{
+       struct squashfs_stream *stream = msblk->stream;
+
+       if (stream) {
+               msblk->decompressor->free(stream->stream);
+               kfree(stream);
+       }
+}
+
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
+       int b, int offset, int length, struct squashfs_page_actor *output)
+{
+       int res;
+       struct squashfs_stream *stream = msblk->stream;
+
+       mutex_lock(&stream->mutex);
+       res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
+               offset, length, output);
+       mutex_unlock(&stream->mutex);
+
+       if (res < 0)
+               ERROR("%s decompression failed, data probably corrupt\n",
+                       msblk->decompressor->name);
+
+       return res;
+}
+
+int squashfs_max_decompressors(void)
+{
+       return 1;
+}
index 8ca62c2..e5c9689 100644 (file)
@@ -370,77 +370,15 @@ static int read_blocklist(struct inode *inode, int index, u64 *block)
        return le32_to_cpu(size);
 }
 
-
-static int squashfs_readpage(struct file *file, struct page *page)
+/* Copy data into page cache  */
+void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer,
+       int bytes, int offset)
 {
        struct inode *inode = page->mapping->host;
        struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-       int bytes, i, offset = 0, sparse = 0;
-       struct squashfs_cache_entry *buffer = NULL;
        void *pageaddr;
-
-       int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
-       int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
-       int start_index = page->index & ~mask;
-       int end_index = start_index | mask;
-       int file_end = i_size_read(inode) >> msblk->block_log;
-
-       TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
-                               page->index, squashfs_i(inode)->start);
-
-       if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
-                                       PAGE_CACHE_SHIFT))
-               goto out;
-
-       if (index < file_end || squashfs_i(inode)->fragment_block ==
-                                       SQUASHFS_INVALID_BLK) {
-               /*
-                * Reading a datablock from disk.  Need to read block list
-                * to get location and block size.
-                */
-               u64 block = 0;
-               int bsize = read_blocklist(inode, index, &block);
-               if (bsize < 0)
-                       goto error_out;
-
-               if (bsize == 0) { /* hole */
-                       bytes = index == file_end ?
-                               (i_size_read(inode) & (msblk->block_size - 1)) :
-                                msblk->block_size;
-                       sparse = 1;
-               } else {
-                       /*
-                        * Read and decompress datablock.
-                        */
-                       buffer = squashfs_get_datablock(inode->i_sb,
-                                                               block, bsize);
-                       if (buffer->error) {
-                               ERROR("Unable to read page, block %llx, size %x"
-                                       "\n", block, bsize);
-                               squashfs_cache_put(buffer);
-                               goto error_out;
-                       }
-                       bytes = buffer->length;
-               }
-       } else {
-               /*
-                * Datablock is stored inside a fragment (tail-end packed
-                * block).
-                */
-               buffer = squashfs_get_fragment(inode->i_sb,
-                               squashfs_i(inode)->fragment_block,
-                               squashfs_i(inode)->fragment_size);
-
-               if (buffer->error) {
-                       ERROR("Unable to read page, block %llx, size %x\n",
-                               squashfs_i(inode)->fragment_block,
-                               squashfs_i(inode)->fragment_size);
-                       squashfs_cache_put(buffer);
-                       goto error_out;
-               }
-               bytes = i_size_read(inode) & (msblk->block_size - 1);
-               offset = squashfs_i(inode)->fragment_offset;
-       }
+       int i, mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
+       int start_index = page->index & ~mask, end_index = start_index | mask;
 
        /*
         * Loop copying datablock into pages.  As the datablock likely covers
@@ -451,7 +389,7 @@ static int squashfs_readpage(struct file *file, struct page *page)
        for (i = start_index; i <= end_index && bytes > 0; i++,
                        bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
                struct page *push_page;
-               int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE);
+               int avail = buffer ? min_t(int, bytes, PAGE_CACHE_SIZE) : 0;
 
                TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
 
@@ -475,11 +413,75 @@ skip_page:
                if (i != page->index)
                        page_cache_release(push_page);
        }
+}
+
+/* Read datablock stored packed inside a fragment (tail-end packed block) */
+static int squashfs_readpage_fragment(struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb,
+               squashfs_i(inode)->fragment_block,
+               squashfs_i(inode)->fragment_size);
+       int res = buffer->error;
+
+       if (res)
+               ERROR("Unable to read page, block %llx, size %x\n",
+                       squashfs_i(inode)->fragment_block,
+                       squashfs_i(inode)->fragment_size);
+       else
+               squashfs_copy_cache(page, buffer, i_size_read(inode) &
+                       (msblk->block_size - 1),
+                       squashfs_i(inode)->fragment_offset);
+
+       squashfs_cache_put(buffer);
+       return res;
+}
 
-       if (!sparse)
-               squashfs_cache_put(buffer);
+static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
+{
+       struct inode *inode = page->mapping->host;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       int bytes = index == file_end ?
+                       (i_size_read(inode) & (msblk->block_size - 1)) :
+                        msblk->block_size;
 
+       squashfs_copy_cache(page, NULL, bytes, 0);
        return 0;
+}
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
+       int file_end = i_size_read(inode) >> msblk->block_log;
+       int res;
+       void *pageaddr;
+
+       TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
+                               page->index, squashfs_i(inode)->start);
+
+       if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+                                       PAGE_CACHE_SHIFT))
+               goto out;
+
+       if (index < file_end || squashfs_i(inode)->fragment_block ==
+                                       SQUASHFS_INVALID_BLK) {
+               u64 block = 0;
+               int bsize = read_blocklist(inode, index, &block);
+               if (bsize < 0)
+                       goto error_out;
+
+               if (bsize == 0)
+                       res = squashfs_readpage_sparse(page, index, file_end);
+               else
+                       res = squashfs_readpage_block(page, block, bsize);
+       } else
+               res = squashfs_readpage_fragment(page);
+
+       if (!res)
+               return 0;
 
 error_out:
        SetPageError(page);
diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c
new file mode 100644 (file)
index 0000000..f2310d2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/mutex.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/* Read separately compressed datablock and memcopy into page cache */
+int squashfs_readpage_block(struct page *page, u64 block, int bsize)
+{
+       struct inode *i = page->mapping->host;
+       struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
+               block, bsize);
+       int res = buffer->error;
+
+       if (res)
+               ERROR("Unable to read page, block %llx, size %x\n", block,
+                       bsize);
+       else
+               squashfs_copy_cache(page, buffer, buffer->length, 0);
+
+       squashfs_cache_put(buffer);
+       return res;
+}
diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c
new file mode 100644 (file)
index 0000000..2943b2b
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/mutex.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+#include "page_actor.h"
+
+static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
+       int pages, struct page **page);
+
+/* Read separately compressed datablock directly into page cache */
+int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
+
+{
+       struct inode *inode = target_page->mapping->host;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+
+       int file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT;
+       int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
+       int start_index = target_page->index & ~mask;
+       int end_index = start_index | mask;
+       int i, n, pages, missing_pages, bytes, res = -ENOMEM;
+       struct page **page;
+       struct squashfs_page_actor *actor;
+       void *pageaddr;
+
+       if (end_index > file_end)
+               end_index = file_end;
+
+       pages = end_index - start_index + 1;
+
+       page = kmalloc(sizeof(void *) * pages, GFP_KERNEL);
+       if (page == NULL)
+               return res;
+
+       /*
+        * Create a "page actor" which will kmap and kunmap the
+        * page cache pages appropriately within the decompressor
+        */
+       actor = squashfs_page_actor_init_special(page, pages, 0);
+       if (actor == NULL)
+               goto out;
+
+       /* Try to grab all the pages covered by the Squashfs block */
+       for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) {
+               page[i] = (n == target_page->index) ? target_page :
+                       grab_cache_page_nowait(target_page->mapping, n);
+
+               if (page[i] == NULL) {
+                       missing_pages++;
+                       continue;
+               }
+
+               if (PageUptodate(page[i])) {
+                       unlock_page(page[i]);
+                       page_cache_release(page[i]);
+                       page[i] = NULL;
+                       missing_pages++;
+               }
+       }
+
+       if (missing_pages) {
+               /*
+                * Couldn't get one or more pages, this page has either
+                * been VM reclaimed, but others are still in the page cache
+                * and uptodate, or we're racing with another thread in
+                * squashfs_readpage also trying to grab them.  Fall back to
+                * using an intermediate buffer.
+                */
+               res = squashfs_read_cache(target_page, block, bsize, pages,
+                                                               page);
+               goto out;
+       }
+
+       /* Decompress directly into the page cache buffers */
+       res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
+       if (res < 0)
+               goto mark_errored;
+
+       /* Last page may have trailing bytes not filled */
+       bytes = res % PAGE_CACHE_SIZE;
+       if (bytes) {
+               pageaddr = kmap_atomic(page[pages - 1]);
+               memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+               kunmap_atomic(pageaddr);
+       }
+
+       /* Mark pages as uptodate, unlock and release */
+       for (i = 0; i < pages; i++) {
+               flush_dcache_page(page[i]);
+               SetPageUptodate(page[i]);
+               unlock_page(page[i]);
+               if (page[i] != target_page)
+                       page_cache_release(page[i]);
+       }
+
+       kfree(actor);
+       kfree(page);
+
+       return 0;
+
+mark_errored:
+       /* Decompression failed, mark pages as errored.  Target_page is
+        * dealt with by the caller
+        */
+       for (i = 0; i < pages; i++) {
+               if (page[i] == target_page)
+                       continue;
+               flush_dcache_page(page[i]);
+               SetPageError(page[i]);
+               unlock_page(page[i]);
+               page_cache_release(page[i]);
+       }
+
+out:
+       kfree(actor);
+       kfree(page);
+       return res;
+}
+
+
+static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
+       int pages, struct page **page)
+{
+       struct inode *i = target_page->mapping->host;
+       struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
+                                                block, bsize);
+       int bytes = buffer->length, res = buffer->error, n, offset = 0;
+       void *pageaddr;
+
+       if (res) {
+               ERROR("Unable to read page, block %llx, size %x\n", block,
+                       bsize);
+               goto out;
+       }
+
+       for (n = 0; n < pages && bytes > 0; n++,
+                       bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
+               int avail = min_t(int, bytes, PAGE_CACHE_SIZE);
+
+               if (page[n] == NULL)
+                       continue;
+
+               pageaddr = kmap_atomic(page[n]);
+               squashfs_copy_data(pageaddr, buffer, offset, avail);
+               memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
+               kunmap_atomic(pageaddr);
+               flush_dcache_page(page[n]);
+               SetPageUptodate(page[n]);
+               unlock_page(page[n]);
+               if (page[n] != target_page)
+                       page_cache_release(page[n]);
+       }
+
+out:
+       squashfs_cache_put(buffer);
+       return res;
+}
index 00f4dfc..244b9fb 100644 (file)
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "page_actor.h"
 
 struct squashfs_lzo {
        void    *input;
        void    *output;
 };
 
-static void *lzo_init(struct squashfs_sb_info *msblk, void *buff, int len)
+static void *lzo_init(struct squashfs_sb_info *msblk, void *buff)
 {
        int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
 
@@ -74,22 +75,16 @@ static void lzo_free(void *strm)
 }
 
 
-static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
-       struct buffer_head **bh, int b, int offset, int length, int srclength,
-       int pages)
+static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
+       struct buffer_head **bh, int b, int offset, int length,
+       struct squashfs_page_actor *output)
 {
-       struct squashfs_lzo *stream = msblk->stream;
-       void *buff = stream->input;
+       struct squashfs_lzo *stream = strm;
+       void *buff = stream->input, *data;
        int avail, i, bytes = length, res;
-       size_t out_len = srclength;
-
-       mutex_lock(&msblk->read_data_mutex);
+       size_t out_len = output->length;
 
        for (i = 0; i < b; i++) {
-               wait_on_buffer(bh[i]);
-               if (!buffer_uptodate(bh[i]))
-                       goto block_release;
-
                avail = min(bytes, msblk->devblksize - offset);
                memcpy(buff, bh[i]->b_data + offset, avail);
                buff += avail;
@@ -104,24 +99,24 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
                goto failed;
 
        res = bytes = (int)out_len;
-       for (i = 0, buff = stream->output; bytes && i < pages; i++) {
-               avail = min_t(int, bytes, PAGE_CACHE_SIZE);
-               memcpy(buffer[i], buff, avail);
-               buff += avail;
-               bytes -= avail;
+       data = squashfs_first_page(output);
+       buff = stream->output;
+       while (data) {
+               if (bytes <= PAGE_CACHE_SIZE) {
+                       memcpy(data, buff, bytes);
+                       break;
+               } else {
+                       memcpy(data, buff, PAGE_CACHE_SIZE);
+                       buff += PAGE_CACHE_SIZE;
+                       bytes -= PAGE_CACHE_SIZE;
+                       data = squashfs_next_page(output);
+               }
        }
+       squashfs_finish_page(output);
 
-       mutex_unlock(&msblk->read_data_mutex);
        return res;
 
-block_release:
-       for (; i < b; i++)
-               put_bh(bh[i]);
-
 failed:
-       mutex_unlock(&msblk->read_data_mutex);
-
-       ERROR("lzo decompression failed, data probably corrupt\n");
        return -EIO;
 }
 
diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c
new file mode 100644 (file)
index 0000000..5a1c11f
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include "page_actor.h"
+
+/*
+ * This file contains implementations of page_actor for decompressing into
+ * an intermediate buffer, and for decompressing directly into the
+ * page cache.
+ *
+ * Calling code should avoid sleeping between calls to squashfs_first_page()
+ * and squashfs_finish_page().
+ */
+
+/* Implementation of page_actor for decompressing into intermediate buffer */
+static void *cache_first_page(struct squashfs_page_actor *actor)
+{
+       actor->next_page = 1;
+       return actor->buffer[0];
+}
+
+static void *cache_next_page(struct squashfs_page_actor *actor)
+{
+       if (actor->next_page == actor->pages)
+               return NULL;
+
+       return actor->buffer[actor->next_page++];
+}
+
+static void cache_finish_page(struct squashfs_page_actor *actor)
+{
+       /* empty */
+}
+
+struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
+       int pages, int length)
+{
+       struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
+
+       if (actor == NULL)
+               return NULL;
+
+       actor->length = length ? : pages * PAGE_CACHE_SIZE;
+       actor->buffer = buffer;
+       actor->pages = pages;
+       actor->next_page = 0;
+       actor->squashfs_first_page = cache_first_page;
+       actor->squashfs_next_page = cache_next_page;
+       actor->squashfs_finish_page = cache_finish_page;
+       return actor;
+}
+
+/* Implementation of page_actor for decompressing directly into page cache. */
+static void *direct_first_page(struct squashfs_page_actor *actor)
+{
+       actor->next_page = 1;
+       return actor->pageaddr = kmap_atomic(actor->page[0]);
+}
+
+static void *direct_next_page(struct squashfs_page_actor *actor)
+{
+       if (actor->pageaddr)
+               kunmap_atomic(actor->pageaddr);
+
+       return actor->pageaddr = actor->next_page == actor->pages ? NULL :
+               kmap_atomic(actor->page[actor->next_page++]);
+}
+
+static void direct_finish_page(struct squashfs_page_actor *actor)
+{
+       if (actor->pageaddr)
+               kunmap_atomic(actor->pageaddr);
+}
+
+struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
+       int pages, int length)
+{
+       struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
+
+       if (actor == NULL)
+               return NULL;
+
+       actor->length = length ? : pages * PAGE_CACHE_SIZE;
+       actor->page = page;
+       actor->pages = pages;
+       actor->next_page = 0;
+       actor->pageaddr = NULL;
+       actor->squashfs_first_page = direct_first_page;
+       actor->squashfs_next_page = direct_next_page;
+       actor->squashfs_finish_page = direct_finish_page;
+       return actor;
+}
diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h
new file mode 100644 (file)
index 0000000..26dd820
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef PAGE_ACTOR_H
+#define PAGE_ACTOR_H
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef CONFIG_SQUASHFS_FILE_DIRECT
+struct squashfs_page_actor {
+       void    **page;
+       int     pages;
+       int     length;
+       int     next_page;
+};
+
+static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page,
+       int pages, int length)
+{
+       struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
+
+       if (actor == NULL)
+               return NULL;
+
+       actor->length = length ? : pages * PAGE_CACHE_SIZE;
+       actor->page = page;
+       actor->pages = pages;
+       actor->next_page = 0;
+       return actor;
+}
+
+static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
+{
+       actor->next_page = 1;
+       return actor->page[0];
+}
+
+static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
+{
+       return actor->next_page == actor->pages ? NULL :
+               actor->page[actor->next_page++];
+}
+
+static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
+{
+       /* empty */
+}
+#else
+struct squashfs_page_actor {
+       union {
+               void            **buffer;
+               struct page     **page;
+       };
+       void    *pageaddr;
+       void    *(*squashfs_first_page)(struct squashfs_page_actor *);
+       void    *(*squashfs_next_page)(struct squashfs_page_actor *);
+       void    (*squashfs_finish_page)(struct squashfs_page_actor *);
+       int     pages;
+       int     length;
+       int     next_page;
+};
+
+extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int);
+extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page
+                                                        **, int, int);
+static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
+{
+       return actor->squashfs_first_page(actor);
+}
+static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
+{
+       return actor->squashfs_next_page(actor);
+}
+static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
+{
+       actor->squashfs_finish_page(actor);
+}
+#endif
+#endif
index d126651..9e1bb79 100644 (file)
@@ -28,8 +28,8 @@
 #define WARNING(s, args...)    pr_warning("SQUASHFS: "s, ## args)
 
 /* block.c */
-extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *,
-                               int, int);
+extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
+                               struct squashfs_page_actor *);
 
 /* cache.c */
 extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
@@ -48,7 +48,14 @@ extern void *squashfs_read_table(struct super_block *, u64, int);
 
 /* decompressor.c */
 extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
-extern void *squashfs_decompressor_init(struct super_block *, unsigned short);
+extern void *squashfs_decompressor_setup(struct super_block *, unsigned short);
+
+/* decompressor_xxx.c */
+extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *);
+extern void squashfs_decompressor_destroy(struct squashfs_sb_info *);
+extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **,
+       int, int, int, struct squashfs_page_actor *);
+extern int squashfs_max_decompressors(void);
 
 /* export.c */
 extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64,
@@ -59,6 +66,13 @@ extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *);
 extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
                                u64, u64, unsigned int);
 
+/* file.c */
+void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
+                               int);
+
+/* file_xxx.c */
+extern int squashfs_readpage_block(struct page *, u64, int);
+
 /* id.c */
 extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
 extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64,
index 52934a2..1da565c 100644 (file)
@@ -50,6 +50,7 @@ struct squashfs_cache_entry {
        wait_queue_head_t       wait_queue;
        struct squashfs_cache   *cache;
        void                    **data;
+       struct squashfs_page_actor      *actor;
 };
 
 struct squashfs_sb_info {
@@ -63,10 +64,9 @@ struct squashfs_sb_info {
        __le64                                  *id_table;
        __le64                                  *fragment_index;
        __le64                                  *xattr_id_table;
-       struct mutex                            read_data_mutex;
        struct mutex                            meta_index_mutex;
        struct meta_index                       *meta_index;
-       void                                    *stream;
+       struct squashfs_stream                  *stream;
        __le64                                  *inode_lookup_table;
        u64                                     inode_table;
        u64                                     directory_table;
index 60553a9..202df63 100644 (file)
@@ -98,7 +98,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
        msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE);
        msblk->devblksize_log2 = ffz(~msblk->devblksize);
 
-       mutex_init(&msblk->read_data_mutex);
        mutex_init(&msblk->meta_index_mutex);
 
        /*
@@ -206,13 +205,14 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
                goto failed_mount;
 
        /* Allocate read_page block */
-       msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size);
+       msblk->read_page = squashfs_cache_init("data",
+               squashfs_max_decompressors(), msblk->block_size);
        if (msblk->read_page == NULL) {
                ERROR("Failed to allocate read_page block\n");
                goto failed_mount;
        }
 
-       msblk->stream = squashfs_decompressor_init(sb, flags);
+       msblk->stream = squashfs_decompressor_setup(sb, flags);
        if (IS_ERR(msblk->stream)) {
                err = PTR_ERR(msblk->stream);
                msblk->stream = NULL;
@@ -336,7 +336,7 @@ failed_mount:
        squashfs_cache_delete(msblk->block_cache);
        squashfs_cache_delete(msblk->fragment_cache);
        squashfs_cache_delete(msblk->read_page);
-       squashfs_decompressor_free(msblk, msblk->stream);
+       squashfs_decompressor_destroy(msblk);
        kfree(msblk->inode_lookup_table);
        kfree(msblk->fragment_index);
        kfree(msblk->id_table);
@@ -383,7 +383,7 @@ static void squashfs_put_super(struct super_block *sb)
                squashfs_cache_delete(sbi->block_cache);
                squashfs_cache_delete(sbi->fragment_cache);
                squashfs_cache_delete(sbi->read_page);
-               squashfs_decompressor_free(sbi, sbi->stream);
+               squashfs_decompressor_destroy(sbi);
                kfree(sbi->id_table);
                kfree(sbi->fragment_index);
                kfree(sbi->meta_index);
index 1760b7d..c609624 100644 (file)
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "page_actor.h"
 
 struct squashfs_xz {
        struct xz_dec *state;
        struct xz_buf buf;
 };
 
-struct comp_opts {
+struct disk_comp_opts {
        __le32 dictionary_size;
        __le32 flags;
 };
 
-static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff,
-       int len)
+struct comp_opts {
+       int dict_size;
+};
+
+static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk,
+       void *buff, int len)
 {
-       struct comp_opts *comp_opts = buff;
-       struct squashfs_xz *stream;
-       int dict_size = msblk->block_size;
-       int err, n;
+       struct disk_comp_opts *comp_opts = buff;
+       struct comp_opts *opts;
+       int err = 0, n;
+
+       opts = kmalloc(sizeof(*opts), GFP_KERNEL);
+       if (opts == NULL) {
+               err = -ENOMEM;
+               goto out2;
+       }
 
        if (comp_opts) {
                /* check compressor options are the expected length */
                if (len < sizeof(*comp_opts)) {
                        err = -EIO;
-                       goto failed;
+                       goto out;
                }
 
-               dict_size = le32_to_cpu(comp_opts->dictionary_size);
+               opts->dict_size = le32_to_cpu(comp_opts->dictionary_size);
 
                /* the dictionary size should be 2^n or 2^n+2^(n+1) */
-               n = ffs(dict_size) - 1;
-               if (dict_size != (1 << n) && dict_size != (1 << n) +
+               n = ffs(opts->dict_size) - 1;
+               if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) +
                                                (1 << (n + 1))) {
                        err = -EIO;
-                       goto failed;
+                       goto out;
                }
-       }
+       } else
+               /* use defaults */
+               opts->dict_size = max_t(int, msblk->block_size,
+                                                       SQUASHFS_METADATA_SIZE);
+
+       return opts;
+
+out:
+       kfree(opts);
+out2:
+       return ERR_PTR(err);
+}
+
 
-       dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE);
+static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff)
+{
+       struct comp_opts *comp_opts = buff;
+       struct squashfs_xz *stream;
+       int err;
 
        stream = kmalloc(sizeof(*stream), GFP_KERNEL);
        if (stream == NULL) {
@@ -77,7 +103,7 @@ static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff,
                goto failed;
        }
 
-       stream->state = xz_dec_init(XZ_PREALLOC, dict_size);
+       stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size);
        if (stream->state == NULL) {
                kfree(stream);
                err = -ENOMEM;
@@ -103,42 +129,37 @@ static void squashfs_xz_free(void *strm)
 }
 
 
-static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
-       struct buffer_head **bh, int b, int offset, int length, int srclength,
-       int pages)
+static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
+       struct buffer_head **bh, int b, int offset, int length,
+       struct squashfs_page_actor *output)
 {
        enum xz_ret xz_err;
-       int avail, total = 0, k = 0, page = 0;
-       struct squashfs_xz *stream = msblk->stream;
-
-       mutex_lock(&msblk->read_data_mutex);
+       int avail, total = 0, k = 0;
+       struct squashfs_xz *stream = strm;
 
        xz_dec_reset(stream->state);
        stream->buf.in_pos = 0;
        stream->buf.in_size = 0;
        stream->buf.out_pos = 0;
        stream->buf.out_size = PAGE_CACHE_SIZE;
-       stream->buf.out = buffer[page++];
+       stream->buf.out = squashfs_first_page(output);
 
        do {
                if (stream->buf.in_pos == stream->buf.in_size && k < b) {
                        avail = min(length, msblk->devblksize - offset);
                        length -= avail;
-                       wait_on_buffer(bh[k]);
-                       if (!buffer_uptodate(bh[k]))
-                               goto release_mutex;
-
                        stream->buf.in = bh[k]->b_data + offset;
                        stream->buf.in_size = avail;
                        stream->buf.in_pos = 0;
                        offset = 0;
                }
 
-               if (stream->buf.out_pos == stream->buf.out_size
-                                                       && page < pages) {
-                       stream->buf.out = buffer[page++];
-                       stream->buf.out_pos = 0;
-                       total += PAGE_CACHE_SIZE;
+               if (stream->buf.out_pos == stream->buf.out_size) {
+                       stream->buf.out = squashfs_next_page(output);
+                       if (stream->buf.out != NULL) {
+                               stream->buf.out_pos = 0;
+                               total += PAGE_CACHE_SIZE;
+                       }
                }
 
                xz_err = xz_dec_run(stream->state, &stream->buf);
@@ -147,23 +168,14 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
                        put_bh(bh[k++]);
        } while (xz_err == XZ_OK);
 
-       if (xz_err != XZ_STREAM_END) {
-               ERROR("xz_dec_run error, data probably corrupt\n");
-               goto release_mutex;
-       }
-
-       if (k < b) {
-               ERROR("xz_uncompress error, input remaining\n");
-               goto release_mutex;
-       }
+       squashfs_finish_page(output);
 
-       total += stream->buf.out_pos;
-       mutex_unlock(&msblk->read_data_mutex);
-       return total;
+       if (xz_err != XZ_STREAM_END || k < b)
+               goto out;
 
-release_mutex:
-       mutex_unlock(&msblk->read_data_mutex);
+       return total + stream->buf.out_pos;
 
+out:
        for (; k < b; k++)
                put_bh(bh[k]);
 
@@ -172,6 +184,7 @@ release_mutex:
 
 const struct squashfs_decompressor squashfs_xz_comp_ops = {
        .init = squashfs_xz_init,
+       .comp_opts = squashfs_xz_comp_opts,
        .free = squashfs_xz_free,
        .decompress = squashfs_xz_uncompress,
        .id = XZ_COMPRESSION,
index 55d918f..8727cab 100644 (file)
@@ -32,8 +32,9 @@
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "page_actor.h"
 
-static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len)
+static void *zlib_init(struct squashfs_sb_info *dummy, void *buff)
 {
        z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL);
        if (stream == NULL)
@@ -61,44 +62,37 @@ static void zlib_free(void *strm)
 }
 
 
-static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
-       struct buffer_head **bh, int b, int offset, int length, int srclength,
-       int pages)
+static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
+       struct buffer_head **bh, int b, int offset, int length,
+       struct squashfs_page_actor *output)
 {
-       int zlib_err, zlib_init = 0;
-       int k = 0, page = 0;
-       z_stream *stream = msblk->stream;
-
-       mutex_lock(&msblk->read_data_mutex);
+       int zlib_err, zlib_init = 0, k = 0;
+       z_stream *stream = strm;
 
-       stream->avail_out = 0;
+       stream->avail_out = PAGE_CACHE_SIZE;
+       stream->next_out = squashfs_first_page(output);
        stream->avail_in = 0;
 
        do {
                if (stream->avail_in == 0 && k < b) {
                        int avail = min(length, msblk->devblksize - offset);
                        length -= avail;
-                       wait_on_buffer(bh[k]);
-                       if (!buffer_uptodate(bh[k]))
-                               goto release_mutex;
-
                        stream->next_in = bh[k]->b_data + offset;
                        stream->avail_in = avail;
                        offset = 0;
                }
 
-               if (stream->avail_out == 0 && page < pages) {
-                       stream->next_out = buffer[page++];
-                       stream->avail_out = PAGE_CACHE_SIZE;
+               if (stream->avail_out == 0) {
+                       stream->next_out = squashfs_next_page(output);
+                       if (stream->next_out != NULL)
+                               stream->avail_out = PAGE_CACHE_SIZE;
                }
 
                if (!zlib_init) {
                        zlib_err = zlib_inflateInit(stream);
                        if (zlib_err != Z_OK) {
-                               ERROR("zlib_inflateInit returned unexpected "
-                                       "result 0x%x, srclength %d\n",
-                                       zlib_err, srclength);
-                               goto release_mutex;
+                               squashfs_finish_page(output);
+                               goto out;
                        }
                        zlib_init = 1;
                }
@@ -109,29 +103,21 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
                        put_bh(bh[k++]);
        } while (zlib_err == Z_OK);
 
-       if (zlib_err != Z_STREAM_END) {
-               ERROR("zlib_inflate error, data probably corrupt\n");
-               goto release_mutex;
-       }
+       squashfs_finish_page(output);
 
-       zlib_err = zlib_inflateEnd(stream);
-       if (zlib_err != Z_OK) {
-               ERROR("zlib_inflate error, data probably corrupt\n");
-               goto release_mutex;
-       }
+       if (zlib_err != Z_STREAM_END)
+               goto out;
 
-       if (k < b) {
-               ERROR("zlib_uncompress error, data remaining\n");
-               goto release_mutex;
-       }
+       zlib_err = zlib_inflateEnd(stream);
+       if (zlib_err != Z_OK)
+               goto out;
 
-       length = stream->total_out;
-       mutex_unlock(&msblk->read_data_mutex);
-       return length;
+       if (k < b)
+               goto out;
 
-release_mutex:
-       mutex_unlock(&msblk->read_data_mutex);
+       return stream->total_out;
 
+out:
        for (; k < b; k++)
                put_bh(bh[k]);
 
index 1c02da8..3ef11b2 100644 (file)
@@ -1137,6 +1137,7 @@ xfs_bmap_add_attrfork(
        int                     committed;      /* xaction was committed */
        int                     logflags;       /* logging flags */
        int                     error;          /* error return value */
+       int                     cancel_flags = 0;
 
        ASSERT(XFS_IFORK_Q(ip) == 0);
 
@@ -1147,19 +1148,20 @@ xfs_bmap_add_attrfork(
        if (rsvd)
                tp->t_flags |= XFS_TRANS_RESERVE;
        error = xfs_trans_reserve(tp, &M_RES(mp)->tr_addafork, blks, 0);
-       if (error)
-               goto error0;
+       if (error) {
+               xfs_trans_cancel(tp, 0);
+               return error;
+       }
+       cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
        xfs_ilock(ip, XFS_ILOCK_EXCL);
        error = xfs_trans_reserve_quota_nblks(tp, ip, blks, 0, rsvd ?
                        XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES :
                        XFS_QMOPT_RES_REGBLKS);
-       if (error) {
-               xfs_iunlock(ip, XFS_ILOCK_EXCL);
-               xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES);
-               return error;
-       }
+       if (error)
+               goto trans_cancel;
+       cancel_flags |= XFS_TRANS_ABORT;
        if (XFS_IFORK_Q(ip))
-               goto error1;
+               goto trans_cancel;
        if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS) {
                /*
                 * For inodes coming from pre-6.2 filesystems.
@@ -1169,7 +1171,7 @@ xfs_bmap_add_attrfork(
        }
        ASSERT(ip->i_d.di_anextents == 0);
 
-       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
        switch (ip->i_d.di_format) {
@@ -1191,7 +1193,7 @@ xfs_bmap_add_attrfork(
        default:
                ASSERT(0);
                error = XFS_ERROR(EINVAL);
-               goto error1;
+               goto trans_cancel;
        }
 
        ASSERT(ip->i_afp == NULL);
@@ -1219,7 +1221,7 @@ xfs_bmap_add_attrfork(
        if (logflags)
                xfs_trans_log_inode(tp, ip, logflags);
        if (error)
-               goto error2;
+               goto bmap_cancel;
        if (!xfs_sb_version_hasattr(&mp->m_sb) ||
           (!xfs_sb_version_hasattr2(&mp->m_sb) && version == 2)) {
                __int64_t sbfields = 0;
@@ -1242,14 +1244,16 @@ xfs_bmap_add_attrfork(
 
        error = xfs_bmap_finish(&tp, &flist, &committed);
        if (error)
-               goto error2;
-       return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
-error2:
+               goto bmap_cancel;
+       error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return error;
+
+bmap_cancel:
        xfs_bmap_cancel(&flist);
-error1:
+trans_cancel:
+       xfs_trans_cancel(tp, cancel_flags);
        xfs_iunlock(ip, XFS_ILOCK_EXCL);
-error0:
-       xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
        return error;
 }
 
index da88f16..02df7b4 100644 (file)
@@ -41,6 +41,7 @@
 #include "xfs_fsops.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
+#include "xfs_dinode.h"
 
 
 #ifdef HAVE_PERCPU_SB
@@ -718,8 +719,22 @@ xfs_mountfs(
         * Set the inode cluster size.
         * This may still be overridden by the file system
         * block size if it is larger than the chosen cluster size.
+        *
+        * For v5 filesystems, scale the cluster size with the inode size to
+        * keep a constant ratio of inode per cluster buffer, but only if mkfs
+        * has set the inode alignment value appropriately for larger cluster
+        * sizes.
         */
        mp->m_inode_cluster_size = XFS_INODE_BIG_CLUSTER_SIZE;
+       if (xfs_sb_version_hascrc(&mp->m_sb)) {
+               int     new_size = mp->m_inode_cluster_size;
+
+               new_size *= mp->m_sb.sb_inodesize / XFS_DINODE_MIN_SIZE;
+               if (mp->m_sb.sb_inoalignmt >= XFS_B_TO_FSBT(mp, new_size))
+                       mp->m_inode_cluster_size = new_size;
+               xfs_info(mp, "Using inode cluster size of %d bytes",
+                        mp->m_inode_cluster_size);
+       }
 
        /*
         * Set inode alignment fields
index 1d8101a..a466c5e 100644 (file)
@@ -112,7 +112,7 @@ typedef struct xfs_mount {
        __uint8_t               m_blkbb_log;    /* blocklog - BBSHIFT */
        __uint8_t               m_agno_log;     /* log #ag's */
        __uint8_t               m_agino_log;    /* #bits for agino in inum */
-       __uint16_t              m_inode_cluster_size;/* min inode buf size */
+       uint                    m_inode_cluster_size;/* min inode buf size */
        uint                    m_blockmask;    /* sb_blocksize-1 */
        uint                    m_blockwsize;   /* sb_blocksize in words */
        uint                    m_blockwmask;   /* blockwsize-1 */
index 1bba7f6..50c3f56 100644 (file)
@@ -111,12 +111,14 @@ xfs_trans_log_inode(
 
        /*
         * First time we log the inode in a transaction, bump the inode change
-        * counter if it is configured for this to occur.
+        * counter if it is configured for this to occur. We don't use
+        * inode_inc_version() because there is no need for extra locking around
+        * i_version as we already hold the inode locked exclusively for
+        * metadata modification.
         */
        if (!(ip->i_itemp->ili_item.li_desc->lid_flags & XFS_LID_DIRTY) &&
            IS_I_VERSION(VFS_I(ip))) {
-               inode_inc_iversion(VFS_I(ip));
-               ip->i_d.di_changecount = VFS_I(ip)->i_version;
+               ip->i_d.di_changecount = ++VFS_I(ip)->i_version;
                flags |= XFS_ILOG_CORE;
        }
 
index d53d9f0..2fd59c0 100644 (file)
@@ -385,8 +385,7 @@ xfs_calc_ifree_reservation(
                xfs_calc_inode_res(mp, 1) +
                xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
                xfs_calc_buf_res(1, XFS_FSB_TO_B(mp, 1)) +
-               MAX((__uint16_t)XFS_FSB_TO_B(mp, 1),
-                   XFS_INODE_CLUSTER_SIZE(mp)) +
+               max_t(uint, XFS_FSB_TO_B(mp, 1), XFS_INODE_CLUSTER_SIZE(mp)) +
                xfs_calc_buf_res(1, 0) +
                xfs_calc_buf_res(2 + XFS_IALLOC_BLOCKS(mp) +
                                 mp->m_in_maxlevels, 0) +
index 89c60b0..7b2de02 100644 (file)
@@ -431,9 +431,9 @@ static inline acpi_handle acpi_get_child(acpi_handle handle, u64 addr)
 {
        return acpi_find_child(handle, addr, false);
 }
+void acpi_preset_companion(struct device *dev, acpi_handle parent, u64 addr);
 int acpi_is_root_bridge(acpi_handle);
 struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle);
-#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev))
 
 int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state);
 int acpi_disable_wakeup_device_power(struct acpi_device *dev);
index aea9e45..14909b0 100644 (file)
@@ -53,7 +53,7 @@
 
 #elif defined(CONFIG_SPARSEMEM)
 /*
- * Note: section's mem_map is encorded to reflect its start_pfn.
+ * Note: section's mem_map is encoded to reflect its start_pfn.
  * section[i].section_mem_map == mem_map's address - start_pfn;
  */
 #define __page_to_pfn(pg)                                      \
index 83e2c31..bc2121f 100644 (file)
 #define KERNEL_CTORS() . = ALIGN(8);                      \
                        VMLINUX_SYMBOL(__ctors_start) = .; \
                        *(.ctors)                          \
+                       *(.init_array)                     \
                        VMLINUX_SYMBOL(__ctors_end) = .;
 #else
 #define KERNEL_CTORS()
diff --git a/include/crypto/hash_info.h b/include/crypto/hash_info.h
new file mode 100644 (file)
index 0000000..e1e5a3e
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Hash Info: Hash algorithms information
+ *
+ * Copyright (c) 2013 Dmitry Kasatkin <d.kasatkin@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 _CRYPTO_HASH_INFO_H
+#define _CRYPTO_HASH_INFO_H
+
+#include <crypto/sha.h>
+#include <crypto/md5.h>
+
+#include <uapi/linux/hash_info.h>
+
+/* not defined in include/crypto/ */
+#define RMD128_DIGEST_SIZE      16
+#define RMD160_DIGEST_SIZE     20
+#define RMD256_DIGEST_SIZE      32
+#define RMD320_DIGEST_SIZE      40
+
+/* not defined in include/crypto/ */
+#define WP512_DIGEST_SIZE      64
+#define WP384_DIGEST_SIZE      48
+#define WP256_DIGEST_SIZE      32
+
+/* not defined in include/crypto/ */
+#define TGR128_DIGEST_SIZE 16
+#define TGR160_DIGEST_SIZE 20
+#define TGR192_DIGEST_SIZE 24
+
+extern const char *const hash_algo_name[HASH_ALGO__LAST];
+extern const int hash_digest_size[HASH_ALGO__LAST];
+
+#endif /* _CRYPTO_HASH_INFO_H */
index f5b0224..fc09732 100644 (file)
@@ -15,6 +15,7 @@
 #define _LINUX_PUBLIC_KEY_H
 
 #include <linux/mpi.h>
+#include <crypto/hash_info.h>
 
 enum pkey_algo {
        PKEY_ALGO_DSA,
@@ -22,21 +23,11 @@ enum pkey_algo {
        PKEY_ALGO__LAST
 };
 
-extern const char *const pkey_algo[PKEY_ALGO__LAST];
+extern const char *const pkey_algo_name[PKEY_ALGO__LAST];
+extern const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST];
 
-enum pkey_hash_algo {
-       PKEY_HASH_MD4,
-       PKEY_HASH_MD5,
-       PKEY_HASH_SHA1,
-       PKEY_HASH_RIPE_MD_160,
-       PKEY_HASH_SHA256,
-       PKEY_HASH_SHA384,
-       PKEY_HASH_SHA512,
-       PKEY_HASH_SHA224,
-       PKEY_HASH__LAST
-};
-
-extern const char *const pkey_hash_algo[PKEY_HASH__LAST];
+/* asymmetric key implementation supports only up to SHA224 */
+#define PKEY_HASH__LAST                (HASH_ALGO_SHA224 + 1)
 
 enum pkey_id_type {
        PKEY_ID_PGP,            /* OpenPGP generated key ID */
@@ -44,7 +35,7 @@ enum pkey_id_type {
        PKEY_ID_TYPE__LAST
 };
 
-extern const char *const pkey_id_type[PKEY_ID_TYPE__LAST];
+extern const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST];
 
 /*
  * Cryptographic data for the public-key subtype of the asymmetric key type.
@@ -59,6 +50,7 @@ struct public_key {
 #define PKEY_CAN_DECRYPT       0x02
 #define PKEY_CAN_SIGN          0x04
 #define PKEY_CAN_VERIFY                0x08
+       enum pkey_algo pkey_algo : 8;
        enum pkey_id_type id_type : 8;
        union {
                MPI     mpi[5];
@@ -88,7 +80,8 @@ struct public_key_signature {
        u8 *digest;
        u8 digest_size;                 /* Number of bytes in digest */
        u8 nr_mpi;                      /* Occupancy of mpi[] */
-       enum pkey_hash_algo pkey_hash_algo : 8;
+       enum pkey_algo pkey_algo : 8;
+       enum hash_algo pkey_hash_algo : 8;
        union {
                MPI mpi[2];
                struct {
index b46fb45..1d4a920 100644 (file)
@@ -150,6 +150,7 @@ int drm_err(const char *func, const char *format, ...);
 #define DRIVER_BUS_PCI 0x1
 #define DRIVER_BUS_PLATFORM 0x2
 #define DRIVER_BUS_USB 0x3
+#define DRIVER_BUS_HOST1X 0x4
 
 /***********************************************************************/
 /** \name Begin the DRM... */
@@ -412,7 +413,12 @@ struct drm_prime_file_private {
 
 /** File private data */
 struct drm_file {
-       int authenticated;
+       unsigned always_authenticated :1;
+       unsigned authenticated :1;
+       unsigned is_master :1; /* this file private is a master for a minor */
+       /* true when the client has asked us to expose stereo 3D mode flags */
+       unsigned stereo_allowed :1;
+
        struct pid *pid;
        kuid_t uid;
        drm_magic_t magic;
@@ -429,10 +435,8 @@ struct drm_file {
        struct file *filp;
        void *driver_priv;
 
-       int is_master; /* this file private is a master for a minor */
        struct drm_master *master; /* master this node is currently associated with
                                      N.B. not always minor->master */
-
        /**
         * fbs - List of framebuffers associated with this file.
         *
@@ -667,8 +671,6 @@ struct drm_gem_object {
        uint32_t pending_read_domains;
        uint32_t pending_write_domain;
 
-       void *driver_private;
-
        /**
         * dma_buf - dma buf associated with this GEM object
         *
@@ -834,12 +836,17 @@ struct drm_driver {
        /**
         * Called by vblank timestamping code.
         *
-        * Return the current display scanout position from a crtc.
+        * Return the current display scanout position from a crtc, and an
+        * optional accurate ktime_get timestamp of when position was measured.
         *
         * \param dev  DRM device.
         * \param crtc Id of the crtc to query.
         * \param *vpos Target location for current vertical scanout position.
         * \param *hpos Target location for current horizontal scanout position.
+        * \param *stime Target location for timestamp taken immediately before
+        *               scanout position query. Can be NULL to skip timestamp.
+        * \param *etime Target location for timestamp taken immediately after
+        *               scanout position query. Can be NULL to skip timestamp.
         *
         * Returns vpos as a positive number while in active scanout area.
         * Returns vpos as a negative number inside vblank, counting the number
@@ -856,7 +863,8 @@ struct drm_driver {
         *
         */
        int (*get_scanout_position) (struct drm_device *dev, int crtc,
-                                    int *vpos, int *hpos);
+                                    int *vpos, int *hpos, ktime_t *stime,
+                                    ktime_t *etime);
 
        /**
         * Called by \c drm_get_last_vbltimestamp. Should return a precise
@@ -922,7 +930,6 @@ struct drm_driver {
         *
         * Returns 0 on success.
         */
-       int (*gem_init_object) (struct drm_gem_object *obj);
        void (*gem_free_object) (struct drm_gem_object *obj);
        int (*gem_open_object) (struct drm_gem_object *, struct drm_file *);
        void (*gem_close_object) (struct drm_gem_object *, struct drm_file *);
@@ -997,27 +1004,6 @@ struct drm_driver {
 #define DRM_MINOR_CONTROL 2
 #define DRM_MINOR_RENDER 3
 
-
-/**
- * debugfs node list. This structure represents a debugfs file to
- * be created by the drm core
- */
-struct drm_debugfs_list {
-       const char *name; /** file name */
-       int (*show)(struct seq_file*, void*); /** show callback */
-       u32 driver_features; /**< Required driver features for this entry */
-};
-
-/**
- * debugfs node structure. This structure represents a debugfs file.
- */
-struct drm_debugfs_node {
-       struct list_head list;
-       struct drm_minor *minor;
-       struct drm_debugfs_list *debugfs_ent;
-       struct dentry *dent;
-};
-
 /**
  * Info file list entry. This structure represents a debugfs or proc file to
  * be created by the drm core
@@ -1046,7 +1032,7 @@ struct drm_minor {
        int index;                      /**< Minor device number */
        int type;                       /**< Control or render */
        dev_t device;                   /**< Device number for mknod */
-       struct device kdev;             /**< Linux device */
+       struct device *kdev;            /**< Linux device */
        struct drm_device *dev;
 
        struct dentry *debugfs_root;
@@ -1081,6 +1067,19 @@ struct drm_pending_vblank_event {
        struct drm_event_vblank event;
 };
 
+struct drm_vblank_crtc {
+       wait_queue_head_t queue;        /**< VBLANK wait queue */
+       struct timeval time[DRM_VBLANKTIME_RBSIZE];     /**< timestamp of current count */
+       atomic_t count;                 /**< number of VBLANK interrupts */
+       atomic_t refcount;              /* number of users of vblank interruptsper crtc */
+       u32 last;                       /* protected by dev->vbl_lock, used */
+                                       /* for wraparound handling */
+       u32 last_wait;                  /* Last vblank seqno waited per CRTC */
+       unsigned int inmodeset;         /* Display driver is setting mode */
+       bool enabled;                   /* so we don't call enable more than
+                                          once per disable */
+};
+
 /**
  * DRM device structure. This structure represent a complete card that
  * may contain multiple heads.
@@ -1105,25 +1104,16 @@ struct drm_device {
        atomic_t buf_alloc;             /**< Buffer allocation in progress */
        /*@} */
 
-       /** \name Performance counters */
-       /*@{ */
-       unsigned long counters;
-       enum drm_stat_type types[15];
-       atomic_t counts[15];
-       /*@} */
-
        struct list_head filelist;
 
        /** \name Memory management */
        /*@{ */
        struct list_head maplist;       /**< Linked list of regions */
-       int map_count;                  /**< Number of mappable regions */
        struct drm_open_hash map_hash;  /**< User token hash table for maps */
 
        /** \name Context handle management */
        /*@{ */
        struct list_head ctxlist;       /**< Linked list of context handles */
-       int ctx_count;                  /**< Number of context handles */
        struct mutex ctxlist_mutex;     /**< For ctxlist */
 
        struct idr ctx_idr;
@@ -1139,12 +1129,11 @@ struct drm_device {
 
        /** \name Context support */
        /*@{ */
-       int irq_enabled;                /**< True if irq handler is enabled */
+       bool irq_enabled;               /**< True if irq handler is enabled */
        __volatile__ long context_flag; /**< Context swapping flag */
        int last_context;               /**< Last current context */
        /*@} */
 
-       struct work_struct work;
        /** \name VBLANK IRQ support */
        /*@{ */
 
@@ -1154,20 +1143,13 @@ struct drm_device {
         * Once the modeset ioctl *has* been called though, we can safely
         * disable them when unused.
         */
-       int vblank_disable_allowed;
+       bool vblank_disable_allowed;
+
+       /* array of size num_crtcs */
+       struct drm_vblank_crtc *vblank;
 
-       wait_queue_head_t *vbl_queue;   /**< VBLANK wait queue */
-       atomic_t *_vblank_count;        /**< number of VBLANK interrupts (driver must alloc the right number of counters) */
-       struct timeval *_vblank_time;   /**< timestamp of current vblank_count (drivers must alloc right number of fields) */
        spinlock_t vblank_time_lock;    /**< Protects vblank count and time updates during vblank enable/disable */
        spinlock_t vbl_lock;
-       atomic_t *vblank_refcount;      /* number of users of vblank interruptsper crtc */
-       u32 *last_vblank;               /* protected by dev->vbl_lock, used */
-                                       /* for wraparound handling */
-       int *vblank_enabled;            /* so we don't call enable more than
-                                          once per disable */
-       int *vblank_inmodeset;          /* Display driver is setting mode */
-       u32 *last_vblank_wait;          /* Last vblank seqno waited per CRTC */
        struct timer_list vblank_disable_timer;
 
        u32 max_vblank_count;           /**< size of vblank counter register */
@@ -1184,8 +1166,6 @@ struct drm_device {
 
        struct device *dev;             /**< Device structure */
        struct pci_dev *pdev;           /**< PCI device structure */
-       int pci_vendor;                 /**< PCI vendor id */
-       int pci_device;                 /**< PCI device id */
 #ifdef __alpha__
        struct pci_controller *hose;
 #endif
@@ -1303,6 +1283,8 @@ extern int drm_getstats(struct drm_device *dev, void *data,
                        struct drm_file *file_priv);
 extern int drm_getcap(struct drm_device *dev, void *data,
                      struct drm_file *file_priv);
+extern int drm_setclientcap(struct drm_device *dev, void *data,
+                           struct drm_file *file_priv);
 extern int drm_setversion(struct drm_device *dev, void *data,
                          struct drm_file *file_priv);
 extern int drm_noop(struct drm_device *dev, void *data,
@@ -1454,7 +1436,6 @@ extern struct drm_master *drm_master_get(struct drm_master *master);
 extern void drm_master_put(struct drm_master **master);
 
 extern void drm_put_dev(struct drm_device *dev);
-extern int drm_put_minor(struct drm_minor **minor);
 extern void drm_unplug_dev(struct drm_device *dev);
 extern unsigned int drm_debug;
 extern unsigned int drm_rnodes;
@@ -1474,10 +1455,11 @@ extern struct drm_local_map *drm_getsarea(struct drm_device *dev);
 #if defined(CONFIG_DEBUG_FS)
 extern int drm_debugfs_init(struct drm_minor *minor, int minor_id,
                            struct dentry *root);
-extern int drm_debugfs_create_files(struct drm_info_list *files, int count,
-                                   struct dentry *root, struct drm_minor *minor);
-extern int drm_debugfs_remove_files(struct drm_info_list *files, int count,
-                                    struct drm_minor *minor);
+extern int drm_debugfs_create_files(const struct drm_info_list *files,
+                                   int count, struct dentry *root,
+                                   struct drm_minor *minor);
+extern int drm_debugfs_remove_files(const struct drm_info_list *files,
+                                   int count, struct drm_minor *minor);
 extern int drm_debugfs_cleanup(struct drm_minor *minor);
 #endif
 
@@ -1556,8 +1538,6 @@ int drm_gem_init(struct drm_device *dev);
 void drm_gem_destroy(struct drm_device *dev);
 void drm_gem_object_release(struct drm_gem_object *obj);
 void drm_gem_object_free(struct kref *kref);
-struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev,
-                                           size_t size);
 int drm_gem_object_init(struct drm_device *dev,
                        struct drm_gem_object *obj, size_t size);
 void drm_gem_private_object_init(struct drm_device *dev,
@@ -1645,10 +1625,11 @@ static __inline__ void drm_core_dropmap(struct drm_local_map *map)
 
 #include <drm/drm_mem_util.h>
 
-extern int drm_fill_in_dev(struct drm_device *dev,
-                          const struct pci_device_id *ent,
-                          struct drm_driver *driver);
-int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type);
+struct drm_device *drm_dev_alloc(struct drm_driver *driver,
+                                struct device *parent);
+void drm_dev_free(struct drm_device *dev);
+int drm_dev_register(struct drm_device *dev, unsigned long flags);
+void drm_dev_unregister(struct drm_device *dev);
 /*@}*/
 
 /* PCI section */
index 24f4995..f32c5cd 100644 (file)
@@ -108,6 +108,7 @@ enum drm_mode_status {
     MODE_ONE_HEIGHT,    /* only one height is supported */
     MODE_ONE_SIZE,      /* only one resolution is supported */
     MODE_NO_REDUCED,    /* monitor doesn't accept reduced blanking */
+    MODE_NO_STEREO,    /* stereo modes not supported */
     MODE_UNVERIFIED = -3, /* mode needs to reverified */
     MODE_BAD = -2,     /* unspecified reason */
     MODE_ERROR = -1    /* error condition */
@@ -124,7 +125,10 @@ enum drm_mode_status {
        .vscan = (vs), .flags = (f), \
        .base.type = DRM_MODE_OBJECT_MODE
 
-#define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */
+#define CRTC_INTERLACE_HALVE_V (1 << 0) /* halve V values for interlacing */
+#define CRTC_STEREO_DOUBLE     (1 << 1) /* adjust timings for stereo modes */
+
+#define DRM_MODE_FLAG_3D_MAX   DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF
 
 struct drm_display_mode {
        /* Header */
@@ -155,8 +159,7 @@ struct drm_display_mode {
        int height_mm;
 
        /* Actual mode we give to hw */
-       int clock_index;
-       int synth_clock;
+       int crtc_clock;         /* in KHz */
        int crtc_hdisplay;
        int crtc_hblank_start;
        int crtc_hblank_end;
@@ -180,6 +183,11 @@ struct drm_display_mode {
        int hsync;              /* in kHz */
 };
 
+static inline bool drm_mode_is_stereo(const struct drm_display_mode *mode)
+{
+       return mode->flags & DRM_MODE_FLAG_3D_MASK;
+}
+
 enum drm_connector_status {
        connector_status_connected = 1,
        connector_status_disconnected = 2,
@@ -587,7 +595,7 @@ enum drm_connector_force {
  */
 struct drm_connector {
        struct drm_device *dev;
-       struct device kdev;
+       struct device *kdev;
        struct device_attribute *attr;
        struct list_head head;
 
@@ -597,6 +605,7 @@ struct drm_connector {
        int connector_type_id;
        bool interlace_allowed;
        bool doublescan_allowed;
+       bool stereo_allowed;
        struct list_head modes; /* list of modes on this connector */
 
        enum drm_connector_status status;
@@ -964,6 +973,7 @@ extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_m
 extern bool drm_probe_ddc(struct i2c_adapter *adapter);
 extern struct edid *drm_get_edid(struct drm_connector *connector,
                                 struct i2c_adapter *adapter);
+extern struct edid *drm_edid_duplicate(const struct edid *edid);
 extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
 extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
 extern void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src);
@@ -975,7 +985,7 @@ extern void drm_mode_config_reset(struct drm_device *dev);
 extern void drm_mode_config_cleanup(struct drm_device *dev);
 extern void drm_mode_set_name(struct drm_display_mode *mode);
 extern bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2);
-extern bool drm_mode_equal_no_clocks(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2);
+extern bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2);
 extern int drm_mode_width(const struct drm_display_mode *mode);
 extern int drm_mode_height(const struct drm_display_mode *mode);
 
@@ -1108,6 +1118,8 @@ extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev,
                                int GTF_2C, int GTF_K, int GTF_2J);
 extern int drm_add_modes_noedid(struct drm_connector *connector,
                                int hdisplay, int vdisplay);
+extern void drm_set_preferred_mode(struct drm_connector *connector,
+                                  int hpref, int vpref);
 
 extern int drm_edid_header_is_valid(const u8 *raw_edid);
 extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid);
@@ -1135,4 +1147,21 @@ extern int drm_format_horz_chroma_subsampling(uint32_t format);
 extern int drm_format_vert_chroma_subsampling(uint32_t format);
 extern const char *drm_get_format_name(uint32_t format);
 
+/* Helpers */
+static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev,
+       uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CRTC);
+       return mo ? obj_to_crtc(mo) : NULL;
+}
+
+static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
+       uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
+       return mo ? obj_to_encoder(mo) : NULL;
+}
+
 #endif /* __DRM_CRTC_H__ */
index f43d556..ef6ad3a 100644 (file)
@@ -163,7 +163,7 @@ static inline void drm_connector_helper_add(struct drm_connector *connector,
 extern int drm_helper_resume_force_mode(struct drm_device *dev);
 extern void drm_kms_helper_poll_init(struct drm_device *dev);
 extern void drm_kms_helper_poll_fini(struct drm_device *dev);
-extern void drm_helper_hpd_irq_event(struct drm_device *dev);
+extern bool drm_helper_hpd_irq_event(struct drm_device *dev);
 extern void drm_kms_helper_hotplug_event(struct drm_device *dev);
 
 extern void drm_kms_helper_poll_disable(struct drm_device *dev);
index ae8dbfb..a92c375 100644 (file)
 #define DP_DOWNSTREAMPORT_PRESENT           0x005
 # define DP_DWN_STRM_PORT_PRESENT           (1 << 0)
 # define DP_DWN_STRM_PORT_TYPE_MASK         0x06
-/* 00b = DisplayPort */
-/* 01b = Analog */
-/* 10b = TMDS or HDMI */
-/* 11b = Other */
+# define DP_DWN_STRM_PORT_TYPE_DP           (0 << 1)
+# define DP_DWN_STRM_PORT_TYPE_ANALOG       (1 << 1)
+# define DP_DWN_STRM_PORT_TYPE_TMDS         (2 << 1)
+# define DP_DWN_STRM_PORT_TYPE_OTHER        (3 << 1)
 # define DP_FORMAT_CONVERSION               (1 << 3)
 # define DP_DETAILED_CAP_INFO_AVAILABLE            (1 << 4) /* DPI */
 
@@ -333,20 +333,20 @@ i2c_dp_aux_add_bus(struct i2c_adapter *adapter);
 
 
 #define DP_LINK_STATUS_SIZE       6
-bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE],
+bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
                          int lane_count);
-bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE],
+bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
                              int lane_count);
-u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE],
+u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
                                     int lane);
-u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
+u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
                                          int lane);
 
 #define DP_RECEIVER_CAP_SIZE           0xf
 #define EDP_PSR_RECEIVER_CAP_SIZE      2
 
-void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]);
-void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]);
+void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
+void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
 
 u8 drm_dp_link_rate_to_bw_code(int link_rate);
 int drm_dp_bw_code_to_link_rate(u8 link_bw);
@@ -379,15 +379,22 @@ struct edp_vsc_psr {
 #define EDP_VSC_PSR_CRC_VALUES_VALID   (1<<2)
 
 static inline int
-drm_dp_max_link_rate(u8 dpcd[DP_RECEIVER_CAP_SIZE])
+drm_dp_max_link_rate(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
 {
        return drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]);
 }
 
 static inline u8
-drm_dp_max_lane_count(u8 dpcd[DP_RECEIVER_CAP_SIZE])
+drm_dp_max_lane_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
 {
        return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
 }
 
+static inline bool
+drm_dp_enhanced_frame_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_DPCD_REV] >= 0x11 &&
+               (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP);
+}
+
 #endif /* _DRM_DP_HELPER_H_ */
index 3d79e51..87578c1 100644 (file)
        {0x1002, 0x679B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x679E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x679F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67A8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67A9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67AA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67B0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67B8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67BA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x67BE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
index 3abfa6e..97d5497 100644 (file)
@@ -49,6 +49,10 @@ extern bool i915_gpu_turbo_disable(void);
 #define    SNB_GMCH_GGMS_MASK  0x3
 #define    SNB_GMCH_GMS_SHIFT   3 /* Graphics Mode Select */
 #define    SNB_GMCH_GMS_MASK    0x1f
+#define    BDW_GMCH_GGMS_SHIFT 6
+#define    BDW_GMCH_GGMS_MASK  0x3
+#define    BDW_GMCH_GMS_SHIFT   8
+#define    BDW_GMCH_GMS_MASK    0xff
 
 #define I830_GMCH_CTRL                 0x52
 
index 8a10f5c..940ece4 100644 (file)
 #define INTEL_VLV_D_IDS(info) \
        INTEL_VGA_DEVICE(0x0155, info)
 
+#define _INTEL_BDW_M(gt, id, info) \
+       INTEL_VGA_DEVICE((((gt) - 1) << 4) | (id), info)
+#define _INTEL_BDW_D(gt, id, info) \
+       INTEL_VGA_DEVICE((((gt) - 1) << 4) | (id), info)
+
+#define _INTEL_BDW_M_IDS(gt, info) \
+       _INTEL_BDW_M(gt, 0x1602, info), /* ULT */ \
+       _INTEL_BDW_M(gt, 0x1606, info), /* ULT */ \
+       _INTEL_BDW_M(gt, 0x160B, info), /* Iris */ \
+       _INTEL_BDW_M(gt, 0x160E, info) /* ULX */
+
+#define _INTEL_BDW_D_IDS(gt, info) \
+       _INTEL_BDW_D(gt, 0x160A, info), /* Server */ \
+       _INTEL_BDW_D(gt, 0x160D, info) /* Workstation */
+
+#define INTEL_BDW_M_IDS(info) \
+       _INTEL_BDW_M_IDS(1, info), \
+       _INTEL_BDW_M_IDS(2, info), \
+       _INTEL_BDW_M_IDS(3, info)
+
+#define INTEL_BDW_D_IDS(info) \
+       _INTEL_BDW_D_IDS(1, info), \
+       _INTEL_BDW_D_IDS(2, info), \
+       _INTEL_BDW_D_IDS(3, info)
+
 #endif /* _I915_PCIIDS_H */
index 706b962..d1f61bf 100644 (file)
@@ -62,7 +62,7 @@ extern void ttm_pool_unpopulate(struct ttm_tt *ttm);
 extern int ttm_page_alloc_debugfs(struct seq_file *m, void *data);
 
 
-#ifdef CONFIG_SWIOTLB
+#if defined(CONFIG_SWIOTLB) || defined(CONFIG_INTEL_IOMMU)
 /**
  * Initialize pool allocator.
  */
@@ -94,6 +94,15 @@ static inline int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data)
 {
        return 0;
 }
+static inline int ttm_dma_populate(struct ttm_dma_tt *ttm_dma,
+                                  struct device *dev)
+{
+       return -ENOMEM;
+}
+static inline void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma,
+                                     struct device *dev)
+{
+}
 #endif
 
 #endif
diff --git a/include/dt-bindings/mfd/as3722.h b/include/dt-bindings/mfd/as3722.h
new file mode 100644 (file)
index 0000000..0e69256
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * This header provides macros for ams AS3722 device bindings.
+ *
+ * Copyright (c) 2013, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ */
+
+#ifndef __DT_BINDINGS_AS3722_H__
+#define __DT_BINDINGS_AS3722_H__
+
+/* External control pins */
+#define AS3722_EXT_CONTROL_PIN_ENABLE1 1
+#define AS3722_EXT_CONTROL_PIN_ENABLE2 2
+#define AS3722_EXT_CONTROL_PIN_ENABLE2 3
+
+/* Interrupt numbers for AS3722 */
+#define AS3722_IRQ_LID                 0
+#define AS3722_IRQ_ACOK                        1
+#define AS3722_IRQ_ENABLE1             2
+#define AS3722_IRQ_OCCUR_ALARM_SD0     3
+#define AS3722_IRQ_ONKEY_LONG_PRESS    4
+#define AS3722_IRQ_ONKEY               5
+#define AS3722_IRQ_OVTMP               6
+#define AS3722_IRQ_LOWBAT              7
+#define AS3722_IRQ_SD0_LV              8
+#define AS3722_IRQ_SD1_LV              9
+#define AS3722_IRQ_SD2_LV              10
+#define AS3722_IRQ_PWM1_OV_PROT                11
+#define AS3722_IRQ_PWM2_OV_PROT                12
+#define AS3722_IRQ_ENABLE2             13
+#define AS3722_IRQ_SD6_LV              14
+#define AS3722_IRQ_RTC_REP             15
+#define AS3722_IRQ_RTC_ALARM           16
+#define AS3722_IRQ_GPIO1               17
+#define AS3722_IRQ_GPIO2               18
+#define AS3722_IRQ_GPIO3               19
+#define AS3722_IRQ_GPIO4               20
+#define AS3722_IRQ_GPIO5               21
+#define AS3722_IRQ_WATCHDOG            22
+#define AS3722_IRQ_ENABLE3             23
+#define AS3722_IRQ_TEMP_SD0_SHUTDOWN   24
+#define AS3722_IRQ_TEMP_SD1_SHUTDOWN   25
+#define AS3722_IRQ_TEMP_SD2_SHUTDOWN   26
+#define AS3722_IRQ_TEMP_SD0_ALARM      27
+#define AS3722_IRQ_TEMP_SD1_ALARM      28
+#define AS3722_IRQ_TEMP_SD6_ALARM      29
+#define AS3722_IRQ_OCCUR_ALARM_SD6     30
+#define AS3722_IRQ_ADC                 31
+
+#endif /* __DT_BINDINGS_AS3722_H__ */
diff --git a/include/keys/big_key-type.h b/include/keys/big_key-type.h
new file mode 100644 (file)
index 0000000..d69bc8a
--- /dev/null
@@ -0,0 +1,25 @@
+/* Big capacity key type.
+ *
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _KEYS_BIG_KEY_TYPE_H
+#define _KEYS_BIG_KEY_TYPE_H
+
+#include <linux/key-type.h>
+
+extern struct key_type key_type_big_key;
+
+extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep);
+extern void big_key_revoke(struct key *key);
+extern void big_key_destroy(struct key *key);
+extern void big_key_describe(const struct key *big_key, struct seq_file *m);
+extern long big_key_read(const struct key *key, char __user *buffer, size_t buflen);
+
+#endif /* _KEYS_BIG_KEY_TYPE_H */
index cf49159..fca5c62 100644 (file)
@@ -1,6 +1,6 @@
 /* Keyring key type
  *
- * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2008, 2013 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
 #define _KEYS_KEYRING_TYPE_H
 
 #include <linux/key.h>
-#include <linux/rcupdate.h>
-
-/*
- * the keyring payload contains a list of the keys to which the keyring is
- * subscribed
- */
-struct keyring_list {
-       struct rcu_head rcu;            /* RCU deletion hook */
-       unsigned short  maxkeys;        /* max keys this list can hold */
-       unsigned short  nkeys;          /* number of keys currently held */
-       unsigned short  delkey;         /* key to be unlinked by RCU */
-       struct key __rcu *keys[0];
-};
-
+#include <linux/assoc_array.h>
 
 #endif /* _KEYS_KEYRING_TYPE_H */
diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h
new file mode 100644 (file)
index 0000000..8dabc39
--- /dev/null
@@ -0,0 +1,23 @@
+/* System keyring containing trusted public keys.
+ *
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _KEYS_SYSTEM_KEYRING_H
+#define _KEYS_SYSTEM_KEYRING_H
+
+#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
+
+#include <linux/key.h>
+
+extern struct key *system_trusted_keyring;
+
+#endif
+
+#endif /* _KEYS_SYSTEM_KEYRING_H */
index b0972c4..d9099b1 100644 (file)
 #include <acpi/acpi_numa.h>
 #include <asm/acpi.h>
 
+static inline acpi_handle acpi_device_handle(struct acpi_device *adev)
+{
+       return adev ? adev->handle : NULL;
+}
+
+#define ACPI_COMPANION(dev)            ((dev)->acpi_node.companion)
+#define ACPI_COMPANION_SET(dev, adev)  ACPI_COMPANION(dev) = (adev)
+#define ACPI_HANDLE(dev)               acpi_device_handle(ACPI_COMPANION(dev))
+
+static inline const char *acpi_dev_name(struct acpi_device *adev)
+{
+       return dev_name(&adev->dev);
+}
+
 enum acpi_irq_model_id {
        ACPI_IRQ_MODEL_PIC = 0,
        ACPI_IRQ_MODEL_IOAPIC,
@@ -401,6 +415,15 @@ static inline bool acpi_driver_match_device(struct device *dev,
 
 #define acpi_disabled 1
 
+#define ACPI_COMPANION(dev)            (NULL)
+#define ACPI_COMPANION_SET(dev, adev)  do { } while (0)
+#define ACPI_HANDLE(dev)               (NULL)
+
+static inline const char *acpi_dev_name(struct acpi_device *adev)
+{
+       return NULL;
+}
+
 static inline void acpi_early_init(void) { }
 
 static inline int early_acpi_boot_init(void)
index 62d9303..0ddb5c0 100644 (file)
@@ -40,7 +40,7 @@
 #define UART010_LCRL           0x10    /* Line control register, low byte. */
 #define UART010_CR             0x14    /* Control register. */
 #define UART01x_FR             0x18    /* Flag register (Read only). */
-#define UART010_IIR            0x1C    /* Interrupt indentification register (Read). */
+#define UART010_IIR            0x1C    /* Interrupt identification register (Read). */
 #define UART010_ICR            0x1C    /* Interrupt clear register (Write). */
 #define ST_UART011_LCRH_RX     0x1C    /* Rx line control register. */
 #define UART01x_ILPR           0x20    /* IrDA low power counter register. */
diff --git a/include/linux/assoc_array.h b/include/linux/assoc_array.h
new file mode 100644 (file)
index 0000000..9a193b8
--- /dev/null
@@ -0,0 +1,92 @@
+/* Generic associative array implementation.
+ *
+ * See Documentation/assoc_array.txt for information.
+ *
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_ASSOC_ARRAY_H
+#define _LINUX_ASSOC_ARRAY_H
+
+#ifdef CONFIG_ASSOCIATIVE_ARRAY
+
+#include <linux/types.h>
+
+#define ASSOC_ARRAY_KEY_CHUNK_SIZE BITS_PER_LONG /* Key data retrieved in chunks of this size */
+
+/*
+ * Generic associative array.
+ */
+struct assoc_array {
+       struct assoc_array_ptr  *root;          /* The node at the root of the tree */
+       unsigned long           nr_leaves_on_tree;
+};
+
+/*
+ * Operations on objects and index keys for use by array manipulation routines.
+ */
+struct assoc_array_ops {
+       /* Method to get a chunk of an index key from caller-supplied data */
+       unsigned long (*get_key_chunk)(const void *index_key, int level);
+
+       /* Method to get a piece of an object's index key */
+       unsigned long (*get_object_key_chunk)(const void *object, int level);
+
+       /* Is this the object we're looking for? */
+       bool (*compare_object)(const void *object, const void *index_key);
+
+       /* How different are two objects, to a bit position in their keys? (or
+        * -1 if they're the same)
+        */
+       int (*diff_objects)(const void *a, const void *b);
+
+       /* Method to free an object. */
+       void (*free_object)(void *object);
+};
+
+/*
+ * Access and manipulation functions.
+ */
+struct assoc_array_edit;
+
+static inline void assoc_array_init(struct assoc_array *array)
+{
+       array->root = NULL;
+       array->nr_leaves_on_tree = 0;
+}
+
+extern int assoc_array_iterate(const struct assoc_array *array,
+                              int (*iterator)(const void *object,
+                                              void *iterator_data),
+                              void *iterator_data);
+extern void *assoc_array_find(const struct assoc_array *array,
+                             const struct assoc_array_ops *ops,
+                             const void *index_key);
+extern void assoc_array_destroy(struct assoc_array *array,
+                               const struct assoc_array_ops *ops);
+extern struct assoc_array_edit *assoc_array_insert(struct assoc_array *array,
+                                                  const struct assoc_array_ops *ops,
+                                                  const void *index_key,
+                                                  void *object);
+extern void assoc_array_insert_set_object(struct assoc_array_edit *edit,
+                                         void *object);
+extern struct assoc_array_edit *assoc_array_delete(struct assoc_array *array,
+                                                  const struct assoc_array_ops *ops,
+                                                  const void *index_key);
+extern struct assoc_array_edit *assoc_array_clear(struct assoc_array *array,
+                                                 const struct assoc_array_ops *ops);
+extern void assoc_array_apply_edit(struct assoc_array_edit *edit);
+extern void assoc_array_cancel_edit(struct assoc_array_edit *edit);
+extern int assoc_array_gc(struct assoc_array *array,
+                         const struct assoc_array_ops *ops,
+                         bool (*iterator)(void *object, void *iterator_data),
+                         void *iterator_data);
+
+#endif /* CONFIG_ASSOCIATIVE_ARRAY */
+#endif /* _LINUX_ASSOC_ARRAY_H */
diff --git a/include/linux/assoc_array_priv.h b/include/linux/assoc_array_priv.h
new file mode 100644 (file)
index 0000000..711275e
--- /dev/null
@@ -0,0 +1,182 @@
+/* Private definitions for the generic associative array implementation.
+ *
+ * See Documentation/assoc_array.txt for information.
+ *
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_ASSOC_ARRAY_PRIV_H
+#define _LINUX_ASSOC_ARRAY_PRIV_H
+
+#ifdef CONFIG_ASSOCIATIVE_ARRAY
+
+#include <linux/assoc_array.h>
+
+#define ASSOC_ARRAY_FAN_OUT            16      /* Number of slots per node */
+#define ASSOC_ARRAY_FAN_MASK           (ASSOC_ARRAY_FAN_OUT - 1)
+#define ASSOC_ARRAY_LEVEL_STEP         (ilog2(ASSOC_ARRAY_FAN_OUT))
+#define ASSOC_ARRAY_LEVEL_STEP_MASK    (ASSOC_ARRAY_LEVEL_STEP - 1)
+#define ASSOC_ARRAY_KEY_CHUNK_MASK     (ASSOC_ARRAY_KEY_CHUNK_SIZE - 1)
+#define ASSOC_ARRAY_KEY_CHUNK_SHIFT    (ilog2(BITS_PER_LONG))
+
+/*
+ * Undefined type representing a pointer with type information in the bottom
+ * two bits.
+ */
+struct assoc_array_ptr;
+
+/*
+ * An N-way node in the tree.
+ *
+ * Each slot contains one of four things:
+ *
+ *     (1) Nothing (NULL).
+ *
+ *     (2) A leaf object (pointer types 0).
+ *
+ *     (3) A next-level node (pointer type 1, subtype 0).
+ *
+ *     (4) A shortcut (pointer type 1, subtype 1).
+ *
+ * The tree is optimised for search-by-ID, but permits reasonable iteration
+ * also.
+ *
+ * The tree is navigated by constructing an index key consisting of an array of
+ * segments, where each segment is ilog2(ASSOC_ARRAY_FAN_OUT) bits in size.
+ *
+ * The segments correspond to levels of the tree (the first segment is used at
+ * level 0, the second at level 1, etc.).
+ */
+struct assoc_array_node {
+       struct assoc_array_ptr  *back_pointer;
+       u8                      parent_slot;
+       struct assoc_array_ptr  *slots[ASSOC_ARRAY_FAN_OUT];
+       unsigned long           nr_leaves_on_branch;
+};
+
+/*
+ * A shortcut through the index space out to where a collection of nodes/leaves
+ * with the same IDs live.
+ */
+struct assoc_array_shortcut {
+       struct assoc_array_ptr  *back_pointer;
+       int                     parent_slot;
+       int                     skip_to_level;
+       struct assoc_array_ptr  *next_node;
+       unsigned long           index_key[];
+};
+
+/*
+ * Preallocation cache.
+ */
+struct assoc_array_edit {
+       struct rcu_head                 rcu;
+       struct assoc_array              *array;
+       const struct assoc_array_ops    *ops;
+       const struct assoc_array_ops    *ops_for_excised_subtree;
+       struct assoc_array_ptr          *leaf;
+       struct assoc_array_ptr          **leaf_p;
+       struct assoc_array_ptr          *dead_leaf;
+       struct assoc_array_ptr          *new_meta[3];
+       struct assoc_array_ptr          *excised_meta[1];
+       struct assoc_array_ptr          *excised_subtree;
+       struct assoc_array_ptr          **set_backpointers[ASSOC_ARRAY_FAN_OUT];
+       struct assoc_array_ptr          *set_backpointers_to;
+       struct assoc_array_node         *adjust_count_on;
+       long                            adjust_count_by;
+       struct {
+               struct assoc_array_ptr  **ptr;
+               struct assoc_array_ptr  *to;
+       } set[2];
+       struct {
+               u8                      *p;
+               u8                      to;
+       } set_parent_slot[1];
+       u8                              segment_cache[ASSOC_ARRAY_FAN_OUT + 1];
+};
+
+/*
+ * Internal tree member pointers are marked in the bottom one or two bits to
+ * indicate what type they are so that we don't have to look behind every
+ * pointer to see what it points to.
+ *
+ * We provide functions to test type annotations and to create and translate
+ * the annotated pointers.
+ */
+#define ASSOC_ARRAY_PTR_TYPE_MASK 0x1UL
+#define ASSOC_ARRAY_PTR_LEAF_TYPE 0x0UL        /* Points to leaf (or nowhere) */
+#define ASSOC_ARRAY_PTR_META_TYPE 0x1UL        /* Points to node or shortcut */
+#define ASSOC_ARRAY_PTR_SUBTYPE_MASK   0x2UL
+#define ASSOC_ARRAY_PTR_NODE_SUBTYPE   0x0UL
+#define ASSOC_ARRAY_PTR_SHORTCUT_SUBTYPE 0x2UL
+
+static inline bool assoc_array_ptr_is_meta(const struct assoc_array_ptr *x)
+{
+       return (unsigned long)x & ASSOC_ARRAY_PTR_TYPE_MASK;
+}
+static inline bool assoc_array_ptr_is_leaf(const struct assoc_array_ptr *x)
+{
+       return !assoc_array_ptr_is_meta(x);
+}
+static inline bool assoc_array_ptr_is_shortcut(const struct assoc_array_ptr *x)
+{
+       return (unsigned long)x & ASSOC_ARRAY_PTR_SUBTYPE_MASK;
+}
+static inline bool assoc_array_ptr_is_node(const struct assoc_array_ptr *x)
+{
+       return !assoc_array_ptr_is_shortcut(x);
+}
+
+static inline void *assoc_array_ptr_to_leaf(const struct assoc_array_ptr *x)
+{
+       return (void *)((unsigned long)x & ~ASSOC_ARRAY_PTR_TYPE_MASK);
+}
+
+static inline
+unsigned long __assoc_array_ptr_to_meta(const struct assoc_array_ptr *x)
+{
+       return (unsigned long)x &
+               ~(ASSOC_ARRAY_PTR_SUBTYPE_MASK | ASSOC_ARRAY_PTR_TYPE_MASK);
+}
+static inline
+struct assoc_array_node *assoc_array_ptr_to_node(const struct assoc_array_ptr *x)
+{
+       return (struct assoc_array_node *)__assoc_array_ptr_to_meta(x);
+}
+static inline
+struct assoc_array_shortcut *assoc_array_ptr_to_shortcut(const struct assoc_array_ptr *x)
+{
+       return (struct assoc_array_shortcut *)__assoc_array_ptr_to_meta(x);
+}
+
+static inline
+struct assoc_array_ptr *__assoc_array_x_to_ptr(const void *p, unsigned long t)
+{
+       return (struct assoc_array_ptr *)((unsigned long)p | t);
+}
+static inline
+struct assoc_array_ptr *assoc_array_leaf_to_ptr(const void *p)
+{
+       return __assoc_array_x_to_ptr(p, ASSOC_ARRAY_PTR_LEAF_TYPE);
+}
+static inline
+struct assoc_array_ptr *assoc_array_node_to_ptr(const struct assoc_array_node *p)
+{
+       return __assoc_array_x_to_ptr(
+               p, ASSOC_ARRAY_PTR_META_TYPE | ASSOC_ARRAY_PTR_NODE_SUBTYPE);
+}
+static inline
+struct assoc_array_ptr *assoc_array_shortcut_to_ptr(const struct assoc_array_shortcut *p)
+{
+       return __assoc_array_x_to_ptr(
+               p, ASSOC_ARRAY_PTR_META_TYPE | ASSOC_ARRAY_PTR_SHORTCUT_SUBTYPE);
+}
+
+#endif /* CONFIG_ASSOCIATIVE_ARRAY */
+#endif /* _LINUX_ASSOC_ARRAY_PRIV_H */
index 729a4d1..a406419 100644 (file)
@@ -73,6 +73,8 @@ struct audit_field {
        void                            *lsm_rule;
 };
 
+extern int is_audit_feature_set(int which);
+
 extern int __init audit_register_class(int class, unsigned *list);
 extern int audit_classify_syscall(int abi, unsigned syscall);
 extern int audit_classify_arch(int arch);
@@ -207,7 +209,7 @@ static inline int audit_get_sessionid(struct task_struct *tsk)
 
 extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp);
 extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode);
-extern int __audit_bprm(struct linux_binprm *bprm);
+extern void __audit_bprm(struct linux_binprm *bprm);
 extern int __audit_socketcall(int nargs, unsigned long *args);
 extern int __audit_sockaddr(int len, void *addr);
 extern void __audit_fd_pair(int fd1, int fd2);
@@ -236,11 +238,10 @@ static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid
        if (unlikely(!audit_dummy_context()))
                __audit_ipc_set_perm(qbytes, uid, gid, mode);
 }
-static inline int audit_bprm(struct linux_binprm *bprm)
+static inline void audit_bprm(struct linux_binprm *bprm)
 {
        if (unlikely(!audit_dummy_context()))
-               return __audit_bprm(bprm);
-       return 0;
+               __audit_bprm(bprm);
 }
 static inline int audit_socketcall(int nargs, unsigned long *args)
 {
@@ -367,10 +368,8 @@ static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
 static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid,
                                        gid_t gid, umode_t mode)
 { }
-static inline int audit_bprm(struct linux_binprm *bprm)
-{
-       return 0;
-}
+static inline void audit_bprm(struct linux_binprm *bprm)
+{ }
 static inline int audit_socketcall(int nargs, unsigned long *args)
 {
        return 0;
index f26ec20..1b135d4 100644 (file)
@@ -505,6 +505,9 @@ struct request_queue {
                                 (1 << QUEUE_FLAG_SAME_COMP)    |       \
                                 (1 << QUEUE_FLAG_ADD_RANDOM))
 
+#define QUEUE_FLAG_MQ_DEFAULT  ((1 << QUEUE_FLAG_IO_STAT) |            \
+                                (1 << QUEUE_FLAG_SAME_COMP))
+
 static inline void queue_lockdep_assert_held(struct request_queue *q)
 {
        if (q->queue_lock)
index 98e892e..a0f9280 100644 (file)
@@ -8,6 +8,8 @@
 #define CMDLINEPARSEH
 
 #include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
 
 /* partition flags */
 #define PF_RDONLY                   0x01 /* Device is read only */
index 22c33e3..5d5aaae 100644 (file)
@@ -19,8 +19,8 @@
  *
  * See also:  complete(), wait_for_completion() (and friends _timeout,
  * _interruptible, _interruptible_timeout, and _killable), init_completion(),
- * and macros DECLARE_COMPLETION(), DECLARE_COMPLETION_ONSTACK(), and
- * INIT_COMPLETION().
+ * reinit_completion(), and macros DECLARE_COMPLETION(),
+ * DECLARE_COMPLETION_ONSTACK().
  */
 struct completion {
        unsigned int done;
@@ -65,7 +65,7 @@ struct completion {
 
 /**
  * init_completion - Initialize a dynamically allocated completion
- * @x:  completion structure that is to be initialized
+ * @x:  pointer to completion structure that is to be initialized
  *
  * This inline function will initialize a dynamically created completion
  * structure.
@@ -76,6 +76,18 @@ static inline void init_completion(struct completion *x)
        init_waitqueue_head(&x->wait);
 }
 
+/**
+ * reinit_completion - reinitialize a completion structure
+ * @x:  pointer to completion structure that is to be reinitialized
+ *
+ * This inline function should be used to reinitialize a completion structure so it can
+ * be reused. This is especially important after complete_all() is used.
+ */
+static inline void reinit_completion(struct completion *x)
+{
+       x->done = 0;
+}
+
 extern void wait_for_completion(struct completion *);
 extern void wait_for_completion_io(struct completion *);
 extern int wait_for_completion_interruptible(struct completion *x);
@@ -94,14 +106,4 @@ extern bool completion_done(struct completion *x);
 extern void complete(struct completion *);
 extern void complete_all(struct completion *);
 
-/**
- * INIT_COMPLETION - reinitialize a completion structure
- * @x:  completion structure to be reinitialized
- *
- * This macro should be used to reinitialize a completion structure so it can
- * be reused. This is especially important after complete_all() is used.
- */
-#define INIT_COMPLETION(x)     ((x).done = 0)
-
-
 #endif
index 5bd6ab9..dc196bb 100644 (file)
@@ -107,8 +107,16 @@ struct cpufreq_policy {
 #define CPUFREQ_SHARED_TYPE_ALL         (2) /* All dependent CPUs should set freq */
 #define CPUFREQ_SHARED_TYPE_ANY         (3) /* Freq can be set from any dependent CPU*/
 
+#ifdef CONFIG_CPU_FREQ
 struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu);
 void cpufreq_cpu_put(struct cpufreq_policy *policy);
+#else
+static inline struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
+{
+       return NULL;
+}
+static inline void cpufreq_cpu_put(struct cpufreq_policy *policy) { }
+#endif
 
 static inline bool policy_is_shared(struct cpufreq_policy *policy)
 {
index 7a7cc74..d48dc00 100644 (file)
@@ -168,7 +168,7 @@ struct devfreq {
        unsigned long max_freq;
        bool stop_polling;
 
-       /* information for device freqeuncy transition */
+       /* information for device frequency transition */
        unsigned int total_trans;
        unsigned int *trans_table;
        unsigned long *time_in_state;
index b025925..952b010 100644 (file)
@@ -644,9 +644,11 @@ struct device_dma_parameters {
        unsigned long segment_boundary_mask;
 };
 
+struct acpi_device;
+
 struct acpi_dev_node {
 #ifdef CONFIG_ACPI
-       void    *handle;
+       struct acpi_device *companion;
 #endif
 };
 
@@ -790,14 +792,6 @@ static inline struct device *kobj_to_dev(struct kobject *kobj)
        return container_of(kobj, struct device, kobj);
 }
 
-#ifdef CONFIG_ACPI
-#define ACPI_HANDLE(dev)       ((dev)->acpi_node.handle)
-#define ACPI_HANDLE_SET(dev, _handle_) (dev)->acpi_node.handle = (_handle_)
-#else
-#define ACPI_HANDLE(dev)       (NULL)
-#define ACPI_HANDLE_SET(dev, _handle_) do { } while (0)
-#endif
-
 /* Get the wakeup routines, which depend on struct device */
 #include <linux/pm_wakeup.h>
 
index 0bc7275..41cf0c3 100644 (file)
@@ -45,13 +45,13 @@ static inline int dma_submit_error(dma_cookie_t cookie)
 
 /**
  * enum dma_status - DMA transaction status
- * @DMA_SUCCESS: transaction completed successfully
+ * @DMA_COMPLETE: transaction completed
  * @DMA_IN_PROGRESS: transaction not yet processed
  * @DMA_PAUSED: transaction is paused
  * @DMA_ERROR: transaction failed
  */
 enum dma_status {
-       DMA_SUCCESS,
+       DMA_COMPLETE,
        DMA_IN_PROGRESS,
        DMA_PAUSED,
        DMA_ERROR,
@@ -171,12 +171,6 @@ struct dma_interleaved_template {
  * @DMA_CTRL_ACK - if clear, the descriptor cannot be reused until the client
  *  acknowledges receipt, i.e. has has a chance to establish any dependency
  *  chains
- * @DMA_COMPL_SKIP_SRC_UNMAP - set to disable dma-unmapping the source buffer(s)
- * @DMA_COMPL_SKIP_DEST_UNMAP - set to disable dma-unmapping the destination(s)
- * @DMA_COMPL_SRC_UNMAP_SINGLE - set to do the source dma-unmapping as single
- *     (if not set, do the source dma-unmapping as page)
- * @DMA_COMPL_DEST_UNMAP_SINGLE - set to do the destination dma-unmapping as single
- *     (if not set, do the destination dma-unmapping as page)
  * @DMA_PREP_PQ_DISABLE_P - prevent generation of P while generating Q
  * @DMA_PREP_PQ_DISABLE_Q - prevent generation of Q while generating P
  * @DMA_PREP_CONTINUE - indicate to a driver that it is reusing buffers as
@@ -188,14 +182,10 @@ struct dma_interleaved_template {
 enum dma_ctrl_flags {
        DMA_PREP_INTERRUPT = (1 << 0),
        DMA_CTRL_ACK = (1 << 1),
-       DMA_COMPL_SKIP_SRC_UNMAP = (1 << 2),
-       DMA_COMPL_SKIP_DEST_UNMAP = (1 << 3),
-       DMA_COMPL_SRC_UNMAP_SINGLE = (1 << 4),
-       DMA_COMPL_DEST_UNMAP_SINGLE = (1 << 5),
-       DMA_PREP_PQ_DISABLE_P = (1 << 6),
-       DMA_PREP_PQ_DISABLE_Q = (1 << 7),
-       DMA_PREP_CONTINUE = (1 << 8),
-       DMA_PREP_FENCE = (1 << 9),
+       DMA_PREP_PQ_DISABLE_P = (1 << 2),
+       DMA_PREP_PQ_DISABLE_Q = (1 << 3),
+       DMA_PREP_CONTINUE = (1 << 4),
+       DMA_PREP_FENCE = (1 << 5),
 };
 
 /**
@@ -413,6 +403,17 @@ void dma_chan_cleanup(struct kref *kref);
 typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
 
 typedef void (*dma_async_tx_callback)(void *dma_async_param);
+
+struct dmaengine_unmap_data {
+       u8 to_cnt;
+       u8 from_cnt;
+       u8 bidi_cnt;
+       struct device *dev;
+       struct kref kref;
+       size_t len;
+       dma_addr_t addr[0];
+};
+
 /**
  * struct dma_async_tx_descriptor - async transaction descriptor
  * ---dma generic offload fields---
@@ -438,6 +439,7 @@ struct dma_async_tx_descriptor {
        dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx);
        dma_async_tx_callback callback;
        void *callback_param;
+       struct dmaengine_unmap_data *unmap;
 #ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
        struct dma_async_tx_descriptor *next;
        struct dma_async_tx_descriptor *parent;
@@ -445,6 +447,40 @@ struct dma_async_tx_descriptor {
 #endif
 };
 
+#ifdef CONFIG_DMA_ENGINE
+static inline void dma_set_unmap(struct dma_async_tx_descriptor *tx,
+                                struct dmaengine_unmap_data *unmap)
+{
+       kref_get(&unmap->kref);
+       tx->unmap = unmap;
+}
+
+struct dmaengine_unmap_data *
+dmaengine_get_unmap_data(struct device *dev, int nr, gfp_t flags);
+void dmaengine_unmap_put(struct dmaengine_unmap_data *unmap);
+#else
+static inline void dma_set_unmap(struct dma_async_tx_descriptor *tx,
+                                struct dmaengine_unmap_data *unmap)
+{
+}
+static inline struct dmaengine_unmap_data *
+dmaengine_get_unmap_data(struct device *dev, int nr, gfp_t flags)
+{
+       return NULL;
+}
+static inline void dmaengine_unmap_put(struct dmaengine_unmap_data *unmap)
+{
+}
+#endif
+
+static inline void dma_descriptor_unmap(struct dma_async_tx_descriptor *tx)
+{
+       if (tx->unmap) {
+               dmaengine_unmap_put(tx->unmap);
+               tx->unmap = NULL;
+       }
+}
+
 #ifndef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
 static inline void txd_lock(struct dma_async_tx_descriptor *txd)
 {
@@ -979,10 +1015,10 @@ static inline enum dma_status dma_async_is_complete(dma_cookie_t cookie,
 {
        if (last_complete <= last_used) {
                if ((cookie <= last_complete) || (cookie > last_used))
-                       return DMA_SUCCESS;
+                       return DMA_COMPLETE;
        } else {
                if ((cookie <= last_complete) && (cookie > last_used))
-                       return DMA_SUCCESS;
+                       return DMA_COMPLETE;
        }
        return DMA_IN_PROGRESS;
 }
@@ -1013,11 +1049,11 @@ static inline struct dma_chan *dma_find_channel(enum dma_transaction_type tx_typ
 }
 static inline enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie)
 {
-       return DMA_SUCCESS;
+       return DMA_COMPLETE;
 }
 static inline enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
 {
-       return DMA_SUCCESS;
+       return DMA_COMPLETE;
 }
 static inline void dma_issue_pending_all(void)
 {
index 412cd50..3f2793d 100644 (file)
@@ -43,7 +43,7 @@ extern struct module __this_module;
 /* Mark the CRC weak since genksyms apparently decides not to
  * generate a checksums for some symbols */
 #define __CRC_SYMBOL(sym, sec)                                 \
-       extern void *__crc_##sym __attribute__((weak));         \
+       extern __visible void *__crc_##sym __attribute__((weak));               \
        static const unsigned long __kcrctab_##sym              \
        __used                                                  \
        __attribute__((section("___kcrctab" sec "+" #sym), unused))     \
@@ -59,7 +59,7 @@ extern struct module __this_module;
        static const char __kstrtab_##sym[]                     \
        __attribute__((section("__ksymtab_strings"), aligned(1))) \
        = VMLINUX_SYMBOL_STR(sym);                              \
-       static const struct kernel_symbol __ksymtab_##sym       \
+       __visible const struct kernel_symbol __ksymtab_##sym    \
        __used                                                  \
        __attribute__((section("___ksymtab" sec "+" #sym), unused))     \
        = { (unsigned long)&sym, __kstrtab_##sym }
index bf5d574..121f11f 100644 (file)
@@ -2622,7 +2622,9 @@ extern int simple_write_begin(struct file *file, struct address_space *mapping,
 extern int simple_write_end(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned copied,
                        struct page *page, void *fsdata);
+extern int always_delete_dentry(const struct dentry *);
 extern struct inode *alloc_anon_inode(struct super_block *);
+extern const struct dentry_operations simple_dentry_operations;
 
 extern struct dentry *simple_lookup(struct inode *, struct dentry *, unsigned int flags);
 extern ssize_t generic_read_dir(struct file *, char __user *, size_t, loff_t *);
index 9f15c00..31ea4b4 100644 (file)
@@ -533,11 +533,11 @@ static inline int ftrace_force_update(void) { return 0; }
 static inline void ftrace_disable_daemon(void) { }
 static inline void ftrace_enable_daemon(void) { }
 static inline void ftrace_release_mod(struct module *mod) {}
-static inline int register_ftrace_command(struct ftrace_func_command *cmd)
+static inline __init int register_ftrace_command(struct ftrace_func_command *cmd)
 {
        return -EINVAL;
 }
-static inline int unregister_ftrace_command(char *cmd_name)
+static inline __init int unregister_ftrace_command(char *cmd_name)
 {
        return -EINVAL;
 }
@@ -721,6 +721,7 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth,
 extern char __irqentry_text_start[];
 extern char __irqentry_text_end[];
 
+#define FTRACE_NOTRACE_DEPTH 65536
 #define FTRACE_RETFUNC_DEPTH 50
 #define FTRACE_RETSTACK_ALLOC_SIZE 32
 extern int register_ftrace_graph(trace_func_graph_ret_t retfunc,
index 5eaa746..9abbe63 100644 (file)
@@ -202,6 +202,7 @@ enum {
        TRACE_EVENT_FL_NO_SET_FILTER_BIT,
        TRACE_EVENT_FL_IGNORE_ENABLE_BIT,
        TRACE_EVENT_FL_WAS_ENABLED_BIT,
+       TRACE_EVENT_FL_USE_CALL_FILTER_BIT,
 };
 
 /*
@@ -213,6 +214,7 @@ enum {
  *  WAS_ENABLED   - Set and stays set when an event was ever enabled
  *                    (used for module unloading, if a module event is enabled,
  *                     it is best to clear the buffers that used it).
+ *  USE_CALL_FILTER - For ftrace internal events, don't use file filter
  */
 enum {
        TRACE_EVENT_FL_FILTERED         = (1 << TRACE_EVENT_FL_FILTERED_BIT),
@@ -220,6 +222,7 @@ enum {
        TRACE_EVENT_FL_NO_SET_FILTER    = (1 << TRACE_EVENT_FL_NO_SET_FILTER_BIT),
        TRACE_EVENT_FL_IGNORE_ENABLE    = (1 << TRACE_EVENT_FL_IGNORE_ENABLE_BIT),
        TRACE_EVENT_FL_WAS_ENABLED      = (1 << TRACE_EVENT_FL_WAS_ENABLED_BIT),
+       TRACE_EVENT_FL_USE_CALL_FILTER  = (1 << TRACE_EVENT_FL_USE_CALL_FILTER_BIT),
 };
 
 struct ftrace_event_call {
@@ -238,6 +241,7 @@ struct ftrace_event_call {
         *   bit 2:             failed to apply filter
         *   bit 3:             ftrace internal event (do not enable)
         *   bit 4:             Event was enabled by module
+        *   bit 5:             use call filter rather than file filter
         */
        int                     flags; /* static flags of different events */
 
@@ -253,6 +257,8 @@ struct ftrace_subsystem_dir;
 enum {
        FTRACE_EVENT_FL_ENABLED_BIT,
        FTRACE_EVENT_FL_RECORDED_CMD_BIT,
+       FTRACE_EVENT_FL_FILTERED_BIT,
+       FTRACE_EVENT_FL_NO_SET_FILTER_BIT,
        FTRACE_EVENT_FL_SOFT_MODE_BIT,
        FTRACE_EVENT_FL_SOFT_DISABLED_BIT,
 };
@@ -261,6 +267,8 @@ enum {
  * Ftrace event file flags:
  *  ENABLED      - The event is enabled
  *  RECORDED_CMD  - The comms should be recorded at sched_switch
+ *  FILTERED     - The event has a filter attached
+ *  NO_SET_FILTER - Set when filter has error and is to be ignored
  *  SOFT_MODE     - The event is enabled/disabled by SOFT_DISABLED
  *  SOFT_DISABLED - When set, do not trace the event (even though its
  *                   tracepoint may be enabled)
@@ -268,6 +276,8 @@ enum {
 enum {
        FTRACE_EVENT_FL_ENABLED         = (1 << FTRACE_EVENT_FL_ENABLED_BIT),
        FTRACE_EVENT_FL_RECORDED_CMD    = (1 << FTRACE_EVENT_FL_RECORDED_CMD_BIT),
+       FTRACE_EVENT_FL_FILTERED        = (1 << FTRACE_EVENT_FL_FILTERED_BIT),
+       FTRACE_EVENT_FL_NO_SET_FILTER   = (1 << FTRACE_EVENT_FL_NO_SET_FILTER_BIT),
        FTRACE_EVENT_FL_SOFT_MODE       = (1 << FTRACE_EVENT_FL_SOFT_MODE_BIT),
        FTRACE_EVENT_FL_SOFT_DISABLED   = (1 << FTRACE_EVENT_FL_SOFT_DISABLED_BIT),
 };
@@ -275,6 +285,7 @@ enum {
 struct ftrace_event_file {
        struct list_head                list;
        struct ftrace_event_call        *event_call;
+       struct event_filter             *filter;
        struct dentry                   *dir;
        struct trace_array              *tr;
        struct ftrace_subsystem_dir     *system;
@@ -310,12 +321,16 @@ struct ftrace_event_file {
 
 #define MAX_FILTER_STR_VAL     256     /* Should handle KSYM_SYMBOL_LEN */
 
-extern void destroy_preds(struct ftrace_event_call *call);
+extern void destroy_preds(struct ftrace_event_file *file);
+extern void destroy_call_preds(struct ftrace_event_call *call);
 extern int filter_match_preds(struct event_filter *filter, void *rec);
-extern int filter_current_check_discard(struct ring_buffer *buffer,
-                                       struct ftrace_event_call *call,
-                                       void *rec,
-                                       struct ring_buffer_event *event);
+
+extern int filter_check_discard(struct ftrace_event_file *file, void *rec,
+                               struct ring_buffer *buffer,
+                               struct ring_buffer_event *event);
+extern int call_filter_check_discard(struct ftrace_event_call *call, void *rec,
+                                    struct ring_buffer *buffer,
+                                    struct ring_buffer_event *event);
 
 enum {
        FILTER_OTHER = 0,
index 023bc34..c0894dd 100644 (file)
@@ -273,49 +273,40 @@ static struct genl_family ZZZ_genl_family __read_mostly = {
  * Magic: define multicast groups
  * Magic: define multicast group registration helper
  */
+#define ZZZ_genl_mcgrps                CONCAT_(GENL_MAGIC_FAMILY, _genl_mcgrps)
+static const struct genl_multicast_group ZZZ_genl_mcgrps[] = {
+#undef GENL_mc_group
+#define GENL_mc_group(group) { .name = #group, },
+#include GENL_MAGIC_INCLUDE_FILE
+};
+
+enum CONCAT_(GENL_MAGIC_FAMILY, group_ids) {
+#undef GENL_mc_group
+#define GENL_mc_group(group) CONCAT_(GENL_MAGIC_FAMILY, _group_ ## group),
+#include GENL_MAGIC_INCLUDE_FILE
+};
+
 #undef GENL_mc_group
 #define GENL_mc_group(group)                                           \
-static struct genl_multicast_group                                     \
-CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group) __read_mostly = {           \
-       .name = #group,                                                 \
-};                                                                     \
 static int CONCAT_(GENL_MAGIC_FAMILY, _genl_multicast_ ## group)(      \
        struct sk_buff *skb, gfp_t flags)                               \
 {                                                                      \
        unsigned int group_id =                                         \
-               CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group).id;  \
-       if (!group_id)                                                  \
-               return -EINVAL;                                         \
-       return genlmsg_multicast(skb, 0, group_id, flags);              \
+               CONCAT_(GENL_MAGIC_FAMILY, _group_ ## group);           \
+       return genlmsg_multicast(&ZZZ_genl_family, skb, 0,              \
+                                group_id, flags);                      \
 }
 
 #include GENL_MAGIC_INCLUDE_FILE
 
-int CONCAT_(GENL_MAGIC_FAMILY, _genl_register)(void)
-{
-       int err = genl_register_family_with_ops(&ZZZ_genl_family,
-               ZZZ_genl_ops, ARRAY_SIZE(ZZZ_genl_ops));
-       if (err)
-               return err;
-#undef GENL_mc_group
-#define GENL_mc_group(group)                                           \
-       err = genl_register_mc_group(&ZZZ_genl_family,                  \
-               &CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group));           \
-       if (err)                                                        \
-               goto fail;                                              \
-       else                                                            \
-               pr_info("%s: mcg %s: %u\n", #group,                     \
-                       __stringify(GENL_MAGIC_FAMILY),                 \
-                       CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group).id);
-
-#include GENL_MAGIC_INCLUDE_FILE
-
 #undef GENL_mc_group
 #define GENL_mc_group(group)
-       return 0;
-fail:
-       genl_unregister_family(&ZZZ_genl_family);
-       return err;
+
+int CONCAT_(GENL_MAGIC_FAMILY, _genl_register)(void)
+{
+       return genl_register_family_with_ops_groups(&ZZZ_genl_family,   \
+                                                   ZZZ_genl_ops,       \
+                                                   ZZZ_genl_mcgrps);
 }
 
 void CONCAT_(GENL_MAGIC_FAMILY, _genl_unregister)(void)
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
new file mode 100644 (file)
index 0000000..f5b9b87
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2009-2013, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LINUX_HOST1X_H
+#define __LINUX_HOST1X_H
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+enum host1x_class {
+       HOST1X_CLASS_HOST1X = 0x1,
+       HOST1X_CLASS_GR2D = 0x51,
+       HOST1X_CLASS_GR2D_SB = 0x52,
+       HOST1X_CLASS_GR3D = 0x60,
+};
+
+struct host1x_client;
+
+struct host1x_client_ops {
+       int (*init)(struct host1x_client *client);
+       int (*exit)(struct host1x_client *client);
+};
+
+struct host1x_client {
+       struct list_head list;
+       struct device *parent;
+       struct device *dev;
+
+       const struct host1x_client_ops *ops;
+
+       enum host1x_class class;
+       struct host1x_channel *channel;
+
+       struct host1x_syncpt **syncpts;
+       unsigned int num_syncpts;
+};
+
+/*
+ * host1x buffer objects
+ */
+
+struct host1x_bo;
+struct sg_table;
+
+struct host1x_bo_ops {
+       struct host1x_bo *(*get)(struct host1x_bo *bo);
+       void (*put)(struct host1x_bo *bo);
+       dma_addr_t (*pin)(struct host1x_bo *bo, struct sg_table **sgt);
+       void (*unpin)(struct host1x_bo *bo, struct sg_table *sgt);
+       void *(*mmap)(struct host1x_bo *bo);
+       void (*munmap)(struct host1x_bo *bo, void *addr);
+       void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum);
+       void (*kunmap)(struct host1x_bo *bo, unsigned int pagenum, void *addr);
+};
+
+struct host1x_bo {
+       const struct host1x_bo_ops *ops;
+};
+
+static inline void host1x_bo_init(struct host1x_bo *bo,
+                                 const struct host1x_bo_ops *ops)
+{
+       bo->ops = ops;
+}
+
+static inline struct host1x_bo *host1x_bo_get(struct host1x_bo *bo)
+{
+       return bo->ops->get(bo);
+}
+
+static inline void host1x_bo_put(struct host1x_bo *bo)
+{
+       bo->ops->put(bo);
+}
+
+static inline dma_addr_t host1x_bo_pin(struct host1x_bo *bo,
+                                      struct sg_table **sgt)
+{
+       return bo->ops->pin(bo, sgt);
+}
+
+static inline void host1x_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
+{
+       bo->ops->unpin(bo, sgt);
+}
+
+static inline void *host1x_bo_mmap(struct host1x_bo *bo)
+{
+       return bo->ops->mmap(bo);
+}
+
+static inline void host1x_bo_munmap(struct host1x_bo *bo, void *addr)
+{
+       bo->ops->munmap(bo, addr);
+}
+
+static inline void *host1x_bo_kmap(struct host1x_bo *bo, unsigned int pagenum)
+{
+       return bo->ops->kmap(bo, pagenum);
+}
+
+static inline void host1x_bo_kunmap(struct host1x_bo *bo,
+                                   unsigned int pagenum, void *addr)
+{
+       bo->ops->kunmap(bo, pagenum, addr);
+}
+
+/*
+ * host1x syncpoints
+ */
+
+#define HOST1X_SYNCPT_CLIENT_MANAGED   (1 << 0)
+#define HOST1X_SYNCPT_HAS_BASE         (1 << 1)
+
+struct host1x_syncpt_base;
+struct host1x_syncpt;
+struct host1x;
+
+struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id);
+u32 host1x_syncpt_id(struct host1x_syncpt *sp);
+u32 host1x_syncpt_read_min(struct host1x_syncpt *sp);
+u32 host1x_syncpt_read_max(struct host1x_syncpt *sp);
+int host1x_syncpt_incr(struct host1x_syncpt *sp);
+int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
+                      u32 *value);
+struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
+                                           unsigned long flags);
+void host1x_syncpt_free(struct host1x_syncpt *sp);
+
+struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp);
+u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base);
+
+/*
+ * host1x channel
+ */
+
+struct host1x_channel;
+struct host1x_job;
+
+struct host1x_channel *host1x_channel_request(struct device *dev);
+void host1x_channel_free(struct host1x_channel *channel);
+struct host1x_channel *host1x_channel_get(struct host1x_channel *channel);
+void host1x_channel_put(struct host1x_channel *channel);
+int host1x_job_submit(struct host1x_job *job);
+
+/*
+ * host1x job
+ */
+
+struct host1x_reloc {
+       struct host1x_bo *cmdbuf;
+       u32 cmdbuf_offset;
+       struct host1x_bo *target;
+       u32 target_offset;
+       u32 shift;
+       u32 pad;
+};
+
+struct host1x_job {
+       /* When refcount goes to zero, job can be freed */
+       struct kref ref;
+
+       /* List entry */
+       struct list_head list;
+
+       /* Channel where job is submitted to */
+       struct host1x_channel *channel;
+
+       u32 client;
+
+       /* Gathers and their memory */
+       struct host1x_job_gather *gathers;
+       unsigned int num_gathers;
+
+       /* Wait checks to be processed at submit time */
+       struct host1x_waitchk *waitchk;
+       unsigned int num_waitchk;
+       u32 waitchk_mask;
+
+       /* Array of handles to be pinned & unpinned */
+       struct host1x_reloc *relocarray;
+       unsigned int num_relocs;
+       struct host1x_job_unpin_data *unpins;
+       unsigned int num_unpins;
+
+       dma_addr_t *addr_phys;
+       dma_addr_t *gather_addr_phys;
+       dma_addr_t *reloc_addr_phys;
+
+       /* Sync point id, number of increments and end related to the submit */
+       u32 syncpt_id;
+       u32 syncpt_incrs;
+       u32 syncpt_end;
+
+       /* Maximum time to wait for this job */
+       unsigned int timeout;
+
+       /* Index and number of slots used in the push buffer */
+       unsigned int first_get;
+       unsigned int num_slots;
+
+       /* Copy of gathers */
+       size_t gather_copy_size;
+       dma_addr_t gather_copy;
+       u8 *gather_copy_mapped;
+
+       /* Check if register is marked as an address reg */
+       int (*is_addr_reg)(struct device *dev, u32 reg, u32 class);
+
+       /* Request a SETCLASS to this class */
+       u32 class;
+
+       /* Add a channel wait for previous ops to complete */
+       bool serialize;
+};
+
+struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
+                                   u32 num_cmdbufs, u32 num_relocs,
+                                   u32 num_waitchks);
+void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *mem_id,
+                          u32 words, u32 offset);
+struct host1x_job *host1x_job_get(struct host1x_job *job);
+void host1x_job_put(struct host1x_job *job);
+int host1x_job_pin(struct host1x_job *job, struct device *dev);
+void host1x_job_unpin(struct host1x_job *job);
+
+/*
+ * subdevice probe infrastructure
+ */
+
+struct host1x_device;
+
+struct host1x_driver {
+       const struct of_device_id *subdevs;
+       struct list_head list;
+       const char *name;
+
+       int (*probe)(struct host1x_device *device);
+       int (*remove)(struct host1x_device *device);
+};
+
+int host1x_driver_register(struct host1x_driver *driver);
+void host1x_driver_unregister(struct host1x_driver *driver);
+
+struct host1x_device {
+       struct host1x_driver *driver;
+       struct list_head list;
+       struct device dev;
+
+       struct mutex subdevs_lock;
+       struct list_head subdevs;
+       struct list_head active;
+
+       struct mutex clients_lock;
+       struct list_head clients;
+};
+
+static inline struct host1x_device *to_host1x_device(struct device *dev)
+{
+       return container_of(dev, struct host1x_device, dev);
+}
+
+int host1x_device_init(struct host1x_device *device);
+int host1x_device_exit(struct host1x_device *device);
+
+int host1x_client_register(struct host1x_client *client);
+int host1x_client_unregister(struct host1x_client *client);
+
+#endif
index 3935428..91672e2 100644 (file)
@@ -54,7 +54,8 @@ enum page_check_address_pmd_flag {
 extern pmd_t *page_check_address_pmd(struct page *page,
                                     struct mm_struct *mm,
                                     unsigned long address,
-                                    enum page_check_address_pmd_flag flag);
+                                    enum page_check_address_pmd_flag flag,
+                                    spinlock_t **ptl);
 
 #define HPAGE_PMD_ORDER (HPAGE_PMD_SHIFT-PAGE_SHIFT)
 #define HPAGE_PMD_NR (1<<HPAGE_PMD_ORDER)
@@ -129,15 +130,15 @@ extern void __vma_adjust_trans_huge(struct vm_area_struct *vma,
                                    unsigned long start,
                                    unsigned long end,
                                    long adjust_next);
-extern int __pmd_trans_huge_lock(pmd_t *pmd,
-                                struct vm_area_struct *vma);
+extern int __pmd_trans_huge_lock(pmd_t *pmd, struct vm_area_struct *vma,
+               spinlock_t **ptl);
 /* mmap_sem must be held on entry */
-static inline int pmd_trans_huge_lock(pmd_t *pmd,
-                                     struct vm_area_struct *vma)
+static inline int pmd_trans_huge_lock(pmd_t *pmd, struct vm_area_struct *vma,
+               spinlock_t **ptl)
 {
        VM_BUG_ON(!rwsem_is_locked(&vma->vm_mm->mmap_sem));
        if (pmd_trans_huge(*pmd))
-               return __pmd_trans_huge_lock(pmd, vma);
+               return __pmd_trans_huge_lock(pmd, vma, ptl);
        else
                return 0;
 }
@@ -215,8 +216,8 @@ static inline void vma_adjust_trans_huge(struct vm_area_struct *vma,
                                         long adjust_next)
 {
 }
-static inline int pmd_trans_huge_lock(pmd_t *pmd,
-                                     struct vm_area_struct *vma)
+static inline int pmd_trans_huge_lock(pmd_t *pmd, struct vm_area_struct *vma,
+               spinlock_t **ptl)
 {
        return 0;
 }
index 0393270..9649ff0 100644 (file)
@@ -31,6 +31,7 @@ struct hugepage_subpool *hugepage_new_subpool(long nr_blocks);
 void hugepage_put_subpool(struct hugepage_subpool *spool);
 
 int PageHuge(struct page *page);
+int PageHeadHuge(struct page *page_head);
 
 void reset_vma_resv_huge_pages(struct vm_area_struct *vma);
 int hugetlb_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
@@ -69,7 +70,6 @@ int dequeue_hwpoisoned_huge_page(struct page *page);
 bool isolate_huge_page(struct page *page, struct list_head *list);
 void putback_active_hugepage(struct page *page);
 bool is_hugepage_active(struct page *page);
-void copy_huge_page(struct page *dst, struct page *src);
 
 #ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE
 pte_t *huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud);
@@ -104,6 +104,11 @@ static inline int PageHuge(struct page *page)
        return 0;
 }
 
+static inline int PageHeadHuge(struct page *page_head)
+{
+       return 0;
+}
+
 static inline void reset_vma_resv_huge_pages(struct vm_area_struct *vma)
 {
 }
@@ -140,9 +145,6 @@ static inline int dequeue_hwpoisoned_huge_page(struct page *page)
 #define isolate_huge_page(p, l) false
 #define putback_active_hugepage(p)     do {} while (0)
 #define is_hugepage_active(x)  false
-static inline void copy_huge_page(struct page *dst, struct page *src)
-{
-}
 
 static inline unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
                unsigned long address, unsigned long end, pgprot_t newprot)
@@ -392,6 +394,15 @@ static inline int hugepage_migration_support(struct hstate *h)
        return pmd_huge_support() && (huge_page_shift(h) == PMD_SHIFT);
 }
 
+static inline spinlock_t *huge_pte_lockptr(struct hstate *h,
+                                          struct mm_struct *mm, pte_t *pte)
+{
+       if (huge_page_size(h) == PMD_SIZE)
+               return pmd_lockptr(mm, (pmd_t *) pte);
+       VM_BUG_ON(huge_page_size(h) == PAGE_SIZE);
+       return &mm->page_table_lock;
+}
+
 #else  /* CONFIG_HUGETLB_PAGE */
 struct hstate {};
 #define alloc_huge_page_node(h, nid) NULL
@@ -401,6 +412,7 @@ struct hstate {};
 #define hstate_sizelog(s) NULL
 #define hstate_vma(v) NULL
 #define hstate_inode(i) NULL
+#define page_hstate(page) NULL
 #define huge_page_size(h) PAGE_SIZE
 #define huge_page_mask(h) PAGE_MASK
 #define vma_kernel_pagesize(v) PAGE_SIZE
@@ -421,6 +433,22 @@ static inline pgoff_t basepage_index(struct page *page)
 #define dissolve_free_huge_pages(s, e) do {} while (0)
 #define pmd_huge_support()     0
 #define hugepage_migration_support(h)  0
+
+static inline spinlock_t *huge_pte_lockptr(struct hstate *h,
+                                          struct mm_struct *mm, pte_t *pte)
+{
+       return &mm->page_table_lock;
+}
 #endif /* CONFIG_HUGETLB_PAGE */
 
+static inline spinlock_t *huge_pte_lock(struct hstate *h,
+                                       struct mm_struct *mm, pte_t *pte)
+{
+       spinlock_t *ptl;
+
+       ptl = huge_pte_lockptr(h, mm, pte);
+       spin_lock(ptl);
+       return ptl;
+}
+
 #endif /* _LINUX_HUGETLB_H */
index 2ab11dc..eff50e0 100644 (file)
@@ -205,7 +205,6 @@ struct i2c_driver {
  * @name: Indicates the type of the device, usually a chip name that's
  *     generic enough to hide second-sourcing and compatible revisions.
  * @adapter: manages the bus segment hosting this I2C device
- * @driver: device's driver, hence pointer to access routines
  * @dev: Driver model device node for the slave.
  * @irq: indicates the IRQ generated by this device (if any)
  * @detected: member of an i2c_driver.clients list or i2c-core's
@@ -222,7 +221,6 @@ struct i2c_client {
                                        /* _LOWER_ 7 bits               */
        char name[I2C_NAME_SIZE];
        struct i2c_adapter *adapter;    /* the adapter we sit on        */
-       struct i2c_driver *driver;      /* and our access routines      */
        struct device dev;              /* the device structure         */
        int irq;                        /* irq issued by device         */
        struct list_head detected;
index c270285..84ba5ac 100644 (file)
@@ -119,4 +119,21 @@ extern int macvlan_link_register(struct rtnl_link_ops *ops);
 extern netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
                                      struct net_device *dev);
 
+#if IS_ENABLED(CONFIG_MACVLAN)
+static inline struct net_device *
+macvlan_dev_real_dev(const struct net_device *dev)
+{
+       struct macvlan_dev *macvlan = netdev_priv(dev);
+
+       return macvlan->lowerdev;
+}
+#else
+static inline struct net_device *
+macvlan_dev_real_dev(const struct net_device *dev)
+{
+       BUG();
+       return NULL;
+}
+#endif
+
 #endif /* _LINUX_IF_MACVLAN_H */
index c9e831d..db43b58 100644 (file)
@@ -11,8 +11,6 @@
 #include <linux/irqnr.h>
 #include <linux/hardirq.h>
 #include <linux/irqflags.h>
-#include <linux/smp.h>
-#include <linux/percpu.h>
 #include <linux/hrtimer.h>
 #include <linux/kref.h>
 #include <linux/workqueue.h>
@@ -392,15 +390,6 @@ extern void __raise_softirq_irqoff(unsigned int nr);
 extern void raise_softirq_irqoff(unsigned int nr);
 extern void raise_softirq(unsigned int nr);
 
-/* This is the worklist that queues up per-cpu softirq work.
- *
- * send_remote_sendirq() adds work to these lists, and
- * the softirq handler itself dequeues from them.  The queues
- * are protected by disabling local cpu interrupts and they must
- * only be accessed by the local cpu that they are for.
- */
-DECLARE_PER_CPU(struct list_head [NR_SOFTIRQS], softirq_work_list);
-
 DECLARE_PER_CPU(struct task_struct *, ksoftirqd);
 
 static inline struct task_struct *this_cpu_ksoftirqd(void)
@@ -408,17 +397,6 @@ static inline struct task_struct *this_cpu_ksoftirqd(void)
        return this_cpu_read(ksoftirqd);
 }
 
-/* Try to send a softirq to a remote cpu.  If this cannot be done, the
- * work will be queued to the local cpu.
- */
-extern void send_remote_softirq(struct call_single_data *cp, int cpu, int softirq);
-
-/* Like send_remote_softirq(), but the caller must disable local cpu interrupts
- * and compute the current cpu, passed in as 'this_cpu'.
- */
-extern void __send_remote_softirq(struct call_single_data *cp, int cpu,
-                                 int this_cpu, int softirq);
-
 /* Tasklets --- multithreaded analogue of BHs.
 
    Main feature differing them of generic softirqs: tasklet
index 7ea319e..a444c79 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/types.h>
+#include <trace/events/iommu.h>
 
 #define IOMMU_READ     (1)
 #define IOMMU_WRITE    (2)
@@ -227,6 +228,7 @@ static inline int report_iommu_fault(struct iommu_domain *domain,
                ret = domain->handler(domain, dev, iova, flags,
                                                domain->handler_token);
 
+       trace_io_page_fault(dev, iova, flags);
        return ret;
 }
 
index 56bb0dc..7dc1003 100644 (file)
@@ -70,6 +70,9 @@ typedef       void (*irq_preflow_handler_t)(struct irq_data *data);
  * IRQ_MOVE_PCNTXT             - Interrupt can be migrated from process context
  * IRQ_NESTED_TRHEAD           - Interrupt nests into another thread
  * IRQ_PER_CPU_DEVID           - Dev_id is a per-cpu variable
+ * IRQ_IS_POLLED               - Always polled by another interrupt. Exclude
+ *                               it from the spurious interrupt detection
+ *                               mechanism and from core side polling.
  */
 enum {
        IRQ_TYPE_NONE           = 0x00000000,
@@ -94,12 +97,14 @@ enum {
        IRQ_NESTED_THREAD       = (1 << 15),
        IRQ_NOTHREAD            = (1 << 16),
        IRQ_PER_CPU_DEVID       = (1 << 17),
+       IRQ_IS_POLLED           = (1 << 18),
 };
 
 #define IRQF_MODIFY_MASK       \
        (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
         IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
-        IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID)
+        IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID | \
+        IRQ_IS_POLLED)
 
 #define IRQ_NO_BALANCING_MASK  (IRQ_PER_CPU | IRQ_NO_BALANCING)
 
index 672ddc4..d4e98d1 100644 (file)
@@ -501,7 +501,6 @@ void tracing_snapshot_alloc(void);
 
 extern void tracing_start(void);
 extern void tracing_stop(void);
-extern void ftrace_off_permanent(void);
 
 static inline __printf(1, 2)
 void ____trace_printk_check_format(const char *fmt, ...)
@@ -639,7 +638,6 @@ extern void ftrace_dump(enum ftrace_dump_mode oops_dump_mode);
 #else
 static inline void tracing_start(void) { }
 static inline void tracing_stop(void) { }
-static inline void ftrace_off_permanent(void) { }
 static inline void trace_dump_stack(int skip) { }
 
 static inline void tracing_on(void) { }
index 518a53a..a74c3a8 100644 (file)
@@ -45,6 +45,7 @@ struct key_preparsed_payload {
        const void      *data;          /* Raw data */
        size_t          datalen;        /* Raw datalen */
        size_t          quotalen;       /* Quota length for proposed payload */
+       bool            trusted;        /* True if key is trusted */
 };
 
 typedef int (*request_key_actor_t)(struct key_construction *key,
@@ -63,6 +64,11 @@ struct key_type {
         */
        size_t def_datalen;
 
+       /* Default key search algorithm. */
+       unsigned def_lookup_type;
+#define KEYRING_SEARCH_LOOKUP_DIRECT   0x0000  /* Direct lookup by description. */
+#define KEYRING_SEARCH_LOOKUP_ITERATE  0x0001  /* Iterative search. */
+
        /* vet a description */
        int (*vet_description)(const char *description);
 
index 4dfde11..80d6774 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/sysctl.h>
 #include <linux/rwsem.h>
 #include <linux/atomic.h>
+#include <linux/assoc_array.h>
 
 #ifdef __KERNEL__
 #include <linux/uidgid.h>
@@ -82,6 +83,12 @@ struct key_owner;
 struct keyring_list;
 struct keyring_name;
 
+struct keyring_index_key {
+       struct key_type         *type;
+       const char              *description;
+       size_t                  desc_len;
+};
+
 /*****************************************************************************/
 /*
  * key reference with possession attribute handling
@@ -99,7 +106,7 @@ struct keyring_name;
 typedef struct __key_reference_with_attributes *key_ref_t;
 
 static inline key_ref_t make_key_ref(const struct key *key,
-                                    unsigned long possession)
+                                    bool possession)
 {
        return (key_ref_t) ((unsigned long) key | possession);
 }
@@ -109,7 +116,7 @@ static inline struct key *key_ref_to_ptr(const key_ref_t key_ref)
        return (struct key *) ((unsigned long) key_ref & ~1UL);
 }
 
-static inline unsigned long is_key_possessed(const key_ref_t key_ref)
+static inline bool is_key_possessed(const key_ref_t key_ref)
 {
        return (unsigned long) key_ref & 1UL;
 }
@@ -129,7 +136,6 @@ struct key {
                struct list_head graveyard_link;
                struct rb_node  serial_node;
        };
-       struct key_type         *type;          /* type of key */
        struct rw_semaphore     sem;            /* change vs change sem */
        struct key_user         *user;          /* owner of this key */
        void                    *security;      /* security data for this key */
@@ -162,13 +168,21 @@ struct key {
 #define KEY_FLAG_NEGATIVE      5       /* set if key is negative */
 #define KEY_FLAG_ROOT_CAN_CLEAR        6       /* set if key can be cleared by root without permission */
 #define KEY_FLAG_INVALIDATED   7       /* set if key has been invalidated */
+#define KEY_FLAG_TRUSTED       8       /* set if key is trusted */
+#define KEY_FLAG_TRUSTED_ONLY  9       /* set if keyring only accepts links to trusted keys */
 
-       /* the description string
-        * - this is used to match a key against search criteria
-        * - this should be a printable string
+       /* the key type and key description string
+        * - the desc is used to match a key against search criteria
+        * - it should be a printable string
         * - eg: for krb5 AFS, this might be "afs@REDHAT.COM"
         */
-       char                    *description;
+       union {
+               struct keyring_index_key index_key;
+               struct {
+                       struct key_type *type;          /* type of key */
+                       char            *description;
+               };
+       };
 
        /* type specific data
         * - this is used by the keyring type to index the name
@@ -185,11 +199,14 @@ struct key {
         *   whatever
         */
        union {
-               unsigned long           value;
-               void __rcu              *rcudata;
-               void                    *data;
-               struct keyring_list __rcu *subscriptions;
-       } payload;
+               union {
+                       unsigned long           value;
+                       void __rcu              *rcudata;
+                       void                    *data;
+                       void                    *data2[2];
+               } payload;
+               struct assoc_array keys;
+       };
 };
 
 extern struct key *key_alloc(struct key_type *type,
@@ -203,18 +220,23 @@ extern struct key *key_alloc(struct key_type *type,
 #define KEY_ALLOC_IN_QUOTA     0x0000  /* add to quota, reject if would overrun */
 #define KEY_ALLOC_QUOTA_OVERRUN        0x0001  /* add to quota, permit even if overrun */
 #define KEY_ALLOC_NOT_IN_QUOTA 0x0002  /* not in quota */
+#define KEY_ALLOC_TRUSTED      0x0004  /* Key should be flagged as trusted */
 
 extern void key_revoke(struct key *key);
 extern void key_invalidate(struct key *key);
 extern void key_put(struct key *key);
 
-static inline struct key *key_get(struct key *key)
+static inline struct key *__key_get(struct key *key)
 {
-       if (key)
-               atomic_inc(&key->usage);
+       atomic_inc(&key->usage);
        return key;
 }
 
+static inline struct key *key_get(struct key *key)
+{
+       return key ? __key_get(key) : key;
+}
+
 static inline void key_ref_put(key_ref_t key_ref)
 {
        key_put(key_ref_to_ptr(key_ref));
index 10308c6..552d51e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * A generic kernel FIFO implementation
  *
- * Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net>
+ * Copyright (C) 2013 Stefani Seibold <stefani@seibold.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -67,9 +67,10 @@ struct __kfifo {
        union { \
                struct __kfifo  kfifo; \
                datatype        *type; \
+               const datatype  *const_type; \
                char            (*rectype)[recsize]; \
                ptrtype         *ptr; \
-               const ptrtype   *ptr_const; \
+               ptrtype const   *ptr_const; \
        }
 
 #define __STRUCT_KFIFO(type, size, recsize, ptrtype) \
@@ -386,16 +387,12 @@ __kfifo_int_must_check_helper( \
 #define        kfifo_put(fifo, val) \
 ({ \
        typeof((fifo) + 1) __tmp = (fifo); \
-       typeof((val) + 1) __val = (val); \
+       typeof(*__tmp->const_type) __val = (val); \
        unsigned int __ret; \
-       const size_t __recsize = sizeof(*__tmp->rectype); \
+       size_t __recsize = sizeof(*__tmp->rectype); \
        struct __kfifo *__kfifo = &__tmp->kfifo; \
-       if (0) { \
-               typeof(__tmp->ptr_const) __dummy __attribute__ ((unused)); \
-               __dummy = (typeof(__val))NULL; \
-       } \
        if (__recsize) \
-               __ret = __kfifo_in_r(__kfifo, __val, sizeof(*__val), \
+               __ret = __kfifo_in_r(__kfifo, &__val, sizeof(__val), \
                        __recsize); \
        else { \
                __ret = !kfifo_is_full(__tmp); \
@@ -404,7 +401,7 @@ __kfifo_int_must_check_helper( \
                        ((typeof(__tmp->type))__kfifo->data) : \
                        (__tmp->buf) \
                        )[__kfifo->in & __tmp->kfifo.mask] = \
-                               *(typeof(__tmp->type))__val; \
+                               (typeof(*__tmp->type))__val; \
                        smp_wmb(); \
                        __kfifo->in++; \
                } \
@@ -415,7 +412,7 @@ __kfifo_int_must_check_helper( \
 /**
  * kfifo_get - get data from the fifo
  * @fifo: address of the fifo to be used
- * @val: the var where to store the data to be added
+ * @val: address where to store the data
  *
  * This macro reads the data from the fifo.
  * It returns 0 if the fifo was empty. Otherwise it returns the number
@@ -428,12 +425,10 @@ __kfifo_int_must_check_helper( \
 __kfifo_uint_must_check_helper( \
 ({ \
        typeof((fifo) + 1) __tmp = (fifo); \
-       typeof((val) + 1) __val = (val); \
+       typeof(__tmp->ptr) __val = (val); \
        unsigned int __ret; \
        const size_t __recsize = sizeof(*__tmp->rectype); \
        struct __kfifo *__kfifo = &__tmp->kfifo; \
-       if (0) \
-               __val = (typeof(__tmp->ptr))0; \
        if (__recsize) \
                __ret = __kfifo_out_r(__kfifo, __val, sizeof(*__val), \
                        __recsize); \
@@ -456,7 +451,7 @@ __kfifo_uint_must_check_helper( \
 /**
  * kfifo_peek - get data from the fifo without removing
  * @fifo: address of the fifo to be used
- * @val: the var where to store the data to be added
+ * @val: address where to store the data
  *
  * This reads the data from the fifo without removing it from the fifo.
  * It returns 0 if the fifo was empty. Otherwise it returns the number
@@ -469,12 +464,10 @@ __kfifo_uint_must_check_helper( \
 __kfifo_uint_must_check_helper( \
 ({ \
        typeof((fifo) + 1) __tmp = (fifo); \
-       typeof((val) + 1) __val = (val); \
+       typeof(__tmp->ptr) __val = (val); \
        unsigned int __ret; \
        const size_t __recsize = sizeof(*__tmp->rectype); \
        struct __kfifo *__kfifo = &__tmp->kfifo; \
-       if (0) \
-               __val = (typeof(__tmp->ptr))NULL; \
        if (__recsize) \
                __ret = __kfifo_out_peek_r(__kfifo, __val, sizeof(*__val), \
                        __recsize); \
@@ -508,14 +501,10 @@ __kfifo_uint_must_check_helper( \
 #define        kfifo_in(fifo, buf, n) \
 ({ \
        typeof((fifo) + 1) __tmp = (fifo); \
-       typeof((buf) + 1) __buf = (buf); \
+       typeof(__tmp->ptr_const) __buf = (buf); \
        unsigned long __n = (n); \
        const size_t __recsize = sizeof(*__tmp->rectype); \
        struct __kfifo *__kfifo = &__tmp->kfifo; \
-       if (0) { \
-               typeof(__tmp->ptr_const) __dummy __attribute__ ((unused)); \
-               __dummy = (typeof(__buf))NULL; \
-       } \
        (__recsize) ?\
        __kfifo_in_r(__kfifo, __buf, __n, __recsize) : \
        __kfifo_in(__kfifo, __buf, __n); \
@@ -561,14 +550,10 @@ __kfifo_uint_must_check_helper( \
 __kfifo_uint_must_check_helper( \
 ({ \
        typeof((fifo) + 1) __tmp = (fifo); \
-       typeof((buf) + 1) __buf = (buf); \
+       typeof(__tmp->ptr) __buf = (buf); \
        unsigned long __n = (n); \
        const size_t __recsize = sizeof(*__tmp->rectype); \
        struct __kfifo *__kfifo = &__tmp->kfifo; \
-       if (0) { \
-               typeof(__tmp->ptr) __dummy = NULL; \
-               __buf = __dummy; \
-       } \
        (__recsize) ?\
        __kfifo_out_r(__kfifo, __buf, __n, __recsize) : \
        __kfifo_out(__kfifo, __buf, __n); \
@@ -773,14 +758,10 @@ __kfifo_uint_must_check_helper( \
 __kfifo_uint_must_check_helper( \
 ({ \
        typeof((fifo) + 1) __tmp = (fifo); \
-       typeof((buf) + 1) __buf = (buf); \
+       typeof(__tmp->ptr) __buf = (buf); \
        unsigned long __n = (n); \
        const size_t __recsize = sizeof(*__tmp->rectype); \
        struct __kfifo *__kfifo = &__tmp->kfifo; \
-       if (0) { \
-               typeof(__tmp->ptr) __dummy __attribute__ ((unused)) = NULL; \
-               __buf = __dummy; \
-       } \
        (__recsize) ? \
        __kfifo_out_peek_r(__kfifo, __buf, __n, __recsize) : \
        __kfifo_out_peek(__kfifo, __buf, __n); \
index 0fbbc7a..9523d2a 100644 (file)
@@ -142,7 +142,7 @@ struct kvm;
 struct kvm_vcpu;
 extern struct kmem_cache *kvm_vcpu_cache;
 
-extern raw_spinlock_t kvm_lock;
+extern spinlock_t kvm_lock;
 extern struct list_head vm_list;
 
 struct kvm_io_range {
@@ -189,8 +189,7 @@ struct kvm_async_pf {
        gva_t gva;
        unsigned long addr;
        struct kvm_arch_async_pf arch;
-       struct page *page;
-       bool done;
+       bool   wakeup_all;
 };
 
 void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu);
@@ -508,9 +507,10 @@ int kvm_set_memory_region(struct kvm *kvm,
                          struct kvm_userspace_memory_region *mem);
 int __kvm_set_memory_region(struct kvm *kvm,
                            struct kvm_userspace_memory_region *mem);
-void kvm_arch_free_memslot(struct kvm_memory_slot *free,
+void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
                           struct kvm_memory_slot *dont);
-int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages);
+int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
+                           unsigned long npages);
 void kvm_arch_memslots_updated(struct kvm *kvm);
 int kvm_arch_prepare_memory_region(struct kvm *kvm,
                                struct kvm_memory_slot *memslot,
@@ -671,6 +671,25 @@ static inline void kvm_arch_free_vm(struct kvm *kvm)
 }
 #endif
 
+#ifdef __KVM_HAVE_ARCH_NONCOHERENT_DMA
+void kvm_arch_register_noncoherent_dma(struct kvm *kvm);
+void kvm_arch_unregister_noncoherent_dma(struct kvm *kvm);
+bool kvm_arch_has_noncoherent_dma(struct kvm *kvm);
+#else
+static inline void kvm_arch_register_noncoherent_dma(struct kvm *kvm)
+{
+}
+
+static inline void kvm_arch_unregister_noncoherent_dma(struct kvm *kvm)
+{
+}
+
+static inline bool kvm_arch_has_noncoherent_dma(struct kvm *kvm)
+{
+       return false;
+}
+#endif
+
 static inline wait_queue_head_t *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu)
 {
 #ifdef __KVM_HAVE_ARCH_WQP
@@ -747,9 +766,6 @@ void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
 int kvm_request_irq_source_id(struct kvm *kvm);
 void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id);
 
-/* For vcpu->arch.iommu_flags */
-#define KVM_IOMMU_CACHE_COHERENCY      0x1
-
 #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
 int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot);
 void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot);
@@ -789,7 +805,7 @@ static inline void kvm_guest_enter(void)
 
        /* KVM does not hold any references to rcu protected data when it
         * switches CPU into a guest mode. In fact switching to a guest mode
-        * is very similar to exiting to userspase from rcu point of view. In
+        * is very similar to exiting to userspace from rcu point of view. In
         * addition CPU may stay in a guest mode for quite a long time (up to
         * one time slice). Lets treat guest mode as quiescent state, just like
         * we do with user-mode execution.
@@ -842,13 +858,6 @@ static inline int memslot_id(struct kvm *kvm, gfn_t gfn)
        return gfn_to_memslot(kvm, gfn)->id;
 }
 
-static inline gfn_t gfn_to_index(gfn_t gfn, gfn_t base_gfn, int level)
-{
-       /* KVM_HPAGE_GFN_SHIFT(PT_PAGE_TABLE_LEVEL) must be 0. */
-       return (gfn >> KVM_HPAGE_GFN_SHIFT(level)) -
-               (base_gfn >> KVM_HPAGE_GFN_SHIFT(level));
-}
-
 static inline gfn_t
 hva_to_gfn_memslot(unsigned long hva, struct kvm_memory_slot *slot)
 {
@@ -1066,6 +1075,7 @@ struct kvm_device *kvm_device_from_filp(struct file *filp);
 
 extern struct kvm_device_ops kvm_mpic_ops;
 extern struct kvm_device_ops kvm_xics_ops;
+extern struct kvm_device_ops kvm_vfio_ops;
 
 #ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT
 
index 8828a78..fbf10a0 100644 (file)
@@ -195,4 +195,6 @@ static inline struct llist_node *llist_del_all(struct llist_head *head)
 
 extern struct llist_node *llist_del_first(struct llist_head *head);
 
+struct llist_node *llist_reverse_order(struct llist_node *head);
+
 #endif /* LLIST_H */
index 13dfd36..c8929c3 100644 (file)
  */
 
 #include <linux/spinlock.h>
+#include <generated/bounds.h>
+
+#define USE_CMPXCHG_LOCKREF \
+       (IS_ENABLED(CONFIG_ARCH_USE_CMPXCHG_LOCKREF) && \
+        IS_ENABLED(CONFIG_SMP) && !BLOATED_SPINLOCKS)
 
 struct lockref {
        union {
-#ifdef CONFIG_CMPXCHG_LOCKREF
+#if USE_CMPXCHG_LOCKREF
                aligned_u64 lock_count;
 #endif
                struct {
index 4706d3d..cb49417 100644 (file)
 #define ARIZONA_FLL2_SYNC_GAIN_MASK              0x003c  /* FLL2_SYNC_GAIN */
 #define ARIZONA_FLL2_SYNC_GAIN_SHIFT                  2  /* FLL2_SYNC_GAIN */
 #define ARIZONA_FLL2_SYNC_GAIN_WIDTH                  4  /* FLL2_SYNC_GAIN */
-#define ARIZONA_FLL2_SYNC_BW_MASK                0x0001  /* FLL2_SYNC_BW */
+#define ARIZONA_FLL2_SYNC_BW                     0x0001  /* FLL2_SYNC_BW */
 #define ARIZONA_FLL2_SYNC_BW_MASK                0x0001  /* FLL2_SYNC_BW */
 #define ARIZONA_FLL2_SYNC_BW_SHIFT                    0  /* FLL2_SYNC_BW */
 #define ARIZONA_FLL2_SYNC_BW_WIDTH                    1  /* FLL2_SYNC_BW */
diff --git a/include/linux/mfd/as3722.h b/include/linux/mfd/as3722.h
new file mode 100644 (file)
index 0000000..16bf8a0
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * as3722 definitions
+ *
+ * Copyright (C) 2013 ams
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * Author: Florian Lobmaier <florian.lobmaier@ams.com>
+ * Author: Laxman Dewangan <ldewangan@nvidia.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_AS3722_H__
+#define __LINUX_MFD_AS3722_H__
+
+#include <linux/regmap.h>
+
+/* AS3722 registers */
+#define AS3722_SD0_VOLTAGE_REG                         0x00
+#define AS3722_SD1_VOLTAGE_REG                         0x01
+#define AS3722_SD2_VOLTAGE_REG                         0x02
+#define AS3722_SD3_VOLTAGE_REG                         0x03
+#define AS3722_SD4_VOLTAGE_REG                         0x04
+#define AS3722_SD5_VOLTAGE_REG                         0x05
+#define AS3722_SD6_VOLTAGE_REG                         0x06
+#define AS3722_GPIO0_CONTROL_REG                       0x08
+#define AS3722_GPIO1_CONTROL_REG                       0x09
+#define AS3722_GPIO2_CONTROL_REG                       0x0A
+#define AS3722_GPIO3_CONTROL_REG                       0x0B
+#define AS3722_GPIO4_CONTROL_REG                       0x0C
+#define AS3722_GPIO5_CONTROL_REG                       0x0D
+#define AS3722_GPIO6_CONTROL_REG                       0x0E
+#define AS3722_GPIO7_CONTROL_REG                       0x0F
+#define AS3722_LDO0_VOLTAGE_REG                                0x10
+#define AS3722_LDO1_VOLTAGE_REG                                0x11
+#define AS3722_LDO2_VOLTAGE_REG                                0x12
+#define AS3722_LDO3_VOLTAGE_REG                                0x13
+#define AS3722_LDO4_VOLTAGE_REG                                0x14
+#define AS3722_LDO5_VOLTAGE_REG                                0x15
+#define AS3722_LDO6_VOLTAGE_REG                                0x16
+#define AS3722_LDO7_VOLTAGE_REG                                0x17
+#define AS3722_LDO9_VOLTAGE_REG                                0x19
+#define AS3722_LDO10_VOLTAGE_REG                       0x1A
+#define AS3722_LDO11_VOLTAGE_REG                       0x1B
+#define AS3722_GPIO_DEB1_REG                           0x1E
+#define AS3722_GPIO_DEB2_REG                           0x1F
+#define AS3722_GPIO_SIGNAL_OUT_REG                     0x20
+#define AS3722_GPIO_SIGNAL_IN_REG                      0x21
+#define AS3722_REG_SEQU_MOD1_REG                       0x22
+#define AS3722_REG_SEQU_MOD2_REG                       0x23
+#define AS3722_REG_SEQU_MOD3_REG                       0x24
+#define AS3722_SD_PHSW_CTRL_REG                                0x27
+#define AS3722_SD_PHSW_STATUS                          0x28
+#define AS3722_SD0_CONTROL_REG                         0x29
+#define AS3722_SD1_CONTROL_REG                         0x2A
+#define AS3722_SDmph_CONTROL_REG                       0x2B
+#define AS3722_SD23_CONTROL_REG                                0x2C
+#define AS3722_SD4_CONTROL_REG                         0x2D
+#define AS3722_SD5_CONTROL_REG                         0x2E
+#define AS3722_SD6_CONTROL_REG                         0x2F
+#define AS3722_SD_DVM_REG                              0x30
+#define AS3722_RESET_REASON_REG                                0x31
+#define AS3722_BATTERY_VOLTAGE_MONITOR_REG             0x32
+#define AS3722_STARTUP_CONTROL_REG                     0x33
+#define AS3722_RESET_TIMER_REG                         0x34
+#define AS3722_REFERENCE_CONTROL_REG                   0x35
+#define AS3722_RESET_CONTROL_REG                       0x36
+#define AS3722_OVER_TEMP_CONTROL_REG                   0x37
+#define AS3722_WATCHDOG_CONTROL_REG                    0x38
+#define AS3722_REG_STANDBY_MOD1_REG                    0x39
+#define AS3722_REG_STANDBY_MOD2_REG                    0x3A
+#define AS3722_REG_STANDBY_MOD3_REG                    0x3B
+#define AS3722_ENABLE_CTRL1_REG                                0x3C
+#define AS3722_ENABLE_CTRL2_REG                                0x3D
+#define AS3722_ENABLE_CTRL3_REG                                0x3E
+#define AS3722_ENABLE_CTRL4_REG                                0x3F
+#define AS3722_ENABLE_CTRL5_REG                                0x40
+#define AS3722_PWM_CONTROL_L_REG                       0x41
+#define AS3722_PWM_CONTROL_H_REG                       0x42
+#define AS3722_WATCHDOG_TIMER_REG                      0x46
+#define AS3722_WATCHDOG_SOFTWARE_SIGNAL_REG            0x48
+#define AS3722_IOVOLTAGE_REG                           0x49
+#define AS3722_BATTERY_VOLTAGE_MONITOR2_REG            0x4A
+#define AS3722_SD_CONTROL_REG                          0x4D
+#define AS3722_LDOCONTROL0_REG                         0x4E
+#define AS3722_LDOCONTROL1_REG                         0x4F
+#define AS3722_SD0_PROTECT_REG                         0x50
+#define AS3722_SD6_PROTECT_REG                         0x51
+#define AS3722_PWM_VCONTROL1_REG                       0x52
+#define AS3722_PWM_VCONTROL2_REG                       0x53
+#define AS3722_PWM_VCONTROL3_REG                       0x54
+#define AS3722_PWM_VCONTROL4_REG                       0x55
+#define AS3722_BB_CHARGER_REG                          0x57
+#define AS3722_CTRL_SEQU1_REG                          0x58
+#define AS3722_CTRL_SEQU2_REG                          0x59
+#define AS3722_OVCURRENT_REG                           0x5A
+#define AS3722_OVCURRENT_DEB_REG                       0x5B
+#define AS3722_SDLV_DEB_REG                            0x5C
+#define AS3722_OC_PG_CTRL_REG                          0x5D
+#define AS3722_OC_PG_CTRL2_REG                         0x5E
+#define AS3722_CTRL_STATUS                             0x5F
+#define AS3722_RTC_CONTROL_REG                         0x60
+#define AS3722_RTC_SECOND_REG                          0x61
+#define AS3722_RTC_MINUTE_REG                          0x62
+#define AS3722_RTC_HOUR_REG                            0x63
+#define AS3722_RTC_DAY_REG                             0x64
+#define AS3722_RTC_MONTH_REG                           0x65
+#define AS3722_RTC_YEAR_REG                            0x66
+#define AS3722_RTC_ALARM_SECOND_REG                    0x67
+#define AS3722_RTC_ALARM_MINUTE_REG                    0x68
+#define AS3722_RTC_ALARM_HOUR_REG                      0x69
+#define AS3722_RTC_ALARM_DAY_REG                       0x6A
+#define AS3722_RTC_ALARM_MONTH_REG                     0x6B
+#define AS3722_RTC_ALARM_YEAR_REG                      0x6C
+#define AS3722_SRAM_REG                                        0x6D
+#define AS3722_RTC_ACCESS_REG                          0x6F
+#define AS3722_RTC_STATUS_REG                          0x73
+#define AS3722_INTERRUPT_MASK1_REG                     0x74
+#define AS3722_INTERRUPT_MASK2_REG                     0x75
+#define AS3722_INTERRUPT_MASK3_REG                     0x76
+#define AS3722_INTERRUPT_MASK4_REG                     0x77
+#define AS3722_INTERRUPT_STATUS1_REG                   0x78
+#define AS3722_INTERRUPT_STATUS2_REG                   0x79
+#define AS3722_INTERRUPT_STATUS3_REG                   0x7A
+#define AS3722_INTERRUPT_STATUS4_REG                   0x7B
+#define AS3722_TEMP_STATUS_REG                         0x7D
+#define AS3722_ADC0_CONTROL_REG                                0x80
+#define AS3722_ADC1_CONTROL_REG                                0x81
+#define AS3722_ADC0_MSB_RESULT_REG                     0x82
+#define AS3722_ADC0_LSB_RESULT_REG                     0x83
+#define AS3722_ADC1_MSB_RESULT_REG                     0x84
+#define AS3722_ADC1_LSB_RESULT_REG                     0x85
+#define AS3722_ADC1_THRESHOLD_HI_MSB_REG               0x86
+#define AS3722_ADC1_THRESHOLD_HI_LSB_REG               0x87
+#define AS3722_ADC1_THRESHOLD_LO_MSB_REG               0x88
+#define AS3722_ADC1_THRESHOLD_LO_LSB_REG               0x89
+#define AS3722_ADC_CONFIGURATION_REG                   0x8A
+#define AS3722_ASIC_ID1_REG                            0x90
+#define AS3722_ASIC_ID2_REG                            0x91
+#define AS3722_LOCK_REG                                        0x9E
+#define AS3722_MAX_REGISTER                            0xF4
+
+#define AS3722_SD0_EXT_ENABLE_MASK                     0x03
+#define AS3722_SD1_EXT_ENABLE_MASK                     0x0C
+#define AS3722_SD2_EXT_ENABLE_MASK                     0x30
+#define AS3722_SD3_EXT_ENABLE_MASK                     0xC0
+#define AS3722_SD4_EXT_ENABLE_MASK                     0x03
+#define AS3722_SD5_EXT_ENABLE_MASK                     0x0C
+#define AS3722_SD6_EXT_ENABLE_MASK                     0x30
+#define AS3722_LDO0_EXT_ENABLE_MASK                    0x03
+#define AS3722_LDO1_EXT_ENABLE_MASK                    0x0C
+#define AS3722_LDO2_EXT_ENABLE_MASK                    0x30
+#define AS3722_LDO3_EXT_ENABLE_MASK                    0xC0
+#define AS3722_LDO4_EXT_ENABLE_MASK                    0x03
+#define AS3722_LDO5_EXT_ENABLE_MASK                    0x0C
+#define AS3722_LDO6_EXT_ENABLE_MASK                    0x30
+#define AS3722_LDO7_EXT_ENABLE_MASK                    0xC0
+#define AS3722_LDO9_EXT_ENABLE_MASK                    0x0C
+#define AS3722_LDO10_EXT_ENABLE_MASK                   0x30
+#define AS3722_LDO11_EXT_ENABLE_MASK                   0xC0
+
+#define AS3722_OVCURRENT_SD0_ALARM_MASK                        0x07
+#define AS3722_OVCURRENT_SD0_ALARM_SHIFT               0x01
+#define AS3722_OVCURRENT_SD0_TRIP_MASK                 0x18
+#define AS3722_OVCURRENT_SD0_TRIP_SHIFT                        0x03
+#define AS3722_OVCURRENT_SD1_TRIP_MASK                 0x60
+#define AS3722_OVCURRENT_SD1_TRIP_SHIFT                        0x05
+
+#define AS3722_OVCURRENT_SD6_ALARM_MASK                        0x07
+#define AS3722_OVCURRENT_SD6_ALARM_SHIFT               0x01
+#define AS3722_OVCURRENT_SD6_TRIP_MASK                 0x18
+#define AS3722_OVCURRENT_SD6_TRIP_SHIFT                        0x03
+
+/* AS3722 register bits and bit masks */
+#define AS3722_LDO_ILIMIT_MASK                         BIT(7)
+#define AS3722_LDO_ILIMIT_BIT                          BIT(7)
+#define AS3722_LDO0_VSEL_MASK                          0x1F
+#define AS3722_LDO0_VSEL_MIN                           0x01
+#define AS3722_LDO0_VSEL_MAX                           0x12
+#define AS3722_LDO0_NUM_VOLT                           0x12
+#define AS3722_LDO3_VSEL_MASK                          0x3F
+#define AS3722_LDO3_VSEL_MIN                           0x01
+#define AS3722_LDO3_VSEL_MAX                           0x2D
+#define AS3722_LDO3_NUM_VOLT                           0x2D
+#define AS3722_LDO_VSEL_MASK                           0x7F
+#define AS3722_LDO_VSEL_MIN                            0x01
+#define AS3722_LDO_VSEL_MAX                            0x7F
+#define AS3722_LDO_VSEL_DNU_MIN                                0x25
+#define AS3722_LDO_VSEL_DNU_MAX                                0x3F
+#define AS3722_LDO_NUM_VOLT                            0x80
+
+#define AS3722_LDO0_CTRL                               BIT(0)
+#define AS3722_LDO1_CTRL                               BIT(1)
+#define AS3722_LDO2_CTRL                               BIT(2)
+#define AS3722_LDO3_CTRL                               BIT(3)
+#define AS3722_LDO4_CTRL                               BIT(4)
+#define AS3722_LDO5_CTRL                               BIT(5)
+#define AS3722_LDO6_CTRL                               BIT(6)
+#define AS3722_LDO7_CTRL                               BIT(7)
+#define AS3722_LDO9_CTRL                               BIT(1)
+#define AS3722_LDO10_CTRL                              BIT(2)
+#define AS3722_LDO11_CTRL                              BIT(3)
+
+#define AS3722_LDO3_MODE_MASK                          (3 << 6)
+#define AS3722_LDO3_MODE_VAL(n)                                (((n) & 0x3) << 6)
+#define AS3722_LDO3_MODE_PMOS                          AS3722_LDO3_MODE_VAL(0)
+#define AS3722_LDO3_MODE_PMOS_TRACKING                 AS3722_LDO3_MODE_VAL(1)
+#define AS3722_LDO3_MODE_NMOS                          AS3722_LDO3_MODE_VAL(2)
+#define AS3722_LDO3_MODE_SWITCH                                AS3722_LDO3_MODE_VAL(3)
+
+#define AS3722_SD_VSEL_MASK                            0x7F
+#define AS3722_SD0_VSEL_MIN                            0x01
+#define AS3722_SD0_VSEL_MAX                            0x5A
+#define AS3722_SD2_VSEL_MIN                            0x01
+#define AS3722_SD2_VSEL_MAX                            0x7F
+
+#define AS3722_SDn_CTRL(n)                             BIT(n)
+
+#define AS3722_SD0_MODE_FAST                           BIT(4)
+#define AS3722_SD1_MODE_FAST                           BIT(4)
+#define AS3722_SD2_MODE_FAST                           BIT(2)
+#define AS3722_SD3_MODE_FAST                           BIT(6)
+#define AS3722_SD4_MODE_FAST                           BIT(2)
+#define AS3722_SD5_MODE_FAST                           BIT(2)
+#define AS3722_SD6_MODE_FAST                           BIT(4)
+
+#define AS3722_POWER_OFF                               BIT(1)
+
+#define AS3722_INTERRUPT_MASK1_LID                     BIT(0)
+#define AS3722_INTERRUPT_MASK1_ACOK                    BIT(1)
+#define AS3722_INTERRUPT_MASK1_ENABLE1                 BIT(2)
+#define AS3722_INTERRUPT_MASK1_OCURR_ALARM_SD0         BIT(3)
+#define AS3722_INTERRUPT_MASK1_ONKEY_LONG              BIT(4)
+#define AS3722_INTERRUPT_MASK1_ONKEY                   BIT(5)
+#define AS3722_INTERRUPT_MASK1_OVTMP                   BIT(6)
+#define AS3722_INTERRUPT_MASK1_LOWBAT                  BIT(7)
+
+#define AS3722_INTERRUPT_MASK2_SD0_LV                  BIT(0)
+#define AS3722_INTERRUPT_MASK2_SD1_LV                  BIT(1)
+#define AS3722_INTERRUPT_MASK2_SD2345_LV               BIT(2)
+#define AS3722_INTERRUPT_MASK2_PWM1_OV_PROT            BIT(3)
+#define AS3722_INTERRUPT_MASK2_PWM2_OV_PROT            BIT(4)
+#define AS3722_INTERRUPT_MASK2_ENABLE2                 BIT(5)
+#define AS3722_INTERRUPT_MASK2_SD6_LV                  BIT(6)
+#define AS3722_INTERRUPT_MASK2_RTC_REP                 BIT(7)
+
+#define AS3722_INTERRUPT_MASK3_RTC_ALARM               BIT(0)
+#define AS3722_INTERRUPT_MASK3_GPIO1                   BIT(1)
+#define AS3722_INTERRUPT_MASK3_GPIO2                   BIT(2)
+#define AS3722_INTERRUPT_MASK3_GPIO3                   BIT(3)
+#define AS3722_INTERRUPT_MASK3_GPIO4                   BIT(4)
+#define AS3722_INTERRUPT_MASK3_GPIO5                   BIT(5)
+#define AS3722_INTERRUPT_MASK3_WATCHDOG                        BIT(6)
+#define AS3722_INTERRUPT_MASK3_ENABLE3                 BIT(7)
+
+#define AS3722_INTERRUPT_MASK4_TEMP_SD0_SHUTDOWN       BIT(0)
+#define AS3722_INTERRUPT_MASK4_TEMP_SD1_SHUTDOWN       BIT(1)
+#define AS3722_INTERRUPT_MASK4_TEMP_SD6_SHUTDOWN       BIT(2)
+#define AS3722_INTERRUPT_MASK4_TEMP_SD0_ALARM          BIT(3)
+#define AS3722_INTERRUPT_MASK4_TEMP_SD1_ALARM          BIT(4)
+#define AS3722_INTERRUPT_MASK4_TEMP_SD6_ALARM          BIT(5)
+#define AS3722_INTERRUPT_MASK4_OCCUR_ALARM_SD6         BIT(6)
+#define AS3722_INTERRUPT_MASK4_ADC                     BIT(7)
+
+#define AS3722_ADC1_INTERVAL_TIME                      BIT(0)
+#define AS3722_ADC1_INT_MODE_ON                                BIT(1)
+#define AS3722_ADC_BUF_ON                              BIT(2)
+#define AS3722_ADC1_LOW_VOLTAGE_RANGE                  BIT(5)
+#define AS3722_ADC1_INTEVAL_SCAN                       BIT(6)
+#define AS3722_ADC1_INT_MASK                           BIT(7)
+
+#define AS3722_ADC_MSB_VAL_MASK                                0x7F
+#define AS3722_ADC_LSB_VAL_MASK                                0x07
+
+#define AS3722_ADC0_CONV_START                         BIT(7)
+#define AS3722_ADC0_CONV_NOTREADY                      BIT(7)
+#define AS3722_ADC0_SOURCE_SELECT_MASK                 0x1F
+
+#define AS3722_ADC1_CONV_START                         BIT(7)
+#define AS3722_ADC1_CONV_NOTREADY                      BIT(7)
+#define AS3722_ADC1_SOURCE_SELECT_MASK                 0x1F
+
+/* GPIO modes */
+#define AS3722_GPIO_MODE_MASK                          0x07
+#define AS3722_GPIO_MODE_INPUT                         0x00
+#define AS3722_GPIO_MODE_OUTPUT_VDDH                   0x01
+#define AS3722_GPIO_MODE_IO_OPEN_DRAIN                 0x02
+#define AS3722_GPIO_MODE_ADC_IN                                0x03
+#define AS3722_GPIO_MODE_INPUT_PULL_UP                 0x04
+#define AS3722_GPIO_MODE_INPUT_PULL_DOWN               0x05
+#define AS3722_GPIO_MODE_IO_OPEN_DRAIN_PULL_UP         0x06
+#define AS3722_GPIO_MODE_OUTPUT_VDDL                   0x07
+#define AS3722_GPIO_MODE_VAL(n)                        ((n) & AS3722_GPIO_MODE_MASK)
+
+#define AS3722_GPIO_INV                                        BIT(7)
+#define AS3722_GPIO_IOSF_MASK                          0x78
+#define AS3722_GPIO_IOSF_VAL(n)                                (((n) & 0xF) << 3)
+#define AS3722_GPIO_IOSF_NORMAL                                AS3722_GPIO_IOSF_VAL(0)
+#define AS3722_GPIO_IOSF_INTERRUPT_OUT                 AS3722_GPIO_IOSF_VAL(1)
+#define AS3722_GPIO_IOSF_VSUP_LOW_OUT                  AS3722_GPIO_IOSF_VAL(2)
+#define AS3722_GPIO_IOSF_GPIO_INTERRUPT_IN             AS3722_GPIO_IOSF_VAL(3)
+#define AS3722_GPIO_IOSF_ISINK_PWM_IN                  AS3722_GPIO_IOSF_VAL(4)
+#define AS3722_GPIO_IOSF_VOLTAGE_STBY                  AS3722_GPIO_IOSF_VAL(5)
+#define AS3722_GPIO_IOSF_PWR_GOOD_OUT                  AS3722_GPIO_IOSF_VAL(7)
+#define AS3722_GPIO_IOSF_Q32K_OUT                      AS3722_GPIO_IOSF_VAL(8)
+#define AS3722_GPIO_IOSF_WATCHDOG_IN                   AS3722_GPIO_IOSF_VAL(9)
+#define AS3722_GPIO_IOSF_SOFT_RESET_IN                 AS3722_GPIO_IOSF_VAL(11)
+#define AS3722_GPIO_IOSF_PWM_OUT                       AS3722_GPIO_IOSF_VAL(12)
+#define AS3722_GPIO_IOSF_VSUP_LOW_DEB_OUT              AS3722_GPIO_IOSF_VAL(13)
+#define AS3722_GPIO_IOSF_SD6_LOW_VOLT_LOW              AS3722_GPIO_IOSF_VAL(14)
+
+#define AS3722_GPIOn_SIGNAL(n)                         BIT(n)
+#define AS3722_GPIOn_CONTROL_REG(n)            (AS3722_GPIO0_CONTROL_REG + n)
+#define AS3722_I2C_PULL_UP                             BIT(4)
+#define AS3722_INT_PULL_UP                             BIT(5)
+
+#define AS3722_RTC_REP_WAKEUP_EN                       BIT(0)
+#define AS3722_RTC_ALARM_WAKEUP_EN                     BIT(1)
+#define AS3722_RTC_ON                                  BIT(2)
+#define AS3722_RTC_IRQMODE                             BIT(3)
+#define AS3722_RTC_CLK32K_OUT_EN                       BIT(5)
+
+#define AS3722_WATCHDOG_TIMER_MAX                      0x7F
+#define AS3722_WATCHDOG_ON                             BIT(0)
+#define AS3722_WATCHDOG_SW_SIG                         BIT(0)
+
+#define AS3722_EXT_CONTROL_ENABLE1                     0x1
+#define AS3722_EXT_CONTROL_ENABLE2                     0x2
+#define AS3722_EXT_CONTROL_ENABLE3                     0x3
+
+/* Interrupt IDs */
+enum as3722_irq {
+       AS3722_IRQ_LID,
+       AS3722_IRQ_ACOK,
+       AS3722_IRQ_ENABLE1,
+       AS3722_IRQ_OCCUR_ALARM_SD0,
+       AS3722_IRQ_ONKEY_LONG_PRESS,
+       AS3722_IRQ_ONKEY,
+       AS3722_IRQ_OVTMP,
+       AS3722_IRQ_LOWBAT,
+       AS3722_IRQ_SD0_LV,
+       AS3722_IRQ_SD1_LV,
+       AS3722_IRQ_SD2_LV,
+       AS3722_IRQ_PWM1_OV_PROT,
+       AS3722_IRQ_PWM2_OV_PROT,
+       AS3722_IRQ_ENABLE2,
+       AS3722_IRQ_SD6_LV,
+       AS3722_IRQ_RTC_REP,
+       AS3722_IRQ_RTC_ALARM,
+       AS3722_IRQ_GPIO1,
+       AS3722_IRQ_GPIO2,
+       AS3722_IRQ_GPIO3,
+       AS3722_IRQ_GPIO4,
+       AS3722_IRQ_GPIO5,
+       AS3722_IRQ_WATCHDOG,
+       AS3722_IRQ_ENABLE3,
+       AS3722_IRQ_TEMP_SD0_SHUTDOWN,
+       AS3722_IRQ_TEMP_SD1_SHUTDOWN,
+       AS3722_IRQ_TEMP_SD2_SHUTDOWN,
+       AS3722_IRQ_TEMP_SD0_ALARM,
+       AS3722_IRQ_TEMP_SD1_ALARM,
+       AS3722_IRQ_TEMP_SD6_ALARM,
+       AS3722_IRQ_OCCUR_ALARM_SD6,
+       AS3722_IRQ_ADC,
+       AS3722_IRQ_MAX,
+};
+
+struct as3722 {
+       struct device *dev;
+       struct regmap *regmap;
+       int chip_irq;
+       unsigned long irq_flags;
+       bool en_intern_int_pullup;
+       bool en_intern_i2c_pullup;
+       struct regmap_irq_chip_data *irq_data;
+};
+
+static inline int as3722_read(struct as3722 *as3722, u32 reg, u32 *dest)
+{
+       return regmap_read(as3722->regmap, reg, dest);
+}
+
+static inline int as3722_write(struct as3722 *as3722, u32 reg, u32 value)
+{
+       return regmap_write(as3722->regmap, reg, value);
+}
+
+static inline int as3722_block_read(struct as3722 *as3722, u32 reg,
+               int count, u8 *buf)
+{
+       return regmap_bulk_read(as3722->regmap, reg, buf, count);
+}
+
+static inline int as3722_block_write(struct as3722 *as3722, u32 reg,
+               int count, u8 *data)
+{
+       return regmap_bulk_write(as3722->regmap, reg, data, count);
+}
+
+static inline int as3722_update_bits(struct as3722 *as3722, u32 reg,
+               u32 mask, u8 val)
+{
+       return regmap_update_bits(as3722->regmap, reg, mask, val);
+}
+
+static inline int as3722_irq_get_virq(struct as3722 *as3722, int irq)
+{
+       return regmap_irq_get_virq(as3722->irq_data, irq);
+}
+#endif /* __LINUX_MFD_AS3722_H__ */
index 7314fc4..bdba8c6 100644 (file)
@@ -104,7 +104,7 @@ static inline const struct mfd_cell *mfd_get_cell(struct platform_device *pdev)
 }
 
 extern int mfd_add_devices(struct device *parent, int id,
-                          struct mfd_cell *cells, int n_devs,
+                          const struct mfd_cell *cells, int n_devs,
                           struct resource *mem_base,
                           int irq_base, struct irq_domain *irq_domain);
 
index 786d02e..21e21b8 100644 (file)
@@ -148,10 +148,15 @@ static inline int da9052_group_read(struct da9052 *da9052, unsigned char reg,
                                     unsigned reg_cnt, unsigned char *val)
 {
        int ret;
+       unsigned int tmp;
+       int i;
 
-       ret = regmap_bulk_read(da9052->regmap, reg, val, reg_cnt);
-       if (ret < 0)
-               return ret;
+       for (i = 0; i < reg_cnt; i++) {
+               ret = regmap_read(da9052->regmap, reg + i, &tmp);
+               val[i] = (unsigned char)tmp;
+               if (ret < 0)
+                       return ret;
+       }
 
        if (da9052->fix_io) {
                ret = da9052->fix_io(da9052, reg);
@@ -166,10 +171,13 @@ static inline int da9052_group_write(struct da9052 *da9052, unsigned char reg,
                                      unsigned reg_cnt, unsigned char *val)
 {
        int ret;
+       int i;
 
-       ret = regmap_raw_write(da9052->regmap, reg, val, reg_cnt);
-       if (ret < 0)
-               return ret;
+       for (i = 0; i < reg_cnt; i++) {
+               ret = regmap_write(da9052->regmap, reg + i, val[i]);
+               if (ret < 0)
+                       return ret;
+       }
 
        if (da9052->fix_io) {
                ret = da9052->fix_io(da9052, reg);
index 244fb0d..3e050b9 100644 (file)
@@ -323,7 +323,6 @@ struct max77693_dev {
 
        int irq;
        int irq_gpio;
-       bool wakeup;
        struct mutex irqlock;
        int irq_masks_cur[MAX77693_IRQ_GROUP_NR];
        int irq_masks_cache[MAX77693_IRQ_GROUP_NR];
index 676f0f3..3f3dc45 100644 (file)
@@ -64,8 +64,6 @@ struct max77693_muic_platform_data {
 };
 
 struct max77693_platform_data {
-       int wakeup;
-
        /* regulator data */
        struct max77693_regulator_data *regulators;
        int num_regulators;
index d1382df..0ce7721 100644 (file)
 #define PCR_SETTING_REG2               0x814
 #define PCR_SETTING_REG3               0x747
 
+/* Phy bits */
+#define PHY_PCR_FORCE_CODE                     0xB000
+#define PHY_PCR_OOBS_CALI_50                   0x0800
+#define PHY_PCR_OOBS_VCM_08                    0x0200
+#define PHY_PCR_OOBS_SEN_90                    0x0040
+#define PHY_PCR_RSSI_EN                                0x0002
+
+#define PHY_RCR1_ADP_TIME                      0x0100
+#define PHY_RCR1_VCO_COARSE                    0x001F
+
+#define PHY_RCR2_EMPHASE_EN                    0x8000
+#define PHY_RCR2_NADJR                         0x4000
+#define PHY_RCR2_CDR_CP_10                     0x0400
+#define PHY_RCR2_CDR_SR_2                      0x0100
+#define PHY_RCR2_FREQSEL_12                    0x0040
+#define PHY_RCR2_CPADJEN                       0x0020
+#define PHY_RCR2_CDR_SC_8                      0x0008
+#define PHY_RCR2_CALIB_LATE                    0x0002
+
+#define PHY_RDR_RXDSEL_1_9                     0x4000
+
+#define PHY_TUNE_TUNEREF_1_0                   0x4000
+#define PHY_TUNE_VBGSEL_1252                   0x0C00
+#define PHY_TUNE_SDBUS_33                      0x0200
+#define PHY_TUNE_TUNED18                       0x01C0
+#define PHY_TUNE_TUNED12                       0X0020
+
+#define PHY_BPCR_IBRXSEL                       0x0400
+#define PHY_BPCR_IBTXSEL                       0x0100
+#define PHY_BPCR_IB_FILTER                     0x0080
+#define PHY_BPCR_CMIRROR_EN                    0x0040
+
+#define PHY_REG_REV_RESV                       0xE000
+#define PHY_REG_REV_RXIDLE_LATCHED             0x1000
+#define PHY_REG_REV_P1_EN                      0x0800
+#define PHY_REG_REV_RXIDLE_EN                  0x0400
+#define PHY_REG_REV_CLKREQ_DLY_TIMER_1_0       0x0040
+#define PHY_REG_REV_STOP_CLKRD                 0x0020
+#define PHY_REG_REV_RX_PWST                    0x0008
+#define PHY_REG_REV_STOP_CLKWR                 0x0004
+
+#define PHY_FLD3_TIMER_4                       0x7800
+#define PHY_FLD3_TIMER_6                       0x00E0
+#define PHY_FLD3_RXDELINK                      0x0004
+
+#define PHY_FLD4_FLDEN_SEL                     0x4000
+#define PHY_FLD4_REQ_REF                       0x2000
+#define PHY_FLD4_RXAMP_OFF                     0x1000
+#define PHY_FLD4_REQ_ADDA                      0x0800
+#define PHY_FLD4_BER_COUNT                     0x00E0
+#define PHY_FLD4_BER_TIMER                     0x000A
+#define PHY_FLD4_BER_CHK_EN                    0x0001
+
 #define rtsx_pci_init_cmd(pcr)         ((pcr)->ci = 0)
 
 struct rtsx_pcr;
index ba89b94..674b45d 100644 (file)
@@ -316,7 +316,7 @@ enum si476x_smoothmetrics {
  * response to 'FM_RD_STATUS' command
  * @rdstpptyint: Traffic program flag(TP) and/or program type(PTY)
  * code has changed.
- * @rdspiint: Program indentifiaction(PI) code has changed.
+ * @rdspiint: Program identification(PI) code has changed.
  * @rdssyncint: RDS synchronization has changed.
  * @rdsfifoint: RDS was received and the RDS FIFO has at least
  * 'FM_RDS_INTERRUPT_FIFO_COUNT' elements in it.
diff --git a/include/linux/mfd/stw481x.h b/include/linux/mfd/stw481x.h
new file mode 100644 (file)
index 0000000..eda1215
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#ifndef MFD_STW481X_H
+#define MFD_STW481X_H
+
+#include <linux/i2c.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+
+/* These registers are accessed from more than one driver */
+#define STW_CONF1                      0x11U
+#define STW_CONF1_PDN_VMMC             0x01U
+#define STW_CONF1_VMMC_MASK            0x0eU
+#define STW_CONF1_VMMC_1_8V            0x02U
+#define STW_CONF1_VMMC_2_85V           0x04U
+#define STW_CONF1_VMMC_3V              0x06U
+#define STW_CONF1_VMMC_1_85V           0x08U
+#define STW_CONF1_VMMC_2_6V            0x0aU
+#define STW_CONF1_VMMC_2_7V            0x0cU
+#define STW_CONF1_VMMC_3_3V            0x0eU
+#define STW_CONF1_MMC_LS_STATUS                0x10U
+#define STW_PCTL_REG_LO                        0x1eU
+#define STW_PCTL_REG_HI                        0x1fU
+#define STW_CONF1_V_MONITORING         0x20U
+#define STW_CONF1_IT_WARN              0x40U
+#define STW_CONF1_PDN_VAUX             0x80U
+#define STW_CONF2                      0x20U
+#define STW_CONF2_MASK_TWARN           0x01U
+#define STW_CONF2_VMMC_EXT             0x02U
+#define STW_CONF2_MASK_IT_WAKE_UP      0x04U
+#define STW_CONF2_GPO1                 0x08U
+#define STW_CONF2_GPO2                 0x10U
+#define STW_VCORE_SLEEP                        0x21U
+
+/**
+ * struct stw481x - state holder for the Stw481x drivers
+ * @mutex: mutex to serialize I2C accesses
+ * @i2c_client: corresponding I2C client
+ * @regulator: regulator device for regulator children
+ * @map: regmap handle to access device registers
+ */
+struct stw481x {
+       struct mutex            lock;
+       struct i2c_client       *client;
+       struct regulator_dev    *vmmc_regulator;
+       struct regmap           *map;
+};
+
+#endif
index b473577..8789fa3 100644 (file)
 
 struct device_node;
 
+#ifdef CONFIG_MFD_SYSCON
 extern struct regmap *syscon_node_to_regmap(struct device_node *np);
 extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s);
 extern struct regmap *syscon_regmap_lookup_by_pdevname(const char *s);
 extern struct regmap *syscon_regmap_lookup_by_phandle(
                                        struct device_node *np,
                                        const char *property);
+#else
+static inline struct regmap *syscon_node_to_regmap(struct device_node *np)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct regmap *syscon_regmap_lookup_by_pdevname(const char *s)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct regmap *syscon_regmap_lookup_by_phandle(
+                                       struct device_node *np,
+                                       const char *property)
+{
+       return ERR_PTR(-ENOSYS);
+}
+#endif
+
 #endif /* __LINUX_MFD_SYSCON_H__ */
index 08cce7f..d498d98 100644 (file)
 #define FIFO1_THRESHOLD                19
 
 /*
-* ADC runs at 3MHz, and it takes
-* 15 cycles to latch one data output.
-* Hence the idle time for ADC to
-* process one sample data would be
-* around 5 micro seconds.
-*/
-#define IDLE_TIMEOUT 5 /* microsec */
+ * time in us for processing a single channel, calculated as follows:
+ *
+ * num cycles = open delay + (sample delay + conv time) * averaging
+ *
+ * num cycles: 152 + (1 + 13) * 16 = 376
+ *
+ * clock frequency: 26MHz / 8 = 3.25MHz
+ * clock period: 1 / 3.25MHz = 308ns
+ *
+ * processing time: 376 * 308ns = 116us
+ */
+#define IDLE_TIMEOUT 116 /* microsec */
 
 #define TSCADC_CELLS           2
 
@@ -155,6 +160,7 @@ struct ti_tscadc_dev {
        struct mfd_cell cells[TSCADC_CELLS];
        u32 reg_se_cache;
        spinlock_t reg_lock;
+       unsigned int clk_div;
 
        /* tsc device */
        struct titsc *tsc;
index 40854ac..eefafa6 100644 (file)
@@ -56,8 +56,6 @@ struct irq_domain;
 #define WM8994_IRQ_GPIO(x) (x + WM8994_IRQ_TEMP_WARN)
 
 struct wm8994 {
-       struct mutex irq_lock;
-
        struct wm8994_pdata pdata;
 
        enum wm8994_type type;
@@ -85,16 +83,43 @@ struct wm8994 {
 };
 
 /* Device I/O API */
-int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg);
-int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
-                unsigned short val);
-int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
-                   unsigned short mask, unsigned short val);
-int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
-                    int count, u16 *buf);
-int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg,
-                    int count, const u16 *buf);
 
+static inline int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg)
+{
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(wm8994->regmap, reg, &val);
+
+       if (ret < 0)
+               return ret;
+       else
+               return val;
+}
+
+static inline int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
+                                  unsigned short val)
+{
+       return regmap_write(wm8994->regmap, reg, val);
+}
+
+static inline int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
+                                  int count, u16 *buf)
+{
+       return regmap_bulk_read(wm8994->regmap, reg, buf, count);
+}
+
+static inline int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg,
+                                   int count, const u16 *buf)
+{
+       return regmap_raw_write(wm8994->regmap, reg, buf, count * sizeof(u16));
+}
+
+static inline int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
+                   unsigned short mask, unsigned short val)
+{
+       return regmap_update_bits(wm8994->regmap, reg, mask, val);
+}
 
 /* Helper to save on boilerplate */
 static inline int wm8994_request_irq(struct wm8994 *wm8994, int irq,
index 5eb4e31..da78875 100644 (file)
@@ -230,6 +230,15 @@ enum {
        MLX5_MAX_PAGE_SHIFT             = 31
 };
 
+enum {
+       MLX5_ADAPTER_PAGE_SHIFT         = 12
+};
+
+enum {
+       MLX5_CAP_OFF_DCT                = 41,
+       MLX5_CAP_OFF_CMDIF_CSUM         = 46,
+};
+
 struct mlx5_inbox_hdr {
        __be16          opcode;
        u8              rsvd[4];
@@ -319,9 +328,9 @@ struct mlx5_hca_cap {
        u8      rsvd25[42];
        __be16  log_uar_page_sz;
        u8      rsvd26[28];
-       u8      log_msx_atomic_size_qp;
+       u8      log_max_atomic_size_qp;
        u8      rsvd27[2];
-       u8      log_msx_atomic_size_dc;
+       u8      log_max_atomic_size_dc;
        u8      rsvd28[76];
 };
 
index 6b8c496..554548c 100644 (file)
@@ -483,6 +483,7 @@ struct mlx5_priv {
        struct rb_root          page_root;
        int                     fw_pages;
        int                     reg_pages;
+       struct list_head        free_list;
 
        struct mlx5_core_health health;
 
@@ -557,9 +558,11 @@ typedef void (*mlx5_cmd_cbk_t)(int status, void *context);
 struct mlx5_cmd_work_ent {
        struct mlx5_cmd_msg    *in;
        struct mlx5_cmd_msg    *out;
+       void                   *uout;
+       int                     uout_size;
        mlx5_cmd_cbk_t          callback;
        void                   *context;
-       int idx;
+       int                     idx;
        struct completion       done;
        struct mlx5_cmd        *cmd;
        struct work_struct      work;
@@ -570,6 +573,7 @@ struct mlx5_cmd_work_ent {
        u8                      token;
        struct timespec         ts1;
        struct timespec         ts2;
+       u16                     op;
 };
 
 struct mlx5_pas {
@@ -653,6 +657,9 @@ void mlx5_cmd_use_polling(struct mlx5_core_dev *dev);
 int mlx5_cmd_status_to_err(struct mlx5_outbox_hdr *hdr);
 int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
                  int out_size);
+int mlx5_cmd_exec_cb(struct mlx5_core_dev *dev, void *in, int in_size,
+                    void *out, int out_size, mlx5_cmd_cbk_t callback,
+                    void *context);
 int mlx5_cmd_alloc_uar(struct mlx5_core_dev *dev, u32 *uarn);
 int mlx5_cmd_free_uar(struct mlx5_core_dev *dev, u32 uarn);
 int mlx5_alloc_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari);
@@ -676,7 +683,9 @@ int mlx5_core_query_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
 int mlx5_core_arm_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
                      u16 lwm, int is_srq);
 int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
-                         struct mlx5_create_mkey_mbox_in *in, int inlen);
+                         struct mlx5_create_mkey_mbox_in *in, int inlen,
+                         mlx5_cmd_cbk_t callback, void *context,
+                         struct mlx5_create_mkey_mbox_out *out);
 int mlx5_core_destroy_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr);
 int mlx5_core_query_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
                         struct mlx5_query_mkey_mbox_out *out, int outlen);
@@ -745,6 +754,11 @@ static inline u32 mlx5_idx_to_mkey(u32 mkey_idx)
        return mkey_idx << 8;
 }
 
+static inline u8 mlx5_mkey_variant(u32 mkey)
+{
+       return mkey & 0xff;
+}
+
 enum {
        MLX5_PROF_MASK_QP_SIZE          = (u64)1 << 0,
        MLX5_PROF_MASK_MR_CACHE         = (u64)1 << 1,
index 42a35d9..1cedd00 100644 (file)
@@ -1316,32 +1316,76 @@ static inline pmd_t *pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long a
 }
 #endif /* CONFIG_MMU && !__ARCH_HAS_4LEVEL_HACK */
 
-#if USE_SPLIT_PTLOCKS
-/*
- * We tuck a spinlock to guard each pagetable page into its struct page,
- * at page->private, with BUILD_BUG_ON to make sure that this will not
- * overflow into the next struct page (as it might with DEBUG_SPINLOCK).
- * When freeing, reset page->mapping so free_pages_check won't complain.
- */
-#define __pte_lockptr(page)    &((page)->ptl)
-#define pte_lock_init(_page)   do {                                    \
-       spin_lock_init(__pte_lockptr(_page));                           \
-} while (0)
-#define pte_lock_deinit(page)  ((page)->mapping = NULL)
-#define pte_lockptr(mm, pmd)   ({(void)(mm); __pte_lockptr(pmd_page(*(pmd)));})
-#else  /* !USE_SPLIT_PTLOCKS */
+#if USE_SPLIT_PTE_PTLOCKS
+#if BLOATED_SPINLOCKS
+extern bool ptlock_alloc(struct page *page);
+extern void ptlock_free(struct page *page);
+
+static inline spinlock_t *ptlock_ptr(struct page *page)
+{
+       return page->ptl;
+}
+#else /* BLOATED_SPINLOCKS */
+static inline bool ptlock_alloc(struct page *page)
+{
+       return true;
+}
+
+static inline void ptlock_free(struct page *page)
+{
+}
+
+static inline spinlock_t *ptlock_ptr(struct page *page)
+{
+       return &page->ptl;
+}
+#endif /* BLOATED_SPINLOCKS */
+
+static inline spinlock_t *pte_lockptr(struct mm_struct *mm, pmd_t *pmd)
+{
+       return ptlock_ptr(pmd_page(*pmd));
+}
+
+static inline bool ptlock_init(struct page *page)
+{
+       /*
+        * prep_new_page() initialize page->private (and therefore page->ptl)
+        * with 0. Make sure nobody took it in use in between.
+        *
+        * It can happen if arch try to use slab for page table allocation:
+        * slab code uses page->slab_cache and page->first_page (for tail
+        * pages), which share storage with page->ptl.
+        */
+       VM_BUG_ON(*(unsigned long *)&page->ptl);
+       if (!ptlock_alloc(page))
+               return false;
+       spin_lock_init(ptlock_ptr(page));
+       return true;
+}
+
+/* Reset page->mapping so free_pages_check won't complain. */
+static inline void pte_lock_deinit(struct page *page)
+{
+       page->mapping = NULL;
+       ptlock_free(page);
+}
+
+#else  /* !USE_SPLIT_PTE_PTLOCKS */
 /*
  * We use mm->page_table_lock to guard all pagetable pages of the mm.
  */
-#define pte_lock_init(page)    do {} while (0)
-#define pte_lock_deinit(page)  do {} while (0)
-#define pte_lockptr(mm, pmd)   ({(void)(pmd); &(mm)->page_table_lock;})
-#endif /* USE_SPLIT_PTLOCKS */
+static inline spinlock_t *pte_lockptr(struct mm_struct *mm, pmd_t *pmd)
+{
+       return &mm->page_table_lock;
+}
+static inline bool ptlock_init(struct page *page) { return true; }
+static inline void pte_lock_deinit(struct page *page) {}
+#endif /* USE_SPLIT_PTE_PTLOCKS */
 
-static inline void pgtable_page_ctor(struct page *page)
+static inline bool pgtable_page_ctor(struct page *page)
 {
-       pte_lock_init(page);
        inc_zone_page_state(page, NR_PAGETABLE);
+       return ptlock_init(page);
 }
 
 static inline void pgtable_page_dtor(struct page *page)
@@ -1378,6 +1422,52 @@ static inline void pgtable_page_dtor(struct page *page)
        ((unlikely(pmd_none(*(pmd))) && __pte_alloc_kernel(pmd, address))? \
                NULL: pte_offset_kernel(pmd, address))
 
+#if USE_SPLIT_PMD_PTLOCKS
+
+static inline spinlock_t *pmd_lockptr(struct mm_struct *mm, pmd_t *pmd)
+{
+       return ptlock_ptr(virt_to_page(pmd));
+}
+
+static inline bool pgtable_pmd_page_ctor(struct page *page)
+{
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       page->pmd_huge_pte = NULL;
+#endif
+       return ptlock_init(page);
+}
+
+static inline void pgtable_pmd_page_dtor(struct page *page)
+{
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       VM_BUG_ON(page->pmd_huge_pte);
+#endif
+       ptlock_free(page);
+}
+
+#define pmd_huge_pte(mm, pmd) (virt_to_page(pmd)->pmd_huge_pte)
+
+#else
+
+static inline spinlock_t *pmd_lockptr(struct mm_struct *mm, pmd_t *pmd)
+{
+       return &mm->page_table_lock;
+}
+
+static inline bool pgtable_pmd_page_ctor(struct page *page) { return true; }
+static inline void pgtable_pmd_page_dtor(struct page *page) {}
+
+#define pmd_huge_pte(mm, pmd) ((mm)->pmd_huge_pte)
+
+#endif
+
+static inline spinlock_t *pmd_lock(struct mm_struct *mm, pmd_t *pmd)
+{
+       spinlock_t *ptl = pmd_lockptr(mm, pmd);
+       spin_lock(ptl);
+       return ptl;
+}
+
 extern void free_area_init(unsigned long * zones_size);
 extern void free_area_init_node(int nid, unsigned long * zones_size,
                unsigned long zone_start_pfn, unsigned long *zholes_size);
index a3198e5..bd29941 100644 (file)
@@ -23,7 +23,9 @@
 
 struct address_space;
 
-#define USE_SPLIT_PTLOCKS      (NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS)
+#define USE_SPLIT_PTE_PTLOCKS  (NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS)
+#define USE_SPLIT_PMD_PTLOCKS  (USE_SPLIT_PTE_PTLOCKS && \
+               IS_ENABLED(CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK))
 
 /*
  * Each physical page in the system has a struct page associated with
@@ -42,18 +44,22 @@ struct page {
        /* First double word block */
        unsigned long flags;            /* Atomic flags, some possibly
                                         * updated asynchronously */
-       struct address_space *mapping;  /* If low bit clear, points to
-                                        * inode address_space, or NULL.
-                                        * If page mapped as anonymous
-                                        * memory, low bit is set, and
-                                        * it points to anon_vma object:
-                                        * see PAGE_MAPPING_ANON below.
-                                        */
+       union {
+               struct address_space *mapping;  /* If low bit clear, points to
+                                                * inode address_space, or NULL.
+                                                * If page mapped as anonymous
+                                                * memory, low bit is set, and
+                                                * it points to anon_vma object:
+                                                * see PAGE_MAPPING_ANON below.
+                                                */
+               void *s_mem;                    /* slab first object */
+       };
+
        /* Second double word */
        struct {
                union {
                        pgoff_t index;          /* Our offset within mapping. */
-                       void *freelist;         /* slub/slob first free object */
+                       void *freelist;         /* sl[aou]b first free object */
                        bool pfmemalloc;        /* If set by the page allocator,
                                                 * ALLOC_NO_WATERMARKS was set
                                                 * and the low watermark was not
@@ -109,6 +115,7 @@ struct page {
                                };
                                atomic_t _count;                /* Usage count, see below. */
                        };
+                       unsigned int active;    /* SLAB */
                };
        };
 
@@ -130,6 +137,12 @@ struct page {
 
                struct list_head list;  /* slobs list of pages */
                struct slab *slab_page; /* slab fields */
+               struct rcu_head rcu_head;       /* Used by SLAB
+                                                * when destroying via RCU
+                                                */
+#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && USE_SPLIT_PMD_PTLOCKS
+               pgtable_t pmd_huge_pte; /* protected by page->ptl */
+#endif
        };
 
        /* Remainder is not double word aligned */
@@ -141,9 +154,13 @@ struct page {
                                                 * indicates order in the buddy
                                                 * system if PG_buddy is set.
                                                 */
-#if USE_SPLIT_PTLOCKS
+#if USE_SPLIT_PTE_PTLOCKS
+#if BLOATED_SPINLOCKS
+               spinlock_t *ptl;
+#else
                spinlock_t ptl;
 #endif
+#endif
                struct kmem_cache *slab_cache;  /* SL[AU]B: Pointer to slab */
                struct page *first_page;        /* Compound tail pages */
        };
@@ -309,14 +326,14 @@ enum {
        NR_MM_COUNTERS
 };
 
-#if USE_SPLIT_PTLOCKS && defined(CONFIG_MMU)
+#if USE_SPLIT_PTE_PTLOCKS && defined(CONFIG_MMU)
 #define SPLIT_RSS_COUNTING
 /* per-thread cached information, */
 struct task_rss_stat {
        int events;     /* for synchronization threshold */
        int count[NR_MM_COUNTERS];
 };
-#endif /* USE_SPLIT_PTLOCKS */
+#endif /* USE_SPLIT_PTE_PTLOCKS */
 
 struct mm_rss_stat {
        atomic_long_t count[NR_MM_COUNTERS];
@@ -339,6 +356,7 @@ struct mm_struct {
        pgd_t * pgd;
        atomic_t mm_users;                      /* How many users with user space? */
        atomic_t mm_count;                      /* How many references to "struct mm_struct" (users count as 1) */
+       atomic_long_t nr_ptes;                  /* Page table pages */
        int map_count;                          /* number of VMAs */
 
        spinlock_t page_table_lock;             /* Protects page tables and some counters */
@@ -360,7 +378,6 @@ struct mm_struct {
        unsigned long exec_vm;          /* VM_EXEC & ~VM_WRITE */
        unsigned long stack_vm;         /* VM_GROWSUP/DOWN */
        unsigned long def_flags;
-       unsigned long nr_ptes;          /* Page table pages */
        unsigned long start_code, end_code, start_data, end_data;
        unsigned long start_brk, brk, start_stack;
        unsigned long arg_start, arg_end, env_start, env_end;
@@ -406,7 +423,7 @@ struct mm_struct {
 #ifdef CONFIG_MMU_NOTIFIER
        struct mmu_notifier_mm *mmu_notifier_mm;
 #endif
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS
        pgtable_t pmd_huge_pte; /* protected by page_table_lock */
 #endif
 #ifdef CONFIG_CPUMASK_OFFSTACK
index 842de3e..176fdf8 100644 (file)
@@ -240,6 +240,7 @@ struct mmc_part {
 struct mmc_card {
        struct mmc_host         *host;          /* the host this device belongs to */
        struct device           dev;            /* the device */
+       u32                     ocr;            /* the current OCR setting */
        unsigned int            rca;            /* relative card address of device */
        unsigned int            type;           /* card type */
 #define MMC_TYPE_MMC           0               /* MMC card */
@@ -257,6 +258,7 @@ struct mmc_card {
 #define MMC_CARD_REMOVED       (1<<7)          /* card has been removed */
 #define MMC_STATE_HIGHSPEED_200        (1<<8)          /* card is in HS200 mode */
 #define MMC_STATE_DOING_BKOPS  (1<<10)         /* card is doing BKOPS */
+#define MMC_STATE_SUSPENDED    (1<<11)         /* card is suspended */
        unsigned int            quirks;         /* card quirks */
 #define MMC_QUIRK_LENIENT_FN0  (1<<0)          /* allow SDIO FN0 writes outside of the VS CCCR range */
 #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)   /* use func->cur_blksize */
@@ -420,10 +422,10 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_blockaddr(c)  ((c)->state & MMC_STATE_BLOCKADDR)
 #define mmc_card_ddr_mode(c)   ((c)->state & MMC_STATE_HIGHSPEED_DDR)
 #define mmc_card_uhs(c)                ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
-#define mmc_sd_card_uhs(c)     ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
 #define mmc_card_removed(c)    ((c) && ((c)->state & MMC_CARD_REMOVED))
 #define mmc_card_doing_bkops(c)        ((c)->state & MMC_STATE_DOING_BKOPS)
+#define mmc_card_suspended(c)  ((c)->state & MMC_STATE_SUSPENDED)
 
 #define mmc_card_set_present(c)        ((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@@ -432,11 +434,12 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
 #define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
 #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
-#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
 #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
 #define mmc_card_set_doing_bkops(c)    ((c)->state |= MMC_STATE_DOING_BKOPS)
 #define mmc_card_clr_doing_bkops(c)    ((c)->state &= ~MMC_STATE_DOING_BKOPS)
+#define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED)
+#define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED)
 
 /*
  * Quirk add/remove for MMC products.
index da51bec..87079fc 100644 (file)
@@ -151,7 +151,8 @@ extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
 extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
        struct mmc_command *, int);
 extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
-extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool);
+extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool,
+                       bool);
 extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
 extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
 
@@ -188,7 +189,6 @@ extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
 
 extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
 extern void mmc_release_host(struct mmc_host *host);
-extern int mmc_try_claim_host(struct mmc_host *host);
 
 extern void mmc_get_card(struct mmc_card *card);
 extern void mmc_put_card(struct mmc_card *card);
index 198f0fa..6ce7d2c 100644 (file)
@@ -15,6 +15,7 @@
 #define LINUX_MMC_DW_MMC_H
 
 #include <linux/scatterlist.h>
+#include <linux/mmc/core.h>
 
 #define MAX_MCI_SLOTS  2
 
@@ -129,6 +130,9 @@ struct dw_mci {
        struct mmc_request      *mrq;
        struct mmc_command      *cmd;
        struct mmc_data         *data;
+       struct mmc_command      stop_abort;
+       unsigned int            prev_blksz;
+       unsigned char           timing;
        struct workqueue_struct *card_workqueue;
 
        /* DMA interface members*/
index 3b0c33a..99f5709 100644 (file)
@@ -254,6 +254,7 @@ struct mmc_host {
 #define MMC_CAP_UHS_SDR50      (1 << 17)       /* Host supports UHS SDR50 mode */
 #define MMC_CAP_UHS_SDR104     (1 << 18)       /* Host supports UHS SDR104 mode */
 #define MMC_CAP_UHS_DDR50      (1 << 19)       /* Host supports UHS DDR50 mode */
+#define MMC_CAP_RUNTIME_RESUME (1 << 20)       /* Resume at runtime_resume. */
 #define MMC_CAP_DRIVER_TYPE_A  (1 << 23)       /* Host supports Driver Type A */
 #define MMC_CAP_DRIVER_TYPE_C  (1 << 24)       /* Host supports Driver Type C */
 #define MMC_CAP_DRIVER_TYPE_D  (1 << 25)       /* Host supports Driver Type D */
@@ -309,7 +310,6 @@ struct mmc_host {
        spinlock_t              lock;           /* lock for claim and bus ops */
 
        struct mmc_ios          ios;            /* current io bus settings */
-       u32                     ocr;            /* the current OCR setting */
 
        /* group bitfields together to minimize padding */
        unsigned int            use_spi_crc:1;
@@ -382,9 +382,6 @@ static inline void *mmc_priv(struct mmc_host *host)
 #define mmc_classdev(x)        (&(x)->class_dev)
 #define mmc_hostname(x)        (dev_name(&(x)->class_dev))
 
-int mmc_suspend_host(struct mmc_host *);
-int mmc_resume_host(struct mmc_host *);
-
 int mmc_power_save_host(struct mmc_host *host);
 int mmc_power_restore_host(struct mmc_host *host);
 
index 05f2447..15cd6b1 100644 (file)
@@ -367,9 +367,6 @@ struct module
        /* What modules do I depend on? */
        struct list_head target_list;
 
-       /* Who is waiting for us to be unloaded */
-       struct task_struct *waiter;
-
        /* Destruction function. */
        void (*exit)(void);
 
index b292a04..4bcee94 100644 (file)
@@ -164,6 +164,14 @@ struct proto_ops {
 #endif
        int             (*sendmsg)   (struct kiocb *iocb, struct socket *sock,
                                      struct msghdr *m, size_t total_len);
+       /* Notes for implementing recvmsg:
+        * ===============================
+        * msg->msg_namelen should get updated by the recvmsg handlers
+        * iff msg_name != NULL. It is by default 0 to prevent
+        * returning uninitialized memory to user space.  The recvfrom
+        * handlers can assume that msg.msg_name is either NULL or has
+        * a minimum size of sizeof(struct sockaddr_storage).
+        */
        int             (*recvmsg)   (struct kiocb *iocb, struct socket *sock,
                                      struct msghdr *m, size_t total_len,
                                      int flags);
index 8b3de7c..7f0ed42 100644 (file)
@@ -1587,7 +1587,7 @@ static inline void *netdev_priv(const struct net_device *dev)
 #define SET_NETDEV_DEV(net, pdev)      ((net)->dev.parent = (pdev))
 
 /* Set the sysfs device type for the network logical device to allow
- * fin grained indentification of different network device types. For
+ * fine-grained identification of different network device types. For
  * example Ethernet, Wirelss LAN, Bluetooth, WiMAX etc.
  */
 #define SET_NETDEV_DEVTYPE(net, devtype)       ((net)->dev.type = (devtype))
index c6f41b6..c163706 100644 (file)
@@ -118,6 +118,9 @@ Needs to be updated if more operations are defined in future.*/
 
 #define FIRST_NFS4_OP  OP_ACCESS
 #define LAST_NFS4_OP   OP_RECLAIM_COMPLETE
+#define LAST_NFS40_OP  OP_RELEASE_LOCKOWNER
+#define LAST_NFS41_OP  OP_RECLAIM_COMPLETE
+#define LAST_NFS42_OP  OP_RECLAIM_COMPLETE
 
 enum nfsstat4 {
        NFS4_OK = 0,
index d006f0c..5a462c4 100644 (file)
@@ -27,7 +27,7 @@ static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev)
        while (!pci_is_root_bus(pbus))
                pbus = pbus->parent;
 
-       return DEVICE_ACPI_HANDLE(pbus->bridge);
+       return ACPI_HANDLE(pbus->bridge);
 }
 
 static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus)
@@ -39,7 +39,7 @@ static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus)
        else
                dev = &pbus->self->dev;
 
-       return DEVICE_ACPI_HANDLE(dev);
+       return ACPI_HANDLE(dev);
 }
 
 void acpi_pci_add_bus(struct pci_bus *bus);
index 64ab823..48a4dc3 100644 (file)
@@ -559,6 +559,7 @@ static inline int phy_read_status(struct phy_device *phydev) {
        return phydev->drv->read_status(phydev);
 }
 
+int genphy_setup_forced(struct phy_device *phydev);
 int genphy_restart_aneg(struct phy_device *phydev);
 int genphy_config_aneg(struct phy_device *phydev);
 int genphy_update_link(struct phy_device *phydev);
similarity index 97%
rename from include/linux/i2c/at24.h
rename to include/linux/platform_data/at24.h
index 285025a..c42aa89 100644 (file)
@@ -28,7 +28,7 @@
  *
  * void get_mac_addr(struct memory_accessor *mem_acc, void *context)
  * {
- *     u8 *mac_addr = ethernet_pdata->mac_addr;
+ *     u8 *mac_addr = ethernet_pdata->mac_addr;
  *     off_t offset = context;
  *
  *     // Read MAC addr from EEPROM
index 179fb91..f50821c 100644 (file)
@@ -67,10 +67,10 @@ struct edmacc_param {
 #define ITCCHEN                BIT(23)
 
 /*ch_status paramater of callback function possible values*/
-#define DMA_COMPLETE 1
-#define DMA_CC_ERROR 2
-#define DMA_TC1_ERROR 3
-#define DMA_TC2_ERROR 4
+#define EDMA_DMA_COMPLETE 1
+#define EDMA_DMA_CC_ERROR 2
+#define EDMA_DMA_TC1_ERROR 3
+#define EDMA_DMA_TC2_ERROR 4
 
 enum address_mode {
        INCR = 0,
index d44912d..75f70f6 100644 (file)
@@ -10,6 +10,8 @@
 #ifndef __ASM_ARCH_IMX_ESDHC_H
 #define __ASM_ARCH_IMX_ESDHC_H
 
+#include <linux/types.h>
+
 enum wp_types {
        ESDHC_WP_NONE,          /* no WP, neither controller nor gpio */
        ESDHC_WP_CONTROLLER,    /* mmc controller internal WP */
@@ -32,6 +34,7 @@ enum cd_types {
  * @cd_gpio:   gpio for card_detect interrupt
  * @wp_type:   type of write_protect method (see wp_types enum above)
  * @cd_type:   type of card_detect method (see cd_types enum above)
+ * @support_vsel:  indicate it supports 1.8v switching
  */
 
 struct esdhc_platform_data {
@@ -41,5 +44,7 @@ struct esdhc_platform_data {
        enum cd_types cd_type;
        int max_bus_width;
        unsigned int f_max;
+       bool support_vsel;
+       unsigned int delay_line;
 };
 #endif /* __ASM_ARCH_IMX_ESDHC_H */
diff --git a/include/linux/platform_data/zforce_ts.h b/include/linux/platform_data/zforce_ts.h
new file mode 100644 (file)
index 0000000..0472ab2
--- /dev/null
@@ -0,0 +1,26 @@
+/* drivers/input/touchscreen/zforce.c
+ *
+ * Copyright (C) 2012-2013 MundoReader S.L.
+ *
+ * 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.
+ */
+
+#ifndef _LINUX_INPUT_ZFORCE_TS_H
+#define _LINUX_INPUT_ZFORCE_TS_H
+
+struct zforce_ts_platdata {
+       int gpio_int;
+       int gpio_rst;
+
+       unsigned int x_max;
+       unsigned int y_max;
+};
+
+#endif /* _LINUX_INPUT_ZFORCE_TS_H */
diff --git a/include/linux/power/bq24735-charger.h b/include/linux/power/bq24735-charger.h
new file mode 100644 (file)
index 0000000..f536164
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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 __CHARGER_BQ24735_H_
+#define __CHARGER_BQ24735_H_
+
+#include <linux/types.h>
+#include <linux/power_supply.h>
+
+struct bq24735_platform {
+       uint32_t charge_current;
+       uint32_t charge_voltage;
+       uint32_t input_current;
+
+       const char *name;
+
+       int status_gpio;
+       int status_gpio_active_low;
+       bool status_gpio_valid;
+
+       char **supplied_to;
+       size_t num_supplicants;
+};
+
+#endif /* __CHARGER_BQ24735_H_ */
index 931bc61..d169820 100644 (file)
  * - bits 0-7 are the preemption count (max preemption depth: 256)
  * - bits 8-15 are the softirq count (max # of softirqs: 256)
  *
- * The hardirq count can in theory reach the same as NR_IRQS.
- * In reality, the number of nested IRQS is limited to the stack
- * size as well. For archs with over 1000 IRQS it is not practical
- * to expect that they will all nest. We give a max of 10 bits for
- * hardirq nesting. An arch may choose to give less than 10 bits.
- * m68k expects it to be 8.
+ * The hardirq count could in theory be the same as the number of
+ * interrupts in the system, but we run all interrupt handlers with
+ * interrupts disabled, so we cannot have nesting interrupts. Though
+ * there are a few palaeontologic drivers which reenable interrupts in
+ * the handler, so we need more than one bit here.
  *
- * - bits 16-25 are the hardirq count (max # of nested hardirqs: 1024)
- * - bit 26 is the NMI_MASK
- * - bit 27 is the PREEMPT_ACTIVE flag
- *
- * PREEMPT_MASK: 0x000000ff
- * SOFTIRQ_MASK: 0x0000ff00
- * HARDIRQ_MASK: 0x03ff0000
- *     NMI_MASK: 0x04000000
+ * PREEMPT_MASK:       0x000000ff
+ * SOFTIRQ_MASK:       0x0000ff00
+ * HARDIRQ_MASK:       0x000f0000
+ *     NMI_MASK:       0x00100000
+ * PREEMPT_ACTIVE:     0x00200000
  */
 #define PREEMPT_BITS   8
 #define SOFTIRQ_BITS   8
+#define HARDIRQ_BITS   4
 #define NMI_BITS       1
 
-#define MAX_HARDIRQ_BITS 10
-
-#ifndef HARDIRQ_BITS
-# define HARDIRQ_BITS  MAX_HARDIRQ_BITS
-#endif
-
-#if HARDIRQ_BITS > MAX_HARDIRQ_BITS
-#error HARDIRQ_BITS too high!
-#endif
-
 #define PREEMPT_SHIFT  0
 #define SOFTIRQ_SHIFT  (PREEMPT_SHIFT + PREEMPT_BITS)
 #define HARDIRQ_SHIFT  (SOFTIRQ_SHIFT + SOFTIRQ_BITS)
 
 #define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET)
 
-#ifndef PREEMPT_ACTIVE
 #define PREEMPT_ACTIVE_BITS    1
 #define PREEMPT_ACTIVE_SHIFT   (NMI_SHIFT + NMI_BITS)
 #define PREEMPT_ACTIVE (__IRQ_MASK(PREEMPT_ACTIVE_BITS) << PREEMPT_ACTIVE_SHIFT)
-#endif
-
-#if PREEMPT_ACTIVE < (1 << (NMI_SHIFT + NMI_BITS))
-#error PREEMPT_ACTIVE is too low!
-#endif
 
 #define hardirq_count()        (preempt_count() & HARDIRQ_MASK)
 #define softirq_count()        (preempt_count() & SOFTIRQ_MASK)
index 56f4a86..2de2e27 100644 (file)
@@ -6,6 +6,9 @@
 
 #include <linux/backlight.h>
 
+/* TODO: convert to gpiod_*() API once it has been merged */
+#define PWM_BACKLIGHT_GPIO_ACTIVE_LOW  (1 << 0)
+
 struct platform_pwm_backlight_data {
        int pwm_id;
        unsigned int max_brightness;
@@ -13,6 +16,8 @@ struct platform_pwm_backlight_data {
        unsigned int lth_brightness;
        unsigned int pwm_period_ns;
        unsigned int *levels;
+       int enable_gpio;
+       unsigned long enable_gpio_flags;
        int (*init)(struct device *dev);
        int (*notify)(struct device *dev, int brightness);
        void (*notify_after)(struct device *dev, int brightness);
index f7efc86..7e35d4b 100644 (file)
@@ -22,7 +22,7 @@ struct sched_param {
 #include <linux/errno.h>
 #include <linux/nodemask.h>
 #include <linux/mm_types.h>
-#include <linux/preempt.h>
+#include <linux/preempt_mask.h>
 
 #include <asm/page.h>
 #include <asm/ptrace.h>
@@ -286,6 +286,14 @@ static inline void lockup_detector_init(void)
 }
 #endif
 
+#ifdef CONFIG_DETECT_HUNG_TASK
+void reset_hung_task_detector(void);
+#else
+static inline void reset_hung_task_detector(void)
+{
+}
+#endif
+
 /* Attach to any functions which should be ignored in wchan output. */
 #define __sched                __attribute__((__section__(".sched.text")))
 
index 9d37e2b..5623a7f 100644 (file)
@@ -1052,17 +1052,25 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  * @xfrm_policy_delete_security:
  *     @ctx contains the xfrm_sec_ctx.
  *     Authorize deletion of xp->security.
- * @xfrm_state_alloc_security:
+ * @xfrm_state_alloc:
  *     @x contains the xfrm_state being added to the Security Association
  *     Database by the XFRM system.
  *     @sec_ctx contains the security context information being provided by
  *     the user-level SA generation program (e.g., setkey or racoon).
- *     @secid contains the secid from which to take the mls portion of the context.
  *     Allocate a security structure to the x->security field; the security
  *     field is initialized to NULL when the xfrm_state is allocated. Set the
- *     context to correspond to either sec_ctx or polsec, with the mls portion
- *     taken from secid in the latter case.
- *     Return 0 if operation was successful (memory to allocate, legal context).
+ *     context to correspond to sec_ctx. Return 0 if operation was successful
+ *     (memory to allocate, legal context).
+ * @xfrm_state_alloc_acquire:
+ *     @x contains the xfrm_state being added to the Security Association
+ *     Database by the XFRM system.
+ *     @polsec contains the policy's security context.
+ *     @secid contains the secid from which to take the mls portion of the
+ *     context.
+ *     Allocate a security structure to the x->security field; the security
+ *     field is initialized to NULL when the xfrm_state is allocated. Set the
+ *     context to correspond to secid. Return 0 if operation was successful
+ *     (memory to allocate, legal context).
  * @xfrm_state_free_security:
  *     @x contains the xfrm_state.
  *     Deallocate x->security.
@@ -1679,9 +1687,11 @@ struct security_operations {
        int (*xfrm_policy_clone_security) (struct xfrm_sec_ctx *old_ctx, struct xfrm_sec_ctx **new_ctx);
        void (*xfrm_policy_free_security) (struct xfrm_sec_ctx *ctx);
        int (*xfrm_policy_delete_security) (struct xfrm_sec_ctx *ctx);
-       int (*xfrm_state_alloc_security) (struct xfrm_state *x,
-               struct xfrm_user_sec_ctx *sec_ctx,
-               u32 secid);
+       int (*xfrm_state_alloc) (struct xfrm_state *x,
+                                struct xfrm_user_sec_ctx *sec_ctx);
+       int (*xfrm_state_alloc_acquire) (struct xfrm_state *x,
+                                        struct xfrm_sec_ctx *polsec,
+                                        u32 secid);
        void (*xfrm_state_free_security) (struct xfrm_state *x);
        int (*xfrm_state_delete_security) (struct xfrm_state *x);
        int (*xfrm_policy_lookup) (struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir);
index 4e32edc..52e0097 100644 (file)
@@ -20,6 +20,7 @@ struct seq_file {
        size_t size;
        size_t from;
        size_t count;
+       size_t pad_until;
        loff_t index;
        loff_t read_pos;
        u64 version;
@@ -79,6 +80,20 @@ static inline void seq_commit(struct seq_file *m, int num)
        }
 }
 
+/**
+ * seq_setwidth - set padding width
+ * @m: the seq_file handle
+ * @size: the max number of bytes to pad.
+ *
+ * Call seq_setwidth() for setting max width, then call seq_printf() etc. and
+ * finally call seq_pad() to pad the remaining bytes.
+ */
+static inline void seq_setwidth(struct seq_file *m, size_t size)
+{
+       m->pad_until = m->count + size;
+}
+void seq_pad(struct seq_file *m, char c);
+
 char *mangle_path(char *s, const char *p, const char *esc);
 int seq_open(struct file *, const struct seq_operations *);
 ssize_t seq_read(struct file *, char __user *, size_t, loff_t *);
index 1e8a8b6..cf87a24 100644 (file)
@@ -354,6 +354,35 @@ static inline void read_sequnlock_excl(seqlock_t *sl)
        spin_unlock(&sl->lock);
 }
 
+/**
+ * read_seqbegin_or_lock - begin a sequence number check or locking block
+ * @lock: sequence lock
+ * @seq : sequence number to be checked
+ *
+ * First try it once optimistically without taking the lock. If that fails,
+ * take the lock. The sequence number is also used as a marker for deciding
+ * whether to be a reader (even) or writer (odd).
+ * N.B. seq must be initialized to an even number to begin with.
+ */
+static inline void read_seqbegin_or_lock(seqlock_t *lock, int *seq)
+{
+       if (!(*seq & 1))        /* Even */
+               *seq = read_seqbegin(lock);
+       else                    /* Odd */
+               read_seqlock_excl(lock);
+}
+
+static inline int need_seqretry(seqlock_t *lock, int seq)
+{
+       return !(seq & 1) && read_seqretry(lock, seq);
+}
+
+static inline void done_seqretry(seqlock_t *lock, int seq)
+{
+       if (seq & 1)
+               read_sequnlock_excl(lock);
+}
+
 static inline void read_seqlock_excl_bh(seqlock_t *sl)
 {
        spin_lock_bh(&sl->lock);
index 215b5ea..bec1cc7 100644 (file)
@@ -2263,24 +2263,6 @@ static inline void skb_postpull_rcsum(struct sk_buff *skb,
 
 unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len);
 
-/**
- *     pskb_trim_rcsum - trim received skb and update checksum
- *     @skb: buffer to trim
- *     @len: new length
- *
- *     This is exactly the same as pskb_trim except that it ensures the
- *     checksum of received packets are still valid after the operation.
- */
-
-static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len)
-{
-       if (likely(len >= skb->len))
-               return 0;
-       if (skb->ip_summed == CHECKSUM_COMPLETE)
-               skb->ip_summed = CHECKSUM_NONE;
-       return __pskb_trim(skb, len);
-}
-
 #define skb_queue_walk(queue, skb) \
                for (skb = (queue)->next;                                       \
                     skb != (struct sk_buff *)(queue);                          \
@@ -2378,6 +2360,27 @@ __wsum __skb_checksum(const struct sk_buff *skb, int offset, int len,
 __wsum skb_checksum(const struct sk_buff *skb, int offset, int len,
                    __wsum csum);
 
+/**
+ *     pskb_trim_rcsum - trim received skb and update checksum
+ *     @skb: buffer to trim
+ *     @len: new length
+ *
+ *     This is exactly the same as pskb_trim except that it ensures the
+ *     checksum of received packets are still valid after the operation.
+ */
+
+static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len)
+{
+       if (likely(len >= skb->len))
+               return 0;
+       if (skb->ip_summed == CHECKSUM_COMPLETE) {
+               __wsum adj = skb_checksum(skb, len, skb->len - len, 0);
+
+               skb->csum = csum_sub(skb->csum, adj);
+       }
+       return __pskb_trim(skb, len);
+}
+
 static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
                                       int len, void *buffer)
 {
index 74f1058..c2bba24 100644 (file)
  *  }
  *  rcu_read_unlock();
  *
- * See also the comment on struct slab_rcu in mm/slab.c.
+ * This is useful if we need to approach a kernel structure obliquely,
+ * from its address obtained without the usual locking. We can lock
+ * the structure to stabilize it and check it's still at the given address,
+ * only if we can be sure that the memory has not been meanwhile reused
+ * for some other kind of object (which our subsystem's lock might corrupt).
+ *
+ * rcu_read_lock before reading the address, then rcu_read_unlock after
+ * taking the spinlock within the structure expected at that address.
  */
 #define SLAB_DESTROY_BY_RCU    0x00080000UL    /* Defer freeing slabs to RCU */
 #define SLAB_MEM_SPREAD                0x00100000UL    /* Spread some memory over cpuset */
index e9346b4..09bfffb 100644 (file)
@@ -27,8 +27,8 @@ struct kmem_cache {
 
        size_t colour;                  /* cache colouring range */
        unsigned int colour_off;        /* colour offset */
-       struct kmem_cache *slabp_cache;
-       unsigned int slab_size;
+       struct kmem_cache *freelist_cache;
+       unsigned int freelist_size;
 
        /* constructor func */
        void (*ctor)(void *obj);
index cc0b67e..f56bfa9 100644 (file)
@@ -11,7 +11,7 @@
 enum stat_item {
        ALLOC_FASTPATH,         /* Allocation from cpu slab */
        ALLOC_SLOWPATH,         /* Allocation by getting a new cpu slab */
-       FREE_FASTPATH,          /* Free to cpu slub */
+       FREE_FASTPATH,          /* Free to cpu slab */
        FREE_SLOWPATH,          /* Freeing not to cpu slab */
        FREE_FROZEN,            /* Freeing to frozen slab */
        FREE_ADD_PARTIAL,       /* Freeing moves slab to partial list */
index 731f523..5da22ee 100644 (file)
@@ -49,6 +49,9 @@ void on_each_cpu_cond(bool (*cond_func)(int cpu, void *info),
                smp_call_func_t func, void *info, bool wait,
                gfp_t gfp_flags);
 
+void __smp_call_function_single(int cpuid, struct call_single_data *data,
+                               int wait);
+
 #ifdef CONFIG_SMP
 
 #include <linux/preempt.h>
@@ -95,9 +98,6 @@ int smp_call_function(smp_call_func_t func, void *info, int wait);
 void smp_call_function_many(const struct cpumask *mask,
                            smp_call_func_t func, void *info, bool wait);
 
-void __smp_call_function_single(int cpuid, struct call_single_data *data,
-                               int wait);
-
 int smp_call_function_any(const struct cpumask *mask,
                          smp_call_func_t func, void *info, int wait);
 
@@ -106,14 +106,10 @@ void kick_all_cpus_sync(void);
 /*
  * Generic and arch helpers
  */
-#ifdef CONFIG_USE_GENERIC_SMP_HELPERS
 void __init call_function_init(void);
 void generic_smp_call_function_single_interrupt(void);
 #define generic_smp_call_function_interrupt \
        generic_smp_call_function_single_interrupt
-#else
-static inline void call_function_init(void) { }
-#endif
 
 /*
  * Mark the boot cpu "online" so that it can call console drivers in
@@ -155,12 +151,6 @@ smp_call_function_any(const struct cpumask *mask, smp_call_func_t func,
 
 static inline void kick_all_cpus_sync(void) {  }
 
-static inline void __smp_call_function_single(int cpuid,
-               struct call_single_data *data, int wait)
-{
-       on_each_cpu(data->func, data->info, wait);
-}
-
 #endif /* !SMP */
 
 /*
index c114614..9b058ee 100644 (file)
@@ -237,4 +237,18 @@ static inline void srcu_read_unlock(struct srcu_struct *sp, int idx)
        __srcu_read_unlock(sp, idx);
 }
 
+/**
+ * smp_mb__after_srcu_read_unlock - ensure full ordering after srcu_read_unlock
+ *
+ * Converts the preceding srcu_read_unlock into a two-way memory barrier.
+ *
+ * Call this after srcu_read_unlock, to guarantee that all memory operations
+ * that occur after smp_mb__after_srcu_read_unlock will appear to happen after
+ * the preceding srcu_read_unlock.
+ */
+static inline void smp_mb__after_srcu_read_unlock(void)
+{
+       /* __srcu_read_unlock has smp_mb() internally so nothing to do here. */
+}
+
 #endif
index 8d4fa82..c0f7526 100644 (file)
@@ -139,7 +139,8 @@ static inline void make_migration_entry_read(swp_entry_t *entry)
 
 extern void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd,
                                        unsigned long address);
-extern void migration_entry_wait_huge(struct mm_struct *mm, pte_t *pte);
+extern void migration_entry_wait_huge(struct vm_area_struct *vma,
+               struct mm_struct *mm, pte_t *pte);
 #else
 
 #define make_migration_entry(page, write) swp_entry(0, 0)
@@ -151,8 +152,8 @@ static inline int is_migration_entry(swp_entry_t swp)
 static inline void make_migration_entry_read(swp_entry_t *entryp) { }
 static inline void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd,
                                         unsigned long address) { }
-static inline void migration_entry_wait_huge(struct mm_struct *mm,
-                                       pte_t *pte) { }
+static inline void migration_entry_wait_huge(struct vm_area_struct *vma,
+               struct mm_struct *mm, pte_t *pte) { }
 static inline int is_write_migration_entry(swp_entry_t entry)
 {
        return 0;
index c27f846..94273bb 100644 (file)
@@ -120,7 +120,7 @@ extern struct trace_event_functions exit_syscall_print_funcs;
                .class                  = &event_class_syscall_enter,   \
                .event.funcs            = &enter_syscall_print_funcs,   \
                .data                   = (void *)&__syscall_meta_##sname,\
-               .flags                  = TRACE_EVENT_FL_CAP_ANY,       \
+               .flags                  = TRACE_EVENT_FL_CAP_ANY,       \
        };                                                              \
        static struct ftrace_event_call __used                          \
          __attribute__((section("_ftrace_events")))                    \
@@ -134,7 +134,7 @@ extern struct trace_event_functions exit_syscall_print_funcs;
                .class                  = &event_class_syscall_exit,    \
                .event.funcs            = &exit_syscall_print_funcs,    \
                .data                   = (void *)&__syscall_meta_##sname,\
-               .flags                  = TRACE_EVENT_FL_CAP_ANY,       \
+               .flags                  = TRACE_EVENT_FL_CAP_ANY,       \
        };                                                              \
        static struct ftrace_event_call __used                          \
          __attribute__((section("_ftrace_events")))                    \
index 4db2985..4836ba3 100644 (file)
@@ -27,6 +27,12 @@ struct user_namespace {
        kuid_t                  owner;
        kgid_t                  group;
        unsigned int            proc_inum;
+
+       /* Register of per-UID persistent keyrings for this namespace */
+#ifdef CONFIG_PERSISTENT_KEYRINGS
+       struct key              *persistent_keyring_register;
+       struct rw_semaphore     persistent_keyring_register_sem;
+#endif
 };
 
 extern struct user_namespace init_user_ns;
index 36d36cc..e4abb84 100644 (file)
@@ -51,11 +51,11 @@ int virtqueue_add_sgs(struct virtqueue *vq,
                      void *data,
                      gfp_t gfp);
 
-void virtqueue_kick(struct virtqueue *vq);
+bool virtqueue_kick(struct virtqueue *vq);
 
 bool virtqueue_kick_prepare(struct virtqueue *vq);
 
-void virtqueue_notify(struct virtqueue *vq);
+bool virtqueue_notify(struct virtqueue *vq);
 
 void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len);
 
@@ -73,6 +73,8 @@ void *virtqueue_detach_unused_buf(struct virtqueue *vq);
 
 unsigned int virtqueue_get_vring_size(struct virtqueue *vq);
 
+bool virtqueue_is_broken(struct virtqueue *vq);
+
 /**
  * virtio_device - representation of a device using virtio
  * @index: unique position on the virtio bus
index 29b9104..e8f8f71 100644 (file)
@@ -96,33 +96,6 @@ static inline bool virtio_has_feature(const struct virtio_device *vdev,
        return test_bit(fbit, vdev->features);
 }
 
-/**
- * virtio_config_val - look for a feature and get a virtio config entry.
- * @vdev: the virtio device
- * @fbit: the feature bit
- * @offset: the type to search for.
- * @v: a pointer to the value to fill in.
- *
- * The return value is -ENOENT if the feature doesn't exist.  Otherwise
- * the config value is copied into whatever is pointed to by v. */
-#define virtio_config_val(vdev, fbit, offset, v) \
-       virtio_config_buf((vdev), (fbit), (offset), (v), sizeof(*v))
-
-#define virtio_config_val_len(vdev, fbit, offset, v, len) \
-       virtio_config_buf((vdev), (fbit), (offset), (v), (len))
-
-static inline int virtio_config_buf(struct virtio_device *vdev,
-                                   unsigned int fbit,
-                                   unsigned int offset,
-                                   void *buf, unsigned len)
-{
-       if (!virtio_has_feature(vdev, fbit))
-               return -ENOENT;
-
-       vdev->config->get(vdev, offset, buf, len);
-       return 0;
-}
-
 static inline
 struct virtqueue *virtio_find_single_vq(struct virtio_device *vdev,
                                        vq_callback_t *c, const char *n)
@@ -162,5 +135,139 @@ int virtqueue_set_affinity(struct virtqueue *vq, int cpu)
        return 0;
 }
 
+/* Config space accessors. */
+#define virtio_cread(vdev, structname, member, ptr)                    \
+       do {                                                            \
+               /* Must match the member's type, and be integer */      \
+               if (!typecheck(typeof((((structname*)0)->member)), *(ptr))) \
+                       (*ptr) = 1;                                     \
+                                                                       \
+               switch (sizeof(*ptr)) {                                 \
+               case 1:                                                 \
+                       *(ptr) = virtio_cread8(vdev,                    \
+                                              offsetof(structname, member)); \
+                       break;                                          \
+               case 2:                                                 \
+                       *(ptr) = virtio_cread16(vdev,                   \
+                                               offsetof(structname, member)); \
+                       break;                                          \
+               case 4:                                                 \
+                       *(ptr) = virtio_cread32(vdev,                   \
+                                               offsetof(structname, member)); \
+                       break;                                          \
+               case 8:                                                 \
+                       *(ptr) = virtio_cread64(vdev,                   \
+                                               offsetof(structname, member)); \
+                       break;                                          \
+               default:                                                \
+                       BUG();                                          \
+               }                                                       \
+       } while(0)
+
+/* Config space accessors. */
+#define virtio_cwrite(vdev, structname, member, ptr)                   \
+       do {                                                            \
+               /* Must match the member's type, and be integer */      \
+               if (!typecheck(typeof((((structname*)0)->member)), *(ptr))) \
+                       BUG_ON((*ptr) == 1);                            \
+                                                                       \
+               switch (sizeof(*ptr)) {                                 \
+               case 1:                                                 \
+                       virtio_cwrite8(vdev,                            \
+                                      offsetof(structname, member),    \
+                                      *(ptr));                         \
+                       break;                                          \
+               case 2:                                                 \
+                       virtio_cwrite16(vdev,                           \
+                                       offsetof(structname, member),   \
+                                       *(ptr));                        \
+                       break;                                          \
+               case 4:                                                 \
+                       virtio_cwrite32(vdev,                           \
+                                       offsetof(structname, member),   \
+                                       *(ptr));                        \
+                       break;                                          \
+               case 8:                                                 \
+                       virtio_cwrite64(vdev,                           \
+                                       offsetof(structname, member),   \
+                                       *(ptr));                        \
+                       break;                                          \
+               default:                                                \
+                       BUG();                                          \
+               }                                                       \
+       } while(0)
+
+static inline u8 virtio_cread8(struct virtio_device *vdev, unsigned int offset)
+{
+       u8 ret;
+       vdev->config->get(vdev, offset, &ret, sizeof(ret));
+       return ret;
+}
+
+static inline void virtio_cread_bytes(struct virtio_device *vdev,
+                                     unsigned int offset,
+                                     void *buf, size_t len)
+{
+       vdev->config->get(vdev, offset, buf, len);
+}
+
+static inline void virtio_cwrite8(struct virtio_device *vdev,
+                                 unsigned int offset, u8 val)
+{
+       vdev->config->set(vdev, offset, &val, sizeof(val));
+}
+
+static inline u16 virtio_cread16(struct virtio_device *vdev,
+                                unsigned int offset)
+{
+       u16 ret;
+       vdev->config->get(vdev, offset, &ret, sizeof(ret));
+       return ret;
+}
+
+static inline void virtio_cwrite16(struct virtio_device *vdev,
+                                  unsigned int offset, u16 val)
+{
+       vdev->config->set(vdev, offset, &val, sizeof(val));
+}
+
+static inline u32 virtio_cread32(struct virtio_device *vdev,
+                                unsigned int offset)
+{
+       u32 ret;
+       vdev->config->get(vdev, offset, &ret, sizeof(ret));
+       return ret;
+}
+
+static inline void virtio_cwrite32(struct virtio_device *vdev,
+                                  unsigned int offset, u32 val)
+{
+       vdev->config->set(vdev, offset, &val, sizeof(val));
+}
+
+static inline u64 virtio_cread64(struct virtio_device *vdev,
+                                unsigned int offset)
+{
+       u64 ret;
+       vdev->config->get(vdev, offset, &ret, sizeof(ret));
+       return ret;
+}
+
+static inline void virtio_cwrite64(struct virtio_device *vdev,
+                                  unsigned int offset, u64 val)
+{
+       vdev->config->set(vdev, offset, &val, sizeof(val));
+}
+
+/* Conditional config space accessors. */
+#define virtio_cread_feature(vdev, fbit, structname, member, ptr)      \
+       ({                                                              \
+               int _r = 0;                                             \
+               if (!virtio_has_feature(vdev, fbit))                    \
+                       _r = -ENOENT;                                   \
+               else                                                    \
+                       virtio_cread((vdev), structname, member, ptr);  \
+               _r;                                                     \
+       })
 
 #endif /* _LINUX_VIRTIO_CONFIG_H */
index b300787..67e06fe 100644 (file)
@@ -71,7 +71,7 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
                                      struct virtio_device *vdev,
                                      bool weak_barriers,
                                      void *pages,
-                                     void (*notify)(struct virtqueue *vq),
+                                     bool (*notify)(struct virtqueue *vq),
                                      void (*callback)(struct virtqueue *vq),
                                      const char *name);
 void vring_del_virtqueue(struct virtqueue *vq);
index 61939ba..eaa00b1 100644 (file)
@@ -278,6 +278,31 @@ do {                                                                       \
        __ret;                                                          \
 })
 
+#define __wait_event_cmd(wq, condition, cmd1, cmd2)                    \
+       (void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0,  \
+                           cmd1; schedule(); cmd2)
+
+/**
+ * wait_event_cmd - sleep until a condition gets true
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ * cmd1: the command will be executed before sleep
+ * cmd2: the command will be executed after sleep
+ *
+ * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
+ * @condition evaluates to true. The @condition is checked each time
+ * the waitqueue @wq is woken up.
+ *
+ * wake_up() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ */
+#define wait_event_cmd(wq, condition, cmd1, cmd2)                      \
+do {                                                                   \
+       if (condition)                                                  \
+               break;                                                  \
+       __wait_event_cmd(wq, condition, cmd1, cmd2);                    \
+} while (0)
+
 #define __wait_event_interruptible(wq, condition)                      \
        ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0,          \
                      schedule())
diff --git a/include/media/lm3560.h b/include/media/lm3560.h
new file mode 100644 (file)
index 0000000..4667070
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * include/media/lm3560.h
+ *
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * Contact: Daniel Jeong <gshark.jeong@gmail.com>
+ *                     Ldd-Mlp <ldd-mlp@list.ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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 __LM3560_H__
+#define __LM3560_H__
+
+#include <media/v4l2-subdev.h>
+
+#define LM3560_NAME    "lm3560"
+#define LM3560_I2C_ADDR        (0x53)
+
+/*  FLASH Brightness
+ *     min 62500uA, step 62500uA, max 1000000uA
+ */
+#define LM3560_FLASH_BRT_MIN 62500
+#define LM3560_FLASH_BRT_STEP 62500
+#define LM3560_FLASH_BRT_MAX 1000000
+#define LM3560_FLASH_BRT_uA_TO_REG(a)  \
+       ((a) < LM3560_FLASH_BRT_MIN ? 0 :       \
+        (((a) - LM3560_FLASH_BRT_MIN) / LM3560_FLASH_BRT_STEP))
+#define LM3560_FLASH_BRT_REG_TO_uA(a)          \
+       ((a) * LM3560_FLASH_BRT_STEP + LM3560_FLASH_BRT_MIN)
+
+/*  FLASH TIMEOUT DURATION
+ *     min 32ms, step 32ms, max 1024ms
+ */
+#define LM3560_FLASH_TOUT_MIN 32
+#define LM3560_FLASH_TOUT_STEP 32
+#define LM3560_FLASH_TOUT_MAX 1024
+#define LM3560_FLASH_TOUT_ms_TO_REG(a) \
+       ((a) < LM3560_FLASH_TOUT_MIN ? 0 :      \
+        (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP))
+#define LM3560_FLASH_TOUT_REG_TO_ms(a)         \
+       ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN)
+
+/*  TORCH BRT
+ *     min 31250uA, step 31250uA, max 250000uA
+ */
+#define LM3560_TORCH_BRT_MIN 31250
+#define LM3560_TORCH_BRT_STEP 31250
+#define LM3560_TORCH_BRT_MAX 250000
+#define LM3560_TORCH_BRT_uA_TO_REG(a)  \
+       ((a) < LM3560_TORCH_BRT_MIN ? 0 :       \
+        (((a) - LM3560_TORCH_BRT_MIN) / LM3560_TORCH_BRT_STEP))
+#define LM3560_TORCH_BRT_REG_TO_uA(a)          \
+       ((a) * LM3560_TORCH_BRT_STEP + LM3560_TORCH_BRT_MIN)
+
+enum lm3560_led_id {
+       LM3560_LED0 = 0,
+       LM3560_LED1,
+       LM3560_LED_MAX
+};
+
+enum lm3560_peak_current {
+       LM3560_PEAK_1600mA = 0x00,
+       LM3560_PEAK_2300mA = 0x20,
+       LM3560_PEAK_3000mA = 0x40,
+       LM3560_PEAK_3600mA = 0x60
+};
+
+/* struct lm3560_platform_data
+ *
+ * @peak :  peak current
+ * @max_flash_timeout: flash timeout
+ * @max_flash_brt: flash mode led brightness
+ * @max_torch_brt: torch mode led brightness
+ */
+struct lm3560_platform_data {
+       enum lm3560_peak_current peak;
+
+       u32 max_flash_timeout;
+       u32 max_flash_brt[LM3560_LED_MAX];
+       u32 max_torch_brt[LM3560_LED_MAX];
+};
+
+#endif /* __LM3560_H__ */
index 34d2414..865246b 100644 (file)
@@ -146,9 +146,14 @@ struct soc_camera_subdev_desc {
        /* sensor driver private platform data */
        void *drv_priv;
 
-       /* Optional regulators that have to be managed on power on/off events */
-       struct regulator_bulk_data *regulators;
-       int num_regulators;
+       /*
+        * Set unbalanced_power to true to deal with legacy drivers, failing to
+        * balance their calls to subdevice's .s_power() method. clock_state is
+        * then used internally by helper functions, it shouldn't be touched by
+        * drivers or the platform code.
+        */
+       bool unbalanced_power;
+       unsigned long clock_state;
 
        /* Optional callbacks to power on or off and reset the sensor */
        int (*power)(struct device *, int);
@@ -162,6 +167,9 @@ struct soc_camera_subdev_desc {
        int (*set_bus_param)(struct soc_camera_subdev_desc *, unsigned long flags);
        unsigned long (*query_bus_param)(struct soc_camera_subdev_desc *);
        void (*free_bus)(struct soc_camera_subdev_desc *);
+
+       /* Optional regulators that have to be managed on power on/off events */
+       struct v4l2_subdev_platform_data sd_pdata;
 };
 
 struct soc_camera_host_desc {
@@ -202,9 +210,10 @@ struct soc_camera_link {
 
        void *priv;
 
-       /* Optional regulators that have to be managed on power on/off events */
-       struct regulator_bulk_data *regulators;
-       int num_regulators;
+       /* Set by platforms to handle misbehaving drivers */
+       bool unbalanced_power;
+       /* Used by soc-camera helper functions */
+       unsigned long clock_state;
 
        /* Optional callbacks to power on or off and reset the sensor */
        int (*power)(struct device *, int);
@@ -218,6 +227,12 @@ struct soc_camera_link {
        unsigned long (*query_bus_param)(struct soc_camera_link *);
        void (*free_bus)(struct soc_camera_link *);
 
+       /* Optional regulators that have to be managed on power on/off events */
+       struct regulator_bulk_data *regulators;
+       int num_regulators;
+
+       void *host_priv;
+
        /*
         * Host part - keep at bottom and compatible to
         * struct soc_camera_host_desc
index 0503a90..0b36cc1 100644 (file)
@@ -15,6 +15,7 @@
 #define MEDIA_V4L2_CLK_H
 
 #include <linux/atomic.h>
+#include <linux/export.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
 
@@ -51,4 +52,20 @@ void v4l2_clk_disable(struct v4l2_clk *clk);
 unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk);
 int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate);
 
+struct module;
+
+struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id,
+               const char *id, unsigned long rate, struct module *owner);
+void v4l2_clk_unregister_fixed(struct v4l2_clk *clk);
+
+static inline struct v4l2_clk *v4l2_clk_register_fixed(const char *dev_id,
+                                                       const char *id,
+                                                       unsigned long rate)
+{
+       return __v4l2_clk_register_fixed(dev_id, id, rate, THIS_MODULE);
+}
+
+#define v4l2_clk_name_i2c(name, size, adap, client) snprintf(name, size, \
+                         "%d-%04x", adap, client)
+
 #endif
index 16550c4..48f9748 100644 (file)
@@ -35,7 +35,7 @@
        printk(level "%s %d-%04x: " fmt, name, i2c_adapter_id(adapter), addr , ## arg)
 
 #define v4l_client_printk(level, client, fmt, arg...)                      \
-       v4l_printk(level, (client)->driver->driver.name, (client)->adapter, \
+       v4l_printk(level, (client)->dev.driver->name, (client)->adapter, \
                   (client)->addr, fmt , ## arg)
 
 #define v4l_err(client, fmt, arg...) \
@@ -86,7 +86,7 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
                const char * const *menu_items);
 const char *v4l2_ctrl_get_name(u32 id);
 const char * const *v4l2_ctrl_get_menu(u32 id);
-const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len);
+const s64 *v4l2_ctrl_get_int_menu(u32 id, u32 *len);
 int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def);
 int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu,
                struct v4l2_queryctrl *qctrl, const char * const *menu_items);
index 47ada23..16f7f26 100644 (file)
@@ -571,7 +571,7 @@ static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl)
        mutex_lock(ctrl->handler->lock);
 }
 
-/** v4l2_ctrl_lock() - Helper function to unlock the handler
+/** v4l2_ctrl_unlock() - Helper function to unlock the handler
   * associated with the control.
   * @ctrl:     The control to unlock.
   */
index a62ee18..528cdaf 100644 (file)
@@ -26,7 +26,9 @@
 #ifndef V4L2_FH_H
 #define V4L2_FH_H
 
+#include <linux/fs.h>
 #include <linux/list.h>
+#include <linux/videodev2.h>
 
 struct video_device;
 struct v4l2_ctrl_handler;
index bfda0fe..d67210a 100644 (file)
@@ -559,6 +559,17 @@ struct v4l2_subdev_internal_ops {
 /* Set this flag if this subdev generates events. */
 #define V4L2_SUBDEV_FL_HAS_EVENTS              (1U << 3)
 
+struct regulator_bulk_data;
+
+struct v4l2_subdev_platform_data {
+       /* Optional regulators uset to power on/off the subdevice */
+       struct regulator_bulk_data *regulators;
+       int num_regulators;
+
+       /* Per-subdevice data, specific for a certain video host device */
+       void *host_priv;
+};
+
 /* Each instance of a subdev driver should create this struct, either
    stand-alone or embedded in a larger struct.
  */
@@ -592,6 +603,8 @@ struct v4l2_subdev {
        struct v4l2_async_subdev *asd;
        /* Pointer to the managing notifier. */
        struct v4l2_async_notifier *notifier;
+       /* common part of subdevice platform data */
+       struct v4l2_subdev_platform_data *pdata;
 };
 
 #define media_entity_to_v4l2_subdev(ent) \
@@ -622,13 +635,13 @@ struct v4l2_subdev_fh {
        v4l2_subdev_get_try_##fun_name(struct v4l2_subdev_fh *fh,       \
                                       unsigned int pad)                \
        {                                                               \
-               BUG_ON(unlikely(pad >= vdev_to_v4l2_subdev(             \
-                                       fh->vfh.vdev)->entity.num_pads)); \
+               BUG_ON(pad >= vdev_to_v4l2_subdev(                      \
+                                       fh->vfh.vdev)->entity.num_pads); \
                return &fh->pad[pad].field_name;                        \
        }
 
 __V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, format, try_fmt)
-__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, crop, try_compose)
+__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, crop, try_crop)
 __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, compose, try_compose)
 #endif
 
index 6781258..bd8218b 100644 (file)
@@ -391,7 +391,7 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
 unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait);
 size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
                loff_t *ppos, int nonblock);
-size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
+size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
                loff_t *ppos, int nonblock);
 
 /**
@@ -491,7 +491,7 @@ int vb2_ioctl_expbuf(struct file *file, void *priv,
 
 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,
+ssize_t vb2_fop_write(struct file *file, const 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);
index 0038526..7b89852 100644 (file)
 
 #include <media/videobuf2-core.h>
 
-struct vb2_dma_sg_desc {
-       unsigned long           size;
-       unsigned int            num_pages;
-       struct scatterlist      *sglist;
-};
-
-static inline struct vb2_dma_sg_desc *vb2_dma_sg_plane_desc(
+static inline struct sg_table *vb2_dma_sg_plane_desc(
                struct vb2_buffer *vb, unsigned int plane_no)
 {
-       return (struct vb2_dma_sg_desc *)vb2_plane_cookie(vb, plane_no);
+       return (struct sg_table *)vb2_plane_cookie(vb, plane_no);
 }
 
 extern const struct vb2_mem_ops vb2_dma_sg_memops;
index 5132990..c853b16 100644 (file)
@@ -240,7 +240,7 @@ struct l2cap_conn_rsp {
 #define L2CAP_PSM_RFCOMM       0x0003
 #define L2CAP_PSM_3DSP         0x0021
 
-/* channel indentifier */
+/* channel identifier */
 #define L2CAP_CID_SIGNALING    0x0001
 #define L2CAP_CID_CONN_LESS    0x0002
 #define L2CAP_CID_A2MP         0x0003
index 9b787b6..1b177ed 100644 (file)
 /**
  * struct genl_multicast_group - generic netlink multicast group
  * @name: name of the multicast group, names are per-family
- * @id: multicast group ID, assigned by the core, to use with
- *      genlmsg_multicast().
- * @list: list entry for linking
- * @family: pointer to family, need not be set before registering
  */
 struct genl_multicast_group {
-       struct genl_family      *family;        /* private */
-       struct list_head        list;           /* private */
        char                    name[GENL_NAMSIZ];
-       u32                     id;
 };
 
 struct genl_ops;
@@ -39,9 +32,12 @@ struct genl_info;
  * @post_doit: called after an operation's doit callback, it may
  *     undo operations done by pre_doit, for example release locks
  * @attrbuf: buffer to store parsed attributes
- * @ops_list: list of all assigned operations
  * @family_list: family list
- * @mcast_groups: multicast groups list
+ * @mcgrps: multicast groups used by this family (private)
+ * @n_mcgrps: number of multicast groups (private)
+ * @mcgrp_offset: starting number of multicast group IDs in this family
+ * @ops: the operations supported by this family (private)
+ * @n_ops: number of operations supported by this family (private)
  */
 struct genl_family {
        unsigned int            id;
@@ -51,16 +47,19 @@ struct genl_family {
        unsigned int            maxattr;
        bool                    netnsok;
        bool                    parallel_ops;
-       int                     (*pre_doit)(struct genl_ops *ops,
+       int                     (*pre_doit)(const struct genl_ops *ops,
                                            struct sk_buff *skb,
                                            struct genl_info *info);
-       void                    (*post_doit)(struct genl_ops *ops,
+       void                    (*post_doit)(const struct genl_ops *ops,
                                             struct sk_buff *skb,
                                             struct genl_info *info);
        struct nlattr **        attrbuf;        /* private */
-       struct list_head        ops_list;       /* private */
+       const struct genl_ops * ops;            /* private */
+       const struct genl_multicast_group *mcgrps; /* private */
+       unsigned int            n_ops;          /* private */
+       unsigned int            n_mcgrps;       /* private */
+       unsigned int            mcgrp_offset;   /* private */
        struct list_head        family_list;    /* private */
-       struct list_head        mcast_groups;   /* private */
        struct module           *module;
 };
 
@@ -110,16 +109,15 @@ static inline void genl_info_net_set(struct genl_info *info, struct net *net)
  * @ops_list: operations list
  */
 struct genl_ops {
-       u8                      cmd;
-       u8                      internal_flags;
-       unsigned int            flags;
        const struct nla_policy *policy;
        int                    (*doit)(struct sk_buff *skb,
                                       struct genl_info *info);
        int                    (*dumpit)(struct sk_buff *skb,
                                         struct netlink_callback *cb);
        int                    (*done)(struct netlink_callback *cb);
-       struct list_head        ops_list;
+       u8                      cmd;
+       u8                      internal_flags;
+       u8                      flags;
 };
 
 int __genl_register_family(struct genl_family *family);
@@ -130,24 +128,53 @@ static inline int genl_register_family(struct genl_family *family)
        return __genl_register_family(family);
 }
 
-int __genl_register_family_with_ops(struct genl_family *family,
-                                   struct genl_ops *ops, size_t n_ops);
-
-static inline int genl_register_family_with_ops(struct genl_family *family,
-       struct genl_ops *ops, size_t n_ops)
+/**
+ * genl_register_family_with_ops - register a generic netlink family with ops
+ * @family: generic netlink family
+ * @ops: operations to be registered
+ * @n_ops: number of elements to register
+ *
+ * Registers the specified family and operations from the specified table.
+ * Only one family may be registered with the same family name or identifier.
+ *
+ * The family id may equal GENL_ID_GENERATE causing an unique id to
+ * be automatically generated and assigned.
+ *
+ * Either a doit or dumpit callback must be specified for every registered
+ * operation or the function will fail. Only one operation structure per
+ * command identifier may be registered.
+ *
+ * See include/net/genetlink.h for more documenation on the operations
+ * structure.
+ *
+ * Return 0 on success or a negative error code.
+ */
+static inline int
+_genl_register_family_with_ops_grps(struct genl_family *family,
+                                   const struct genl_ops *ops, size_t n_ops,
+                                   const struct genl_multicast_group *mcgrps,
+                                   size_t n_mcgrps)
 {
        family->module = THIS_MODULE;
-       return __genl_register_family_with_ops(family, ops, n_ops);
+       family->ops = ops;
+       family->n_ops = n_ops;
+       family->mcgrps = mcgrps;
+       family->n_mcgrps = n_mcgrps;
+       return __genl_register_family(family);
 }
 
+#define genl_register_family_with_ops(family, ops)                     \
+       _genl_register_family_with_ops_grps((family),                   \
+                                           (ops), ARRAY_SIZE(ops),     \
+                                           NULL, 0)
+#define genl_register_family_with_ops_groups(family, ops, grps)        \
+       _genl_register_family_with_ops_grps((family),                   \
+                                           (ops), ARRAY_SIZE(ops),     \
+                                           (grps), ARRAY_SIZE(grps))
+
 int genl_unregister_family(struct genl_family *family);
-int genl_register_ops(struct genl_family *, struct genl_ops *ops);
-int genl_unregister_ops(struct genl_family *, struct genl_ops *ops);
-int genl_register_mc_group(struct genl_family *family,
-                          struct genl_multicast_group *grp);
-void genl_unregister_mc_group(struct genl_family *family,
-                             struct genl_multicast_group *grp);
-void genl_notify(struct sk_buff *skb, struct net *net, u32 portid,
+void genl_notify(struct genl_family *family,
+                struct sk_buff *skb, struct net *net, u32 portid,
                 u32 group, struct nlmsghdr *nlh, gfp_t flags);
 
 void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
@@ -227,41 +254,51 @@ static inline void genlmsg_cancel(struct sk_buff *skb, void *hdr)
 
 /**
  * genlmsg_multicast_netns - multicast a netlink message to a specific netns
+ * @family: the generic netlink family
  * @net: the net namespace
  * @skb: netlink message as socket buffer
  * @portid: own netlink portid to avoid sending to yourself
- * @group: multicast group id
+ * @group: offset of multicast group in groups array
  * @flags: allocation flags
  */
-static inline int genlmsg_multicast_netns(struct net *net, struct sk_buff *skb,
+static inline int genlmsg_multicast_netns(struct genl_family *family,
+                                         struct net *net, struct sk_buff *skb,
                                          u32 portid, unsigned int group, gfp_t flags)
 {
+       if (WARN_ON_ONCE(group >= family->n_mcgrps))
+               return -EINVAL;
+       group = family->mcgrp_offset + group;
        return nlmsg_multicast(net->genl_sock, skb, portid, group, flags);
 }
 
 /**
  * genlmsg_multicast - multicast a netlink message to the default netns
+ * @family: the generic netlink family
  * @skb: netlink message as socket buffer
  * @portid: own netlink portid to avoid sending to yourself
- * @group: multicast group id
+ * @group: offset of multicast group in groups array
  * @flags: allocation flags
  */
-static inline int genlmsg_multicast(struct sk_buff *skb, u32 portid,
+static inline int genlmsg_multicast(struct genl_family *family,
+                                   struct sk_buff *skb, u32 portid,
                                    unsigned int group, gfp_t flags)
 {
-       return genlmsg_multicast_netns(&init_net, skb, portid, group, flags);
+       return genlmsg_multicast_netns(family, &init_net, skb,
+                                      portid, group, flags);
 }
 
 /**
  * genlmsg_multicast_allns - multicast a netlink message to all net namespaces
+ * @family: the generic netlink family
  * @skb: netlink message as socket buffer
  * @portid: own netlink portid to avoid sending to yourself
- * @group: multicast group id
+ * @group: offset of multicast group in groups array
  * @flags: allocation flags
  *
  * This function must hold the RTNL or rcu_read_lock().
  */
-int genlmsg_multicast_allns(struct sk_buff *skb, u32 portid,
+int genlmsg_multicast_allns(struct genl_family *family,
+                           struct sk_buff *skb, u32 portid,
                            unsigned int group, gfp_t flags);
 
 /**
@@ -332,5 +369,25 @@ static inline struct sk_buff *genlmsg_new(size_t payload, gfp_t flags)
        return nlmsg_new(genlmsg_total_size(payload), flags);
 }
 
+/**
+ * genl_set_err - report error to genetlink broadcast listeners
+ * @family: the generic netlink family
+ * @net: the network namespace to report the error to
+ * @portid: the PORTID of a process that we want to skip (if any)
+ * @group: the broadcast group that will notice the error
+ *     (this is the offset of the multicast group in the groups array)
+ * @code: error code, must be negative (as usual in kernelspace)
+ *
+ * This function returns the number of broadcast listeners that have set the
+ * NETLINK_RECV_NO_ENOBUFS socket option.
+ */
+static inline int genl_set_err(struct genl_family *family, struct net *net,
+                              u32 portid, u32 group, int code)
+{
+       if (WARN_ON_ONCE(group >= family->n_mcgrps))
+               return -EINVAL;
+       group = family->mcgrp_offset + group;
+       return netlink_set_err(net->genl_sock, portid, group, code);
+}
 
 #endif /* __NET_GENERIC_NETLINK_H */
index e393171..979874c 100644 (file)
@@ -67,12 +67,14 @@ enum rdma_node_type {
        RDMA_NODE_IB_CA         = 1,
        RDMA_NODE_IB_SWITCH,
        RDMA_NODE_IB_ROUTER,
-       RDMA_NODE_RNIC
+       RDMA_NODE_RNIC,
+       RDMA_NODE_USNIC,
 };
 
 enum rdma_transport_type {
        RDMA_TRANSPORT_IB,
-       RDMA_TRANSPORT_IWARP
+       RDMA_TRANSPORT_IWARP,
+       RDMA_TRANSPORT_USNIC
 };
 
 enum rdma_transport_type
@@ -1436,6 +1438,7 @@ struct ib_device {
 
        int                          uverbs_abi_ver;
        u64                          uverbs_cmd_mask;
+       u64                          uverbs_ex_cmd_mask;
 
        char                         node_desc[64];
        __be64                       node_guid;
@@ -2384,4 +2387,17 @@ struct ib_flow *ib_create_flow(struct ib_qp *qp,
                               struct ib_flow_attr *flow_attr, int domain);
 int ib_destroy_flow(struct ib_flow *flow_id);
 
+static inline int ib_check_mr_access(int flags)
+{
+       /*
+        * Local write permission is required if remote write or
+        * remote atomic permission is also requested.
+        */
+       if (flags & (IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_REMOTE_WRITE) &&
+           !(flags & IB_ACCESS_LOCAL_WRITE))
+               return -EINVAL;
+
+       return 0;
+}
+
 #endif /* IB_VERBS_H */
index ff0f04a..4ebf691 100644 (file)
@@ -13,6 +13,27 @@ struct srp_rport_identifiers {
        u8 roles;
 };
 
+/**
+ * enum srp_rport_state - SRP transport layer state
+ * @SRP_RPORT_RUNNING:   Transport layer operational.
+ * @SRP_RPORT_BLOCKED:   Transport layer not operational; fast I/O fail timer
+ *                       is running and I/O has been blocked.
+ * @SRP_RPORT_FAIL_FAST: Fast I/O fail timer has expired; fail I/O fast.
+ * @SRP_RPORT_LOST:      Device loss timer has expired; port is being removed.
+ */
+enum srp_rport_state {
+       SRP_RPORT_RUNNING,
+       SRP_RPORT_BLOCKED,
+       SRP_RPORT_FAIL_FAST,
+       SRP_RPORT_LOST,
+};
+
+/**
+ * struct srp_rport
+ * @lld_data: LLD private data.
+ * @mutex:    Protects against concurrent rport reconnect / fast_io_fail /
+ *   dev_loss_tmo activity.
+ */
 struct srp_rport {
        /* for initiator and target drivers */
 
@@ -23,11 +44,43 @@ struct srp_rport {
 
        /* for initiator drivers */
 
-       void *lld_data; /* LLD private data */
+       void                    *lld_data;
+
+       struct mutex            mutex;
+       enum srp_rport_state    state;
+       bool                    deleted;
+       int                     reconnect_delay;
+       int                     failed_reconnects;
+       struct delayed_work     reconnect_work;
+       int                     fast_io_fail_tmo;
+       int                     dev_loss_tmo;
+       struct delayed_work     fast_io_fail_work;
+       struct delayed_work     dev_loss_work;
 };
 
+/**
+ * struct srp_function_template
+ * @has_rport_state: Whether or not to create the state, fast_io_fail_tmo and
+ *     dev_loss_tmo sysfs attribute for an rport.
+ * @reset_timer_if_blocked: Whether or srp_timed_out() should reset the command
+ *     timer if the device on which it has been queued is blocked.
+ * @reconnect_delay: If not NULL, points to the default reconnect_delay value.
+ * @fast_io_fail_tmo: If not NULL, points to the default fast_io_fail_tmo value.
+ * @dev_loss_tmo: If not NULL, points to the default dev_loss_tmo value.
+ * @reconnect: Callback function for reconnecting to the target. See also
+ *     srp_reconnect_rport().
+ * @terminate_rport_io: Callback function for terminating all outstanding I/O
+ *     requests for an rport.
+ */
 struct srp_function_template {
        /* for initiator drivers */
+       bool has_rport_state;
+       bool reset_timer_if_blocked;
+       int *reconnect_delay;
+       int *fast_io_fail_tmo;
+       int *dev_loss_tmo;
+       int (*reconnect)(struct srp_rport *rport);
+       void (*terminate_rport_io)(struct srp_rport *rport);
        void (*rport_delete)(struct srp_rport *rport);
        /* for target drivers */
        int (* tsk_mgmt_response)(struct Scsi_Host *, u64, u64, int);
@@ -38,10 +91,36 @@ extern struct scsi_transport_template *
 srp_attach_transport(struct srp_function_template *);
 extern void srp_release_transport(struct scsi_transport_template *);
 
+extern void srp_rport_get(struct srp_rport *rport);
+extern void srp_rport_put(struct srp_rport *rport);
 extern struct srp_rport *srp_rport_add(struct Scsi_Host *,
                                       struct srp_rport_identifiers *);
 extern void srp_rport_del(struct srp_rport *);
-
+extern int srp_tmo_valid(int reconnect_delay, int fast_io_fail_tmo,
+                        int dev_loss_tmo);
+extern int srp_reconnect_rport(struct srp_rport *rport);
+extern void srp_start_tl_fail_timers(struct srp_rport *rport);
 extern void srp_remove_host(struct Scsi_Host *);
 
+/**
+ * srp_chkready() - evaluate the transport layer state before I/O
+ *
+ * Returns a SCSI result code that can be returned by the LLD queuecommand()
+ * implementation. The role of this function is similar to that of
+ * fc_remote_port_chkready().
+ */
+static inline int srp_chkready(struct srp_rport *rport)
+{
+       switch (rport->state) {
+       case SRP_RPORT_RUNNING:
+       case SRP_RPORT_BLOCKED:
+       default:
+               return 0;
+       case SRP_RPORT_FAIL_FAST:
+               return DID_TRANSPORT_FAILFAST << 16;
+       case SRP_RPORT_LOST:
+               return DID_NO_CONNECT << 16;
+       }
+}
+
 #endif
index 5ebe21c..39e0114 100644 (file)
@@ -34,6 +34,11 @@ struct se_subsystem_api {
        sense_reason_t (*parse_cdb)(struct se_cmd *cmd);
        u32 (*get_device_type)(struct se_device *);
        sector_t (*get_blocks)(struct se_device *);
+       sector_t (*get_alignment_offset_lbas)(struct se_device *);
+       /* lbppbe = logical blocks per physical block exponent. see SBC-3 */
+       unsigned int (*get_lbppbe)(struct se_device *);
+       unsigned int (*get_io_min)(struct se_device *);
+       unsigned int (*get_io_opt)(struct se_device *);
        unsigned char *(*get_sense_buffer)(struct se_cmd *);
        bool (*get_write_cache)(struct se_device *);
 };
index 5bdb8b7..45412a6 100644 (file)
@@ -227,6 +227,7 @@ enum tcm_tmreq_table {
 
 /* fabric independent task management response values */
 enum tcm_tmrsp_table {
+       TMR_FUNCTION_FAILED             = 0,
        TMR_FUNCTION_COMPLETE           = 1,
        TMR_TASK_DOES_NOT_EXIST         = 2,
        TMR_LUN_DOES_NOT_EXIST          = 3,
@@ -282,11 +283,12 @@ struct t10_alua_lu_gp_member {
 struct t10_alua_tg_pt_gp {
        u16     tg_pt_gp_id;
        int     tg_pt_gp_valid_id;
+       int     tg_pt_gp_alua_supported_states;
        int     tg_pt_gp_alua_access_status;
        int     tg_pt_gp_alua_access_type;
        int     tg_pt_gp_nonop_delay_msecs;
        int     tg_pt_gp_trans_delay_msecs;
-       int     tg_pt_gp_implict_trans_secs;
+       int     tg_pt_gp_implicit_trans_secs;
        int     tg_pt_gp_pref;
        int     tg_pt_gp_write_metadata;
        /* Used by struct t10_alua_tg_pt_gp->tg_pt_gp_md_buf_len */
@@ -442,7 +444,6 @@ struct se_cmd {
        /* Used for sense data */
        void                    *sense_buffer;
        struct list_head        se_delayed_node;
-       struct list_head        se_lun_node;
        struct list_head        se_qf_node;
        struct se_device      *se_dev;
        struct se_dev_entry   *se_deve;
@@ -470,15 +471,11 @@ struct se_cmd {
 #define CMD_T_SENT             (1 << 4)
 #define CMD_T_STOP             (1 << 5)
 #define CMD_T_FAILED           (1 << 6)
-#define CMD_T_LUN_STOP         (1 << 7)
-#define CMD_T_LUN_FE_STOP      (1 << 8)
-#define CMD_T_DEV_ACTIVE       (1 << 9)
-#define CMD_T_REQUEST_STOP     (1 << 10)
-#define CMD_T_BUSY             (1 << 11)
+#define CMD_T_DEV_ACTIVE       (1 << 7)
+#define CMD_T_REQUEST_STOP     (1 << 8)
+#define CMD_T_BUSY             (1 << 9)
        spinlock_t              t_state_lock;
        struct completion       t_transport_stop_comp;
-       struct completion       transport_lun_fe_stop_comp;
-       struct completion       transport_lun_stop_comp;
 
        struct work_struct      work;
 
@@ -498,6 +495,9 @@ struct se_cmd {
 
        /* backend private data */
        void                    *priv;
+
+       /* Used for lun->lun_ref counting */
+       bool                    lun_ref_active;
 };
 
 struct se_ua {
@@ -628,6 +628,34 @@ struct se_dev_attrib {
        struct config_group da_group;
 };
 
+struct se_port_stat_grps {
+       struct config_group stat_group;
+       struct config_group scsi_port_group;
+       struct config_group scsi_tgt_port_group;
+       struct config_group scsi_transport_group;
+};
+
+struct se_lun {
+#define SE_LUN_LINK_MAGIC                      0xffff7771
+       u32                     lun_link_magic;
+       /* See transport_lun_status_table */
+       enum transport_lun_status_table lun_status;
+       u32                     lun_access;
+       u32                     lun_flags;
+       u32                     unpacked_lun;
+       atomic_t                lun_acl_count;
+       spinlock_t              lun_acl_lock;
+       spinlock_t              lun_sep_lock;
+       struct completion       lun_shutdown_comp;
+       struct list_head        lun_acl_list;
+       struct se_device        *lun_se_dev;
+       struct se_port          *lun_sep;
+       struct config_group     lun_group;
+       struct se_port_stat_grps port_stat_grps;
+       struct completion       lun_ref_comp;
+       struct percpu_ref       lun_ref;
+};
+
 struct se_dev_stat_grps {
        struct config_group stat_group;
        struct config_group scsi_dev_group;
@@ -656,11 +684,10 @@ struct se_device {
        /* Pointer to transport specific device structure */
        u32                     dev_index;
        u64                     creation_time;
-       u32                     num_resets;
-       u64                     num_cmds;
-       u64                     read_bytes;
-       u64                     write_bytes;
-       spinlock_t              stats_lock;
+       atomic_long_t           num_resets;
+       atomic_long_t           num_cmds;
+       atomic_long_t           read_bytes;
+       atomic_long_t           write_bytes;
        /* Active commands on this virtual SE device */
        atomic_t                simple_cmds;
        atomic_t                dev_ordered_id;
@@ -711,6 +738,7 @@ struct se_device {
        struct se_subsystem_api *transport;
        /* Linked list for struct se_hba struct se_device list */
        struct list_head        dev_list;
+       struct se_lun           xcopy_lun;
 };
 
 struct se_hba {
@@ -730,34 +758,6 @@ struct se_hba {
        struct se_subsystem_api *transport;
 };
 
-struct se_port_stat_grps {
-       struct config_group stat_group;
-       struct config_group scsi_port_group;
-       struct config_group scsi_tgt_port_group;
-       struct config_group scsi_transport_group;
-};
-
-struct se_lun {
-#define SE_LUN_LINK_MAGIC                      0xffff7771
-       u32                     lun_link_magic;
-       /* See transport_lun_status_table */
-       enum transport_lun_status_table lun_status;
-       u32                     lun_access;
-       u32                     lun_flags;
-       u32                     unpacked_lun;
-       atomic_t                lun_acl_count;
-       spinlock_t              lun_acl_lock;
-       spinlock_t              lun_cmd_lock;
-       spinlock_t              lun_sep_lock;
-       struct completion       lun_shutdown_comp;
-       struct list_head        lun_cmd_list;
-       struct list_head        lun_acl_list;
-       struct se_device        *lun_se_dev;
-       struct se_port          *lun_sep;
-       struct config_group     lun_group;
-       struct se_port_stat_grps port_stat_grps;
-};
-
 struct scsi_port_stats {
        u64     cmd_pdus;
        u64     tx_data_octets;
index 713c500..e080138 100644 (file)
@@ -54,4 +54,3 @@ struct target_fabric_configfs {
        struct target_fabric_configfs_template tf_cit_tmpl;
 };
 
-#define TF_CIT_TMPL(tf) (&(tf)->tf_cit_tmpl)
index 882b650..4cf4fda 100644 (file)
@@ -137,6 +137,8 @@ void        transport_generic_request_failure(struct se_cmd *, sense_reason_t);
 void   __target_execute_cmd(struct se_cmd *);
 int    transport_lookup_tmr_lun(struct se_cmd *, u32);
 
+struct se_node_acl *core_tpg_get_initiator_node_acl(struct se_portal_group *tpg,
+               unsigned char *);
 struct se_node_acl *core_tpg_check_initiator_node_acl(struct se_portal_group *,
                unsigned char *);
 void   core_tpg_clear_object_luns(struct se_portal_group *);
index 5ebda97..e2b9576 100644 (file)
@@ -6,11 +6,9 @@
 
 #include <linux/tracepoint.h>
 
-struct search;
-
 DECLARE_EVENT_CLASS(bcache_request,
-       TP_PROTO(struct search *s, struct bio *bio),
-       TP_ARGS(s, bio),
+       TP_PROTO(struct bcache_device *d, struct bio *bio),
+       TP_ARGS(d, bio),
 
        TP_STRUCT__entry(
                __field(dev_t,          dev                     )
@@ -24,8 +22,8 @@ DECLARE_EVENT_CLASS(bcache_request,
 
        TP_fast_assign(
                __entry->dev            = bio->bi_bdev->bd_dev;
-               __entry->orig_major     = s->d->disk->major;
-               __entry->orig_minor     = s->d->disk->first_minor;
+               __entry->orig_major     = d->disk->major;
+               __entry->orig_minor     = d->disk->first_minor;
                __entry->sector         = bio->bi_sector;
                __entry->orig_sector    = bio->bi_sector - 16;
                __entry->nr_sector      = bio->bi_size >> 9;
@@ -79,13 +77,13 @@ DECLARE_EVENT_CLASS(btree_node,
 /* request.c */
 
 DEFINE_EVENT(bcache_request, bcache_request_start,
-       TP_PROTO(struct search *s, struct bio *bio),
-       TP_ARGS(s, bio)
+       TP_PROTO(struct bcache_device *d, struct bio *bio),
+       TP_ARGS(d, bio)
 );
 
 DEFINE_EVENT(bcache_request, bcache_request_end,
-       TP_PROTO(struct search *s, struct bio *bio),
-       TP_ARGS(s, bio)
+       TP_PROTO(struct bcache_device *d, struct bio *bio),
+       TP_ARGS(d, bio)
 );
 
 DECLARE_EVENT_CLASS(bcache_bio,
@@ -370,6 +368,35 @@ DEFINE_EVENT(btree_node, bcache_btree_set_root,
        TP_ARGS(b)
 );
 
+TRACE_EVENT(bcache_keyscan,
+       TP_PROTO(unsigned nr_found,
+                unsigned start_inode, uint64_t start_offset,
+                unsigned end_inode, uint64_t end_offset),
+       TP_ARGS(nr_found,
+               start_inode, start_offset,
+               end_inode, end_offset),
+
+       TP_STRUCT__entry(
+               __field(__u32,  nr_found                        )
+               __field(__u32,  start_inode                     )
+               __field(__u64,  start_offset                    )
+               __field(__u32,  end_inode                       )
+               __field(__u64,  end_offset                      )
+       ),
+
+       TP_fast_assign(
+               __entry->nr_found       = nr_found;
+               __entry->start_inode    = start_inode;
+               __entry->start_offset   = start_offset;
+               __entry->end_inode      = end_inode;
+               __entry->end_offset     = end_offset;
+       ),
+
+       TP_printk("found %u keys from %u:%llu to %u:%llu", __entry->nr_found,
+                 __entry->start_inode, __entry->start_offset,
+                 __entry->end_inode, __entry->end_offset)
+);
+
 /* Allocator */
 
 TRACE_EVENT(bcache_alloc_invalidate,
index f18b3b7..4832d75 100644 (file)
@@ -162,12 +162,14 @@ DEFINE_EVENT(btrfs__inode, btrfs_inode_evict,
                { EXTENT_FLAG_LOGGING,          "LOGGING"       },      \
                { EXTENT_FLAG_FILLING,          "FILLING"       })
 
-TRACE_EVENT(btrfs_get_extent,
+TRACE_EVENT_CONDITION(btrfs_get_extent,
 
        TP_PROTO(struct btrfs_root *root, struct extent_map *map),
 
        TP_ARGS(root, map),
 
+       TP_CONDITION(map),
+
        TP_STRUCT__entry(
                __field(        u64,  root_objectid     )
                __field(        u64,  start             )
diff --git a/include/trace/events/iommu.h b/include/trace/events/iommu.h
new file mode 100644 (file)
index 0000000..a8f5c32
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * iommu trace points
+ *
+ * Copyright (C) 2013 Shuah Khan <shuah.kh@samsung.com>
+ *
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iommu
+
+#if !defined(_TRACE_IOMMU_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_IOMMU_H
+
+#include <linux/tracepoint.h>
+#include <linux/pci.h>
+
+struct device;
+
+DECLARE_EVENT_CLASS(iommu_group_event,
+
+       TP_PROTO(int group_id, struct device *dev),
+
+       TP_ARGS(group_id, dev),
+
+       TP_STRUCT__entry(
+               __field(int, gid)
+               __string(device, dev_name(dev))
+       ),
+
+       TP_fast_assign(
+               __entry->gid = group_id;
+               __assign_str(device, dev_name(dev));
+       ),
+
+       TP_printk("IOMMU: groupID=%d device=%s",
+                       __entry->gid, __get_str(device)
+       )
+);
+
+DEFINE_EVENT(iommu_group_event, add_device_to_group,
+
+       TP_PROTO(int group_id, struct device *dev),
+
+       TP_ARGS(group_id, dev)
+
+);
+
+DEFINE_EVENT(iommu_group_event, remove_device_from_group,
+
+       TP_PROTO(int group_id, struct device *dev),
+
+       TP_ARGS(group_id, dev)
+);
+
+DECLARE_EVENT_CLASS(iommu_device_event,
+
+       TP_PROTO(struct device *dev),
+
+       TP_ARGS(dev),
+
+       TP_STRUCT__entry(
+               __string(device, dev_name(dev))
+       ),
+
+       TP_fast_assign(
+               __assign_str(device, dev_name(dev));
+       ),
+
+       TP_printk("IOMMU: device=%s", __get_str(device)
+       )
+);
+
+DEFINE_EVENT(iommu_device_event, attach_device_to_domain,
+
+       TP_PROTO(struct device *dev),
+
+       TP_ARGS(dev)
+);
+
+DEFINE_EVENT(iommu_device_event, detach_device_from_domain,
+
+       TP_PROTO(struct device *dev),
+
+       TP_ARGS(dev)
+);
+
+DECLARE_EVENT_CLASS(iommu_map_unmap,
+
+       TP_PROTO(unsigned long iova, phys_addr_t paddr, size_t size),
+
+       TP_ARGS(iova, paddr, size),
+
+       TP_STRUCT__entry(
+               __field(u64, iova)
+               __field(u64, paddr)
+               __field(int, size)
+       ),
+
+       TP_fast_assign(
+               __entry->iova = iova;
+               __entry->paddr = paddr;
+               __entry->size = size;
+       ),
+
+       TP_printk("IOMMU: iova=0x%016llx paddr=0x%016llx size=0x%x",
+                       __entry->iova, __entry->paddr, __entry->size
+       )
+);
+
+DEFINE_EVENT(iommu_map_unmap, map,
+
+       TP_PROTO(unsigned long iova, phys_addr_t paddr, size_t size),
+
+       TP_ARGS(iova, paddr, size)
+);
+
+DEFINE_EVENT_PRINT(iommu_map_unmap, unmap,
+
+       TP_PROTO(unsigned long iova, phys_addr_t paddr, size_t size),
+
+       TP_ARGS(iova, paddr, size),
+
+       TP_printk("IOMMU: iova=0x%016llx size=0x%x",
+                       __entry->iova, __entry->size
+       )
+);
+
+DECLARE_EVENT_CLASS(iommu_error,
+
+       TP_PROTO(struct device *dev, unsigned long iova, int flags),
+
+       TP_ARGS(dev, iova, flags),
+
+       TP_STRUCT__entry(
+               __string(device, dev_name(dev))
+               __string(driver, dev_driver_string(dev))
+               __field(u64, iova)
+               __field(int, flags)
+       ),
+
+       TP_fast_assign(
+               __assign_str(device, dev_name(dev));
+               __assign_str(driver, dev_driver_string(dev));
+               __entry->iova = iova;
+               __entry->flags = flags;
+       ),
+
+       TP_printk("IOMMU:%s %s iova=0x%016llx flags=0x%04x",
+                       __get_str(driver), __get_str(device),
+                       __entry->iova, __entry->flags
+       )
+);
+
+DEFINE_EVENT(iommu_error, io_page_fault,
+
+       TP_PROTO(struct device *dev, unsigned long iova, int flags),
+
+       TP_ARGS(dev, iova, flags)
+);
+#endif /* _TRACE_IOMMU_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 7005d11..131a0bd 100644 (file)
@@ -296,23 +296,21 @@ DEFINE_EVENT(kvm_async_pf_nopresent_ready, kvm_async_pf_ready,
 
 TRACE_EVENT(
        kvm_async_pf_completed,
-       TP_PROTO(unsigned long address, struct page *page, u64 gva),
-       TP_ARGS(address, page, gva),
+       TP_PROTO(unsigned long address, u64 gva),
+       TP_ARGS(address, gva),
 
        TP_STRUCT__entry(
                __field(unsigned long, address)
-               __field(pfn_t, pfn)
                __field(u64, gva)
                ),
 
        TP_fast_assign(
                __entry->address = address;
-               __entry->pfn = page ? page_to_pfn(page) : 0;
                __entry->gva = gva;
                ),
 
-       TP_printk("gva %#llx address %#lx pfn %#llx",  __entry->gva,
-                 __entry->address, __entry->pfn)
+       TP_printk("gva %#llx address %#lx",  __entry->gva,
+                 __entry->address)
 );
 
 #endif
index 422df19..805af6d 100644 (file)
@@ -7,6 +7,25 @@
 #include <linux/writeback.h>
 #include <linux/tracepoint.h>
 
+TRACE_EVENT(add_device_randomness,
+       TP_PROTO(int bytes, unsigned long IP),
+
+       TP_ARGS(bytes, IP),
+
+       TP_STRUCT__entry(
+               __field(          int,  bytes                   )
+               __field(unsigned long,  IP                      )
+       ),
+
+       TP_fast_assign(
+               __entry->bytes          = bytes;
+               __entry->IP             = IP;
+       ),
+
+       TP_printk("bytes %d caller %pF",
+               __entry->bytes, (void *)__entry->IP)
+);
+
 DECLARE_EVENT_CLASS(random__mix_pool_bytes,
        TP_PROTO(const char *pool_name, int bytes, unsigned long IP),
 
@@ -68,7 +87,112 @@ TRACE_EVENT(credit_entropy_bits,
                  (void *)__entry->IP)
 );
 
-TRACE_EVENT(get_random_bytes,
+TRACE_EVENT(push_to_pool,
+       TP_PROTO(const char *pool_name, int pool_bits, int input_bits),
+
+       TP_ARGS(pool_name, pool_bits, input_bits),
+
+       TP_STRUCT__entry(
+               __field( const char *,  pool_name               )
+               __field(          int,  pool_bits               )
+               __field(          int,  input_bits              )
+       ),
+
+       TP_fast_assign(
+               __entry->pool_name      = pool_name;
+               __entry->pool_bits      = pool_bits;
+               __entry->input_bits     = input_bits;
+       ),
+
+       TP_printk("%s: pool_bits %d input_pool_bits %d",
+                 __entry->pool_name, __entry->pool_bits,
+                 __entry->input_bits)
+);
+
+TRACE_EVENT(debit_entropy,
+       TP_PROTO(const char *pool_name, int debit_bits),
+
+       TP_ARGS(pool_name, debit_bits),
+
+       TP_STRUCT__entry(
+               __field( const char *,  pool_name               )
+               __field(          int,  debit_bits              )
+       ),
+
+       TP_fast_assign(
+               __entry->pool_name      = pool_name;
+               __entry->debit_bits     = debit_bits;
+       ),
+
+       TP_printk("%s: debit_bits %d", __entry->pool_name,
+                 __entry->debit_bits)
+);
+
+TRACE_EVENT(add_input_randomness,
+       TP_PROTO(int input_bits),
+
+       TP_ARGS(input_bits),
+
+       TP_STRUCT__entry(
+               __field(          int,  input_bits              )
+       ),
+
+       TP_fast_assign(
+               __entry->input_bits     = input_bits;
+       ),
+
+       TP_printk("input_pool_bits %d", __entry->input_bits)
+);
+
+TRACE_EVENT(add_disk_randomness,
+       TP_PROTO(dev_t dev, int input_bits),
+
+       TP_ARGS(dev, input_bits),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(          int,  input_bits              )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = dev;
+               __entry->input_bits     = input_bits;
+       ),
+
+       TP_printk("dev %d,%d input_pool_bits %d", MAJOR(__entry->dev),
+                 MINOR(__entry->dev), __entry->input_bits)
+);
+
+TRACE_EVENT(xfer_secondary_pool,
+       TP_PROTO(const char *pool_name, int xfer_bits, int request_bits,
+                int pool_entropy, int input_entropy),
+
+       TP_ARGS(pool_name, xfer_bits, request_bits, pool_entropy,
+               input_entropy),
+
+       TP_STRUCT__entry(
+               __field( const char *,  pool_name               )
+               __field(          int,  xfer_bits               )
+               __field(          int,  request_bits            )
+               __field(          int,  pool_entropy            )
+               __field(          int,  input_entropy           )
+       ),
+
+       TP_fast_assign(
+               __entry->pool_name      = pool_name;
+               __entry->xfer_bits      = xfer_bits;
+               __entry->request_bits   = request_bits;
+               __entry->pool_entropy   = pool_entropy;
+               __entry->input_entropy  = input_entropy;
+       ),
+
+       TP_printk("pool %s xfer_bits %d request_bits %d pool_entropy %d "
+                 "input_entropy %d", __entry->pool_name, __entry->xfer_bits,
+                 __entry->request_bits, __entry->pool_entropy,
+                 __entry->input_entropy)
+);
+
+DECLARE_EVENT_CLASS(random__get_random_bytes,
        TP_PROTO(int nbytes, unsigned long IP),
 
        TP_ARGS(nbytes, IP),
@@ -86,6 +210,18 @@ TRACE_EVENT(get_random_bytes,
        TP_printk("nbytes %d caller %pF", __entry->nbytes, (void *)__entry->IP)
 );
 
+DEFINE_EVENT(random__get_random_bytes, get_random_bytes,
+       TP_PROTO(int nbytes, unsigned long IP),
+
+       TP_ARGS(nbytes, IP)
+);
+
+DEFINE_EVENT(random__get_random_bytes, get_random_bytes_arch,
+       TP_PROTO(int nbytes, unsigned long IP),
+
+       TP_ARGS(nbytes, IP)
+);
+
 DECLARE_EVENT_CLASS(random__extract_entropy,
        TP_PROTO(const char *pool_name, int nbytes, int entropy_count,
                 unsigned long IP),
@@ -126,7 +262,52 @@ DEFINE_EVENT(random__extract_entropy, extract_entropy_user,
        TP_ARGS(pool_name, nbytes, entropy_count, IP)
 );
 
+TRACE_EVENT(random_read,
+       TP_PROTO(int got_bits, int need_bits, int pool_left, int input_left),
+
+       TP_ARGS(got_bits, need_bits, pool_left, input_left),
+
+       TP_STRUCT__entry(
+               __field(          int,  got_bits                )
+               __field(          int,  need_bits               )
+               __field(          int,  pool_left               )
+               __field(          int,  input_left              )
+       ),
+
+       TP_fast_assign(
+               __entry->got_bits       = got_bits;
+               __entry->need_bits      = need_bits;
+               __entry->pool_left      = pool_left;
+               __entry->input_left     = input_left;
+       ),
+
+       TP_printk("got_bits %d still_needed_bits %d "
+                 "blocking_pool_entropy_left %d input_entropy_left %d",
+                 __entry->got_bits, __entry->got_bits, __entry->pool_left,
+                 __entry->input_left)
+);
+
+TRACE_EVENT(urandom_read,
+       TP_PROTO(int got_bits, int pool_left, int input_left),
+
+       TP_ARGS(got_bits, pool_left, input_left),
+
+       TP_STRUCT__entry(
+               __field(          int,  got_bits                )
+               __field(          int,  pool_left               )
+               __field(          int,  input_left              )
+       ),
+
+       TP_fast_assign(
+               __entry->got_bits       = got_bits;
+               __entry->pool_left      = pool_left;
+               __entry->input_left     = input_left;
+       ),
 
+       TP_printk("got_bits %d nonblocking_pool_entropy_left %d "
+                 "input_entropy_left %d", __entry->got_bits,
+                 __entry->pool_left, __entry->input_left)
+);
 
 #endif /* _TRACE_RANDOM_H */
 
diff --git a/include/trace/events/swiotlb.h b/include/trace/events/swiotlb.h
new file mode 100644 (file)
index 0000000..7ea4c5e
--- /dev/null
@@ -0,0 +1,46 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM swiotlb
+
+#if !defined(_TRACE_SWIOTLB_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_SWIOTLB_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(swiotlb_bounced,
+
+       TP_PROTO(struct device *dev,
+                dma_addr_t dev_addr,
+                size_t size,
+                int swiotlb_force),
+
+       TP_ARGS(dev, dev_addr, size, swiotlb_force),
+
+       TP_STRUCT__entry(
+               __string(       dev_name,       dev_name(dev)   )
+               __field(        u64,    dma_mask                )
+               __field(        dma_addr_t,     dev_addr        )
+               __field(        size_t, size                    )
+               __field(        int,    swiotlb_force           )
+       ),
+
+       TP_fast_assign(
+               __assign_str(dev_name, dev_name(dev));
+               __entry->dma_mask = (dev->dma_mask ? *dev->dma_mask : 0);
+               __entry->dev_addr = dev_addr;
+               __entry->size = size;
+               __entry->swiotlb_force = swiotlb_force;
+       ),
+
+       TP_printk("dev_name: %s dma_mask=%llx dev_addr=%llx "
+               "size=%zu %s",
+               __get_str(dev_name),
+               __entry->dma_mask,
+               (unsigned long long)__entry->dev_addr,
+               __entry->size,
+               __entry->swiotlb_force ? "swiotlb_force" : "" )
+);
+
+#endif /*  _TRACE_SWIOTLB_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 5c7ab17..52594b2 100644 (file)
@@ -437,9 +437,8 @@ static inline notrace int ftrace_get_offsets_##call(                        \
  *     { <assign>; }  <-- Here we assign the entries by the __field and
  *                        __array macros.
  *
- *     if (!filter_current_check_discard(buffer, event_call, entry, event))
- *             trace_nowake_buffer_unlock_commit(buffer,
- *                                                event, irq_flags, pc);
+ *     if (!filter_check_discard(ftrace_file, entry, buffer, event))
+ *             trace_buffer_unlock_commit(buffer, event, irq_flags, pc);
  * }
  *
  * static struct trace_event ftrace_event_type_<call> = {
@@ -553,7 +552,7 @@ ftrace_raw_event_##call(void *__data, proto)                                \
                                                                        \
        { assign; }                                                     \
                                                                        \
-       if (!filter_current_check_discard(buffer, event_call, entry, event)) \
+       if (!filter_check_discard(ftrace_file, entry, buffer, event))   \
                trace_buffer_unlock_commit(buffer, event, irq_flags, pc); \
 }
 /*
diff --git a/include/uapi/drm/armada_drm.h b/include/uapi/drm/armada_drm.h
new file mode 100644 (file)
index 0000000..8dec3fd
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  With inspiration from the i915 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef DRM_ARMADA_IOCTL_H
+#define DRM_ARMADA_IOCTL_H
+
+#define DRM_ARMADA_GEM_CREATE          0x00
+#define DRM_ARMADA_GEM_MMAP            0x02
+#define DRM_ARMADA_GEM_PWRITE          0x03
+
+#define ARMADA_IOCTL(dir, name, str) \
+       DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
+
+struct drm_armada_gem_create {
+       uint32_t handle;
+       uint32_t size;
+};
+#define DRM_IOCTL_ARMADA_GEM_CREATE \
+       ARMADA_IOCTL(IOWR, GEM_CREATE, gem_create)
+
+struct drm_armada_gem_mmap {
+       uint32_t handle;
+       uint32_t pad;
+       uint64_t offset;
+       uint64_t size;
+       uint64_t addr;
+};
+#define DRM_IOCTL_ARMADA_GEM_MMAP \
+       ARMADA_IOCTL(IOWR, GEM_MMAP, gem_mmap)
+
+struct drm_armada_gem_pwrite {
+       uint64_t ptr;
+       uint32_t handle;
+       uint32_t offset;
+       uint32_t size;
+};
+#define DRM_IOCTL_ARMADA_GEM_PWRITE \
+       ARMADA_IOCTL(IOW, GEM_PWRITE, gem_pwrite)
+
+#endif
index ece8678..9b24d65 100644 (file)
@@ -611,12 +611,37 @@ struct drm_gem_open {
        __u64 size;
 };
 
+#define DRM_CAP_DUMB_BUFFER            0x1
+#define DRM_CAP_VBLANK_HIGH_CRTC       0x2
+#define DRM_CAP_DUMB_PREFERRED_DEPTH   0x3
+#define DRM_CAP_DUMB_PREFER_SHADOW     0x4
+#define DRM_CAP_PRIME                  0x5
+#define  DRM_PRIME_CAP_IMPORT          0x1
+#define  DRM_PRIME_CAP_EXPORT          0x2
+#define DRM_CAP_TIMESTAMP_MONOTONIC    0x6
+#define DRM_CAP_ASYNC_PAGE_FLIP                0x7
+
 /** DRM_IOCTL_GET_CAP ioctl argument type */
 struct drm_get_cap {
        __u64 capability;
        __u64 value;
 };
 
+/**
+ * DRM_CLIENT_CAP_STEREO_3D
+ *
+ * if set to 1, the DRM core will expose the stereo 3D capabilities of the
+ * monitor by advertising the supported 3D layouts in the flags of struct
+ * drm_mode_modeinfo.
+ */
+#define DRM_CLIENT_CAP_STEREO_3D       1
+
+/** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
+struct drm_set_client_cap {
+       __u64 capability;
+       __u64 value;
+};
+
 #define DRM_CLOEXEC O_CLOEXEC
 struct drm_prime_handle {
        __u32 handle;
@@ -649,6 +674,7 @@ struct drm_prime_handle {
 #define DRM_IOCTL_GEM_FLINK            DRM_IOWR(0x0a, struct drm_gem_flink)
 #define DRM_IOCTL_GEM_OPEN             DRM_IOWR(0x0b, struct drm_gem_open)
 #define DRM_IOCTL_GET_CAP              DRM_IOWR(0x0c, struct drm_get_cap)
+#define DRM_IOCTL_SET_CLIENT_CAP       DRM_IOW( 0x0d, struct drm_set_client_cap)
 
 #define DRM_IOCTL_SET_UNIQUE           DRM_IOW( 0x10, struct drm_unique)
 #define DRM_IOCTL_AUTH_MAGIC           DRM_IOW( 0x11, struct drm_auth)
@@ -774,17 +800,6 @@ struct drm_event_vblank {
        __u32 reserved;
 };
 
-#define DRM_CAP_DUMB_BUFFER 0x1
-#define DRM_CAP_VBLANK_HIGH_CRTC 0x2
-#define DRM_CAP_DUMB_PREFERRED_DEPTH 0x3
-#define DRM_CAP_DUMB_PREFER_SHADOW 0x4
-#define DRM_CAP_PRIME 0x5
-#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
-#define DRM_CAP_ASYNC_PAGE_FLIP 0x7
-
-#define DRM_PRIME_CAP_IMPORT 0x1
-#define DRM_PRIME_CAP_EXPORT 0x2
-
 /* typedef area */
 #ifndef __KERNEL__
 typedef struct drm_clip_rect drm_clip_rect_t;
index 28acbaf..f104c26 100644 (file)
 
 /* Video mode flags */
 /* bit compatible with the xorg definitions. */
-#define DRM_MODE_FLAG_PHSYNC   (1<<0)
-#define DRM_MODE_FLAG_NHSYNC   (1<<1)
-#define DRM_MODE_FLAG_PVSYNC   (1<<2)
-#define DRM_MODE_FLAG_NVSYNC   (1<<3)
-#define DRM_MODE_FLAG_INTERLACE        (1<<4)
-#define DRM_MODE_FLAG_DBLSCAN  (1<<5)
-#define DRM_MODE_FLAG_CSYNC    (1<<6)
-#define DRM_MODE_FLAG_PCSYNC   (1<<7)
-#define DRM_MODE_FLAG_NCSYNC   (1<<8)
-#define DRM_MODE_FLAG_HSKEW    (1<<9) /* hskew provided */
-#define DRM_MODE_FLAG_BCAST    (1<<10)
-#define DRM_MODE_FLAG_PIXMUX   (1<<11)
-#define DRM_MODE_FLAG_DBLCLK   (1<<12)
-#define DRM_MODE_FLAG_CLKDIV2  (1<<13)
+#define DRM_MODE_FLAG_PHSYNC                   (1<<0)
+#define DRM_MODE_FLAG_NHSYNC                   (1<<1)
+#define DRM_MODE_FLAG_PVSYNC                   (1<<2)
+#define DRM_MODE_FLAG_NVSYNC                   (1<<3)
+#define DRM_MODE_FLAG_INTERLACE                        (1<<4)
+#define DRM_MODE_FLAG_DBLSCAN                  (1<<5)
+#define DRM_MODE_FLAG_CSYNC                    (1<<6)
+#define DRM_MODE_FLAG_PCSYNC                   (1<<7)
+#define DRM_MODE_FLAG_NCSYNC                   (1<<8)
+#define DRM_MODE_FLAG_HSKEW                    (1<<9) /* hskew provided */
+#define DRM_MODE_FLAG_BCAST                    (1<<10)
+#define DRM_MODE_FLAG_PIXMUX                   (1<<11)
+#define DRM_MODE_FLAG_DBLCLK                   (1<<12)
+#define DRM_MODE_FLAG_CLKDIV2                  (1<<13)
+ /*
+  * When adding a new stereo mode don't forget to adjust DRM_MODE_FLAGS_3D_MAX
+  * (define not exposed to user space).
+  */
+#define DRM_MODE_FLAG_3D_MASK                  (0x1f<<14)
+#define  DRM_MODE_FLAG_3D_NONE                 (0<<14)
+#define  DRM_MODE_FLAG_3D_FRAME_PACKING                (1<<14)
+#define  DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE    (2<<14)
+#define  DRM_MODE_FLAG_3D_LINE_ALTERNATIVE     (3<<14)
+#define  DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL    (4<<14)
+#define  DRM_MODE_FLAG_3D_L_DEPTH              (5<<14)
+#define  DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH        (6<<14)
+#define  DRM_MODE_FLAG_3D_TOP_AND_BOTTOM       (7<<14)
+#define  DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF    (8<<14)
+
 
 /* DPMS flags */
 /* bit compatible with the xorg definitions. */
@@ -165,6 +180,7 @@ struct drm_mode_get_plane_res {
 #define DRM_MODE_ENCODER_LVDS  3
 #define DRM_MODE_ENCODER_TVDAC 4
 #define DRM_MODE_ENCODER_VIRTUAL 5
+#define DRM_MODE_ENCODER_DSI   6
 
 struct drm_mode_get_encoder {
        __u32 encoder_id;
@@ -203,6 +219,7 @@ struct drm_mode_get_encoder {
 #define DRM_MODE_CONNECTOR_TV          13
 #define DRM_MODE_CONNECTOR_eDP         14
 #define DRM_MODE_CONNECTOR_VIRTUAL      15
+#define DRM_MODE_CONNECTOR_DSI         16
 
 struct drm_mode_get_connector {
 
index 55bb572..3a4e97b 100644 (file)
  *
  * I915_L3_PARITY_UEVENT - Generated when the driver receives a parity mismatch
  *     event from the gpu l3 cache. Additional information supplied is ROW,
- *     BANK, SUBBANK of the affected cacheline. Userspace should keep track of
- *     these events and if a specific cache-line seems to have a persistent
- *     error remap it with the l3 remapping tool supplied in intel-gpu-tools.
- *     The value supplied with the event is always 1.
+ *     BANK, SUBBANK, SLICE of the affected cacheline. Userspace should keep
+ *     track of these events and if a specific cache-line seems to have a
+ *     persistent error remap it with the l3 remapping tool supplied in
+ *     intel-gpu-tools.  The value supplied with the event is always 1.
  *
  * I915_ERROR_UEVENT - Generated upon error detection, currently only via
  *     hangcheck. The error detection event is a good indicator of when things
index 73bde4e..5e1ab55 100644 (file)
@@ -19,6 +19,9 @@
 
 #include <drm/drm.h>
 
+#define DRM_TEGRA_GEM_CREATE_TILED     (1 << 0)
+#define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1)
+
 struct drm_tegra_gem_create {
        __u64 size;
        __u32 flags;
@@ -65,6 +68,12 @@ struct drm_tegra_get_syncpt {
        __u32 id;
 };
 
+struct drm_tegra_get_syncpt_base {
+       __u64 context;
+       __u32 syncpt;
+       __u32 id;
+};
+
 struct drm_tegra_syncpt {
        __u32 id;
        __u32 incrs;
@@ -115,15 +124,16 @@ struct drm_tegra_submit {
        __u32 reserved[5];      /* future expansion */
 };
 
-#define DRM_TEGRA_GEM_CREATE   0x00
-#define DRM_TEGRA_GEM_MMAP     0x01
-#define DRM_TEGRA_SYNCPT_READ  0x02
-#define DRM_TEGRA_SYNCPT_INCR  0x03
-#define DRM_TEGRA_SYNCPT_WAIT  0x04
-#define DRM_TEGRA_OPEN_CHANNEL 0x05
-#define DRM_TEGRA_CLOSE_CHANNEL        0x06
-#define DRM_TEGRA_GET_SYNCPT   0x07
-#define DRM_TEGRA_SUBMIT       0x08
+#define DRM_TEGRA_GEM_CREATE           0x00
+#define DRM_TEGRA_GEM_MMAP             0x01
+#define DRM_TEGRA_SYNCPT_READ          0x02
+#define DRM_TEGRA_SYNCPT_INCR          0x03
+#define DRM_TEGRA_SYNCPT_WAIT          0x04
+#define DRM_TEGRA_OPEN_CHANNEL         0x05
+#define DRM_TEGRA_CLOSE_CHANNEL                0x06
+#define DRM_TEGRA_GET_SYNCPT           0x07
+#define DRM_TEGRA_SUBMIT               0x08
+#define DRM_TEGRA_GET_SYNCPT_BASE      0x09
 
 #define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create)
 #define DRM_IOCTL_TEGRA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_MMAP, struct drm_tegra_gem_mmap)
@@ -134,5 +144,6 @@ struct drm_tegra_submit {
 #define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_open_channel)
 #define DRM_IOCTL_TEGRA_GET_SYNCPT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT, struct drm_tegra_get_syncpt)
 #define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit)
+#define DRM_IOCTL_TEGRA_GET_SYNCPT_BASE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT_BASE, struct drm_tegra_get_syncpt_base)
 
 #endif
index db0b825..44b05a0 100644 (file)
@@ -68,6 +68,9 @@
 #define AUDIT_MAKE_EQUIV       1015    /* Append to watched tree */
 #define AUDIT_TTY_GET          1016    /* Get TTY auditing status */
 #define AUDIT_TTY_SET          1017    /* Set TTY auditing status */
+#define AUDIT_SET_FEATURE      1018    /* Turn an audit feature on or off */
+#define AUDIT_GET_FEATURE      1019    /* Get which features are enabled */
+#define AUDIT_FEATURE_CHANGE   1020    /* audit log listing feature changes */
 
 #define AUDIT_FIRST_USER_MSG   1100    /* Userspace messages mostly uninteresting to kernel */
 #define AUDIT_USER_AVC         1107    /* We filter this differently */
@@ -357,6 +360,12 @@ enum {
 #define AUDIT_PERM_READ                4
 #define AUDIT_PERM_ATTR                8
 
+/* MAX_AUDIT_MESSAGE_LENGTH is set in audit:lib/libaudit.h as:
+ * 8970 // PATH_MAX*2+CONTEXT_SIZE*2+11+256+1
+ * max header+body+tailer: 44 + 29 + 32 + 262 + 7 + pad
+ */
+#define AUDIT_MESSAGE_TEXT_MAX 8560
+
 struct audit_status {
        __u32           mask;           /* Bit mask for valid entries */
        __u32           enabled;        /* 1 = enabled, 0 = disabled */
@@ -368,11 +377,28 @@ struct audit_status {
        __u32           backlog;        /* messages waiting in queue */
 };
 
+struct audit_features {
+#define AUDIT_FEATURE_VERSION  1
+       __u32   vers;
+       __u32   mask;           /* which bits we are dealing with */
+       __u32   features;       /* which feature to enable/disable */
+       __u32   lock;           /* which features to lock */
+};
+
+#define AUDIT_FEATURE_ONLY_UNSET_LOGINUID      0
+#define AUDIT_FEATURE_LOGINUID_IMMUTABLE       1
+#define AUDIT_LAST_FEATURE                     AUDIT_FEATURE_LOGINUID_IMMUTABLE
+
+#define audit_feature_valid(x)         ((x) >= 0 && (x) <= AUDIT_LAST_FEATURE)
+#define AUDIT_FEATURE_TO_MASK(x)       (1 << ((x) & 31)) /* mask for __u32 */
+
 struct audit_tty_status {
        __u32           enabled;        /* 1 = enabled, 0 = disabled */
        __u32           log_passwd;     /* 1 = enabled, 0 = disabled */
 };
 
+#define AUDIT_UID_UNSET (unsigned int)-1
+
 /* audit_rule_data supports filter rules with both integer and string
  * fields.  It corresponds with AUDIT_ADD_RULE, AUDIT_DEL_RULE and
  * AUDIT_LIST_RULES requests.
diff --git a/include/uapi/linux/bcache.h b/include/uapi/linux/bcache.h
new file mode 100644 (file)
index 0000000..164a7e2
--- /dev/null
@@ -0,0 +1,373 @@
+#ifndef _LINUX_BCACHE_H
+#define _LINUX_BCACHE_H
+
+/*
+ * Bcache on disk data structures
+ */
+
+#include <asm/types.h>
+
+#define BITMASK(name, type, field, offset, size)               \
+static inline __u64 name(const type *k)                                \
+{ return (k->field >> offset) & ~(~0ULL << size); }            \
+                                                               \
+static inline void SET_##name(type *k, __u64 v)                        \
+{                                                              \
+       k->field &= ~(~(~0ULL << size) << offset);              \
+       k->field |= (v & ~(~0ULL << size)) << offset;           \
+}
+
+/* Btree keys - all units are in sectors */
+
+struct bkey {
+       __u64   high;
+       __u64   low;
+       __u64   ptr[];
+};
+
+#define KEY_FIELD(name, field, offset, size)                           \
+       BITMASK(name, struct bkey, field, offset, size)
+
+#define PTR_FIELD(name, offset, size)                                  \
+static inline __u64 name(const struct bkey *k, unsigned i)             \
+{ return (k->ptr[i] >> offset) & ~(~0ULL << size); }                   \
+                                                                       \
+static inline void SET_##name(struct bkey *k, unsigned i, __u64 v)     \
+{                                                                      \
+       k->ptr[i] &= ~(~(~0ULL << size) << offset);                     \
+       k->ptr[i] |= (v & ~(~0ULL << size)) << offset;                  \
+}
+
+#define KEY_SIZE_BITS          16
+
+KEY_FIELD(KEY_PTRS,    high, 60, 3)
+KEY_FIELD(HEADER_SIZE, high, 58, 2)
+KEY_FIELD(KEY_CSUM,    high, 56, 2)
+KEY_FIELD(KEY_PINNED,  high, 55, 1)
+KEY_FIELD(KEY_DIRTY,   high, 36, 1)
+
+KEY_FIELD(KEY_SIZE,    high, 20, KEY_SIZE_BITS)
+KEY_FIELD(KEY_INODE,   high, 0,  20)
+
+/* Next time I change the on disk format, KEY_OFFSET() won't be 64 bits */
+
+static inline __u64 KEY_OFFSET(const struct bkey *k)
+{
+       return k->low;
+}
+
+static inline void SET_KEY_OFFSET(struct bkey *k, __u64 v)
+{
+       k->low = v;
+}
+
+/*
+ * The high bit being set is a relic from when we used it to do binary
+ * searches - it told you where a key started. It's not used anymore,
+ * and can probably be safely dropped.
+ */
+#define KEY(inode, offset, size)                                       \
+((struct bkey) {                                                       \
+       .high = (1ULL << 63) | ((__u64) (size) << 20) | (inode),        \
+       .low = (offset)                                                 \
+})
+
+#define ZERO_KEY                       KEY(0, 0, 0)
+
+#define MAX_KEY_INODE                  (~(~0 << 20))
+#define MAX_KEY_OFFSET                 (~0ULL >> 1)
+#define MAX_KEY                                KEY(MAX_KEY_INODE, MAX_KEY_OFFSET, 0)
+
+#define KEY_START(k)                   (KEY_OFFSET(k) - KEY_SIZE(k))
+#define START_KEY(k)                   KEY(KEY_INODE(k), KEY_START(k), 0)
+
+#define PTR_DEV_BITS                   12
+
+PTR_FIELD(PTR_DEV,                     51, PTR_DEV_BITS)
+PTR_FIELD(PTR_OFFSET,                  8,  43)
+PTR_FIELD(PTR_GEN,                     0,  8)
+
+#define PTR_CHECK_DEV                  ((1 << PTR_DEV_BITS) - 1)
+
+#define PTR(gen, offset, dev)                                          \
+       ((((__u64) dev) << 51) | ((__u64) offset) << 8 | gen)
+
+/* Bkey utility code */
+
+static inline unsigned long bkey_u64s(const struct bkey *k)
+{
+       return (sizeof(struct bkey) / sizeof(__u64)) + KEY_PTRS(k);
+}
+
+static inline unsigned long bkey_bytes(const struct bkey *k)
+{
+       return bkey_u64s(k) * sizeof(__u64);
+}
+
+#define bkey_copy(_dest, _src) memcpy(_dest, _src, bkey_bytes(_src))
+
+static inline void bkey_copy_key(struct bkey *dest, const struct bkey *src)
+{
+       SET_KEY_INODE(dest, KEY_INODE(src));
+       SET_KEY_OFFSET(dest, KEY_OFFSET(src));
+}
+
+static inline struct bkey *bkey_next(const struct bkey *k)
+{
+       __u64 *d = (void *) k;
+       return (struct bkey *) (d + bkey_u64s(k));
+}
+
+static inline struct bkey *bkey_last(const struct bkey *k, unsigned nr_keys)
+{
+       __u64 *d = (void *) k;
+       return (struct bkey *) (d + nr_keys);
+}
+/* Enough for a key with 6 pointers */
+#define BKEY_PAD               8
+
+#define BKEY_PADDED(key)                                       \
+       union { struct bkey key; __u64 key ## _pad[BKEY_PAD]; }
+
+/* Superblock */
+
+/* Version 0: Cache device
+ * Version 1: Backing device
+ * Version 2: Seed pointer into btree node checksum
+ * Version 3: Cache device with new UUID format
+ * Version 4: Backing device with data offset
+ */
+#define BCACHE_SB_VERSION_CDEV         0
+#define BCACHE_SB_VERSION_BDEV         1
+#define BCACHE_SB_VERSION_CDEV_WITH_UUID 3
+#define BCACHE_SB_VERSION_BDEV_WITH_OFFSET 4
+#define BCACHE_SB_MAX_VERSION          4
+
+#define SB_SECTOR                      8
+#define SB_SIZE                                4096
+#define SB_LABEL_SIZE                  32
+#define SB_JOURNAL_BUCKETS             256U
+/* SB_JOURNAL_BUCKETS must be divisible by BITS_PER_LONG */
+#define MAX_CACHES_PER_SET             8
+
+#define BDEV_DATA_START_DEFAULT                16      /* sectors */
+
+struct cache_sb {
+       __u64                   csum;
+       __u64                   offset; /* sector where this sb was written */
+       __u64                   version;
+
+       __u8                    magic[16];
+
+       __u8                    uuid[16];
+       union {
+               __u8            set_uuid[16];
+               __u64           set_magic;
+       };
+       __u8                    label[SB_LABEL_SIZE];
+
+       __u64                   flags;
+       __u64                   seq;
+       __u64                   pad[8];
+
+       union {
+       struct {
+               /* Cache devices */
+               __u64           nbuckets;       /* device size */
+
+               __u16           block_size;     /* sectors */
+               __u16           bucket_size;    /* sectors */
+
+               __u16           nr_in_set;
+               __u16           nr_this_dev;
+       };
+       struct {
+               /* Backing devices */
+               __u64           data_offset;
+
+               /*
+                * block_size from the cache device section is still used by
+                * backing devices, so don't add anything here until we fix
+                * things to not need it for backing devices anymore
+                */
+       };
+       };
+
+       __u32                   last_mount;     /* time_t */
+
+       __u16                   first_bucket;
+       union {
+               __u16           njournal_buckets;
+               __u16           keys;
+       };
+       __u64                   d[SB_JOURNAL_BUCKETS];  /* journal buckets */
+};
+
+static inline _Bool SB_IS_BDEV(const struct cache_sb *sb)
+{
+       return sb->version == BCACHE_SB_VERSION_BDEV
+               || sb->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET;
+}
+
+BITMASK(CACHE_SYNC,                    struct cache_sb, flags, 0, 1);
+BITMASK(CACHE_DISCARD,                 struct cache_sb, flags, 1, 1);
+BITMASK(CACHE_REPLACEMENT,             struct cache_sb, flags, 2, 3);
+#define CACHE_REPLACEMENT_LRU          0U
+#define CACHE_REPLACEMENT_FIFO         1U
+#define CACHE_REPLACEMENT_RANDOM       2U
+
+BITMASK(BDEV_CACHE_MODE,               struct cache_sb, flags, 0, 4);
+#define CACHE_MODE_WRITETHROUGH                0U
+#define CACHE_MODE_WRITEBACK           1U
+#define CACHE_MODE_WRITEAROUND         2U
+#define CACHE_MODE_NONE                        3U
+BITMASK(BDEV_STATE,                    struct cache_sb, flags, 61, 2);
+#define BDEV_STATE_NONE                        0U
+#define BDEV_STATE_CLEAN               1U
+#define BDEV_STATE_DIRTY               2U
+#define BDEV_STATE_STALE               3U
+
+/*
+ * Magic numbers
+ *
+ * The various other data structures have their own magic numbers, which are
+ * xored with the first part of the cache set's UUID
+ */
+
+#define JSET_MAGIC                     0x245235c1a3625032ULL
+#define PSET_MAGIC                     0x6750e15f87337f91ULL
+#define BSET_MAGIC                     0x90135c78b99e07f5ULL
+
+static inline __u64 jset_magic(struct cache_sb *sb)
+{
+       return sb->set_magic ^ JSET_MAGIC;
+}
+
+static inline __u64 pset_magic(struct cache_sb *sb)
+{
+       return sb->set_magic ^ PSET_MAGIC;
+}
+
+static inline __u64 bset_magic(struct cache_sb *sb)
+{
+       return sb->set_magic ^ BSET_MAGIC;
+}
+
+/*
+ * Journal
+ *
+ * On disk format for a journal entry:
+ * seq is monotonically increasing; every journal entry has its own unique
+ * sequence number.
+ *
+ * last_seq is the oldest journal entry that still has keys the btree hasn't
+ * flushed to disk yet.
+ *
+ * version is for on disk format changes.
+ */
+
+#define BCACHE_JSET_VERSION_UUIDv1     1
+#define BCACHE_JSET_VERSION_UUID       1       /* Always latest UUID format */
+#define BCACHE_JSET_VERSION            1
+
+struct jset {
+       __u64                   csum;
+       __u64                   magic;
+       __u64                   seq;
+       __u32                   version;
+       __u32                   keys;
+
+       __u64                   last_seq;
+
+       BKEY_PADDED(uuid_bucket);
+       BKEY_PADDED(btree_root);
+       __u16                   btree_level;
+       __u16                   pad[3];
+
+       __u64                   prio_bucket[MAX_CACHES_PER_SET];
+
+       union {
+               struct bkey     start[0];
+               __u64           d[0];
+       };
+};
+
+/* Bucket prios/gens */
+
+struct prio_set {
+       __u64                   csum;
+       __u64                   magic;
+       __u64                   seq;
+       __u32                   version;
+       __u32                   pad;
+
+       __u64                   next_bucket;
+
+       struct bucket_disk {
+               __u16           prio;
+               __u8            gen;
+       } __attribute((packed)) data[];
+};
+
+/* UUIDS - per backing device/flash only volume metadata */
+
+struct uuid_entry {
+       union {
+               struct {
+                       __u8    uuid[16];
+                       __u8    label[32];
+                       __u32   first_reg;
+                       __u32   last_reg;
+                       __u32   invalidated;
+
+                       __u32   flags;
+                       /* Size of flash only volumes */
+                       __u64   sectors;
+               };
+
+               __u8            pad[128];
+       };
+};
+
+BITMASK(UUID_FLASH_ONLY,       struct uuid_entry, flags, 0, 1);
+
+/* Btree nodes */
+
+/* Version 1: Seed pointer into btree node checksum
+ */
+#define BCACHE_BSET_CSUM               1
+#define BCACHE_BSET_VERSION            1
+
+/*
+ * Btree nodes
+ *
+ * On disk a btree node is a list/log of these; within each set the keys are
+ * sorted
+ */
+struct bset {
+       __u64                   csum;
+       __u64                   magic;
+       __u64                   seq;
+       __u32                   version;
+       __u32                   keys;
+
+       union {
+               struct bkey     start[0];
+               __u64           d[0];
+       };
+};
+
+/* OBSOLETE */
+
+/* UUIDS - per backing device/flash only volume metadata */
+
+struct uuid_entry_v0 {
+       __u8            uuid[16];
+       __u8            label[32];
+       __u32           first_reg;
+       __u32           last_reg;
+       __u32           invalidated;
+       __u32           pad;
+};
+
+#endif /* _LINUX_BCACHE_H */
index c880a41..1af72d8 100644 (file)
@@ -27,6 +27,7 @@ struct genlmsghdr {
  */
 #define GENL_ID_GENERATE       0
 #define GENL_ID_CTRL           NLMSG_MIN_TYPE
+#define GENL_ID_VFS_DQUOT      (NLMSG_MIN_TYPE + 1)
 
 /**************************************************************************
  * Controller
diff --git a/include/uapi/linux/hash_info.h b/include/uapi/linux/hash_info.h
new file mode 100644 (file)
index 0000000..ca18c45
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Hash Info: Hash algorithms information
+ *
+ * Copyright (c) 2013 Dmitry Kasatkin <d.kasatkin@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 _UAPI_LINUX_HASH_INFO_H
+#define _UAPI_LINUX_HASH_INFO_H
+
+enum hash_algo {
+       HASH_ALGO_MD4,
+       HASH_ALGO_MD5,
+       HASH_ALGO_SHA1,
+       HASH_ALGO_RIPE_MD_160,
+       HASH_ALGO_SHA256,
+       HASH_ALGO_SHA384,
+       HASH_ALGO_SHA512,
+       HASH_ALGO_SHA224,
+       HASH_ALGO_RIPE_MD_128,
+       HASH_ALGO_RIPE_MD_256,
+       HASH_ALGO_RIPE_MD_320,
+       HASH_ALGO_WP_256,
+       HASH_ALGO_WP_384,
+       HASH_ALGO_WP_512,
+       HASH_ALGO_TGR_128,
+       HASH_ALGO_TGR_160,
+       HASH_ALGO_TGR_192,
+       HASH_ALGO__LAST
+};
+
+#endif /* _UAPI_LINUX_HASH_INFO_H */
index c9b7f4f..840cb99 100644 (file)
@@ -56,5 +56,6 @@
 #define KEYCTL_REJECT                  19      /* reject a partially constructed key */
 #define KEYCTL_INSTANTIATE_IOV         20      /* instantiate a partially constructed key */
 #define KEYCTL_INVALIDATE              21      /* invalidate a key */
+#define KEYCTL_GET_PERSISTENT          22      /* get a user's persistent keyring */
 
 #endif /*  _LINUX_KEYCTL_H */
index 99c2533..902f124 100644 (file)
@@ -518,6 +518,10 @@ struct kvm_ppc_smmu_info {
 /* machine type bits, to be used as argument to KVM_CREATE_VM */
 #define KVM_VM_S390_UCONTROL   1
 
+/* on ppc, 0 indicate default, 1 should force HV and 2 PR */
+#define KVM_VM_PPC_HV 1
+#define KVM_VM_PPC_PR 2
+
 #define KVM_S390_SIE_PAGE_OFFSET 1
 
 /*
@@ -541,6 +545,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_TRACE_ENABLE          __KVM_DEPRECATED_MAIN_W_0x06
 #define KVM_TRACE_PAUSE           __KVM_DEPRECATED_MAIN_0x07
 #define KVM_TRACE_DISABLE         __KVM_DEPRECATED_MAIN_0x08
+#define KVM_GET_EMULATED_CPUID   _IOWR(KVMIO, 0x09, struct kvm_cpuid2)
 
 /*
  * Extension capability list.
@@ -668,6 +673,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_IRQ_XICS 92
 #define KVM_CAP_ARM_EL1_32BIT 93
 #define KVM_CAP_SPAPR_MULTITCE 94
+#define KVM_CAP_EXT_EMUL_CPUID 95
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -843,6 +849,10 @@ struct kvm_device_attr {
 #define KVM_DEV_TYPE_FSL_MPIC_20       1
 #define KVM_DEV_TYPE_FSL_MPIC_42       2
 #define KVM_DEV_TYPE_XICS              3
+#define KVM_DEV_TYPE_VFIO              4
+#define  KVM_DEV_VFIO_GROUP                    1
+#define   KVM_DEV_VFIO_GROUP_ADD                       1
+#define   KVM_DEV_VFIO_GROUP_DEL                       2
 
 /*
  * ioctls for VM fds
@@ -1012,6 +1022,7 @@ struct kvm_s390_ucas_mapping {
 /* VM is being stopped by host */
 #define KVM_KVMCLOCK_CTRL        _IO(KVMIO,   0xad)
 #define KVM_ARM_VCPU_INIT        _IOW(KVMIO,  0xae, struct kvm_vcpu_init)
+#define KVM_ARM_PREFERRED_TARGET  _IOR(KVMIO,  0xaf, struct kvm_vcpu_init)
 #define KVM_GET_REG_LIST         _IOWR(KVMIO, 0xb0, struct kvm_reg_list)
 
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU    (1 << 0)
index 2944278..77c6031 100644 (file)
@@ -71,6 +71,6 @@
 #define USBDEVICE_SUPER_MAGIC  0x9fa2
 #define MTD_INODE_FS_MAGIC      0x11307854
 #define ANON_INODE_FS_MAGIC    0x09041934
-
+#define BTRFS_TEST_MAGIC       0x73727279
 
 #endif /* __LINUX_MAGIC_H__ */
index 307f293..a806687 100644 (file)
@@ -763,13 +763,14 @@ enum {
 
        TCA_FQ_RATE_ENABLE,     /* enable/disable rate limiting */
 
-       TCA_FQ_FLOW_DEFAULT_RATE,/* for sockets with unspecified sk_rate,
-                                 * use the following rate
-                                 */
+       TCA_FQ_FLOW_DEFAULT_RATE,/* obsolete, do not use */
 
        TCA_FQ_FLOW_MAX_RATE,   /* per flow max rate */
 
        TCA_FQ_BUCKETS_LOG,     /* log2(number of buckets) */
+
+       TCA_FQ_FLOW_REFILL_DELAY,       /* flow credit refill delay in usec */
+
        __TCA_FQ_MAX
 };
 
index fe1a540..f7cf7f3 100644 (file)
@@ -16,6 +16,7 @@
 #define _MD_P_H
 
 #include <linux/types.h>
+#include <asm/byteorder.h>
 
 /*
  * RAID superblock.
index 083bb5a..1666aab 100644 (file)
@@ -160,6 +160,10 @@ enum v4l2_colorfx {
  * of controls. Total of 16 controls is reserved for this driver */
 #define V4L2_CID_USER_SI476X_BASE              (V4L2_CID_USER_BASE + 0x1040)
 
+/* The base for the TI VPE driver controls. Total of 16 controls is reserved for
+ * this driver */
+#define V4L2_CID_USER_TI_VPE_BASE              (V4L2_CID_USER_BASE + 0x1050)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
index e3ddd86..cbfdd4c 100644 (file)
@@ -87,10 +87,11 @@ enum {
        IB_USER_VERBS_CMD_CLOSE_XRCD,
        IB_USER_VERBS_CMD_CREATE_XSRQ,
        IB_USER_VERBS_CMD_OPEN_QP,
-#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
-       IB_USER_VERBS_CMD_CREATE_FLOW = IB_USER_VERBS_CMD_THRESHOLD,
-       IB_USER_VERBS_CMD_DESTROY_FLOW
-#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
+};
+
+enum {
+       IB_USER_VERBS_EX_CMD_CREATE_FLOW = IB_USER_VERBS_CMD_THRESHOLD,
+       IB_USER_VERBS_EX_CMD_DESTROY_FLOW
 };
 
 /*
@@ -122,22 +123,24 @@ struct ib_uverbs_comp_event_desc {
  * the rest of the command struct based on these value.
  */
 
+#define IB_USER_VERBS_CMD_COMMAND_MASK 0xff
+#define IB_USER_VERBS_CMD_FLAGS_MASK 0xff000000u
+#define IB_USER_VERBS_CMD_FLAGS_SHIFT 24
+
+#define IB_USER_VERBS_CMD_FLAG_EXTENDED 0x80
+
 struct ib_uverbs_cmd_hdr {
        __u32 command;
        __u16 in_words;
        __u16 out_words;
 };
 
-#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
-struct ib_uverbs_cmd_hdr_ex {
-       __u32 command;
-       __u16 in_words;
-       __u16 out_words;
+struct ib_uverbs_ex_cmd_hdr {
+       __u64 response;
        __u16 provider_in_words;
        __u16 provider_out_words;
        __u32 cmd_hdr_reserved;
 };
-#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 struct ib_uverbs_get_context {
        __u64 response;
@@ -700,62 +703,71 @@ struct ib_uverbs_detach_mcast {
        __u64 driver_data[0];
 };
 
-#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
-struct ib_kern_eth_filter {
+struct ib_uverbs_flow_spec_hdr {
+       __u32 type;
+       __u16 size;
+       __u16 reserved;
+       /* followed by flow_spec */
+       __u64 flow_spec_data[0];
+};
+
+struct ib_uverbs_flow_eth_filter {
        __u8  dst_mac[6];
        __u8  src_mac[6];
        __be16 ether_type;
        __be16 vlan_tag;
 };
 
-struct ib_kern_spec_eth {
-       __u32  type;
-       __u16  size;
-       __u16  reserved;
-       struct ib_kern_eth_filter val;
-       struct ib_kern_eth_filter mask;
+struct ib_uverbs_flow_spec_eth {
+       union {
+               struct ib_uverbs_flow_spec_hdr hdr;
+               struct {
+                       __u32 type;
+                       __u16 size;
+                       __u16 reserved;
+               };
+       };
+       struct ib_uverbs_flow_eth_filter val;
+       struct ib_uverbs_flow_eth_filter mask;
 };
 
-struct ib_kern_ipv4_filter {
+struct ib_uverbs_flow_ipv4_filter {
        __be32 src_ip;
        __be32 dst_ip;
 };
 
-struct ib_kern_spec_ipv4 {
-       __u32  type;
-       __u16  size;
-       __u16  reserved;
-       struct ib_kern_ipv4_filter val;
-       struct ib_kern_ipv4_filter mask;
+struct ib_uverbs_flow_spec_ipv4 {
+       union {
+               struct ib_uverbs_flow_spec_hdr hdr;
+               struct {
+                       __u32 type;
+                       __u16 size;
+                       __u16 reserved;
+               };
+       };
+       struct ib_uverbs_flow_ipv4_filter val;
+       struct ib_uverbs_flow_ipv4_filter mask;
 };
 
-struct ib_kern_tcp_udp_filter {
+struct ib_uverbs_flow_tcp_udp_filter {
        __be16 dst_port;
        __be16 src_port;
 };
 
-struct ib_kern_spec_tcp_udp {
-       __u32  type;
-       __u16  size;
-       __u16  reserved;
-       struct ib_kern_tcp_udp_filter val;
-       struct ib_kern_tcp_udp_filter mask;
-};
-
-struct ib_kern_spec {
+struct ib_uverbs_flow_spec_tcp_udp {
        union {
+               struct ib_uverbs_flow_spec_hdr hdr;
                struct {
                        __u32 type;
                        __u16 size;
                        __u16 reserved;
                };
-               struct ib_kern_spec_eth     eth;
-               struct ib_kern_spec_ipv4    ipv4;
-               struct ib_kern_spec_tcp_udp tcp_udp;
        };
+       struct ib_uverbs_flow_tcp_udp_filter val;
+       struct ib_uverbs_flow_tcp_udp_filter mask;
 };
 
-struct ib_kern_flow_attr {
+struct ib_uverbs_flow_attr {
        __u32 type;
        __u16 size;
        __u16 priority;
@@ -767,13 +779,13 @@ struct ib_kern_flow_attr {
         * struct ib_flow_spec_xxx
         * struct ib_flow_spec_yyy
         */
+       struct ib_uverbs_flow_spec_hdr flow_specs[0];
 };
 
 struct ib_uverbs_create_flow  {
        __u32 comp_mask;
-       __u64 response;
        __u32 qp_handle;
-       struct ib_kern_flow_attr flow_attr;
+       struct ib_uverbs_flow_attr flow_attr;
 };
 
 struct ib_uverbs_create_flow_resp {
@@ -785,7 +797,6 @@ struct ib_uverbs_destroy_flow  {
        __u32 comp_mask;
        __u32 flow_handle;
 };
-#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 struct ib_uverbs_create_srq {
        __u64 response;
index 7000bb1..42721d1 100644 (file)
@@ -231,6 +231,17 @@ struct physdev_get_free_pirq {
 #define XEN_PCI_DEV_VIRTFN             0x2
 #define XEN_PCI_DEV_PXM                0x4
 
+#define XEN_PCI_MMCFG_RESERVED         0x1
+
+#define PHYSDEVOP_pci_mmcfg_reserved    24
+struct physdev_pci_mmcfg_reserved {
+    uint64_t address;
+    uint16_t segment;
+    uint8_t start_bus;
+    uint8_t end_bus;
+    uint32_t flags;
+};
+
 #define PHYSDEVOP_pci_device_add        25
 struct physdev_pci_device_add {
     /* IN */
index de8bcc6..8b2eb93 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef __LINUX_SWIOTLB_XEN_H
 #define __LINUX_SWIOTLB_XEN_H
 
+#include <linux/dma-direction.h>
 #include <linux/swiotlb.h>
 
 extern int xen_swiotlb_init(int verbose, bool early);
@@ -55,4 +56,6 @@ xen_swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr);
 extern int
 xen_swiotlb_dma_supported(struct device *hwdev, u64 mask);
 
+extern int
+xen_swiotlb_set_dma_mask(struct device *dev, u64 dma_mask);
 #endif /* __LINUX_SWIOTLB_XEN_H */
index d6fe062..fb2ea8f 100644 (file)
@@ -19,10 +19,11 @@ void xen_arch_resume(void);
 int xen_setup_shutdown_event(void);
 
 extern unsigned long *xen_contiguous_bitmap;
-int xen_create_contiguous_region(unsigned long vstart, unsigned int order,
-                               unsigned int address_bits);
+int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
+                               unsigned int address_bits,
+                               dma_addr_t *dma_handle);
 
-void xen_destroy_contiguous_region(unsigned long vstart, unsigned int order);
+void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order);
 
 struct vm_area_struct;
 int xen_remap_domain_mfn_range(struct vm_area_struct *vma,
index 5496f30..79383d3 100644 (file)
@@ -118,6 +118,7 @@ config HAVE_KERNEL_LZ4
 choice
        prompt "Kernel compression mode"
        default KERNEL_GZIP
+       depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO || HAVE_KERNEL_LZ4
        help
          The linux kernel is a kind of self-extracting executable.
          Several compression algorithms are available, which differ
@@ -136,13 +137,6 @@ choice
 
          If in doubt, select 'gzip'
 
-config KERNEL_UNCOMPRESSED
-       bool "No compression"
-       help
-         No compression at all. The kernel is huge but the compression and
-         decompression times are zero.
-         This is usually not what you want.
-
 config KERNEL_GZIP
        bool "Gzip"
        depends on HAVE_KERNEL_GZIP
@@ -307,20 +301,6 @@ config AUDIT_TREE
        depends on AUDITSYSCALL
        select FSNOTIFY
 
-config AUDIT_LOGINUID_IMMUTABLE
-       bool "Make audit loginuid immutable"
-       depends on AUDIT
-       help
-         The config option toggles if a task setting its loginuid requires
-         CAP_SYS_AUDITCONTROL or if that task should require no special permissions
-         but should instead only allow setting its loginuid if it was never
-         previously set.  On systems which use systemd or a similar central
-         process to restart login services this should be set to true.  On older
-         systems in which an admin would typically have to directly stop and
-         start processes this should be set to false.  Setting this to true allows
-         one to drop potentially dangerous capabilites from the login tasks,
-         but may not be backwards compatible with older init systems.
-
 source "kernel/irq/Kconfig"
 source "kernel/time/Kconfig"
 
@@ -851,7 +831,7 @@ config NUMA_BALANCING_DEFAULT_ENABLED
        default y
        depends on NUMA_BALANCING
        help
-         If set, autonumic NUMA balancing will be enabled if running on a NUMA
+         If set, automatic NUMA balancing will be enabled if running on a NUMA
          machine.
 
 config NUMA_BALANCING
@@ -862,7 +842,7 @@ config NUMA_BALANCING
        help
          This option adds support for automatic NUMA aware memory/task placement.
          The mechanism is quite primitive and is based on migrating memory when
-         it is references to the node the task is running on.
+         it has references to the node the task is running on.
 
          This system will be inactive on UMA systems.
 
@@ -1675,6 +1655,18 @@ config BASE_SMALL
        default 0 if BASE_FULL
        default 1 if !BASE_FULL
 
+config SYSTEM_TRUSTED_KEYRING
+       bool "Provide system-wide ring of trusted keys"
+       depends on KEYS
+       help
+         Provide a system keyring to which trusted keys can be added.  Keys in
+         the keyring are considered to be trusted.  Keys may be added at will
+         by the kernel from compiled-in data and from hardware key stores, but
+         userspace may only add extra keys if those keys can be verified by
+         keys already in the keyring.
+
+         Keys in this keyring are used by module signature checking.
+
 menuconfig MODULES
        bool "Enable loadable module support"
        option modules
@@ -1748,6 +1740,7 @@ config MODULE_SRCVERSION_ALL
 config MODULE_SIG
        bool "Module signature verification"
        depends on MODULES
+       select SYSTEM_TRUSTED_KEYRING
        select KEYS
        select CRYPTO
        select ASYMMETRIC_KEY_TYPE
index 6ad1a53..febc511 100644 (file)
@@ -131,6 +131,8 @@ char __initdata boot_command_line[COMMAND_LINE_SIZE];
 char *saved_command_line;
 /* Command line for parameter parsing */
 static char *static_command_line;
+/* Command line for per-initcall parameter parsing */
+static char *initcall_command_line;
 
 static char *execute_command;
 static char *ramdisk_execute_command;
@@ -354,6 +356,7 @@ static inline void smp_prepare_cpus(unsigned int maxcpus) { }
 static void __init setup_command_line(char *command_line)
 {
        saved_command_line = alloc_bootmem(strlen (boot_command_line)+1);
+       initcall_command_line = alloc_bootmem(strlen (boot_command_line)+1);
        static_command_line = alloc_bootmem(strlen (command_line)+1);
        strcpy (saved_command_line, boot_command_line);
        strcpy (static_command_line, command_line);
@@ -751,9 +754,9 @@ static void __init do_initcall_level(int level)
        extern const struct kernel_param __start___param[], __stop___param[];
        initcall_t *fn;
 
-       strcpy(static_command_line, saved_command_line);
+       strcpy(initcall_command_line, saved_command_line);
        parse_args(initcall_level_names[level],
-                  static_command_line, __start___param,
+                  initcall_command_line, __start___param,
                   __stop___param - __start___param,
                   level, level,
                   &repair_env_string);
index d697396..7a51443 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -208,15 +208,18 @@ static void shm_open(struct vm_area_struct *vma)
  */
 static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
 {
+       struct file *shm_file;
+
+       shm_file = shp->shm_file;
+       shp->shm_file = NULL;
        ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT;
        shm_rmid(ns, shp);
        shm_unlock(shp);
-       if (!is_file_hugepages(shp->shm_file))
-               shmem_lock(shp->shm_file, 0, shp->mlock_user);
+       if (!is_file_hugepages(shm_file))
+               shmem_lock(shm_file, 0, shp->mlock_user);
        else if (shp->mlock_user)
-               user_shm_unlock(file_inode(shp->shm_file)->i_size,
-                                               shp->mlock_user);
-       fput (shp->shm_file);
+               user_shm_unlock(file_inode(shm_file)->i_size, shp->mlock_user);
+       fput(shm_file);
        ipc_rcu_putref(shp, shm_rcu_free);
 }
 
@@ -974,15 +977,25 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
                ipc_lock_object(&shp->shm_perm);
                if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
                        kuid_t euid = current_euid();
-                       err = -EPERM;
                        if (!uid_eq(euid, shp->shm_perm.uid) &&
-                           !uid_eq(euid, shp->shm_perm.cuid))
+                           !uid_eq(euid, shp->shm_perm.cuid)) {
+                               err = -EPERM;
                                goto out_unlock0;
-                       if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK))
+                       }
+                       if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) {
+                               err = -EPERM;
                                goto out_unlock0;
+                       }
                }
 
                shm_file = shp->shm_file;
+
+               /* check if shm_destroy() is tearing down shp */
+               if (shm_file == NULL) {
+                       err = -EIDRM;
+                       goto out_unlock0;
+               }
+
                if (is_file_hugepages(shm_file))
                        goto out_unlock0;
 
@@ -1101,6 +1114,14 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr,
                goto out_unlock;
 
        ipc_lock_object(&shp->shm_perm);
+
+       /* check if shm_destroy() is tearing down shp */
+       if (shp->shm_file == NULL) {
+               ipc_unlock_object(&shp->shm_perm);
+               err = -EIDRM;
+               goto out_unlock;
+       }
+
        path = shp->shm_file->f_path;
        path_get(&path);
        shp->shm_nattch++;
index 94fabd5..2a202a8 100644 (file)
@@ -55,4 +55,4 @@ config HZ
        default 1000 if HZ_1000
 
 config SCHED_HRTICK
-       def_bool HIGH_RES_TIMERS && (!SMP || USE_GENERIC_SMP_HELPERS)
+       def_bool HIGH_RES_TIMERS
index 09a9c94..bbaf7d5 100644 (file)
@@ -41,8 +41,9 @@ ifneq ($(CONFIG_SMP),y)
 obj-y += up.o
 endif
 obj-$(CONFIG_UID16) += uid16.o
+obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
 obj-$(CONFIG_MODULES) += module.o
-obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o modsign_certificate.o
+obj-$(CONFIG_MODULE_SIG) += module_signing.o
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 obj-$(CONFIG_KEXEC) += kexec.o
@@ -122,19 +123,52 @@ targets += timeconst.h
 $(obj)/timeconst.h: $(obj)/hz.bc $(src)/timeconst.bc FORCE
        $(call if_changed,bc)
 
-ifeq ($(CONFIG_MODULE_SIG),y)
+###############################################################################
+#
+# Roll all the X.509 certificates that we can find together and pull them into
+# the kernel so that they get loaded into the system trusted keyring during
+# boot.
 #
-# Pull the signing certificate and any extra certificates into the kernel
+# We look in the source root and the build root for all files whose name ends
+# in ".x509".  Unfortunately, this will generate duplicate filenames, so we
+# have make canonicalise the pathnames and then sort them to discard the
+# duplicates.
 #
+###############################################################################
+ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
+X509_CERTIFICATES-y := $(wildcard *.x509) $(wildcard $(srctree)/*.x509)
+X509_CERTIFICATES-$(CONFIG_MODULE_SIG) += signing_key.x509
+X509_CERTIFICATES := $(sort $(foreach CERT,$(X509_CERTIFICATES-y), \
+                               $(or $(realpath $(CERT)),$(CERT))))
+
+ifeq ($(X509_CERTIFICATES),)
+$(warning *** No X.509 certificates found ***)
+endif
+
+ifneq ($(wildcard $(obj)/.x509.list),)
+ifneq ($(shell cat $(obj)/.x509.list),$(X509_CERTIFICATES))
+$(info X.509 certificate list changed)
+$(shell rm $(obj)/.x509.list)
+endif
+endif
+
+kernel/system_certificates.o: $(obj)/x509_certificate_list
 
-quiet_cmd_touch = TOUCH   $@
-      cmd_touch = touch   $@
+quiet_cmd_x509certs  = CERTS   $@
+      cmd_x509certs  = cat $(X509_CERTIFICATES) /dev/null >$@ $(foreach X509,$(X509_CERTIFICATES),; echo "  - Including cert $(X509)")
 
-extra_certificates:
-       $(call cmd,touch)
+targets += $(obj)/x509_certificate_list
+$(obj)/x509_certificate_list: $(X509_CERTIFICATES) $(obj)/.x509.list
+       $(call if_changed,x509certs)
 
-kernel/modsign_certificate.o: signing_key.x509 extra_certificates
+targets += $(obj)/.x509.list
+$(obj)/.x509.list:
+       @echo $(X509_CERTIFICATES) >$@
 
+clean-files := x509_certificate_list .x509.list
+endif
+
+ifeq ($(CONFIG_MODULE_SIG),y)
 ###############################################################################
 #
 # If module signing is requested, say by allyesconfig, but a key has not been
index 7b0e23a..906ae5a 100644 (file)
@@ -60,7 +60,6 @@
 #ifdef CONFIG_SECURITY
 #include <linux/security.h>
 #endif
-#include <net/netlink.h>
 #include <linux/freezer.h>
 #include <linux/tty.h>
 #include <linux/pid_namespace.h>
@@ -140,6 +139,17 @@ static struct task_struct *kauditd_task;
 static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
 static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
 
+static struct audit_features af = {.vers = AUDIT_FEATURE_VERSION,
+                                  .mask = -1,
+                                  .features = 0,
+                                  .lock = 0,};
+
+static char *audit_feature_names[2] = {
+       "only_unset_loginuid",
+       "loginuid_immutable",
+};
+
+
 /* Serialize requests from userspace. */
 DEFINE_MUTEX(audit_cmd_mutex);
 
@@ -584,6 +594,8 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type)
                return -EOPNOTSUPP;
        case AUDIT_GET:
        case AUDIT_SET:
+       case AUDIT_GET_FEATURE:
+       case AUDIT_SET_FEATURE:
        case AUDIT_LIST_RULES:
        case AUDIT_ADD_RULE:
        case AUDIT_DEL_RULE:
@@ -613,7 +625,7 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type)
        int rc = 0;
        uid_t uid = from_kuid(&init_user_ns, current_uid());
 
-       if (!audit_enabled) {
+       if (!audit_enabled && msg_type != AUDIT_USER_AVC) {
                *ab = NULL;
                return rc;
        }
@@ -628,6 +640,94 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type)
        return rc;
 }
 
+int is_audit_feature_set(int i)
+{
+       return af.features & AUDIT_FEATURE_TO_MASK(i);
+}
+
+
+static int audit_get_feature(struct sk_buff *skb)
+{
+       u32 seq;
+
+       seq = nlmsg_hdr(skb)->nlmsg_seq;
+
+       audit_send_reply(NETLINK_CB(skb).portid, seq, AUDIT_GET, 0, 0,
+                        &af, sizeof(af));
+
+       return 0;
+}
+
+static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature,
+                                    u32 old_lock, u32 new_lock, int res)
+{
+       struct audit_buffer *ab;
+
+       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE);
+       audit_log_format(ab, "feature=%s new=%d old=%d old_lock=%d new_lock=%d res=%d",
+                        audit_feature_names[which], !!old_feature, !!new_feature,
+                        !!old_lock, !!new_lock, res);
+       audit_log_end(ab);
+}
+
+static int audit_set_feature(struct sk_buff *skb)
+{
+       struct audit_features *uaf;
+       int i;
+
+       BUILD_BUG_ON(AUDIT_LAST_FEATURE + 1 > sizeof(audit_feature_names)/sizeof(audit_feature_names[0]));
+       uaf = nlmsg_data(nlmsg_hdr(skb));
+
+       /* if there is ever a version 2 we should handle that here */
+
+       for (i = 0; i <= AUDIT_LAST_FEATURE; i++) {
+               u32 feature = AUDIT_FEATURE_TO_MASK(i);
+               u32 old_feature, new_feature, old_lock, new_lock;
+
+               /* if we are not changing this feature, move along */
+               if (!(feature & uaf->mask))
+                       continue;
+
+               old_feature = af.features & feature;
+               new_feature = uaf->features & feature;
+               new_lock = (uaf->lock | af.lock) & feature;
+               old_lock = af.lock & feature;
+
+               /* are we changing a locked feature? */
+               if ((af.lock & feature) && (new_feature != old_feature)) {
+                       audit_log_feature_change(i, old_feature, new_feature,
+                                                old_lock, new_lock, 0);
+                       return -EPERM;
+               }
+       }
+       /* nothing invalid, do the changes */
+       for (i = 0; i <= AUDIT_LAST_FEATURE; i++) {
+               u32 feature = AUDIT_FEATURE_TO_MASK(i);
+               u32 old_feature, new_feature, old_lock, new_lock;
+
+               /* if we are not changing this feature, move along */
+               if (!(feature & uaf->mask))
+                       continue;
+
+               old_feature = af.features & feature;
+               new_feature = uaf->features & feature;
+               old_lock = af.lock & feature;
+               new_lock = (uaf->lock | af.lock) & feature;
+
+               if (new_feature != old_feature)
+                       audit_log_feature_change(i, old_feature, new_feature,
+                                                old_lock, new_lock, 1);
+
+               if (new_feature)
+                       af.features |= feature;
+               else
+                       af.features &= ~feature;
+               af.lock |= new_lock;
+       }
+
+       return 0;
+}
+
 static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        u32                     seq;
@@ -659,6 +759,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 
        switch (msg_type) {
        case AUDIT_GET:
+               memset(&status_set, 0, sizeof(status_set));
                status_set.enabled       = audit_enabled;
                status_set.failure       = audit_failure;
                status_set.pid           = audit_pid;
@@ -670,7 +771,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                                 &status_set, sizeof(status_set));
                break;
        case AUDIT_SET:
-               if (nlh->nlmsg_len < sizeof(struct audit_status))
+               if (nlmsg_len(nlh) < sizeof(struct audit_status))
                        return -EINVAL;
                status_get   = (struct audit_status *)data;
                if (status_get->mask & AUDIT_STATUS_ENABLED) {
@@ -699,6 +800,16 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)
                        err = audit_set_backlog_limit(status_get->backlog_limit);
                break;
+       case AUDIT_GET_FEATURE:
+               err = audit_get_feature(skb);
+               if (err)
+                       return err;
+               break;
+       case AUDIT_SET_FEATURE:
+               err = audit_set_feature(skb);
+               if (err)
+                       return err;
+               break;
        case AUDIT_USER:
        case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
        case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
@@ -715,7 +826,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                        }
                        audit_log_common_recv_msg(&ab, msg_type);
                        if (msg_type != AUDIT_USER_TTY)
-                               audit_log_format(ab, " msg='%.1024s'",
+                               audit_log_format(ab, " msg='%.*s'",
+                                                AUDIT_MESSAGE_TEXT_MAX,
                                                 (char *)data);
                        else {
                                int size;
@@ -818,7 +930,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                struct task_struct *tsk = current;
 
                spin_lock(&tsk->sighand->siglock);
-               s.enabled = tsk->signal->audit_tty != 0;
+               s.enabled = tsk->signal->audit_tty;
                s.log_passwd = tsk->signal->audit_tty_log_passwd;
                spin_unlock(&tsk->sighand->siglock);
 
@@ -832,7 +944,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 
                memset(&s, 0, sizeof(s));
                /* guard against past and future API changes */
-               memcpy(&s, data, min(sizeof(s), (size_t)nlh->nlmsg_len));
+               memcpy(&s, data, min_t(size_t, sizeof(s), nlmsg_len(nlh)));
                if ((s.enabled != 0 && s.enabled != 1) ||
                    (s.log_passwd != 0 && s.log_passwd != 1))
                        return -EINVAL;
@@ -1067,13 +1179,6 @@ static void wait_for_auditd(unsigned long sleep_time)
        remove_wait_queue(&audit_backlog_wait, &wait);
 }
 
-/* Obtain an audit buffer.  This routine does locking to obtain the
- * audit buffer, but then no locking is required for calls to
- * audit_log_*format.  If the tsk is a task that is currently in a
- * syscall, then the syscall is marked as auditable and an audit record
- * will be written at syscall exit.  If there is no associated task, tsk
- * should be NULL. */
-
 /**
  * audit_log_start - obtain an audit buffer
  * @ctx: audit_context (may be NULL)
@@ -1389,7 +1494,7 @@ void audit_log_session_info(struct audit_buffer *ab)
        u32 sessionid = audit_get_sessionid(current);
        uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current));
 
-       audit_log_format(ab, " auid=%u ses=%u\n", auid, sessionid);
+       audit_log_format(ab, " auid=%u ses=%u", auid, sessionid);
 }
 
 void audit_log_key(struct audit_buffer *ab, char *key)
@@ -1536,6 +1641,26 @@ void audit_log_name(struct audit_context *context, struct audit_names *n,
                }
        }
 
+       /* log the audit_names record type */
+       audit_log_format(ab, " nametype=");
+       switch(n->type) {
+       case AUDIT_TYPE_NORMAL:
+               audit_log_format(ab, "NORMAL");
+               break;
+       case AUDIT_TYPE_PARENT:
+               audit_log_format(ab, "PARENT");
+               break;
+       case AUDIT_TYPE_CHILD_DELETE:
+               audit_log_format(ab, "DELETE");
+               break;
+       case AUDIT_TYPE_CHILD_CREATE:
+               audit_log_format(ab, "CREATE");
+               break;
+       default:
+               audit_log_format(ab, "UNKNOWN");
+               break;
+       }
+
        audit_log_fcaps(ab, n);
        audit_log_end(ab);
 }
index 123c9b7..b779642 100644 (file)
@@ -197,6 +197,9 @@ struct audit_context {
                        int                     fd;
                        int                     flags;
                } mmap;
+               struct {
+                       int                     argc;
+               } execve;
        };
        int fds[2];
 
index f7aee8b..51f3fd4 100644 (file)
@@ -343,6 +343,7 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f)
        case AUDIT_DEVMINOR:
        case AUDIT_EXIT:
        case AUDIT_SUCCESS:
+       case AUDIT_INODE:
                /* bit ops are only useful on syscall args */
                if (f->op == Audit_bitmask || f->op == Audit_bittest)
                        return -EINVAL;
@@ -423,7 +424,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                f->lsm_rule = NULL;
 
                /* Support legacy tests for a valid loginuid */
-               if ((f->type == AUDIT_LOGINUID) && (f->val == ~0U)) {
+               if ((f->type == AUDIT_LOGINUID) && (f->val == AUDIT_UID_UNSET)) {
                        f->type = AUDIT_LOGINUID_SET;
                        f->val = 0;
                }
index 9845cb3..90594c9 100644 (file)
@@ -95,13 +95,6 @@ struct audit_aux_data {
 /* Number of target pids per aux struct. */
 #define AUDIT_AUX_PIDS 16
 
-struct audit_aux_data_execve {
-       struct audit_aux_data   d;
-       int argc;
-       int envc;
-       struct mm_struct *mm;
-};
-
 struct audit_aux_data_pids {
        struct audit_aux_data   d;
        pid_t                   target_pid[AUDIT_AUX_PIDS];
@@ -121,12 +114,6 @@ struct audit_aux_data_bprm_fcaps {
        struct audit_cap_data   new_pcap;
 };
 
-struct audit_aux_data_capset {
-       struct audit_aux_data   d;
-       pid_t                   pid;
-       struct audit_cap_data   cap;
-};
-
 struct audit_tree_refs {
        struct audit_tree_refs *next;
        struct audit_chunk *c[31];
@@ -566,7 +553,7 @@ static int audit_filter_rules(struct task_struct *tsk,
                        break;
                case AUDIT_INODE:
                        if (name)
-                               result = (name->ino == f->val);
+                               result = audit_comparator(name->ino, f->op, f->val);
                        else if (ctx) {
                                list_for_each_entry(n, &ctx->names_list, list) {
                                        if (audit_comparator(n->ino, f->op, f->val)) {
@@ -943,8 +930,10 @@ int audit_alloc(struct task_struct *tsk)
                return 0; /* Return if not auditing. */
 
        state = audit_filter_task(tsk, &key);
-       if (state == AUDIT_DISABLED)
+       if (state == AUDIT_DISABLED) {
+               clear_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT);
                return 0;
+       }
 
        if (!(context = audit_alloc_context(state))) {
                kfree(key);
@@ -1149,20 +1138,16 @@ static int audit_log_single_execve_arg(struct audit_context *context,
 }
 
 static void audit_log_execve_info(struct audit_context *context,
-                                 struct audit_buffer **ab,
-                                 struct audit_aux_data_execve *axi)
+                                 struct audit_buffer **ab)
 {
        int i, len;
        size_t len_sent = 0;
        const char __user *p;
        char *buf;
 
-       if (axi->mm != current->mm)
-               return; /* execve failed, no additional info */
-
-       p = (const char __user *)axi->mm->arg_start;
+       p = (const char __user *)current->mm->arg_start;
 
-       audit_log_format(*ab, "argc=%d", axi->argc);
+       audit_log_format(*ab, "argc=%d", context->execve.argc);
 
        /*
         * we need some kernel buffer to hold the userspace args.  Just
@@ -1176,7 +1161,7 @@ static void audit_log_execve_info(struct audit_context *context,
                return;
        }
 
-       for (i = 0; i < axi->argc; i++) {
+       for (i = 0; i < context->execve.argc; i++) {
                len = audit_log_single_execve_arg(context, ab, i,
                                                  &len_sent, p, buf);
                if (len <= 0)
@@ -1279,6 +1264,9 @@ static void show_special(struct audit_context *context, int *call_panic)
                audit_log_format(ab, "fd=%d flags=0x%x", context->mmap.fd,
                                 context->mmap.flags);
                break; }
+       case AUDIT_EXECVE: {
+               audit_log_execve_info(context, &ab);
+               break; }
        }
        audit_log_end(ab);
 }
@@ -1325,11 +1313,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
 
                switch (aux->type) {
 
-               case AUDIT_EXECVE: {
-                       struct audit_aux_data_execve *axi = (void *)aux;
-                       audit_log_execve_info(context, &ab, axi);
-                       break; }
-
                case AUDIT_BPRM_FCAPS: {
                        struct audit_aux_data_bprm_fcaps *axs = (void *)aux;
                        audit_log_format(ab, "fver=%x", axs->fcap_ver);
@@ -1964,6 +1947,43 @@ int auditsc_get_stamp(struct audit_context *ctx,
 /* global counter which is incremented every time something logs in */
 static atomic_t session_id = ATOMIC_INIT(0);
 
+static int audit_set_loginuid_perm(kuid_t loginuid)
+{
+       /* if we are unset, we don't need privs */
+       if (!audit_loginuid_set(current))
+               return 0;
+       /* if AUDIT_FEATURE_LOGINUID_IMMUTABLE means never ever allow a change*/
+       if (is_audit_feature_set(AUDIT_FEATURE_LOGINUID_IMMUTABLE))
+               return -EPERM;
+       /* it is set, you need permission */
+       if (!capable(CAP_AUDIT_CONTROL))
+               return -EPERM;
+       /* reject if this is not an unset and we don't allow that */
+       if (is_audit_feature_set(AUDIT_FEATURE_ONLY_UNSET_LOGINUID) && uid_valid(loginuid))
+               return -EPERM;
+       return 0;
+}
+
+static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid,
+                                  unsigned int oldsessionid, unsigned int sessionid,
+                                  int rc)
+{
+       struct audit_buffer *ab;
+       uid_t uid, ologinuid, nloginuid;
+
+       uid = from_kuid(&init_user_ns, task_uid(current));
+       ologinuid = from_kuid(&init_user_ns, koldloginuid);
+       nloginuid = from_kuid(&init_user_ns, kloginuid),
+
+       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
+       if (!ab)
+               return;
+       audit_log_format(ab, "pid=%d uid=%u old auid=%u new auid=%u old "
+                        "ses=%u new ses=%u res=%d", current->pid, uid, ologinuid,
+                        nloginuid, oldsessionid, sessionid, !rc);
+       audit_log_end(ab);
+}
+
 /**
  * audit_set_loginuid - set current task's audit_context loginuid
  * @loginuid: loginuid value
@@ -1975,37 +1995,26 @@ static atomic_t session_id = ATOMIC_INIT(0);
 int audit_set_loginuid(kuid_t loginuid)
 {
        struct task_struct *task = current;
-       struct audit_context *context = task->audit_context;
-       unsigned int sessionid;
+       unsigned int oldsessionid, sessionid = (unsigned int)-1;
+       kuid_t oldloginuid;
+       int rc;
 
-#ifdef CONFIG_AUDIT_LOGINUID_IMMUTABLE
-       if (audit_loginuid_set(task))
-               return -EPERM;
-#else /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */
-       if (!capable(CAP_AUDIT_CONTROL))
-               return -EPERM;
-#endif  /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */
+       oldloginuid = audit_get_loginuid(current);
+       oldsessionid = audit_get_sessionid(current);
 
-       sessionid = atomic_inc_return(&session_id);
-       if (context && context->in_syscall) {
-               struct audit_buffer *ab;
+       rc = audit_set_loginuid_perm(loginuid);
+       if (rc)
+               goto out;
+
+       /* are we setting or clearing? */
+       if (uid_valid(loginuid))
+               sessionid = atomic_inc_return(&session_id);
 
-               ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
-               if (ab) {
-                       audit_log_format(ab, "login pid=%d uid=%u "
-                               "old auid=%u new auid=%u"
-                               " old ses=%u new ses=%u",
-                               task->pid,
-                               from_kuid(&init_user_ns, task_uid(task)),
-                               from_kuid(&init_user_ns, task->loginuid),
-                               from_kuid(&init_user_ns, loginuid),
-                               task->sessionid, sessionid);
-                       audit_log_end(ab);
-               }
-       }
        task->sessionid = sessionid;
        task->loginuid = loginuid;
-       return 0;
+out:
+       audit_log_set_loginuid(oldloginuid, loginuid, oldsessionid, sessionid, rc);
+       return rc;
 }
 
 /**
@@ -2126,22 +2135,12 @@ void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mo
        context->ipc.has_perm = 1;
 }
 
-int __audit_bprm(struct linux_binprm *bprm)
+void __audit_bprm(struct linux_binprm *bprm)
 {
-       struct audit_aux_data_execve *ax;
        struct audit_context *context = current->audit_context;
 
-       ax = kmalloc(sizeof(*ax), GFP_KERNEL);
-       if (!ax)
-               return -ENOMEM;
-
-       ax->argc = bprm->argc;
-       ax->envc = bprm->envc;
-       ax->mm = bprm->mm;
-       ax->d.type = AUDIT_EXECVE;
-       ax->d.next = context->aux;
-       context->aux = (void *)ax;
-       return 0;
+       context->type = AUDIT_EXECVE;
+       context->execve.argc = bprm->argc;
 }
 
 
index e8ca97b..5253204 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kbuild.h>
 #include <linux/page_cgroup.h>
 #include <linux/log2.h>
+#include <linux/spinlock_types.h>
 
 void foo(void)
 {
@@ -21,5 +22,6 @@ void foo(void)
 #ifdef CONFIG_SMP
        DEFINE(NR_CPUS_BITS, ilog2(CONFIG_NR_CPUS));
 #endif
+       DEFINE(BLOATED_SPINLOCKS, sizeof(spinlock_t) > sizeof(int));
        /* End of constants */
 }
index e0839bc..4c62513 100644 (file)
@@ -895,11 +895,6 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
        iput(inode);
 }
 
-static int cgroup_delete(const struct dentry *d)
-{
-       return 1;
-}
-
 static void remove_dir(struct dentry *d)
 {
        struct dentry *parent = dget(d->d_parent);
@@ -1486,7 +1481,7 @@ static int cgroup_get_rootdir(struct super_block *sb)
 {
        static const struct dentry_operations cgroup_dops = {
                .d_iput = cgroup_diput,
-               .d_delete = cgroup_delete,
+               .d_delete = always_delete_dentry,
        };
 
        struct inode *inode =
index f6d11fc..728d5be 100644 (file)
@@ -532,7 +532,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
        mm->flags = (current->mm) ?
                (current->mm->flags & MMF_INIT_MASK) : default_dump_filter;
        mm->core_state = NULL;
-       mm->nr_ptes = 0;
+       atomic_long_set(&mm->nr_ptes, 0);
        memset(&mm->rss_stat, 0, sizeof(mm->rss_stat));
        spin_lock_init(&mm->page_table_lock);
        mm_init_aio(mm);
@@ -560,7 +560,7 @@ static void check_mm(struct mm_struct *mm)
                                          "mm:%p idx:%d val:%ld\n", mm, i, x);
        }
 
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS
        VM_BUG_ON(mm->pmd_huge_pte);
 #endif
 }
@@ -814,7 +814,7 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
        memcpy(mm, oldmm, sizeof(*mm));
        mm_init_cpumask(mm);
 
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS
        mm->pmd_huge_pte = NULL;
 #endif
        if (!mm_init(mm, tsk))
index 8807061..9328b80 100644 (file)
@@ -207,6 +207,14 @@ int proc_dohung_task_timeout_secs(struct ctl_table *table, int write,
        return ret;
 }
 
+static atomic_t reset_hung_task = ATOMIC_INIT(0);
+
+void reset_hung_task_detector(void)
+{
+       atomic_set(&reset_hung_task, 1);
+}
+EXPORT_SYMBOL_GPL(reset_hung_task_detector);
+
 /*
  * kthread which checks for tasks stuck in D state
  */
@@ -220,6 +228,9 @@ static int watchdog(void *dummy)
                while (schedule_timeout_interruptible(timeout_jiffies(timeout)))
                        timeout = sysctl_hung_task_timeout_secs;
 
+               if (atomic_xchg(&reset_hung_task, 0))
+                       continue;
+
                check_hung_uninterruptible_tasks(timeout);
        }
 
index a3bb14f..dc04c16 100644 (file)
@@ -214,7 +214,7 @@ void irq_enable(struct irq_desc *desc)
 }
 
 /**
- * irq_disable - Mark interupt disabled
+ * irq_disable - Mark interrupt disabled
  * @desc:      irq descriptor which should be disabled
  *
  * If the chip does not implement the irq_disable callback, we
index 3e59f95..481a13c 100644 (file)
@@ -786,7 +786,7 @@ irq_forced_thread_fn(struct irq_desc *desc, struct irqaction *action)
 }
 
 /*
- * Interrupts explicitely requested as threaded interupts want to be
+ * Interrupts explicitly requested as threaded interrupts want to be
  * preemtible - many of them need to sleep and wait for slow busses to
  * complete.
  */
index 1162f10..3320b84 100644 (file)
@@ -14,6 +14,7 @@ enum {
        _IRQ_NO_BALANCING       = IRQ_NO_BALANCING,
        _IRQ_NESTED_THREAD      = IRQ_NESTED_THREAD,
        _IRQ_PER_CPU_DEVID      = IRQ_PER_CPU_DEVID,
+       _IRQ_IS_POLLED          = IRQ_IS_POLLED,
        _IRQF_MODIFY_MASK       = IRQF_MODIFY_MASK,
 };
 
@@ -26,6 +27,7 @@ enum {
 #define IRQ_NOAUTOEN           GOT_YOU_MORON
 #define IRQ_NESTED_THREAD      GOT_YOU_MORON
 #define IRQ_PER_CPU_DEVID      GOT_YOU_MORON
+#define IRQ_IS_POLLED          GOT_YOU_MORON
 #undef IRQF_MODIFY_MASK
 #define IRQF_MODIFY_MASK       GOT_YOU_MORON
 
@@ -147,3 +149,8 @@ static inline bool irq_settings_is_nested_thread(struct irq_desc *desc)
 {
        return desc->status_use_accessors & _IRQ_NESTED_THREAD;
 }
+
+static inline bool irq_settings_is_polled(struct irq_desc *desc)
+{
+       return desc->status_use_accessors & _IRQ_IS_POLLED;
+}
index 7b5f012..a1d8cc6 100644 (file)
@@ -67,8 +67,13 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force)
 
        raw_spin_lock(&desc->lock);
 
-       /* PER_CPU and nested thread interrupts are never polled */
-       if (irq_settings_is_per_cpu(desc) || irq_settings_is_nested_thread(desc))
+       /*
+        * PER_CPU, nested thread interrupts and interrupts explicitely
+        * marked polled are excluded from polling.
+        */
+       if (irq_settings_is_per_cpu(desc) ||
+           irq_settings_is_nested_thread(desc) ||
+           irq_settings_is_polled(desc))
                goto out;
 
        /*
@@ -268,7 +273,8 @@ try_misrouted_irq(unsigned int irq, struct irq_desc *desc,
 void note_interrupt(unsigned int irq, struct irq_desc *desc,
                    irqreturn_t action_ret)
 {
-       if (desc->istate & IRQS_POLL_INPROGRESS)
+       if (desc->istate & IRQS_POLL_INPROGRESS ||
+           irq_settings_is_polled(desc))
                return;
 
        /* we get here again via the threaded handler */
index 2a74f30..490afc0 100644 (file)
@@ -921,7 +921,7 @@ static int kimage_load_segment(struct kimage *image,
  *   reinitialize them.
  *
  * - A machine specific part that includes the syscall number
- *   and the copies the image to it's final destination.  And
+ *   and then copies the image to it's final destination.  And
  *   jumps into the image at entry.
  *
  * kexec does not sync, or unmount filesystems so if you need
diff --git a/kernel/modsign_certificate.S b/kernel/modsign_certificate.S
deleted file mode 100644 (file)
index 4a9a86d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#include <linux/export.h>
-
-#define GLOBAL(name)   \
-       .globl VMLINUX_SYMBOL(name);    \
-       VMLINUX_SYMBOL(name):
-
-       .section ".init.data","aw"
-
-GLOBAL(modsign_certificate_list)
-       .incbin "signing_key.x509"
-       .incbin "extra_certificates"
-GLOBAL(modsign_certificate_list_end)
diff --git a/kernel/modsign_pubkey.c b/kernel/modsign_pubkey.c
deleted file mode 100644 (file)
index 7cbd450..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/* Public keys for module signature verification
- *
- * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/cred.h>
-#include <linux/err.h>
-#include <keys/asymmetric-type.h>
-#include "module-internal.h"
-
-struct key *modsign_keyring;
-
-extern __initconst const u8 modsign_certificate_list[];
-extern __initconst const u8 modsign_certificate_list_end[];
-
-/*
- * We need to make sure ccache doesn't cache the .o file as it doesn't notice
- * if modsign.pub changes.
- */
-static __initconst const char annoy_ccache[] = __TIME__ "foo";
-
-/*
- * Load the compiled-in keys
- */
-static __init int module_verify_init(void)
-{
-       pr_notice("Initialise module verification\n");
-
-       modsign_keyring = keyring_alloc(".module_sign",
-                                       KUIDT_INIT(0), KGIDT_INIT(0),
-                                       current_cred(),
-                                       ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                                        KEY_USR_VIEW | KEY_USR_READ),
-                                       KEY_ALLOC_NOT_IN_QUOTA, NULL);
-       if (IS_ERR(modsign_keyring))
-               panic("Can't allocate module signing keyring\n");
-
-       return 0;
-}
-
-/*
- * Must be initialised before we try and load the keys into the keyring.
- */
-device_initcall(module_verify_init);
-
-/*
- * Load the compiled-in keys
- */
-static __init int load_module_signing_keys(void)
-{
-       key_ref_t key;
-       const u8 *p, *end;
-       size_t plen;
-
-       pr_notice("Loading module verification certificates\n");
-
-       end = modsign_certificate_list_end;
-       p = modsign_certificate_list;
-       while (p < end) {
-               /* Each cert begins with an ASN.1 SEQUENCE tag and must be more
-                * than 256 bytes in size.
-                */
-               if (end - p < 4)
-                       goto dodgy_cert;
-               if (p[0] != 0x30 &&
-                   p[1] != 0x82)
-                       goto dodgy_cert;
-               plen = (p[2] << 8) | p[3];
-               plen += 4;
-               if (plen > end - p)
-                       goto dodgy_cert;
-
-               key = key_create_or_update(make_key_ref(modsign_keyring, 1),
-                                          "asymmetric",
-                                          NULL,
-                                          p,
-                                          plen,
-                                          (KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                                          KEY_USR_VIEW,
-                                          KEY_ALLOC_NOT_IN_QUOTA);
-               if (IS_ERR(key))
-                       pr_err("MODSIGN: Problem loading in-kernel X.509 certificate (%ld)\n",
-                              PTR_ERR(key));
-               else
-                       pr_notice("MODSIGN: Loaded cert '%s'\n",
-                                 key_ref_to_ptr(key)->description);
-               p += plen;
-       }
-
-       return 0;
-
-dodgy_cert:
-       pr_err("MODSIGN: Problem parsing in-kernel X.509 certificate list\n");
-       return 0;
-}
-late_initcall(load_module_signing_keys);
index 24f9247..915e123 100644 (file)
@@ -9,6 +9,4 @@
  * 2 of the Licence, or (at your option) any later version.
  */
 
-extern struct key *modsign_keyring;
-
 extern int mod_verify_sig(const void *mod, unsigned long *_modlen);
index af5ebd2..f5a3b1e 100644 (file)
@@ -641,8 +641,6 @@ static int module_unload_init(struct module *mod)
 
        /* Hold reference count during initialization. */
        __this_cpu_write(mod->refptr->incs, 1);
-       /* Backwards compatibility macros put refcount during init. */
-       mod->waiter = current;
 
        return 0;
 }
@@ -768,16 +766,9 @@ static int __try_stop_module(void *_sref)
 
 static int try_stop_module(struct module *mod, int flags, int *forced)
 {
-       if (flags & O_NONBLOCK) {
-               struct stopref sref = { mod, flags, forced };
+       struct stopref sref = { mod, flags, forced };
 
-               return stop_machine(__try_stop_module, &sref, NULL);
-       } else {
-               /* We don't need to stop the machine for this. */
-               mod->state = MODULE_STATE_GOING;
-               synchronize_sched();
-               return 0;
-       }
+       return stop_machine(__try_stop_module, &sref, NULL);
 }
 
 unsigned long module_refcount(struct module *mod)
@@ -810,21 +801,6 @@ EXPORT_SYMBOL(module_refcount);
 /* This exists whether we can unload or not */
 static void free_module(struct module *mod);
 
-static void wait_for_zero_refcount(struct module *mod)
-{
-       /* Since we might sleep for some time, release the mutex first */
-       mutex_unlock(&module_mutex);
-       for (;;) {
-               pr_debug("Looking at refcount...\n");
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               if (module_refcount(mod) == 0)
-                       break;
-               schedule();
-       }
-       current->state = TASK_RUNNING;
-       mutex_lock(&module_mutex);
-}
-
 SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
                unsigned int, flags)
 {
@@ -839,6 +815,11 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
                return -EFAULT;
        name[MODULE_NAME_LEN-1] = '\0';
 
+       if (!(flags & O_NONBLOCK)) {
+               printk(KERN_WARNING
+                      "waiting module removal not supported: please upgrade");
+       }
+
        if (mutex_lock_interruptible(&module_mutex) != 0)
                return -EINTR;
 
@@ -856,8 +837,7 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
 
        /* Doing init or already dying? */
        if (mod->state != MODULE_STATE_LIVE) {
-               /* FIXME: if (force), slam module count and wake up
-                   waiter --RR */
+               /* FIXME: if (force), slam module count damn the torpedoes */
                pr_debug("%s already dying\n", mod->name);
                ret = -EBUSY;
                goto out;
@@ -873,18 +853,11 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
                }
        }
 
-       /* Set this up before setting mod->state */
-       mod->waiter = current;
-
        /* Stop the machine so refcounts can't move and disable module. */
        ret = try_stop_module(mod, flags, &forced);
        if (ret != 0)
                goto out;
 
-       /* Never wait if forced. */
-       if (!forced && module_refcount(mod) != 0)
-               wait_for_zero_refcount(mod);
-
        mutex_unlock(&module_mutex);
        /* Final destruction now no one is using it. */
        if (mod->exit != NULL)
@@ -1002,9 +975,6 @@ void module_put(struct module *module)
                __this_cpu_inc(module->refptr->decs);
 
                trace_module_put(module, _RET_IP_);
-               /* Maybe they're waiting for us to drop reference? */
-               if (unlikely(!module_is_live(module)))
-                       wake_up_process(module->waiter);
                preempt_enable();
        }
 }
@@ -2728,7 +2698,7 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags)
        return 0;
 }
 
-static void find_module_sections(struct module *mod, struct load_info *info)
+static int find_module_sections(struct module *mod, struct load_info *info)
 {
        mod->kp = section_objs(info, "__param",
                               sizeof(*mod->kp), &mod->num_kp);
@@ -2758,6 +2728,18 @@ static void find_module_sections(struct module *mod, struct load_info *info)
 #ifdef CONFIG_CONSTRUCTORS
        mod->ctors = section_objs(info, ".ctors",
                                  sizeof(*mod->ctors), &mod->num_ctors);
+       if (!mod->ctors)
+               mod->ctors = section_objs(info, ".init_array",
+                               sizeof(*mod->ctors), &mod->num_ctors);
+       else if (find_sec(info, ".init_array")) {
+               /*
+                * This shouldn't happen with same compiler and binutils
+                * building all parts of the module.
+                */
+               printk(KERN_WARNING "%s: has both .ctors and .init_array.\n",
+                      mod->name);
+               return -EINVAL;
+       }
 #endif
 
 #ifdef CONFIG_TRACEPOINTS
@@ -2795,6 +2777,8 @@ static void find_module_sections(struct module *mod, struct load_info *info)
 
        info->debug = section_objs(info, "__verbose",
                                   sizeof(*info->debug), &info->num_debug);
+
+       return 0;
 }
 
 static int move_module(struct module *mod, struct load_info *info)
@@ -3248,7 +3232,9 @@ static int load_module(struct load_info *info, const char __user *uargs,
 
        /* Now we've got everything in the final locations, we can
         * find optional sections. */
-       find_module_sections(mod, info);
+       err = find_module_sections(mod, info);
+       if (err)
+               goto free_unload;
 
        err = check_module_license_and_versions(mod);
        if (err)
index f2970bd..be5b8fa 100644 (file)
@@ -14,6 +14,7 @@
 #include <crypto/public_key.h>
 #include <crypto/hash.h>
 #include <keys/asymmetric-type.h>
+#include <keys/system_keyring.h>
 #include "module-internal.h"
 
 /*
@@ -28,7 +29,7 @@
  */
 struct module_signature {
        u8      algo;           /* Public-key crypto algorithm [enum pkey_algo] */
-       u8      hash;           /* Digest algorithm [enum pkey_hash_algo] */
+       u8      hash;           /* Digest algorithm [enum hash_algo] */
        u8      id_type;        /* Key identifier type [enum pkey_id_type] */
        u8      signer_len;     /* Length of signer's name */
        u8      key_id_len;     /* Length of key identifier */
@@ -39,7 +40,7 @@ struct module_signature {
 /*
  * Digest the module contents.
  */
-static struct public_key_signature *mod_make_digest(enum pkey_hash_algo hash,
+static struct public_key_signature *mod_make_digest(enum hash_algo hash,
                                                    const void *mod,
                                                    unsigned long modlen)
 {
@@ -54,7 +55,7 @@ static struct public_key_signature *mod_make_digest(enum pkey_hash_algo hash,
        /* Allocate the hashing algorithm we're going to need and find out how
         * big the hash operational data will be.
         */
-       tfm = crypto_alloc_shash(pkey_hash_algo[hash], 0, 0);
+       tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0);
        if (IS_ERR(tfm))
                return (PTR_ERR(tfm) == -ENOENT) ? ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
 
@@ -157,7 +158,7 @@ static struct key *request_asymmetric_key(const char *signer, size_t signer_len,
 
        pr_debug("Look up: \"%s\"\n", id);
 
-       key = keyring_search(make_key_ref(modsign_keyring, 1),
+       key = keyring_search(make_key_ref(system_trusted_keyring, 1),
                             &key_type_asymmetric, id);
        if (IS_ERR(key))
                pr_warn("Request for unknown module key '%s' err %ld\n",
@@ -217,7 +218,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
                return -ENOPKG;
 
        if (ms.hash >= PKEY_HASH__LAST ||
-           !pkey_hash_algo[ms.hash])
+           !hash_algo_name[ms.hash])
                return -ENOPKG;
 
        key = request_asymmetric_key(sig, ms.signer_len,
index 10c22ca..b38109e 100644 (file)
@@ -792,7 +792,8 @@ void free_basic_memory_bitmaps(void)
 {
        struct memory_bitmap *bm1, *bm2;
 
-       BUG_ON(!(forbidden_pages_map && free_pages_map));
+       if (WARN_ON(!(forbidden_pages_map && free_pages_map)))
+               return;
 
        bm1 = forbidden_pages_map;
        bm2 = free_pages_map;
index 2485027..98d3575 100644 (file)
@@ -70,6 +70,7 @@ static int snapshot_open(struct inode *inode, struct file *filp)
                data->swap = swsusp_resume_device ?
                        swap_type_of(swsusp_resume_device, 0, NULL) : -1;
                data->mode = O_RDONLY;
+               data->free_bitmaps = false;
                error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
                if (error)
                        pm_notifier_call_chain(PM_POST_HIBERNATION);
index 0c9a934..1254f31 100644 (file)
@@ -181,7 +181,7 @@ EXPORT_SYMBOL_GPL(rcu_irq_enter);
 /*
  * Test whether RCU thinks that the current CPU is idle.
  */
-bool __rcu_is_watching(void)
+bool notrace __rcu_is_watching(void)
 {
        return rcu_dynticks_nesting;
 }
index 4c06ddf..dd08198 100644 (file)
@@ -664,7 +664,7 @@ void rcu_nmi_exit(void)
  * rcu_is_watching(), the caller of __rcu_is_watching() must have at
  * least disabled preemption.
  */
-bool __rcu_is_watching(void)
+bool notrace __rcu_is_watching(void)
 {
        return atomic_read(this_cpu_ptr(&rcu_dynticks.dynticks)) & 0x1;
 }
@@ -675,7 +675,7 @@ bool __rcu_is_watching(void)
  * If the current CPU is in its idle loop and is neither in an interrupt
  * or NMI handler, return true.
  */
-bool rcu_is_watching(void)
+bool notrace rcu_is_watching(void)
 {
        int ret;
 
index 4611610..bd9f940 100644 (file)
@@ -15,7 +15,6 @@
 
 #include "smpboot.h"
 
-#ifdef CONFIG_USE_GENERIC_SMP_HELPERS
 enum {
        CSD_FLAG_LOCK           = 0x01,
        CSD_FLAG_WAIT           = 0x02,
@@ -140,8 +139,7 @@ static void csd_unlock(struct call_single_data *csd)
  * for execution on the given CPU. data must already have
  * ->func, ->info, and ->flags set.
  */
-static
-void generic_exec_single(int cpu, struct call_single_data *csd, int wait)
+static void generic_exec_single(int cpu, struct call_single_data *csd, int wait)
 {
        struct call_single_queue *dst = &per_cpu(call_single_queue, cpu);
        unsigned long flags;
@@ -464,7 +462,6 @@ int smp_call_function(smp_call_func_t func, void *info, int wait)
        return 0;
 }
 EXPORT_SYMBOL(smp_call_function);
-#endif /* USE_GENERIC_SMP_HELPERS */
 
 /* Setup configured maximum number of CPUs to activate */
 unsigned int setup_max_cpus = NR_CPUS;
index b249883..11025cc 100644 (file)
@@ -6,8 +6,6 @@
  *     Distribute under GPLv2.
  *
  *     Rewritten. Old one was good in 2.2, but in 2.3 it was immoral. --ANK (990903)
- *
- *     Remote softirq infrastructure is by Jens Axboe.
  */
 
 #include <linux/export.h>
@@ -627,146 +625,17 @@ void tasklet_hrtimer_init(struct tasklet_hrtimer *ttimer,
 }
 EXPORT_SYMBOL_GPL(tasklet_hrtimer_init);
 
-/*
- * Remote softirq bits
- */
-
-DEFINE_PER_CPU(struct list_head [NR_SOFTIRQS], softirq_work_list);
-EXPORT_PER_CPU_SYMBOL(softirq_work_list);
-
-static void __local_trigger(struct call_single_data *cp, int softirq)
-{
-       struct list_head *head = &__get_cpu_var(softirq_work_list[softirq]);
-
-       list_add_tail(&cp->list, head);
-
-       /* Trigger the softirq only if the list was previously empty.  */
-       if (head->next == &cp->list)
-               raise_softirq_irqoff(softirq);
-}
-
-#ifdef CONFIG_USE_GENERIC_SMP_HELPERS
-static void remote_softirq_receive(void *data)
-{
-       struct call_single_data *cp = data;
-       unsigned long flags;
-       int softirq;
-
-       softirq = *(int *)cp->info;
-       local_irq_save(flags);
-       __local_trigger(cp, softirq);
-       local_irq_restore(flags);
-}
-
-static int __try_remote_softirq(struct call_single_data *cp, int cpu, int softirq)
-{
-       if (cpu_online(cpu)) {
-               cp->func = remote_softirq_receive;
-               cp->info = &softirq;
-               cp->flags = 0;
-
-               __smp_call_function_single(cpu, cp, 0);
-               return 0;
-       }
-       return 1;
-}
-#else /* CONFIG_USE_GENERIC_SMP_HELPERS */
-static int __try_remote_softirq(struct call_single_data *cp, int cpu, int softirq)
-{
-       return 1;
-}
-#endif
-
-/**
- * __send_remote_softirq - try to schedule softirq work on a remote cpu
- * @cp: private SMP call function data area
- * @cpu: the remote cpu
- * @this_cpu: the currently executing cpu
- * @softirq: the softirq for the work
- *
- * Attempt to schedule softirq work on a remote cpu.  If this cannot be
- * done, the work is instead queued up on the local cpu.
- *
- * Interrupts must be disabled.
- */
-void __send_remote_softirq(struct call_single_data *cp, int cpu, int this_cpu, int softirq)
-{
-       if (cpu == this_cpu || __try_remote_softirq(cp, cpu, softirq))
-               __local_trigger(cp, softirq);
-}
-EXPORT_SYMBOL(__send_remote_softirq);
-
-/**
- * send_remote_softirq - try to schedule softirq work on a remote cpu
- * @cp: private SMP call function data area
- * @cpu: the remote cpu
- * @softirq: the softirq for the work
- *
- * Like __send_remote_softirq except that disabling interrupts and
- * computing the current cpu is done for the caller.
- */
-void send_remote_softirq(struct call_single_data *cp, int cpu, int softirq)
-{
-       unsigned long flags;
-       int this_cpu;
-
-       local_irq_save(flags);
-       this_cpu = smp_processor_id();
-       __send_remote_softirq(cp, cpu, this_cpu, softirq);
-       local_irq_restore(flags);
-}
-EXPORT_SYMBOL(send_remote_softirq);
-
-static int remote_softirq_cpu_notify(struct notifier_block *self,
-                                              unsigned long action, void *hcpu)
-{
-       /*
-        * If a CPU goes away, splice its entries to the current CPU
-        * and trigger a run of the softirq
-        */
-       if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) {
-               int cpu = (unsigned long) hcpu;
-               int i;
-
-               local_irq_disable();
-               for (i = 0; i < NR_SOFTIRQS; i++) {
-                       struct list_head *head = &per_cpu(softirq_work_list[i], cpu);
-                       struct list_head *local_head;
-
-                       if (list_empty(head))
-                               continue;
-
-                       local_head = &__get_cpu_var(softirq_work_list[i]);
-                       list_splice_init(head, local_head);
-                       raise_softirq_irqoff(i);
-               }
-               local_irq_enable();
-       }
-
-       return NOTIFY_OK;
-}
-
-static struct notifier_block remote_softirq_cpu_notifier = {
-       .notifier_call  = remote_softirq_cpu_notify,
-};
-
 void __init softirq_init(void)
 {
        int cpu;
 
        for_each_possible_cpu(cpu) {
-               int i;
-
                per_cpu(tasklet_vec, cpu).tail =
                        &per_cpu(tasklet_vec, cpu).head;
                per_cpu(tasklet_hi_vec, cpu).tail =
                        &per_cpu(tasklet_hi_vec, cpu).head;
-               for (i = 0; i < NR_SOFTIRQS; i++)
-                       INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));
        }
 
-       register_hotcpu_notifier(&remote_softirq_cpu_notifier);
-
        open_softirq(TASKLET_SOFTIRQ, tasklet_action);
        open_softirq(HI_SOFTIRQ, tasklet_hi_action);
 }
diff --git a/kernel/system_certificates.S b/kernel/system_certificates.S
new file mode 100644 (file)
index 0000000..4aef390
--- /dev/null
@@ -0,0 +1,10 @@
+#include <linux/export.h>
+#include <linux/init.h>
+
+       __INITRODATA
+
+       .globl VMLINUX_SYMBOL(system_certificate_list)
+VMLINUX_SYMBOL(system_certificate_list):
+       .incbin "kernel/x509_certificate_list"
+       .globl VMLINUX_SYMBOL(system_certificate_list_end)
+VMLINUX_SYMBOL(system_certificate_list_end):
diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c
new file mode 100644 (file)
index 0000000..564dd93
--- /dev/null
@@ -0,0 +1,105 @@
+/* System trusted keyring for trusted public keys
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <keys/asymmetric-type.h>
+#include <keys/system_keyring.h>
+#include "module-internal.h"
+
+struct key *system_trusted_keyring;
+EXPORT_SYMBOL_GPL(system_trusted_keyring);
+
+extern __initconst const u8 system_certificate_list[];
+extern __initconst const u8 system_certificate_list_end[];
+
+/*
+ * Load the compiled-in keys
+ */
+static __init int system_trusted_keyring_init(void)
+{
+       pr_notice("Initialise system trusted keyring\n");
+
+       system_trusted_keyring =
+               keyring_alloc(".system_keyring",
+                             KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
+                             ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                             KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH),
+                             KEY_ALLOC_NOT_IN_QUOTA, NULL);
+       if (IS_ERR(system_trusted_keyring))
+               panic("Can't allocate system trusted keyring\n");
+
+       set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags);
+       return 0;
+}
+
+/*
+ * Must be initialised before we try and load the keys into the keyring.
+ */
+device_initcall(system_trusted_keyring_init);
+
+/*
+ * Load the compiled-in list of X.509 certificates.
+ */
+static __init int load_system_certificate_list(void)
+{
+       key_ref_t key;
+       const u8 *p, *end;
+       size_t plen;
+
+       pr_notice("Loading compiled-in X.509 certificates\n");
+
+       end = system_certificate_list_end;
+       p = system_certificate_list;
+       while (p < end) {
+               /* Each cert begins with an ASN.1 SEQUENCE tag and must be more
+                * than 256 bytes in size.
+                */
+               if (end - p < 4)
+                       goto dodgy_cert;
+               if (p[0] != 0x30 &&
+                   p[1] != 0x82)
+                       goto dodgy_cert;
+               plen = (p[2] << 8) | p[3];
+               plen += 4;
+               if (plen > end - p)
+                       goto dodgy_cert;
+
+               key = key_create_or_update(make_key_ref(system_trusted_keyring, 1),
+                                          "asymmetric",
+                                          NULL,
+                                          p,
+                                          plen,
+                                          ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                                          KEY_USR_VIEW | KEY_USR_READ),
+                                          KEY_ALLOC_NOT_IN_QUOTA |
+                                          KEY_ALLOC_TRUSTED);
+               if (IS_ERR(key)) {
+                       pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
+                              PTR_ERR(key));
+               } else {
+                       pr_notice("Loaded X.509 cert '%s'\n",
+                                 key_ref_to_ptr(key)->description);
+                       key_ref_put(key);
+               }
+               p += plen;
+       }
+
+       return 0;
+
+dodgy_cert:
+       pr_err("Problem parsing in-kernel X.509 certificate list\n");
+       return 0;
+}
+late_initcall(load_system_certificate_list);
index 9f4618e..13d2f7c 100644 (file)
@@ -673,17 +673,18 @@ err:
        nlmsg_free(rep_skb);
 }
 
-static struct genl_ops taskstats_ops = {
-       .cmd            = TASKSTATS_CMD_GET,
-       .doit           = taskstats_user_cmd,
-       .policy         = taskstats_cmd_get_policy,
-       .flags          = GENL_ADMIN_PERM,
-};
-
-static struct genl_ops cgroupstats_ops = {
-       .cmd            = CGROUPSTATS_CMD_GET,
-       .doit           = cgroupstats_user_cmd,
-       .policy         = cgroupstats_cmd_get_policy,
+static const struct genl_ops taskstats_ops[] = {
+       {
+               .cmd            = TASKSTATS_CMD_GET,
+               .doit           = taskstats_user_cmd,
+               .policy         = taskstats_cmd_get_policy,
+               .flags          = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd            = CGROUPSTATS_CMD_GET,
+               .doit           = cgroupstats_user_cmd,
+               .policy         = cgroupstats_cmd_get_policy,
+       },
 };
 
 /* Needed early in initialization */
@@ -702,26 +703,13 @@ static int __init taskstats_init(void)
 {
        int rc;
 
-       rc = genl_register_family(&family);
+       rc = genl_register_family_with_ops(&family, taskstats_ops);
        if (rc)
                return rc;
 
-       rc = genl_register_ops(&family, &taskstats_ops);
-       if (rc < 0)
-               goto err;
-
-       rc = genl_register_ops(&family, &cgroupstats_ops);
-       if (rc < 0)
-               goto err_cgroup_ops;
-
        family_registered = 1;
        pr_info("registered taskstats version %d\n", TASKSTATS_GENL_VERSION);
        return 0;
-err_cgroup_ops:
-       genl_unregister_ops(&family, &taskstats_ops);
-err:
-       genl_unregister_family(&family);
-       return rc;
 }
 
 /*
index 03cf44a..22fa556 100644 (file)
@@ -3307,7 +3307,11 @@ void unregister_ftrace_function_probe_all(char *glob)
 static LIST_HEAD(ftrace_commands);
 static DEFINE_MUTEX(ftrace_cmd_mutex);
 
-int register_ftrace_command(struct ftrace_func_command *cmd)
+/*
+ * Currently we only register ftrace commands from __init, so mark this
+ * __init too.
+ */
+__init int register_ftrace_command(struct ftrace_func_command *cmd)
 {
        struct ftrace_func_command *p;
        int ret = 0;
@@ -3326,7 +3330,11 @@ int register_ftrace_command(struct ftrace_func_command *cmd)
        return ret;
 }
 
-int unregister_ftrace_command(struct ftrace_func_command *cmd)
+/*
+ * Currently we only unregister ftrace commands from __init, so mark
+ * this __init too.
+ */
+__init int unregister_ftrace_command(struct ftrace_func_command *cmd)
 {
        struct ftrace_func_command *p, *n;
        int ret = -ENODEV;
@@ -3641,7 +3649,7 @@ __setup("ftrace_filter=", set_ftrace_filter);
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 static char ftrace_graph_buf[FTRACE_FILTER_SIZE] __initdata;
-static int ftrace_set_func(unsigned long *array, int *idx, char *buffer);
+static int ftrace_set_func(unsigned long *array, int *idx, int size, char *buffer);
 
 static int __init set_graph_function(char *str)
 {
@@ -3659,7 +3667,7 @@ static void __init set_ftrace_early_graph(char *buf)
                func = strsep(&buf, ",");
                /* we allow only one expression at a time */
                ret = ftrace_set_func(ftrace_graph_funcs, &ftrace_graph_count,
-                                     func);
+                                     FTRACE_GRAPH_MAX_FUNCS, func);
                if (ret)
                        printk(KERN_DEBUG "ftrace: function %s not "
                                          "traceable\n", func);
@@ -3776,15 +3784,25 @@ static const struct file_operations ftrace_notrace_fops = {
 static DEFINE_MUTEX(graph_lock);
 
 int ftrace_graph_count;
-int ftrace_graph_filter_enabled;
+int ftrace_graph_notrace_count;
 unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly;
+unsigned long ftrace_graph_notrace_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly;
+
+struct ftrace_graph_data {
+       unsigned long *table;
+       size_t size;
+       int *count;
+       const struct seq_operations *seq_ops;
+};
 
 static void *
 __g_next(struct seq_file *m, loff_t *pos)
 {
-       if (*pos >= ftrace_graph_count)
+       struct ftrace_graph_data *fgd = m->private;
+
+       if (*pos >= *fgd->count)
                return NULL;
-       return &ftrace_graph_funcs[*pos];
+       return &fgd->table[*pos];
 }
 
 static void *
@@ -3796,10 +3814,12 @@ g_next(struct seq_file *m, void *v, loff_t *pos)
 
 static void *g_start(struct seq_file *m, loff_t *pos)
 {
+       struct ftrace_graph_data *fgd = m->private;
+
        mutex_lock(&graph_lock);
 
        /* Nothing, tell g_show to print all functions are enabled */
-       if (!ftrace_graph_filter_enabled && !*pos)
+       if (!*fgd->count && !*pos)
                return (void *)1;
 
        return __g_next(m, pos);
@@ -3835,38 +3855,88 @@ static const struct seq_operations ftrace_graph_seq_ops = {
 };
 
 static int
-ftrace_graph_open(struct inode *inode, struct file *file)
+__ftrace_graph_open(struct inode *inode, struct file *file,
+                   struct ftrace_graph_data *fgd)
 {
        int ret = 0;
 
-       if (unlikely(ftrace_disabled))
-               return -ENODEV;
-
        mutex_lock(&graph_lock);
        if ((file->f_mode & FMODE_WRITE) &&
            (file->f_flags & O_TRUNC)) {
-               ftrace_graph_filter_enabled = 0;
-               ftrace_graph_count = 0;
-               memset(ftrace_graph_funcs, 0, sizeof(ftrace_graph_funcs));
+               *fgd->count = 0;
+               memset(fgd->table, 0, fgd->size * sizeof(*fgd->table));
        }
        mutex_unlock(&graph_lock);
 
-       if (file->f_mode & FMODE_READ)
-               ret = seq_open(file, &ftrace_graph_seq_ops);
+       if (file->f_mode & FMODE_READ) {
+               ret = seq_open(file, fgd->seq_ops);
+               if (!ret) {
+                       struct seq_file *m = file->private_data;
+                       m->private = fgd;
+               }
+       } else
+               file->private_data = fgd;
 
        return ret;
 }
 
 static int
+ftrace_graph_open(struct inode *inode, struct file *file)
+{
+       struct ftrace_graph_data *fgd;
+
+       if (unlikely(ftrace_disabled))
+               return -ENODEV;
+
+       fgd = kmalloc(sizeof(*fgd), GFP_KERNEL);
+       if (fgd == NULL)
+               return -ENOMEM;
+
+       fgd->table = ftrace_graph_funcs;
+       fgd->size = FTRACE_GRAPH_MAX_FUNCS;
+       fgd->count = &ftrace_graph_count;
+       fgd->seq_ops = &ftrace_graph_seq_ops;
+
+       return __ftrace_graph_open(inode, file, fgd);
+}
+
+static int
+ftrace_graph_notrace_open(struct inode *inode, struct file *file)
+{
+       struct ftrace_graph_data *fgd;
+
+       if (unlikely(ftrace_disabled))
+               return -ENODEV;
+
+       fgd = kmalloc(sizeof(*fgd), GFP_KERNEL);
+       if (fgd == NULL)
+               return -ENOMEM;
+
+       fgd->table = ftrace_graph_notrace_funcs;
+       fgd->size = FTRACE_GRAPH_MAX_FUNCS;
+       fgd->count = &ftrace_graph_notrace_count;
+       fgd->seq_ops = &ftrace_graph_seq_ops;
+
+       return __ftrace_graph_open(inode, file, fgd);
+}
+
+static int
 ftrace_graph_release(struct inode *inode, struct file *file)
 {
-       if (file->f_mode & FMODE_READ)
+       if (file->f_mode & FMODE_READ) {
+               struct seq_file *m = file->private_data;
+
+               kfree(m->private);
                seq_release(inode, file);
+       } else {
+               kfree(file->private_data);
+       }
+
        return 0;
 }
 
 static int
-ftrace_set_func(unsigned long *array, int *idx, char *buffer)
+ftrace_set_func(unsigned long *array, int *idx, int size, char *buffer)
 {
        struct dyn_ftrace *rec;
        struct ftrace_page *pg;
@@ -3879,7 +3949,7 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer)
 
        /* decode regex */
        type = filter_parse_regex(buffer, strlen(buffer), &search, &not);
-       if (!not && *idx >= FTRACE_GRAPH_MAX_FUNCS)
+       if (!not && *idx >= size)
                return -EBUSY;
 
        search_len = strlen(search);
@@ -3907,7 +3977,7 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer)
                                fail = 0;
                                if (!exists) {
                                        array[(*idx)++] = rec->ip;
-                                       if (*idx >= FTRACE_GRAPH_MAX_FUNCS)
+                                       if (*idx >= size)
                                                goto out;
                                }
                        } else {
@@ -3925,8 +3995,6 @@ out:
        if (fail)
                return -EINVAL;
 
-       ftrace_graph_filter_enabled = !!(*idx);
-
        return 0;
 }
 
@@ -3935,36 +4003,33 @@ ftrace_graph_write(struct file *file, const char __user *ubuf,
                   size_t cnt, loff_t *ppos)
 {
        struct trace_parser parser;
-       ssize_t read, ret;
+       ssize_t read, ret = 0;
+       struct ftrace_graph_data *fgd = file->private_data;
 
        if (!cnt)
                return 0;
 
-       mutex_lock(&graph_lock);
-
-       if (trace_parser_get_init(&parser, FTRACE_BUFF_MAX)) {
-               ret = -ENOMEM;
-               goto out_unlock;
-       }
+       if (trace_parser_get_init(&parser, FTRACE_BUFF_MAX))
+               return -ENOMEM;
 
        read = trace_get_user(&parser, ubuf, cnt, ppos);
 
        if (read >= 0 && trace_parser_loaded((&parser))) {
                parser.buffer[parser.idx] = 0;
 
+               mutex_lock(&graph_lock);
+
                /* we allow only one expression at a time */
-               ret = ftrace_set_func(ftrace_graph_funcs, &ftrace_graph_count,
-                                       parser.buffer);
-               if (ret)
-                       goto out_free;
+               ret = ftrace_set_func(fgd->table, fgd->count, fgd->size,
+                                     parser.buffer);
+
+               mutex_unlock(&graph_lock);
        }
 
-       ret = read;
+       if (!ret)
+               ret = read;
 
-out_free:
        trace_parser_put(&parser);
-out_unlock:
-       mutex_unlock(&graph_lock);
 
        return ret;
 }
@@ -3976,6 +4041,14 @@ static const struct file_operations ftrace_graph_fops = {
        .llseek         = ftrace_filter_lseek,
        .release        = ftrace_graph_release,
 };
+
+static const struct file_operations ftrace_graph_notrace_fops = {
+       .open           = ftrace_graph_notrace_open,
+       .read           = seq_read,
+       .write          = ftrace_graph_write,
+       .llseek         = ftrace_filter_lseek,
+       .release        = ftrace_graph_release,
+};
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 
 static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
@@ -3997,6 +4070,9 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
        trace_create_file("set_graph_function", 0444, d_tracer,
                                    NULL,
                                    &ftrace_graph_fops);
+       trace_create_file("set_graph_notrace", 0444, d_tracer,
+                                   NULL,
+                                   &ftrace_graph_notrace_fops);
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 
        return 0;
@@ -4320,12 +4396,21 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip,
         */
        preempt_disable_notrace();
        trace_recursion_set(TRACE_CONTROL_BIT);
+
+       /*
+        * Control funcs (perf) uses RCU. Only trace if
+        * RCU is currently active.
+        */
+       if (!rcu_is_watching())
+               goto out;
+
        do_for_each_ftrace_op(op, ftrace_control_list) {
                if (!(op->flags & FTRACE_OPS_FL_STUB) &&
                    !ftrace_function_local_disabled(op) &&
                    ftrace_ops_test(op, ip, regs))
                        op->func(ip, parent_ip, op, regs);
        } while_for_each_ftrace_op(op);
+ out:
        trace_recursion_clear(TRACE_CONTROL_BIT);
        preempt_enable_notrace();
 }
index d9fea7d..9d20cd9 100644 (file)
@@ -235,13 +235,33 @@ void trace_array_put(struct trace_array *this_tr)
        mutex_unlock(&trace_types_lock);
 }
 
-int filter_current_check_discard(struct ring_buffer *buffer,
-                                struct ftrace_event_call *call, void *rec,
-                                struct ring_buffer_event *event)
+int filter_check_discard(struct ftrace_event_file *file, void *rec,
+                        struct ring_buffer *buffer,
+                        struct ring_buffer_event *event)
 {
-       return filter_check_discard(call, rec, buffer, event);
+       if (unlikely(file->flags & FTRACE_EVENT_FL_FILTERED) &&
+           !filter_match_preds(file->filter, rec)) {
+               ring_buffer_discard_commit(buffer, event);
+               return 1;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(filter_check_discard);
+
+int call_filter_check_discard(struct ftrace_event_call *call, void *rec,
+                             struct ring_buffer *buffer,
+                             struct ring_buffer_event *event)
+{
+       if (unlikely(call->flags & TRACE_EVENT_FL_FILTERED) &&
+           !filter_match_preds(call->filter, rec)) {
+               ring_buffer_discard_commit(buffer, event);
+               return 1;
+       }
+
+       return 0;
 }
-EXPORT_SYMBOL_GPL(filter_current_check_discard);
+EXPORT_SYMBOL_GPL(call_filter_check_discard);
 
 cycle_t buffer_ftrace_now(struct trace_buffer *buf, int cpu)
 {
@@ -843,9 +863,12 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
        if (isspace(ch)) {
                parser->buffer[parser->idx] = 0;
                parser->cont = false;
-       } else {
+       } else if (parser->idx < parser->size - 1) {
                parser->cont = true;
                parser->buffer[parser->idx++] = ch;
+       } else {
+               ret = -EINVAL;
+               goto out;
        }
 
        *ppos += read;
@@ -1261,21 +1284,6 @@ int is_tracing_stopped(void)
 }
 
 /**
- * ftrace_off_permanent - disable all ftrace code permanently
- *
- * This should only be called when a serious anomally has
- * been detected.  This will turn off the function tracing,
- * ring buffers, and other tracing utilites. It takes no
- * locks and can be called from any context.
- */
-void ftrace_off_permanent(void)
-{
-       tracing_disabled = 1;
-       ftrace_stop();
-       tracing_off_permanent();
-}
-
-/**
  * tracing_start - quick start of the tracer
  *
  * If tracing is enabled but was stopped by tracing_stop,
@@ -1631,7 +1639,7 @@ trace_function(struct trace_array *tr,
        entry->ip                       = ip;
        entry->parent_ip                = parent_ip;
 
-       if (!filter_check_discard(call, entry, buffer, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                __buffer_unlock_commit(buffer, event);
 }
 
@@ -1715,7 +1723,7 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer,
 
        entry->size = trace.nr_entries;
 
-       if (!filter_check_discard(call, entry, buffer, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                __buffer_unlock_commit(buffer, event);
 
  out:
@@ -1817,7 +1825,7 @@ ftrace_trace_userstack(struct ring_buffer *buffer, unsigned long flags, int pc)
        trace.entries           = entry->caller;
 
        save_stack_trace_user(&trace);
-       if (!filter_check_discard(call, entry, buffer, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                __buffer_unlock_commit(buffer, event);
 
  out_drop_count:
@@ -2009,7 +2017,7 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
        entry->fmt                      = fmt;
 
        memcpy(entry->buf, tbuffer, sizeof(u32) * len);
-       if (!filter_check_discard(call, entry, buffer, event)) {
+       if (!call_filter_check_discard(call, entry, buffer, event)) {
                __buffer_unlock_commit(buffer, event);
                ftrace_trace_stack(buffer, flags, 6, pc);
        }
@@ -2064,7 +2072,7 @@ __trace_array_vprintk(struct ring_buffer *buffer,
 
        memcpy(&entry->buf, tbuffer, len);
        entry->buf[len] = '\0';
-       if (!filter_check_discard(call, entry, buffer, event)) {
+       if (!call_filter_check_discard(call, entry, buffer, event)) {
                __buffer_unlock_commit(buffer, event);
                ftrace_trace_stack(buffer, flags, 6, pc);
        }
@@ -2761,7 +2769,7 @@ static void show_snapshot_main_help(struct seq_file *m)
        seq_printf(m, "# echo 0 > snapshot : Clears and frees snapshot buffer\n");
        seq_printf(m, "# echo 1 > snapshot : Allocates snapshot buffer, if not already allocated.\n");
        seq_printf(m, "#                      Takes a snapshot of the main buffer.\n");
-       seq_printf(m, "# echo 2 > snapshot : Clears snapshot buffer (but does not allocate)\n");
+       seq_printf(m, "# echo 2 > snapshot : Clears snapshot buffer (but does not allocate or free)\n");
        seq_printf(m, "#                      (Doesn't have to be '2' works with any number that\n");
        seq_printf(m, "#                       is not a '0' or '1')\n");
 }
@@ -2965,6 +2973,11 @@ int tracing_open_generic(struct inode *inode, struct file *filp)
        return 0;
 }
 
+bool tracing_is_disabled(void)
+{
+       return (tracing_disabled) ? true: false;
+}
+
 /*
  * Open and update trace_array ref count.
  * Must have the current trace_array passed to it.
@@ -5455,12 +5468,12 @@ static struct ftrace_func_command ftrace_snapshot_cmd = {
        .func                   = ftrace_trace_snapshot_callback,
 };
 
-static int register_snapshot_cmd(void)
+static __init int register_snapshot_cmd(void)
 {
        return register_ftrace_command(&ftrace_snapshot_cmd);
 }
 #else
-static inline int register_snapshot_cmd(void) { return 0; }
+static inline __init int register_snapshot_cmd(void) { return 0; }
 #endif /* defined(CONFIG_TRACER_SNAPSHOT) && defined(CONFIG_DYNAMIC_FTRACE) */
 
 struct dentry *tracing_init_dentry_tr(struct trace_array *tr)
@@ -6254,6 +6267,17 @@ void trace_init_global_iter(struct trace_iterator *iter)
        iter->trace = iter->tr->current_trace;
        iter->cpu_file = RING_BUFFER_ALL_CPUS;
        iter->trace_buffer = &global_trace.trace_buffer;
+
+       if (iter->trace && iter->trace->open)
+               iter->trace->open(iter);
+
+       /* Annotate start of buffers if we had overruns */
+       if (ring_buffer_overruns(iter->trace_buffer->buffer))
+               iter->iter_flags |= TRACE_FILE_ANNOTATE;
+
+       /* Output in nanoseconds only if we are using a clock in nanoseconds. */
+       if (trace_clocks[iter->tr->clock_id].in_ns)
+               iter->iter_flags |= TRACE_FILE_TIME_IN_NS;
 }
 
 void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
index 73d08aa..ea189e0 100644 (file)
@@ -193,8 +193,8 @@ struct trace_array {
 #ifdef CONFIG_FTRACE_SYSCALLS
        int                     sys_refcount_enter;
        int                     sys_refcount_exit;
-       DECLARE_BITMAP(enabled_enter_syscalls, NR_syscalls);
-       DECLARE_BITMAP(enabled_exit_syscalls, NR_syscalls);
+       struct ftrace_event_file __rcu *enter_syscall_files[NR_syscalls];
+       struct ftrace_event_file __rcu *exit_syscall_files[NR_syscalls];
 #endif
        int                     stop_count;
        int                     clock_id;
@@ -515,6 +515,7 @@ void tracing_reset_online_cpus(struct trace_buffer *buf);
 void tracing_reset_current(int cpu);
 void tracing_reset_all_online_cpus(void);
 int tracing_open_generic(struct inode *inode, struct file *filp);
+bool tracing_is_disabled(void);
 struct dentry *trace_create_file(const char *name,
                                 umode_t mode,
                                 struct dentry *parent,
@@ -712,6 +713,8 @@ extern unsigned long trace_flags;
 #define TRACE_GRAPH_PRINT_PROC          0x8
 #define TRACE_GRAPH_PRINT_DURATION      0x10
 #define TRACE_GRAPH_PRINT_ABS_TIME      0x20
+#define TRACE_GRAPH_PRINT_FILL_SHIFT   28
+#define TRACE_GRAPH_PRINT_FILL_MASK    (0x3 << TRACE_GRAPH_PRINT_FILL_SHIFT)
 
 extern enum print_line_t
 print_graph_function_flags(struct trace_iterator *iter, u32 flags);
@@ -731,15 +734,16 @@ extern void __trace_graph_return(struct trace_array *tr,
 #ifdef CONFIG_DYNAMIC_FTRACE
 /* TODO: make this variable */
 #define FTRACE_GRAPH_MAX_FUNCS         32
-extern int ftrace_graph_filter_enabled;
 extern int ftrace_graph_count;
 extern unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS];
+extern int ftrace_graph_notrace_count;
+extern unsigned long ftrace_graph_notrace_funcs[FTRACE_GRAPH_MAX_FUNCS];
 
 static inline int ftrace_graph_addr(unsigned long addr)
 {
        int i;
 
-       if (!ftrace_graph_filter_enabled)
+       if (!ftrace_graph_count)
                return 1;
 
        for (i = 0; i < ftrace_graph_count; i++) {
@@ -759,11 +763,31 @@ static inline int ftrace_graph_addr(unsigned long addr)
 
        return 0;
 }
+
+static inline int ftrace_graph_notrace_addr(unsigned long addr)
+{
+       int i;
+
+       if (!ftrace_graph_notrace_count)
+               return 0;
+
+       for (i = 0; i < ftrace_graph_notrace_count; i++) {
+               if (addr == ftrace_graph_notrace_funcs[i])
+                       return 1;
+       }
+
+       return 0;
+}
 #else
 static inline int ftrace_graph_addr(unsigned long addr)
 {
        return 1;
 }
+
+static inline int ftrace_graph_notrace_addr(unsigned long addr)
+{
+       return 0;
+}
 #endif /* CONFIG_DYNAMIC_FTRACE */
 #else /* CONFIG_FUNCTION_GRAPH_TRACER */
 static inline enum print_line_t
@@ -987,9 +1011,9 @@ struct filter_pred {
 
 extern enum regex_type
 filter_parse_regex(char *buff, int len, char **search, int *not);
-extern void print_event_filter(struct ftrace_event_call *call,
+extern void print_event_filter(struct ftrace_event_file *file,
                               struct trace_seq *s);
-extern int apply_event_filter(struct ftrace_event_call *call,
+extern int apply_event_filter(struct ftrace_event_file *file,
                              char *filter_string);
 extern int apply_subsystem_event_filter(struct ftrace_subsystem_dir *dir,
                                        char *filter_string);
@@ -1000,20 +1024,6 @@ extern int filter_assign_type(const char *type);
 struct ftrace_event_field *
 trace_find_event_field(struct ftrace_event_call *call, char *name);
 
-static inline int
-filter_check_discard(struct ftrace_event_call *call, void *rec,
-                    struct ring_buffer *buffer,
-                    struct ring_buffer_event *event)
-{
-       if (unlikely(call->flags & TRACE_EVENT_FL_FILTERED) &&
-           !filter_match_preds(call->filter, rec)) {
-               ring_buffer_discard_commit(buffer, event);
-               return 1;
-       }
-
-       return 0;
-}
-
 extern void trace_event_enable_cmd_record(bool enable);
 extern int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr);
 extern int event_trace_del_tracer(struct trace_array *tr);
index d594da0..697fb9b 100644 (file)
@@ -78,7 +78,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
        entry->line = f->line;
        entry->correct = val == expect;
 
-       if (!filter_check_discard(call, entry, buffer, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                __buffer_unlock_commit(buffer, event);
 
  out:
index 368a4d5..f919a2e 100644 (file)
@@ -989,7 +989,7 @@ static ssize_t
 event_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
                  loff_t *ppos)
 {
-       struct ftrace_event_call *call;
+       struct ftrace_event_file *file;
        struct trace_seq *s;
        int r = -ENODEV;
 
@@ -1004,12 +1004,12 @@ event_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
        trace_seq_init(s);
 
        mutex_lock(&event_mutex);
-       call = event_file_data(filp);
-       if (call)
-               print_event_filter(call, s);
+       file = event_file_data(filp);
+       if (file)
+               print_event_filter(file, s);
        mutex_unlock(&event_mutex);
 
-       if (call)
+       if (file)
                r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
 
        kfree(s);
@@ -1021,7 +1021,7 @@ static ssize_t
 event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
                   loff_t *ppos)
 {
-       struct ftrace_event_call *call;
+       struct ftrace_event_file *file;
        char *buf;
        int err = -ENODEV;
 
@@ -1039,9 +1039,9 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
        buf[cnt] = '\0';
 
        mutex_lock(&event_mutex);
-       call = event_file_data(filp);
-       if (call)
-               err = apply_event_filter(call, buf);
+       file = event_file_data(filp);
+       if (file)
+               err = apply_event_filter(file, buf);
        mutex_unlock(&event_mutex);
 
        free_page((unsigned long) buf);
@@ -1062,6 +1062,9 @@ static int subsystem_open(struct inode *inode, struct file *filp)
        struct trace_array *tr;
        int ret;
 
+       if (tracing_is_disabled())
+               return -ENODEV;
+
        /* Make sure the system still exists */
        mutex_lock(&trace_types_lock);
        mutex_lock(&event_mutex);
@@ -1108,6 +1111,9 @@ static int system_tr_open(struct inode *inode, struct file *filp)
        struct trace_array *tr = inode->i_private;
        int ret;
 
+       if (tracing_is_disabled())
+               return -ENODEV;
+
        if (trace_array_get(tr) < 0)
                return -ENODEV;
 
@@ -1124,11 +1130,12 @@ static int system_tr_open(struct inode *inode, struct file *filp)
        if (ret < 0) {
                trace_array_put(tr);
                kfree(dir);
+               return ret;
        }
 
        filp->private_data = dir;
 
-       return ret;
+       return 0;
 }
 
 static int subsystem_release(struct inode *inode, struct file *file)
@@ -1539,7 +1546,7 @@ event_create_dir(struct dentry *parent, struct ftrace_event_file *file)
                        return -1;
                }
        }
-       trace_create_file("filter", 0644, file->dir, call,
+       trace_create_file("filter", 0644, file->dir, file,
                          &ftrace_event_filter_fops);
 
        trace_create_file("format", 0444, file->dir, call,
@@ -1577,6 +1584,7 @@ static void event_remove(struct ftrace_event_call *call)
                if (file->event_call != call)
                        continue;
                ftrace_event_enable_disable(file, 0);
+               destroy_preds(file);
                /*
                 * The do_for_each_event_file() is
                 * a double loop. After finding the call for this
@@ -1700,7 +1708,7 @@ static void __trace_remove_event_call(struct ftrace_event_call *call)
 {
        event_remove(call);
        trace_destroy_fields(call);
-       destroy_preds(call);
+       destroy_call_preds(call);
 }
 
 static int probe_remove_event_call(struct ftrace_event_call *call)
index 97daa8c..2468f56 100644 (file)
@@ -637,10 +637,18 @@ static void append_filter_err(struct filter_parse_state *ps,
        free_page((unsigned long) buf);
 }
 
+static inline struct event_filter *event_filter(struct ftrace_event_file *file)
+{
+       if (file->event_call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               return file->event_call->filter;
+       else
+               return file->filter;
+}
+
 /* caller must hold event_mutex */
-void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s)
+void print_event_filter(struct ftrace_event_file *file, struct trace_seq *s)
 {
-       struct event_filter *filter = call->filter;
+       struct event_filter *filter = event_filter(file);
 
        if (filter && filter->filter_string)
                trace_seq_printf(s, "%s\n", filter->filter_string);
@@ -766,11 +774,21 @@ static void __free_preds(struct event_filter *filter)
        filter->n_preds = 0;
 }
 
-static void filter_disable(struct ftrace_event_call *call)
+static void call_filter_disable(struct ftrace_event_call *call)
 {
        call->flags &= ~TRACE_EVENT_FL_FILTERED;
 }
 
+static void filter_disable(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               call_filter_disable(call);
+       else
+               file->flags &= ~FTRACE_EVENT_FL_FILTERED;
+}
+
 static void __free_filter(struct event_filter *filter)
 {
        if (!filter)
@@ -781,16 +799,30 @@ static void __free_filter(struct event_filter *filter)
        kfree(filter);
 }
 
+void destroy_call_preds(struct ftrace_event_call *call)
+{
+       __free_filter(call->filter);
+       call->filter = NULL;
+}
+
+static void destroy_file_preds(struct ftrace_event_file *file)
+{
+       __free_filter(file->filter);
+       file->filter = NULL;
+}
+
 /*
- * Called when destroying the ftrace_event_call.
- * The call is being freed, so we do not need to worry about
- * the call being currently used. This is for module code removing
+ * Called when destroying the ftrace_event_file.
+ * The file is being freed, so we do not need to worry about
+ * the file being currently used. This is for module code removing
  * the tracepoints from within it.
  */
-void destroy_preds(struct ftrace_event_call *call)
+void destroy_preds(struct ftrace_event_file *file)
 {
-       __free_filter(call->filter);
-       call->filter = NULL;
+       if (file->event_call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               destroy_call_preds(file->event_call);
+       else
+               destroy_file_preds(file);
 }
 
 static struct event_filter *__alloc_filter(void)
@@ -825,28 +857,56 @@ static int __alloc_preds(struct event_filter *filter, int n_preds)
        return 0;
 }
 
-static void filter_free_subsystem_preds(struct event_subsystem *system)
+static inline void __remove_filter(struct ftrace_event_file *file)
 {
+       struct ftrace_event_call *call = file->event_call;
+
+       filter_disable(file);
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               remove_filter_string(call->filter);
+       else
+               remove_filter_string(file->filter);
+}
+
+static void filter_free_subsystem_preds(struct event_subsystem *system,
+                                       struct trace_array *tr)
+{
+       struct ftrace_event_file *file;
        struct ftrace_event_call *call;
 
-       list_for_each_entry(call, &ftrace_events, list) {
+       list_for_each_entry(file, &tr->events, list) {
+               call = file->event_call;
                if (strcmp(call->class->system, system->name) != 0)
                        continue;
 
-               filter_disable(call);
-               remove_filter_string(call->filter);
+               __remove_filter(file);
        }
 }
 
-static void filter_free_subsystem_filters(struct event_subsystem *system)
+static inline void __free_subsystem_filter(struct ftrace_event_file *file)
 {
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER) {
+               __free_filter(call->filter);
+               call->filter = NULL;
+       } else {
+               __free_filter(file->filter);
+               file->filter = NULL;
+       }
+}
+
+static void filter_free_subsystem_filters(struct event_subsystem *system,
+                                         struct trace_array *tr)
+{
+       struct ftrace_event_file *file;
        struct ftrace_event_call *call;
 
-       list_for_each_entry(call, &ftrace_events, list) {
+       list_for_each_entry(file, &tr->events, list) {
+               call = file->event_call;
                if (strcmp(call->class->system, system->name) != 0)
                        continue;
-               __free_filter(call->filter);
-               call->filter = NULL;
+               __free_subsystem_filter(file);
        }
 }
 
@@ -1617,15 +1677,85 @@ fail:
        return err;
 }
 
+static inline void event_set_filtered_flag(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               call->flags |= TRACE_EVENT_FL_FILTERED;
+       else
+               file->flags |= FTRACE_EVENT_FL_FILTERED;
+}
+
+static inline void event_set_filter(struct ftrace_event_file *file,
+                                   struct event_filter *filter)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               rcu_assign_pointer(call->filter, filter);
+       else
+               rcu_assign_pointer(file->filter, filter);
+}
+
+static inline void event_clear_filter(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               RCU_INIT_POINTER(call->filter, NULL);
+       else
+               RCU_INIT_POINTER(file->filter, NULL);
+}
+
+static inline void
+event_set_no_set_filter_flag(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               call->flags |= TRACE_EVENT_FL_NO_SET_FILTER;
+       else
+               file->flags |= FTRACE_EVENT_FL_NO_SET_FILTER;
+}
+
+static inline void
+event_clear_no_set_filter_flag(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               call->flags &= ~TRACE_EVENT_FL_NO_SET_FILTER;
+       else
+               file->flags &= ~FTRACE_EVENT_FL_NO_SET_FILTER;
+}
+
+static inline bool
+event_no_set_filter_flag(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (file->flags & FTRACE_EVENT_FL_NO_SET_FILTER)
+               return true;
+
+       if ((call->flags & TRACE_EVENT_FL_USE_CALL_FILTER) &&
+           (call->flags & TRACE_EVENT_FL_NO_SET_FILTER))
+               return true;
+
+       return false;
+}
+
 struct filter_list {
        struct list_head        list;
        struct event_filter     *filter;
 };
 
 static int replace_system_preds(struct event_subsystem *system,
+                               struct trace_array *tr,
                                struct filter_parse_state *ps,
                                char *filter_string)
 {
+       struct ftrace_event_file *file;
        struct ftrace_event_call *call;
        struct filter_list *filter_item;
        struct filter_list *tmp;
@@ -1633,8 +1763,8 @@ static int replace_system_preds(struct event_subsystem *system,
        bool fail = true;
        int err;
 
-       list_for_each_entry(call, &ftrace_events, list) {
-
+       list_for_each_entry(file, &tr->events, list) {
+               call = file->event_call;
                if (strcmp(call->class->system, system->name) != 0)
                        continue;
 
@@ -1644,18 +1774,20 @@ static int replace_system_preds(struct event_subsystem *system,
                 */
                err = replace_preds(call, NULL, ps, filter_string, true);
                if (err)
-                       call->flags |= TRACE_EVENT_FL_NO_SET_FILTER;
+                       event_set_no_set_filter_flag(file);
                else
-                       call->flags &= ~TRACE_EVENT_FL_NO_SET_FILTER;
+                       event_clear_no_set_filter_flag(file);
        }
 
-       list_for_each_entry(call, &ftrace_events, list) {
+       list_for_each_entry(file, &tr->events, list) {
                struct event_filter *filter;
 
+               call = file->event_call;
+
                if (strcmp(call->class->system, system->name) != 0)
                        continue;
 
-               if (call->flags & TRACE_EVENT_FL_NO_SET_FILTER)
+               if (event_no_set_filter_flag(file))
                        continue;
 
                filter_item = kzalloc(sizeof(*filter_item), GFP_KERNEL);
@@ -1676,17 +1808,17 @@ static int replace_system_preds(struct event_subsystem *system,
 
                err = replace_preds(call, filter, ps, filter_string, false);
                if (err) {
-                       filter_disable(call);
+                       filter_disable(file);
                        parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0);
                        append_filter_err(ps, filter);
                } else
-                       call->flags |= TRACE_EVENT_FL_FILTERED;
+                       event_set_filtered_flag(file);
                /*
                 * Regardless of if this returned an error, we still
                 * replace the filter for the call.
                 */
-               filter = call->filter;
-               rcu_assign_pointer(call->filter, filter_item->filter);
+               filter = event_filter(file);
+               event_set_filter(file, filter_item->filter);
                filter_item->filter = filter;
 
                fail = false;
@@ -1816,6 +1948,7 @@ static int create_filter(struct ftrace_event_call *call,
  * and always remembers @filter_str.
  */
 static int create_system_filter(struct event_subsystem *system,
+                               struct trace_array *tr,
                                char *filter_str, struct event_filter **filterp)
 {
        struct event_filter *filter = NULL;
@@ -1824,7 +1957,7 @@ static int create_system_filter(struct event_subsystem *system,
 
        err = create_filter_start(filter_str, true, &ps, &filter);
        if (!err) {
-               err = replace_system_preds(system, ps, filter_str);
+               err = replace_system_preds(system, tr, ps, filter_str);
                if (!err) {
                        /* System filters just show a default message */
                        kfree(filter->filter_string);
@@ -1840,20 +1973,25 @@ static int create_system_filter(struct event_subsystem *system,
 }
 
 /* caller must hold event_mutex */
-int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
+int apply_event_filter(struct ftrace_event_file *file, char *filter_string)
 {
+       struct ftrace_event_call *call = file->event_call;
        struct event_filter *filter;
        int err;
 
        if (!strcmp(strstrip(filter_string), "0")) {
-               filter_disable(call);
-               filter = call->filter;
+               filter_disable(file);
+               filter = event_filter(file);
+
                if (!filter)
                        return 0;
-               RCU_INIT_POINTER(call->filter, NULL);
+
+               event_clear_filter(file);
+
                /* Make sure the filter is not being used */
                synchronize_sched();
                __free_filter(filter);
+
                return 0;
        }
 
@@ -1866,14 +2004,15 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
         * string
         */
        if (filter) {
-               struct event_filter *tmp = call->filter;
+               struct event_filter *tmp;
 
+               tmp = event_filter(file);
                if (!err)
-                       call->flags |= TRACE_EVENT_FL_FILTERED;
+                       event_set_filtered_flag(file);
                else
-                       filter_disable(call);
+                       filter_disable(file);
 
-               rcu_assign_pointer(call->filter, filter);
+               event_set_filter(file, filter);
 
                if (tmp) {
                        /* Make sure the call is done with the filter */
@@ -1889,6 +2028,7 @@ int apply_subsystem_event_filter(struct ftrace_subsystem_dir *dir,
                                 char *filter_string)
 {
        struct event_subsystem *system = dir->subsystem;
+       struct trace_array *tr = dir->tr;
        struct event_filter *filter;
        int err = 0;
 
@@ -1901,18 +2041,18 @@ int apply_subsystem_event_filter(struct ftrace_subsystem_dir *dir,
        }
 
        if (!strcmp(strstrip(filter_string), "0")) {
-               filter_free_subsystem_preds(system);
+               filter_free_subsystem_preds(system, tr);
                remove_filter_string(system->filter);
                filter = system->filter;
                system->filter = NULL;
                /* Ensure all filters are no longer used */
                synchronize_sched();
-               filter_free_subsystem_filters(system);
+               filter_free_subsystem_filters(system, tr);
                __free_filter(filter);
                goto out_unlock;
        }
 
-       err = create_system_filter(system, filter_string, &filter);
+       err = create_system_filter(system, tr, filter_string, &filter);
        if (filter) {
                /*
                 * No event actually uses the system filter
index d21a746..7c3e3e7 100644 (file)
@@ -180,7 +180,7 @@ struct ftrace_event_call __used event_##call = {                    \
        .event.type             = etype,                                \
        .class                  = &event_class_ftrace_##call,           \
        .print_fmt              = print,                                \
-       .flags                  = TRACE_EVENT_FL_IGNORE_ENABLE,         \
+       .flags                  = TRACE_EVENT_FL_IGNORE_ENABLE | TRACE_EVENT_FL_USE_CALL_FILTER, \
 };                                                                     \
 struct ftrace_event_call __used                                                \
 __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call;
index b5c0924..0b99120 100644 (file)
@@ -82,9 +82,9 @@ static struct trace_array *graph_array;
  * to fill in space into DURATION column.
  */
 enum {
-       DURATION_FILL_FULL  = -1,
-       DURATION_FILL_START = -2,
-       DURATION_FILL_END   = -3,
+       FLAGS_FILL_FULL  = 1 << TRACE_GRAPH_PRINT_FILL_SHIFT,
+       FLAGS_FILL_START = 2 << TRACE_GRAPH_PRINT_FILL_SHIFT,
+       FLAGS_FILL_END   = 3 << TRACE_GRAPH_PRINT_FILL_SHIFT,
 };
 
 static enum print_line_t
@@ -114,16 +114,37 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth,
                return -EBUSY;
        }
 
+       /*
+        * The curr_ret_stack is an index to ftrace return stack of
+        * current task.  Its value should be in [0, FTRACE_RETFUNC_
+        * DEPTH) when the function graph tracer is used.  To support
+        * filtering out specific functions, it makes the index
+        * negative by subtracting huge value (FTRACE_NOTRACE_DEPTH)
+        * so when it sees a negative index the ftrace will ignore
+        * the record.  And the index gets recovered when returning
+        * from the filtered function by adding the FTRACE_NOTRACE_
+        * DEPTH and then it'll continue to record functions normally.
+        *
+        * The curr_ret_stack is initialized to -1 and get increased
+        * in this function.  So it can be less than -1 only if it was
+        * filtered out via ftrace_graph_notrace_addr() which can be
+        * set from set_graph_notrace file in debugfs by user.
+        */
+       if (current->curr_ret_stack < -1)
+               return -EBUSY;
+
        calltime = trace_clock_local();
 
        index = ++current->curr_ret_stack;
+       if (ftrace_graph_notrace_addr(func))
+               current->curr_ret_stack -= FTRACE_NOTRACE_DEPTH;
        barrier();
        current->ret_stack[index].ret = ret;
        current->ret_stack[index].func = func;
        current->ret_stack[index].calltime = calltime;
        current->ret_stack[index].subtime = 0;
        current->ret_stack[index].fp = frame_pointer;
-       *depth = index;
+       *depth = current->curr_ret_stack;
 
        return 0;
 }
@@ -137,7 +158,17 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret,
 
        index = current->curr_ret_stack;
 
-       if (unlikely(index < 0)) {
+       /*
+        * A negative index here means that it's just returned from a
+        * notrace'd function.  Recover index to get an original
+        * return address.  See ftrace_push_return_trace().
+        *
+        * TODO: Need to check whether the stack gets corrupted.
+        */
+       if (index < 0)
+               index += FTRACE_NOTRACE_DEPTH;
+
+       if (unlikely(index < 0 || index >= FTRACE_RETFUNC_DEPTH)) {
                ftrace_graph_stop();
                WARN_ON(1);
                /* Might as well panic, otherwise we have no where to go */
@@ -193,6 +224,15 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)
        trace.rettime = trace_clock_local();
        barrier();
        current->curr_ret_stack--;
+       /*
+        * The curr_ret_stack can be less than -1 only if it was
+        * filtered out and it's about to return from the function.
+        * Recover the index and continue to trace normal functions.
+        */
+       if (current->curr_ret_stack < -1) {
+               current->curr_ret_stack += FTRACE_NOTRACE_DEPTH;
+               return ret;
+       }
 
        /*
         * The trace should run after decrementing the ret counter
@@ -230,7 +270,7 @@ int __trace_graph_entry(struct trace_array *tr,
                return 0;
        entry   = ring_buffer_event_data(event);
        entry->graph_ent                        = *trace;
-       if (!filter_current_check_discard(buffer, call, entry, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                __buffer_unlock_commit(buffer, event);
 
        return 1;
@@ -259,10 +299,20 @@ int trace_graph_entry(struct ftrace_graph_ent *trace)
 
        /* trace it when it is-nested-in or is a function enabled. */
        if ((!(trace->depth || ftrace_graph_addr(trace->func)) ||
-            ftrace_graph_ignore_irqs()) ||
+            ftrace_graph_ignore_irqs()) || (trace->depth < 0) ||
            (max_depth && trace->depth >= max_depth))
                return 0;
 
+       /*
+        * Do not trace a function if it's filtered by set_graph_notrace.
+        * Make the index of ret stack negative to indicate that it should
+        * ignore further functions.  But it needs its own ret stack entry
+        * to recover the original index in order to continue tracing after
+        * returning from the function.
+        */
+       if (ftrace_graph_notrace_addr(trace->func))
+               return 1;
+
        local_irq_save(flags);
        cpu = raw_smp_processor_id();
        data = per_cpu_ptr(tr->trace_buffer.data, cpu);
@@ -335,7 +385,7 @@ void __trace_graph_return(struct trace_array *tr,
                return;
        entry   = ring_buffer_event_data(event);
        entry->ret                              = *trace;
-       if (!filter_current_check_discard(buffer, call, entry, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                __buffer_unlock_commit(buffer, event);
 }
 
@@ -652,7 +702,7 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr,
        }
 
        /* No overhead */
-       ret = print_graph_duration(DURATION_FILL_START, s, flags);
+       ret = print_graph_duration(0, s, flags | FLAGS_FILL_START);
        if (ret != TRACE_TYPE_HANDLED)
                return ret;
 
@@ -664,7 +714,7 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr,
        if (!ret)
                return TRACE_TYPE_PARTIAL_LINE;
 
-       ret = print_graph_duration(DURATION_FILL_END, s, flags);
+       ret = print_graph_duration(0, s, flags | FLAGS_FILL_END);
        if (ret != TRACE_TYPE_HANDLED)
                return ret;
 
@@ -729,14 +779,14 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s,
                        return TRACE_TYPE_HANDLED;
 
        /* No real adata, just filling the column with spaces */
-       switch (duration) {
-       case DURATION_FILL_FULL:
+       switch (flags & TRACE_GRAPH_PRINT_FILL_MASK) {
+       case FLAGS_FILL_FULL:
                ret = trace_seq_puts(s, "              |  ");
                return ret ? TRACE_TYPE_HANDLED : TRACE_TYPE_PARTIAL_LINE;
-       case DURATION_FILL_START:
+       case FLAGS_FILL_START:
                ret = trace_seq_puts(s, "  ");
                return ret ? TRACE_TYPE_HANDLED : TRACE_TYPE_PARTIAL_LINE;
-       case DURATION_FILL_END:
+       case FLAGS_FILL_END:
                ret = trace_seq_puts(s, " |");
                return ret ? TRACE_TYPE_HANDLED : TRACE_TYPE_PARTIAL_LINE;
        }
@@ -852,7 +902,7 @@ print_graph_entry_nested(struct trace_iterator *iter,
        }
 
        /* No time */
-       ret = print_graph_duration(DURATION_FILL_FULL, s, flags);
+       ret = print_graph_duration(0, s, flags | FLAGS_FILL_FULL);
        if (ret != TRACE_TYPE_HANDLED)
                return ret;
 
@@ -1172,7 +1222,7 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent,
                return TRACE_TYPE_PARTIAL_LINE;
 
        /* No time */
-       ret = print_graph_duration(DURATION_FILL_FULL, s, flags);
+       ret = print_graph_duration(0, s, flags | FLAGS_FILL_FULL);
        if (ret != TRACE_TYPE_HANDLED)
                return ret;
 
index 243f683..dae9541 100644 (file)
@@ -835,7 +835,7 @@ __kprobe_trace_func(struct trace_probe *tp, struct pt_regs *regs,
        entry->ip = (unsigned long)tp->rp.kp.addr;
        store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
 
-       if (!filter_current_check_discard(buffer, call, entry, event))
+       if (!filter_check_discard(ftrace_file, entry, buffer, event))
                trace_buffer_unlock_commit_regs(buffer, event,
                                                irq_flags, pc, regs);
 }
@@ -884,7 +884,7 @@ __kretprobe_trace_func(struct trace_probe *tp, struct kretprobe_instance *ri,
        entry->ret_ip = (unsigned long)ri->ret_addr;
        store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
 
-       if (!filter_current_check_discard(buffer, call, entry, event))
+       if (!filter_check_discard(ftrace_file, entry, buffer, event))
                trace_buffer_unlock_commit_regs(buffer, event,
                                                irq_flags, pc, regs);
 }
index b3dcfb2..0abd9b8 100644 (file)
@@ -323,7 +323,7 @@ static void __trace_mmiotrace_rw(struct trace_array *tr,
        entry   = ring_buffer_event_data(event);
        entry->rw                       = *rw;
 
-       if (!filter_check_discard(call, entry, buffer, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                trace_buffer_unlock_commit(buffer, event, 0, pc);
 }
 
@@ -353,7 +353,7 @@ static void __trace_mmiotrace_map(struct trace_array *tr,
        entry   = ring_buffer_event_data(event);
        entry->map                      = *map;
 
-       if (!filter_check_discard(call, entry, buffer, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                trace_buffer_unlock_commit(buffer, event, 0, pc);
 }
 
index 4e98e3b..3f34dc9 100644 (file)
@@ -45,7 +45,7 @@ tracing_sched_switch_trace(struct trace_array *tr,
        entry->next_state               = next->state;
        entry->next_cpu = task_cpu(next);
 
-       if (!filter_check_discard(call, entry, buffer, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                trace_buffer_unlock_commit(buffer, event, flags, pc);
 }
 
@@ -101,7 +101,7 @@ tracing_sched_wakeup_trace(struct trace_array *tr,
        entry->next_state               = wakee->state;
        entry->next_cpu                 = task_cpu(wakee);
 
-       if (!filter_check_discard(call, entry, buffer, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                trace_buffer_unlock_commit(buffer, event, flags, pc);
 }
 
index 847f88a..7af6736 100644 (file)
@@ -43,46 +43,15 @@ static DEFINE_MUTEX(all_stat_sessions_mutex);
 /* The root directory for all stat files */
 static struct dentry           *stat_dir;
 
-/*
- * Iterate through the rbtree using a post order traversal path
- * to release the next node.
- * It won't necessary release one at each iteration
- * but it will at least advance closer to the next one
- * to be released.
- */
-static struct rb_node *release_next(struct tracer_stat *ts,
-                                   struct rb_node *node)
+static void __reset_stat_session(struct stat_session *session)
 {
-       struct stat_node *snode;
-       struct rb_node *parent = rb_parent(node);
-
-       if (node->rb_left)
-               return node->rb_left;
-       else if (node->rb_right)
-               return node->rb_right;
-       else {
-               if (!parent)
-                       ;
-               else if (parent->rb_left == node)
-                       parent->rb_left = NULL;
-               else
-                       parent->rb_right = NULL;
+       struct stat_node *snode, *n;
 
-               snode = container_of(node, struct stat_node, node);
-               if (ts->stat_release)
-                       ts->stat_release(snode->stat);
+       rbtree_postorder_for_each_entry_safe(snode, n, &session->stat_root, node) {
+               if (session->ts->stat_release)
+                       session->ts->stat_release(snode->stat);
                kfree(snode);
-
-               return parent;
        }
-}
-
-static void __reset_stat_session(struct stat_session *session)
-{
-       struct rb_node *node = session->stat_root.rb_node;
-
-       while (node)
-               node = release_next(session->ts, node);
 
        session->stat_root = RB_ROOT;
 }
index 559329d..e4b6d11 100644 (file)
@@ -302,6 +302,7 @@ static int __init syscall_exit_define_fields(struct ftrace_event_call *call)
 static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)
 {
        struct trace_array *tr = data;
+       struct ftrace_event_file *ftrace_file;
        struct syscall_trace_enter *entry;
        struct syscall_metadata *sys_data;
        struct ring_buffer_event *event;
@@ -314,7 +315,13 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)
        syscall_nr = trace_get_syscall_nr(current, regs);
        if (syscall_nr < 0)
                return;
-       if (!test_bit(syscall_nr, tr->enabled_enter_syscalls))
+
+       /* Here we're inside tp handler's rcu_read_lock_sched (__DO_TRACE) */
+       ftrace_file = rcu_dereference_sched(tr->enter_syscall_files[syscall_nr]);
+       if (!ftrace_file)
+               return;
+
+       if (test_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &ftrace_file->flags))
                return;
 
        sys_data = syscall_nr_to_meta(syscall_nr);
@@ -336,8 +343,7 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)
        entry->nr = syscall_nr;
        syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
 
-       if (!filter_current_check_discard(buffer, sys_data->enter_event,
-                                         entry, event))
+       if (!filter_check_discard(ftrace_file, entry, buffer, event))
                trace_current_buffer_unlock_commit(buffer, event,
                                                   irq_flags, pc);
 }
@@ -345,6 +351,7 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)
 static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret)
 {
        struct trace_array *tr = data;
+       struct ftrace_event_file *ftrace_file;
        struct syscall_trace_exit *entry;
        struct syscall_metadata *sys_data;
        struct ring_buffer_event *event;
@@ -356,7 +363,13 @@ static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret)
        syscall_nr = trace_get_syscall_nr(current, regs);
        if (syscall_nr < 0)
                return;
-       if (!test_bit(syscall_nr, tr->enabled_exit_syscalls))
+
+       /* Here we're inside tp handler's rcu_read_lock_sched (__DO_TRACE()) */
+       ftrace_file = rcu_dereference_sched(tr->exit_syscall_files[syscall_nr]);
+       if (!ftrace_file)
+               return;
+
+       if (test_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &ftrace_file->flags))
                return;
 
        sys_data = syscall_nr_to_meta(syscall_nr);
@@ -377,8 +390,7 @@ static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret)
        entry->nr = syscall_nr;
        entry->ret = syscall_get_return_value(current, regs);
 
-       if (!filter_current_check_discard(buffer, sys_data->exit_event,
-                                         entry, event))
+       if (!filter_check_discard(ftrace_file, entry, buffer, event))
                trace_current_buffer_unlock_commit(buffer, event,
                                                   irq_flags, pc);
 }
@@ -397,7 +409,7 @@ static int reg_event_syscall_enter(struct ftrace_event_file *file,
        if (!tr->sys_refcount_enter)
                ret = register_trace_sys_enter(ftrace_syscall_enter, tr);
        if (!ret) {
-               set_bit(num, tr->enabled_enter_syscalls);
+               rcu_assign_pointer(tr->enter_syscall_files[num], file);
                tr->sys_refcount_enter++;
        }
        mutex_unlock(&syscall_trace_lock);
@@ -415,10 +427,15 @@ static void unreg_event_syscall_enter(struct ftrace_event_file *file,
                return;
        mutex_lock(&syscall_trace_lock);
        tr->sys_refcount_enter--;
-       clear_bit(num, tr->enabled_enter_syscalls);
+       rcu_assign_pointer(tr->enter_syscall_files[num], NULL);
        if (!tr->sys_refcount_enter)
                unregister_trace_sys_enter(ftrace_syscall_enter, tr);
        mutex_unlock(&syscall_trace_lock);
+       /*
+        * Callers expect the event to be completely disabled on
+        * return, so wait for current handlers to finish.
+        */
+       synchronize_sched();
 }
 
 static int reg_event_syscall_exit(struct ftrace_event_file *file,
@@ -435,7 +452,7 @@ static int reg_event_syscall_exit(struct ftrace_event_file *file,
        if (!tr->sys_refcount_exit)
                ret = register_trace_sys_exit(ftrace_syscall_exit, tr);
        if (!ret) {
-               set_bit(num, tr->enabled_exit_syscalls);
+               rcu_assign_pointer(tr->exit_syscall_files[num], file);
                tr->sys_refcount_exit++;
        }
        mutex_unlock(&syscall_trace_lock);
@@ -453,10 +470,15 @@ static void unreg_event_syscall_exit(struct ftrace_event_file *file,
                return;
        mutex_lock(&syscall_trace_lock);
        tr->sys_refcount_exit--;
-       clear_bit(num, tr->enabled_exit_syscalls);
+       rcu_assign_pointer(tr->exit_syscall_files[num], NULL);
        if (!tr->sys_refcount_exit)
                unregister_trace_sys_exit(ftrace_syscall_exit, tr);
        mutex_unlock(&syscall_trace_lock);
+       /*
+        * Callers expect the event to be completely disabled on
+        * return, so wait for current handlers to finish.
+        */
+       synchronize_sched();
 }
 
 static int __init init_syscall_trace(struct ftrace_event_call *call)
index 272261b..b6dcc42 100644 (file)
@@ -128,6 +128,7 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret)
        if (is_ret)
                tu->consumer.ret_handler = uretprobe_dispatcher;
        init_trace_uprobe_filter(&tu->filter);
+       tu->call.flags |= TRACE_EVENT_FL_USE_CALL_FILTER;
        return tu;
 
 error:
@@ -561,7 +562,7 @@ static void uprobe_trace_print(struct trace_uprobe *tu,
        for (i = 0; i < tu->nr_args; i++)
                call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset);
 
-       if (!filter_current_check_discard(buffer, call, entry, event))
+       if (!call_filter_check_discard(call, entry, buffer, event))
                trace_buffer_unlock_commit(buffer, event, 0, 0);
 }
 
index 630d72b..509403e 100644 (file)
@@ -22,6 +22,17 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
 }
 EXPORT_SYMBOL(smp_call_function_single);
 
+void __smp_call_function_single(int cpu, struct call_single_data *csd,
+                               int wait)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       csd->func(csd->info);
+       local_irq_restore(flags);
+}
+EXPORT_SYMBOL(__smp_call_function_single);
+
 int on_each_cpu(smp_call_func_t func, void *info, int wait)
 {
        unsigned long flags;
index 5bbb919..a3a0dbf 100644 (file)
@@ -51,6 +51,10 @@ struct user_namespace init_user_ns = {
        .owner = GLOBAL_ROOT_UID,
        .group = GLOBAL_ROOT_GID,
        .proc_inum = PROC_USER_INIT_INO,
+#ifdef CONFIG_KEYS_KERBEROS_CACHE
+       .krb_cache_register_sem =
+       __RWSEM_INITIALIZER(init_user_ns.krb_cache_register_sem),
+#endif
 };
 EXPORT_SYMBOL_GPL(init_user_ns);
 
index 13fb113..240fb62 100644 (file)
@@ -101,6 +101,9 @@ int create_user_ns(struct cred *new)
 
        set_cred_user_ns(new, ns);
 
+#ifdef CONFIG_PERSISTENT_KEYRINGS
+       init_rwsem(&ns->persistent_keyring_register_sem);
+#endif
        return 0;
 }
 
@@ -130,6 +133,9 @@ void free_user_ns(struct user_namespace *ns)
 
        do {
                parent = ns->parent;
+#ifdef CONFIG_PERSISTENT_KEYRINGS
+               key_put(ns->persistent_keyring_register);
+#endif
                proc_free_inum(ns->proc_inum);
                kmem_cache_free(user_ns_cachep, ns);
                ns = parent;
index 75485e1..991c98b 100644 (file)
@@ -51,13 +51,6 @@ config PERCPU_RWSEM
 config ARCH_USE_CMPXCHG_LOCKREF
        bool
 
-config CMPXCHG_LOCKREF
-       def_bool y if ARCH_USE_CMPXCHG_LOCKREF
-       depends on SMP
-       depends on !GENERIC_LOCKBREAK
-       depends on !DEBUG_SPINLOCK
-       depends on !DEBUG_LOCK_ALLOC
-
 config CRC_CCITT
        tristate "CRC-CCITT functions"
        help
@@ -329,6 +322,20 @@ config TEXTSEARCH_FSM
 config BTREE
        boolean
 
+config ASSOCIATIVE_ARRAY
+       bool
+       help
+         Generic associative array.  Can be searched and iterated over whilst
+         it is being modified.  It is also reasonably quick to search and
+         modify.  The algorithms are non-recursive, and the trees are highly
+         capacious.
+
+         See:
+
+               Documentation/assoc_array.txt
+
+         for more information.
+
 config HAS_IOMEM
        boolean
        depends on !NO_IOMEM
index d480a8c..a459c31 100644 (file)
@@ -13,7 +13,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
         sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \
         proportions.o flex_proportions.o prio_heap.o ratelimit.o show_mem.o \
         is_single_threaded.o plist.o decompress.o kobject_uevent.o \
-        earlycpio.o percpu-refcount.o percpu_ida.o
+        earlycpio.o
 
 obj-$(CONFIG_ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS) += usercopy.o
 lib-$(CONFIG_MMU) += ioremap.o
@@ -26,7 +26,7 @@ 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 \
         gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \
         bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \
-        percpu_ida.o
+        percpu-refcount.o percpu_ida.o
 obj-y += string_helpers.o
 obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
 obj-y += kstrtox.o
@@ -47,6 +47,7 @@ CFLAGS_hweight.o = $(subst $(quote),,$(CONFIG_ARCH_HWEIGHT_CFLAGS))
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
 
 obj-$(CONFIG_BTREE) += btree.o
+obj-$(CONFIG_ASSOCIATIVE_ARRAY) += assoc_array.o
 obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
 obj-$(CONFIG_DEBUG_LIST) += list_debug.o
 obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
diff --git a/lib/assoc_array.c b/lib/assoc_array.c
new file mode 100644 (file)
index 0000000..17edeaf
--- /dev/null
@@ -0,0 +1,1746 @@
+/* Generic associative array implementation.
+ *
+ * See Documentation/assoc_array.txt for information.
+ *
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+//#define DEBUG
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/assoc_array_priv.h>
+
+/*
+ * Iterate over an associative array.  The caller must hold the RCU read lock
+ * or better.
+ */
+static int assoc_array_subtree_iterate(const struct assoc_array_ptr *root,
+                                      const struct assoc_array_ptr *stop,
+                                      int (*iterator)(const void *leaf,
+                                                      void *iterator_data),
+                                      void *iterator_data)
+{
+       const struct assoc_array_shortcut *shortcut;
+       const struct assoc_array_node *node;
+       const struct assoc_array_ptr *cursor, *ptr, *parent;
+       unsigned long has_meta;
+       int slot, ret;
+
+       cursor = root;
+
+begin_node:
+       if (assoc_array_ptr_is_shortcut(cursor)) {
+               /* Descend through a shortcut */
+               shortcut = assoc_array_ptr_to_shortcut(cursor);
+               smp_read_barrier_depends();
+               cursor = ACCESS_ONCE(shortcut->next_node);
+       }
+
+       node = assoc_array_ptr_to_node(cursor);
+       smp_read_barrier_depends();
+       slot = 0;
+
+       /* We perform two passes of each node.
+        *
+        * The first pass does all the leaves in this node.  This means we
+        * don't miss any leaves if the node is split up by insertion whilst
+        * we're iterating over the branches rooted here (we may, however, see
+        * some leaves twice).
+        */
+       has_meta = 0;
+       for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
+               ptr = ACCESS_ONCE(node->slots[slot]);
+               has_meta |= (unsigned long)ptr;
+               if (ptr && assoc_array_ptr_is_leaf(ptr)) {
+                       /* We need a barrier between the read of the pointer
+                        * and dereferencing the pointer - but only if we are
+                        * actually going to dereference it.
+                        */
+                       smp_read_barrier_depends();
+
+                       /* Invoke the callback */
+                       ret = iterator(assoc_array_ptr_to_leaf(ptr),
+                                      iterator_data);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       /* The second pass attends to all the metadata pointers.  If we follow
+        * one of these we may find that we don't come back here, but rather go
+        * back to a replacement node with the leaves in a different layout.
+        *
+        * We are guaranteed to make progress, however, as the slot number for
+        * a particular portion of the key space cannot change - and we
+        * continue at the back pointer + 1.
+        */
+       if (!(has_meta & ASSOC_ARRAY_PTR_META_TYPE))
+               goto finished_node;
+       slot = 0;
+
+continue_node:
+       node = assoc_array_ptr_to_node(cursor);
+       smp_read_barrier_depends();
+
+       for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
+               ptr = ACCESS_ONCE(node->slots[slot]);
+               if (assoc_array_ptr_is_meta(ptr)) {
+                       cursor = ptr;
+                       goto begin_node;
+               }
+       }
+
+finished_node:
+       /* Move up to the parent (may need to skip back over a shortcut) */
+       parent = ACCESS_ONCE(node->back_pointer);
+       slot = node->parent_slot;
+       if (parent == stop)
+               return 0;
+
+       if (assoc_array_ptr_is_shortcut(parent)) {
+               shortcut = assoc_array_ptr_to_shortcut(parent);
+               smp_read_barrier_depends();
+               cursor = parent;
+               parent = ACCESS_ONCE(shortcut->back_pointer);
+               slot = shortcut->parent_slot;
+               if (parent == stop)
+                       return 0;
+       }
+
+       /* Ascend to next slot in parent node */
+       cursor = parent;
+       slot++;
+       goto continue_node;
+}
+
+/**
+ * assoc_array_iterate - Pass all objects in the array to a callback
+ * @array: The array to iterate over.
+ * @iterator: The callback function.
+ * @iterator_data: Private data for the callback function.
+ *
+ * Iterate over all the objects in an associative array.  Each one will be
+ * presented to the iterator function.
+ *
+ * If the array is being modified concurrently with the iteration then it is
+ * possible that some objects in the array will be passed to the iterator
+ * callback more than once - though every object should be passed at least
+ * once.  If this is undesirable then the caller must lock against modification
+ * for the duration of this function.
+ *
+ * The function will return 0 if no objects were in the array or else it will
+ * return the result of the last iterator function called.  Iteration stops
+ * immediately if any call to the iteration function results in a non-zero
+ * return.
+ *
+ * The caller should hold the RCU read lock or better if concurrent
+ * modification is possible.
+ */
+int assoc_array_iterate(const struct assoc_array *array,
+                       int (*iterator)(const void *object,
+                                       void *iterator_data),
+                       void *iterator_data)
+{
+       struct assoc_array_ptr *root = ACCESS_ONCE(array->root);
+
+       if (!root)
+               return 0;
+       return assoc_array_subtree_iterate(root, NULL, iterator, iterator_data);
+}
+
+enum assoc_array_walk_status {
+       assoc_array_walk_tree_empty,
+       assoc_array_walk_found_terminal_node,
+       assoc_array_walk_found_wrong_shortcut,
+} status;
+
+struct assoc_array_walk_result {
+       struct {
+               struct assoc_array_node *node;  /* Node in which leaf might be found */
+               int             level;
+               int             slot;
+       } terminal_node;
+       struct {
+               struct assoc_array_shortcut *shortcut;
+               int             level;
+               int             sc_level;
+               unsigned long   sc_segments;
+               unsigned long   dissimilarity;
+       } wrong_shortcut;
+};
+
+/*
+ * Navigate through the internal tree looking for the closest node to the key.
+ */
+static enum assoc_array_walk_status
+assoc_array_walk(const struct assoc_array *array,
+                const struct assoc_array_ops *ops,
+                const void *index_key,
+                struct assoc_array_walk_result *result)
+{
+       struct assoc_array_shortcut *shortcut;
+       struct assoc_array_node *node;
+       struct assoc_array_ptr *cursor, *ptr;
+       unsigned long sc_segments, dissimilarity;
+       unsigned long segments;
+       int level, sc_level, next_sc_level;
+       int slot;
+
+       pr_devel("-->%s()\n", __func__);
+
+       cursor = ACCESS_ONCE(array->root);
+       if (!cursor)
+               return assoc_array_walk_tree_empty;
+
+       level = 0;
+
+       /* Use segments from the key for the new leaf to navigate through the
+        * internal tree, skipping through nodes and shortcuts that are on
+        * route to the destination.  Eventually we'll come to a slot that is
+        * either empty or contains a leaf at which point we've found a node in
+        * which the leaf we're looking for might be found or into which it
+        * should be inserted.
+        */
+jumped:
+       segments = ops->get_key_chunk(index_key, level);
+       pr_devel("segments[%d]: %lx\n", level, segments);
+
+       if (assoc_array_ptr_is_shortcut(cursor))
+               goto follow_shortcut;
+
+consider_node:
+       node = assoc_array_ptr_to_node(cursor);
+       smp_read_barrier_depends();
+
+       slot = segments >> (level & ASSOC_ARRAY_KEY_CHUNK_MASK);
+       slot &= ASSOC_ARRAY_FAN_MASK;
+       ptr = ACCESS_ONCE(node->slots[slot]);
+
+       pr_devel("consider slot %x [ix=%d type=%lu]\n",
+                slot, level, (unsigned long)ptr & 3);
+
+       if (!assoc_array_ptr_is_meta(ptr)) {
+               /* The node doesn't have a node/shortcut pointer in the slot
+                * corresponding to the index key that we have to follow.
+                */
+               result->terminal_node.node = node;
+               result->terminal_node.level = level;
+               result->terminal_node.slot = slot;
+               pr_devel("<--%s() = terminal_node\n", __func__);
+               return assoc_array_walk_found_terminal_node;
+       }
+
+       if (assoc_array_ptr_is_node(ptr)) {
+               /* There is a pointer to a node in the slot corresponding to
+                * this index key segment, so we need to follow it.
+                */
+               cursor = ptr;
+               level += ASSOC_ARRAY_LEVEL_STEP;
+               if ((level & ASSOC_ARRAY_KEY_CHUNK_MASK) != 0)
+                       goto consider_node;
+               goto jumped;
+       }
+
+       /* There is a shortcut in the slot corresponding to the index key
+        * segment.  We follow the shortcut if its partial index key matches
+        * this leaf's.  Otherwise we need to split the shortcut.
+        */
+       cursor = ptr;
+follow_shortcut:
+       shortcut = assoc_array_ptr_to_shortcut(cursor);
+       smp_read_barrier_depends();
+       pr_devel("shortcut to %d\n", shortcut->skip_to_level);
+       sc_level = level + ASSOC_ARRAY_LEVEL_STEP;
+       BUG_ON(sc_level > shortcut->skip_to_level);
+
+       do {
+               /* Check the leaf against the shortcut's index key a word at a
+                * time, trimming the final word (the shortcut stores the index
+                * key completely from the root to the shortcut's target).
+                */
+               if ((sc_level & ASSOC_ARRAY_KEY_CHUNK_MASK) == 0)
+                       segments = ops->get_key_chunk(index_key, sc_level);
+
+               sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
+               dissimilarity = segments ^ sc_segments;
+
+               if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
+                       /* Trim segments that are beyond the shortcut */
+                       int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+                       dissimilarity &= ~(ULONG_MAX << shift);
+                       next_sc_level = shortcut->skip_to_level;
+               } else {
+                       next_sc_level = sc_level + ASSOC_ARRAY_KEY_CHUNK_SIZE;
+                       next_sc_level = round_down(next_sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
+               }
+
+               if (dissimilarity != 0) {
+                       /* This shortcut points elsewhere */
+                       result->wrong_shortcut.shortcut = shortcut;
+                       result->wrong_shortcut.level = level;
+                       result->wrong_shortcut.sc_level = sc_level;
+                       result->wrong_shortcut.sc_segments = sc_segments;
+                       result->wrong_shortcut.dissimilarity = dissimilarity;
+                       return assoc_array_walk_found_wrong_shortcut;
+               }
+
+               sc_level = next_sc_level;
+       } while (sc_level < shortcut->skip_to_level);
+
+       /* The shortcut matches the leaf's index to this point. */
+       cursor = ACCESS_ONCE(shortcut->next_node);
+       if (((level ^ sc_level) & ~ASSOC_ARRAY_KEY_CHUNK_MASK) != 0) {
+               level = sc_level;
+               goto jumped;
+       } else {
+               level = sc_level;
+               goto consider_node;
+       }
+}
+
+/**
+ * assoc_array_find - Find an object by index key
+ * @array: The associative array to search.
+ * @ops: The operations to use.
+ * @index_key: The key to the object.
+ *
+ * Find an object in an associative array by walking through the internal tree
+ * to the node that should contain the object and then searching the leaves
+ * there.  NULL is returned if the requested object was not found in the array.
+ *
+ * The caller must hold the RCU read lock or better.
+ */
+void *assoc_array_find(const struct assoc_array *array,
+                      const struct assoc_array_ops *ops,
+                      const void *index_key)
+{
+       struct assoc_array_walk_result result;
+       const struct assoc_array_node *node;
+       const struct assoc_array_ptr *ptr;
+       const void *leaf;
+       int slot;
+
+       if (assoc_array_walk(array, ops, index_key, &result) !=
+           assoc_array_walk_found_terminal_node)
+               return NULL;
+
+       node = result.terminal_node.node;
+       smp_read_barrier_depends();
+
+       /* If the target key is available to us, it's has to be pointed to by
+        * the terminal node.
+        */
+       for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
+               ptr = ACCESS_ONCE(node->slots[slot]);
+               if (ptr && assoc_array_ptr_is_leaf(ptr)) {
+                       /* We need a barrier between the read of the pointer
+                        * and dereferencing the pointer - but only if we are
+                        * actually going to dereference it.
+                        */
+                       leaf = assoc_array_ptr_to_leaf(ptr);
+                       smp_read_barrier_depends();
+                       if (ops->compare_object(leaf, index_key))
+                               return (void *)leaf;
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * Destructively iterate over an associative array.  The caller must prevent
+ * other simultaneous accesses.
+ */
+static void assoc_array_destroy_subtree(struct assoc_array_ptr *root,
+                                       const struct assoc_array_ops *ops)
+{
+       struct assoc_array_shortcut *shortcut;
+       struct assoc_array_node *node;
+       struct assoc_array_ptr *cursor, *parent = NULL;
+       int slot = -1;
+
+       pr_devel("-->%s()\n", __func__);
+
+       cursor = root;
+       if (!cursor) {
+               pr_devel("empty\n");
+               return;
+       }
+
+move_to_meta:
+       if (assoc_array_ptr_is_shortcut(cursor)) {
+               /* Descend through a shortcut */
+               pr_devel("[%d] shortcut\n", slot);
+               BUG_ON(!assoc_array_ptr_is_shortcut(cursor));
+               shortcut = assoc_array_ptr_to_shortcut(cursor);
+               BUG_ON(shortcut->back_pointer != parent);
+               BUG_ON(slot != -1 && shortcut->parent_slot != slot);
+               parent = cursor;
+               cursor = shortcut->next_node;
+               slot = -1;
+               BUG_ON(!assoc_array_ptr_is_node(cursor));
+       }
+
+       pr_devel("[%d] node\n", slot);
+       node = assoc_array_ptr_to_node(cursor);
+       BUG_ON(node->back_pointer != parent);
+       BUG_ON(slot != -1 && node->parent_slot != slot);
+       slot = 0;
+
+continue_node:
+       pr_devel("Node %p [back=%p]\n", node, node->back_pointer);
+       for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
+               struct assoc_array_ptr *ptr = node->slots[slot];
+               if (!ptr)
+                       continue;
+               if (assoc_array_ptr_is_meta(ptr)) {
+                       parent = cursor;
+                       cursor = ptr;
+                       goto move_to_meta;
+               }
+
+               if (ops) {
+                       pr_devel("[%d] free leaf\n", slot);
+                       ops->free_object(assoc_array_ptr_to_leaf(ptr));
+               }
+       }
+
+       parent = node->back_pointer;
+       slot = node->parent_slot;
+       pr_devel("free node\n");
+       kfree(node);
+       if (!parent)
+               return; /* Done */
+
+       /* Move back up to the parent (may need to free a shortcut on
+        * the way up) */
+       if (assoc_array_ptr_is_shortcut(parent)) {
+               shortcut = assoc_array_ptr_to_shortcut(parent);
+               BUG_ON(shortcut->next_node != cursor);
+               cursor = parent;
+               parent = shortcut->back_pointer;
+               slot = shortcut->parent_slot;
+               pr_devel("free shortcut\n");
+               kfree(shortcut);
+               if (!parent)
+                       return;
+
+               BUG_ON(!assoc_array_ptr_is_node(parent));
+       }
+
+       /* Ascend to next slot in parent node */
+       pr_devel("ascend to %p[%d]\n", parent, slot);
+       cursor = parent;
+       node = assoc_array_ptr_to_node(cursor);
+       slot++;
+       goto continue_node;
+}
+
+/**
+ * assoc_array_destroy - Destroy an associative array
+ * @array: The array to destroy.
+ * @ops: The operations to use.
+ *
+ * Discard all metadata and free all objects in an associative array.  The
+ * array will be empty and ready to use again upon completion.  This function
+ * cannot fail.
+ *
+ * The caller must prevent all other accesses whilst this takes place as no
+ * attempt is made to adjust pointers gracefully to permit RCU readlock-holding
+ * accesses to continue.  On the other hand, no memory allocation is required.
+ */
+void assoc_array_destroy(struct assoc_array *array,
+                        const struct assoc_array_ops *ops)
+{
+       assoc_array_destroy_subtree(array->root, ops);
+       array->root = NULL;
+}
+
+/*
+ * Handle insertion into an empty tree.
+ */
+static bool assoc_array_insert_in_empty_tree(struct assoc_array_edit *edit)
+{
+       struct assoc_array_node *new_n0;
+
+       pr_devel("-->%s()\n", __func__);
+
+       new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
+       if (!new_n0)
+               return false;
+
+       edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
+       edit->leaf_p = &new_n0->slots[0];
+       edit->adjust_count_on = new_n0;
+       edit->set[0].ptr = &edit->array->root;
+       edit->set[0].to = assoc_array_node_to_ptr(new_n0);
+
+       pr_devel("<--%s() = ok [no root]\n", __func__);
+       return true;
+}
+
+/*
+ * Handle insertion into a terminal node.
+ */
+static bool assoc_array_insert_into_terminal_node(struct assoc_array_edit *edit,
+                                                 const struct assoc_array_ops *ops,
+                                                 const void *index_key,
+                                                 struct assoc_array_walk_result *result)
+{
+       struct assoc_array_shortcut *shortcut, *new_s0;
+       struct assoc_array_node *node, *new_n0, *new_n1, *side;
+       struct assoc_array_ptr *ptr;
+       unsigned long dissimilarity, base_seg, blank;
+       size_t keylen;
+       bool have_meta;
+       int level, diff;
+       int slot, next_slot, free_slot, i, j;
+
+       node    = result->terminal_node.node;
+       level   = result->terminal_node.level;
+       edit->segment_cache[ASSOC_ARRAY_FAN_OUT] = result->terminal_node.slot;
+
+       pr_devel("-->%s()\n", __func__);
+
+       /* We arrived at a node which doesn't have an onward node or shortcut
+        * pointer that we have to follow.  This means that (a) the leaf we
+        * want must go here (either by insertion or replacement) or (b) we
+        * need to split this node and insert in one of the fragments.
+        */
+       free_slot = -1;
+
+       /* Firstly, we have to check the leaves in this node to see if there's
+        * a matching one we should replace in place.
+        */
+       for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
+               ptr = node->slots[i];
+               if (!ptr) {
+                       free_slot = i;
+                       continue;
+               }
+               if (ops->compare_object(assoc_array_ptr_to_leaf(ptr), index_key)) {
+                       pr_devel("replace in slot %d\n", i);
+                       edit->leaf_p = &node->slots[i];
+                       edit->dead_leaf = node->slots[i];
+                       pr_devel("<--%s() = ok [replace]\n", __func__);
+                       return true;
+               }
+       }
+
+       /* If there is a free slot in this node then we can just insert the
+        * leaf here.
+        */
+       if (free_slot >= 0) {
+               pr_devel("insert in free slot %d\n", free_slot);
+               edit->leaf_p = &node->slots[free_slot];
+               edit->adjust_count_on = node;
+               pr_devel("<--%s() = ok [insert]\n", __func__);
+               return true;
+       }
+
+       /* The node has no spare slots - so we're either going to have to split
+        * it or insert another node before it.
+        *
+        * Whatever, we're going to need at least two new nodes - so allocate
+        * those now.  We may also need a new shortcut, but we deal with that
+        * when we need it.
+        */
+       new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
+       if (!new_n0)
+               return false;
+       edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
+       new_n1 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
+       if (!new_n1)
+               return false;
+       edit->new_meta[1] = assoc_array_node_to_ptr(new_n1);
+
+       /* We need to find out how similar the leaves are. */
+       pr_devel("no spare slots\n");
+       have_meta = false;
+       for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
+               ptr = node->slots[i];
+               if (assoc_array_ptr_is_meta(ptr)) {
+                       edit->segment_cache[i] = 0xff;
+                       have_meta = true;
+                       continue;
+               }
+               base_seg = ops->get_object_key_chunk(
+                       assoc_array_ptr_to_leaf(ptr), level);
+               base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+               edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK;
+       }
+
+       if (have_meta) {
+               pr_devel("have meta\n");
+               goto split_node;
+       }
+
+       /* The node contains only leaves */
+       dissimilarity = 0;
+       base_seg = edit->segment_cache[0];
+       for (i = 1; i < ASSOC_ARRAY_FAN_OUT; i++)
+               dissimilarity |= edit->segment_cache[i] ^ base_seg;
+
+       pr_devel("only leaves; dissimilarity=%lx\n", dissimilarity);
+
+       if ((dissimilarity & ASSOC_ARRAY_FAN_MASK) == 0) {
+               /* The old leaves all cluster in the same slot.  We will need
+                * to insert a shortcut if the new node wants to cluster with them.
+                */
+               if ((edit->segment_cache[ASSOC_ARRAY_FAN_OUT] ^ base_seg) == 0)
+                       goto all_leaves_cluster_together;
+
+               /* Otherwise we can just insert a new node ahead of the old
+                * one.
+                */
+               goto present_leaves_cluster_but_not_new_leaf;
+       }
+
+split_node:
+       pr_devel("split node\n");
+
+       /* We need to split the current node; we know that the node doesn't
+        * simply contain a full set of leaves that cluster together (it
+        * contains meta pointers and/or non-clustering leaves).
+        *
+        * We need to expel at least two leaves out of a set consisting of the
+        * leaves in the node and the new leaf.
+        *
+        * We need a new node (n0) to replace the current one and a new node to
+        * take the expelled nodes (n1).
+        */
+       edit->set[0].to = assoc_array_node_to_ptr(new_n0);
+       new_n0->back_pointer = node->back_pointer;
+       new_n0->parent_slot = node->parent_slot;
+       new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
+       new_n1->parent_slot = -1; /* Need to calculate this */
+
+do_split_node:
+       pr_devel("do_split_node\n");
+
+       new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
+       new_n1->nr_leaves_on_branch = 0;
+
+       /* Begin by finding two matching leaves.  There have to be at least two
+        * that match - even if there are meta pointers - because any leaf that
+        * would match a slot with a meta pointer in it must be somewhere
+        * behind that meta pointer and cannot be here.  Further, given N
+        * remaining leaf slots, we now have N+1 leaves to go in them.
+        */
+       for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
+               slot = edit->segment_cache[i];
+               if (slot != 0xff)
+                       for (j = i + 1; j < ASSOC_ARRAY_FAN_OUT + 1; j++)
+                               if (edit->segment_cache[j] == slot)
+                                       goto found_slot_for_multiple_occupancy;
+       }
+found_slot_for_multiple_occupancy:
+       pr_devel("same slot: %x %x [%02x]\n", i, j, slot);
+       BUG_ON(i >= ASSOC_ARRAY_FAN_OUT);
+       BUG_ON(j >= ASSOC_ARRAY_FAN_OUT + 1);
+       BUG_ON(slot >= ASSOC_ARRAY_FAN_OUT);
+
+       new_n1->parent_slot = slot;
+
+       /* Metadata pointers cannot change slot */
+       for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++)
+               if (assoc_array_ptr_is_meta(node->slots[i]))
+                       new_n0->slots[i] = node->slots[i];
+               else
+                       new_n0->slots[i] = NULL;
+       BUG_ON(new_n0->slots[slot] != NULL);
+       new_n0->slots[slot] = assoc_array_node_to_ptr(new_n1);
+
+       /* Filter the leaf pointers between the new nodes */
+       free_slot = -1;
+       next_slot = 0;
+       for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
+               if (assoc_array_ptr_is_meta(node->slots[i]))
+                       continue;
+               if (edit->segment_cache[i] == slot) {
+                       new_n1->slots[next_slot++] = node->slots[i];
+                       new_n1->nr_leaves_on_branch++;
+               } else {
+                       do {
+                               free_slot++;
+                       } while (new_n0->slots[free_slot] != NULL);
+                       new_n0->slots[free_slot] = node->slots[i];
+               }
+       }
+
+       pr_devel("filtered: f=%x n=%x\n", free_slot, next_slot);
+
+       if (edit->segment_cache[ASSOC_ARRAY_FAN_OUT] != slot) {
+               do {
+                       free_slot++;
+               } while (new_n0->slots[free_slot] != NULL);
+               edit->leaf_p = &new_n0->slots[free_slot];
+               edit->adjust_count_on = new_n0;
+       } else {
+               edit->leaf_p = &new_n1->slots[next_slot++];
+               edit->adjust_count_on = new_n1;
+       }
+
+       BUG_ON(next_slot <= 1);
+
+       edit->set_backpointers_to = assoc_array_node_to_ptr(new_n0);
+       for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
+               if (edit->segment_cache[i] == 0xff) {
+                       ptr = node->slots[i];
+                       BUG_ON(assoc_array_ptr_is_leaf(ptr));
+                       if (assoc_array_ptr_is_node(ptr)) {
+                               side = assoc_array_ptr_to_node(ptr);
+                               edit->set_backpointers[i] = &side->back_pointer;
+                       } else {
+                               shortcut = assoc_array_ptr_to_shortcut(ptr);
+                               edit->set_backpointers[i] = &shortcut->back_pointer;
+                       }
+               }
+       }
+
+       ptr = node->back_pointer;
+       if (!ptr)
+               edit->set[0].ptr = &edit->array->root;
+       else if (assoc_array_ptr_is_node(ptr))
+               edit->set[0].ptr = &assoc_array_ptr_to_node(ptr)->slots[node->parent_slot];
+       else
+               edit->set[0].ptr = &assoc_array_ptr_to_shortcut(ptr)->next_node;
+       edit->excised_meta[0] = assoc_array_node_to_ptr(node);
+       pr_devel("<--%s() = ok [split node]\n", __func__);
+       return true;
+
+present_leaves_cluster_but_not_new_leaf:
+       /* All the old leaves cluster in the same slot, but the new leaf wants
+        * to go into a different slot, so we create a new node to hold the new
+        * leaf and a pointer to a new node holding all the old leaves.
+        */
+       pr_devel("present leaves cluster but not new leaf\n");
+
+       new_n0->back_pointer = node->back_pointer;
+       new_n0->parent_slot = node->parent_slot;
+       new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
+       new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
+       new_n1->parent_slot = edit->segment_cache[0];
+       new_n1->nr_leaves_on_branch = node->nr_leaves_on_branch;
+       edit->adjust_count_on = new_n0;
+
+       for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++)
+               new_n1->slots[i] = node->slots[i];
+
+       new_n0->slots[edit->segment_cache[0]] = assoc_array_node_to_ptr(new_n0);
+       edit->leaf_p = &new_n0->slots[edit->segment_cache[ASSOC_ARRAY_FAN_OUT]];
+
+       edit->set[0].ptr = &assoc_array_ptr_to_node(node->back_pointer)->slots[node->parent_slot];
+       edit->set[0].to = assoc_array_node_to_ptr(new_n0);
+       edit->excised_meta[0] = assoc_array_node_to_ptr(node);
+       pr_devel("<--%s() = ok [insert node before]\n", __func__);
+       return true;
+
+all_leaves_cluster_together:
+       /* All the leaves, new and old, want to cluster together in this node
+        * in the same slot, so we have to replace this node with a shortcut to
+        * skip over the identical parts of the key and then place a pair of
+        * nodes, one inside the other, at the end of the shortcut and
+        * distribute the keys between them.
+        *
+        * Firstly we need to work out where the leaves start diverging as a
+        * bit position into their keys so that we know how big the shortcut
+        * needs to be.
+        *
+        * We only need to make a single pass of N of the N+1 leaves because if
+        * any keys differ between themselves at bit X then at least one of
+        * them must also differ with the base key at bit X or before.
+        */
+       pr_devel("all leaves cluster together\n");
+       diff = INT_MAX;
+       for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
+               int x = ops->diff_objects(assoc_array_ptr_to_leaf(edit->leaf),
+                                         assoc_array_ptr_to_leaf(node->slots[i]));
+               if (x < diff) {
+                       BUG_ON(x < 0);
+                       diff = x;
+               }
+       }
+       BUG_ON(diff == INT_MAX);
+       BUG_ON(diff < level + ASSOC_ARRAY_LEVEL_STEP);
+
+       keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE);
+       keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
+
+       new_s0 = kzalloc(sizeof(struct assoc_array_shortcut) +
+                        keylen * sizeof(unsigned long), GFP_KERNEL);
+       if (!new_s0)
+               return false;
+       edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s0);
+
+       edit->set[0].to = assoc_array_shortcut_to_ptr(new_s0);
+       new_s0->back_pointer = node->back_pointer;
+       new_s0->parent_slot = node->parent_slot;
+       new_s0->next_node = assoc_array_node_to_ptr(new_n0);
+       new_n0->back_pointer = assoc_array_shortcut_to_ptr(new_s0);
+       new_n0->parent_slot = 0;
+       new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
+       new_n1->parent_slot = -1; /* Need to calculate this */
+
+       new_s0->skip_to_level = level = diff & ~ASSOC_ARRAY_LEVEL_STEP_MASK;
+       pr_devel("skip_to_level = %d [diff %d]\n", level, diff);
+       BUG_ON(level <= 0);
+
+       for (i = 0; i < keylen; i++)
+               new_s0->index_key[i] =
+                       ops->get_key_chunk(index_key, i * ASSOC_ARRAY_KEY_CHUNK_SIZE);
+
+       blank = ULONG_MAX << (level & ASSOC_ARRAY_KEY_CHUNK_MASK);
+       pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, level, blank);
+       new_s0->index_key[keylen - 1] &= ~blank;
+
+       /* This now reduces to a node splitting exercise for which we'll need
+        * to regenerate the disparity table.
+        */
+       for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
+               ptr = node->slots[i];
+               base_seg = ops->get_object_key_chunk(assoc_array_ptr_to_leaf(ptr),
+                                                    level);
+               base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+               edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK;
+       }
+
+       base_seg = ops->get_key_chunk(index_key, level);
+       base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+       edit->segment_cache[ASSOC_ARRAY_FAN_OUT] = base_seg & ASSOC_ARRAY_FAN_MASK;
+       goto do_split_node;
+}
+
+/*
+ * Handle insertion into the middle of a shortcut.
+ */
+static bool assoc_array_insert_mid_shortcut(struct assoc_array_edit *edit,
+                                           const struct assoc_array_ops *ops,
+                                           struct assoc_array_walk_result *result)
+{
+       struct assoc_array_shortcut *shortcut, *new_s0, *new_s1;
+       struct assoc_array_node *node, *new_n0, *side;
+       unsigned long sc_segments, dissimilarity, blank;
+       size_t keylen;
+       int level, sc_level, diff;
+       int sc_slot;
+
+       shortcut        = result->wrong_shortcut.shortcut;
+       level           = result->wrong_shortcut.level;
+       sc_level        = result->wrong_shortcut.sc_level;
+       sc_segments     = result->wrong_shortcut.sc_segments;
+       dissimilarity   = result->wrong_shortcut.dissimilarity;
+
+       pr_devel("-->%s(ix=%d dis=%lx scix=%d)\n",
+                __func__, level, dissimilarity, sc_level);
+
+       /* We need to split a shortcut and insert a node between the two
+        * pieces.  Zero-length pieces will be dispensed with entirely.
+        *
+        * First of all, we need to find out in which level the first
+        * difference was.
+        */
+       diff = __ffs(dissimilarity);
+       diff &= ~ASSOC_ARRAY_LEVEL_STEP_MASK;
+       diff += sc_level & ~ASSOC_ARRAY_KEY_CHUNK_MASK;
+       pr_devel("diff=%d\n", diff);
+
+       if (!shortcut->back_pointer) {
+               edit->set[0].ptr = &edit->array->root;
+       } else if (assoc_array_ptr_is_node(shortcut->back_pointer)) {
+               node = assoc_array_ptr_to_node(shortcut->back_pointer);
+               edit->set[0].ptr = &node->slots[shortcut->parent_slot];
+       } else {
+               BUG();
+       }
+
+       edit->excised_meta[0] = assoc_array_shortcut_to_ptr(shortcut);
+
+       /* Create a new node now since we're going to need it anyway */
+       new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
+       if (!new_n0)
+               return false;
+       edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
+       edit->adjust_count_on = new_n0;
+
+       /* Insert a new shortcut before the new node if this segment isn't of
+        * zero length - otherwise we just connect the new node directly to the
+        * parent.
+        */
+       level += ASSOC_ARRAY_LEVEL_STEP;
+       if (diff > level) {
+               pr_devel("pre-shortcut %d...%d\n", level, diff);
+               keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE);
+               keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
+
+               new_s0 = kzalloc(sizeof(struct assoc_array_shortcut) +
+                                keylen * sizeof(unsigned long), GFP_KERNEL);
+               if (!new_s0)
+                       return false;
+               edit->new_meta[1] = assoc_array_shortcut_to_ptr(new_s0);
+               edit->set[0].to = assoc_array_shortcut_to_ptr(new_s0);
+               new_s0->back_pointer = shortcut->back_pointer;
+               new_s0->parent_slot = shortcut->parent_slot;
+               new_s0->next_node = assoc_array_node_to_ptr(new_n0);
+               new_s0->skip_to_level = diff;
+
+               new_n0->back_pointer = assoc_array_shortcut_to_ptr(new_s0);
+               new_n0->parent_slot = 0;
+
+               memcpy(new_s0->index_key, shortcut->index_key,
+                      keylen * sizeof(unsigned long));
+
+               blank = ULONG_MAX << (diff & ASSOC_ARRAY_KEY_CHUNK_MASK);
+               pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, diff, blank);
+               new_s0->index_key[keylen - 1] &= ~blank;
+       } else {
+               pr_devel("no pre-shortcut\n");
+               edit->set[0].to = assoc_array_node_to_ptr(new_n0);
+               new_n0->back_pointer = shortcut->back_pointer;
+               new_n0->parent_slot = shortcut->parent_slot;
+       }
+
+       side = assoc_array_ptr_to_node(shortcut->next_node);
+       new_n0->nr_leaves_on_branch = side->nr_leaves_on_branch;
+
+       /* We need to know which slot in the new node is going to take a
+        * metadata pointer.
+        */
+       sc_slot = sc_segments >> (diff & ASSOC_ARRAY_KEY_CHUNK_MASK);
+       sc_slot &= ASSOC_ARRAY_FAN_MASK;
+
+       pr_devel("new slot %lx >> %d -> %d\n",
+                sc_segments, diff & ASSOC_ARRAY_KEY_CHUNK_MASK, sc_slot);
+
+       /* Determine whether we need to follow the new node with a replacement
+        * for the current shortcut.  We could in theory reuse the current
+        * shortcut if its parent slot number doesn't change - but that's a
+        * 1-in-16 chance so not worth expending the code upon.
+        */
+       level = diff + ASSOC_ARRAY_LEVEL_STEP;
+       if (level < shortcut->skip_to_level) {
+               pr_devel("post-shortcut %d...%d\n", level, shortcut->skip_to_level);
+               keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
+               keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
+
+               new_s1 = kzalloc(sizeof(struct assoc_array_shortcut) +
+                                keylen * sizeof(unsigned long), GFP_KERNEL);
+               if (!new_s1)
+                       return false;
+               edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s1);
+
+               new_s1->back_pointer = assoc_array_node_to_ptr(new_n0);
+               new_s1->parent_slot = sc_slot;
+               new_s1->next_node = shortcut->next_node;
+               new_s1->skip_to_level = shortcut->skip_to_level;
+
+               new_n0->slots[sc_slot] = assoc_array_shortcut_to_ptr(new_s1);
+
+               memcpy(new_s1->index_key, shortcut->index_key,
+                      keylen * sizeof(unsigned long));
+
+               edit->set[1].ptr = &side->back_pointer;
+               edit->set[1].to = assoc_array_shortcut_to_ptr(new_s1);
+       } else {
+               pr_devel("no post-shortcut\n");
+
+               /* We don't have to replace the pointed-to node as long as we
+                * use memory barriers to make sure the parent slot number is
+                * changed before the back pointer (the parent slot number is
+                * irrelevant to the old parent shortcut).
+                */
+               new_n0->slots[sc_slot] = shortcut->next_node;
+               edit->set_parent_slot[0].p = &side->parent_slot;
+               edit->set_parent_slot[0].to = sc_slot;
+               edit->set[1].ptr = &side->back_pointer;
+               edit->set[1].to = assoc_array_node_to_ptr(new_n0);
+       }
+
+       /* Install the new leaf in a spare slot in the new node. */
+       if (sc_slot == 0)
+               edit->leaf_p = &new_n0->slots[1];
+       else
+               edit->leaf_p = &new_n0->slots[0];
+
+       pr_devel("<--%s() = ok [split shortcut]\n", __func__);
+       return edit;
+}
+
+/**
+ * assoc_array_insert - Script insertion of an object into an associative array
+ * @array: The array to insert into.
+ * @ops: The operations to use.
+ * @index_key: The key to insert at.
+ * @object: The object to insert.
+ *
+ * Precalculate and preallocate a script for the insertion or replacement of an
+ * object in an associative array.  This results in an edit script that can
+ * either be applied or cancelled.
+ *
+ * The function returns a pointer to an edit script or -ENOMEM.
+ *
+ * The caller should lock against other modifications and must continue to hold
+ * the lock until assoc_array_apply_edit() has been called.
+ *
+ * Accesses to the tree may take place concurrently with this function,
+ * provided they hold the RCU read lock.
+ */
+struct assoc_array_edit *assoc_array_insert(struct assoc_array *array,
+                                           const struct assoc_array_ops *ops,
+                                           const void *index_key,
+                                           void *object)
+{
+       struct assoc_array_walk_result result;
+       struct assoc_array_edit *edit;
+
+       pr_devel("-->%s()\n", __func__);
+
+       /* The leaf pointer we're given must not have the bottom bit set as we
+        * use those for type-marking the pointer.  NULL pointers are also not
+        * allowed as they indicate an empty slot but we have to allow them
+        * here as they can be updated later.
+        */
+       BUG_ON(assoc_array_ptr_is_meta(object));
+
+       edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
+       if (!edit)
+               return ERR_PTR(-ENOMEM);
+       edit->array = array;
+       edit->ops = ops;
+       edit->leaf = assoc_array_leaf_to_ptr(object);
+       edit->adjust_count_by = 1;
+
+       switch (assoc_array_walk(array, ops, index_key, &result)) {
+       case assoc_array_walk_tree_empty:
+               /* Allocate a root node if there isn't one yet */
+               if (!assoc_array_insert_in_empty_tree(edit))
+                       goto enomem;
+               return edit;
+
+       case assoc_array_walk_found_terminal_node:
+               /* We found a node that doesn't have a node/shortcut pointer in
+                * the slot corresponding to the index key that we have to
+                * follow.
+                */
+               if (!assoc_array_insert_into_terminal_node(edit, ops, index_key,
+                                                          &result))
+                       goto enomem;
+               return edit;
+
+       case assoc_array_walk_found_wrong_shortcut:
+               /* We found a shortcut that didn't match our key in a slot we
+                * needed to follow.
+                */
+               if (!assoc_array_insert_mid_shortcut(edit, ops, &result))
+                       goto enomem;
+               return edit;
+       }
+
+enomem:
+       /* Clean up after an out of memory error */
+       pr_devel("enomem\n");
+       assoc_array_cancel_edit(edit);
+       return ERR_PTR(-ENOMEM);
+}
+
+/**
+ * assoc_array_insert_set_object - Set the new object pointer in an edit script
+ * @edit: The edit script to modify.
+ * @object: The object pointer to set.
+ *
+ * Change the object to be inserted in an edit script.  The object pointed to
+ * by the old object is not freed.  This must be done prior to applying the
+ * script.
+ */
+void assoc_array_insert_set_object(struct assoc_array_edit *edit, void *object)
+{
+       BUG_ON(!object);
+       edit->leaf = assoc_array_leaf_to_ptr(object);
+}
+
+struct assoc_array_delete_collapse_context {
+       struct assoc_array_node *node;
+       const void              *skip_leaf;
+       int                     slot;
+};
+
+/*
+ * Subtree collapse to node iterator.
+ */
+static int assoc_array_delete_collapse_iterator(const void *leaf,
+                                               void *iterator_data)
+{
+       struct assoc_array_delete_collapse_context *collapse = iterator_data;
+
+       if (leaf == collapse->skip_leaf)
+               return 0;
+
+       BUG_ON(collapse->slot >= ASSOC_ARRAY_FAN_OUT);
+
+       collapse->node->slots[collapse->slot++] = assoc_array_leaf_to_ptr(leaf);
+       return 0;
+}
+
+/**
+ * assoc_array_delete - Script deletion of an object from an associative array
+ * @array: The array to search.
+ * @ops: The operations to use.
+ * @index_key: The key to the object.
+ *
+ * Precalculate and preallocate a script for the deletion of an object from an
+ * associative array.  This results in an edit script that can either be
+ * applied or cancelled.
+ *
+ * The function returns a pointer to an edit script if the object was found,
+ * NULL if the object was not found or -ENOMEM.
+ *
+ * The caller should lock against other modifications and must continue to hold
+ * the lock until assoc_array_apply_edit() has been called.
+ *
+ * Accesses to the tree may take place concurrently with this function,
+ * provided they hold the RCU read lock.
+ */
+struct assoc_array_edit *assoc_array_delete(struct assoc_array *array,
+                                           const struct assoc_array_ops *ops,
+                                           const void *index_key)
+{
+       struct assoc_array_delete_collapse_context collapse;
+       struct assoc_array_walk_result result;
+       struct assoc_array_node *node, *new_n0;
+       struct assoc_array_edit *edit;
+       struct assoc_array_ptr *ptr;
+       bool has_meta;
+       int slot, i;
+
+       pr_devel("-->%s()\n", __func__);
+
+       edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
+       if (!edit)
+               return ERR_PTR(-ENOMEM);
+       edit->array = array;
+       edit->ops = ops;
+       edit->adjust_count_by = -1;
+
+       switch (assoc_array_walk(array, ops, index_key, &result)) {
+       case assoc_array_walk_found_terminal_node:
+               /* We found a node that should contain the leaf we've been
+                * asked to remove - *if* it's in the tree.
+                */
+               pr_devel("terminal_node\n");
+               node = result.terminal_node.node;
+
+               for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
+                       ptr = node->slots[slot];
+                       if (ptr &&
+                           assoc_array_ptr_is_leaf(ptr) &&
+                           ops->compare_object(assoc_array_ptr_to_leaf(ptr),
+                                               index_key))
+                               goto found_leaf;
+               }
+       case assoc_array_walk_tree_empty:
+       case assoc_array_walk_found_wrong_shortcut:
+       default:
+               assoc_array_cancel_edit(edit);
+               pr_devel("not found\n");
+               return NULL;
+       }
+
+found_leaf:
+       BUG_ON(array->nr_leaves_on_tree <= 0);
+
+       /* In the simplest form of deletion we just clear the slot and release
+        * the leaf after a suitable interval.
+        */
+       edit->dead_leaf = node->slots[slot];
+       edit->set[0].ptr = &node->slots[slot];
+       edit->set[0].to = NULL;
+       edit->adjust_count_on = node;
+
+       /* If that concludes erasure of the last leaf, then delete the entire
+        * internal array.
+        */
+       if (array->nr_leaves_on_tree == 1) {
+               edit->set[1].ptr = &array->root;
+               edit->set[1].to = NULL;
+               edit->adjust_count_on = NULL;
+               edit->excised_subtree = array->root;
+               pr_devel("all gone\n");
+               return edit;
+       }
+
+       /* However, we'd also like to clear up some metadata blocks if we
+        * possibly can.
+        *
+        * We go for a simple algorithm of: if this node has FAN_OUT or fewer
+        * leaves in it, then attempt to collapse it - and attempt to
+        * recursively collapse up the tree.
+        *
+        * We could also try and collapse in partially filled subtrees to take
+        * up space in this node.
+        */
+       if (node->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) {
+               struct assoc_array_node *parent, *grandparent;
+               struct assoc_array_ptr *ptr;
+
+               /* First of all, we need to know if this node has metadata so
+                * that we don't try collapsing if all the leaves are already
+                * here.
+                */
+               has_meta = false;
+               for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
+                       ptr = node->slots[i];
+                       if (assoc_array_ptr_is_meta(ptr)) {
+                               has_meta = true;
+                               break;
+                       }
+               }
+
+               pr_devel("leaves: %ld [m=%d]\n",
+                        node->nr_leaves_on_branch - 1, has_meta);
+
+               /* Look further up the tree to see if we can collapse this node
+                * into a more proximal node too.
+                */
+               parent = node;
+       collapse_up:
+               pr_devel("collapse subtree: %ld\n", parent->nr_leaves_on_branch);
+
+               ptr = parent->back_pointer;
+               if (!ptr)
+                       goto do_collapse;
+               if (assoc_array_ptr_is_shortcut(ptr)) {
+                       struct assoc_array_shortcut *s = assoc_array_ptr_to_shortcut(ptr);
+                       ptr = s->back_pointer;
+                       if (!ptr)
+                               goto do_collapse;
+               }
+
+               grandparent = assoc_array_ptr_to_node(ptr);
+               if (grandparent->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) {
+                       parent = grandparent;
+                       goto collapse_up;
+               }
+
+       do_collapse:
+               /* There's no point collapsing if the original node has no meta
+                * pointers to discard and if we didn't merge into one of that
+                * node's ancestry.
+                */
+               if (has_meta || parent != node) {
+                       node = parent;
+
+                       /* Create a new node to collapse into */
+                       new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
+                       if (!new_n0)
+                               goto enomem;
+                       edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
+
+                       new_n0->back_pointer = node->back_pointer;
+                       new_n0->parent_slot = node->parent_slot;
+                       new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
+                       edit->adjust_count_on = new_n0;
+
+                       collapse.node = new_n0;
+                       collapse.skip_leaf = assoc_array_ptr_to_leaf(edit->dead_leaf);
+                       collapse.slot = 0;
+                       assoc_array_subtree_iterate(assoc_array_node_to_ptr(node),
+                                                   node->back_pointer,
+                                                   assoc_array_delete_collapse_iterator,
+                                                   &collapse);
+                       pr_devel("collapsed %d,%lu\n", collapse.slot, new_n0->nr_leaves_on_branch);
+                       BUG_ON(collapse.slot != new_n0->nr_leaves_on_branch - 1);
+
+                       if (!node->back_pointer) {
+                               edit->set[1].ptr = &array->root;
+                       } else if (assoc_array_ptr_is_leaf(node->back_pointer)) {
+                               BUG();
+                       } else if (assoc_array_ptr_is_node(node->back_pointer)) {
+                               struct assoc_array_node *p =
+                                       assoc_array_ptr_to_node(node->back_pointer);
+                               edit->set[1].ptr = &p->slots[node->parent_slot];
+                       } else if (assoc_array_ptr_is_shortcut(node->back_pointer)) {
+                               struct assoc_array_shortcut *s =
+                                       assoc_array_ptr_to_shortcut(node->back_pointer);
+                               edit->set[1].ptr = &s->next_node;
+                       }
+                       edit->set[1].to = assoc_array_node_to_ptr(new_n0);
+                       edit->excised_subtree = assoc_array_node_to_ptr(node);
+               }
+       }
+
+       return edit;
+
+enomem:
+       /* Clean up after an out of memory error */
+       pr_devel("enomem\n");
+       assoc_array_cancel_edit(edit);
+       return ERR_PTR(-ENOMEM);
+}
+
+/**
+ * assoc_array_clear - Script deletion of all objects from an associative array
+ * @array: The array to clear.
+ * @ops: The operations to use.
+ *
+ * Precalculate and preallocate a script for the deletion of all the objects
+ * from an associative array.  This results in an edit script that can either
+ * be applied or cancelled.
+ *
+ * The function returns a pointer to an edit script if there are objects to be
+ * deleted, NULL if there are no objects in the array or -ENOMEM.
+ *
+ * The caller should lock against other modifications and must continue to hold
+ * the lock until assoc_array_apply_edit() has been called.
+ *
+ * Accesses to the tree may take place concurrently with this function,
+ * provided they hold the RCU read lock.
+ */
+struct assoc_array_edit *assoc_array_clear(struct assoc_array *array,
+                                          const struct assoc_array_ops *ops)
+{
+       struct assoc_array_edit *edit;
+
+       pr_devel("-->%s()\n", __func__);
+
+       if (!array->root)
+               return NULL;
+
+       edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
+       if (!edit)
+               return ERR_PTR(-ENOMEM);
+       edit->array = array;
+       edit->ops = ops;
+       edit->set[1].ptr = &array->root;
+       edit->set[1].to = NULL;
+       edit->excised_subtree = array->root;
+       edit->ops_for_excised_subtree = ops;
+       pr_devel("all gone\n");
+       return edit;
+}
+
+/*
+ * Handle the deferred destruction after an applied edit.
+ */
+static void assoc_array_rcu_cleanup(struct rcu_head *head)
+{
+       struct assoc_array_edit *edit =
+               container_of(head, struct assoc_array_edit, rcu);
+       int i;
+
+       pr_devel("-->%s()\n", __func__);
+
+       if (edit->dead_leaf)
+               edit->ops->free_object(assoc_array_ptr_to_leaf(edit->dead_leaf));
+       for (i = 0; i < ARRAY_SIZE(edit->excised_meta); i++)
+               if (edit->excised_meta[i])
+                       kfree(assoc_array_ptr_to_node(edit->excised_meta[i]));
+
+       if (edit->excised_subtree) {
+               BUG_ON(assoc_array_ptr_is_leaf(edit->excised_subtree));
+               if (assoc_array_ptr_is_node(edit->excised_subtree)) {
+                       struct assoc_array_node *n =
+                               assoc_array_ptr_to_node(edit->excised_subtree);
+                       n->back_pointer = NULL;
+               } else {
+                       struct assoc_array_shortcut *s =
+                               assoc_array_ptr_to_shortcut(edit->excised_subtree);
+                       s->back_pointer = NULL;
+               }
+               assoc_array_destroy_subtree(edit->excised_subtree,
+                                           edit->ops_for_excised_subtree);
+       }
+
+       kfree(edit);
+}
+
+/**
+ * assoc_array_apply_edit - Apply an edit script to an associative array
+ * @edit: The script to apply.
+ *
+ * Apply an edit script to an associative array to effect an insertion,
+ * deletion or clearance.  As the edit script includes preallocated memory,
+ * this is guaranteed not to fail.
+ *
+ * The edit script, dead objects and dead metadata will be scheduled for
+ * destruction after an RCU grace period to permit those doing read-only
+ * accesses on the array to continue to do so under the RCU read lock whilst
+ * the edit is taking place.
+ */
+void assoc_array_apply_edit(struct assoc_array_edit *edit)
+{
+       struct assoc_array_shortcut *shortcut;
+       struct assoc_array_node *node;
+       struct assoc_array_ptr *ptr;
+       int i;
+
+       pr_devel("-->%s()\n", __func__);
+
+       smp_wmb();
+       if (edit->leaf_p)
+               *edit->leaf_p = edit->leaf;
+
+       smp_wmb();
+       for (i = 0; i < ARRAY_SIZE(edit->set_parent_slot); i++)
+               if (edit->set_parent_slot[i].p)
+                       *edit->set_parent_slot[i].p = edit->set_parent_slot[i].to;
+
+       smp_wmb();
+       for (i = 0; i < ARRAY_SIZE(edit->set_backpointers); i++)
+               if (edit->set_backpointers[i])
+                       *edit->set_backpointers[i] = edit->set_backpointers_to;
+
+       smp_wmb();
+       for (i = 0; i < ARRAY_SIZE(edit->set); i++)
+               if (edit->set[i].ptr)
+                       *edit->set[i].ptr = edit->set[i].to;
+
+       if (edit->array->root == NULL) {
+               edit->array->nr_leaves_on_tree = 0;
+       } else if (edit->adjust_count_on) {
+               node = edit->adjust_count_on;
+               for (;;) {
+                       node->nr_leaves_on_branch += edit->adjust_count_by;
+
+                       ptr = node->back_pointer;
+                       if (!ptr)
+                               break;
+                       if (assoc_array_ptr_is_shortcut(ptr)) {
+                               shortcut = assoc_array_ptr_to_shortcut(ptr);
+                               ptr = shortcut->back_pointer;
+                               if (!ptr)
+                                       break;
+                       }
+                       BUG_ON(!assoc_array_ptr_is_node(ptr));
+                       node = assoc_array_ptr_to_node(ptr);
+               }
+
+               edit->array->nr_leaves_on_tree += edit->adjust_count_by;
+       }
+
+       call_rcu(&edit->rcu, assoc_array_rcu_cleanup);
+}
+
+/**
+ * assoc_array_cancel_edit - Discard an edit script.
+ * @edit: The script to discard.
+ *
+ * Free an edit script and all the preallocated data it holds without making
+ * any changes to the associative array it was intended for.
+ *
+ * NOTE!  In the case of an insertion script, this does _not_ release the leaf
+ * that was to be inserted.  That is left to the caller.
+ */
+void assoc_array_cancel_edit(struct assoc_array_edit *edit)
+{
+       struct assoc_array_ptr *ptr;
+       int i;
+
+       pr_devel("-->%s()\n", __func__);
+
+       /* Clean up after an out of memory error */
+       for (i = 0; i < ARRAY_SIZE(edit->new_meta); i++) {
+               ptr = edit->new_meta[i];
+               if (ptr) {
+                       if (assoc_array_ptr_is_node(ptr))
+                               kfree(assoc_array_ptr_to_node(ptr));
+                       else
+                               kfree(assoc_array_ptr_to_shortcut(ptr));
+               }
+       }
+       kfree(edit);
+}
+
+/**
+ * assoc_array_gc - Garbage collect an associative array.
+ * @array: The array to clean.
+ * @ops: The operations to use.
+ * @iterator: A callback function to pass judgement on each object.
+ * @iterator_data: Private data for the callback function.
+ *
+ * Collect garbage from an associative array and pack down the internal tree to
+ * save memory.
+ *
+ * The iterator function is asked to pass judgement upon each object in the
+ * array.  If it returns false, the object is discard and if it returns true,
+ * the object is kept.  If it returns true, it must increment the object's
+ * usage count (or whatever it needs to do to retain it) before returning.
+ *
+ * This function returns 0 if successful or -ENOMEM if out of memory.  In the
+ * latter case, the array is not changed.
+ *
+ * The caller should lock against other modifications and must continue to hold
+ * the lock until assoc_array_apply_edit() has been called.
+ *
+ * Accesses to the tree may take place concurrently with this function,
+ * provided they hold the RCU read lock.
+ */
+int assoc_array_gc(struct assoc_array *array,
+                  const struct assoc_array_ops *ops,
+                  bool (*iterator)(void *object, void *iterator_data),
+                  void *iterator_data)
+{
+       struct assoc_array_shortcut *shortcut, *new_s;
+       struct assoc_array_node *node, *new_n;
+       struct assoc_array_edit *edit;
+       struct assoc_array_ptr *cursor, *ptr;
+       struct assoc_array_ptr *new_root, *new_parent, **new_ptr_pp;
+       unsigned long nr_leaves_on_tree;
+       int keylen, slot, nr_free, next_slot, i;
+
+       pr_devel("-->%s()\n", __func__);
+
+       if (!array->root)
+               return 0;
+
+       edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
+       if (!edit)
+               return -ENOMEM;
+       edit->array = array;
+       edit->ops = ops;
+       edit->ops_for_excised_subtree = ops;
+       edit->set[0].ptr = &array->root;
+       edit->excised_subtree = array->root;
+
+       new_root = new_parent = NULL;
+       new_ptr_pp = &new_root;
+       cursor = array->root;
+
+descend:
+       /* If this point is a shortcut, then we need to duplicate it and
+        * advance the target cursor.
+        */
+       if (assoc_array_ptr_is_shortcut(cursor)) {
+               shortcut = assoc_array_ptr_to_shortcut(cursor);
+               keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
+               keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
+               new_s = kmalloc(sizeof(struct assoc_array_shortcut) +
+                               keylen * sizeof(unsigned long), GFP_KERNEL);
+               if (!new_s)
+                       goto enomem;
+               pr_devel("dup shortcut %p -> %p\n", shortcut, new_s);
+               memcpy(new_s, shortcut, (sizeof(struct assoc_array_shortcut) +
+                                        keylen * sizeof(unsigned long)));
+               new_s->back_pointer = new_parent;
+               new_s->parent_slot = shortcut->parent_slot;
+               *new_ptr_pp = new_parent = assoc_array_shortcut_to_ptr(new_s);
+               new_ptr_pp = &new_s->next_node;
+               cursor = shortcut->next_node;
+       }
+
+       /* Duplicate the node at this position */
+       node = assoc_array_ptr_to_node(cursor);
+       new_n = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
+       if (!new_n)
+               goto enomem;
+       pr_devel("dup node %p -> %p\n", node, new_n);
+       new_n->back_pointer = new_parent;
+       new_n->parent_slot = node->parent_slot;
+       *new_ptr_pp = new_parent = assoc_array_node_to_ptr(new_n);
+       new_ptr_pp = NULL;
+       slot = 0;
+
+continue_node:
+       /* Filter across any leaves and gc any subtrees */
+       for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
+               ptr = node->slots[slot];
+               if (!ptr)
+                       continue;
+
+               if (assoc_array_ptr_is_leaf(ptr)) {
+                       if (iterator(assoc_array_ptr_to_leaf(ptr),
+                                    iterator_data))
+                               /* The iterator will have done any reference
+                                * counting on the object for us.
+                                */
+                               new_n->slots[slot] = ptr;
+                       continue;
+               }
+
+               new_ptr_pp = &new_n->slots[slot];
+               cursor = ptr;
+               goto descend;
+       }
+
+       pr_devel("-- compress node %p --\n", new_n);
+
+       /* Count up the number of empty slots in this node and work out the
+        * subtree leaf count.
+        */
+       new_n->nr_leaves_on_branch = 0;
+       nr_free = 0;
+       for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
+               ptr = new_n->slots[slot];
+               if (!ptr)
+                       nr_free++;
+               else if (assoc_array_ptr_is_leaf(ptr))
+                       new_n->nr_leaves_on_branch++;
+       }
+       pr_devel("free=%d, leaves=%lu\n", nr_free, new_n->nr_leaves_on_branch);
+
+       /* See what we can fold in */
+       next_slot = 0;
+       for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
+               struct assoc_array_shortcut *s;
+               struct assoc_array_node *child;
+
+               ptr = new_n->slots[slot];
+               if (!ptr || assoc_array_ptr_is_leaf(ptr))
+                       continue;
+
+               s = NULL;
+               if (assoc_array_ptr_is_shortcut(ptr)) {
+                       s = assoc_array_ptr_to_shortcut(ptr);
+                       ptr = s->next_node;
+               }
+
+               child = assoc_array_ptr_to_node(ptr);
+               new_n->nr_leaves_on_branch += child->nr_leaves_on_branch;
+
+               if (child->nr_leaves_on_branch <= nr_free + 1) {
+                       /* Fold the child node into this one */
+                       pr_devel("[%d] fold node %lu/%d [nx %d]\n",
+                                slot, child->nr_leaves_on_branch, nr_free + 1,
+                                next_slot);
+
+                       /* We would already have reaped an intervening shortcut
+                        * on the way back up the tree.
+                        */
+                       BUG_ON(s);
+
+                       new_n->slots[slot] = NULL;
+                       nr_free++;
+                       if (slot < next_slot)
+                               next_slot = slot;
+                       for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
+                               struct assoc_array_ptr *p = child->slots[i];
+                               if (!p)
+                                       continue;
+                               BUG_ON(assoc_array_ptr_is_meta(p));
+                               while (new_n->slots[next_slot])
+                                       next_slot++;
+                               BUG_ON(next_slot >= ASSOC_ARRAY_FAN_OUT);
+                               new_n->slots[next_slot++] = p;
+                               nr_free--;
+                       }
+                       kfree(child);
+               } else {
+                       pr_devel("[%d] retain node %lu/%d [nx %d]\n",
+                                slot, child->nr_leaves_on_branch, nr_free + 1,
+                                next_slot);
+               }
+       }
+
+       pr_devel("after: %lu\n", new_n->nr_leaves_on_branch);
+
+       nr_leaves_on_tree = new_n->nr_leaves_on_branch;
+
+       /* Excise this node if it is singly occupied by a shortcut */
+       if (nr_free == ASSOC_ARRAY_FAN_OUT - 1) {
+               for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++)
+                       if ((ptr = new_n->slots[slot]))
+                               break;
+
+               if (assoc_array_ptr_is_meta(ptr) &&
+                   assoc_array_ptr_is_shortcut(ptr)) {
+                       pr_devel("excise node %p with 1 shortcut\n", new_n);
+                       new_s = assoc_array_ptr_to_shortcut(ptr);
+                       new_parent = new_n->back_pointer;
+                       slot = new_n->parent_slot;
+                       kfree(new_n);
+                       if (!new_parent) {
+                               new_s->back_pointer = NULL;
+                               new_s->parent_slot = 0;
+                               new_root = ptr;
+                               goto gc_complete;
+                       }
+
+                       if (assoc_array_ptr_is_shortcut(new_parent)) {
+                               /* We can discard any preceding shortcut also */
+                               struct assoc_array_shortcut *s =
+                                       assoc_array_ptr_to_shortcut(new_parent);
+
+                               pr_devel("excise preceding shortcut\n");
+
+                               new_parent = new_s->back_pointer = s->back_pointer;
+                               slot = new_s->parent_slot = s->parent_slot;
+                               kfree(s);
+                               if (!new_parent) {
+                                       new_s->back_pointer = NULL;
+                                       new_s->parent_slot = 0;
+                                       new_root = ptr;
+                                       goto gc_complete;
+                               }
+                       }
+
+                       new_s->back_pointer = new_parent;
+                       new_s->parent_slot = slot;
+                       new_n = assoc_array_ptr_to_node(new_parent);
+                       new_n->slots[slot] = ptr;
+                       goto ascend_old_tree;
+               }
+       }
+
+       /* Excise any shortcuts we might encounter that point to nodes that
+        * only contain leaves.
+        */
+       ptr = new_n->back_pointer;
+       if (!ptr)
+               goto gc_complete;
+
+       if (assoc_array_ptr_is_shortcut(ptr)) {
+               new_s = assoc_array_ptr_to_shortcut(ptr);
+               new_parent = new_s->back_pointer;
+               slot = new_s->parent_slot;
+
+               if (new_n->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT) {
+                       struct assoc_array_node *n;
+
+                       pr_devel("excise shortcut\n");
+                       new_n->back_pointer = new_parent;
+                       new_n->parent_slot = slot;
+                       kfree(new_s);
+                       if (!new_parent) {
+                               new_root = assoc_array_node_to_ptr(new_n);
+                               goto gc_complete;
+                       }
+
+                       n = assoc_array_ptr_to_node(new_parent);
+                       n->slots[slot] = assoc_array_node_to_ptr(new_n);
+               }
+       } else {
+               new_parent = ptr;
+       }
+       new_n = assoc_array_ptr_to_node(new_parent);
+
+ascend_old_tree:
+       ptr = node->back_pointer;
+       if (assoc_array_ptr_is_shortcut(ptr)) {
+               shortcut = assoc_array_ptr_to_shortcut(ptr);
+               slot = shortcut->parent_slot;
+               cursor = shortcut->back_pointer;
+       } else {
+               slot = node->parent_slot;
+               cursor = ptr;
+       }
+       BUG_ON(!ptr);
+       node = assoc_array_ptr_to_node(cursor);
+       slot++;
+       goto continue_node;
+
+gc_complete:
+       edit->set[0].to = new_root;
+       assoc_array_apply_edit(edit);
+       edit->array->nr_leaves_on_tree = nr_leaves_on_tree;
+       return 0;
+
+enomem:
+       pr_devel("enomem\n");
+       assoc_array_destroy_subtree(new_root, edit->ops);
+       kfree(edit);
+       return -ENOMEM;
+}
index 7b7f830..d79b9d2 100644 (file)
@@ -215,7 +215,7 @@ static unsigned long kfifo_copy_from_user(struct __kfifo *fifo,
         * incrementing the fifo->in index counter
         */
        smp_wmb();
-       *copied = len - ret;
+       *copied = len - ret * esize;
        /* return the number of elements which are not copied */
        return ret;
 }
@@ -275,7 +275,7 @@ static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to,
         * incrementing the fifo->out index counter
         */
        smp_wmb();
-       *copied = len - ret;
+       *copied = len - ret * esize;
        /* return the number of elements which are not copied */
        return ret;
 }
index 4a70d12..f76196d 100644 (file)
@@ -81,3 +81,25 @@ struct llist_node *llist_del_first(struct llist_head *head)
        return entry;
 }
 EXPORT_SYMBOL_GPL(llist_del_first);
+
+/**
+ * llist_reverse_order - reverse order of a llist chain
+ * @head:      first item of the list to be reversed
+ *
+ * Reverse the order of a chain of llist entries and return the
+ * new first entry.
+ */
+struct llist_node *llist_reverse_order(struct llist_node *head)
+{
+       struct llist_node *new_head = NULL;
+
+       while (head) {
+               struct llist_node *tmp = head;
+               head = head->next;
+               tmp->next = new_head;
+               new_head = tmp;
+       }
+
+       return new_head;
+}
+EXPORT_SYMBOL_GPL(llist_reverse_order);
index af6e95d..d2b123f 100644 (file)
@@ -1,7 +1,7 @@
 #include <linux/export.h>
 #include <linux/lockref.h>
 
-#ifdef CONFIG_CMPXCHG_LOCKREF
+#if USE_CMPXCHG_LOCKREF
 
 /*
  * Allow weakly-ordered memory architectures to provide barrier-less
index 657979f..bf076d2 100644 (file)
@@ -121,3 +121,6 @@ void mpi_free(MPI a)
        kfree(a);
 }
 EXPORT_SYMBOL_GPL(mpi_free);
+
+MODULE_DESCRIPTION("Multiprecision maths library");
+MODULE_LICENSE("GPL");
index b0698ea..9d054bf 100644 (file)
@@ -117,8 +117,7 @@ static inline void alloc_global_tags(struct percpu_ida *pool,
                  min(pool->nr_free, pool->percpu_batch_size));
 }
 
-static inline unsigned alloc_local_tag(struct percpu_ida *pool,
-                                      struct percpu_ida_cpu *tags)
+static inline unsigned alloc_local_tag(struct percpu_ida_cpu *tags)
 {
        int tag = -ENOSPC;
 
@@ -159,7 +158,7 @@ int percpu_ida_alloc(struct percpu_ida *pool, gfp_t gfp)
        tags = this_cpu_ptr(pool->tag_cpu);
 
        /* Fastpath */
-       tag = alloc_local_tag(pool, tags);
+       tag = alloc_local_tag(tags);
        if (likely(tag >= 0)) {
                local_irq_restore(flags);
                return tag;
index 82da4f4..1e5b2df 100644 (file)
@@ -214,18 +214,22 @@ static DEFINE_TIMER(seed_timer, __prandom_timer, 0, 0);
 static void __prandom_timer(unsigned long dontcare)
 {
        u32 entropy;
+       unsigned long expires;
 
        get_random_bytes(&entropy, sizeof(entropy));
        prandom_seed(entropy);
+
        /* reseed every ~60 seconds, in [40 .. 80) interval with slack */
-       seed_timer.expires = jiffies + (40 * HZ + (prandom_u32() % (40 * HZ)));
+       expires = 40 + (prandom_u32() % 40);
+       seed_timer.expires = jiffies + msecs_to_jiffies(expires * MSEC_PER_SEC);
+
        add_timer(&seed_timer);
 }
 
-static void prandom_start_seed_timer(void)
+static void __init __prandom_start_seed_timer(void)
 {
        set_timer_slack(&seed_timer, HZ);
-       seed_timer.expires = jiffies + 40 * HZ;
+       seed_timer.expires = jiffies + msecs_to_jiffies(40 * MSEC_PER_SEC);
        add_timer(&seed_timer);
 }
 
@@ -270,7 +274,7 @@ void prandom_reseed_late(void)
 static int __init prandom_reseed(void)
 {
        __prandom_reseed(false);
-       prandom_start_seed_timer();
+       __prandom_start_seed_timer();
        return 0;
 }
 late_initcall(prandom_reseed);
index 4e8686c..e4399fa 100644 (file)
@@ -38,6 +38,9 @@
 #include <linux/bootmem.h>
 #include <linux/iommu-helper.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/swiotlb.h>
+
 #define OFFSET(val,align) ((unsigned long)     \
                           ( (val) & ( (align) - 1)))
 
@@ -502,6 +505,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
 
 not_found:
        spin_unlock_irqrestore(&io_tlb_lock, flags);
+       dev_warn(hwdev, "swiotlb buffer is full\n");
        return SWIOTLB_MAP_ERROR;
 found:
        spin_unlock_irqrestore(&io_tlb_lock, flags);
@@ -726,6 +730,8 @@ dma_addr_t swiotlb_map_page(struct device *dev, struct page *page,
        if (dma_capable(dev, dev_addr, size) && !swiotlb_force)
                return dev_addr;
 
+       trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force);
+
        /* Oh well, have to allocate and map a bounce buffer. */
        map = map_single(dev, phys, size, dir);
        if (map == SWIOTLB_MAP_ERROR) {
index 48586ac..10909c5 100644 (file)
@@ -1712,18 +1712,16 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
                        break;
 
                case FORMAT_TYPE_NRCHARS: {
-                       u8 qualifier = spec.qualifier;
+                       /*
+                        * Since %n poses a greater security risk than
+                        * utility, ignore %n and skip its argument.
+                        */
+                       void *skip_arg;
 
-                       if (qualifier == 'l') {
-                               long *ip = va_arg(args, long *);
-                               *ip = (str - buf);
-                       } else if (_tolower(qualifier) == 'z') {
-                               size_t *ip = va_arg(args, size_t *);
-                               *ip = (str - buf);
-                       } else {
-                               int *ip = va_arg(args, int *);
-                               *ip = (str - buf);
-                       }
+                       WARN_ONCE(1, "Please remove ignored %%n in '%s'\n",
+                                       old_fmt);
+
+                       skip_arg = va_arg(args, void *);
                        break;
                }
 
index 3f4ffda..eb69f35 100644 (file)
@@ -20,7 +20,7 @@ config FLATMEM_MANUAL
 
          Some users of more advanced features like NUMA and
          memory hotplug may have different options here.
-         DISCONTIGMEM is an more mature, better tested system,
+         DISCONTIGMEM is a more mature, better tested system,
          but is incompatible with memory hotplug and may suffer
          decreased performance over SPARSEMEM.  If unsure between
          "Sparse Memory" and "Discontiguous Memory", choose
@@ -218,9 +218,11 @@ config SPLIT_PTLOCK_CPUS
        int
        default "999999" if ARM && !CPU_CACHE_VIPT
        default "999999" if PARISC && !PA20
-       default "999999" if DEBUG_SPINLOCK || DEBUG_LOCK_ALLOC
        default "4"
 
+config ARCH_ENABLE_SPLIT_PMD_PTLOCK
+       boolean
+
 #
 # support for memory balloon compaction
 config BALLOON_COMPACTION
index ae4846f..b7749a9 100644 (file)
@@ -1090,7 +1090,6 @@ static void shrink_readahead_size_eio(struct file *filp,
  * @filp:      the file to read
  * @ppos:      current file position
  * @desc:      read_descriptor
- * @actor:     read method
  *
  * This is a generic file read routine, and uses the
  * mapping->a_ops->readpage() function for the actual low-level stuff.
@@ -1099,7 +1098,7 @@ static void shrink_readahead_size_eio(struct file *filp,
  * of the logic when it comes to error handling etc.
  */
 static void do_generic_file_read(struct file *filp, loff_t *ppos,
-               read_descriptor_t *desc, read_actor_t actor)
+               read_descriptor_t *desc)
 {
        struct address_space *mapping = filp->f_mapping;
        struct inode *inode = mapping->host;
@@ -1200,13 +1199,14 @@ page_ok:
                 * Ok, we have the page, and it's up-to-date, so
                 * now we can copy it to user space...
                 *
-                * The actor routine returns how many bytes were actually used..
+                * The file_read_actor routine returns how many bytes were
+                * actually used..
                 * NOTE! This may not be the same as how much of a user buffer
                 * we filled up (we may be padding etc), so we can only update
                 * "pos" here (the actor routine has to update the user buffer
                 * pointers and the remaining count).
                 */
-               ret = actor(desc, page, offset, nr);
+               ret = file_read_actor(desc, page, offset, nr);
                offset += ret;
                index += offset >> PAGE_CACHE_SHIFT;
                offset &= ~PAGE_CACHE_MASK;
@@ -1479,7 +1479,7 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
                if (desc.count == 0)
                        continue;
                desc.error = 0;
-               do_generic_file_read(filp, ppos, &desc, file_read_actor);
+               do_generic_file_read(filp, ppos, &desc);
                retval += desc.written;
                if (desc.error) {
                        retval = retval ?: desc.error;
index 0556c6a..bccd5a6 100644 (file)
@@ -710,6 +710,7 @@ static int __do_huge_pmd_anonymous_page(struct mm_struct *mm,
                                        struct page *page)
 {
        pgtable_t pgtable;
+       spinlock_t *ptl;
 
        VM_BUG_ON(!PageCompound(page));
        pgtable = pte_alloc_one(mm, haddr);
@@ -724,9 +725,9 @@ static int __do_huge_pmd_anonymous_page(struct mm_struct *mm,
         */
        __SetPageUptodate(page);
 
-       spin_lock(&mm->page_table_lock);
+       ptl = pmd_lock(mm, pmd);
        if (unlikely(!pmd_none(*pmd))) {
-               spin_unlock(&mm->page_table_lock);
+               spin_unlock(ptl);
                mem_cgroup_uncharge_page(page);
                put_page(page);
                pte_free(mm, pgtable);
@@ -738,8 +739,8 @@ static int __do_huge_pmd_anonymous_page(struct mm_struct *mm,
                pgtable_trans_huge_deposit(mm, pmd, pgtable);
                set_pmd_at(mm, haddr, pmd, entry);
                add_mm_counter(mm, MM_ANONPAGES, HPAGE_PMD_NR);
-               mm->nr_ptes++;
-               spin_unlock(&mm->page_table_lock);
+               atomic_long_inc(&mm->nr_ptes);
+               spin_unlock(ptl);
        }
 
        return 0;
@@ -759,6 +760,7 @@ static inline struct page *alloc_hugepage_vma(int defrag,
                               HPAGE_PMD_ORDER, vma, haddr, nd);
 }
 
+/* Caller must hold page table lock. */
 static bool set_huge_zero_page(pgtable_t pgtable, struct mm_struct *mm,
                struct vm_area_struct *vma, unsigned long haddr, pmd_t *pmd,
                struct page *zero_page)
@@ -771,7 +773,7 @@ static bool set_huge_zero_page(pgtable_t pgtable, struct mm_struct *mm,
        entry = pmd_mkhuge(entry);
        pgtable_trans_huge_deposit(mm, pmd, pgtable);
        set_pmd_at(mm, haddr, pmd, entry);
-       mm->nr_ptes++;
+       atomic_long_inc(&mm->nr_ptes);
        return true;
 }
 
@@ -790,6 +792,7 @@ int do_huge_pmd_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
                return VM_FAULT_OOM;
        if (!(flags & FAULT_FLAG_WRITE) &&
                        transparent_hugepage_use_zero_page()) {
+               spinlock_t *ptl;
                pgtable_t pgtable;
                struct page *zero_page;
                bool set;
@@ -802,10 +805,10 @@ int do_huge_pmd_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
                        count_vm_event(THP_FAULT_FALLBACK);
                        return VM_FAULT_FALLBACK;
                }
-               spin_lock(&mm->page_table_lock);
+               ptl = pmd_lock(mm, pmd);
                set = set_huge_zero_page(pgtable, mm, vma, haddr, pmd,
                                zero_page);
-               spin_unlock(&mm->page_table_lock);
+               spin_unlock(ptl);
                if (!set) {
                        pte_free(mm, pgtable);
                        put_huge_zero_page();
@@ -838,6 +841,7 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                  pmd_t *dst_pmd, pmd_t *src_pmd, unsigned long addr,
                  struct vm_area_struct *vma)
 {
+       spinlock_t *dst_ptl, *src_ptl;
        struct page *src_page;
        pmd_t pmd;
        pgtable_t pgtable;
@@ -848,8 +852,9 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
        if (unlikely(!pgtable))
                goto out;
 
-       spin_lock(&dst_mm->page_table_lock);
-       spin_lock_nested(&src_mm->page_table_lock, SINGLE_DEPTH_NESTING);
+       dst_ptl = pmd_lock(dst_mm, dst_pmd);
+       src_ptl = pmd_lockptr(src_mm, src_pmd);
+       spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING);
 
        ret = -EAGAIN;
        pmd = *src_pmd;
@@ -858,7 +863,7 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                goto out_unlock;
        }
        /*
-        * mm->page_table_lock is enough to be sure that huge zero pmd is not
+        * When page table lock is held, the huge zero pmd should not be
         * under splitting since we don't split the page itself, only pmd to
         * a page table.
         */
@@ -879,8 +884,8 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
        }
        if (unlikely(pmd_trans_splitting(pmd))) {
                /* split huge page running from under us */
-               spin_unlock(&src_mm->page_table_lock);
-               spin_unlock(&dst_mm->page_table_lock);
+               spin_unlock(src_ptl);
+               spin_unlock(dst_ptl);
                pte_free(dst_mm, pgtable);
 
                wait_split_huge_page(vma->anon_vma, src_pmd); /* src_vma */
@@ -896,12 +901,12 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
        pmd = pmd_mkold(pmd_wrprotect(pmd));
        pgtable_trans_huge_deposit(dst_mm, dst_pmd, pgtable);
        set_pmd_at(dst_mm, addr, dst_pmd, pmd);
-       dst_mm->nr_ptes++;
+       atomic_long_inc(&dst_mm->nr_ptes);
 
        ret = 0;
 out_unlock:
-       spin_unlock(&src_mm->page_table_lock);
-       spin_unlock(&dst_mm->page_table_lock);
+       spin_unlock(src_ptl);
+       spin_unlock(dst_ptl);
 out:
        return ret;
 }
@@ -912,10 +917,11 @@ void huge_pmd_set_accessed(struct mm_struct *mm,
                           pmd_t *pmd, pmd_t orig_pmd,
                           int dirty)
 {
+       spinlock_t *ptl;
        pmd_t entry;
        unsigned long haddr;
 
-       spin_lock(&mm->page_table_lock);
+       ptl = pmd_lock(mm, pmd);
        if (unlikely(!pmd_same(*pmd, orig_pmd)))
                goto unlock;
 
@@ -925,13 +931,14 @@ void huge_pmd_set_accessed(struct mm_struct *mm,
                update_mmu_cache_pmd(vma, address, pmd);
 
 unlock:
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
 }
 
 static int do_huge_pmd_wp_zero_page_fallback(struct mm_struct *mm,
                struct vm_area_struct *vma, unsigned long address,
                pmd_t *pmd, pmd_t orig_pmd, unsigned long haddr)
 {
+       spinlock_t *ptl;
        pgtable_t pgtable;
        pmd_t _pmd;
        struct page *page;
@@ -958,7 +965,7 @@ static int do_huge_pmd_wp_zero_page_fallback(struct mm_struct *mm,
        mmun_end   = haddr + HPAGE_PMD_SIZE;
        mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
 
-       spin_lock(&mm->page_table_lock);
+       ptl = pmd_lock(mm, pmd);
        if (unlikely(!pmd_same(*pmd, orig_pmd)))
                goto out_free_page;
 
@@ -985,7 +992,7 @@ static int do_huge_pmd_wp_zero_page_fallback(struct mm_struct *mm,
        }
        smp_wmb(); /* make pte visible before pmd */
        pmd_populate(mm, pmd, pgtable);
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
        put_huge_zero_page();
        inc_mm_counter(mm, MM_ANONPAGES);
 
@@ -995,7 +1002,7 @@ static int do_huge_pmd_wp_zero_page_fallback(struct mm_struct *mm,
 out:
        return ret;
 out_free_page:
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
        mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
        mem_cgroup_uncharge_page(page);
        put_page(page);
@@ -1009,6 +1016,7 @@ static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm,
                                        struct page *page,
                                        unsigned long haddr)
 {
+       spinlock_t *ptl;
        pgtable_t pgtable;
        pmd_t _pmd;
        int ret = 0, i;
@@ -1055,7 +1063,7 @@ static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm,
        mmun_end   = haddr + HPAGE_PMD_SIZE;
        mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
 
-       spin_lock(&mm->page_table_lock);
+       ptl = pmd_lock(mm, pmd);
        if (unlikely(!pmd_same(*pmd, orig_pmd)))
                goto out_free_pages;
        VM_BUG_ON(!PageHead(page));
@@ -1081,7 +1089,7 @@ static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm,
        smp_wmb(); /* make pte visible before pmd */
        pmd_populate(mm, pmd, pgtable);
        page_remove_rmap(page);
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
 
        mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
 
@@ -1092,7 +1100,7 @@ out:
        return ret;
 
 out_free_pages:
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
        mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
        mem_cgroup_uncharge_start();
        for (i = 0; i < HPAGE_PMD_NR; i++) {
@@ -1107,17 +1115,19 @@ out_free_pages:
 int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
                        unsigned long address, pmd_t *pmd, pmd_t orig_pmd)
 {
+       spinlock_t *ptl;
        int ret = 0;
        struct page *page = NULL, *new_page;
        unsigned long haddr;
        unsigned long mmun_start;       /* For mmu_notifiers */
        unsigned long mmun_end;         /* For mmu_notifiers */
 
+       ptl = pmd_lockptr(mm, pmd);
        VM_BUG_ON(!vma->anon_vma);
        haddr = address & HPAGE_PMD_MASK;
        if (is_huge_zero_pmd(orig_pmd))
                goto alloc;
-       spin_lock(&mm->page_table_lock);
+       spin_lock(ptl);
        if (unlikely(!pmd_same(*pmd, orig_pmd)))
                goto out_unlock;
 
@@ -1133,7 +1143,7 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
                goto out_unlock;
        }
        get_page(page);
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
 alloc:
        if (transparent_hugepage_enabled(vma) &&
            !transparent_hugepage_debug_cow())
@@ -1180,11 +1190,11 @@ alloc:
        mmun_end   = haddr + HPAGE_PMD_SIZE;
        mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
 
-       spin_lock(&mm->page_table_lock);
+       spin_lock(ptl);
        if (page)
                put_page(page);
        if (unlikely(!pmd_same(*pmd, orig_pmd))) {
-               spin_unlock(&mm->page_table_lock);
+               spin_unlock(ptl);
                mem_cgroup_uncharge_page(new_page);
                put_page(new_page);
                goto out_mn;
@@ -1206,13 +1216,13 @@ alloc:
                }
                ret |= VM_FAULT_WRITE;
        }
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
 out_mn:
        mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
 out:
        return ret;
 out_unlock:
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
        return ret;
 }
 
@@ -1224,7 +1234,7 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
        struct mm_struct *mm = vma->vm_mm;
        struct page *page = NULL;
 
-       assert_spin_locked(&mm->page_table_lock);
+       assert_spin_locked(pmd_lockptr(mm, pmd));
 
        if (flags & FOLL_WRITE && !pmd_write(*pmd))
                goto out;
@@ -1271,6 +1281,7 @@ out:
 int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
                                unsigned long addr, pmd_t pmd, pmd_t *pmdp)
 {
+       spinlock_t *ptl;
        struct anon_vma *anon_vma = NULL;
        struct page *page;
        unsigned long haddr = addr & HPAGE_PMD_MASK;
@@ -1280,7 +1291,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
        bool migrated = false;
        int flags = 0;
 
-       spin_lock(&mm->page_table_lock);
+       ptl = pmd_lock(mm, pmdp);
        if (unlikely(!pmd_same(pmd, *pmdp)))
                goto out_unlock;
 
@@ -1318,7 +1329,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
                 * relock and check_same as the page may no longer be mapped.
                 * As the fault is being retried, do not account for it.
                 */
-               spin_unlock(&mm->page_table_lock);
+               spin_unlock(ptl);
                wait_on_page_locked(page);
                page_nid = -1;
                goto out;
@@ -1326,13 +1337,13 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
 
        /* Page is misplaced, serialise migrations and parallel THP splits */
        get_page(page);
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
        if (!page_locked)
                lock_page(page);
        anon_vma = page_lock_anon_vma_read(page);
 
        /* Confirm the PMD did not change while page_table_lock was released */
-       spin_lock(&mm->page_table_lock);
+       spin_lock(ptl);
        if (unlikely(!pmd_same(pmd, *pmdp))) {
                unlock_page(page);
                put_page(page);
@@ -1344,7 +1355,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
         * Migrate the THP to the requested node, returns with page unlocked
         * and pmd_numa cleared.
         */
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
        migrated = migrate_misplaced_transhuge_page(mm, vma,
                                pmdp, pmd, addr, page, target_nid);
        if (migrated) {
@@ -1361,7 +1372,7 @@ clear_pmdnuma:
        update_mmu_cache_pmd(vma, addr, pmdp);
        unlock_page(page);
 out_unlock:
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
 
 out:
        if (anon_vma)
@@ -1376,9 +1387,10 @@ out:
 int zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
                 pmd_t *pmd, unsigned long addr)
 {
+       spinlock_t *ptl;
        int ret = 0;
 
-       if (__pmd_trans_huge_lock(pmd, vma) == 1) {
+       if (__pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
                struct page *page;
                pgtable_t pgtable;
                pmd_t orig_pmd;
@@ -1392,8 +1404,8 @@ int zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
                tlb_remove_pmd_tlb_entry(tlb, pmd, addr);
                pgtable = pgtable_trans_huge_withdraw(tlb->mm, pmd);
                if (is_huge_zero_pmd(orig_pmd)) {
-                       tlb->mm->nr_ptes--;
-                       spin_unlock(&tlb->mm->page_table_lock);
+                       atomic_long_dec(&tlb->mm->nr_ptes);
+                       spin_unlock(ptl);
                        put_huge_zero_page();
                } else {
                        page = pmd_page(orig_pmd);
@@ -1401,8 +1413,8 @@ int zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
                        VM_BUG_ON(page_mapcount(page) < 0);
                        add_mm_counter(tlb->mm, MM_ANONPAGES, -HPAGE_PMD_NR);
                        VM_BUG_ON(!PageHead(page));
-                       tlb->mm->nr_ptes--;
-                       spin_unlock(&tlb->mm->page_table_lock);
+                       atomic_long_dec(&tlb->mm->nr_ptes);
+                       spin_unlock(ptl);
                        tlb_remove_page(tlb, page);
                }
                pte_free(tlb->mm, pgtable);
@@ -1415,14 +1427,15 @@ int mincore_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
                unsigned long addr, unsigned long end,
                unsigned char *vec)
 {
+       spinlock_t *ptl;
        int ret = 0;
 
-       if (__pmd_trans_huge_lock(pmd, vma) == 1) {
+       if (__pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
                /*
                 * All logical pages in the range are present
                 * if backed by a huge page.
                 */
-               spin_unlock(&vma->vm_mm->page_table_lock);
+               spin_unlock(ptl);
                memset(vec, 1, (end - addr) >> PAGE_SHIFT);
                ret = 1;
        }
@@ -1435,6 +1448,7 @@ int move_huge_pmd(struct vm_area_struct *vma, struct vm_area_struct *new_vma,
                  unsigned long new_addr, unsigned long old_end,
                  pmd_t *old_pmd, pmd_t *new_pmd)
 {
+       spinlock_t *old_ptl, *new_ptl;
        int ret = 0;
        pmd_t pmd;
 
@@ -1455,12 +1469,21 @@ int move_huge_pmd(struct vm_area_struct *vma, struct vm_area_struct *new_vma,
                goto out;
        }
 
-       ret = __pmd_trans_huge_lock(old_pmd, vma);
+       /*
+        * We don't have to worry about the ordering of src and dst
+        * ptlocks because exclusive mmap_sem prevents deadlock.
+        */
+       ret = __pmd_trans_huge_lock(old_pmd, vma, &old_ptl);
        if (ret == 1) {
+               new_ptl = pmd_lockptr(mm, new_pmd);
+               if (new_ptl != old_ptl)
+                       spin_lock_nested(new_ptl, SINGLE_DEPTH_NESTING);
                pmd = pmdp_get_and_clear(mm, old_addr, old_pmd);
                VM_BUG_ON(!pmd_none(*new_pmd));
                set_pmd_at(mm, new_addr, new_pmd, pmd_mksoft_dirty(pmd));
-               spin_unlock(&mm->page_table_lock);
+               if (new_ptl != old_ptl)
+                       spin_unlock(new_ptl);
+               spin_unlock(old_ptl);
        }
 out:
        return ret;
@@ -1476,9 +1499,10 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
                unsigned long addr, pgprot_t newprot, int prot_numa)
 {
        struct mm_struct *mm = vma->vm_mm;
+       spinlock_t *ptl;
        int ret = 0;
 
-       if (__pmd_trans_huge_lock(pmd, vma) == 1) {
+       if (__pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
                pmd_t entry;
                ret = 1;
                if (!prot_numa) {
@@ -1507,7 +1531,7 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
                if (ret == HPAGE_PMD_NR)
                        set_pmd_at(mm, addr, pmd, entry);
 
-               spin_unlock(&vma->vm_mm->page_table_lock);
+               spin_unlock(ptl);
        }
 
        return ret;
@@ -1520,12 +1544,13 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
  * Note that if it returns 1, this routine returns without unlocking page
  * table locks. So callers must unlock them.
  */
-int __pmd_trans_huge_lock(pmd_t *pmd, struct vm_area_struct *vma)
+int __pmd_trans_huge_lock(pmd_t *pmd, struct vm_area_struct *vma,
+               spinlock_t **ptl)
 {
-       spin_lock(&vma->vm_mm->page_table_lock);
+       *ptl = pmd_lock(vma->vm_mm, pmd);
        if (likely(pmd_trans_huge(*pmd))) {
                if (unlikely(pmd_trans_splitting(*pmd))) {
-                       spin_unlock(&vma->vm_mm->page_table_lock);
+                       spin_unlock(*ptl);
                        wait_split_huge_page(vma->anon_vma, pmd);
                        return -1;
                } else {
@@ -1534,27 +1559,37 @@ int __pmd_trans_huge_lock(pmd_t *pmd, struct vm_area_struct *vma)
                        return 1;
                }
        }
-       spin_unlock(&vma->vm_mm->page_table_lock);
+       spin_unlock(*ptl);
        return 0;
 }
 
+/*
+ * This function returns whether a given @page is mapped onto the @address
+ * in the virtual space of @mm.
+ *
+ * When it's true, this function returns *pmd with holding the page table lock
+ * and passing it back to the caller via @ptl.
+ * If it's false, returns NULL without holding the page table lock.
+ */
 pmd_t *page_check_address_pmd(struct page *page,
                              struct mm_struct *mm,
                              unsigned long address,
-                             enum page_check_address_pmd_flag flag)
+                             enum page_check_address_pmd_flag flag,
+                             spinlock_t **ptl)
 {
-       pmd_t *pmd, *ret = NULL;
+       pmd_t *pmd;
 
        if (address & ~HPAGE_PMD_MASK)
-               goto out;
+               return NULL;
 
        pmd = mm_find_pmd(mm, address);
        if (!pmd)
-               goto out;
+               return NULL;
+       *ptl = pmd_lock(mm, pmd);
        if (pmd_none(*pmd))
-               goto out;
+               goto unlock;
        if (pmd_page(*pmd) != page)
-               goto out;
+               goto unlock;
        /*
         * split_vma() may create temporary aliased mappings. There is
         * no risk as long as all huge pmd are found and have their
@@ -1564,14 +1599,15 @@ pmd_t *page_check_address_pmd(struct page *page,
         */
        if (flag == PAGE_CHECK_ADDRESS_PMD_NOTSPLITTING_FLAG &&
            pmd_trans_splitting(*pmd))
-               goto out;
+               goto unlock;
        if (pmd_trans_huge(*pmd)) {
                VM_BUG_ON(flag == PAGE_CHECK_ADDRESS_PMD_SPLITTING_FLAG &&
                          !pmd_trans_splitting(*pmd));
-               ret = pmd;
+               return pmd;
        }
-out:
-       return ret;
+unlock:
+       spin_unlock(*ptl);
+       return NULL;
 }
 
 static int __split_huge_page_splitting(struct page *page,
@@ -1579,6 +1615,7 @@ static int __split_huge_page_splitting(struct page *page,
                                       unsigned long address)
 {
        struct mm_struct *mm = vma->vm_mm;
+       spinlock_t *ptl;
        pmd_t *pmd;
        int ret = 0;
        /* For mmu_notifiers */
@@ -1586,9 +1623,8 @@ static int __split_huge_page_splitting(struct page *page,
        const unsigned long mmun_end   = address + HPAGE_PMD_SIZE;
 
        mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
-       spin_lock(&mm->page_table_lock);
        pmd = page_check_address_pmd(page, mm, address,
-                                    PAGE_CHECK_ADDRESS_PMD_NOTSPLITTING_FLAG);
+                       PAGE_CHECK_ADDRESS_PMD_NOTSPLITTING_FLAG, &ptl);
        if (pmd) {
                /*
                 * We can't temporarily set the pmd to null in order
@@ -1599,8 +1635,8 @@ static int __split_huge_page_splitting(struct page *page,
                 */
                pmdp_splitting_flush(vma, address, pmd);
                ret = 1;
+               spin_unlock(ptl);
        }
-       spin_unlock(&mm->page_table_lock);
        mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
 
        return ret;
@@ -1731,14 +1767,14 @@ static int __split_huge_page_map(struct page *page,
                                 unsigned long address)
 {
        struct mm_struct *mm = vma->vm_mm;
+       spinlock_t *ptl;
        pmd_t *pmd, _pmd;
        int ret = 0, i;
        pgtable_t pgtable;
        unsigned long haddr;
 
-       spin_lock(&mm->page_table_lock);
        pmd = page_check_address_pmd(page, mm, address,
-                                    PAGE_CHECK_ADDRESS_PMD_SPLITTING_FLAG);
+                       PAGE_CHECK_ADDRESS_PMD_SPLITTING_FLAG, &ptl);
        if (pmd) {
                pgtable = pgtable_trans_huge_withdraw(mm, pmd);
                pmd_populate(mm, &_pmd, pgtable);
@@ -1793,8 +1829,8 @@ static int __split_huge_page_map(struct page *page,
                pmdp_invalidate(vma, address, pmd);
                pmd_populate(mm, pmd, pgtable);
                ret = 1;
+               spin_unlock(ptl);
        }
-       spin_unlock(&mm->page_table_lock);
 
        return ret;
 }
@@ -2346,7 +2382,7 @@ static void collapse_huge_page(struct mm_struct *mm,
        pte_t *pte;
        pgtable_t pgtable;
        struct page *new_page;
-       spinlock_t *ptl;
+       spinlock_t *pmd_ptl, *pte_ptl;
        int isolated;
        unsigned long hstart, hend;
        unsigned long mmun_start;       /* For mmu_notifiers */
@@ -2389,12 +2425,12 @@ static void collapse_huge_page(struct mm_struct *mm,
        anon_vma_lock_write(vma->anon_vma);
 
        pte = pte_offset_map(pmd, address);
-       ptl = pte_lockptr(mm, pmd);
+       pte_ptl = pte_lockptr(mm, pmd);
 
        mmun_start = address;
        mmun_end   = address + HPAGE_PMD_SIZE;
        mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
-       spin_lock(&mm->page_table_lock); /* probably unnecessary */
+       pmd_ptl = pmd_lock(mm, pmd); /* probably unnecessary */
        /*
         * After this gup_fast can't run anymore. This also removes
         * any huge TLB entry from the CPU so we won't allow
@@ -2402,16 +2438,16 @@ static void collapse_huge_page(struct mm_struct *mm,
         * to avoid the risk of CPU bugs in that area.
         */
        _pmd = pmdp_clear_flush(vma, address, pmd);
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(pmd_ptl);
        mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
 
-       spin_lock(ptl);
+       spin_lock(pte_ptl);
        isolated = __collapse_huge_page_isolate(vma, address, pte);
-       spin_unlock(ptl);
+       spin_unlock(pte_ptl);
 
        if (unlikely(!isolated)) {
                pte_unmap(pte);
-               spin_lock(&mm->page_table_lock);
+               spin_lock(pmd_ptl);
                BUG_ON(!pmd_none(*pmd));
                /*
                 * We can only use set_pmd_at when establishing
@@ -2419,7 +2455,7 @@ static void collapse_huge_page(struct mm_struct *mm,
                 * points to regular pagetables. Use pmd_populate for that
                 */
                pmd_populate(mm, pmd, pmd_pgtable(_pmd));
-               spin_unlock(&mm->page_table_lock);
+               spin_unlock(pmd_ptl);
                anon_vma_unlock_write(vma->anon_vma);
                goto out;
        }
@@ -2430,7 +2466,7 @@ static void collapse_huge_page(struct mm_struct *mm,
         */
        anon_vma_unlock_write(vma->anon_vma);
 
-       __collapse_huge_page_copy(pte, new_page, vma, address, ptl);
+       __collapse_huge_page_copy(pte, new_page, vma, address, pte_ptl);
        pte_unmap(pte);
        __SetPageUptodate(new_page);
        pgtable = pmd_pgtable(_pmd);
@@ -2445,13 +2481,13 @@ static void collapse_huge_page(struct mm_struct *mm,
         */
        smp_wmb();
 
-       spin_lock(&mm->page_table_lock);
+       spin_lock(pmd_ptl);
        BUG_ON(!pmd_none(*pmd));
        page_add_new_anon_rmap(new_page, vma, address);
        pgtable_trans_huge_deposit(mm, pmd, pgtable);
        set_pmd_at(mm, address, pmd, _pmd);
        update_mmu_cache_pmd(vma, address, pmd);
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(pmd_ptl);
 
        *hpage = NULL;
 
@@ -2780,6 +2816,7 @@ static void __split_huge_zero_page_pmd(struct vm_area_struct *vma,
 void __split_huge_page_pmd(struct vm_area_struct *vma, unsigned long address,
                pmd_t *pmd)
 {
+       spinlock_t *ptl;
        struct page *page;
        struct mm_struct *mm = vma->vm_mm;
        unsigned long haddr = address & HPAGE_PMD_MASK;
@@ -2792,22 +2829,22 @@ void __split_huge_page_pmd(struct vm_area_struct *vma, unsigned long address,
        mmun_end   = haddr + HPAGE_PMD_SIZE;
 again:
        mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
-       spin_lock(&mm->page_table_lock);
+       ptl = pmd_lock(mm, pmd);
        if (unlikely(!pmd_trans_huge(*pmd))) {
-               spin_unlock(&mm->page_table_lock);
+               spin_unlock(ptl);
                mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
                return;
        }
        if (is_huge_zero_pmd(*pmd)) {
                __split_huge_zero_page_pmd(vma, haddr, pmd);
-               spin_unlock(&mm->page_table_lock);
+               spin_unlock(ptl);
                mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
                return;
        }
        page = pmd_page(*pmd);
        VM_BUG_ON(!page_count(page));
        get_page(page);
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
        mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
 
        split_huge_page(page);
index 0b7656e..dee6cf4 100644 (file)
@@ -476,40 +476,6 @@ static int vma_has_reserves(struct vm_area_struct *vma, long chg)
        return 0;
 }
 
-static void copy_gigantic_page(struct page *dst, struct page *src)
-{
-       int i;
-       struct hstate *h = page_hstate(src);
-       struct page *dst_base = dst;
-       struct page *src_base = src;
-
-       for (i = 0; i < pages_per_huge_page(h); ) {
-               cond_resched();
-               copy_highpage(dst, src);
-
-               i++;
-               dst = mem_map_next(dst, dst_base, i);
-               src = mem_map_next(src, src_base, i);
-       }
-}
-
-void copy_huge_page(struct page *dst, struct page *src)
-{
-       int i;
-       struct hstate *h = page_hstate(src);
-
-       if (unlikely(pages_per_huge_page(h) > MAX_ORDER_NR_PAGES)) {
-               copy_gigantic_page(dst, src);
-               return;
-       }
-
-       might_sleep();
-       for (i = 0; i < pages_per_huge_page(h); i++) {
-               cond_resched();
-               copy_highpage(dst + i, src + i);
-       }
-}
-
 static void enqueue_huge_page(struct hstate *h, struct page *page)
 {
        int nid = page_to_nid(page);
@@ -736,6 +702,23 @@ int PageHuge(struct page *page)
 }
 EXPORT_SYMBOL_GPL(PageHuge);
 
+/*
+ * PageHeadHuge() only returns true for hugetlbfs head page, but not for
+ * normal or transparent huge pages.
+ */
+int PageHeadHuge(struct page *page_head)
+{
+       compound_page_dtor *dtor;
+
+       if (!PageHead(page_head))
+               return 0;
+
+       dtor = get_compound_page_dtor(page_head);
+
+       return dtor == free_huge_page;
+}
+EXPORT_SYMBOL_GPL(PageHeadHuge);
+
 pgoff_t __basepage_index(struct page *page)
 {
        struct page *page_head = compound_head(page);
@@ -2376,6 +2359,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
        cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
 
        for (addr = vma->vm_start; addr < vma->vm_end; addr += sz) {
+               spinlock_t *src_ptl, *dst_ptl;
                src_pte = huge_pte_offset(src, addr);
                if (!src_pte)
                        continue;
@@ -2387,8 +2371,9 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
                if (dst_pte == src_pte)
                        continue;
 
-               spin_lock(&dst->page_table_lock);
-               spin_lock_nested(&src->page_table_lock, SINGLE_DEPTH_NESTING);
+               dst_ptl = huge_pte_lock(h, dst, dst_pte);
+               src_ptl = huge_pte_lockptr(h, src, src_pte);
+               spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING);
                if (!huge_pte_none(huge_ptep_get(src_pte))) {
                        if (cow)
                                huge_ptep_set_wrprotect(src, addr, src_pte);
@@ -2398,8 +2383,8 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
                        page_dup_rmap(ptepage);
                        set_huge_pte_at(dst, addr, dst_pte, entry);
                }
-               spin_unlock(&src->page_table_lock);
-               spin_unlock(&dst->page_table_lock);
+               spin_unlock(src_ptl);
+               spin_unlock(dst_ptl);
        }
        return 0;
 
@@ -2442,6 +2427,7 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
        unsigned long address;
        pte_t *ptep;
        pte_t pte;
+       spinlock_t *ptl;
        struct page *page;
        struct hstate *h = hstate_vma(vma);
        unsigned long sz = huge_page_size(h);
@@ -2455,25 +2441,25 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
        tlb_start_vma(tlb, vma);
        mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
 again:
-       spin_lock(&mm->page_table_lock);
        for (address = start; address < end; address += sz) {
                ptep = huge_pte_offset(mm, address);
                if (!ptep)
                        continue;
 
+               ptl = huge_pte_lock(h, mm, ptep);
                if (huge_pmd_unshare(mm, &address, ptep))
-                       continue;
+                       goto unlock;
 
                pte = huge_ptep_get(ptep);
                if (huge_pte_none(pte))
-                       continue;
+                       goto unlock;
 
                /*
                 * HWPoisoned hugepage is already unmapped and dropped reference
                 */
                if (unlikely(is_hugetlb_entry_hwpoisoned(pte))) {
                        huge_pte_clear(mm, address, ptep);
-                       continue;
+                       goto unlock;
                }
 
                page = pte_page(pte);
@@ -2484,7 +2470,7 @@ again:
                 */
                if (ref_page) {
                        if (page != ref_page)
-                               continue;
+                               goto unlock;
 
                        /*
                         * Mark the VMA as having unmapped its page so that
@@ -2501,13 +2487,18 @@ again:
 
                page_remove_rmap(page);
                force_flush = !__tlb_remove_page(tlb, page);
-               if (force_flush)
+               if (force_flush) {
+                       spin_unlock(ptl);
                        break;
+               }
                /* Bail out after unmapping reference page if supplied */
-               if (ref_page)
+               if (ref_page) {
+                       spin_unlock(ptl);
                        break;
+               }
+unlock:
+               spin_unlock(ptl);
        }
-       spin_unlock(&mm->page_table_lock);
        /*
         * mmu_gather ran out of room to batch pages, we break out of
         * the PTE lock to avoid doing the potential expensive TLB invalidate
@@ -2613,7 +2604,7 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
  */
 static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
                        unsigned long address, pte_t *ptep, pte_t pte,
-                       struct page *pagecache_page)
+                       struct page *pagecache_page, spinlock_t *ptl)
 {
        struct hstate *h = hstate_vma(vma);
        struct page *old_page, *new_page;
@@ -2647,8 +2638,8 @@ retry_avoidcopy:
 
        page_cache_get(old_page);
 
-       /* Drop page_table_lock as buddy allocator may be called */
-       spin_unlock(&mm->page_table_lock);
+       /* Drop page table lock as buddy allocator may be called */
+       spin_unlock(ptl);
        new_page = alloc_huge_page(vma, address, outside_reserve);
 
        if (IS_ERR(new_page)) {
@@ -2666,13 +2657,13 @@ retry_avoidcopy:
                        BUG_ON(huge_pte_none(pte));
                        if (unmap_ref_private(mm, vma, old_page, address)) {
                                BUG_ON(huge_pte_none(pte));
-                               spin_lock(&mm->page_table_lock);
+                               spin_lock(ptl);
                                ptep = huge_pte_offset(mm, address & huge_page_mask(h));
                                if (likely(pte_same(huge_ptep_get(ptep), pte)))
                                        goto retry_avoidcopy;
                                /*
-                                * race occurs while re-acquiring page_table_lock, and
-                                * our job is done.
+                                * race occurs while re-acquiring page table
+                                * lock, and our job is done.
                                 */
                                return 0;
                        }
@@ -2680,7 +2671,7 @@ retry_avoidcopy:
                }
 
                /* Caller expects lock to be held */
-               spin_lock(&mm->page_table_lock);
+               spin_lock(ptl);
                if (err == -ENOMEM)
                        return VM_FAULT_OOM;
                else
@@ -2695,7 +2686,7 @@ retry_avoidcopy:
                page_cache_release(new_page);
                page_cache_release(old_page);
                /* Caller expects lock to be held */
-               spin_lock(&mm->page_table_lock);
+               spin_lock(ptl);
                return VM_FAULT_OOM;
        }
 
@@ -2707,10 +2698,10 @@ retry_avoidcopy:
        mmun_end = mmun_start + huge_page_size(h);
        mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
        /*
-        * Retake the page_table_lock to check for racing updates
+        * Retake the page table lock to check for racing updates
         * before the page tables are altered
         */
-       spin_lock(&mm->page_table_lock);
+       spin_lock(ptl);
        ptep = huge_pte_offset(mm, address & huge_page_mask(h));
        if (likely(pte_same(huge_ptep_get(ptep), pte))) {
                ClearPagePrivate(new_page);
@@ -2724,13 +2715,13 @@ retry_avoidcopy:
                /* Make the old page be freed below */
                new_page = old_page;
        }
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
        mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
        page_cache_release(new_page);
        page_cache_release(old_page);
 
        /* Caller expects lock to be held */
-       spin_lock(&mm->page_table_lock);
+       spin_lock(ptl);
        return 0;
 }
 
@@ -2778,6 +2769,7 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma,
        struct page *page;
        struct address_space *mapping;
        pte_t new_pte;
+       spinlock_t *ptl;
 
        /*
         * Currently, we are forced to kill the process in the event the
@@ -2864,7 +2856,8 @@ retry:
                        goto backout_unlocked;
                }
 
-       spin_lock(&mm->page_table_lock);
+       ptl = huge_pte_lockptr(h, mm, ptep);
+       spin_lock(ptl);
        size = i_size_read(mapping->host) >> huge_page_shift(h);
        if (idx >= size)
                goto backout;
@@ -2885,16 +2878,16 @@ retry:
 
        if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) {
                /* Optimization, do the COW without a second fault */
-               ret = hugetlb_cow(mm, vma, address, ptep, new_pte, page);
+               ret = hugetlb_cow(mm, vma, address, ptep, new_pte, page, ptl);
        }
 
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
        unlock_page(page);
 out:
        return ret;
 
 backout:
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
 backout_unlocked:
        unlock_page(page);
        put_page(page);
@@ -2906,6 +2899,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
 {
        pte_t *ptep;
        pte_t entry;
+       spinlock_t *ptl;
        int ret;
        struct page *page = NULL;
        struct page *pagecache_page = NULL;
@@ -2918,7 +2912,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
        if (ptep) {
                entry = huge_ptep_get(ptep);
                if (unlikely(is_hugetlb_entry_migration(entry))) {
-                       migration_entry_wait_huge(mm, ptep);
+                       migration_entry_wait_huge(vma, mm, ptep);
                        return 0;
                } else if (unlikely(is_hugetlb_entry_hwpoisoned(entry)))
                        return VM_FAULT_HWPOISON_LARGE |
@@ -2974,17 +2968,18 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
        if (page != pagecache_page)
                lock_page(page);
 
-       spin_lock(&mm->page_table_lock);
+       ptl = huge_pte_lockptr(h, mm, ptep);
+       spin_lock(ptl);
        /* Check for a racing update before calling hugetlb_cow */
        if (unlikely(!pte_same(entry, huge_ptep_get(ptep))))
-               goto out_page_table_lock;
+               goto out_ptl;
 
 
        if (flags & FAULT_FLAG_WRITE) {
                if (!huge_pte_write(entry)) {
                        ret = hugetlb_cow(mm, vma, address, ptep, entry,
-                                                       pagecache_page);
-                       goto out_page_table_lock;
+                                       pagecache_page, ptl);
+                       goto out_ptl;
                }
                entry = huge_pte_mkdirty(entry);
        }
@@ -2993,8 +2988,8 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                                                flags & FAULT_FLAG_WRITE))
                update_mmu_cache(vma, address, ptep);
 
-out_page_table_lock:
-       spin_unlock(&mm->page_table_lock);
+out_ptl:
+       spin_unlock(ptl);
 
        if (pagecache_page) {
                unlock_page(pagecache_page);
@@ -3020,9 +3015,9 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
        unsigned long remainder = *nr_pages;
        struct hstate *h = hstate_vma(vma);
 
-       spin_lock(&mm->page_table_lock);
        while (vaddr < vma->vm_end && remainder) {
                pte_t *pte;
+               spinlock_t *ptl = NULL;
                int absent;
                struct page *page;
 
@@ -3030,8 +3025,12 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
                 * Some archs (sparc64, sh*) have multiple pte_ts to
                 * each hugepage.  We have to make sure we get the
                 * first, for the page indexing below to work.
+                *
+                * Note that page table lock is not held when pte is null.
                 */
                pte = huge_pte_offset(mm, vaddr & huge_page_mask(h));
+               if (pte)
+                       ptl = huge_pte_lock(h, mm, pte);
                absent = !pte || huge_pte_none(huge_ptep_get(pte));
 
                /*
@@ -3043,6 +3042,8 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
                 */
                if (absent && (flags & FOLL_DUMP) &&
                    !hugetlbfs_pagecache_present(h, vma, vaddr)) {
+                       if (pte)
+                               spin_unlock(ptl);
                        remainder = 0;
                        break;
                }
@@ -3062,10 +3063,10 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
                      !huge_pte_write(huge_ptep_get(pte)))) {
                        int ret;
 
-                       spin_unlock(&mm->page_table_lock);
+                       if (pte)
+                               spin_unlock(ptl);
                        ret = hugetlb_fault(mm, vma, vaddr,
                                (flags & FOLL_WRITE) ? FAULT_FLAG_WRITE : 0);
-                       spin_lock(&mm->page_table_lock);
                        if (!(ret & VM_FAULT_ERROR))
                                continue;
 
@@ -3096,8 +3097,8 @@ same_page:
                         */
                        goto same_page;
                }
+               spin_unlock(ptl);
        }
-       spin_unlock(&mm->page_table_lock);
        *nr_pages = remainder;
        *position = vaddr;
 
@@ -3118,13 +3119,15 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
        flush_cache_range(vma, address, end);
 
        mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex);
-       spin_lock(&mm->page_table_lock);
        for (; address < end; address += huge_page_size(h)) {
+               spinlock_t *ptl;
                ptep = huge_pte_offset(mm, address);
                if (!ptep)
                        continue;
+               ptl = huge_pte_lock(h, mm, ptep);
                if (huge_pmd_unshare(mm, &address, ptep)) {
                        pages++;
+                       spin_unlock(ptl);
                        continue;
                }
                if (!huge_pte_none(huge_ptep_get(ptep))) {
@@ -3134,8 +3137,8 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
                        set_huge_pte_at(mm, address, ptep, pte);
                        pages++;
                }
+               spin_unlock(ptl);
        }
-       spin_unlock(&mm->page_table_lock);
        /*
         * 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:
@@ -3298,6 +3301,7 @@ pte_t *huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
        unsigned long saddr;
        pte_t *spte = NULL;
        pte_t *pte;
+       spinlock_t *ptl;
 
        if (!vma_shareable(vma, addr))
                return (pte_t *)pmd_alloc(mm, pud, addr);
@@ -3320,13 +3324,14 @@ pte_t *huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
        if (!spte)
                goto out;
 
-       spin_lock(&mm->page_table_lock);
+       ptl = huge_pte_lockptr(hstate_vma(vma), mm, spte);
+       spin_lock(ptl);
        if (pud_none(*pud))
                pud_populate(mm, pud,
                                (pmd_t *)((unsigned long)spte & PAGE_MASK));
        else
                put_page(virt_to_page(spte));
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
 out:
        pte = (pte_t *)pmd_alloc(mm, pud, addr);
        mutex_unlock(&mapping->i_mmap_mutex);
@@ -3340,7 +3345,7 @@ out:
  * indicated by page_count > 1, unmap is achieved by clearing pud and
  * decrementing the ref count. If count == 1, the pte page is not shared.
  *
- * called with vma->vm_mm->page_table_lock held.
+ * called with page table lock held.
  *
  * returns: 1 successfully unmapped a shared pte page
  *         0 the underlying pte page is not shared, or it is the last user
index e3cd40b..f1a0ae6 100644 (file)
@@ -6605,10 +6605,10 @@ static int mem_cgroup_count_precharge_pte_range(pmd_t *pmd,
        pte_t *pte;
        spinlock_t *ptl;
 
-       if (pmd_trans_huge_lock(pmd, vma) == 1) {
+       if (pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
                if (get_mctgt_type_thp(vma, addr, *pmd, NULL) == MC_TARGET_PAGE)
                        mc.precharge += HPAGE_PMD_NR;
-               spin_unlock(&vma->vm_mm->page_table_lock);
+               spin_unlock(ptl);
                return 0;
        }
 
@@ -6797,9 +6797,9 @@ static int mem_cgroup_move_charge_pte_range(pmd_t *pmd,
         *    to be unlocked in __split_huge_page_splitting(), where the main
         *    part of thp split is not executed yet.
         */
-       if (pmd_trans_huge_lock(pmd, vma) == 1) {
+       if (pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
                if (mc.precharge < HPAGE_PMD_NR) {
-                       spin_unlock(&vma->vm_mm->page_table_lock);
+                       spin_unlock(ptl);
                        return 0;
                }
                target_type = get_mctgt_type_thp(vma, addr, *pmd, &target);
@@ -6816,7 +6816,7 @@ static int mem_cgroup_move_charge_pte_range(pmd_t *pmd,
                        }
                        put_page(page);
                }
-               spin_unlock(&vma->vm_mm->page_table_lock);
+               spin_unlock(ptl);
                return 0;
        }
 
index f9d78ec..b7c1716 100644 (file)
@@ -1269,7 +1269,7 @@ void memory_failure_queue(unsigned long pfn, int trapno, int flags)
 
        mf_cpu = &get_cpu_var(memory_failure_cpu);
        spin_lock_irqsave(&mf_cpu->lock, proc_flags);
-       if (kfifo_put(&mf_cpu->fifo, &entry))
+       if (kfifo_put(&mf_cpu->fifo, entry))
                schedule_work_on(smp_processor_id(), &mf_cpu->work);
        else
                pr_err("Memory failure: buffer overflow when queuing memory failure at %#lx\n",
index bf86658..5d9025f 100644 (file)
@@ -382,7 +382,7 @@ static void free_pte_range(struct mmu_gather *tlb, pmd_t *pmd,
        pgtable_t token = pmd_pgtable(*pmd);
        pmd_clear(pmd);
        pte_free_tlb(tlb, token, addr);
-       tlb->mm->nr_ptes--;
+       atomic_long_dec(&tlb->mm->nr_ptes);
 }
 
 static inline void free_pmd_range(struct mmu_gather *tlb, pud_t *pud,
@@ -550,6 +550,7 @@ void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *vma,
 int __pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
                pmd_t *pmd, unsigned long address)
 {
+       spinlock_t *ptl;
        pgtable_t new = pte_alloc_one(mm, address);
        int wait_split_huge_page;
        if (!new)
@@ -570,15 +571,15 @@ int __pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
         */
        smp_wmb(); /* Could be smp_wmb__xxx(before|after)_spin_lock */
 
-       spin_lock(&mm->page_table_lock);
+       ptl = pmd_lock(mm, pmd);
        wait_split_huge_page = 0;
        if (likely(pmd_none(*pmd))) {   /* Has another populated it ? */
-               mm->nr_ptes++;
+               atomic_long_inc(&mm->nr_ptes);
                pmd_populate(mm, pmd, new);
                new = NULL;
        } else if (unlikely(pmd_trans_splitting(*pmd)))
                wait_split_huge_page = 1;
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
        if (new)
                pte_free(mm, new);
        if (wait_split_huge_page)
@@ -1516,20 +1517,20 @@ struct page *follow_page_mask(struct vm_area_struct *vma,
                        split_huge_page_pmd(vma, address, pmd);
                        goto split_fallthrough;
                }
-               spin_lock(&mm->page_table_lock);
+               ptl = pmd_lock(mm, pmd);
                if (likely(pmd_trans_huge(*pmd))) {
                        if (unlikely(pmd_trans_splitting(*pmd))) {
-                               spin_unlock(&mm->page_table_lock);
+                               spin_unlock(ptl);
                                wait_split_huge_page(vma->anon_vma, pmd);
                        } else {
                                page = follow_trans_huge_pmd(vma, address,
                                                             pmd, flags);
-                               spin_unlock(&mm->page_table_lock);
+                               spin_unlock(ptl);
                                *page_mask = HPAGE_PMD_NR - 1;
                                goto out;
                        }
                } else
-                       spin_unlock(&mm->page_table_lock);
+                       spin_unlock(ptl);
                /* fall through */
        }
 split_fallthrough:
@@ -4269,3 +4270,21 @@ void copy_user_huge_page(struct page *dst, struct page *src,
        }
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_HUGETLBFS */
+
+#if USE_SPLIT_PTE_PTLOCKS && BLOATED_SPINLOCKS
+bool ptlock_alloc(struct page *page)
+{
+       spinlock_t *ptl;
+
+       ptl = kmalloc(sizeof(spinlock_t), GFP_KERNEL);
+       if (!ptl)
+               return false;
+       page->ptl = ptl;
+       return true;
+}
+
+void ptlock_free(struct page *page)
+{
+       kfree(page->ptl);
+}
+#endif
index 4cc19f6..eca4a31 100644 (file)
@@ -525,8 +525,9 @@ static void queue_pages_hugetlb_pmd_range(struct vm_area_struct *vma,
 #ifdef CONFIG_HUGETLB_PAGE
        int nid;
        struct page *page;
+       spinlock_t *ptl;
 
-       spin_lock(&vma->vm_mm->page_table_lock);
+       ptl = huge_pte_lock(hstate_vma(vma), vma->vm_mm, (pte_t *)pmd);
        page = pte_page(huge_ptep_get((pte_t *)pmd));
        nid = page_to_nid(page);
        if (node_isset(nid, *nodes) == !!(flags & MPOL_MF_INVERT))
@@ -536,7 +537,7 @@ static void queue_pages_hugetlb_pmd_range(struct vm_area_struct *vma,
            (flags & MPOL_MF_MOVE && page_mapcount(page) == 1))
                isolate_huge_page(page, private);
 unlock:
-       spin_unlock(&vma->vm_mm->page_table_lock);
+       spin_unlock(ptl);
 #else
        BUG();
 #endif
@@ -2949,7 +2950,7 @@ void mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol)
                return;
        }
 
-       p += snprintf(p, maxlen, policy_modes[mode]);
+       p += snprintf(p, maxlen, "%s", policy_modes[mode]);
 
        if (flags & MPOL_MODE_FLAGS) {
                p += snprintf(p, buffer + maxlen - p, "=");
index dfc8300..bb94004 100644 (file)
@@ -130,7 +130,7 @@ static int remove_migration_pte(struct page *new, struct vm_area_struct *vma,
                ptep = huge_pte_offset(mm, addr);
                if (!ptep)
                        goto out;
-               ptl = &mm->page_table_lock;
+               ptl = huge_pte_lockptr(hstate_vma(vma), mm, ptep);
        } else {
                pmd = mm_find_pmd(mm, addr);
                if (!pmd)
@@ -249,9 +249,10 @@ void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd,
        __migration_entry_wait(mm, ptep, ptl);
 }
 
-void migration_entry_wait_huge(struct mm_struct *mm, pte_t *pte)
+void migration_entry_wait_huge(struct vm_area_struct *vma,
+               struct mm_struct *mm, pte_t *pte)
 {
-       spinlock_t *ptl = &(mm)->page_table_lock;
+       spinlock_t *ptl = huge_pte_lockptr(hstate_vma(vma), mm, pte);
        __migration_entry_wait(mm, pte, ptl);
 }
 
@@ -441,6 +442,54 @@ int migrate_huge_page_move_mapping(struct address_space *mapping,
 }
 
 /*
+ * Gigantic pages are so large that we do not guarantee that page++ pointer
+ * arithmetic will work across the entire page.  We need something more
+ * specialized.
+ */
+static void __copy_gigantic_page(struct page *dst, struct page *src,
+                               int nr_pages)
+{
+       int i;
+       struct page *dst_base = dst;
+       struct page *src_base = src;
+
+       for (i = 0; i < nr_pages; ) {
+               cond_resched();
+               copy_highpage(dst, src);
+
+               i++;
+               dst = mem_map_next(dst, dst_base, i);
+               src = mem_map_next(src, src_base, i);
+       }
+}
+
+static void copy_huge_page(struct page *dst, struct page *src)
+{
+       int i;
+       int nr_pages;
+
+       if (PageHuge(src)) {
+               /* hugetlbfs page */
+               struct hstate *h = page_hstate(src);
+               nr_pages = pages_per_huge_page(h);
+
+               if (unlikely(nr_pages > MAX_ORDER_NR_PAGES)) {
+                       __copy_gigantic_page(dst, src, nr_pages);
+                       return;
+               }
+       } else {
+               /* thp page */
+               BUG_ON(!PageTransHuge(src));
+               nr_pages = hpage_nr_pages(src);
+       }
+
+       for (i = 0; i < nr_pages; i++) {
+               cond_resched();
+               copy_highpage(dst + i, src + i);
+       }
+}
+
+/*
  * Copy the page to its new location
  */
 void migrate_page_copy(struct page *newpage, struct page *page)
@@ -1666,6 +1715,7 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
                                unsigned long address,
                                struct page *page, int node)
 {
+       spinlock_t *ptl;
        unsigned long haddr = address & HPAGE_PMD_MASK;
        pg_data_t *pgdat = NODE_DATA(node);
        int isolated = 0;
@@ -1705,9 +1755,9 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
        WARN_ON(PageLRU(new_page));
 
        /* Recheck the target PMD */
-       spin_lock(&mm->page_table_lock);
+       ptl = pmd_lock(mm, pmd);
        if (unlikely(!pmd_same(*pmd, entry))) {
-               spin_unlock(&mm->page_table_lock);
+               spin_unlock(ptl);
 
                /* Reverse changes made by migrate_page_copy() */
                if (TestClearPageActive(new_page))
@@ -1752,7 +1802,7 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
         * before it's fully transferred to the new page.
         */
        mem_cgroup_end_migration(memcg, page, new_page, true);
-       spin_unlock(&mm->page_table_lock);
+       spin_unlock(ptl);
 
        unlock_page(new_page);
        unlock_page(page);
index 5a6badd..834b2d7 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2724,7 +2724,8 @@ void exit_mmap(struct mm_struct *mm)
        }
        vm_unacct_memory(nr_accounted);
 
-       WARN_ON(mm->nr_ptes > (FIRST_USER_ADDRESS+PMD_SIZE-1)>>PMD_SHIFT);
+       WARN_ON(atomic_long_read(&mm->nr_ptes) >
+                       (FIRST_USER_ADDRESS+PMD_SIZE-1)>>PMD_SHIFT);
 }
 
 /* Insert vm structure into process list sorted by address
index 6738c47..1e4a600 100644 (file)
@@ -161,7 +161,7 @@ unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
         * The baseline for the badness score is the proportion of RAM that each
         * task's rss, pagetable and swap space use.
         */
-       points = get_mm_rss(p->mm) + p->mm->nr_ptes +
+       points = get_mm_rss(p->mm) + atomic_long_read(&p->mm->nr_ptes) +
                 get_mm_counter(p->mm, MM_SWAPENTS);
        task_unlock(p);
 
@@ -364,10 +364,10 @@ static void dump_tasks(const struct mem_cgroup *memcg, const nodemask_t *nodemas
                        continue;
                }
 
-               pr_info("[%5d] %5d %5d %8lu %8lu %7lu %8lu         %5hd %s\n",
+               pr_info("[%5d] %5d %5d %8lu %8lu %7ld %8lu         %5hd %s\n",
                        task->pid, from_kuid(&init_user_ns, task_uid(task)),
                        task->tgid, task->mm->total_vm, get_mm_rss(task->mm),
-                       task->mm->nr_ptes,
+                       atomic_long_read(&task->mm->nr_ptes),
                        get_mm_counter(task->mm, MM_SWAPENTS),
                        task->signal->oom_score_adj, task->comm);
                task_unlock(task);
index 3929a40..cbb3854 100644 (file)
@@ -151,14 +151,14 @@ void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address,
 void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
                                pgtable_t pgtable)
 {
-       assert_spin_locked(&mm->page_table_lock);
+       assert_spin_locked(pmd_lockptr(mm, pmdp));
 
        /* FIFO */
-       if (!mm->pmd_huge_pte)
+       if (!pmd_huge_pte(mm, pmdp))
                INIT_LIST_HEAD(&pgtable->lru);
        else
-               list_add(&pgtable->lru, &mm->pmd_huge_pte->lru);
-       mm->pmd_huge_pte = pgtable;
+               list_add(&pgtable->lru, &pmd_huge_pte(mm, pmdp)->lru);
+       pmd_huge_pte(mm, pmdp) = pgtable;
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #endif
@@ -170,14 +170,14 @@ pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
 {
        pgtable_t pgtable;
 
-       assert_spin_locked(&mm->page_table_lock);
+       assert_spin_locked(pmd_lockptr(mm, pmdp));
 
        /* FIFO */
-       pgtable = mm->pmd_huge_pte;
+       pgtable = pmd_huge_pte(mm, pmdp);
        if (list_empty(&pgtable->lru))
-               mm->pmd_huge_pte = NULL;
+               pmd_huge_pte(mm, pmdp) = NULL;
        else {
-               mm->pmd_huge_pte = list_entry(pgtable->lru.next,
+               pmd_huge_pte(mm, pmdp) = list_entry(pgtable->lru.next,
                                              struct page, lru);
                list_del(&pgtable->lru);
        }
index fd3ee7a..55c8b8d 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -601,7 +601,7 @@ pte_t *__page_check_address(struct page *page, struct mm_struct *mm,
 
        if (unlikely(PageHuge(page))) {
                pte = huge_pte_offset(mm, address);
-               ptl = &mm->page_table_lock;
+               ptl = huge_pte_lockptr(page_hstate(page), mm, pte);
                goto check;
        }
 
@@ -665,25 +665,23 @@ int page_referenced_one(struct page *page, struct vm_area_struct *vma,
                        unsigned long *vm_flags)
 {
        struct mm_struct *mm = vma->vm_mm;
+       spinlock_t *ptl;
        int referenced = 0;
 
        if (unlikely(PageTransHuge(page))) {
                pmd_t *pmd;
 
-               spin_lock(&mm->page_table_lock);
                /*
                 * rmap might return false positives; we must filter
                 * these out using page_check_address_pmd().
                 */
                pmd = page_check_address_pmd(page, mm, address,
-                                            PAGE_CHECK_ADDRESS_PMD_FLAG);
-               if (!pmd) {
-                       spin_unlock(&mm->page_table_lock);
+                                            PAGE_CHECK_ADDRESS_PMD_FLAG, &ptl);
+               if (!pmd)
                        goto out;
-               }
 
                if (vma->vm_flags & VM_LOCKED) {
-                       spin_unlock(&mm->page_table_lock);
+                       spin_unlock(ptl);
                        *mapcount = 0;  /* break early from loop */
                        *vm_flags |= VM_LOCKED;
                        goto out;
@@ -692,10 +690,9 @@ int page_referenced_one(struct page *page, struct vm_area_struct *vma,
                /* go ahead even if the pmd is pmd_trans_splitting() */
                if (pmdp_clear_flush_young_notify(vma, address, pmd))
                        referenced++;
-               spin_unlock(&mm->page_table_lock);
+               spin_unlock(ptl);
        } else {
                pte_t *pte;
-               spinlock_t *ptl;
 
                /*
                 * rmap might return false positives; we must filter
index 0c8967b..eb043bf 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
 static bool pfmemalloc_active __read_mostly;
 
 /*
- * kmem_bufctl_t:
- *
- * Bufctl's are used for linking objs within a slab
- * linked offsets.
- *
- * This implementation relies on "struct page" for locating the cache &
- * slab an object belongs to.
- * This allows the bufctl structure to be small (one int), but limits
- * the number of objects a slab (not a cache) can contain when off-slab
- * bufctls are used. The limit is the size of the largest general cache
- * that does not use off-slab slabs.
- * For 32bit archs with 4 kB pages, is this 56.
- * This is not serious, as it is only for large objects, when it is unwise
- * to have too many per slab.
- * Note: This limit can be raised by introducing a general cache whose size
- * is less than 512 (PAGE_SIZE<<3), but greater than 256.
- */
-
-typedef unsigned int kmem_bufctl_t;
-#define BUFCTL_END     (((kmem_bufctl_t)(~0U))-0)
-#define BUFCTL_FREE    (((kmem_bufctl_t)(~0U))-1)
-#define        BUFCTL_ACTIVE   (((kmem_bufctl_t)(~0U))-2)
-#define        SLAB_LIMIT      (((kmem_bufctl_t)(~0U))-3)
-
-/*
- * struct slab_rcu
- *
- * slab_destroy on a SLAB_DESTROY_BY_RCU cache uses this structure to
- * arrange for kmem_freepages to be called via RCU.  This is useful if
- * we need to approach a kernel structure obliquely, from its address
- * obtained without the usual locking.  We can lock the structure to
- * stabilize it and check it's still at the given address, only if we
- * can be sure that the memory has not been meanwhile reused for some
- * other kind of object (which our subsystem's lock might corrupt).
- *
- * rcu_read_lock before reading the address, then rcu_read_unlock after
- * taking the spinlock within the structure expected at that address.
- */
-struct slab_rcu {
-       struct rcu_head head;
-       struct kmem_cache *cachep;
-       void *addr;
-};
-
-/*
- * struct slab
- *
- * Manages the objs in a slab. Placed either at the beginning of mem allocated
- * for a slab, or allocated from an general cache.
- * Slabs are chained into three list: fully used, partial, fully free slabs.
- */
-struct slab {
-       union {
-               struct {
-                       struct list_head list;
-                       unsigned long colouroff;
-                       void *s_mem;            /* including colour offset */
-                       unsigned int inuse;     /* num of objs active in slab */
-                       kmem_bufctl_t free;
-                       unsigned short nodeid;
-               };
-               struct slab_rcu __slab_cover_slab_rcu;
-       };
-};
-
-/*
  * struct array_cache
  *
  * Purpose:
@@ -456,18 +390,10 @@ static inline struct kmem_cache *virt_to_cache(const void *obj)
        return page->slab_cache;
 }
 
-static inline struct slab *virt_to_slab(const void *obj)
-{
-       struct page *page = virt_to_head_page(obj);
-
-       VM_BUG_ON(!PageSlab(page));
-       return page->slab_page;
-}
-
-static inline void *index_to_obj(struct kmem_cache *cache, struct slab *slab,
+static inline void *index_to_obj(struct kmem_cache *cache, struct page *page,
                                 unsigned int idx)
 {
-       return slab->s_mem + cache->size * idx;
+       return page->s_mem + cache->size * idx;
 }
 
 /*
@@ -477,9 +403,9 @@ static inline void *index_to_obj(struct kmem_cache *cache, struct slab *slab,
  *   reciprocal_divide(offset, cache->reciprocal_buffer_size)
  */
 static inline unsigned int obj_to_index(const struct kmem_cache *cache,
-                                       const struct slab *slab, void *obj)
+                                       const struct page *page, void *obj)
 {
-       u32 offset = (obj - slab->s_mem);
+       u32 offset = (obj - page->s_mem);
        return reciprocal_divide(offset, cache->reciprocal_buffer_size);
 }
 
@@ -641,7 +567,7 @@ static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep)
 
 static size_t slab_mgmt_size(size_t nr_objs, size_t align)
 {
-       return ALIGN(sizeof(struct slab)+nr_objs*sizeof(kmem_bufctl_t), align);
+       return ALIGN(nr_objs * sizeof(unsigned int), align);
 }
 
 /*
@@ -660,8 +586,7 @@ static void cache_estimate(unsigned long gfporder, size_t buffer_size,
         * on it. For the latter case, the memory allocated for a
         * slab is used for:
         *
-        * - The struct slab
-        * - One kmem_bufctl_t for each object
+        * - One unsigned int for each object
         * - Padding to respect alignment of @align
         * - @buffer_size bytes for each object
         *
@@ -674,8 +599,6 @@ static void cache_estimate(unsigned long gfporder, size_t buffer_size,
                mgmt_size = 0;
                nr_objs = slab_size / buffer_size;
 
-               if (nr_objs > SLAB_LIMIT)
-                       nr_objs = SLAB_LIMIT;
        } else {
                /*
                 * Ignore padding for the initial guess. The padding
@@ -685,8 +608,7 @@ static void cache_estimate(unsigned long gfporder, size_t buffer_size,
                 * into the memory allocation when taking the padding
                 * into account.
                 */
-               nr_objs = (slab_size - sizeof(struct slab)) /
-                         (buffer_size + sizeof(kmem_bufctl_t));
+               nr_objs = (slab_size) / (buffer_size + sizeof(unsigned int));
 
                /*
                 * This calculated number will be either the right
@@ -696,9 +618,6 @@ static void cache_estimate(unsigned long gfporder, size_t buffer_size,
                       > slab_size)
                        nr_objs--;
 
-               if (nr_objs > SLAB_LIMIT)
-                       nr_objs = SLAB_LIMIT;
-
                mgmt_size = slab_mgmt_size(nr_objs, align);
        }
        *num = nr_objs;
@@ -829,10 +748,8 @@ static struct array_cache *alloc_arraycache(int node, int entries,
        return nc;
 }
 
-static inline bool is_slab_pfmemalloc(struct slab *slabp)
+static inline bool is_slab_pfmemalloc(struct page *page)
 {
-       struct page *page = virt_to_page(slabp->s_mem);
-
        return PageSlabPfmemalloc(page);
 }
 
@@ -841,23 +758,23 @@ static void recheck_pfmemalloc_active(struct kmem_cache *cachep,
                                                struct array_cache *ac)
 {
        struct kmem_cache_node *n = cachep->node[numa_mem_id()];
-       struct slab *slabp;
+       struct page *page;
        unsigned long flags;
 
        if (!pfmemalloc_active)
                return;
 
        spin_lock_irqsave(&n->list_lock, flags);
-       list_for_each_entry(slabp, &n->slabs_full, list)
-               if (is_slab_pfmemalloc(slabp))
+       list_for_each_entry(page, &n->slabs_full, lru)
+               if (is_slab_pfmemalloc(page))
                        goto out;
 
-       list_for_each_entry(slabp, &n->slabs_partial, list)
-               if (is_slab_pfmemalloc(slabp))
+       list_for_each_entry(page, &n->slabs_partial, lru)
+               if (is_slab_pfmemalloc(page))
                        goto out;
 
-       list_for_each_entry(slabp, &n->slabs_free, list)
-               if (is_slab_pfmemalloc(slabp))
+       list_for_each_entry(page, &n->slabs_free, lru)
+               if (is_slab_pfmemalloc(page))
                        goto out;
 
        pfmemalloc_active = false;
@@ -897,8 +814,8 @@ static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac,
                 */
                n = cachep->node[numa_mem_id()];
                if (!list_empty(&n->slabs_free) && force_refill) {
-                       struct slab *slabp = virt_to_slab(objp);
-                       ClearPageSlabPfmemalloc(virt_to_head_page(slabp->s_mem));
+                       struct page *page = virt_to_head_page(objp);
+                       ClearPageSlabPfmemalloc(page);
                        clear_obj_pfmemalloc(&objp);
                        recheck_pfmemalloc_active(cachep, ac);
                        return objp;
@@ -1099,8 +1016,7 @@ static void drain_alien_cache(struct kmem_cache *cachep,
 
 static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
 {
-       struct slab *slabp = virt_to_slab(objp);
-       int nodeid = slabp->nodeid;
+       int nodeid = page_to_nid(virt_to_page(objp));
        struct kmem_cache_node *n;
        struct array_cache *alien = NULL;
        int node;
@@ -1111,7 +1027,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
         * Make sure we are not freeing a object from another node to the array
         * cache on this cpu.
         */
-       if (likely(slabp->nodeid == node))
+       if (likely(nodeid == node))
                return 0;
 
        n = cachep->node[node];
@@ -1512,6 +1428,8 @@ void __init kmem_cache_init(void)
 {
        int i;
 
+       BUILD_BUG_ON(sizeof(((struct page *)NULL)->lru) <
+                                       sizeof(struct rcu_head));
        kmem_cache = &kmem_cache_boot;
        setup_node_pointer(kmem_cache);
 
@@ -1687,7 +1605,7 @@ static noinline void
 slab_out_of_memory(struct kmem_cache *cachep, gfp_t gfpflags, int nodeid)
 {
        struct kmem_cache_node *n;
-       struct slab *slabp;
+       struct page *page;
        unsigned long flags;
        int node;
 
@@ -1706,15 +1624,15 @@ slab_out_of_memory(struct kmem_cache *cachep, gfp_t gfpflags, int nodeid)
                        continue;
 
                spin_lock_irqsave(&n->list_lock, flags);
-               list_for_each_entry(slabp, &n->slabs_full, list) {
+               list_for_each_entry(page, &n->slabs_full, lru) {
                        active_objs += cachep->num;
                        active_slabs++;
                }
-               list_for_each_entry(slabp, &n->slabs_partial, list) {
-                       active_objs += slabp->inuse;
+               list_for_each_entry(page, &n->slabs_partial, lru) {
+                       active_objs += page->active;
                        active_slabs++;
                }
-               list_for_each_entry(slabp, &n->slabs_free, list)
+               list_for_each_entry(page, &n->slabs_free, lru)
                        num_slabs++;
 
                free_objects += n->free_objects;
@@ -1736,19 +1654,11 @@ slab_out_of_memory(struct kmem_cache *cachep, gfp_t gfpflags, int nodeid)
  * did not request dmaable memory, we might get it, but that
  * would be relatively rare and ignorable.
  */
-static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
+static struct page *kmem_getpages(struct kmem_cache *cachep, gfp_t flags,
+                                                               int nodeid)
 {
        struct page *page;
        int nr_pages;
-       int i;
-
-#ifndef CONFIG_MMU
-       /*
-        * Nommu uses slab's for process anonymous memory allocations, and thus
-        * requires __GFP_COMP to properly refcount higher order allocations
-        */
-       flags |= __GFP_COMP;
-#endif
 
        flags |= cachep->allocflags;
        if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
@@ -1772,12 +1682,9 @@ 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++) {
-               __SetPageSlab(page + i);
-
-               if (page->pfmemalloc)
-                       SetPageSlabPfmemalloc(page + i);
-       }
+       __SetPageSlab(page);
+       if (page->pfmemalloc)
+               SetPageSlabPfmemalloc(page);
        memcg_bind_pages(cachep, cachep->gfporder);
 
        if (kmemcheck_enabled && !(cachep->flags & SLAB_NOTRACK)) {
@@ -1789,17 +1696,15 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
                        kmemcheck_mark_unallocated_pages(page, nr_pages);
        }
 
-       return page_address(page);
+       return page;
 }
 
 /*
  * Interface to system's page release.
  */
-static void kmem_freepages(struct kmem_cache *cachep, void *addr)
+static void kmem_freepages(struct kmem_cache *cachep, struct page *page)
 {
-       unsigned long i = (1 << cachep->gfporder);
-       struct page *page = virt_to_page(addr);
-       const unsigned long nr_freed = i;
+       const unsigned long nr_freed = (1 << cachep->gfporder);
 
        kmemcheck_free_shadow(page, cachep->gfporder);
 
@@ -1809,27 +1714,28 @@ static void kmem_freepages(struct kmem_cache *cachep, void *addr)
        else
                sub_zone_page_state(page_zone(page),
                                NR_SLAB_UNRECLAIMABLE, nr_freed);
-       while (i--) {
-               BUG_ON(!PageSlab(page));
-               __ClearPageSlabPfmemalloc(page);
-               __ClearPageSlab(page);
-               page++;
-       }
+
+       BUG_ON(!PageSlab(page));
+       __ClearPageSlabPfmemalloc(page);
+       __ClearPageSlab(page);
+       page_mapcount_reset(page);
+       page->mapping = NULL;
 
        memcg_release_pages(cachep, cachep->gfporder);
        if (current->reclaim_state)
                current->reclaim_state->reclaimed_slab += nr_freed;
-       free_memcg_kmem_pages((unsigned long)addr, cachep->gfporder);
+       __free_memcg_kmem_pages(page, cachep->gfporder);
 }
 
 static void kmem_rcu_free(struct rcu_head *head)
 {
-       struct slab_rcu *slab_rcu = (struct slab_rcu *)head;
-       struct kmem_cache *cachep = slab_rcu->cachep;
+       struct kmem_cache *cachep;
+       struct page *page;
 
-       kmem_freepages(cachep, slab_rcu->addr);
-       if (OFF_SLAB(cachep))
-               kmem_cache_free(cachep->slabp_cache, slab_rcu);
+       page = container_of(head, struct page, rcu_head);
+       cachep = page->slab_cache;
+
+       kmem_freepages(cachep, page);
 }
 
 #if DEBUG
@@ -1978,19 +1884,19 @@ static void check_poison_obj(struct kmem_cache *cachep, void *objp)
                /* Print some data about the neighboring objects, if they
                 * exist:
                 */
-               struct slab *slabp = virt_to_slab(objp);
+               struct page *page = virt_to_head_page(objp);
                unsigned int objnr;
 
-               objnr = obj_to_index(cachep, slabp, objp);
+               objnr = obj_to_index(cachep, page, objp);
                if (objnr) {
-                       objp = index_to_obj(cachep, slabp, objnr - 1);
+                       objp = index_to_obj(cachep, page, objnr - 1);
                        realobj = (char *)objp + obj_offset(cachep);
                        printk(KERN_ERR "Prev obj: start=%p, len=%d\n",
                               realobj, size);
                        print_objinfo(cachep, objp, 2);
                }
                if (objnr + 1 < cachep->num) {
-                       objp = index_to_obj(cachep, slabp, objnr + 1);
+                       objp = index_to_obj(cachep, page, objnr + 1);
                        realobj = (char *)objp + obj_offset(cachep);
                        printk(KERN_ERR "Next obj: start=%p, len=%d\n",
                               realobj, size);
@@ -2001,11 +1907,12 @@ static void check_poison_obj(struct kmem_cache *cachep, void *objp)
 #endif
 
 #if DEBUG
-static void slab_destroy_debugcheck(struct kmem_cache *cachep, struct slab *slabp)
+static void slab_destroy_debugcheck(struct kmem_cache *cachep,
+                                               struct page *page)
 {
        int i;
        for (i = 0; i < cachep->num; i++) {
-               void *objp = index_to_obj(cachep, slabp, i);
+               void *objp = index_to_obj(cachep, page, i);
 
                if (cachep->flags & SLAB_POISON) {
 #ifdef CONFIG_DEBUG_PAGEALLOC
@@ -2030,7 +1937,8 @@ static void slab_destroy_debugcheck(struct kmem_cache *cachep, struct slab *slab
        }
 }
 #else
-static void slab_destroy_debugcheck(struct kmem_cache *cachep, struct slab *slabp)
+static void slab_destroy_debugcheck(struct kmem_cache *cachep,
+                                               struct page *page)
 {
 }
 #endif
@@ -2044,23 +1952,34 @@ static void slab_destroy_debugcheck(struct kmem_cache *cachep, struct slab *slab
  * Before calling the slab must have been unlinked from the cache.  The
  * cache-lock is not held/needed.
  */
-static void slab_destroy(struct kmem_cache *cachep, struct slab *slabp)
+static void slab_destroy(struct kmem_cache *cachep, struct page *page)
 {
-       void *addr = slabp->s_mem - slabp->colouroff;
+       void *freelist;
 
-       slab_destroy_debugcheck(cachep, slabp);
+       freelist = page->freelist;
+       slab_destroy_debugcheck(cachep, page);
        if (unlikely(cachep->flags & SLAB_DESTROY_BY_RCU)) {
-               struct slab_rcu *slab_rcu;
+               struct rcu_head *head;
+
+               /*
+                * RCU free overloads the RCU head over the LRU.
+                * slab_page has been overloeaded over the LRU,
+                * however it is not used from now on so that
+                * we can use it safely.
+                */
+               head = (void *)&page->rcu_head;
+               call_rcu(head, kmem_rcu_free);
 
-               slab_rcu = (struct slab_rcu *)slabp;
-               slab_rcu->cachep = cachep;
-               slab_rcu->addr = addr;
-               call_rcu(&slab_rcu->head, kmem_rcu_free);
        } else {
-               kmem_freepages(cachep, addr);
-               if (OFF_SLAB(cachep))
-                       kmem_cache_free(cachep->slabp_cache, slabp);
+               kmem_freepages(cachep, page);
        }
+
+       /*
+        * From now on, we don't use freelist
+        * although actual page can be freed in rcu context
+        */
+       if (OFF_SLAB(cachep))
+               kmem_cache_free(cachep->freelist_cache, freelist);
 }
 
 /**
@@ -2097,8 +2016,8 @@ static size_t calculate_slab_order(struct kmem_cache *cachep,
                         * use off-slab slabs. Needed to avoid a possible
                         * looping condition in cache_grow().
                         */
-                       offslab_limit = size - sizeof(struct slab);
-                       offslab_limit /= sizeof(kmem_bufctl_t);
+                       offslab_limit = size;
+                       offslab_limit /= sizeof(unsigned int);
 
                        if (num > offslab_limit)
                                break;
@@ -2220,7 +2139,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
 int
 __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags)
 {
-       size_t left_over, slab_size, ralign;
+       size_t left_over, freelist_size, ralign;
        gfp_t gfp;
        int err;
        size_t size = cachep->size;
@@ -2339,22 +2258,21 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags)
        if (!cachep->num)
                return -E2BIG;
 
-       slab_size = ALIGN(cachep->num * sizeof(kmem_bufctl_t)
-                         + sizeof(struct slab), cachep->align);
+       freelist_size =
+               ALIGN(cachep->num * sizeof(unsigned int), cachep->align);
 
        /*
         * If the slab has been placed off-slab, and we have enough space then
         * move it on-slab. This is at the expense of any extra colouring.
         */
-       if (flags & CFLGS_OFF_SLAB && left_over >= slab_size) {
+       if (flags & CFLGS_OFF_SLAB && left_over >= freelist_size) {
                flags &= ~CFLGS_OFF_SLAB;
-               left_over -= slab_size;
+               left_over -= freelist_size;
        }
 
        if (flags & CFLGS_OFF_SLAB) {
                /* really off slab. No need for manual alignment */
-               slab_size =
-                   cachep->num * sizeof(kmem_bufctl_t) + sizeof(struct slab);
+               freelist_size = cachep->num * sizeof(unsigned int);
 
 #ifdef CONFIG_PAGE_POISONING
                /* If we're going to use the generic kernel_map_pages()
@@ -2371,16 +2289,16 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags)
        if (cachep->colour_off < cachep->align)
                cachep->colour_off = cachep->align;
        cachep->colour = left_over / cachep->colour_off;
-       cachep->slab_size = slab_size;
+       cachep->freelist_size = freelist_size;
        cachep->flags = flags;
-       cachep->allocflags = 0;
+       cachep->allocflags = __GFP_COMP;
        if (CONFIG_ZONE_DMA_FLAG && (flags & SLAB_CACHE_DMA))
                cachep->allocflags |= GFP_DMA;
        cachep->size = size;
        cachep->reciprocal_buffer_size = reciprocal_value(size);
 
        if (flags & CFLGS_OFF_SLAB) {
-               cachep->slabp_cache = kmalloc_slab(slab_size, 0u);
+               cachep->freelist_cache = kmalloc_slab(freelist_size, 0u);
                /*
                 * This is a possibility for one of the malloc_sizes caches.
                 * But since we go off slab only for object size greater than
@@ -2388,7 +2306,7 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags)
                 * this should not happen at all.
                 * But leave a BUG_ON for some lucky dude.
                 */
-               BUG_ON(ZERO_OR_NULL_PTR(cachep->slabp_cache));
+               BUG_ON(ZERO_OR_NULL_PTR(cachep->freelist_cache));
        }
 
        err = setup_cpu_cache(cachep, gfp);
@@ -2494,7 +2412,7 @@ static int drain_freelist(struct kmem_cache *cache,
 {
        struct list_head *p;
        int nr_freed;
-       struct slab *slabp;
+       struct page *page;
 
        nr_freed = 0;
        while (nr_freed < tofree && !list_empty(&n->slabs_free)) {
@@ -2506,18 +2424,18 @@ static int drain_freelist(struct kmem_cache *cache,
                        goto out;
                }
 
-               slabp = list_entry(p, struct slab, list);
+               page = list_entry(p, struct page, lru);
 #if DEBUG
-               BUG_ON(slabp->inuse);
+               BUG_ON(page->active);
 #endif
-               list_del(&slabp->list);
+               list_del(&page->lru);
                /*
                 * Safe to drop the lock. The slab is no longer linked
                 * to the cache.
                 */
                n->free_objects -= cache->num;
                spin_unlock_irq(&n->list_lock);
-               slab_destroy(cache, slabp);
+               slab_destroy(cache, page);
                nr_freed++;
        }
 out:
@@ -2600,52 +2518,42 @@ int __kmem_cache_shutdown(struct kmem_cache *cachep)
  * descriptors in kmem_cache_create, we search through the malloc_sizes array.
  * If we are creating a malloc_sizes cache here it would not be visible to
  * kmem_find_general_cachep till the initialization is complete.
- * Hence we cannot have slabp_cache same as the original cache.
+ * Hence we cannot have freelist_cache same as the original cache.
  */
-static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
-                                  int colour_off, gfp_t local_flags,
-                                  int nodeid)
+static void *alloc_slabmgmt(struct kmem_cache *cachep,
+                                  struct page *page, int colour_off,
+                                  gfp_t local_flags, int nodeid)
 {
-       struct slab *slabp;
+       void *freelist;
+       void *addr = page_address(page);
 
        if (OFF_SLAB(cachep)) {
                /* Slab management obj is off-slab. */
-               slabp = kmem_cache_alloc_node(cachep->slabp_cache,
+               freelist = kmem_cache_alloc_node(cachep->freelist_cache,
                                              local_flags, nodeid);
-               /*
-                * If the first object in the slab is leaked (it's allocated
-                * but no one has a reference to it), we want to make sure
-                * kmemleak does not treat the ->s_mem pointer as a reference
-                * to the object. Otherwise we will not report the leak.
-                */
-               kmemleak_scan_area(&slabp->list, sizeof(struct list_head),
-                                  local_flags);
-               if (!slabp)
+               if (!freelist)
                        return NULL;
        } else {
-               slabp = objp + colour_off;
-               colour_off += cachep->slab_size;
+               freelist = addr + colour_off;
+               colour_off += cachep->freelist_size;
        }
-       slabp->inuse = 0;
-       slabp->colouroff = colour_off;
-       slabp->s_mem = objp + colour_off;
-       slabp->nodeid = nodeid;
-       slabp->free = 0;
-       return slabp;
+       page->active = 0;
+       page->s_mem = addr + colour_off;
+       return freelist;
 }
 
-static inline kmem_bufctl_t *slab_bufctl(struct slab *slabp)
+static inline unsigned int *slab_freelist(struct page *page)
 {
-       return (kmem_bufctl_t *) (slabp + 1);
+       return (unsigned int *)(page->freelist);
 }
 
 static void cache_init_objs(struct kmem_cache *cachep,
-                           struct slab *slabp)
+                           struct page *page)
 {
        int i;
 
        for (i = 0; i < cachep->num; i++) {
-               void *objp = index_to_obj(cachep, slabp, i);
+               void *objp = index_to_obj(cachep, page, i);
 #if DEBUG
                /* need to poison the objs? */
                if (cachep->flags & SLAB_POISON)
@@ -2681,9 +2589,8 @@ static void cache_init_objs(struct kmem_cache *cachep,
                if (cachep->ctor)
                        cachep->ctor(objp);
 #endif
-               slab_bufctl(slabp)[i] = i + 1;
+               slab_freelist(page)[i] = i;
        }
-       slab_bufctl(slabp)[i - 1] = BUFCTL_END;
 }
 
 static void kmem_flagcheck(struct kmem_cache *cachep, gfp_t flags)
@@ -2696,41 +2603,41 @@ static void kmem_flagcheck(struct kmem_cache *cachep, gfp_t flags)
        }
 }
 
-static void *slab_get_obj(struct kmem_cache *cachep, struct slab *slabp,
+static void *slab_get_obj(struct kmem_cache *cachep, struct page *page,
                                int nodeid)
 {
-       void *objp = index_to_obj(cachep, slabp, slabp->free);
-       kmem_bufctl_t next;
+       void *objp;
 
-       slabp->inuse++;
-       next = slab_bufctl(slabp)[slabp->free];
+       objp = index_to_obj(cachep, page, slab_freelist(page)[page->active]);
+       page->active++;
 #if DEBUG
-       slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;
-       WARN_ON(slabp->nodeid != nodeid);
+       WARN_ON(page_to_nid(virt_to_page(objp)) != nodeid);
 #endif
-       slabp->free = next;
 
        return objp;
 }
 
-static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,
+static void slab_put_obj(struct kmem_cache *cachep, struct page *page,
                                void *objp, int nodeid)
 {
-       unsigned int objnr = obj_to_index(cachep, slabp, objp);
-
+       unsigned int objnr = obj_to_index(cachep, page, objp);
 #if DEBUG
+       unsigned int i;
+
        /* Verify that the slab belongs to the intended node */
-       WARN_ON(slabp->nodeid != nodeid);
+       WARN_ON(page_to_nid(virt_to_page(objp)) != nodeid);
 
-       if (slab_bufctl(slabp)[objnr] + 1 <= SLAB_LIMIT + 1) {
-               printk(KERN_ERR "slab: double free detected in cache "
-                               "'%s', objp %p\n", cachep->name, objp);
-               BUG();
+       /* Verify double free bug */
+       for (i = page->active; i < cachep->num; i++) {
+               if (slab_freelist(page)[i] == objnr) {
+                       printk(KERN_ERR "slab: double free detected in cache "
+                                       "'%s', objp %p\n", cachep->name, objp);
+                       BUG();
+               }
        }
 #endif
-       slab_bufctl(slabp)[objnr] = slabp->free;
-       slabp->free = objnr;
-       slabp->inuse--;
+       page->active--;
+       slab_freelist(page)[page->active] = objnr;
 }
 
 /*
@@ -2738,23 +2645,11 @@ static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,
  * for the slab allocator to be able to lookup the cache and slab of a
  * virtual address for kfree, ksize, and slab debugging.
  */
-static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
-                          void *addr)
+static void slab_map_pages(struct kmem_cache *cache, struct page *page,
+                          void *freelist)
 {
-       int nr_pages;
-       struct page *page;
-
-       page = virt_to_page(addr);
-
-       nr_pages = 1;
-       if (likely(!PageCompound(page)))
-               nr_pages <<= cache->gfporder;
-
-       do {
-               page->slab_cache = cache;
-               page->slab_page = slab;
-               page++;
-       } while (--nr_pages);
+       page->slab_cache = cache;
+       page->freelist = freelist;
 }
 
 /*
@@ -2762,9 +2657,9 @@ static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
  * kmem_cache_alloc() when there are no active objs left in a cache.
  */
 static int cache_grow(struct kmem_cache *cachep,
-               gfp_t flags, int nodeid, void *objp)
+               gfp_t flags, int nodeid, struct page *page)
 {
-       struct slab *slabp;
+       void *freelist;
        size_t offset;
        gfp_t local_flags;
        struct kmem_cache_node *n;
@@ -2805,20 +2700,20 @@ static int cache_grow(struct kmem_cache *cachep,
         * Get mem for the objs.  Attempt to allocate a physical page from
         * 'nodeid'.
         */
-       if (!objp)
-               objp = kmem_getpages(cachep, local_flags, nodeid);
-       if (!objp)
+       if (!page)
+               page = kmem_getpages(cachep, local_flags, nodeid);
+       if (!page)
                goto failed;
 
        /* Get slab management. */
-       slabp = alloc_slabmgmt(cachep, objp, offset,
+       freelist = alloc_slabmgmt(cachep, page, offset,
                        local_flags & ~GFP_CONSTRAINT_MASK, nodeid);
-       if (!slabp)
+       if (!freelist)
                goto opps1;
 
-       slab_map_pages(cachep, slabp, objp);
+       slab_map_pages(cachep, page, freelist);
 
-       cache_init_objs(cachep, slabp);
+       cache_init_objs(cachep, page);
 
        if (local_flags & __GFP_WAIT)
                local_irq_disable();
@@ -2826,13 +2721,13 @@ static int cache_grow(struct kmem_cache *cachep,
        spin_lock(&n->list_lock);
 
        /* Make slab active. */
-       list_add_tail(&slabp->list, &(n->slabs_free));
+       list_add_tail(&page->lru, &(n->slabs_free));
        STATS_INC_GROWN(cachep);
        n->free_objects += cachep->num;
        spin_unlock(&n->list_lock);
        return 1;
 opps1:
-       kmem_freepages(cachep, objp);
+       kmem_freepages(cachep, page);
 failed:
        if (local_flags & __GFP_WAIT)
                local_irq_disable();
@@ -2880,9 +2775,8 @@ static inline void verify_redzone_free(struct kmem_cache *cache, void *obj)
 static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp,
                                   unsigned long caller)
 {
-       struct page *page;
        unsigned int objnr;
-       struct slab *slabp;
+       struct page *page;
 
        BUG_ON(virt_to_cache(objp) != cachep);
 
@@ -2890,8 +2784,6 @@ static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp,
        kfree_debugcheck(objp);
        page = virt_to_head_page(objp);
 
-       slabp = page->slab_page;
-
        if (cachep->flags & SLAB_RED_ZONE) {
                verify_redzone_free(cachep, objp);
                *dbg_redzone1(cachep, objp) = RED_INACTIVE;
@@ -2900,14 +2792,11 @@ static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp,
        if (cachep->flags & SLAB_STORE_USER)
                *dbg_userword(cachep, objp) = (void *)caller;
 
-       objnr = obj_to_index(cachep, slabp, objp);
+       objnr = obj_to_index(cachep, page, objp);
 
        BUG_ON(objnr >= cachep->num);
-       BUG_ON(objp != index_to_obj(cachep, slabp, objnr));
+       BUG_ON(objp != index_to_obj(cachep, page, objnr));
 
-#ifdef CONFIG_DEBUG_SLAB_LEAK
-       slab_bufctl(slabp)[objnr] = BUFCTL_FREE;
-#endif
        if (cachep->flags & SLAB_POISON) {
 #ifdef CONFIG_DEBUG_PAGEALLOC
                if ((cachep->size % PAGE_SIZE)==0 && OFF_SLAB(cachep)) {
@@ -2924,33 +2813,9 @@ static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp,
        return objp;
 }
 
-static void check_slabp(struct kmem_cache *cachep, struct slab *slabp)
-{
-       kmem_bufctl_t i;
-       int entries = 0;
-
-       /* Check slab's freelist to see if this obj is there. */
-       for (i = slabp->free; i != BUFCTL_END; i = slab_bufctl(slabp)[i]) {
-               entries++;
-               if (entries > cachep->num || i >= cachep->num)
-                       goto bad;
-       }
-       if (entries != cachep->num - slabp->inuse) {
-bad:
-               printk(KERN_ERR "slab: Internal list corruption detected in "
-                       "cache '%s'(%d), slabp %p(%d). Tainted(%s). Hexdump:\n",
-                       cachep->name, cachep->num, slabp, slabp->inuse,
-                       print_tainted());
-               print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 16, 1, slabp,
-                       sizeof(*slabp) + cachep->num * sizeof(kmem_bufctl_t),
-                       1);
-               BUG();
-       }
-}
 #else
 #define kfree_debugcheck(x) do { } while(0)
 #define cache_free_debugcheck(x,objp,z) (objp)
-#define check_slabp(x,y) do { } while(0)
 #endif
 
 static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags,
@@ -2989,7 +2854,7 @@ retry:
 
        while (batchcount > 0) {
                struct list_head *entry;
-               struct slab *slabp;
+               struct page *page;
                /* Get slab alloc is to come from. */
                entry = n->slabs_partial.next;
                if (entry == &n->slabs_partial) {
@@ -2999,8 +2864,7 @@ retry:
                                goto must_grow;
                }
 
-               slabp = list_entry(entry, struct slab, list);
-               check_slabp(cachep, slabp);
+               page = list_entry(entry, struct page, lru);
                check_spinlock_acquired(cachep);
 
                /*
@@ -3008,24 +2872,23 @@ retry:
                 * there must be at least one object available for
                 * allocation.
                 */
-               BUG_ON(slabp->inuse >= cachep->num);
+               BUG_ON(page->active >= cachep->num);
 
-               while (slabp->inuse < cachep->num && batchcount--) {
+               while (page->active < cachep->num && batchcount--) {
                        STATS_INC_ALLOCED(cachep);
                        STATS_INC_ACTIVE(cachep);
                        STATS_SET_HIGH(cachep);
 
-                       ac_put_obj(cachep, ac, slab_get_obj(cachep, slabp,
+                       ac_put_obj(cachep, ac, slab_get_obj(cachep, page,
                                                                        node));
                }
-               check_slabp(cachep, slabp);
 
                /* move slabp to correct slabp list: */
-               list_del(&slabp->list);
-               if (slabp->free == BUFCTL_END)
-                       list_add(&slabp->list, &n->slabs_full);
+               list_del(&page->lru);
+               if (page->active == cachep->num)
+                       list_add(&page->list, &n->slabs_full);
                else
-                       list_add(&slabp->list, &n->slabs_partial);
+                       list_add(&page->list, &n->slabs_partial);
        }
 
 must_grow:
@@ -3097,16 +2960,6 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep,
                *dbg_redzone1(cachep, objp) = RED_ACTIVE;
                *dbg_redzone2(cachep, objp) = RED_ACTIVE;
        }
-#ifdef CONFIG_DEBUG_SLAB_LEAK
-       {
-               struct slab *slabp;
-               unsigned objnr;
-
-               slabp = virt_to_head_page(objp)->slab_page;
-               objnr = (unsigned)(objp - slabp->s_mem) / cachep->size;
-               slab_bufctl(slabp)[objnr] = BUFCTL_ACTIVE;
-       }
-#endif
        objp += obj_offset(cachep);
        if (cachep->ctor && cachep->flags & SLAB_POISON)
                cachep->ctor(objp);
@@ -3248,18 +3101,20 @@ retry:
                 * We may trigger various forms of reclaim on the allowed
                 * set and go into memory reserves if necessary.
                 */
+               struct page *page;
+
                if (local_flags & __GFP_WAIT)
                        local_irq_enable();
                kmem_flagcheck(cache, flags);
-               obj = kmem_getpages(cache, local_flags, numa_mem_id());
+               page = kmem_getpages(cache, local_flags, numa_mem_id());
                if (local_flags & __GFP_WAIT)
                        local_irq_disable();
-               if (obj) {
+               if (page) {
                        /*
                         * Insert into the appropriate per node queues
                         */
-                       nid = page_to_nid(virt_to_page(obj));
-                       if (cache_grow(cache, flags, nid, obj)) {
+                       nid = page_to_nid(page);
+                       if (cache_grow(cache, flags, nid, page)) {
                                obj = ____cache_alloc_node(cache,
                                        flags | GFP_THISNODE, nid);
                                if (!obj)
@@ -3288,7 +3143,7 @@ static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags,
                                int nodeid)
 {
        struct list_head *entry;
-       struct slab *slabp;
+       struct page *page;
        struct kmem_cache_node *n;
        void *obj;
        int x;
@@ -3308,26 +3163,24 @@ retry:
                        goto must_grow;
        }
 
-       slabp = list_entry(entry, struct slab, list);
+       page = list_entry(entry, struct page, lru);
        check_spinlock_acquired_node(cachep, nodeid);
-       check_slabp(cachep, slabp);
 
        STATS_INC_NODEALLOCS(cachep);
        STATS_INC_ACTIVE(cachep);
        STATS_SET_HIGH(cachep);
 
-       BUG_ON(slabp->inuse == cachep->num);
+       BUG_ON(page->active == cachep->num);
 
-       obj = slab_get_obj(cachep, slabp, nodeid);
-       check_slabp(cachep, slabp);
+       obj = slab_get_obj(cachep, page, nodeid);
        n->free_objects--;
        /* move slabp to correct slabp list: */
-       list_del(&slabp->list);
+       list_del(&page->lru);
 
-       if (slabp->free == BUFCTL_END)
-               list_add(&slabp->list, &n->slabs_full);
+       if (page->active == cachep->num)
+               list_add(&page->lru, &n->slabs_full);
        else
-               list_add(&slabp->list, &n->slabs_partial);
+               list_add(&page->lru, &n->slabs_partial);
 
        spin_unlock(&n->list_lock);
        goto done;
@@ -3477,23 +3330,21 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
 
        for (i = 0; i < nr_objects; i++) {
                void *objp;
-               struct slab *slabp;
+               struct page *page;
 
                clear_obj_pfmemalloc(&objpp[i]);
                objp = objpp[i];
 
-               slabp = virt_to_slab(objp);
+               page = virt_to_head_page(objp);
                n = cachep->node[node];
-               list_del(&slabp->list);
+               list_del(&page->lru);
                check_spinlock_acquired_node(cachep, node);
-               check_slabp(cachep, slabp);
-               slab_put_obj(cachep, slabp, objp, node);
+               slab_put_obj(cachep, page, objp, node);
                STATS_DEC_ACTIVE(cachep);
                n->free_objects++;
-               check_slabp(cachep, slabp);
 
                /* fixup slab chains */
-               if (slabp->inuse == 0) {
+               if (page->active == 0) {
                        if (n->free_objects > n->free_limit) {
                                n->free_objects -= cachep->num;
                                /* No need to drop any previously held
@@ -3502,16 +3353,16 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
                                 * a different cache, refer to comments before
                                 * alloc_slabmgmt.
                                 */
-                               slab_destroy(cachep, slabp);
+                               slab_destroy(cachep, page);
                        } else {
-                               list_add(&slabp->list, &n->slabs_free);
+                               list_add(&page->lru, &n->slabs_free);
                        }
                } else {
                        /* Unconditionally move a slab to the end of the
                         * partial list on free - maximum time for the
                         * other objects to be freed, too.
                         */
-                       list_add_tail(&slabp->list, &n->slabs_partial);
+                       list_add_tail(&page->lru, &n->slabs_partial);
                }
        }
 }
@@ -3551,10 +3402,10 @@ free_done:
 
                p = n->slabs_free.next;
                while (p != &(n->slabs_free)) {
-                       struct slab *slabp;
+                       struct page *page;
 
-                       slabp = list_entry(p, struct slab, list);
-                       BUG_ON(slabp->inuse);
+                       page = list_entry(p, struct page, lru);
+                       BUG_ON(page->active);
 
                        i++;
                        p = p->next;
@@ -4158,7 +4009,7 @@ out:
 #ifdef CONFIG_SLABINFO
 void get_slabinfo(struct kmem_cache *cachep, struct slabinfo *sinfo)
 {
-       struct slab *slabp;
+       struct page *page;
        unsigned long active_objs;
        unsigned long num_objs;
        unsigned long active_slabs = 0;
@@ -4178,23 +4029,23 @@ void get_slabinfo(struct kmem_cache *cachep, struct slabinfo *sinfo)
                check_irq_on();
                spin_lock_irq(&n->list_lock);
 
-               list_for_each_entry(slabp, &n->slabs_full, list) {
-                       if (slabp->inuse != cachep->num && !error)
+               list_for_each_entry(page, &n->slabs_full, lru) {
+                       if (page->active != cachep->num && !error)
                                error = "slabs_full accounting error";
                        active_objs += cachep->num;
                        active_slabs++;
                }
-               list_for_each_entry(slabp, &n->slabs_partial, list) {
-                       if (slabp->inuse == cachep->num && !error)
-                               error = "slabs_partial inuse accounting error";
-                       if (!slabp->inuse && !error)
-                               error = "slabs_partial/inuse accounting error";
-                       active_objs += slabp->inuse;
+               list_for_each_entry(page, &n->slabs_partial, lru) {
+                       if (page->active == cachep->num && !error)
+                               error = "slabs_partial accounting error";
+                       if (!page->active && !error)
+                               error = "slabs_partial accounting error";
+                       active_objs += page->active;
                        active_slabs++;
                }
-               list_for_each_entry(slabp, &n->slabs_free, list) {
-                       if (slabp->inuse && !error)
-                               error = "slabs_free/inuse accounting error";
+               list_for_each_entry(page, &n->slabs_free, lru) {
+                       if (page->active && !error)
+                               error = "slabs_free accounting error";
                        num_slabs++;
                }
                free_objects += n->free_objects;
@@ -4346,15 +4197,27 @@ static inline int add_caller(unsigned long *n, unsigned long v)
        return 1;
 }
 
-static void handle_slab(unsigned long *n, struct kmem_cache *c, struct slab *s)
+static void handle_slab(unsigned long *n, struct kmem_cache *c,
+                                               struct page *page)
 {
        void *p;
-       int i;
+       int i, j;
+
        if (n[0] == n[1])
                return;
-       for (i = 0, p = s->s_mem; i < c->num; i++, p += c->size) {
-               if (slab_bufctl(s)[i] != BUFCTL_ACTIVE)
+       for (i = 0, p = page->s_mem; i < c->num; i++, p += c->size) {
+               bool active = true;
+
+               for (j = page->active; j < c->num; j++) {
+                       /* Skip freed item */
+                       if (slab_freelist(page)[j] == i) {
+                               active = false;
+                               break;
+                       }
+               }
+               if (!active)
                        continue;
+
                if (!add_caller(n, (unsigned long)*dbg_userword(c, p)))
                        return;
        }
@@ -4379,7 +4242,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, list);
-       struct slab *slabp;
+       struct page *page;
        struct kmem_cache_node *n;
        const char *name;
        unsigned long *x = m->private;
@@ -4403,10 +4266,10 @@ static int leaks_show(struct seq_file *m, void *p)
                check_irq_on();
                spin_lock_irq(&n->list_lock);
 
-               list_for_each_entry(slabp, &n->slabs_full, list)
-                       handle_slab(x, cachep, slabp);
-               list_for_each_entry(slabp, &n->slabs_partial, list)
-                       handle_slab(x, cachep, slabp);
+               list_for_each_entry(page, &n->slabs_full, lru)
+                       handle_slab(x, cachep, page);
+               list_for_each_entry(page, &n->slabs_partial, lru)
+                       handle_slab(x, cachep, page);
                spin_unlock_irq(&n->list_lock);
        }
        name = cachep->name;
index 92737a0..545a170 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -155,7 +155,7 @@ static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s)
 /*
  * Maximum number of desirable partial slabs.
  * The existence of more partial slabs makes kmem_cache_shrink
- * sort the partial list by the number of objects in the.
+ * sort the partial list by the number of objects in use.
  */
 #define MAX_PARTIAL 10
 
@@ -933,6 +933,16 @@ static void trace(struct kmem_cache *s, struct page *page, void *object,
  * Hooks for other subsystems that check memory allocations. In a typical
  * production configuration these hooks all should produce no code at all.
  */
+static inline void kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
+{
+       kmemleak_alloc(ptr, size, 1, flags);
+}
+
+static inline void kfree_hook(const void *x)
+{
+       kmemleak_free(x);
+}
+
 static inline int slab_pre_alloc_hook(struct kmem_cache *s, gfp_t flags)
 {
        flags &= gfp_allowed_mask;
@@ -955,7 +965,7 @@ static inline void slab_free_hook(struct kmem_cache *s, void *x)
        kmemleak_free_recursive(x, s->flags);
 
        /*
-        * Trouble is that we may no longer disable interupts in the fast path
+        * Trouble is that we may no longer disable interrupts in the fast path
         * So in order to make the debug calls that expect irqs to be
         * disabled we need to disable interrupts temporarily.
         */
@@ -1217,8 +1227,8 @@ static unsigned long kmem_cache_flags(unsigned long object_size,
        /*
         * Enable debugging if selected on the kernel commandline.
         */
-       if (slub_debug && (!slub_debug_slabs ||
-               !strncmp(slub_debug_slabs, name, strlen(slub_debug_slabs))))
+       if (slub_debug && (!slub_debug_slabs || (name &&
+               !strncmp(slub_debug_slabs, name, strlen(slub_debug_slabs)))))
                flags |= slub_debug;
 
        return flags;
@@ -1260,13 +1270,30 @@ static inline void inc_slabs_node(struct kmem_cache *s, int node,
 static inline void dec_slabs_node(struct kmem_cache *s, int node,
                                                        int objects) {}
 
+static inline void kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
+{
+       kmemleak_alloc(ptr, size, 1, flags);
+}
+
+static inline void kfree_hook(const void *x)
+{
+       kmemleak_free(x);
+}
+
 static inline int slab_pre_alloc_hook(struct kmem_cache *s, gfp_t flags)
                                                        { return 0; }
 
 static inline void slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags,
-               void *object) {}
+               void *object)
+{
+       kmemleak_alloc_recursive(object, s->object_size, 1, s->flags,
+               flags & gfp_allowed_mask);
+}
 
-static inline void slab_free_hook(struct kmem_cache *s, void *x) {}
+static inline void slab_free_hook(struct kmem_cache *s, void *x)
+{
+       kmemleak_free_recursive(x, s->flags);
+}
 
 #endif /* CONFIG_SLUB_DEBUG */
 
@@ -2829,8 +2856,8 @@ static struct kmem_cache *kmem_cache_node;
  * slab on the node for this slabcache. There are no concurrent accesses
  * possible.
  *
- * Note that this function only works on the kmalloc_node_cache
- * when allocating for the kmalloc_node_cache. This is used for bootstrapping
+ * Note that this function only works on the kmem_cache_node
+ * when allocating for the kmem_cache_node. This is used for bootstrapping
  * memory on a fresh node that has no slab structures yet.
  */
 static void early_kmem_cache_node_alloc(int node)
@@ -3272,7 +3299,7 @@ static void *kmalloc_large_node(size_t size, gfp_t flags, int node)
        if (page)
                ptr = page_address(page);
 
-       kmemleak_alloc(ptr, size, 1, flags);
+       kmalloc_large_node_hook(ptr, size, flags);
        return ptr;
 }
 
@@ -3336,7 +3363,7 @@ void kfree(const void *x)
        page = virt_to_head_page(x);
        if (unlikely(!PageSlab(page))) {
                BUG_ON(!PageCompound(page));
-               kmemleak_free(x);
+               kfree_hook(x);
                __free_memcg_kmem_pages(page, compound_order(page));
                return;
        }
index 7a9f80d..84b26aa 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -82,19 +82,6 @@ static void __put_compound_page(struct page *page)
 
 static void put_compound_page(struct page *page)
 {
-       /*
-        * hugetlbfs pages cannot be split from under us.  If this is a
-        * hugetlbfs page, check refcount on head page and release the page if
-        * the refcount becomes zero.
-        */
-       if (PageHuge(page)) {
-               page = compound_head(page);
-               if (put_page_testzero(page))
-                       __put_compound_page(page);
-
-               return;
-       }
-
        if (unlikely(PageTail(page))) {
                /* __split_huge_page_refcount can run under us */
                struct page *page_head = compound_trans_head(page);
@@ -111,14 +98,31 @@ static void put_compound_page(struct page *page)
                         * still hot on arches that do not support
                         * this_cpu_cmpxchg_double().
                         */
-                       if (PageSlab(page_head)) {
-                               if (PageTail(page)) {
+                       if (PageSlab(page_head) || PageHeadHuge(page_head)) {
+                               if (likely(PageTail(page))) {
+                                       /*
+                                        * __split_huge_page_refcount
+                                        * cannot race here.
+                                        */
+                                       VM_BUG_ON(!PageHead(page_head));
+                                       atomic_dec(&page->_mapcount);
                                        if (put_page_testzero(page_head))
                                                VM_BUG_ON(1);
-
-                                       atomic_dec(&page->_mapcount);
-                                       goto skip_lock_tail;
+                                       if (put_page_testzero(page_head))
+                                               __put_compound_page(page_head);
+                                       return;
                                } else
+                                       /*
+                                        * __split_huge_page_refcount
+                                        * run before us, "page" was a
+                                        * THP tail. The split
+                                        * page_head has been freed
+                                        * and reallocated as slab or
+                                        * hugetlbfs page of smaller
+                                        * order (only possible if
+                                        * reallocated as slab on
+                                        * x86).
+                                        */
                                        goto skip_lock;
                        }
                        /*
@@ -132,8 +136,27 @@ static void put_compound_page(struct page *page)
                                /* __split_huge_page_refcount run before us */
                                compound_unlock_irqrestore(page_head, flags);
 skip_lock:
-                               if (put_page_testzero(page_head))
-                                       __put_single_page(page_head);
+                               if (put_page_testzero(page_head)) {
+                                       /*
+                                        * The head page may have been
+                                        * freed and reallocated as a
+                                        * compound page of smaller
+                                        * order and then freed again.
+                                        * All we know is that it
+                                        * cannot have become: a THP
+                                        * page, a compound page of
+                                        * higher order, a tail page.
+                                        * That is because we still
+                                        * hold the refcount of the
+                                        * split THP tail and
+                                        * page_head was the THP head
+                                        * before the split.
+                                        */
+                                       if (PageHead(page_head))
+                                               __put_compound_page(page_head);
+                                       else
+                                               __put_single_page(page_head);
+                               }
 out_put_single:
                                if (put_page_testzero(page))
                                        __put_single_page(page);
@@ -155,7 +178,6 @@ out_put_single:
                        VM_BUG_ON(atomic_read(&page->_count) != 0);
                        compound_unlock_irqrestore(page_head, flags);
 
-skip_lock_tail:
                        if (put_page_testzero(page_head)) {
                                if (PageHead(page_head))
                                        __put_compound_page(page_head);
@@ -198,51 +220,52 @@ bool __get_page_tail(struct page *page)
         * proper PT lock that already serializes against
         * split_huge_page().
         */
+       unsigned long flags;
        bool got = false;
-       struct page *page_head;
-
-       /*
-        * If this is a hugetlbfs page it cannot be split under us.  Simply
-        * increment refcount for the head page.
-        */
-       if (PageHuge(page)) {
-               page_head = compound_head(page);
-               atomic_inc(&page_head->_count);
-               got = true;
-       } else {
-               unsigned long flags;
+       struct page *page_head = compound_trans_head(page);
 
-               page_head = compound_trans_head(page);
-               if (likely(page != page_head &&
-                                       get_page_unless_zero(page_head))) {
-
-                       /* Ref to put_compound_page() comment. */
-                       if (PageSlab(page_head)) {
-                               if (likely(PageTail(page))) {
-                                       __get_page_tail_foll(page, false);
-                                       return true;
-                               } else {
-                                       put_page(page_head);
-                                       return false;
-                               }
-                       }
-
-                       /*
-                        * page_head wasn't a dangling pointer but it
-                        * may not be a head page anymore by the time
-                        * we obtain the lock. That is ok as long as it
-                        * can't be freed from under us.
-                        */
-                       flags = compound_lock_irqsave(page_head);
-                       /* here __split_huge_page_refcount won't run anymore */
+       if (likely(page != page_head && get_page_unless_zero(page_head))) {
+               /* Ref to put_compound_page() comment. */
+               if (PageSlab(page_head) || PageHeadHuge(page_head)) {
                        if (likely(PageTail(page))) {
+                               /*
+                                * This is a hugetlbfs page or a slab
+                                * page. __split_huge_page_refcount
+                                * cannot race here.
+                                */
+                               VM_BUG_ON(!PageHead(page_head));
                                __get_page_tail_foll(page, false);
-                               got = true;
-                       }
-                       compound_unlock_irqrestore(page_head, flags);
-                       if (unlikely(!got))
+                               return true;
+                       } else {
+                               /*
+                                * __split_huge_page_refcount run
+                                * before us, "page" was a THP
+                                * tail. The split page_head has been
+                                * freed and reallocated as slab or
+                                * hugetlbfs page of smaller order
+                                * (only possible if reallocated as
+                                * slab on x86).
+                                */
                                put_page(page_head);
+                               return false;
+                       }
+               }
+
+               /*
+                * page_head wasn't a dangling pointer but it
+                * may not be a head page anymore by the time
+                * we obtain the lock. That is ok as long as it
+                * can't be freed from under us.
+                */
+               flags = compound_lock_irqsave(page_head);
+               /* here __split_huge_page_refcount won't run anymore */
+               if (likely(PageTail(page))) {
+                       __get_page_tail_foll(page, false);
+                       got = true;
                }
+               compound_unlock_irqrestore(page_head, flags);
+               if (unlikely(!got))
+                       put_page(page_head);
        }
        return got;
 }
index 990afab..9c5a1aa 100644 (file)
@@ -544,9 +544,7 @@ static int p9_virtio_probe(struct virtio_device *vdev)
 
        chan->inuse = false;
        if (virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG)) {
-               vdev->config->get(vdev,
-                               offsetof(struct virtio_9p_config, tag_len),
-                               &tag_len, sizeof(tag_len));
+               virtio_cread(vdev, struct virtio_9p_config, tag_len, &tag_len);
        } else {
                err = -EINVAL;
                goto out_free_vq;
@@ -556,8 +554,9 @@ static int p9_virtio_probe(struct virtio_device *vdev)
                err = -ENOMEM;
                goto out_free_vq;
        }
-       vdev->config->get(vdev, offsetof(struct virtio_9p_config, tag),
-                       tag, tag_len);
+
+       virtio_cread_bytes(vdev, offsetof(struct virtio_9p_config, tag),
+                          tag, tag_len);
        chan->tag = tag;
        chan->tag_len = tag_len;
        err = sysfs_create_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
index 0715db6..d334678 100644 (file)
@@ -224,7 +224,7 @@ source "net/hsr/Kconfig"
 
 config RPS
        boolean
-       depends on SMP && SYSFS && USE_GENERIC_SMP_HELPERS
+       depends on SMP && SYSFS
        default y
 
 config RFS_ACCEL
@@ -235,7 +235,7 @@ config RFS_ACCEL
 
 config XPS
        boolean
-       depends on SMP && USE_GENERIC_SMP_HELPERS
+       depends on SMP
        default y
 
 config NETPRIO_CGROUP
index 7fee50d..7d424ac 100644 (file)
@@ -1735,7 +1735,6 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                         size_t size, int flags)
 {
        struct sock *sk = sock->sk;
-       struct sockaddr_at *sat = (struct sockaddr_at *)msg->msg_name;
        struct ddpehdr *ddp;
        int copied = 0;
        int offset = 0;
@@ -1764,14 +1763,13 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
        }
        err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copied);
 
-       if (!err) {
-               if (sat) {
-                       sat->sat_family      = AF_APPLETALK;
-                       sat->sat_port        = ddp->deh_sport;
-                       sat->sat_addr.s_node = ddp->deh_snode;
-                       sat->sat_addr.s_net  = ddp->deh_snet;
-               }
-               msg->msg_namelen = sizeof(*sat);
+       if (!err && msg->msg_name) {
+               struct sockaddr_at *sat = msg->msg_name;
+               sat->sat_family      = AF_APPLETALK;
+               sat->sat_port        = ddp->deh_sport;
+               sat->sat_addr.s_node = ddp->deh_snode;
+               sat->sat_addr.s_net  = ddp->deh_snet;
+               msg->msg_namelen     = sizeof(*sat);
        }
 
        skb_free_datagram(sk, skb);     /* Free the datagram. */
index 737bef5..7b49100 100644 (file)
@@ -531,8 +531,6 @@ int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
        struct sk_buff *skb;
        int copied, error = -EINVAL;
 
-       msg->msg_namelen = 0;
-
        if (sock->state != SS_CONNECTED)
                return -ENOTCONN;
 
index a00123e..7bb1605 100644 (file)
@@ -1636,11 +1636,11 @@ static int ax25_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 
-       if (msg->msg_namelen != 0) {
-               struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
+       if (msg->msg_name) {
                ax25_digi digi;
                ax25_address src;
                const unsigned char *mac = skb_mac_header(skb);
+               struct sockaddr_ax25 *sax = msg->msg_name;
 
                memset(sax, 0, sizeof(struct full_sockaddr_ax25));
                ax25_addr_parse(mac + 1, skb->data - mac - 1, &src, NULL,
index f6a1671..56ca494 100644 (file)
@@ -224,10 +224,9 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        skb = skb_recv_datagram(sk, flags, noblock, &err);
        if (!skb) {
-               if (sk->sk_shutdown & RCV_SHUTDOWN) {
-                       msg->msg_namelen = 0;
+               if (sk->sk_shutdown & RCV_SHUTDOWN)
                        return 0;
-               }
+
                return err;
        }
 
@@ -245,8 +244,6 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
                if (bt_sk(sk)->skb_msg_name)
                        bt_sk(sk)->skb_msg_name(skb, msg->msg_name,
                                                &msg->msg_namelen);
-               else
-                       msg->msg_namelen = 0;
        }
 
        skb_free_datagram(sk, skb);
@@ -295,8 +292,6 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (flags & MSG_OOB)
                return -EOPNOTSUPP;
 
-       msg->msg_namelen = 0;
-
        BT_DBG("sk %p size %zu", sk, size);
 
        lock_sock(sk);
index 71f0be1..6a6c8bb 100644 (file)
@@ -856,8 +856,6 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (!skb)
                return err;
 
-       msg->msg_namelen = 0;
-
        copied = skb->len;
        if (len < copied) {
                msg->msg_flags |= MSG_TRUNC;
index 0cef677..4af3821 100644 (file)
@@ -2439,6 +2439,9 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
        int err;
        struct sk_buff_head seg_queue;
 
+       if (!chan->conn)
+               return -ENOTCONN;
+
        /* Connectionless channel */
        if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
                skb = l2cap_create_connless_pdu(chan, msg, len, priority);
index 94d06cb..facd8a7 100644 (file)
@@ -694,6 +694,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
        addr.l2_family = AF_BLUETOOTH;
        addr.l2_psm    = 0;
        addr.l2_cid    = 0;
+       addr.l2_bdaddr_type = BDADDR_BREDR;
        *err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
        if (*err < 0)
                goto failed;
@@ -719,6 +720,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
        addr.l2_family = AF_BLUETOOTH;
        addr.l2_psm    = __constant_cpu_to_le16(RFCOMM_PSM);
        addr.l2_cid    = 0;
+       addr.l2_bdaddr_type = BDADDR_BREDR;
        *err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
        if (*err == 0 || *err == -EINPROGRESS)
                return s;
@@ -1983,6 +1985,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
        addr.l2_family = AF_BLUETOOTH;
        addr.l2_psm    = __constant_cpu_to_le16(RFCOMM_PSM);
        addr.l2_cid    = 0;
+       addr.l2_bdaddr_type = BDADDR_BREDR;
        err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
        if (err < 0) {
                BT_ERR("Bind failed %d", err);
index c4d3d42..3c2d3e4 100644 (file)
@@ -615,7 +615,6 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
                rfcomm_dlc_accept(d);
-               msg->msg_namelen = 0;
                return 0;
        }
 
@@ -739,8 +738,9 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
 static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
 {
        struct sock *sk = sock->sk;
+       struct sock *l2cap_sk;
+       struct l2cap_conn *conn;
        struct rfcomm_conninfo cinfo;
-       struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn;
        int len, err = 0;
        u32 opt;
 
@@ -783,6 +783,9 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
                        break;
                }
 
+               l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk;
+               conn = l2cap_pi(l2cap_sk)->chan->conn;
+
                memset(&cinfo, 0, sizeof(cinfo));
                cinfo.hci_handle = conn->hcon->handle;
                memcpy(cinfo.dev_class, conn->hcon->dev_class, 3);
index 12a0e51..24fa396 100644 (file)
@@ -711,7 +711,6 @@ static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
            test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
                sco_conn_defer_accept(pi->conn->hcon, pi->setting);
                sk->sk_state = BT_CONFIG;
-               msg->msg_namelen = 0;
 
                release_sock(sk);
                return 0;
index 85a2796..4b07acb 100644 (file)
@@ -742,6 +742,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
 
        BT_DBG("conn %p", conn);
 
+       if (!(conn->hcon->link_mode & HCI_LM_MASTER))
+               return SMP_CMD_NOTSUPP;
+
        hcon->pending_sec_level = authreq_to_seclevel(rp->auth_req);
 
        if (smp_ltk_encrypt(conn, hcon->pending_sec_level))
index c41d5fb..4bf02ad 100644 (file)
@@ -172,6 +172,9 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)
                del_nbp(p);
        }
 
+       br_fdb_delete_by_port(br, NULL, 1);
+
+       br_vlan_flush(br);
        del_timer_sync(&br->gc_timer);
 
        br_sysfs_delbr(br->dev);
index 53f0990..af5ebd1 100644 (file)
@@ -34,7 +34,6 @@ static void __vlan_add_flags(struct net_port_vlans *v, u16 vid, u16 flags)
 
 static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 {
-       const struct net_device_ops *ops;
        struct net_bridge_port *p = NULL;
        struct net_bridge *br;
        struct net_device *dev;
@@ -53,17 +52,15 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
                br = v->parent.br;
                dev = br->dev;
        }
-       ops = dev->netdev_ops;
 
-       if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
+       if (p) {
                /* Add VLAN to the device filter if it is supported.
                 * Stricly speaking, this is not necessary now, since
                 * devices are made promiscuous by the bridge, but if
                 * that ever changes this code will allow tagged
                 * traffic to enter the bridge.
                 */
-               err = ops->ndo_vlan_rx_add_vid(dev, htons(ETH_P_8021Q),
-                                              vid);
+               err = vlan_vid_add(dev, htons(ETH_P_8021Q), vid);
                if (err)
                        return err;
        }
@@ -82,8 +79,8 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
        return 0;
 
 out_filt:
-       if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER))
-               ops->ndo_vlan_rx_kill_vid(dev, htons(ETH_P_8021Q), vid);
+       if (p)
+               vlan_vid_del(dev, htons(ETH_P_8021Q), vid);
        return err;
 }
 
@@ -95,13 +92,8 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
        __vlan_delete_pvid(v, vid);
        clear_bit(vid, v->untagged_bitmap);
 
-       if (v->port_idx) {
-               struct net_device *dev = v->parent.port->dev;
-               const struct net_device_ops *ops = dev->netdev_ops;
-
-               if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
-                       ops->ndo_vlan_rx_kill_vid(dev, htons(ETH_P_8021Q), vid);
-       }
+       if (v->port_idx)
+               vlan_vid_del(v->parent.port->dev, htons(ETH_P_8021Q), vid);
 
        clear_bit(vid, v->vlan_bitmap);
        v->num_vlans--;
@@ -398,6 +390,7 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
 void nbp_vlan_flush(struct net_bridge_port *port)
 {
        struct net_port_vlans *pv;
+       u16 vid;
 
        ASSERT_RTNL();
 
@@ -405,6 +398,9 @@ void nbp_vlan_flush(struct net_bridge_port *port)
        if (!pv)
                return;
 
+       for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
+               vlan_vid_del(port->dev, htons(ETH_P_8021Q), vid);
+
        __vlan_flush(pv);
 }
 
index 99c8566..17fd5f2 100644 (file)
@@ -48,10 +48,12 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par)
        if (info->bitmask & EBT_IP6_TCLASS &&
           FWINV(info->tclass != ipv6_get_dsfield(ih6), EBT_IP6_TCLASS))
                return false;
-       if (FWINV(ipv6_masked_addr_cmp(&ih6->saddr, &info->smsk,
-                                      &info->saddr), EBT_IP6_SOURCE) ||
+       if ((info->bitmask & EBT_IP6_SOURCE &&
+           FWINV(ipv6_masked_addr_cmp(&ih6->saddr, &info->smsk,
+                                      &info->saddr), EBT_IP6_SOURCE)) ||
+           (info->bitmask & EBT_IP6_DEST &&
            FWINV(ipv6_masked_addr_cmp(&ih6->daddr, &info->dmsk,
-                                      &info->daddr), EBT_IP6_DEST))
+                                      &info->daddr), EBT_IP6_DEST)))
                return false;
        if (info->bitmask & EBT_IP6_PROTO) {
                uint8_t nexthdr = ih6->nexthdr;
index 05a41c7..d6be3ed 100644 (file)
@@ -286,8 +286,6 @@ static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (m->msg_flags&MSG_OOB)
                goto read_error;
 
-       m->msg_namelen = 0;
-
        skb = skb_recv_datagram(sk, flags, 0 , &ret);
        if (!skb)
                goto read_error;
@@ -361,8 +359,6 @@ static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (flags&MSG_OOB)
                goto out;
 
-       msg->msg_namelen = 0;
-
        /*
         * Lock the socket to prevent queue disordering
         * while sleeps in memcpy_tomsg
index 3ab8dd2..d249874 100644 (file)
@@ -420,7 +420,7 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
  * @mask: CAN mask (see description)
  * @func: callback function on filter match
  * @data: returned parameter for callback function
- * @ident: string for calling module indentification
+ * @ident: string for calling module identification
  *
  * Description:
  *  Invokes the callback function with the received sk_buff and the given
index 8903258..618c6a8 100644 (file)
@@ -93,7 +93,8 @@ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov,
                        if (err < 0)
                                return err;
                }
-               kern_msg->msg_name = kern_address;
+               if (kern_msg->msg_name)
+                       kern_msg->msg_name = kern_address;
        } else
                kern_msg->msg_name = NULL;
 
index 8ffc52e..ba3b7ea 100644 (file)
 #include <linux/static_key.h>
 #include <linux/hashtable.h>
 #include <linux/vmalloc.h>
+#include <linux/if_macvlan.h>
 
 #include "net-sysfs.h"
 
@@ -1424,6 +1425,10 @@ void dev_disable_lro(struct net_device *dev)
        if (is_vlan_dev(dev))
                dev = vlan_dev_real_dev(dev);
 
+       /* the same for macvlan devices */
+       if (netif_is_macvlan(dev))
+               dev = macvlan_dev_real_dev(dev);
+
        dev->wanted_features &= ~NETIF_F_LRO;
        netdev_update_features(dev);
 
@@ -1690,13 +1695,9 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
                kfree_skb(skb);
                return NET_RX_DROP;
        }
-       skb->protocol = eth_type_trans(skb, dev);
 
-       /* eth_type_trans() can set pkt_type.
-        * call skb_scrub_packet() after it to clear pkt_type _after_ calling
-        * eth_type_trans().
-        */
        skb_scrub_packet(skb, true);
+       skb->protocol = eth_type_trans(skb, dev);
 
        return netif_rx(skb);
 }
@@ -4995,7 +4996,7 @@ static void dev_change_rx_flags(struct net_device *dev, int flags)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
 
-       if ((dev->flags & IFF_UP) && ops->ndo_change_rx_flags)
+       if (ops->ndo_change_rx_flags)
                ops->ndo_change_rx_flags(dev, flags);
 }
 
index 5e78d44..9589718 100644 (file)
@@ -106,6 +106,10 @@ static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)
        return skb;
 }
 
+static struct genl_multicast_group dropmon_mcgrps[] = {
+       { .name = "events", },
+};
+
 static void send_dm_alert(struct work_struct *work)
 {
        struct sk_buff *skb;
@@ -116,7 +120,8 @@ static void send_dm_alert(struct work_struct *work)
        skb = reset_per_cpu_data(data);
 
        if (skb)
-               genlmsg_multicast(skb, 0, NET_DM_GRP_ALERT, GFP_KERNEL);
+               genlmsg_multicast(&net_drop_monitor_family, skb, 0,
+                                 0, GFP_KERNEL);
 }
 
 /*
@@ -333,7 +338,7 @@ out:
        return NOTIFY_DONE;
 }
 
-static struct genl_ops dropmon_ops[] = {
+static const struct genl_ops dropmon_ops[] = {
        {
                .cmd = NET_DM_CMD_CONFIG,
                .doit = net_dm_cmd_config,
@@ -364,13 +369,13 @@ static int __init init_net_drop_monitor(void)
                return -ENOSPC;
        }
 
-       rc = genl_register_family_with_ops(&net_drop_monitor_family,
-                                          dropmon_ops,
-                                          ARRAY_SIZE(dropmon_ops));
+       rc = genl_register_family_with_ops_groups(&net_drop_monitor_family,
+                                                 dropmon_ops, dropmon_mcgrps);
        if (rc) {
                pr_err("Could not create drop monitor netlink family\n");
                return rc;
        }
+       WARN_ON(net_drop_monitor_family.mcgrp_offset != NET_DM_GRP_ALERT);
 
        rc = register_netdevice_notifier(&dropmon_net_notifier);
        if (rc < 0) {
index 4cdb7c4..b618694 100644 (file)
@@ -48,7 +48,8 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *a
                        if (err < 0)
                                return err;
                }
-               m->msg_name = address;
+               if (m->msg_name)
+                       m->msg_name = address;
        } else {
                m->msg_name = NULL;
        }
index 8cec1e6..2718fed 100644 (file)
@@ -2796,6 +2796,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
        struct sk_buff *segs = NULL;
        struct sk_buff *tail = NULL;
        struct sk_buff *fskb = skb_shinfo(skb)->frag_list;
+       skb_frag_t *skb_frag = skb_shinfo(skb)->frags;
        unsigned int mss = skb_shinfo(skb)->gso_size;
        unsigned int doffset = skb->data - skb_mac_header(skb);
        unsigned int offset = doffset;
@@ -2835,16 +2836,38 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
                if (hsize > len || !sg)
                        hsize = len;
 
-               if (!hsize && i >= nfrags) {
-                       BUG_ON(fskb->len != len);
+               if (!hsize && i >= nfrags && skb_headlen(fskb) &&
+                   (skb_headlen(fskb) == len || sg)) {
+                       BUG_ON(skb_headlen(fskb) > len);
+
+                       i = 0;
+                       nfrags = skb_shinfo(fskb)->nr_frags;
+                       skb_frag = skb_shinfo(fskb)->frags;
+                       pos += skb_headlen(fskb);
+
+                       while (pos < offset + len) {
+                               BUG_ON(i >= nfrags);
+
+                               size = skb_frag_size(skb_frag);
+                               if (pos + size > offset + len)
+                                       break;
+
+                               i++;
+                               pos += size;
+                               skb_frag++;
+                       }
 
-                       pos += len;
                        nskb = skb_clone(fskb, GFP_ATOMIC);
                        fskb = fskb->next;
 
                        if (unlikely(!nskb))
                                goto err;
 
+                       if (unlikely(pskb_trim(nskb, len))) {
+                               kfree_skb(nskb);
+                               goto err;
+                       }
+
                        hsize = skb_end_offset(nskb);
                        if (skb_cow_head(nskb, doffset + headroom)) {
                                kfree_skb(nskb);
@@ -2881,7 +2904,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
                                                 nskb->data - tnl_hlen,
                                                 doffset + tnl_hlen);
 
-               if (fskb != skb_shinfo(skb)->frag_list)
+               if (nskb->len == len + doffset)
                        goto perform_csum_check;
 
                if (!sg) {
@@ -2899,8 +2922,28 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
 
                skb_shinfo(nskb)->tx_flags = skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG;
 
-               while (pos < offset + len && i < nfrags) {
-                       *frag = skb_shinfo(skb)->frags[i];
+               while (pos < offset + len) {
+                       if (i >= nfrags) {
+                               BUG_ON(skb_headlen(fskb));
+
+                               i = 0;
+                               nfrags = skb_shinfo(fskb)->nr_frags;
+                               skb_frag = skb_shinfo(fskb)->frags;
+
+                               BUG_ON(!nfrags);
+
+                               fskb = fskb->next;
+                       }
+
+                       if (unlikely(skb_shinfo(nskb)->nr_frags >=
+                                    MAX_SKB_FRAGS)) {
+                               net_warn_ratelimited(
+                                       "skb_segment: too many frags: %u %u\n",
+                                       pos, mss);
+                               goto err;
+                       }
+
+                       *frag = *skb_frag;
                        __skb_frag_ref(frag);
                        size = skb_frag_size(frag);
 
@@ -2913,6 +2956,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
 
                        if (pos + size <= offset + len) {
                                i++;
+                               skb_frag++;
                                pos += size;
                        } else {
                                skb_frag_size_sub(frag, pos + size - (offset + len));
@@ -2922,25 +2966,6 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
                        frag++;
                }
 
-               if (pos < offset + len) {
-                       struct sk_buff *fskb2 = fskb;
-
-                       BUG_ON(pos + fskb->len != offset + len);
-
-                       pos += fskb->len;
-                       fskb = fskb->next;
-
-                       if (fskb2->next) {
-                               fskb2 = skb_clone(fskb2, GFP_ATOMIC);
-                               if (!fskb2)
-                                       goto err;
-                       } else
-                               skb_get(fskb2);
-
-                       SKB_FRAG_ASSERT(nskb);
-                       skb_shinfo(nskb)->frag_list = fskb2;
-               }
-
 skip_fraglist:
                nskb->data_len = len - hsize;
                nskb->len += nskb->data_len;
index 4e66bf6..5325af8 100644 (file)
@@ -90,8 +90,8 @@ static struct genl_family hsr_genl_family = {
        .maxattr = HSR_A_MAX,
 };
 
-static struct genl_multicast_group hsr_network_genl_mcgrp = {
-       .name = "hsr-network",
+static const struct genl_multicast_group hsr_mcgrps[] = {
+       { .name = "hsr-network", },
 };
 
 
@@ -129,7 +129,7 @@ void hsr_nl_ringerror(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
                goto nla_put_failure;
 
        genlmsg_end(skb, msg_head);
-       genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC);
+       genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC);
 
        return;
 
@@ -163,7 +163,7 @@ void hsr_nl_nodedown(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN])
                goto nla_put_failure;
 
        genlmsg_end(skb, msg_head);
-       genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC);
+       genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC);
 
        return;
 
@@ -249,7 +249,7 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
                        &hsr_node_if2_age,
                        &hsr_node_if2_seq);
        if (res < 0)
-               goto fail;
+               goto nla_put_failure;
 
        res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN,
                                        nla_data(info->attrs[HSR_A_NODE_ADDR]));
@@ -306,15 +306,6 @@ fail:
        return res;
 }
 
-static struct genl_ops hsr_ops_get_node_status = {
-       .cmd = HSR_C_GET_NODE_STATUS,
-       .flags = 0,
-       .policy = hsr_genl_policy,
-       .doit = hsr_get_node_status,
-       .dumpit = NULL,
-};
-
-
 /* Get a list of MacAddressA of all nodes known to this node (other than self).
  */
 static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
@@ -398,12 +389,21 @@ fail:
 }
 
 
-static struct genl_ops hsr_ops_get_node_list = {
-       .cmd = HSR_C_GET_NODE_LIST,
-       .flags = 0,
-       .policy = hsr_genl_policy,
-       .doit = hsr_get_node_list,
-       .dumpit = NULL,
+static const struct genl_ops hsr_ops[] = {
+       {
+               .cmd = HSR_C_GET_NODE_STATUS,
+               .flags = 0,
+               .policy = hsr_genl_policy,
+               .doit = hsr_get_node_status,
+               .dumpit = NULL,
+       },
+       {
+               .cmd = HSR_C_GET_NODE_LIST,
+               .flags = 0,
+               .policy = hsr_genl_policy,
+               .doit = hsr_get_node_list,
+               .dumpit = NULL,
+       },
 };
 
 int __init hsr_netlink_init(void)
@@ -414,30 +414,13 @@ int __init hsr_netlink_init(void)
        if (rc)
                goto fail_rtnl_link_register;
 
-       rc = genl_register_family(&hsr_genl_family);
+       rc = genl_register_family_with_ops_groups(&hsr_genl_family, hsr_ops,
+                                                 hsr_mcgrps);
        if (rc)
                goto fail_genl_register_family;
 
-       rc = genl_register_ops(&hsr_genl_family, &hsr_ops_get_node_status);
-       if (rc)
-               goto fail_genl_register_ops;
-
-       rc = genl_register_ops(&hsr_genl_family, &hsr_ops_get_node_list);
-       if (rc)
-               goto fail_genl_register_ops_node_list;
-
-       rc = genl_register_mc_group(&hsr_genl_family, &hsr_network_genl_mcgrp);
-       if (rc)
-               goto fail_genl_register_mc_group;
-
        return 0;
 
-fail_genl_register_mc_group:
-       genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_list);
-fail_genl_register_ops_node_list:
-       genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_status);
-fail_genl_register_ops:
-       genl_unregister_family(&hsr_genl_family);
 fail_genl_register_family:
        rtnl_link_unregister(&hsr_link_ops);
 fail_rtnl_link_register:
@@ -447,10 +430,7 @@ fail_rtnl_link_register:
 
 void __exit hsr_netlink_exit(void)
 {
-       genl_unregister_mc_group(&hsr_genl_family, &hsr_network_genl_mcgrp);
-       genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_status);
        genl_unregister_family(&hsr_genl_family);
-
        rtnl_link_unregister(&hsr_link_ops);
 }
 
index 426b5df..459e200 100644 (file)
@@ -956,7 +956,7 @@ lowpan_process_data(struct sk_buff *skb)
         * Traffic class carried in-line
         * ECN + DSCP (1 byte), Flow Label is elided
         */
-       case 1: /* 10b */
+       case 2: /* 10b */
                if (lowpan_fetch_skb_u8(skb, &tmp))
                        goto drop;
 
@@ -967,7 +967,7 @@ lowpan_process_data(struct sk_buff *skb)
         * Flow Label carried in-line
         * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
         */
-       case 2: /* 01b */
+       case 1: /* 01b */
                if (lowpan_fetch_skb_u8(skb, &tmp))
                        goto drop;
 
index 581a595..1865fdf 100644 (file)
@@ -315,9 +315,8 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
        if (saddr) {
                saddr->family = AF_IEEE802154;
                saddr->addr = mac_cb(skb)->sa;
-       }
-       if (addr_len)
                *addr_len = sizeof(*saddr);
+       }
 
        if (flags & MSG_TRUNC)
                copied = skb->len;
index aadec42..cee4425 100644 (file)
@@ -47,7 +47,24 @@ struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,
 int ieee802154_nl_reply(struct sk_buff *msg, struct genl_info *info);
 
 extern struct genl_family nl802154_family;
-int nl802154_mac_register(void);
-int nl802154_phy_register(void);
+
+/* genetlink ops/groups */
+int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb);
+int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info);
+
+enum ieee802154_mcgrp_ids {
+       IEEE802154_COORD_MCGRP,
+       IEEE802154_BEACON_MCGRP,
+};
+
+int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb);
 
 #endif
index 7e49bbc..43f1b2b 100644 (file)
@@ -70,7 +70,7 @@ int ieee802154_nl_mcast(struct sk_buff *msg, unsigned int group)
        if (genlmsg_end(msg, hdr) < 0)
                goto out;
 
-       return genlmsg_multicast(msg, 0, group, GFP_ATOMIC);
+       return genlmsg_multicast(&nl802154_family, msg, 0, group, GFP_ATOMIC);
 out:
        nlmsg_free(msg);
        return -ENOBUFS;
@@ -109,31 +109,36 @@ out:
        return -ENOBUFS;
 }
 
-int __init ieee802154_nl_init(void)
-{
-       int rc;
-
-       rc = genl_register_family(&nl802154_family);
-       if (rc)
-               goto fail;
-
-       rc = nl802154_mac_register();
-       if (rc)
-               goto fail;
+static const struct genl_ops ieee8021154_ops[] = {
+       /* see nl-phy.c */
+       IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy,
+                       ieee802154_dump_phy),
+       IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface),
+       IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface),
+       /* see nl-mac.c */
+       IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req),
+       IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp),
+       IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req),
+       IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req),
+       IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req),
+       IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
+                       ieee802154_dump_iface),
+};
 
-       rc = nl802154_phy_register();
-       if (rc)
-               goto fail;
+static const struct genl_multicast_group ieee802154_mcgrps[] = {
+       [IEEE802154_COORD_MCGRP] = { .name = IEEE802154_MCAST_COORD_NAME, },
+       [IEEE802154_BEACON_MCGRP] = { .name = IEEE802154_MCAST_BEACON_NAME, },
+};
 
-       return 0;
 
-fail:
-       genl_unregister_family(&nl802154_family);
-       return rc;
+int __init ieee802154_nl_init(void)
+{
+       return genl_register_family_with_ops_groups(&nl802154_family,
+                                                   ieee8021154_ops,
+                                                   ieee802154_mcgrps);
 }
 
 void __exit ieee802154_nl_exit(void)
 {
        genl_unregister_family(&nl802154_family);
 }
-
index b0bdd8c..ba5c1e0 100644 (file)
 
 #include "ieee802154.h"
 
-static struct genl_multicast_group ieee802154_coord_mcgrp = {
-       .name           = IEEE802154_MCAST_COORD_NAME,
-};
-
-static struct genl_multicast_group ieee802154_beacon_mcgrp = {
-       .name           = IEEE802154_MCAST_BEACON_NAME,
-};
-
 int ieee802154_nl_assoc_indic(struct net_device *dev,
                struct ieee802154_addr *addr, u8 cap)
 {
@@ -72,7 +64,7 @@ int ieee802154_nl_assoc_indic(struct net_device *dev,
            nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
                goto nla_put_failure;
 
-       return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
+       return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
 
 nla_put_failure:
        nlmsg_free(msg);
@@ -98,7 +90,7 @@ int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,
            nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
            nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
                goto nla_put_failure;
-       return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
+       return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
 
 nla_put_failure:
        nlmsg_free(msg);
@@ -133,7 +125,7 @@ int ieee802154_nl_disassoc_indic(struct net_device *dev,
        }
        if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
                goto nla_put_failure;
-       return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
+       return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
 
 nla_put_failure:
        nlmsg_free(msg);
@@ -157,7 +149,7 @@ int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
                    dev->dev_addr) ||
            nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
                goto nla_put_failure;
-       return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
+       return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
 
 nla_put_failure:
        nlmsg_free(msg);
@@ -183,7 +175,7 @@ int ieee802154_nl_beacon_indic(struct net_device *dev,
            nla_put_u16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr) ||
            nla_put_u16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
                goto nla_put_failure;
-       return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
+       return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
 
 nla_put_failure:
        nlmsg_free(msg);
@@ -214,7 +206,7 @@ int ieee802154_nl_scan_confirm(struct net_device *dev,
            (edl &&
             nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
                goto nla_put_failure;
-       return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
+       return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
 
 nla_put_failure:
        nlmsg_free(msg);
@@ -238,7 +230,7 @@ int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
                    dev->dev_addr) ||
            nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
                goto nla_put_failure;
-       return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
+       return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
 
 nla_put_failure:
        nlmsg_free(msg);
@@ -309,8 +301,7 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
        return dev;
 }
 
-static int ieee802154_associate_req(struct sk_buff *skb,
-               struct genl_info *info)
+int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info)
 {
        struct net_device *dev;
        struct ieee802154_addr addr;
@@ -357,8 +348,7 @@ out:
        return ret;
 }
 
-static int ieee802154_associate_resp(struct sk_buff *skb,
-               struct genl_info *info)
+int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info)
 {
        struct net_device *dev;
        struct ieee802154_addr addr;
@@ -390,8 +380,7 @@ out:
        return ret;
 }
 
-static int ieee802154_disassociate_req(struct sk_buff *skb,
-               struct genl_info *info)
+int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info)
 {
        struct net_device *dev;
        struct ieee802154_addr addr;
@@ -433,7 +422,7 @@ out:
  * PAN_coordinator, battery_life_extension = 0,
  * coord_realignment = 0, security_enable = 0
 */
-static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
+int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
 {
        struct net_device *dev;
        struct ieee802154_addr addr;
@@ -492,7 +481,7 @@ out:
        return ret;
 }
 
-static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
+int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
 {
        struct net_device *dev;
        int ret = -EOPNOTSUPP;
@@ -530,8 +519,7 @@ out:
        return ret;
 }
 
-static int ieee802154_list_iface(struct sk_buff *skb,
-       struct genl_info *info)
+int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info)
 {
        /* Request for interface name, index, type, IEEE address,
           PAN Id, short address */
@@ -565,8 +553,7 @@ out_dev:
 
 }
 
-static int ieee802154_dump_iface(struct sk_buff *skb,
-       struct netlink_callback *cb)
+int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct net *net = sock_net(skb->sk);
        struct net_device *dev;
@@ -590,41 +577,3 @@ cont:
 
        return skb->len;
 }
-
-static struct genl_ops ieee802154_coordinator_ops[] = {
-       IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req),
-       IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp),
-       IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req),
-       IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req),
-       IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req),
-       IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
-                                                       ieee802154_dump_iface),
-};
-
-/*
- * No need to unregister as family unregistration will do it.
- */
-int nl802154_mac_register(void)
-{
-       int i;
-       int rc;
-
-       rc = genl_register_mc_group(&nl802154_family,
-                       &ieee802154_coord_mcgrp);
-       if (rc)
-               return rc;
-
-       rc = genl_register_mc_group(&nl802154_family,
-                       &ieee802154_beacon_mcgrp);
-       if (rc)
-               return rc;
-
-       for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) {
-               rc = genl_register_ops(&nl802154_family,
-                               &ieee802154_coordinator_ops[i]);
-               if (rc)
-                       return rc;
-       }
-
-       return 0;
-}
index 22b1a70..d08c7a4 100644 (file)
@@ -77,8 +77,7 @@ out:
        return -EMSGSIZE;
 }
 
-static int ieee802154_list_phy(struct sk_buff *skb,
-       struct genl_info *info)
+int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info)
 {
        /* Request for interface name, index, type, IEEE address,
           PAN Id, short address */
@@ -151,8 +150,7 @@ static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
        return 0;
 }
 
-static int ieee802154_dump_phy(struct sk_buff *skb,
-       struct netlink_callback *cb)
+int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct dump_phy_data data = {
                .cb = cb,
@@ -170,8 +168,7 @@ static int ieee802154_dump_phy(struct sk_buff *skb,
        return skb->len;
 }
 
-static int ieee802154_add_iface(struct sk_buff *skb,
-               struct genl_info *info)
+int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
 {
        struct sk_buff *msg;
        struct wpan_phy *phy;
@@ -273,8 +270,7 @@ out_dev:
        return rc;
 }
 
-static int ieee802154_del_iface(struct sk_buff *skb,
-               struct genl_info *info)
+int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
 {
        struct sk_buff *msg;
        struct wpan_phy *phy;
@@ -356,28 +352,3 @@ out_dev:
 
        return rc;
 }
-
-static struct genl_ops ieee802154_phy_ops[] = {
-       IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy,
-                                                       ieee802154_dump_phy),
-       IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface),
-       IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface),
-};
-
-/*
- * No need to unregister as family unregistration will do it.
- */
-int nl802154_phy_register(void)
-{
-       int i;
-       int rc;
-
-       for (i = 0; i < ARRAY_SIZE(ieee802154_phy_ops); i++) {
-               rc = genl_register_ops(&nl802154_family,
-                               &ieee802154_phy_ops[i]);
-               if (rc)
-                       return rc;
-       }
-
-       return 0;
-}
index b28e863..19e3637 100644 (file)
@@ -57,7 +57,7 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        if (IS_ERR(rt)) {
                err = PTR_ERR(rt);
                if (err == -ENETUNREACH)
-                       IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
+                       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
                goto out;
        }
 
index ec9a9ef..5afeb5a 100644 (file)
@@ -2523,16 +2523,17 @@ static int fib_route_seq_show(struct seq_file *seq, void *v)
                list_for_each_entry_rcu(fa, &li->falh, fa_list) {
                        const struct fib_info *fi = fa->fa_info;
                        unsigned int flags = fib_flag_trans(fa->fa_type, mask, fi);
-                       int len;
 
                        if (fa->fa_type == RTN_BROADCAST
                            || fa->fa_type == RTN_MULTICAST)
                                continue;
 
+                       seq_setwidth(seq, 127);
+
                        if (fi)
                                seq_printf(seq,
                                         "%s\t%08X\t%08X\t%04X\t%d\t%u\t"
-                                        "%d\t%08X\t%d\t%u\t%u%n",
+                                        "%d\t%08X\t%d\t%u\t%u",
                                         fi->fib_dev ? fi->fib_dev->name : "*",
                                         prefix,
                                         fi->fib_nh->nh_gw, flags, 0, 0,
@@ -2541,15 +2542,15 @@ static int fib_route_seq_show(struct seq_file *seq, void *v)
                                         (fi->fib_advmss ?
                                          fi->fib_advmss + 40 : 0),
                                         fi->fib_window,
-                                        fi->fib_rtt >> 3, &len);
+                                        fi->fib_rtt >> 3);
                        else
                                seq_printf(seq,
                                         "*\t%08X\t%08X\t%04X\t%d\t%u\t"
-                                        "%d\t%08X\t%d\t%u\t%u%n",
+                                        "%d\t%08X\t%d\t%u\t%u",
                                         prefix, 0, flags, 0, 0, 0,
-                                        mask, 0, 0, 0, &len);
+                                        mask, 0, 0, 0);
 
-                       seq_printf(seq, "%*s\n", 127 - len, "");
+                       seq_pad(seq, '\n');
                }
        }
 
index caf0117..90ff957 100644 (file)
@@ -454,6 +454,8 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
        tstats->rx_bytes += skb->len;
        u64_stats_update_end(&tstats->syncp);
 
+       skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(tunnel->dev)));
+
        if (tunnel->dev->type == ARPHRD_ETHER) {
                skb->protocol = eth_type_trans(skb, tunnel->dev);
                skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
@@ -461,8 +463,6 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
                skb->dev = tunnel->dev;
        }
 
-       skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(tunnel->dev)));
-
        gro_cells_receive(&tunnel->gro_cells, skb);
        return 0;
 
index 5d9c845..52b802a 100644 (file)
@@ -126,6 +126,7 @@ static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        if (!rt->dst.xfrm ||
            rt->dst.xfrm->props.mode != XFRM_MODE_TUNNEL) {
                dev->stats.tx_carrier_errors++;
+               ip_rt_put(rt);
                goto tx_error_icmp;
        }
        tdev = rt->dst.dev;
index 01cffea..f13bd91 100644 (file)
@@ -244,6 +244,7 @@ synproxy_recv_client_ack(const struct synproxy_net *snet,
 
        this_cpu_inc(snet->stats->cookie_valid);
        opts->mss = mss;
+       opts->options |= XT_SYNPROXY_OPT_MSS;
 
        if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
                synproxy_check_timestamp_cookie(opts);
index 9afbdb1..876c6ca 100644 (file)
@@ -830,8 +830,6 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 {
        struct inet_sock *isk = inet_sk(sk);
        int family = sk->sk_family;
-       struct sockaddr_in *sin;
-       struct sockaddr_in6 *sin6;
        struct sk_buff *skb;
        int copied, err;
 
@@ -841,13 +839,6 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        if (flags & MSG_OOB)
                goto out;
 
-       if (addr_len) {
-               if (family == AF_INET)
-                       *addr_len = sizeof(*sin);
-               else if (family == AF_INET6 && addr_len)
-                       *addr_len = sizeof(*sin6);
-       }
-
        if (flags & MSG_ERRQUEUE) {
                if (family == AF_INET) {
                        return ip_recv_error(sk, msg, len);
@@ -877,11 +868,15 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 
        /* Copy the address and add cmsg data. */
        if (family == AF_INET) {
-               sin = (struct sockaddr_in *) msg->msg_name;
-               sin->sin_family = AF_INET;
-               sin->sin_port = 0 /* skb->h.uh->source */;
-               sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+               struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
+
+               if (sin) {
+                       sin->sin_family = AF_INET;
+                       sin->sin_port = 0 /* skb->h.uh->source */;
+                       sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
+                       memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+                       *addr_len = sizeof(*sin);
+               }
 
                if (isk->cmsg_flags)
                        ip_cmsg_recv(msg, skb);
@@ -890,17 +885,21 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        } else if (family == AF_INET6) {
                struct ipv6_pinfo *np = inet6_sk(sk);
                struct ipv6hdr *ip6 = ipv6_hdr(skb);
-               sin6 = (struct sockaddr_in6 *) msg->msg_name;
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = 0;
-               sin6->sin6_addr = ip6->saddr;
-
-               sin6->sin6_flowinfo = 0;
-               if (np->sndflow)
-                       sin6->sin6_flowinfo = ip6_flowinfo(ip6);
-
-               sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,
-                                                         IP6CB(skb)->iif);
+               struct sockaddr_in6 *sin6 =
+                       (struct sockaddr_in6 *)msg->msg_name;
+
+               if (sin6) {
+                       sin6->sin6_family = AF_INET6;
+                       sin6->sin6_port = 0;
+                       sin6->sin6_addr = ip6->saddr;
+                       sin6->sin6_flowinfo = 0;
+                       if (np->sndflow)
+                               sin6->sin6_flowinfo = ip6_flowinfo(ip6);
+                       sin6->sin6_scope_id =
+                               ipv6_iface_scope_id(&sin6->sin6_addr,
+                                                   IP6CB(skb)->iif);
+                       *addr_len = sizeof(*sin6);
+               }
 
                if (inet6_sk(sk)->rxopt.all)
                        pingv6_ops.ip6_datagram_recv_ctl(sk, msg, skb);
@@ -1076,7 +1075,7 @@ void ping_seq_stop(struct seq_file *seq, void *v)
 EXPORT_SYMBOL_GPL(ping_seq_stop);
 
 static void ping_v4_format_sock(struct sock *sp, struct seq_file *f,
-               int bucket, int *len)
+               int bucket)
 {
        struct inet_sock *inet = inet_sk(sp);
        __be32 dest = inet->inet_daddr;
@@ -1085,7 +1084,7 @@ static void ping_v4_format_sock(struct sock *sp, struct seq_file *f,
        __u16 srcp = ntohs(inet->inet_sport);
 
        seq_printf(f, "%5d: %08X:%04X %08X:%04X"
-               " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %d%n",
+               " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %d",
                bucket, src, srcp, dest, destp, sp->sk_state,
                sk_wmem_alloc_get(sp),
                sk_rmem_alloc_get(sp),
@@ -1093,23 +1092,22 @@ static void ping_v4_format_sock(struct sock *sp, struct seq_file *f,
                from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
                0, sock_i_ino(sp),
                atomic_read(&sp->sk_refcnt), sp,
-               atomic_read(&sp->sk_drops), len);
+               atomic_read(&sp->sk_drops));
 }
 
 static int ping_v4_seq_show(struct seq_file *seq, void *v)
 {
+       seq_setwidth(seq, 127);
        if (v == SEQ_START_TOKEN)
-               seq_printf(seq, "%-127s\n",
-                          "  sl  local_address rem_address   st tx_queue "
+               seq_puts(seq, "  sl  local_address rem_address   st tx_queue "
                           "rx_queue tr tm->when retrnsmt   uid  timeout "
                           "inode ref pointer drops");
        else {
                struct ping_iter_state *state = seq->private;
-               int len;
 
-               ping_v4_format_sock(v, seq, state->bucket, &len);
-               seq_printf(seq, "%*s\n", 127 - len, "");
+               ping_v4_format_sock(v, seq, state->bucket);
        }
+       seq_pad(seq, '\n');
        return 0;
 }
 
index 41e1d28..5cb8ddb 100644 (file)
@@ -696,9 +696,6 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        if (flags & MSG_OOB)
                goto out;
 
-       if (addr_len)
-               *addr_len = sizeof(*sin);
-
        if (flags & MSG_ERRQUEUE) {
                err = ip_recv_error(sk, msg, len);
                goto out;
@@ -726,6 +723,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
                sin->sin_port = 0;
                memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
+               *addr_len = sizeof(*sin);
        }
        if (inet->cmsg_flags)
                ip_cmsg_recv(msg, skb);
index f428935..f8da282 100644 (file)
@@ -1776,8 +1776,12 @@ local_input:
                rth->dst.error= -err;
                rth->rt_flags   &= ~RTCF_LOCAL;
        }
-       if (do_cache)
-               rt_cache_route(&FIB_RES_NH(res), rth);
+       if (do_cache) {
+               if (unlikely(!rt_cache_route(&FIB_RES_NH(res), rth))) {
+                       rth->dst.flags |= DST_NOCACHE;
+                       rt_add_uncached_list(rth);
+               }
+       }
        skb_dst_set(skb, &rth->dst);
        err = 0;
        goto out;
index 8e8529d..c4638e6 100644 (file)
@@ -808,12 +808,6 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now,
                xmit_size_goal = min_t(u32, gso_size,
                                       sk->sk_gso_max_size - 1 - hlen);
 
-               /* TSQ : try to have at least two segments in flight
-                * (one in NIC TX ring, another in Qdisc)
-                */
-               xmit_size_goal = min_t(u32, xmit_size_goal,
-                                      sysctl_tcp_limit_output_bytes >> 1);
-
                xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal);
 
                /* We try hard to avoid divides here */
@@ -1431,7 +1425,7 @@ static void tcp_service_net_dma(struct sock *sk, bool wait)
        do {
                if (dma_async_is_tx_complete(tp->ucopy.dma_chan,
                                              last_issued, &done,
-                                             &used) == DMA_SUCCESS) {
+                                             &used) == DMA_COMPLETE) {
                        /* Safe to free early-copied skbs now */
                        __skb_queue_purge(&sk->sk_async_wait_queue);
                        break;
@@ -1439,7 +1433,7 @@ static void tcp_service_net_dma(struct sock *sk, bool wait)
                        struct sk_buff *skb;
                        while ((skb = skb_peek(&sk->sk_async_wait_queue)) &&
                               (dma_async_is_complete(skb->dma_cookie, done,
-                                                     used) == DMA_SUCCESS)) {
+                                                     used) == DMA_COMPLETE)) {
                                __skb_dequeue(&sk->sk_async_wait_queue);
                                kfree_skb(skb);
                        }
index 14bba8a..59a6f8b 100644 (file)
@@ -2541,13 +2541,13 @@ void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo)
 EXPORT_SYMBOL(tcp_proc_unregister);
 
 static void get_openreq4(const struct sock *sk, const struct request_sock *req,
-                        struct seq_file *f, int i, kuid_t uid, int *len)
+                        struct seq_file *f, int i, kuid_t uid)
 {
        const struct inet_request_sock *ireq = inet_rsk(req);
        long delta = req->expires - jiffies;
 
        seq_printf(f, "%4d: %08X:%04X %08X:%04X"
-               " %02X %08X:%08X %02X:%08lX %08X %5u %8d %u %d %pK%n",
+               " %02X %08X:%08X %02X:%08lX %08X %5u %8d %u %d %pK",
                i,
                ireq->ir_loc_addr,
                ntohs(inet_sk(sk)->inet_sport),
@@ -2562,11 +2562,10 @@ static void get_openreq4(const struct sock *sk, const struct request_sock *req,
                0,  /* non standard timer */
                0, /* open_requests have no inode */
                atomic_read(&sk->sk_refcnt),
-               req,
-               len);
+               req);
 }
 
-static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
+static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
 {
        int timer_active;
        unsigned long timer_expires;
@@ -2605,7 +2604,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
                rx_queue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
 
        seq_printf(f, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX "
-                       "%08X %5u %8d %lu %d %pK %lu %lu %u %u %d%n",
+                       "%08X %5u %8d %lu %d %pK %lu %lu %u %u %d",
                i, src, srcp, dest, destp, sk->sk_state,
                tp->write_seq - tp->snd_una,
                rx_queue,
@@ -2622,12 +2621,11 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
                tp->snd_cwnd,
                sk->sk_state == TCP_LISTEN ?
                    (fastopenq ? fastopenq->max_qlen : 0) :
-                   (tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh),
-               len);
+                   (tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh));
 }
 
 static void get_timewait4_sock(const struct inet_timewait_sock *tw,
-                              struct seq_file *f, int i, int *len)
+                              struct seq_file *f, int i)
 {
        __be32 dest, src;
        __u16 destp, srcp;
@@ -2639,10 +2637,10 @@ static void get_timewait4_sock(const struct inet_timewait_sock *tw,
        srcp  = ntohs(tw->tw_sport);
 
        seq_printf(f, "%4d: %08X:%04X %08X:%04X"
-               " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK%n",
+               " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK",
                i, src, srcp, dest, destp, tw->tw_substate, 0, 0,
                3, jiffies_delta_to_clock_t(delta), 0, 0, 0, 0,
-               atomic_read(&tw->tw_refcnt), tw, len);
+               atomic_read(&tw->tw_refcnt), tw);
 }
 
 #define TMPSZ 150
@@ -2651,11 +2649,10 @@ static int tcp4_seq_show(struct seq_file *seq, void *v)
 {
        struct tcp_iter_state *st;
        struct sock *sk = v;
-       int len;
 
+       seq_setwidth(seq, TMPSZ - 1);
        if (v == SEQ_START_TOKEN) {
-               seq_printf(seq, "%-*s\n", TMPSZ - 1,
-                          "  sl  local_address rem_address   st tx_queue "
+               seq_puts(seq, "  sl  local_address rem_address   st tx_queue "
                           "rx_queue tr tm->when retrnsmt   uid  timeout "
                           "inode");
                goto out;
@@ -2666,16 +2663,16 @@ static int tcp4_seq_show(struct seq_file *seq, void *v)
        case TCP_SEQ_STATE_LISTENING:
        case TCP_SEQ_STATE_ESTABLISHED:
                if (sk->sk_state == TCP_TIME_WAIT)
-                       get_timewait4_sock(v, seq, st->num, &len);
+                       get_timewait4_sock(v, seq, st->num);
                else
-                       get_tcp4_sock(v, seq, st->num, &len);
+                       get_tcp4_sock(v, seq, st->num);
                break;
        case TCP_SEQ_STATE_OPENREQ:
-               get_openreq4(st->syn_wait_sk, v, seq, st->num, st->uid, &len);
+               get_openreq4(st->syn_wait_sk, v, seq, st->num, st->uid);
                break;
        }
-       seq_printf(seq, "%*s\n", TMPSZ - 1 - len, "");
 out:
+       seq_pad(seq, '\n');
        return 0;
 }
 
index 2ab09cb..0649373 100644 (file)
@@ -663,10 +663,13 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
 void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
                            struct tcp_fastopen_cookie *cookie, bool syn_lost)
 {
+       struct dst_entry *dst = __sk_dst_get(sk);
        struct tcp_metrics_block *tm;
 
+       if (!dst)
+               return;
        rcu_read_lock();
-       tm = tcp_get_metrics(sk, __sk_dst_get(sk), true);
+       tm = tcp_get_metrics(sk, dst, true);
        if (tm) {
                struct tcp_fastopen_metrics *tfom = &tm->tcpm_fastopen;
 
@@ -988,7 +991,7 @@ static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info)
        return 0;
 }
 
-static struct genl_ops tcp_metrics_nl_ops[] = {
+static const struct genl_ops tcp_metrics_nl_ops[] = {
        {
                .cmd = TCP_METRICS_CMD_GET,
                .doit = tcp_metrics_nl_cmd_get,
@@ -1079,8 +1082,7 @@ void __init tcp_metrics_init(void)
        if (ret < 0)
                goto cleanup;
        ret = genl_register_family_with_ops(&tcp_metrics_nl_family,
-                                           tcp_metrics_nl_ops,
-                                           ARRAY_SIZE(tcp_metrics_nl_ops));
+                                           tcp_metrics_nl_ops);
        if (ret < 0)
                goto cleanup_subsys;
        return;
index 6728546..7820f3a 100644 (file)
@@ -1875,8 +1875,12 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                 *  - better RTT estimation and ACK scheduling
                 *  - faster recovery
                 *  - high rates
+                * Alas, some drivers / subsystems require a fair amount
+                * of queued bytes to ensure line rate.
+                * One example is wifi aggregation (802.11 AMPDU)
                 */
-               limit = max(skb->truesize, sk->sk_pacing_rate >> 10);
+               limit = max_t(unsigned int, sysctl_tcp_limit_output_bytes,
+                             sk->sk_pacing_rate >> 10);
 
                if (atomic_read(&sk->sk_wmem_alloc) > limit) {
                        set_bit(TSQ_THROTTLED, &tp->tsq_flags);
@@ -3093,7 +3097,6 @@ void tcp_send_window_probe(struct sock *sk)
 {
        if (sk->sk_state == TCP_ESTABLISHED) {
                tcp_sk(sk)->snd_wl1 = tcp_sk(sk)->rcv_nxt - 1;
-               tcp_sk(sk)->snd_nxt = tcp_sk(sk)->write_seq;
                tcp_xmit_probe_skb(sk, 0);
        }
 }
index 89909dd..5944d7d 100644 (file)
@@ -1235,12 +1235,6 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        int is_udplite = IS_UDPLITE(sk);
        bool slow;
 
-       /*
-        *      Check any passed addresses
-        */
-       if (addr_len)
-               *addr_len = sizeof(*sin);
-
        if (flags & MSG_ERRQUEUE)
                return ip_recv_error(sk, msg, len);
 
@@ -1302,6 +1296,7 @@ try_again:
                sin->sin_port = udp_hdr(skb)->source;
                sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+               *addr_len = sizeof(*sin);
        }
        if (inet->cmsg_flags)
                ip_cmsg_recv(msg, skb);
@@ -2331,7 +2326,7 @@ EXPORT_SYMBOL(udp_proc_unregister);
 
 /* ------------------------------------------------------------------------ */
 static void udp4_format_sock(struct sock *sp, struct seq_file *f,
-               int bucket, int *len)
+               int bucket)
 {
        struct inet_sock *inet = inet_sk(sp);
        __be32 dest = inet->inet_daddr;
@@ -2340,7 +2335,7 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f,
        __u16 srcp        = ntohs(inet->inet_sport);
 
        seq_printf(f, "%5d: %08X:%04X %08X:%04X"
-               " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %d%n",
+               " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %d",
                bucket, src, srcp, dest, destp, sp->sk_state,
                sk_wmem_alloc_get(sp),
                sk_rmem_alloc_get(sp),
@@ -2348,23 +2343,22 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f,
                from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
                0, sock_i_ino(sp),
                atomic_read(&sp->sk_refcnt), sp,
-               atomic_read(&sp->sk_drops), len);
+               atomic_read(&sp->sk_drops));
 }
 
 int udp4_seq_show(struct seq_file *seq, void *v)
 {
+       seq_setwidth(seq, 127);
        if (v == SEQ_START_TOKEN)
-               seq_printf(seq, "%-127s\n",
-                          "  sl  local_address rem_address   st tx_queue "
+               seq_puts(seq, "  sl  local_address rem_address   st tx_queue "
                           "rx_queue tr tm->when retrnsmt   uid  timeout "
                           "inode ref pointer drops");
        else {
                struct udp_iter_state *state = seq->private;
-               int len;
 
-               udp4_format_sock(v, seq, state->bucket, &len);
-               seq_printf(seq, "%*s\n", 127 - len, "");
+               udp4_format_sock(v, seq, state->bucket);
        }
+       seq_pad(seq, '\n');
        return 0;
 }
 
index 5658d9d..12c97d8 100644 (file)
@@ -1996,23 +1996,6 @@ static void addrconf_add_mroute(struct net_device *dev)
        ip6_route_add(&cfg);
 }
 
-#if IS_ENABLED(CONFIG_IPV6_SIT)
-static void sit_route_add(struct net_device *dev)
-{
-       struct fib6_config cfg = {
-               .fc_table = RT6_TABLE_MAIN,
-               .fc_metric = IP6_RT_PRIO_ADDRCONF,
-               .fc_ifindex = dev->ifindex,
-               .fc_dst_len = 96,
-               .fc_flags = RTF_UP | RTF_NONEXTHOP,
-               .fc_nlinfo.nl_net = dev_net(dev),
-       };
-
-       /* prefix length - 96 bits "::d.d.d.d" */
-       ip6_route_add(&cfg);
-}
-#endif
-
 static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
 {
        struct inet6_dev *idev;
@@ -2542,7 +2525,8 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
        struct in6_addr addr;
        struct net_device *dev;
        struct net *net = dev_net(idev->dev);
-       int scope;
+       int scope, plen;
+       u32 pflags = 0;
 
        ASSERT_RTNL();
 
@@ -2552,12 +2536,16 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
        if (idev->dev->flags&IFF_POINTOPOINT) {
                addr.s6_addr32[0] = htonl(0xfe800000);
                scope = IFA_LINK;
+               plen = 64;
        } else {
                scope = IPV6_ADDR_COMPATv4;
+               plen = 96;
+               pflags |= RTF_NONEXTHOP;
        }
 
        if (addr.s6_addr32[3]) {
-               add_addr(idev, &addr, 128, scope);
+               add_addr(idev, &addr, plen, scope);
+               addrconf_prefix_route(&addr, plen, idev->dev, 0, pflags);
                return;
        }
 
@@ -2569,7 +2557,6 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
                        int flag = scope;
 
                        for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
-                               int plen;
 
                                addr.s6_addr32[3] = ifa->ifa_local;
 
@@ -2580,12 +2567,10 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
                                                continue;
                                        flag |= IFA_HOST;
                                }
-                               if (idev->dev->flags&IFF_POINTOPOINT)
-                                       plen = 64;
-                               else
-                                       plen = 96;
 
                                add_addr(idev, &addr, plen, flag);
+                               addrconf_prefix_route(&addr, plen, idev->dev, 0,
+                                                     pflags);
                        }
                }
        }
@@ -2711,7 +2696,6 @@ static void addrconf_sit_config(struct net_device *dev)
                struct in6_addr addr;
 
                ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0);
-               addrconf_prefix_route(&addr, 64, dev, 0, 0);
                if (!ipv6_generate_eui64(addr.s6_addr + 8, dev))
                        addrconf_add_linklocal(idev, &addr);
                return;
@@ -2721,8 +2705,6 @@ static void addrconf_sit_config(struct net_device *dev)
 
        if (dev->flags&IFF_POINTOPOINT)
                addrconf_add_mroute(dev);
-       else
-               sit_route_add(dev);
 }
 #endif
 
@@ -2740,8 +2722,6 @@ static void addrconf_gre_config(struct net_device *dev)
        }
 
        ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0);
-       addrconf_prefix_route(&addr, 64, dev, 0, 0);
-
        if (!ipv6_generate_eui64(addr.s6_addr + 8, dev))
                addrconf_add_linklocal(idev, &addr);
 }
index ff75313..4fbdb70 100644 (file)
@@ -972,10 +972,10 @@ out:
 
 #ifdef CONFIG_SYSCTL
 sysctl_fail:
-       ipv6_packet_cleanup();
+       pingv6_exit();
 #endif
 pingv6_fail:
-       pingv6_exit();
+       ipv6_packet_cleanup();
 ipv6_packet_fail:
        tcpv6_exit();
 tcpv6_fail:
index df1fa58..d606232 100644 (file)
@@ -1642,6 +1642,15 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
        return ip6_tnl_update(t, &p);
 }
 
+static void ip6_tnl_dellink(struct net_device *dev, struct list_head *head)
+{
+       struct net *net = dev_net(dev);
+       struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+
+       if (dev != ip6n->fb_tnl_dev)
+               unregister_netdevice_queue(dev, head);
+}
+
 static size_t ip6_tnl_get_size(const struct net_device *dev)
 {
        return
@@ -1706,6 +1715,7 @@ static struct rtnl_link_ops ip6_link_ops __read_mostly = {
        .validate       = ip6_tnl_validate,
        .newlink        = ip6_tnl_newlink,
        .changelink     = ip6_tnl_changelink,
+       .dellink        = ip6_tnl_dellink,
        .get_size       = ip6_tnl_get_size,
        .fill_info      = ip6_tnl_fill_info,
 };
@@ -1722,9 +1732,9 @@ static struct xfrm6_tunnel ip6ip6_handler __read_mostly = {
        .priority       =       1,
 };
 
-static void __net_exit ip6_tnl_destroy_tunnels(struct ip6_tnl_net *ip6n)
+static void __net_exit ip6_tnl_destroy_tunnels(struct net *net)
 {
-       struct net *net = dev_net(ip6n->fb_tnl_dev);
+       struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
        struct net_device *dev, *aux;
        int h;
        struct ip6_tnl *t;
@@ -1792,10 +1802,8 @@ err_alloc_dev:
 
 static void __net_exit ip6_tnl_exit_net(struct net *net)
 {
-       struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
-
        rtnl_lock();
-       ip6_tnl_destroy_tunnels(ip6n);
+       ip6_tnl_destroy_tunnels(net);
        rtnl_unlock();
 }
 
index f8a55ff..3512177 100644 (file)
@@ -1726,8 +1726,8 @@ int __init ndisc_init(void)
                                    &ndisc_ifinfo_sysctl_change);
        if (err)
                goto out_unregister_pernet;
-#endif
 out:
+#endif
        return err;
 
 #ifdef CONFIG_SYSCTL
index bf9f612..f78f41a 100644 (file)
@@ -259,6 +259,7 @@ synproxy_recv_client_ack(const struct synproxy_net *snet,
 
        this_cpu_inc(snet->stats->cookie_valid);
        opts->mss = mss;
+       opts->options |= XT_SYNPROXY_OPT_MSS;
 
        if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
                synproxy_check_timestamp_cookie(opts);
index 3c00842..e24ff1d 100644 (file)
@@ -465,9 +465,6 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
        if (flags & MSG_OOB)
                return -EOPNOTSUPP;
 
-       if (addr_len)
-               *addr_len=sizeof(*sin6);
-
        if (flags & MSG_ERRQUEUE)
                return ipv6_recv_error(sk, msg, len);
 
@@ -506,6 +503,7 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
                sin6->sin6_flowinfo = 0;
                sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,
                                                          IP6CB(skb)->iif);
+               *addr_len = sizeof(*sin6);
        }
 
        sock_recv_ts_and_drops(msg, sk, skb);
index bfc6fce..1b4a4a9 100644 (file)
@@ -1619,6 +1619,15 @@ static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = {
 #endif
 };
 
+static void ipip6_dellink(struct net_device *dev, struct list_head *head)
+{
+       struct net *net = dev_net(dev);
+       struct sit_net *sitn = net_generic(net, sit_net_id);
+
+       if (dev != sitn->fb_tunnel_dev)
+               unregister_netdevice_queue(dev, head);
+}
+
 static struct rtnl_link_ops sit_link_ops __read_mostly = {
        .kind           = "sit",
        .maxtype        = IFLA_IPTUN_MAX,
@@ -1630,6 +1639,7 @@ static struct rtnl_link_ops sit_link_ops __read_mostly = {
        .changelink     = ipip6_changelink,
        .get_size       = ipip6_get_size,
        .fill_info      = ipip6_fill_info,
+       .dellink        = ipip6_dellink,
 };
 
 static struct xfrm_tunnel sit_handler __read_mostly = {
@@ -1644,9 +1654,10 @@ static struct xfrm_tunnel ipip_handler __read_mostly = {
        .priority       =       2,
 };
 
-static void __net_exit sit_destroy_tunnels(struct sit_net *sitn, struct list_head *head)
+static void __net_exit sit_destroy_tunnels(struct net *net,
+                                          struct list_head *head)
 {
-       struct net *net = dev_net(sitn->fb_tunnel_dev);
+       struct sit_net *sitn = net_generic(net, sit_net_id);
        struct net_device *dev, *aux;
        int prio;
 
@@ -1721,11 +1732,10 @@ err_alloc_dev:
 
 static void __net_exit sit_exit_net(struct net *net)
 {
-       struct sit_net *sitn = net_generic(net, sit_net_id);
        LIST_HEAD(list);
 
        rtnl_lock();
-       sit_destroy_tunnels(sitn, &list);
+       sit_destroy_tunnels(net, &list);
        unregister_netdevice_many(&list);
        rtnl_unlock();
 }
index f3893e8..81eb8cf 100644 (file)
@@ -392,9 +392,6 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
        int is_udp4;
        bool slow;
 
-       if (addr_len)
-               *addr_len = sizeof(struct sockaddr_in6);
-
        if (flags & MSG_ERRQUEUE)
                return ipv6_recv_error(sk, msg, len);
 
@@ -480,7 +477,7 @@ try_again:
                                ipv6_iface_scope_id(&sin6->sin6_addr,
                                                    IP6CB(skb)->iif);
                }
-
+               *addr_len = sizeof(*sin6);
        }
        if (is_udp4) {
                if (inet->cmsg_flags)
index 7a1e0fc..e096025 100644 (file)
@@ -1823,8 +1823,6 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (skb->tstamp.tv64)
                sk->sk_stamp = skb->tstamp;
 
-       msg->msg_namelen = sizeof(*sipx);
-
        if (sipx) {
                sipx->sipx_family       = AF_IPX;
                sipx->sipx_port         = ipx->ipx_source.sock;
@@ -1832,6 +1830,7 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
                sipx->sipx_network      = IPX_SKB_CB(skb)->ipx_source_net;
                sipx->sipx_type         = ipx->ipx_type;
                sipx->sipx_zero         = 0;
+               msg->msg_namelen        = sizeof(*sipx);
        }
        rc = copied;
 
index 0f67690..de7db23 100644 (file)
@@ -1385,8 +1385,6 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock,
 
        IRDA_DEBUG(4, "%s()\n", __func__);
 
-       msg->msg_namelen = 0;
-
        skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
                                flags & MSG_DONTWAIT, &err);
        if (!skb)
@@ -1451,8 +1449,6 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock,
        target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
        timeo = sock_rcvtimeo(sk, noblock);
 
-       msg->msg_namelen = 0;
-
        do {
                int chunk;
                struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
index c329712..a37b81f 100644 (file)
@@ -131,7 +131,7 @@ static const struct nla_policy irda_nl_policy[IRDA_NL_ATTR_MAX + 1] = {
        [IRDA_NL_ATTR_MODE] = { .type = NLA_U32 },
 };
 
-static struct genl_ops irda_nl_ops[] = {
+static const struct genl_ops irda_nl_ops[] = {
        {
                .cmd = IRDA_NL_CMD_SET_MODE,
                .doit = irda_nl_set_mode,
@@ -149,8 +149,7 @@ static struct genl_ops irda_nl_ops[] = {
 
 int irda_nl_register(void)
 {
-       return genl_register_family_with_ops(&irda_nl_family,
-               irda_nl_ops, ARRAY_SIZE(irda_nl_ops));
+       return genl_register_family_with_ops(&irda_nl_family, irda_nl_ops);
 }
 
 void irda_nl_unregister(void)
index 168aff5..c4b7218 100644 (file)
@@ -1324,8 +1324,6 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        int err = 0;
        u32 offset;
 
-       msg->msg_namelen = 0;
-
        if ((sk->sk_state == IUCV_DISCONN) &&
            skb_queue_empty(&iucv->backlog_skb_q) &&
            skb_queue_empty(&sk->sk_receive_queue) &&
index 911ef03..545f047 100644 (file)
@@ -3616,7 +3616,6 @@ static int pfkey_recvmsg(struct kiocb *kiocb,
        if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
                goto out;
 
-       msg->msg_namelen = 0;
        skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
        if (skb == NULL)
                goto out;
index 571db8d..da1a1ce 100644 (file)
@@ -518,9 +518,6 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
        if (flags & MSG_OOB)
                goto out;
 
-       if (addr_len)
-               *addr_len = sizeof(*sin);
-
        skb = skb_recv_datagram(sk, flags, noblock, &err);
        if (!skb)
                goto out;
@@ -543,6 +540,7 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
                sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
                sin->sin_port = 0;
                memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
+               *addr_len = sizeof(*sin);
        }
        if (inet->cmsg_flags)
                ip_cmsg_recv(msg, skb);
index be446d5..4cfd722 100644 (file)
@@ -793,7 +793,7 @@ static struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = {
        },
 };
 
-static struct genl_ops l2tp_nl_ops[] = {
+static const struct genl_ops l2tp_nl_ops[] = {
        {
                .cmd = L2TP_CMD_NOOP,
                .doit = l2tp_nl_cmd_noop,
@@ -887,13 +887,8 @@ EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops);
 
 static int l2tp_nl_init(void)
 {
-       int err;
-
        pr_info("L2TP netlink interface\n");
-       err = genl_register_family_with_ops(&l2tp_nl_family, l2tp_nl_ops,
-                                           ARRAY_SIZE(l2tp_nl_ops));
-
-       return err;
+       return genl_register_family_with_ops(&l2tp_nl_family, l2tp_nl_ops);
 }
 
 static void l2tp_nl_cleanup(void)
index ffda81e..be5fadf 100644 (file)
@@ -197,8 +197,6 @@ static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (sk->sk_state & PPPOX_BOUND)
                goto end;
 
-       msg->msg_namelen = 0;
-
        err = 0;
        skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
                                flags & MSG_DONTWAIT, &err);
index 6cba486..7b01b9f 100644 (file)
@@ -720,8 +720,6 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
        int target;     /* Read at least this many bytes */
        long timeo;
 
-       msg->msg_namelen = 0;
-
        lock_sock(sk);
        copied = -ENOTCONN;
        if (unlikely(sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN))
index 48acec1..c3398cd 100644 (file)
@@ -909,7 +909,7 @@ config NETFILTER_XT_MATCH_CONNLABEL
          connection simultaneously.
 
 config NETFILTER_XT_MATCH_CONNLIMIT
-       tristate '"connlimit" match support"'
+       tristate '"connlimit" match support'
        depends on NF_CONNTRACK
        depends on NETFILTER_ADVANCED
        ---help---
index 1ded5c6..35be035 100644 (file)
@@ -3580,7 +3580,7 @@ out:
 }
 
 
-static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
+static const struct genl_ops ip_vs_genl_ops[] __read_mostly = {
        {
                .cmd    = IPVS_CMD_NEW_SERVICE,
                .flags  = GENL_ADMIN_PERM,
@@ -3679,7 +3679,7 @@ static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
 static int __init ip_vs_genl_register(void)
 {
        return genl_register_family_with_ops(&ip_vs_genl_family,
-               ip_vs_genl_ops, ARRAY_SIZE(ip_vs_genl_ops));
+                                            ip_vs_genl_ops);
 }
 
 static void ip_vs_genl_unregister(void)
index e22d950..43549eb 100644 (file)
@@ -764,9 +764,10 @@ void nf_conntrack_free(struct nf_conn *ct)
        struct net *net = nf_ct_net(ct);
 
        nf_ct_ext_destroy(ct);
-       atomic_dec(&net->ct.count);
        nf_ct_ext_free(ct);
        kmem_cache_free(net->ct.nf_conntrack_cachep, ct);
+       smp_mb__before_atomic_dec();
+       atomic_dec(&net->ct.count);
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_free);
 
index 5f9bfd0..17c1bcb 100644 (file)
@@ -41,8 +41,8 @@ int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
        spin_lock_bh(&ct->lock);
        this_way = &seqadj->seq[dir];
        if (this_way->offset_before == this_way->offset_after ||
-           before(this_way->correction_pos, seq)) {
-               this_way->correction_pos = seq;
+           before(this_way->correction_pos, ntohl(seq))) {
+               this_way->correction_pos = ntohl(seq);
                this_way->offset_before  = this_way->offset_after;
                this_way->offset_after  += off;
        }
index cdf4567..9858e3e 100644 (file)
@@ -151,9 +151,10 @@ void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info,
        opts->tsecr = opts->tsval;
        opts->tsval = tcp_time_stamp & ~0x3f;
 
-       if (opts->options & XT_SYNPROXY_OPT_WSCALE)
-               opts->tsval |= info->wscale;
-       else
+       if (opts->options & XT_SYNPROXY_OPT_WSCALE) {
+               opts->tsval |= opts->wscale;
+               opts->wscale = info->wscale;
+       } else
                opts->tsval |= 0xf;
 
        if (opts->options & XT_SYNPROXY_OPT_SACK_PERM)
index a82667c..da0c1f4 100644 (file)
@@ -128,7 +128,7 @@ static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1]
        [NFTA_RULE_COMPAT_FLAGS]        = { .type = NLA_U32 },
 };
 
-static u8 nft_parse_compat(const struct nlattr *attr, bool *inv)
+static int nft_parse_compat(const struct nlattr *attr, u8 *proto, bool *inv)
 {
        struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1];
        u32 flags;
@@ -148,7 +148,8 @@ static u8 nft_parse_compat(const struct nlattr *attr, bool *inv)
        if (flags & NFT_RULE_COMPAT_F_INV)
                *inv = true;
 
-       return ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO]));
+       *proto = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO]));
+       return 0;
 }
 
 static int
@@ -166,8 +167,11 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
 
        target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info);
 
-       if (ctx->nla[NFTA_RULE_COMPAT])
-               proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv);
+       if (ctx->nla[NFTA_RULE_COMPAT]) {
+               ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
+               if (ret < 0)
+                       goto err;
+       }
 
        nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
 
@@ -356,8 +360,11 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
 
        match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info);
 
-       if (ctx->nla[NFTA_RULE_COMPAT])
-               proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv);
+       if (ctx->nla[NFTA_RULE_COMPAT]) {
+               ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
+               if (ret < 0)
+                       goto err;
+       }
 
        nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
 
index e7c4e0e..80c2e2d 100644 (file)
@@ -84,7 +84,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par)
        index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
 
        if (index == IPSET_INVALID_ID) {
-               pr_warning("Cannot find set indentified by id %u to match\n",
+               pr_warning("Cannot find set identified by id %u to match\n",
                           info->match_set.index);
                return -ENOENT;
        }
@@ -134,7 +134,7 @@ set_match_v1_checkentry(const struct xt_mtchk_param *par)
        index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
 
        if (index == IPSET_INVALID_ID) {
-               pr_warning("Cannot find set indentified by id %u to match\n",
+               pr_warning("Cannot find set identified by id %u to match\n",
                           info->match_set.index);
                return -ENOENT;
        }
index a110064..69345ce 100644 (file)
@@ -737,7 +737,7 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
  * NetLabel Generic NETLINK Command Definitions
  */
 
-static struct genl_ops netlbl_cipsov4_ops[] = {
+static const struct genl_ops netlbl_cipsov4_ops[] = {
        {
        .cmd = NLBL_CIPSOV4_C_ADD,
        .flags = GENL_ADMIN_PERM,
@@ -783,5 +783,5 @@ static struct genl_ops netlbl_cipsov4_ops[] = {
 int __init netlbl_cipsov4_genl_init(void)
 {
        return genl_register_family_with_ops(&netlbl_cipsov4_gnl_family,
-               netlbl_cipsov4_ops, ARRAY_SIZE(netlbl_cipsov4_ops));
+                                            netlbl_cipsov4_ops);
 }
index dd1c37d..8ef83ee 100644 (file)
@@ -705,7 +705,7 @@ version_failure:
  * NetLabel Generic NETLINK Command Definitions
  */
 
-static struct genl_ops netlbl_mgmt_genl_ops[] = {
+static const struct genl_ops netlbl_mgmt_genl_ops[] = {
        {
        .cmd = NLBL_MGMT_C_ADD,
        .flags = GENL_ADMIN_PERM,
@@ -779,5 +779,5 @@ static struct genl_ops netlbl_mgmt_genl_ops[] = {
 int __init netlbl_mgmt_genl_init(void)
 {
        return genl_register_family_with_ops(&netlbl_mgmt_gnl_family,
-               netlbl_mgmt_genl_ops, ARRAY_SIZE(netlbl_mgmt_genl_ops));
+                                            netlbl_mgmt_genl_ops);
 }
index 8f08974..43817d7 100644 (file)
@@ -1323,7 +1323,7 @@ unlabel_staticlistdef_return:
  * NetLabel Generic NETLINK Command Definitions
  */
 
-static struct genl_ops netlbl_unlabel_genl_ops[] = {
+static const struct genl_ops netlbl_unlabel_genl_ops[] = {
        {
        .cmd = NLBL_UNLABEL_C_STATICADD,
        .flags = GENL_ADMIN_PERM,
@@ -1397,7 +1397,7 @@ static struct genl_ops netlbl_unlabel_genl_ops[] = {
 int __init netlbl_unlabel_genl_init(void)
 {
        return genl_register_family_with_ops(&netlbl_unlabel_gnl_family,
-               netlbl_unlabel_genl_ops, ARRAY_SIZE(netlbl_unlabel_genl_ops));
+                                            netlbl_unlabel_genl_ops);
 }
 
 /*
index 8df7f64..bca50b9 100644 (file)
@@ -2017,7 +2017,7 @@ out:
  * netlink_set_err - report error to broadcast listeners
  * @ssk: the kernel netlink socket, as returned by netlink_kernel_create()
  * @portid: the PORTID of a process that we want to skip (if any)
- * @groups: the broadcast group that will notice the error
+ * @group: the broadcast group that will notice the error
  * @code: error code, must be negative (as usual in kernelspace)
  *
  * This function returns the number of broadcast listeners that have set the
@@ -2335,8 +2335,6 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
        }
 #endif
 
-       msg->msg_namelen = 0;
-
        copied = data_skb->len;
        if (len < copied) {
                msg->msg_flags |= MSG_TRUNC;
index 0c741ce..4518a57 100644 (file)
@@ -65,12 +65,24 @@ static struct list_head family_ht[GENL_FAM_TAB_SIZE];
  * To avoid an allocation at boot of just one unsigned long,
  * declare it global instead.
  * Bit 0 is marked as already used since group 0 is invalid.
+ * Bit 1 is marked as already used since the drop-monitor code
+ * abuses the API and thinks it can statically use group 1.
+ * That group will typically conflict with other groups that
+ * any proper users use.
+ * Bit 16 is marked as used since it's used for generic netlink
+ * and the code no longer marks pre-reserved IDs as used.
+ * Bit 17 is marked as already used since the VFS quota code
+ * also abused this API and relied on family == group ID, we
+ * cater to that by giving it a static family and group ID.
  */
-static unsigned long mc_group_start = 0x1;
+static unsigned long mc_group_start = 0x3 | BIT(GENL_ID_CTRL) |
+                                     BIT(GENL_ID_VFS_DQUOT);
 static unsigned long *mc_groups = &mc_group_start;
 static unsigned long mc_groups_longs = 1;
 
-static int genl_ctrl_event(int event, void *data);
+static int genl_ctrl_event(int event, struct genl_family *family,
+                          const struct genl_multicast_group *grp,
+                          int grp_id);
 
 static inline unsigned int genl_family_hash(unsigned int id)
 {
@@ -106,13 +118,13 @@ static struct genl_family *genl_family_find_byname(char *name)
        return NULL;
 }
 
-static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
+static const struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
 {
-       struct genl_ops *ops;
+       int i;
 
-       list_for_each_entry(ops, &family->ops_list, ops_list)
-               if (ops->cmd == cmd)
-                       return ops;
+       for (i = 0; i < family->n_ops; i++)
+               if (family->ops[i].cmd == cmd)
+                       return &family->ops[i];
 
        return NULL;
 }
@@ -126,7 +138,8 @@ static u16 genl_generate_id(void)
        int i;
 
        for (i = 0; i <= GENL_MAX_ID - GENL_MIN_ID; i++) {
-               if (!genl_family_find_byid(id_gen_idx))
+               if (id_gen_idx != GENL_ID_VFS_DQUOT &&
+                   !genl_family_find_byid(id_gen_idx))
                        return id_gen_idx;
                if (++id_gen_idx > GENL_MAX_ID)
                        id_gen_idx = GENL_MIN_ID;
@@ -135,62 +148,110 @@ static u16 genl_generate_id(void)
        return 0;
 }
 
-static struct genl_multicast_group notify_grp;
-
-/**
- * genl_register_mc_group - register a multicast group
- *
- * Registers the specified multicast group and notifies userspace
- * about the new group.
- *
- * Returns 0 on success or a negative error code.
- *
- * @family: The generic netlink family the group shall be registered for.
- * @grp: The group to register, must have a name.
- */
-int genl_register_mc_group(struct genl_family *family,
-                          struct genl_multicast_group *grp)
+static int genl_allocate_reserve_groups(int n_groups, int *first_id)
 {
-       int id;
        unsigned long *new_groups;
-       int err = 0;
+       int start = 0;
+       int i;
+       int id;
+       bool fits;
+
+       do {
+               if (start == 0)
+                       id = find_first_zero_bit(mc_groups,
+                                                mc_groups_longs *
+                                                BITS_PER_LONG);
+               else
+                       id = find_next_zero_bit(mc_groups,
+                                               mc_groups_longs * BITS_PER_LONG,
+                                               start);
+
+               fits = true;
+               for (i = id;
+                    i < min_t(int, id + n_groups,
+                              mc_groups_longs * BITS_PER_LONG);
+                    i++) {
+                       if (test_bit(i, mc_groups)) {
+                               start = i;
+                               fits = false;
+                               break;
+                       }
+               }
 
-       BUG_ON(grp->name[0] == '\0');
-       BUG_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL);
+               if (id >= mc_groups_longs * BITS_PER_LONG) {
+                       unsigned long new_longs = mc_groups_longs +
+                                                 BITS_TO_LONGS(n_groups);
+                       size_t nlen = new_longs * sizeof(unsigned long);
+
+                       if (mc_groups == &mc_group_start) {
+                               new_groups = kzalloc(nlen, GFP_KERNEL);
+                               if (!new_groups)
+                                       return -ENOMEM;
+                               mc_groups = new_groups;
+                               *mc_groups = mc_group_start;
+                       } else {
+                               new_groups = krealloc(mc_groups, nlen,
+                                                     GFP_KERNEL);
+                               if (!new_groups)
+                                       return -ENOMEM;
+                               mc_groups = new_groups;
+                               for (i = 0; i < BITS_TO_LONGS(n_groups); i++)
+                                       mc_groups[mc_groups_longs + i] = 0;
+                       }
+                       mc_groups_longs = new_longs;
+               }
+       } while (!fits);
 
-       genl_lock_all();
+       for (i = id; i < id + n_groups; i++)
+               set_bit(i, mc_groups);
+       *first_id = id;
+       return 0;
+}
 
-       /* special-case our own group */
-       if (grp == &notify_grp)
-               id = GENL_ID_CTRL;
-       else
-               id = find_first_zero_bit(mc_groups,
-                                        mc_groups_longs * BITS_PER_LONG);
+static struct genl_family genl_ctrl;
 
+static int genl_validate_assign_mc_groups(struct genl_family *family)
+{
+       int first_id;
+       int n_groups = family->n_mcgrps;
+       int err, i;
+       bool groups_allocated = false;
 
-       if (id >= mc_groups_longs * BITS_PER_LONG) {
-               size_t nlen = (mc_groups_longs + 1) * sizeof(unsigned long);
+       if (!n_groups)
+               return 0;
 
-               if (mc_groups == &mc_group_start) {
-                       new_groups = kzalloc(nlen, GFP_KERNEL);
-                       if (!new_groups) {
-                               err = -ENOMEM;
-                               goto out;
-                       }
-                       mc_groups = new_groups;
-                       *mc_groups = mc_group_start;
-               } else {
-                       new_groups = krealloc(mc_groups, nlen, GFP_KERNEL);
-                       if (!new_groups) {
-                               err = -ENOMEM;
-                               goto out;
-                       }
-                       mc_groups = new_groups;
-                       mc_groups[mc_groups_longs] = 0;
-               }
-               mc_groups_longs++;
+       for (i = 0; i < n_groups; i++) {
+               const struct genl_multicast_group *grp = &family->mcgrps[i];
+
+               if (WARN_ON(grp->name[0] == '\0'))
+                       return -EINVAL;
+               if (WARN_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL))
+                       return -EINVAL;
+       }
+
+       /* special-case our own group and hacks */
+       if (family == &genl_ctrl) {
+               first_id = GENL_ID_CTRL;
+               BUG_ON(n_groups != 1);
+       } else if (strcmp(family->name, "NET_DM") == 0) {
+               first_id = 1;
+               BUG_ON(n_groups != 1);
+       } else if (strcmp(family->name, "VFS_DQUOT") == 0) {
+               first_id = GENL_ID_VFS_DQUOT;
+               BUG_ON(n_groups != 1);
+       } else {
+               groups_allocated = true;
+               err = genl_allocate_reserve_groups(n_groups, &first_id);
+               if (err)
+                       return err;
        }
 
+       family->mcgrp_offset = first_id;
+
+       /* if still initializing, can't and don't need to to realloc bitmaps */
+       if (!init_net.genl_sock)
+               return 0;
+
        if (family->netnsok) {
                struct net *net;
 
@@ -206,9 +267,7 @@ int genl_register_mc_group(struct genl_family *family,
                                 * number of _possible_ groups has been
                                 * increased on some sockets which is ok.
                                 */
-                               rcu_read_unlock();
-                               netlink_table_ungrab();
-                               goto out;
+                               break;
                        }
                }
                rcu_read_unlock();
@@ -216,152 +275,67 @@ int genl_register_mc_group(struct genl_family *family,
        } else {
                err = netlink_change_ngroups(init_net.genl_sock,
                                             mc_groups_longs * BITS_PER_LONG);
-               if (err)
-                       goto out;
        }
 
-       grp->id = id;
-       set_bit(id, mc_groups);
-       list_add_tail(&grp->list, &family->mcast_groups);
-       grp->family = family;
+       if (groups_allocated && err) {
+               for (i = 0; i < family->n_mcgrps; i++)
+                       clear_bit(family->mcgrp_offset + i, mc_groups);
+       }
 
-       genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, grp);
- out:
-       genl_unlock_all();
        return err;
 }
-EXPORT_SYMBOL(genl_register_mc_group);
 
-static void __genl_unregister_mc_group(struct genl_family *family,
-                                      struct genl_multicast_group *grp)
+static void genl_unregister_mc_groups(struct genl_family *family)
 {
        struct net *net;
-       BUG_ON(grp->family != family);
+       int i;
 
        netlink_table_grab();
        rcu_read_lock();
-       for_each_net_rcu(net)
-               __netlink_clear_multicast_users(net->genl_sock, grp->id);
+       for_each_net_rcu(net) {
+               for (i = 0; i < family->n_mcgrps; i++)
+                       __netlink_clear_multicast_users(
+                               net->genl_sock, family->mcgrp_offset + i);
+       }
        rcu_read_unlock();
        netlink_table_ungrab();
 
-       clear_bit(grp->id, mc_groups);
-       list_del(&grp->list);
-       genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp);
-       grp->id = 0;
-       grp->family = NULL;
-}
+       for (i = 0; i < family->n_mcgrps; i++) {
+               int grp_id = family->mcgrp_offset + i;
 
-/**
- * genl_unregister_mc_group - unregister a multicast group
- *
- * Unregisters the specified multicast group and notifies userspace
- * about it. All current listeners on the group are removed.
- *
- * Note: It is not necessary to unregister all multicast groups before
- *       unregistering the family, unregistering the family will cause
- *       all assigned multicast groups to be unregistered automatically.
- *
- * @family: Generic netlink family the group belongs to.
- * @grp: The group to unregister, must have been registered successfully
- *      previously.
- */
-void genl_unregister_mc_group(struct genl_family *family,
-                             struct genl_multicast_group *grp)
-{
-       genl_lock_all();
-       __genl_unregister_mc_group(family, grp);
-       genl_unlock_all();
+               if (grp_id != 1)
+                       clear_bit(grp_id, mc_groups);
+               genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, family,
+                               &family->mcgrps[i], grp_id);
+       }
 }
-EXPORT_SYMBOL(genl_unregister_mc_group);
 
-static void genl_unregister_mc_groups(struct genl_family *family)
+static int genl_validate_ops(struct genl_family *family)
 {
-       struct genl_multicast_group *grp, *tmp;
+       const struct genl_ops *ops = family->ops;
+       unsigned int n_ops = family->n_ops;
+       int i, j;
 
-       list_for_each_entry_safe(grp, tmp, &family->mcast_groups, list)
-               __genl_unregister_mc_group(family, grp);
-}
-
-/**
- * genl_register_ops - register generic netlink operations
- * @family: generic netlink family
- * @ops: operations to be registered
- *
- * Registers the specified operations and assigns them to the specified
- * family. Either a doit or dumpit callback must be specified or the
- * operation will fail. Only one operation structure per command
- * identifier may be registered.
- *
- * See include/net/genetlink.h for more documenation on the operations
- * structure.
- *
- * Returns 0 on success or a negative error code.
- */
-int genl_register_ops(struct genl_family *family, struct genl_ops *ops)
-{
-       int err = -EINVAL;
+       if (WARN_ON(n_ops && !ops))
+               return -EINVAL;
 
-       if (ops->dumpit == NULL && ops->doit == NULL)
-               goto errout;
+       if (!n_ops)
+               return 0;
 
-       if (genl_get_cmd(ops->cmd, family)) {
-               err = -EEXIST;
-               goto errout;
+       for (i = 0; i < n_ops; i++) {
+               if (ops[i].dumpit == NULL && ops[i].doit == NULL)
+                       return -EINVAL;
+               for (j = i + 1; j < n_ops; j++)
+                       if (ops[i].cmd == ops[j].cmd)
+                               return -EINVAL;
        }
 
-       if (ops->dumpit)
-               ops->flags |= GENL_CMD_CAP_DUMP;
-       if (ops->doit)
-               ops->flags |= GENL_CMD_CAP_DO;
-       if (ops->policy)
-               ops->flags |= GENL_CMD_CAP_HASPOL;
+       /* family is not registered yet, so no locking needed */
+       family->ops = ops;
+       family->n_ops = n_ops;
 
-       genl_lock_all();
-       list_add_tail(&ops->ops_list, &family->ops_list);
-       genl_unlock_all();
-
-       genl_ctrl_event(CTRL_CMD_NEWOPS, ops);
-       err = 0;
-errout:
-       return err;
-}
-EXPORT_SYMBOL(genl_register_ops);
-
-/**
- * genl_unregister_ops - unregister generic netlink operations
- * @family: generic netlink family
- * @ops: operations to be unregistered
- *
- * Unregisters the specified operations and unassigns them from the
- * specified family. The operation blocks until the current message
- * processing has finished and doesn't start again until the
- * unregister process has finished.
- *
- * Note: It is not necessary to unregister all operations before
- *       unregistering the family, unregistering the family will cause
- *       all assigned operations to be unregistered automatically.
- *
- * Returns 0 on success or a negative error code.
- */
-int genl_unregister_ops(struct genl_family *family, struct genl_ops *ops)
-{
-       struct genl_ops *rc;
-
-       genl_lock_all();
-       list_for_each_entry(rc, &family->ops_list, ops_list) {
-               if (rc == ops) {
-                       list_del(&ops->ops_list);
-                       genl_unlock_all();
-                       genl_ctrl_event(CTRL_CMD_DELOPS, ops);
-                       return 0;
-               }
-       }
-       genl_unlock_all();
-
-       return -ENOENT;
+       return 0;
 }
-EXPORT_SYMBOL(genl_unregister_ops);
 
 /**
  * __genl_register_family - register a generic netlink family
@@ -372,11 +346,14 @@ EXPORT_SYMBOL(genl_unregister_ops);
  * The family id may equal GENL_ID_GENERATE causing an unique id to
  * be automatically generated and assigned.
  *
+ * The family's ops array must already be assigned, you can use the
+ * genl_register_family_with_ops() helper function.
+ *
  * Return 0 on success or a negative error code.
  */
 int __genl_register_family(struct genl_family *family)
 {
-       int err = -EINVAL;
+       int err = -EINVAL, i;
 
        if (family->id && family->id < GENL_MIN_ID)
                goto errout;
@@ -384,8 +361,9 @@ int __genl_register_family(struct genl_family *family)
        if (family->id > GENL_MAX_ID)
                goto errout;
 
-       INIT_LIST_HEAD(&family->ops_list);
-       INIT_LIST_HEAD(&family->mcast_groups);
+       err = genl_validate_ops(family);
+       if (err)
+               return err;
 
        genl_lock_all();
 
@@ -418,10 +396,18 @@ int __genl_register_family(struct genl_family *family)
        } else
                family->attrbuf = NULL;
 
+       err = genl_validate_assign_mc_groups(family);
+       if (err)
+               goto errout_locked;
+
        list_add_tail(&family->family_list, genl_family_chain(family->id));
        genl_unlock_all();
 
-       genl_ctrl_event(CTRL_CMD_NEWFAMILY, family);
+       /* send all events */
+       genl_ctrl_event(CTRL_CMD_NEWFAMILY, family, NULL, 0);
+       for (i = 0; i < family->n_mcgrps; i++)
+               genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, family,
+                               &family->mcgrps[i], family->mcgrp_offset + i);
 
        return 0;
 
@@ -433,52 +419,6 @@ errout:
 EXPORT_SYMBOL(__genl_register_family);
 
 /**
- * __genl_register_family_with_ops - register a generic netlink family
- * @family: generic netlink family
- * @ops: operations to be registered
- * @n_ops: number of elements to register
- *
- * Registers the specified family and operations from the specified table.
- * Only one family may be registered with the same family name or identifier.
- *
- * The family id may equal GENL_ID_GENERATE causing an unique id to
- * be automatically generated and assigned.
- *
- * Either a doit or dumpit callback must be specified for every registered
- * operation or the function will fail. Only one operation structure per
- * command identifier may be registered.
- *
- * See include/net/genetlink.h for more documenation on the operations
- * structure.
- *
- * This is equivalent to calling genl_register_family() followed by
- * genl_register_ops() for every operation entry in the table taking
- * care to unregister the family on error path.
- *
- * Return 0 on success or a negative error code.
- */
-int __genl_register_family_with_ops(struct genl_family *family,
-       struct genl_ops *ops, size_t n_ops)
-{
-       int err, i;
-
-       err = __genl_register_family(family);
-       if (err)
-               return err;
-
-       for (i = 0; i < n_ops; ++i, ++ops) {
-               err = genl_register_ops(family, ops);
-               if (err)
-                       goto err_out;
-       }
-       return 0;
-err_out:
-       genl_unregister_family(family);
-       return err;
-}
-EXPORT_SYMBOL(__genl_register_family_with_ops);
-
-/**
  * genl_unregister_family - unregister generic netlink family
  * @family: generic netlink family
  *
@@ -499,11 +439,11 @@ int genl_unregister_family(struct genl_family *family)
                        continue;
 
                list_del(&rc->family_list);
-               INIT_LIST_HEAD(&family->ops_list);
+               family->n_ops = 0;
                genl_unlock_all();
 
                kfree(family->attrbuf);
-               genl_ctrl_event(CTRL_CMD_DELFAMILY, family);
+               genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0);
                return 0;
        }
 
@@ -546,7 +486,8 @@ EXPORT_SYMBOL(genlmsg_put);
 
 static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       struct genl_ops *ops = cb->data;
+       /* our ops are always const - netlink API doesn't propagate that */
+       const struct genl_ops *ops = cb->data;
        int rc;
 
        genl_lock();
@@ -557,7 +498,8 @@ static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 
 static int genl_lock_done(struct netlink_callback *cb)
 {
-       struct genl_ops *ops = cb->data;
+       /* our ops are always const - netlink API doesn't propagate that */
+       const struct genl_ops *ops = cb->data;
        int rc = 0;
 
        if (ops->done) {
@@ -572,7 +514,7 @@ static int genl_family_rcv_msg(struct genl_family *family,
                               struct sk_buff *skb,
                               struct nlmsghdr *nlh)
 {
-       struct genl_ops *ops;
+       const struct genl_ops *ops;
        struct net *net = sock_net(skb->sk);
        struct genl_info info;
        struct genlmsghdr *hdr = nlmsg_data(nlh);
@@ -604,7 +546,8 @@ static int genl_family_rcv_msg(struct genl_family *family,
                if (!family->parallel_ops) {
                        struct netlink_dump_control c = {
                                .module = family->module,
-                               .data = ops,
+                               /* we have const, but the netlink API doesn't */
+                               .data = (void *)ops,
                                .dump = genl_lock_dumpit,
                                .done = genl_lock_done,
                        };
@@ -726,24 +669,32 @@ static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq,
            nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr))
                goto nla_put_failure;
 
-       if (!list_empty(&family->ops_list)) {
+       if (family->n_ops) {
                struct nlattr *nla_ops;
-               struct genl_ops *ops;
-               int idx = 1;
+               int i;
 
                nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS);
                if (nla_ops == NULL)
                        goto nla_put_failure;
 
-               list_for_each_entry(ops, &family->ops_list, ops_list) {
+               for (i = 0; i < family->n_ops; i++) {
                        struct nlattr *nest;
+                       const struct genl_ops *ops = &family->ops[i];
+                       u32 op_flags = ops->flags;
 
-                       nest = nla_nest_start(skb, idx++);
+                       if (ops->dumpit)
+                               op_flags |= GENL_CMD_CAP_DUMP;
+                       if (ops->doit)
+                               op_flags |= GENL_CMD_CAP_DO;
+                       if (ops->policy)
+                               op_flags |= GENL_CMD_CAP_HASPOL;
+
+                       nest = nla_nest_start(skb, i + 1);
                        if (nest == NULL)
                                goto nla_put_failure;
 
                        if (nla_put_u32(skb, CTRL_ATTR_OP_ID, ops->cmd) ||
-                           nla_put_u32(skb, CTRL_ATTR_OP_FLAGS, ops->flags))
+                           nla_put_u32(skb, CTRL_ATTR_OP_FLAGS, op_flags))
                                goto nla_put_failure;
 
                        nla_nest_end(skb, nest);
@@ -752,23 +703,26 @@ static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq,
                nla_nest_end(skb, nla_ops);
        }
 
-       if (!list_empty(&family->mcast_groups)) {
-               struct genl_multicast_group *grp;
+       if (family->n_mcgrps) {
                struct nlattr *nla_grps;
-               int idx = 1;
+               int i;
 
                nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS);
                if (nla_grps == NULL)
                        goto nla_put_failure;
 
-               list_for_each_entry(grp, &family->mcast_groups, list) {
+               for (i = 0; i < family->n_mcgrps; i++) {
                        struct nlattr *nest;
+                       const struct genl_multicast_group *grp;
+
+                       grp = &family->mcgrps[i];
 
-                       nest = nla_nest_start(skb, idx++);
+                       nest = nla_nest_start(skb, i + 1);
                        if (nest == NULL)
                                goto nla_put_failure;
 
-                       if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id) ||
+                       if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID,
+                                       family->mcgrp_offset + i) ||
                            nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME,
                                           grp->name))
                                goto nla_put_failure;
@@ -785,9 +739,10 @@ nla_put_failure:
        return -EMSGSIZE;
 }
 
-static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 portid,
-                               u32 seq, u32 flags, struct sk_buff *skb,
-                               u8 cmd)
+static int ctrl_fill_mcgrp_info(struct genl_family *family,
+                               const struct genl_multicast_group *grp,
+                               int grp_id, u32 portid, u32 seq, u32 flags,
+                               struct sk_buff *skb, u8 cmd)
 {
        void *hdr;
        struct nlattr *nla_grps;
@@ -797,8 +752,8 @@ static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 portid,
        if (hdr == NULL)
                return -1;
 
-       if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, grp->family->name) ||
-           nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, grp->family->id))
+       if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) ||
+           nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id))
                goto nla_put_failure;
 
        nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS);
@@ -809,7 +764,7 @@ static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 portid,
        if (nest == NULL)
                goto nla_put_failure;
 
-       if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id) ||
+       if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp_id) ||
            nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME,
                           grp->name))
                goto nla_put_failure;
@@ -875,8 +830,10 @@ static struct sk_buff *ctrl_build_family_msg(struct genl_family *family,
        return skb;
 }
 
-static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_multicast_group *grp,
-                                           u32 portid, int seq, u8 cmd)
+static struct sk_buff *
+ctrl_build_mcgrp_msg(struct genl_family *family,
+                    const struct genl_multicast_group *grp,
+                    int grp_id, u32 portid, int seq, u8 cmd)
 {
        struct sk_buff *skb;
        int err;
@@ -885,7 +842,8 @@ static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_multicast_group *grp,
        if (skb == NULL)
                return ERR_PTR(-ENOBUFS);
 
-       err = ctrl_fill_mcgrp_info(grp, portid, seq, 0, skb, cmd);
+       err = ctrl_fill_mcgrp_info(family, grp, grp_id, portid,
+                                  seq, 0, skb, cmd);
        if (err < 0) {
                nlmsg_free(skb);
                return ERR_PTR(err);
@@ -947,11 +905,11 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info)
        return genlmsg_reply(msg, info);
 }
 
-static int genl_ctrl_event(int event, void *data)
+static int genl_ctrl_event(int event, struct genl_family *family,
+                          const struct genl_multicast_group *grp,
+                          int grp_id)
 {
        struct sk_buff *msg;
-       struct genl_family *family;
-       struct genl_multicast_group *grp;
 
        /* genl is still initialising */
        if (!init_net.genl_sock)
@@ -960,14 +918,13 @@ static int genl_ctrl_event(int event, void *data)
        switch (event) {
        case CTRL_CMD_NEWFAMILY:
        case CTRL_CMD_DELFAMILY:
-               family = data;
+               WARN_ON(grp);
                msg = ctrl_build_family_msg(family, 0, 0, event);
                break;
        case CTRL_CMD_NEWMCAST_GRP:
        case CTRL_CMD_DELMCAST_GRP:
-               grp = data;
-               family = grp->family;
-               msg = ctrl_build_mcgrp_msg(data, 0, 0, event);
+               BUG_ON(!grp);
+               msg = ctrl_build_mcgrp_msg(family, grp, grp_id, 0, 0, event);
                break;
        default:
                return -EINVAL;
@@ -977,26 +934,29 @@ static int genl_ctrl_event(int event, void *data)
                return PTR_ERR(msg);
 
        if (!family->netnsok) {
-               genlmsg_multicast_netns(&init_net, msg, 0,
-                                       GENL_ID_CTRL, GFP_KERNEL);
+               genlmsg_multicast_netns(&genl_ctrl, &init_net, msg, 0,
+                                       0, GFP_KERNEL);
        } else {
                rcu_read_lock();
-               genlmsg_multicast_allns(msg, 0, GENL_ID_CTRL, GFP_ATOMIC);
+               genlmsg_multicast_allns(&genl_ctrl, msg, 0,
+                                       0, GFP_ATOMIC);
                rcu_read_unlock();
        }
 
        return 0;
 }
 
-static struct genl_ops genl_ctrl_ops = {
-       .cmd            = CTRL_CMD_GETFAMILY,
-       .doit           = ctrl_getfamily,
-       .dumpit         = ctrl_dumpfamily,
-       .policy         = ctrl_policy,
+static struct genl_ops genl_ctrl_ops[] = {
+       {
+               .cmd            = CTRL_CMD_GETFAMILY,
+               .doit           = ctrl_getfamily,
+               .dumpit         = ctrl_dumpfamily,
+               .policy         = ctrl_policy,
+       },
 };
 
-static struct genl_multicast_group notify_grp = {
-       .name           = "notify",
+static struct genl_multicast_group genl_ctrl_groups[] = {
+       { .name = "notify", },
 };
 
 static int __net_init genl_pernet_init(struct net *net)
@@ -1036,7 +996,8 @@ static int __init genl_init(void)
        for (i = 0; i < GENL_FAM_TAB_SIZE; i++)
                INIT_LIST_HEAD(&family_ht[i]);
 
-       err = genl_register_family_with_ops(&genl_ctrl, &genl_ctrl_ops, 1);
+       err = genl_register_family_with_ops_groups(&genl_ctrl, genl_ctrl_ops,
+                                                  genl_ctrl_groups);
        if (err < 0)
                goto problem;
 
@@ -1044,10 +1005,6 @@ static int __init genl_init(void)
        if (err)
                goto problem;
 
-       err = genl_register_mc_group(&genl_ctrl, &notify_grp);
-       if (err < 0)
-               goto problem;
-
        return 0;
 
 problem:
@@ -1085,14 +1042,18 @@ static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group,
        return err;
 }
 
-int genlmsg_multicast_allns(struct sk_buff *skb, u32 portid, unsigned int group,
-                           gfp_t flags)
+int genlmsg_multicast_allns(struct genl_family *family, struct sk_buff *skb,
+                           u32 portid, unsigned int group, gfp_t flags)
 {
+       if (WARN_ON_ONCE(group >= family->n_mcgrps))
+               return -EINVAL;
+       group = family->mcgrp_offset + group;
        return genlmsg_mcast(skb, portid, group, flags);
 }
 EXPORT_SYMBOL(genlmsg_multicast_allns);
 
-void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, u32 group,
+void genl_notify(struct genl_family *family,
+                struct sk_buff *skb, struct net *net, u32 portid, u32 group,
                 struct nlmsghdr *nlh, gfp_t flags)
 {
        struct sock *sk = net->genl_sock;
@@ -1101,6 +1062,9 @@ void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, u32 group,
        if (nlh)
                report = nlmsg_report(nlh);
 
+       if (WARN_ON_ONCE(group >= family->n_mcgrps))
+               return;
+       group = family->mcgrp_offset + group;
        nlmsg_notify(sk, skb, portid, group, report, flags);
 }
 EXPORT_SYMBOL(genl_notify);
index 698814b..53c19a3 100644 (file)
@@ -1179,10 +1179,9 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock,
                sax->sax25_family = AF_NETROM;
                skb_copy_from_linear_data_offset(skb, 7, sax->sax25_call.ax25_call,
                              AX25_ADDR_LEN);
+               msg->msg_namelen = sizeof(*sax);
        }
 
-       msg->msg_namelen = sizeof(*sax);
-
        skb_free_datagram(sk, skb);
 
        release_sock(sk);
index d308402..824c605 100644 (file)
@@ -807,8 +807,6 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        pr_debug("%p %zu\n", sk, len);
 
-       msg->msg_namelen = 0;
-
        lock_sock(sk);
 
        if (sk->sk_state == LLCP_CLOSED &&
index 84b7e3e..a9b2342 100644 (file)
@@ -30,8 +30,8 @@
 #include "nfc.h"
 #include "llcp.h"
 
-static struct genl_multicast_group nfc_genl_event_mcgrp = {
-       .name = NFC_GENL_MCAST_EVENT_NAME,
+static const struct genl_multicast_group nfc_genl_mcgrps[] = {
+       { .name = NFC_GENL_MCAST_EVENT_NAME, },
 };
 
 static struct genl_family nfc_genl_family = {
@@ -194,7 +194,7 @@ int nfc_genl_targets_found(struct nfc_dev *dev)
 
        genlmsg_end(msg, hdr);
 
-       return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
+       return genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);
 
 nla_put_failure:
        genlmsg_cancel(msg, hdr);
@@ -223,7 +223,7 @@ int nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
 
        return 0;
 
@@ -255,7 +255,7 @@ int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
 
        return 0;
 
@@ -285,7 +285,7 @@ int nfc_genl_tm_deactivated(struct nfc_dev *dev)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
 
        return 0;
 
@@ -318,7 +318,7 @@ int nfc_genl_device_added(struct nfc_dev *dev)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
 
        return 0;
 
@@ -348,7 +348,7 @@ int nfc_genl_device_removed(struct nfc_dev *dev)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
 
        return 0;
 
@@ -414,7 +414,7 @@ int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list)
 
        genlmsg_end(msg, hdr);
 
-       return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
+       return genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);
 
 nla_put_failure:
        genlmsg_cancel(msg, hdr);
@@ -448,7 +448,7 @@ int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
 
        return 0;
 
@@ -479,7 +479,7 @@ int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
 
        return 0;
 
@@ -600,7 +600,7 @@ int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx,
 
        dev->dep_link_up = true;
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);
 
        return 0;
 
@@ -632,7 +632,7 @@ int nfc_genl_dep_link_down_event(struct nfc_dev *dev)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);
 
        return 0;
 
@@ -1137,7 +1137,7 @@ int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
 
        return 0;
 
@@ -1308,7 +1308,7 @@ static void se_io_cb(void *context, u8 *apdu, size_t apdu_len, int err)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
 
        kfree(ctx);
 
@@ -1364,7 +1364,7 @@ static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info)
        return dev->ops->se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
 }
 
-static struct genl_ops nfc_genl_ops[] = {
+static const struct genl_ops nfc_genl_ops[] = {
        {
                .cmd = NFC_CMD_GET_DEVICE,
                .doit = nfc_genl_get_device,
@@ -1536,16 +1536,15 @@ int __init nfc_genl_init(void)
 {
        int rc;
 
-       rc = genl_register_family_with_ops(&nfc_genl_family, nfc_genl_ops,
-                                          ARRAY_SIZE(nfc_genl_ops));
+       rc = genl_register_family_with_ops_groups(&nfc_genl_family,
+                                                 nfc_genl_ops,
+                                                 nfc_genl_mcgrps);
        if (rc)
                return rc;
 
-       rc = genl_register_mc_group(&nfc_genl_family, &nfc_genl_event_mcgrp);
-
        netlink_register_notifier(&nl_notifier);
 
-       return rc;
+       return 0;
 }
 
 /**
index cd958b3..66bcd2e 100644 (file)
@@ -244,8 +244,6 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (!skb)
                return rc;
 
-       msg->msg_namelen = 0;
-
        copied = skb->len;
        if (len < copied) {
                msg->msg_flags |= MSG_TRUNC;
index 449e077..6f5e1dd 100644 (file)
 
 int ovs_net_id __read_mostly;
 
-static void ovs_notify(struct sk_buff *skb, struct genl_info *info,
-                      struct genl_multicast_group *grp)
+static void ovs_notify(struct genl_family *family,
+                      struct sk_buff *skb, struct genl_info *info)
 {
-       genl_notify(skb, genl_info_net(info), info->snd_portid,
-                   grp->id, info->nlhdr, GFP_KERNEL);
+       genl_notify(family, skb, genl_info_net(info), info->snd_portid,
+                   0, info->nlhdr, GFP_KERNEL);
 }
 
 /**
@@ -557,7 +557,7 @@ static const struct nla_policy packet_policy[OVS_PACKET_ATTR_MAX + 1] = {
        [OVS_PACKET_ATTR_ACTIONS] = { .type = NLA_NESTED },
 };
 
-static struct genl_ops dp_packet_genl_ops[] = {
+static const struct genl_ops dp_packet_genl_ops[] = {
        { .cmd = OVS_PACKET_CMD_EXECUTE,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = packet_policy,
@@ -877,10 +877,10 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
        ovs_unlock();
 
        if (!IS_ERR(reply))
-               ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
+               ovs_notify(&dp_flow_genl_family, reply, info);
        else
-               netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
-                               ovs_dp_flow_multicast_group.id, PTR_ERR(reply));
+               genl_set_err(&dp_flow_genl_family, sock_net(skb->sk), 0,
+                            0, PTR_ERR(reply));
        return 0;
 
 err_flow_free:
@@ -990,7 +990,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
        ovs_flow_free(flow, true);
        ovs_unlock();
 
-       ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
+       ovs_notify(&dp_flow_genl_family, reply, info);
        return 0;
 unlock:
        ovs_unlock();
@@ -1034,7 +1034,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
-static struct genl_ops dp_flow_genl_ops[] = {
+static const struct genl_ops dp_flow_genl_ops[] = {
        { .cmd = OVS_FLOW_CMD_NEW,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = flow_policy,
@@ -1243,7 +1243,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
 
        ovs_unlock();
 
-       ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
+       ovs_notify(&dp_datapath_genl_family, reply, info);
        return 0;
 
 err_destroy_local_port:
@@ -1308,7 +1308,7 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
        __dp_destroy(dp);
        ovs_unlock();
 
-       ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
+       ovs_notify(&dp_datapath_genl_family, reply, info);
 
        return 0;
 unlock:
@@ -1332,14 +1332,14 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
                                      info->snd_seq, OVS_DP_CMD_NEW);
        if (IS_ERR(reply)) {
                err = PTR_ERR(reply);
-               netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
-                               ovs_dp_datapath_multicast_group.id, err);
+               genl_set_err(&dp_datapath_genl_family, sock_net(skb->sk), 0,
+                            0, err);
                err = 0;
                goto unlock;
        }
 
        ovs_unlock();
-       ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
+       ovs_notify(&dp_datapath_genl_family, reply, info);
 
        return 0;
 unlock:
@@ -1398,7 +1398,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
-static struct genl_ops dp_datapath_genl_ops[] = {
+static const struct genl_ops dp_datapath_genl_ops[] = {
        { .cmd = OVS_DP_CMD_NEW,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = datapath_policy,
@@ -1431,7 +1431,7 @@ static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
        [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
 };
 
-static struct genl_family dp_vport_genl_family = {
+struct genl_family dp_vport_genl_family = {
        .id = GENL_ID_GENERATE,
        .hdrsize = sizeof(struct ovs_header),
        .name = OVS_VPORT_FAMILY,
@@ -1601,7 +1601,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
                goto exit_unlock;
        }
 
-       ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
+       ovs_notify(&dp_vport_genl_family, reply, info);
 
 exit_unlock:
        ovs_unlock();
@@ -1648,7 +1648,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
        BUG_ON(err < 0);
 
        ovs_unlock();
-       ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
+       ovs_notify(&dp_vport_genl_family, reply, info);
        return 0;
 
 exit_free:
@@ -1685,7 +1685,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
        err = 0;
        ovs_dp_detach_port(vport);
 
-       ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
+       ovs_notify(&dp_vport_genl_family, reply, info);
 
 exit_unlock:
        ovs_unlock();
@@ -1759,7 +1759,7 @@ out:
        return skb->len;
 }
 
-static struct genl_ops dp_vport_genl_ops[] = {
+static const struct genl_ops dp_vport_genl_ops[] = {
        { .cmd = OVS_VPORT_CMD_NEW,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = vport_policy,
@@ -1785,9 +1785,9 @@ static struct genl_ops dp_vport_genl_ops[] = {
 
 struct genl_family_and_ops {
        struct genl_family *family;
-       struct genl_ops *ops;
+       const struct genl_ops *ops;
        int n_ops;
-       struct genl_multicast_group *group;
+       const struct genl_multicast_group *group;
 };
 
 static const struct genl_family_and_ops dp_genl_families[] = {
@@ -1823,17 +1823,14 @@ static int dp_register_genl(void)
        for (i = 0; i < ARRAY_SIZE(dp_genl_families); i++) {
                const struct genl_family_and_ops *f = &dp_genl_families[i];
 
-               err = genl_register_family_with_ops(f->family, f->ops,
-                                                   f->n_ops);
+               f->family->ops = f->ops;
+               f->family->n_ops = f->n_ops;
+               f->family->mcgrps = f->group;
+               f->family->n_mcgrps = f->group ? 1 : 0;
+               err = genl_register_family(f->family);
                if (err)
                        goto error;
                n_registered++;
-
-               if (f->group) {
-                       err = genl_register_mc_group(f->family, f->group);
-                       if (err)
-                               goto error;
-               }
        }
 
        return 0;
index d3d14a5..4067ea4 100644 (file)
@@ -177,6 +177,7 @@ static inline struct vport *ovs_vport_ovsl(const struct datapath *dp, int port_n
 }
 
 extern struct notifier_block ovs_dp_device_notifier;
+extern struct genl_family dp_vport_genl_family;
 extern struct genl_multicast_group ovs_dp_vport_multicast_group;
 
 void ovs_dp_process_received_packet(struct vport *, struct sk_buff *);
index 5c2dab2..2c631fe 100644 (file)
@@ -34,15 +34,14 @@ static void dp_detach_port_notify(struct vport *vport)
                                          OVS_VPORT_CMD_DEL);
        ovs_dp_detach_port(vport);
        if (IS_ERR(notify)) {
-               netlink_set_err(ovs_dp_get_net(dp)->genl_sock, 0,
-                               ovs_dp_vport_multicast_group.id,
-                               PTR_ERR(notify));
+               genl_set_err(&dp_vport_genl_family, ovs_dp_get_net(dp), 0,
+                            0, PTR_ERR(notify));
                return;
        }
 
-       genlmsg_multicast_netns(ovs_dp_get_net(dp), notify, 0,
-                               ovs_dp_vport_multicast_group.id,
-                               GFP_KERNEL);
+       genlmsg_multicast_netns(&dp_vport_genl_family,
+                               ovs_dp_get_net(dp), notify, 0,
+                               0, GFP_KERNEL);
 }
 
 void ovs_dp_notify_wq(struct work_struct *work)
index 2e8286b..ac27c86 100644 (file)
@@ -244,11 +244,15 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po);
 static void register_prot_hook(struct sock *sk)
 {
        struct packet_sock *po = pkt_sk(sk);
+
        if (!po->running) {
-               if (po->fanout)
+               if (po->fanout) {
                        __fanout_link(sk, po);
-               else
+               } else {
                        dev_add_pack(&po->prot_hook);
+                       rcu_assign_pointer(po->cached_dev, po->prot_hook.dev);
+               }
+
                sock_hold(sk);
                po->running = 1;
        }
@@ -266,10 +270,13 @@ static void __unregister_prot_hook(struct sock *sk, bool sync)
        struct packet_sock *po = pkt_sk(sk);
 
        po->running = 0;
-       if (po->fanout)
+       if (po->fanout) {
                __fanout_unlink(sk, po);
-       else
+       } else {
                __dev_remove_pack(&po->prot_hook);
+               RCU_INIT_POINTER(po->cached_dev, NULL);
+       }
+
        __sock_put(sk);
 
        if (sync) {
@@ -2052,12 +2059,24 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
        return tp_len;
 }
 
+static struct net_device *packet_cached_dev_get(struct packet_sock *po)
+{
+       struct net_device *dev;
+
+       rcu_read_lock();
+       dev = rcu_dereference(po->cached_dev);
+       if (dev)
+               dev_hold(dev);
+       rcu_read_unlock();
+
+       return dev;
+}
+
 static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
 {
        struct sk_buff *skb;
        struct net_device *dev;
        __be16 proto;
-       bool need_rls_dev = false;
        int err, reserve = 0;
        void *ph;
        struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name;
@@ -2070,7 +2089,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
        mutex_lock(&po->pg_vec_lock);
 
        if (saddr == NULL) {
-               dev = po->prot_hook.dev;
+               dev     = packet_cached_dev_get(po);
                proto   = po->num;
                addr    = NULL;
        } else {
@@ -2084,19 +2103,17 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
                proto   = saddr->sll_protocol;
                addr    = saddr->sll_addr;
                dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
-               need_rls_dev = true;
        }
 
        err = -ENXIO;
        if (unlikely(dev == NULL))
                goto out;
-
-       reserve = dev->hard_header_len;
-
        err = -ENETDOWN;
        if (unlikely(!(dev->flags & IFF_UP)))
                goto out_put;
 
+       reserve = dev->hard_header_len;
+
        size_max = po->tx_ring.frame_size
                - (po->tp_hdrlen - sizeof(struct sockaddr_ll));
 
@@ -2173,8 +2190,7 @@ out_status:
        __packet_set_status(po, ph, status);
        kfree_skb(skb);
 out_put:
-       if (need_rls_dev)
-               dev_put(dev);
+       dev_put(dev);
 out:
        mutex_unlock(&po->pg_vec_lock);
        return err;
@@ -2212,7 +2228,6 @@ static int packet_snd(struct socket *sock,
        struct sk_buff *skb;
        struct net_device *dev;
        __be16 proto;
-       bool need_rls_dev = false;
        unsigned char *addr;
        int err, reserve = 0;
        struct virtio_net_hdr vnet_hdr = { 0 };
@@ -2228,7 +2243,7 @@ static int packet_snd(struct socket *sock,
         */
 
        if (saddr == NULL) {
-               dev = po->prot_hook.dev;
+               dev     = packet_cached_dev_get(po);
                proto   = po->num;
                addr    = NULL;
        } else {
@@ -2240,19 +2255,17 @@ static int packet_snd(struct socket *sock,
                proto   = saddr->sll_protocol;
                addr    = saddr->sll_addr;
                dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex);
-               need_rls_dev = true;
        }
 
        err = -ENXIO;
-       if (dev == NULL)
+       if (unlikely(dev == NULL))
                goto out_unlock;
-       if (sock->type == SOCK_RAW)
-               reserve = dev->hard_header_len;
-
        err = -ENETDOWN;
-       if (!(dev->flags & IFF_UP))
+       if (unlikely(!(dev->flags & IFF_UP)))
                goto out_unlock;
 
+       if (sock->type == SOCK_RAW)
+               reserve = dev->hard_header_len;
        if (po->has_vnet_hdr) {
                vnet_hdr_len = sizeof(vnet_hdr);
 
@@ -2386,15 +2399,14 @@ static int packet_snd(struct socket *sock,
        if (err > 0 && (err = net_xmit_errno(err)) != 0)
                goto out_unlock;
 
-       if (need_rls_dev)
-               dev_put(dev);
+       dev_put(dev);
 
        return len;
 
 out_free:
        kfree_skb(skb);
 out_unlock:
-       if (dev && need_rls_dev)
+       if (dev)
                dev_put(dev);
 out:
        return err;
@@ -2614,6 +2626,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol,
        po = pkt_sk(sk);
        sk->sk_family = PF_PACKET;
        po->num = proto;
+       RCU_INIT_POINTER(po->cached_dev, NULL);
 
        sk->sk_destruct = packet_sock_destruct;
        sk_refcnt_debug_inc(sk);
@@ -2660,7 +2673,6 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
        struct sock *sk = sock->sk;
        struct sk_buff *skb;
        int copied, err;
-       struct sockaddr_ll *sll;
        int vnet_hdr_len = 0;
 
        err = -EINVAL;
@@ -2744,22 +2756,10 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
                        goto out_free;
        }
 
-       /*
-        *      If the address length field is there to be filled in, we fill
-        *      it in now.
+       /* You lose any data beyond the buffer you gave. If it worries
+        * a user program they can ask the device for its MTU
+        * anyway.
         */
-
-       sll = &PACKET_SKB_CB(skb)->sa.ll;
-       if (sock->type == SOCK_PACKET)
-               msg->msg_namelen = sizeof(struct sockaddr_pkt);
-       else
-               msg->msg_namelen = sll->sll_halen + offsetof(struct sockaddr_ll, sll_addr);
-
-       /*
-        *      You lose any data beyond the buffer you gave. If it worries a
-        *      user program they can ask the device for its MTU anyway.
-        */
-
        copied = skb->len;
        if (copied > len) {
                copied = len;
@@ -2772,9 +2772,20 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        sock_recv_ts_and_drops(msg, sk, skb);
 
-       if (msg->msg_name)
+       if (msg->msg_name) {
+               /* If the address length field is there to be filled
+                * in, we fill it in now.
+                */
+               if (sock->type == SOCK_PACKET) {
+                       msg->msg_namelen = sizeof(struct sockaddr_pkt);
+               } else {
+                       struct sockaddr_ll *sll = &PACKET_SKB_CB(skb)->sa.ll;
+                       msg->msg_namelen = sll->sll_halen +
+                               offsetof(struct sockaddr_ll, sll_addr);
+               }
                memcpy(msg->msg_name, &PACKET_SKB_CB(skb)->sa,
                       msg->msg_namelen);
+       }
 
        if (pkt_sk(sk)->auxdata) {
                struct tpacket_auxdata aux;
index c4e4b45..1035fa2 100644 (file)
@@ -113,6 +113,7 @@ struct packet_sock {
        unsigned int            tp_loss:1;
        unsigned int            tp_tx_has_off:1;
        unsigned int            tp_tstamp;
+       struct net_device __rcu *cached_dev;
        struct packet_type      prot_hook ____cacheline_aligned_in_smp;
 };
 
index 12c30f3..38946b2 100644 (file)
@@ -139,9 +139,6 @@ static int pn_recvmsg(struct kiocb *iocb, struct sock *sk,
                        MSG_CMSG_COMPAT))
                goto out_nofree;
 
-       if (addr_len)
-               *addr_len = sizeof(sa);
-
        skb = skb_recv_datagram(sk, flags, noblock, &rval);
        if (skb == NULL)
                goto out_nofree;
@@ -162,8 +159,10 @@ static int pn_recvmsg(struct kiocb *iocb, struct sock *sk,
 
        rval = (flags & MSG_TRUNC) ? skb->len : copylen;
 
-       if (msg->msg_name != NULL)
-               memcpy(msg->msg_name, &sa, sizeof(struct sockaddr_pn));
+       if (msg->msg_name != NULL) {
+               memcpy(msg->msg_name, &sa, sizeof(sa));
+               *addr_len = sizeof(sa);
+       }
 
 out:
        skb_free_datagram(sk, skb);
index 77e38f7..008214a 100644 (file)
@@ -595,26 +595,25 @@ static void pn_sock_seq_stop(struct seq_file *seq, void *v)
 
 static int pn_sock_seq_show(struct seq_file *seq, void *v)
 {
-       int len;
-
+       seq_setwidth(seq, 127);
        if (v == SEQ_START_TOKEN)
-               seq_printf(seq, "%s%n", "pt  loc  rem rs st tx_queue rx_queue "
-                       "  uid inode ref pointer drops", &len);
+               seq_puts(seq, "pt  loc  rem rs st tx_queue rx_queue "
+                       "  uid inode ref pointer drops");
        else {
                struct sock *sk = v;
                struct pn_sock *pn = pn_sk(sk);
 
                seq_printf(seq, "%2d %04X:%04X:%02X %02X %08X:%08X %5d %lu "
-                       "%d %pK %d%n",
+                       "%d %pK %d",
                        sk->sk_protocol, pn->sobject, pn->dobject,
                        pn->resource, sk->sk_state,
                        sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk),
                        from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
                        sock_i_ino(sk),
                        atomic_read(&sk->sk_refcnt), sk,
-                       atomic_read(&sk->sk_drops), &len);
+                       atomic_read(&sk->sk_drops));
        }
-       seq_printf(seq, "%*s\n", 127 - len, "");
+       seq_pad(seq, '\n');
        return 0;
 }
 
@@ -785,20 +784,19 @@ static void pn_res_seq_stop(struct seq_file *seq, void *v)
 
 static int pn_res_seq_show(struct seq_file *seq, void *v)
 {
-       int len;
-
+       seq_setwidth(seq, 63);
        if (v == SEQ_START_TOKEN)
-               seq_printf(seq, "%s%n", "rs   uid inode", &len);
+               seq_puts(seq, "rs   uid inode");
        else {
                struct sock **psk = v;
                struct sock *sk = *psk;
 
-               seq_printf(seq, "%02X %5u %lu%n",
+               seq_printf(seq, "%02X %5u %lu",
                           (int) (psk - pnres.sk),
                           from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
-                          sock_i_ino(sk), &len);
+                          sock_i_ino(sk));
        }
-       seq_printf(seq, "%*s\n", 63 - len, "");
+       seq_pad(seq, '\n');
        return 0;
 }
 
index 9f0f17c..de339b2 100644 (file)
@@ -410,8 +410,6 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
 
        rdsdebug("size %zu flags 0x%x timeo %ld\n", size, msg_flags, timeo);
 
-       msg->msg_namelen = 0;
-
        if (msg_flags & MSG_OOB)
                goto out;
 
index e98fcfb..33af772 100644 (file)
@@ -1216,7 +1216,6 @@ static int rose_recvmsg(struct kiocb *iocb, struct socket *sock,
 {
        struct sock *sk = sock->sk;
        struct rose_sock *rose = rose_sk(sk);
-       struct sockaddr_rose *srose = (struct sockaddr_rose *)msg->msg_name;
        size_t copied;
        unsigned char *asmptr;
        struct sk_buff *skb;
@@ -1252,8 +1251,11 @@ static int rose_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 
-       if (srose != NULL) {
-               memset(srose, 0, msg->msg_namelen);
+       if (msg->msg_name) {
+               struct sockaddr_rose *srose;
+
+               memset(msg->msg_name, 0, sizeof(struct full_sockaddr_rose));
+               srose = msg->msg_name;
                srose->srose_family = AF_ROSE;
                srose->srose_addr   = rose->dest_addr;
                srose->srose_call   = rose->dest_call;
index 4b48687..898492a 100644 (file)
@@ -143,10 +143,13 @@ int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock,
 
                /* copy the peer address and timestamp */
                if (!continue_call) {
-                       if (msg->msg_name && msg->msg_namelen > 0)
+                       if (msg->msg_name) {
+                               size_t len =
+                                       sizeof(call->conn->trans->peer->srx);
                                memcpy(msg->msg_name,
-                                      &call->conn->trans->peer->srx,
-                                      sizeof(call->conn->trans->peer->srx));
+                                      &call->conn->trans->peer->srx, len);
+                               msg->msg_namelen = len;
+                       }
                        sock_recv_ts_and_drops(msg, &rx->sk, skb);
                }
 
index fdc041c..95d8439 100644 (file)
@@ -88,7 +88,7 @@ struct fq_sched_data {
        struct fq_flow  internal;       /* for non classified or high prio packets */
        u32             quantum;
        u32             initial_quantum;
-       u32             flow_default_rate;/* rate per flow : bytes per second */
+       u32             flow_refill_delay;
        u32             flow_max_rate;  /* optional max rate per flow */
        u32             flow_plimit;    /* max packets per flow */
        struct rb_root  *fq_root;
@@ -115,6 +115,7 @@ static struct fq_flow detached, throttled;
 static void fq_flow_set_detached(struct fq_flow *f)
 {
        f->next = &detached;
+       f->age = jiffies;
 }
 
 static bool fq_flow_is_detached(const struct fq_flow *f)
@@ -209,21 +210,15 @@ static void fq_gc(struct fq_sched_data *q,
        }
 }
 
-static const u8 prio2band[TC_PRIO_MAX + 1] = {
-       1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1
-};
-
 static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q)
 {
        struct rb_node **p, *parent;
        struct sock *sk = skb->sk;
        struct rb_root *root;
        struct fq_flow *f;
-       int band;
 
        /* warning: no starvation prevention... */
-       band = prio2band[skb->priority & TC_PRIO_MAX];
-       if (unlikely(band == 0))
+       if (unlikely((skb->priority & TC_PRIO_MAX) == TC_PRIO_CONTROL))
                return &q->internal;
 
        if (unlikely(!sk)) {
@@ -373,17 +368,20 @@ static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
        }
 
        f->qlen++;
-       flow_queue_add(f, skb);
        if (skb_is_retransmit(skb))
                q->stat_tcp_retrans++;
        sch->qstats.backlog += qdisc_pkt_len(skb);
        if (fq_flow_is_detached(f)) {
                fq_flow_add_tail(&q->new_flows, f);
-               if (q->quantum > f->credit)
-                       f->credit = q->quantum;
+               if (time_after(jiffies, f->age + q->flow_refill_delay))
+                       f->credit = max_t(u32, f->credit, q->quantum);
                q->inactive_flows--;
                qdisc_unthrottled(sch);
        }
+
+       /* Note: this overwrites f->age */
+       flow_queue_add(f, skb);
+
        if (unlikely(f == &q->internal)) {
                q->stat_internal_packets++;
                qdisc_unthrottled(sch);
@@ -461,7 +459,6 @@ begin:
                        fq_flow_add_tail(&q->old_flows, f);
                } else {
                        fq_flow_set_detached(f);
-                       f->age = jiffies;
                        q->inactive_flows++;
                }
                goto begin;
@@ -615,6 +612,7 @@ static const struct nla_policy fq_policy[TCA_FQ_MAX + 1] = {
        [TCA_FQ_FLOW_DEFAULT_RATE]      = { .type = NLA_U32 },
        [TCA_FQ_FLOW_MAX_RATE]          = { .type = NLA_U32 },
        [TCA_FQ_BUCKETS_LOG]            = { .type = NLA_U32 },
+       [TCA_FQ_FLOW_REFILL_DELAY]      = { .type = NLA_U32 },
 };
 
 static int fq_change(struct Qdisc *sch, struct nlattr *opt)
@@ -656,7 +654,8 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt)
                q->initial_quantum = nla_get_u32(tb[TCA_FQ_INITIAL_QUANTUM]);
 
        if (tb[TCA_FQ_FLOW_DEFAULT_RATE])
-               q->flow_default_rate = nla_get_u32(tb[TCA_FQ_FLOW_DEFAULT_RATE]);
+               pr_warn_ratelimited("sch_fq: defrate %u ignored.\n",
+                                   nla_get_u32(tb[TCA_FQ_FLOW_DEFAULT_RATE]));
 
        if (tb[TCA_FQ_FLOW_MAX_RATE])
                q->flow_max_rate = nla_get_u32(tb[TCA_FQ_FLOW_MAX_RATE]);
@@ -670,6 +669,12 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt)
                        err = -EINVAL;
        }
 
+       if (tb[TCA_FQ_FLOW_REFILL_DELAY]) {
+               u32 usecs_delay = nla_get_u32(tb[TCA_FQ_FLOW_REFILL_DELAY]) ;
+
+               q->flow_refill_delay = usecs_to_jiffies(usecs_delay);
+       }
+
        if (!err)
                err = fq_resize(q, fq_log);
 
@@ -705,7 +710,7 @@ static int fq_init(struct Qdisc *sch, struct nlattr *opt)
        q->flow_plimit          = 100;
        q->quantum              = 2 * psched_mtu(qdisc_dev(sch));
        q->initial_quantum      = 10 * psched_mtu(qdisc_dev(sch));
-       q->flow_default_rate    = 0;
+       q->flow_refill_delay    = msecs_to_jiffies(40);
        q->flow_max_rate        = ~0U;
        q->rate_enable          = 1;
        q->new_flows.first      = NULL;
@@ -732,15 +737,16 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb)
        if (opts == NULL)
                goto nla_put_failure;
 
-       /* TCA_FQ_FLOW_DEFAULT_RATE is not used anymore,
-        * do not bother giving its value
-        */
+       /* TCA_FQ_FLOW_DEFAULT_RATE is not used anymore */
+
        if (nla_put_u32(skb, TCA_FQ_PLIMIT, sch->limit) ||
            nla_put_u32(skb, TCA_FQ_FLOW_PLIMIT, q->flow_plimit) ||
            nla_put_u32(skb, TCA_FQ_QUANTUM, q->quantum) ||
            nla_put_u32(skb, TCA_FQ_INITIAL_QUANTUM, q->initial_quantum) ||
            nla_put_u32(skb, TCA_FQ_RATE_ENABLE, q->rate_enable) ||
            nla_put_u32(skb, TCA_FQ_FLOW_MAX_RATE, q->flow_max_rate) ||
+           nla_put_u32(skb, TCA_FQ_FLOW_REFILL_DELAY,
+                       jiffies_to_usecs(q->flow_refill_delay)) ||
            nla_put_u32(skb, TCA_FQ_BUCKETS_LOG, q->fq_trees_log))
                goto nla_put_failure;
 
index c9b91cb..68a27f9 100644 (file)
@@ -907,8 +907,8 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
                if (!first || t->last_time_heard > first->last_time_heard) {
                        second = first;
                        first = t;
-               }
-               if (!second || t->last_time_heard > second->last_time_heard)
+               } else if (!second ||
+                          t->last_time_heard > second->last_time_heard)
                        second = t;
        }
 
@@ -929,6 +929,8 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
                first = asoc->peer.primary_path;
        }
 
+       if (!second)
+               second = first;
        /* If we failed to find a usable transport, just camp on the
         * primary, even if it is inactive.
         */
index 5ea573b..647396b 100644 (file)
@@ -79,12 +79,13 @@ static sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = {
  */
 static int sctp_objcnt_seq_show(struct seq_file *seq, void *v)
 {
-       int i, len;
+       int i;
 
        i = (int)*(loff_t *)v;
-       seq_printf(seq, "%s: %d%n", sctp_dbg_objcnt[i].label,
-                               atomic_read(sctp_dbg_objcnt[i].counter), &len);
-       seq_printf(seq, "%*s\n", 127 - len, "");
+       seq_setwidth(seq, 127);
+       seq_printf(seq, "%s: %d", sctp_dbg_objcnt[i].label,
+                               atomic_read(sctp_dbg_objcnt[i].counter));
+       seq_pad(seq, '\n');
        return 0;
 }
 
index c226ace..0b18693 100644 (file)
@@ -221,12 +221,13 @@ static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen,
        int err;
        int len;
 
+       BUG_ON(klen > sizeof(struct sockaddr_storage));
        err = get_user(len, ulen);
        if (err)
                return err;
        if (len > klen)
                len = klen;
-       if (len < 0 || len > sizeof(struct sockaddr_storage))
+       if (len < 0)
                return -EINVAL;
        if (len) {
                if (audit_sockaddr(klen, kaddr))
@@ -1840,8 +1841,10 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
        msg.msg_iov = &iov;
        iov.iov_len = size;
        iov.iov_base = ubuf;
-       msg.msg_name = (struct sockaddr *)&address;
-       msg.msg_namelen = sizeof(address);
+       /* Save some cycles and don't copy the address if not needed */
+       msg.msg_name = addr ? (struct sockaddr *)&address : NULL;
+       /* We assume all kernel code knows the size of sockaddr_storage */
+       msg.msg_namelen = 0;
        if (sock->file->f_flags & O_NONBLOCK)
                flags |= MSG_DONTWAIT;
        err = sock_recvmsg(sock, &msg, size, flags);
@@ -2221,16 +2224,14 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
                        goto out;
        }
 
-       /*
-        *      Save the user-mode address (verify_iovec will change the
-        *      kernel msghdr to use the kernel address space)
+       /* Save the user-mode address (verify_iovec will change the
+        * kernel msghdr to use the kernel address space)
         */
-
        uaddr = (__force void __user *)msg_sys->msg_name;
        uaddr_len = COMPAT_NAMELEN(msg);
-       if (MSG_CMSG_COMPAT & flags) {
+       if (MSG_CMSG_COMPAT & flags)
                err = verify_compat_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
-       else
+       else
                err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
        if (err < 0)
                goto out_freeiov;
@@ -2239,6 +2240,9 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
        cmsg_ptr = (unsigned long)msg_sys->msg_control;
        msg_sys->msg_flags = flags & (MSG_CMSG_CLOEXEC|MSG_CMSG_COMPAT);
 
+       /* We assume all kernel code knows the size of sockaddr_storage */
+       msg_sys->msg_namelen = 0;
+
        if (sock->file->f_flags & O_NONBLOCK)
                flags |= MSG_DONTWAIT;
        err = (nosec ? sock_recvmsg_nosec : sock_recvmsg)(sock, msg_sys,
index 6cd930f..6c981dd 100644 (file)
@@ -150,7 +150,6 @@ gss_verify_mic_v2(struct krb5_ctx *ctx,
        struct xdr_netobj cksumobj = {.len = sizeof(cksumdata),
                                      .data = cksumdata};
        s32 now;
-       u64 seqnum;
        u8 *ptr = read_token->data;
        u8 *cksumkey;
        u8 flags;
@@ -197,9 +196,10 @@ gss_verify_mic_v2(struct krb5_ctx *ctx,
        if (now > ctx->endtime)
                return GSS_S_CONTEXT_EXPIRED;
 
-       /* do sequencing checks */
-
-       seqnum = be64_to_cpup((__be64 *)ptr + 8);
+       /*
+        * NOTE: the sequence number at ptr + 8 is skipped, rpcsec_gss
+        * doesn't want it checked; see page 6 of rfc 2203.
+        */
 
        return GSS_S_COMPLETE;
 }
index 1da52d1..42560e5 100644 (file)
@@ -489,7 +489,6 @@ static u32
 gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
 {
        s32             now;
-       u64             seqnum;
        u8              *ptr;
        u8              flags = 0x00;
        u16             ec, rrc;
@@ -525,7 +524,10 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
        ec = be16_to_cpup((__be16 *)(ptr + 4));
        rrc = be16_to_cpup((__be16 *)(ptr + 6));
 
-       seqnum = be64_to_cpup((__be64 *)(ptr + 8));
+       /*
+        * NOTE: the sequence number at ptr + 8 is skipped, rpcsec_gss
+        * doesn't want it checked; see page 6 of rfc 2203.
+        */
 
        if (rrc != 0)
                rotate_left(offset + 16, buf, rrc);
@@ -574,8 +576,8 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
        buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
        buf->len -= GSS_KRB5_TOK_HDR_LEN + headskip;
 
-       /* Trim off the checksum blob */
-       xdr_buf_trim(buf, GSS_KRB5_TOK_HDR_LEN + tailskip);
+       /* Trim off the trailing "extra count" and checksum blob */
+       xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
        return GSS_S_COMPLETE;
 }
 
index f1eb0d1..458f85e 100644 (file)
@@ -298,7 +298,8 @@ int gssp_accept_sec_context_upcall(struct net *net,
        if (res.context_handle) {
                data->out_handle = rctxh.exported_context_token;
                data->mech_oid.len = rctxh.mech.len;
-               memcpy(data->mech_oid.data, rctxh.mech.data,
+               if (rctxh.mech.data)
+                       memcpy(data->mech_oid.data, rctxh.mech.data,
                                                data->mech_oid.len);
                client_name = rctxh.src_name.display_name;
        }
index f0f78c5..1ec19f6 100644 (file)
@@ -559,6 +559,8 @@ static int gssx_enc_cred(struct xdr_stream *xdr,
 
        /* cred->elements */
        err = dummy_enc_credel_array(xdr, &cred->elements);
+       if (err)
+               return err;
 
        /* cred->cred_handle_reference */
        err = gssx_enc_buffer(xdr, &cred->cred_handle_reference);
@@ -740,22 +742,20 @@ void gssx_enc_accept_sec_context(struct rpc_rqst *req,
                goto done;
 
        /* arg->context_handle */
-       if (arg->context_handle) {
+       if (arg->context_handle)
                err = gssx_enc_ctx(xdr, arg->context_handle);
-               if (err)
-                       goto done;
-       } else {
+       else
                err = gssx_enc_bool(xdr, 0);
-       }
+       if (err)
+               goto done;
 
        /* arg->cred_handle */
-       if (arg->cred_handle) {
+       if (arg->cred_handle)
                err = gssx_enc_cred(xdr, arg->cred_handle);
-               if (err)
-                       goto done;
-       } else {
+       else
                err = gssx_enc_bool(xdr, 0);
-       }
+       if (err)
+               goto done;
 
        /* arg->input_token */
        err = gssx_enc_in_token(xdr, &arg->input_token);
@@ -763,13 +763,12 @@ void gssx_enc_accept_sec_context(struct rpc_rqst *req,
                goto done;
 
        /* arg->input_cb */
-       if (arg->input_cb) {
+       if (arg->input_cb)
                err = gssx_enc_cb(xdr, arg->input_cb);
-               if (err)
-                       goto done;
-       } else {
+       else
                err = gssx_enc_bool(xdr, 0);
-       }
+       if (err)
+               goto done;
 
        err = gssx_enc_bool(xdr, arg->ret_deleg_cred);
        if (err)
index 09fb638..008cdad 100644 (file)
@@ -1167,8 +1167,8 @@ static int gss_proxy_save_rsc(struct cache_detail *cd,
        if (!ud->found_creds) {
                /* userspace seem buggy, we should always get at least a
                 * mapping to nobody */
-               dprintk("RPC:       No creds found, marking Negative!\n");
-               set_bit(CACHE_NEGATIVE, &rsci.h.flags);
+               dprintk("RPC:       No creds found!\n");
+               goto out;
        } else {
 
                /* steal creds */
index dab09da..f09b7db 100644 (file)
@@ -750,14 +750,16 @@ EXPORT_SYMBOL_GPL(rpc_shutdown_client);
 /*
  * Free an RPC client
  */
-static void
+static struct rpc_clnt *
 rpc_free_client(struct rpc_clnt *clnt)
 {
+       struct rpc_clnt *parent = NULL;
+
        dprintk_rcu("RPC:       destroying %s client for %s\n",
                        clnt->cl_program->name,
                        rcu_dereference(clnt->cl_xprt)->servername);
        if (clnt->cl_parent != clnt)
-               rpc_release_client(clnt->cl_parent);
+               parent = clnt->cl_parent;
        rpc_clnt_remove_pipedir(clnt);
        rpc_unregister_client(clnt);
        rpc_free_iostats(clnt->cl_metrics);
@@ -766,18 +768,17 @@ rpc_free_client(struct rpc_clnt *clnt)
        rpciod_down();
        rpc_free_clid(clnt);
        kfree(clnt);
+       return parent;
 }
 
 /*
  * Free an RPC client
  */
-static void
+static struct rpc_clnt * 
 rpc_free_auth(struct rpc_clnt *clnt)
 {
-       if (clnt->cl_auth == NULL) {
-               rpc_free_client(clnt);
-               return;
-       }
+       if (clnt->cl_auth == NULL)
+               return rpc_free_client(clnt);
 
        /*
         * Note: RPCSEC_GSS may need to send NULL RPC calls in order to
@@ -788,7 +789,8 @@ rpc_free_auth(struct rpc_clnt *clnt)
        rpcauth_release(clnt->cl_auth);
        clnt->cl_auth = NULL;
        if (atomic_dec_and_test(&clnt->cl_count))
-               rpc_free_client(clnt);
+               return rpc_free_client(clnt);
+       return NULL;
 }
 
 /*
@@ -799,10 +801,13 @@ rpc_release_client(struct rpc_clnt *clnt)
 {
        dprintk("RPC:       rpc_release_client(%p)\n", clnt);
 
-       if (list_empty(&clnt->cl_tasks))
-               wake_up(&destroy_wait);
-       if (atomic_dec_and_test(&clnt->cl_count))
-               rpc_free_auth(clnt);
+       do {
+               if (list_empty(&clnt->cl_tasks))
+                       wake_up(&destroy_wait);
+               if (!atomic_dec_and_test(&clnt->cl_count))
+                       break;
+               clnt = rpc_free_auth(clnt);
+       } while (clnt != NULL);
 }
 EXPORT_SYMBOL_GPL(rpc_release_client);
 
index d0d14a0..bf04b30 100644 (file)
@@ -471,15 +471,6 @@ struct rpc_filelist {
        umode_t mode;
 };
 
-static int rpc_delete_dentry(const struct dentry *dentry)
-{
-       return 1;
-}
-
-static const struct dentry_operations rpc_dentry_operations = {
-       .d_delete = rpc_delete_dentry,
-};
-
 static struct inode *
 rpc_get_inode(struct super_block *sb, umode_t mode)
 {
@@ -1266,7 +1257,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
        sb->s_magic = RPCAUTH_GSSMAGIC;
        sb->s_op = &s_ops;
-       sb->s_d_op = &rpc_dentry_operations;
+       sb->s_d_op = &simple_dentry_operations;
        sb->s_time_gran = 1;
 
        inode = rpc_get_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
index b974571..e7fbe36 100644 (file)
@@ -1104,8 +1104,6 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
        rqstp->rq_vers = vers = svc_getnl(argv);        /* version number */
        rqstp->rq_proc = proc = svc_getnl(argv);        /* procedure number */
 
-       progp = serv->sv_program;
-
        for (progp = serv->sv_program; progp; progp = progp->pg_next)
                if (prog == progp->pg_prog)
                        break;
index 17c8892..dd9d295 100644 (file)
@@ -393,8 +393,10 @@ static int xs_send_kvec(struct socket *sock, struct sockaddr *addr, int addrlen,
        return kernel_sendmsg(sock, &msg, NULL, 0, 0);
 }
 
-static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned int base, int more)
+static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned int base, int more, bool zerocopy)
 {
+       ssize_t (*do_sendpage)(struct socket *sock, struct page *page,
+                       int offset, size_t size, int flags);
        struct page **ppage;
        unsigned int remainder;
        int err, sent = 0;
@@ -403,6 +405,9 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i
        base += xdr->page_base;
        ppage = xdr->pages + (base >> PAGE_SHIFT);
        base &= ~PAGE_MASK;
+       do_sendpage = sock->ops->sendpage;
+       if (!zerocopy)
+               do_sendpage = sock_no_sendpage;
        for(;;) {
                unsigned int len = min_t(unsigned int, PAGE_SIZE - base, remainder);
                int flags = XS_SENDMSG_FLAGS;
@@ -410,7 +415,7 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i
                remainder -= len;
                if (remainder != 0 || more)
                        flags |= MSG_MORE;
-               err = sock->ops->sendpage(sock, *ppage, base, len, flags);
+               err = do_sendpage(sock, *ppage, base, len, flags);
                if (remainder == 0 || err != len)
                        break;
                sent += err;
@@ -431,9 +436,10 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i
  * @addrlen: UDP only -- length of destination address
  * @xdr: buffer containing this request
  * @base: starting position in the buffer
+ * @zerocopy: true if it is safe to use sendpage()
  *
  */
-static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base)
+static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base, bool zerocopy)
 {
        unsigned int remainder = xdr->len - base;
        int err, sent = 0;
@@ -461,7 +467,7 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen,
        if (base < xdr->page_len) {
                unsigned int len = xdr->page_len - base;
                remainder -= len;
-               err = xs_send_pagedata(sock, xdr, base, remainder != 0);
+               err = xs_send_pagedata(sock, xdr, base, remainder != 0, zerocopy);
                if (remainder == 0 || err != len)
                        goto out;
                sent += err;
@@ -564,7 +570,7 @@ static int xs_local_send_request(struct rpc_task *task)
                        req->rq_svec->iov_base, req->rq_svec->iov_len);
 
        status = xs_sendpages(transport->sock, NULL, 0,
-                                               xdr, req->rq_bytes_sent);
+                                               xdr, req->rq_bytes_sent, true);
        dprintk("RPC:       %s(%u) = %d\n",
                        __func__, xdr->len - req->rq_bytes_sent, status);
        if (likely(status >= 0)) {
@@ -620,7 +626,7 @@ static int xs_udp_send_request(struct rpc_task *task)
        status = xs_sendpages(transport->sock,
                              xs_addr(xprt),
                              xprt->addrlen, xdr,
-                             req->rq_bytes_sent);
+                             req->rq_bytes_sent, true);
 
        dprintk("RPC:       xs_udp_send_request(%u) = %d\n",
                        xdr->len - req->rq_bytes_sent, status);
@@ -693,6 +699,7 @@ static int xs_tcp_send_request(struct rpc_task *task)
        struct rpc_xprt *xprt = req->rq_xprt;
        struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
        struct xdr_buf *xdr = &req->rq_snd_buf;
+       bool zerocopy = true;
        int status;
 
        xs_encode_stream_record_marker(&req->rq_snd_buf);
@@ -700,13 +707,20 @@ static int xs_tcp_send_request(struct rpc_task *task)
        xs_pktdump("packet data:",
                                req->rq_svec->iov_base,
                                req->rq_svec->iov_len);
+       /* Don't use zero copy if this is a resend. If the RPC call
+        * completes while the socket holds a reference to the pages,
+        * then we may end up resending corrupted data.
+        */
+       if (task->tk_flags & RPC_TASK_SENT)
+               zerocopy = false;
 
        /* Continue transmitting the packet/record. We must be careful
         * to cope with writespace callbacks arriving _after_ we have
         * called sendmsg(). */
        while (1) {
                status = xs_sendpages(transport->sock,
-                                       NULL, 0, xdr, req->rq_bytes_sent);
+                                       NULL, 0, xdr, req->rq_bytes_sent,
+                                       zerocopy);
 
                dprintk("RPC:       xs_tcp_send_request(%u) = %d\n",
                                xdr->len - req->rq_bytes_sent, status);
index cf465d6..69cd9bf 100644 (file)
@@ -2358,7 +2358,8 @@ int tipc_link_recv_fragment(struct sk_buff **head, struct sk_buff **tail,
                *head = frag;
                skb_frag_list_init(*head);
                return 0;
-       } else if (skb_try_coalesce(*head, frag, &headstolen, &delta)) {
+       } else if (*head &&
+                  skb_try_coalesce(*head, frag, &headstolen, &delta)) {
                kfree_skb_partial(frag, headstolen);
        } else {
                if (!*head)
index 8bcd498..9f72a63 100644 (file)
@@ -76,9 +76,11 @@ static struct genl_family tipc_genl_family = {
        .maxattr        = 0,
 };
 
-static struct genl_ops tipc_genl_ops = {
-       .cmd            = TIPC_GENL_CMD,
-       .doit           = handle_cmd,
+static struct genl_ops tipc_genl_ops[] = {
+       {
+               .cmd            = TIPC_GENL_CMD,
+               .doit           = handle_cmd,
+       },
 };
 
 static int tipc_genl_family_registered;
@@ -87,8 +89,7 @@ int tipc_netlink_start(void)
 {
        int res;
 
-       res = genl_register_family_with_ops(&tipc_genl_family,
-               &tipc_genl_ops, 1);
+       res = genl_register_family_with_ops(&tipc_genl_family, tipc_genl_ops);
        if (res) {
                pr_err("Failed to register netlink interface\n");
                return res;
index 3906527..3b61851 100644 (file)
@@ -980,9 +980,6 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock,
                goto exit;
        }
 
-       /* will be updated in set_orig_addr() if needed */
-       m->msg_namelen = 0;
-
        timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
 restart:
 
@@ -1091,9 +1088,6 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock,
                goto exit;
        }
 
-       /* will be updated in set_orig_addr() if needed */
-       m->msg_namelen = 0;
-
        target = sock_rcvlowat(sk, flags & MSG_WAITALL, buf_len);
        timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
 
index c1f403b..01625cc 100644 (file)
@@ -1754,7 +1754,6 @@ static void unix_copy_addr(struct msghdr *msg, struct sock *sk)
 {
        struct unix_sock *u = unix_sk(sk);
 
-       msg->msg_namelen = 0;
        if (u->addr) {
                msg->msg_namelen = u->addr->len;
                memcpy(msg->msg_name, u->addr->name, u->addr->len);
@@ -1778,8 +1777,6 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (flags&MSG_OOB)
                goto out;
 
-       msg->msg_namelen = 0;
-
        err = mutex_lock_interruptible(&u->readlock);
        if (err) {
                err = sock_intr_errno(sock_rcvtimeo(sk, noblock));
@@ -1924,8 +1921,6 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
        target = sock_rcvlowat(sk, flags&MSG_WAITALL, size);
        timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT);
 
-       msg->msg_namelen = 0;
-
        /* Lock the socket to prevent queue disordering
         * while sleeps in memcpy_tomsg
         */
index b5fa7e4..14810ab 100644 (file)
@@ -6,7 +6,7 @@ config VSOCKETS
        tristate "Virtual Socket protocol"
        help
          Virtual Socket Protocol is a socket protocol similar to TCP/IP
-         allowing comunication between Virtual Machines and hypervisor
+         allowing communication between Virtual Machines and hypervisor
          or host.
 
          You should also select one or more hypervisor-specific transports
index 545c08b..5adfd94 100644 (file)
@@ -1662,8 +1662,6 @@ vsock_stream_recvmsg(struct kiocb *kiocb,
        vsk = vsock_sk(sk);
        err = 0;
 
-       msg->msg_namelen = 0;
-
        lock_sock(sk);
 
        if (sk->sk_state != SS_CONNECTED) {
index 9d69866..687360d 100644 (file)
@@ -1746,8 +1746,6 @@ static int vmci_transport_dgram_dequeue(struct kiocb *kiocb,
        if (flags & MSG_OOB || flags & MSG_ERRQUEUE)
                return -EOPNOTSUPP;
 
-       msg->msg_namelen = 0;
-
        /* Retrieve the head sk_buff from the socket's receive queue. */
        err = 0;
        skb = skb_recv_datagram(&vsk->sk, flags, noblock, &err);
index 0694d62..c278b33 100644 (file)
@@ -279,7 +279,7 @@ int wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb)
 
        d_printf(1, dev, "CTX: wimax msg, %zu bytes\n", size);
        d_dump(2, dev, msg, size);
-       genlmsg_multicast(skb, 0, wimax_gnl_mcg.id, GFP_KERNEL);
+       genlmsg_multicast(&wimax_gnl_family, skb, 0, 0, GFP_KERNEL);
        d_printf(1, dev, "CTX: genl multicast done\n");
        return 0;
 }
@@ -321,17 +321,6 @@ int wimax_msg(struct wimax_dev *wimax_dev, const char *pipe_name,
 }
 EXPORT_SYMBOL_GPL(wimax_msg);
 
-
-static const struct nla_policy wimax_gnl_msg_policy[WIMAX_GNL_ATTR_MAX + 1] = {
-       [WIMAX_GNL_MSG_IFIDX] = {
-               .type = NLA_U32,
-       },
-       [WIMAX_GNL_MSG_DATA] = {
-               .type = NLA_UNSPEC,     /* libnl doesn't grok BINARY yet */
-       },
-};
-
-
 /*
  * Relays a message from user space to the driver
  *
@@ -340,7 +329,6 @@ static const struct nla_policy wimax_gnl_msg_policy[WIMAX_GNL_ATTR_MAX + 1] = {
  *
  * This call will block while handling/relaying the message.
  */
-static
 int wimax_gnl_doit_msg_from_user(struct sk_buff *skb, struct genl_info *info)
 {
        int result, ifindex;
@@ -418,16 +406,3 @@ error_no_wimax_dev:
        return result;
 }
 
-
-/*
- * Generic Netlink glue
- */
-
-struct genl_ops wimax_gnl_msg_from_user = {
-       .cmd = WIMAX_GNL_OP_MSG_FROM_USER,
-       .flags = GENL_ADMIN_PERM,
-       .policy = wimax_gnl_msg_policy,
-       .doit = wimax_gnl_doit_msg_from_user,
-       .dumpit = NULL,
-};
-
index 7ceffe3..eb45807 100644 (file)
@@ -92,13 +92,6 @@ int wimax_reset(struct wimax_dev *wimax_dev)
 EXPORT_SYMBOL(wimax_reset);
 
 
-static const struct nla_policy wimax_gnl_reset_policy[WIMAX_GNL_ATTR_MAX + 1] = {
-       [WIMAX_GNL_RESET_IFIDX] = {
-               .type = NLA_U32,
-       },
-};
-
-
 /*
  * Exporting to user space over generic netlink
  *
@@ -106,7 +99,6 @@ static const struct nla_policy wimax_gnl_reset_policy[WIMAX_GNL_ATTR_MAX + 1] =
  *
  * No attributes.
  */
-static
 int wimax_gnl_doit_reset(struct sk_buff *skb, struct genl_info *info)
 {
        int result, ifindex;
@@ -130,12 +122,3 @@ error_no_wimax_dev:
        d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
        return result;
 }
-
-
-struct genl_ops wimax_gnl_reset = {
-       .cmd = WIMAX_GNL_OP_RESET,
-       .flags = GENL_ADMIN_PERM,
-       .policy = wimax_gnl_reset_policy,
-       .doit = wimax_gnl_doit_reset,
-       .dumpit = NULL,
-};
index 7ab60ba..403078d 100644 (file)
@@ -411,17 +411,6 @@ void wimax_rfkill_rm(struct wimax_dev *wimax_dev)
  * just query).
  */
 
-static const struct nla_policy wimax_gnl_rfkill_policy[WIMAX_GNL_ATTR_MAX + 1] = {
-       [WIMAX_GNL_RFKILL_IFIDX] = {
-               .type = NLA_U32,
-       },
-       [WIMAX_GNL_RFKILL_STATE] = {
-               .type = NLA_U32         /* enum wimax_rf_state */
-       },
-};
-
-
-static
 int wimax_gnl_doit_rfkill(struct sk_buff *skb, struct genl_info *info)
 {
        int result, ifindex;
@@ -457,13 +446,3 @@ error_no_wimax_dev:
        d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
        return result;
 }
-
-
-struct genl_ops wimax_gnl_rfkill = {
-       .cmd = WIMAX_GNL_OP_RFKILL,
-       .flags = GENL_ADMIN_PERM,
-       .policy = wimax_gnl_rfkill_policy,
-       .doit = wimax_gnl_doit_rfkill,
-       .dumpit = NULL,
-};
-
index aff8776..995c08c 100644 (file)
 #include "debug-levels.h"
 
 
-static const struct nla_policy wimax_gnl_state_get_policy[WIMAX_GNL_ATTR_MAX + 1] = {
-       [WIMAX_GNL_STGET_IFIDX] = {
-               .type = NLA_U32,
-       },
-};
-
-
 /*
  * Exporting to user space over generic netlink
  *
@@ -48,7 +41,6 @@ static const struct nla_policy wimax_gnl_state_get_policy[WIMAX_GNL_ATTR_MAX + 1
  *
  * No attributes.
  */
-static
 int wimax_gnl_doit_state_get(struct sk_buff *skb, struct genl_info *info)
 {
        int result, ifindex;
@@ -72,12 +64,3 @@ error_no_wimax_dev:
        d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
        return result;
 }
-
-
-struct genl_ops wimax_gnl_state_get = {
-       .cmd = WIMAX_GNL_OP_STATE_GET,
-       .flags = GENL_ADMIN_PERM,
-       .policy = wimax_gnl_state_get_policy,
-       .doit = wimax_gnl_doit_state_get,
-       .dumpit = NULL,
-};
index a6470ac..ec8b577 100644 (file)
@@ -116,8 +116,9 @@ struct sk_buff *wimax_gnl_re_state_change_alloc(
                dev_err(dev, "RE_STCH: can't create message\n");
                goto error_new;
        }
-       data = genlmsg_put(report_skb, 0, wimax_gnl_mcg.id, &wimax_gnl_family,
-                          0, WIMAX_GNL_RE_STATE_CHANGE);
+       /* FIXME: sending a group ID as the seq is wrong */
+       data = genlmsg_put(report_skb, 0, wimax_gnl_family.mcgrp_offset,
+                          &wimax_gnl_family, 0, WIMAX_GNL_RE_STATE_CHANGE);
        if (data == NULL) {
                dev_err(dev, "RE_STCH: can't put data into message\n");
                goto error_put;
@@ -177,7 +178,7 @@ int wimax_gnl_re_state_change_send(
                goto out;
        }
        genlmsg_end(report_skb, header);
-       genlmsg_multicast(report_skb, 0, wimax_gnl_mcg.id, GFP_KERNEL);
+       genlmsg_multicast(&wimax_gnl_family, report_skb, 0, 0, GFP_KERNEL);
 out:
        d_fnend(3, dev, "(wimax_dev %p report_skb %p) = %d\n",
                wimax_dev, report_skb, result);
@@ -402,22 +403,44 @@ void wimax_dev_init(struct wimax_dev *wimax_dev)
 }
 EXPORT_SYMBOL_GPL(wimax_dev_init);
 
-/*
- * This extern is declared here because it's easier to keep track --
- * both declarations are a list of the same
- */
-extern struct genl_ops
-       wimax_gnl_msg_from_user,
-       wimax_gnl_reset,
-       wimax_gnl_rfkill,
-       wimax_gnl_state_get;
+static const struct nla_policy wimax_gnl_policy[WIMAX_GNL_ATTR_MAX + 1] = {
+       [WIMAX_GNL_RESET_IFIDX] = { .type = NLA_U32, },
+       [WIMAX_GNL_RFKILL_IFIDX] = { .type = NLA_U32, },
+       [WIMAX_GNL_RFKILL_STATE] = {
+               .type = NLA_U32         /* enum wimax_rf_state */
+       },
+       [WIMAX_GNL_STGET_IFIDX] = { .type = NLA_U32, },
+       [WIMAX_GNL_MSG_IFIDX] = { .type = NLA_U32, },
+       [WIMAX_GNL_MSG_DATA] = {
+               .type = NLA_UNSPEC,     /* libnl doesn't grok BINARY yet */
+       },
+};
 
-static
-struct genl_ops *wimax_gnl_ops[] = {
-       &wimax_gnl_msg_from_user,
-       &wimax_gnl_reset,
-       &wimax_gnl_rfkill,
-       &wimax_gnl_state_get,
+static const struct genl_ops wimax_gnl_ops[] = {
+       {
+               .cmd = WIMAX_GNL_OP_MSG_FROM_USER,
+               .flags = GENL_ADMIN_PERM,
+               .policy = wimax_gnl_policy,
+               .doit = wimax_gnl_doit_msg_from_user,
+       },
+       {
+               .cmd = WIMAX_GNL_OP_RESET,
+               .flags = GENL_ADMIN_PERM,
+               .policy = wimax_gnl_policy,
+               .doit = wimax_gnl_doit_reset,
+       },
+       {
+               .cmd = WIMAX_GNL_OP_RFKILL,
+               .flags = GENL_ADMIN_PERM,
+               .policy = wimax_gnl_policy,
+               .doit = wimax_gnl_doit_rfkill,
+       },
+       {
+               .cmd = WIMAX_GNL_OP_STATE_GET,
+               .flags = GENL_ADMIN_PERM,
+               .policy = wimax_gnl_policy,
+               .doit = wimax_gnl_doit_state_get,
+       },
 };
 
 
@@ -557,8 +580,8 @@ struct genl_family wimax_gnl_family = {
        .maxattr = WIMAX_GNL_ATTR_MAX,
 };
 
-struct genl_multicast_group wimax_gnl_mcg = {
-       .name = "msg",
+static const struct genl_multicast_group wimax_gnl_mcgrps[] = {
+       { .name = "msg", },
 };
 
 
@@ -567,7 +590,7 @@ struct genl_multicast_group wimax_gnl_mcg = {
 static
 int __init wimax_subsys_init(void)
 {
-       int result, cnt;
+       int result;
 
        d_fnstart(4, NULL, "()\n");
        d_parse_params(D_LEVEL, D_LEVEL_SIZE, wimax_debug_params,
@@ -575,38 +598,18 @@ int __init wimax_subsys_init(void)
 
        snprintf(wimax_gnl_family.name, sizeof(wimax_gnl_family.name),
                 "WiMAX");
-       result = genl_register_family(&wimax_gnl_family);
+       result = genl_register_family_with_ops_groups(&wimax_gnl_family,
+                                                     wimax_gnl_ops,
+                                                     wimax_gnl_mcgrps);
        if (unlikely(result < 0)) {
                printk(KERN_ERR "cannot register generic netlink family: %d\n",
                       result);
                goto error_register_family;
        }
 
-       for (cnt = 0; cnt < ARRAY_SIZE(wimax_gnl_ops); cnt++) {
-               result = genl_register_ops(&wimax_gnl_family,
-                                          wimax_gnl_ops[cnt]);
-               d_printf(4, NULL, "registering generic netlink op code "
-                        "%u: %d\n", wimax_gnl_ops[cnt]->cmd, result);
-               if (unlikely(result < 0)) {
-                       printk(KERN_ERR "cannot register generic netlink op "
-                              "code %u: %d\n",
-                              wimax_gnl_ops[cnt]->cmd, result);
-                       goto error_register_ops;
-               }
-       }
-
-       result = genl_register_mc_group(&wimax_gnl_family, &wimax_gnl_mcg);
-       if (result < 0)
-               goto error_mc_group;
        d_fnend(4, NULL, "() = 0\n");
        return 0;
 
-error_mc_group:
-error_register_ops:
-       for (cnt--; cnt >= 0; cnt--)
-               genl_unregister_ops(&wimax_gnl_family,
-                                   wimax_gnl_ops[cnt]);
-       genl_unregister_family(&wimax_gnl_family);
 error_register_family:
        d_fnend(4, NULL, "() = %d\n", result);
        return result;
@@ -619,12 +622,7 @@ module_init(wimax_subsys_init);
 static
 void __exit wimax_subsys_exit(void)
 {
-       int cnt;
        wimax_id_table_release();
-       genl_unregister_mc_group(&wimax_gnl_family, &wimax_gnl_mcg);
-       for (cnt = ARRAY_SIZE(wimax_gnl_ops) - 1; cnt >= 0; cnt--)
-               genl_unregister_ops(&wimax_gnl_family,
-                                   wimax_gnl_ops[cnt]);
        genl_unregister_family(&wimax_gnl_family);
 }
 module_exit(wimax_subsys_exit);
index 5dcd9c0..b445b82 100644 (file)
@@ -84,8 +84,14 @@ void wimax_id_table_release(void);
 int wimax_rfkill_add(struct wimax_dev *);
 void wimax_rfkill_rm(struct wimax_dev *);
 
+/* generic netlink */
 extern struct genl_family wimax_gnl_family;
-extern struct genl_multicast_group wimax_gnl_mcg;
+
+/* ops */
+int wimax_gnl_doit_msg_from_user(struct sk_buff *skb, struct genl_info *info);
+int wimax_gnl_doit_reset(struct sk_buff *skb, struct genl_info *info);
+int wimax_gnl_doit_rfkill(struct sk_buff *skb, struct genl_info *info);
+int wimax_gnl_doit_state_get(struct sk_buff *skb, struct genl_info *info);
 
 #endif /* #ifdef __KERNEL__ */
 #endif /* #ifndef __WIMAX_INTERNAL_H__ */
index a7f4e79..a1eb210 100644 (file)
@@ -30,9 +30,9 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
                                   struct cfg80211_crypto_settings *settings,
                                   int cipher_limit);
 
-static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
+static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
                            struct genl_info *info);
-static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
+static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
                              struct genl_info *info);
 
 /* the netlink family */
@@ -47,6 +47,25 @@ static struct genl_family nl80211_fam = {
        .post_doit = nl80211_post_doit,
 };
 
+/* multicast groups */
+enum nl80211_multicast_groups {
+       NL80211_MCGRP_CONFIG,
+       NL80211_MCGRP_SCAN,
+       NL80211_MCGRP_REGULATORY,
+       NL80211_MCGRP_MLME,
+       NL80211_MCGRP_TESTMODE /* keep last - ifdef! */
+};
+
+static const struct genl_multicast_group nl80211_mcgrps[] = {
+       [NL80211_MCGRP_CONFIG] = { .name = "config", },
+       [NL80211_MCGRP_SCAN] = { .name = "scan", },
+       [NL80211_MCGRP_REGULATORY] = { .name = "regulatory", },
+       [NL80211_MCGRP_MLME] = { .name = "mlme", },
+#ifdef CONFIG_NL80211_TESTMODE
+       [NL80211_MCGRP_TESTMODE] = { .name = "testmode", }
+#endif
+};
+
 /* returns ERR_PTR values */
 static struct wireless_dev *
 __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
@@ -6656,10 +6675,6 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
 
 
 #ifdef CONFIG_NL80211_TESTMODE
-static struct genl_multicast_group nl80211_testmode_mcgrp = {
-       .name = "testmode",
-};
-
 static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -6868,8 +6883,8 @@ void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
 
        nla_nest_end(skb, data);
        genlmsg_end(skb, hdr);
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), skb, 0,
-                               nl80211_testmode_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0,
+                               NL80211_MCGRP_TESTMODE, gfp);
 }
 EXPORT_SYMBOL(cfg80211_testmode_event);
 #endif
@@ -8851,7 +8866,7 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb,
 #define NL80211_FLAG_NEED_WDEV_UP      (NL80211_FLAG_NEED_WDEV |\
                                         NL80211_FLAG_CHECK_NETDEV_UP)
 
-static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
+static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
                            struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev;
@@ -8920,7 +8935,7 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
        return 0;
 }
 
-static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
+static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
                              struct genl_info *info)
 {
        if (info->user_ptr[1]) {
@@ -8937,7 +8952,7 @@ static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
                rtnl_unlock();
 }
 
-static struct genl_ops nl80211_ops[] = {
+static const struct genl_ops nl80211_ops[] = {
        {
                .cmd = NL80211_CMD_GET_WIPHY,
                .doit = nl80211_get_wiphy,
@@ -9566,21 +9581,6 @@ static struct genl_ops nl80211_ops[] = {
        },
 };
 
-static struct genl_multicast_group nl80211_mlme_mcgrp = {
-       .name = "mlme",
-};
-
-/* multicast groups */
-static struct genl_multicast_group nl80211_config_mcgrp = {
-       .name = "config",
-};
-static struct genl_multicast_group nl80211_scan_mcgrp = {
-       .name = "scan",
-};
-static struct genl_multicast_group nl80211_regulatory_mcgrp = {
-       .name = "regulatory",
-};
-
 /* notification functions */
 
 void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
@@ -9597,8 +9597,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_config_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_CONFIG, GFP_KERNEL);
 }
 
 static int nl80211_add_scan_req(struct sk_buff *msg,
@@ -9707,8 +9707,8 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
 void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
@@ -9726,8 +9726,8 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
 void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
@@ -9745,8 +9745,8 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
 void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
@@ -9764,8 +9764,8 @@ void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
 void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
@@ -9782,8 +9782,8 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
 /*
@@ -9837,8 +9837,8 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
        genlmsg_end(msg, hdr);
 
        rcu_read_lock();
-       genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
-                               GFP_ATOMIC);
+       genlmsg_multicast_allns(&nl80211_fam, msg, 0,
+                               NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
        rcu_read_unlock();
 
        return;
@@ -9873,8 +9873,8 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -9961,8 +9961,8 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10017,8 +10017,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10056,8 +10056,8 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10094,8 +10094,8 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, GFP_KERNEL);
        return;
 
  nla_put_failure:
@@ -10128,8 +10128,8 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10169,8 +10169,8 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10208,8 +10208,8 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10261,8 +10261,8 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        genlmsg_end(msg, hdr);
 
        rcu_read_lock();
-       genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
-                               GFP_ATOMIC);
+       genlmsg_multicast_allns(&nl80211_fam, msg, 0,
+                               NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
        rcu_read_unlock();
 
        return;
@@ -10307,8 +10307,8 @@ static void nl80211_send_remain_on_chan_event(
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10362,8 +10362,8 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
 }
 EXPORT_SYMBOL(cfg80211_new_sta);
 
@@ -10392,8 +10392,8 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10428,8 +10428,8 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10590,8 +10590,8 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10639,8 +10639,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10684,8 +10684,8 @@ static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10742,8 +10742,8 @@ nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10789,8 +10789,8 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10866,8 +10866,8 @@ void cfg80211_cqm_txe_notify(struct net_device *dev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10915,8 +10915,8 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10962,8 +10962,8 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -11002,8 +11002,8 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -11154,8 +11154,8 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  free_msg:
@@ -11196,8 +11196,8 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -11279,8 +11279,8 @@ void cfg80211_ft_event(struct net_device *netdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, GFP_KERNEL);
 }
 EXPORT_SYMBOL(cfg80211_ft_event);
 
@@ -11329,33 +11329,11 @@ int nl80211_init(void)
 {
        int err;
 
-       err = genl_register_family_with_ops(&nl80211_fam,
-               nl80211_ops, ARRAY_SIZE(nl80211_ops));
+       err = genl_register_family_with_ops_groups(&nl80211_fam, nl80211_ops,
+                                                  nl80211_mcgrps);
        if (err)
                return err;
 
-       err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
-       if (err)
-               goto err_out;
-
-       err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
-       if (err)
-               goto err_out;
-
-       err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
-       if (err)
-               goto err_out;
-
-       err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
-       if (err)
-               goto err_out;
-
-#ifdef CONFIG_NL80211_TESTMODE
-       err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
-       if (err)
-               goto err_out;
-#endif
-
        err = netlink_register_notifier(&nl80211_netlink_notifier);
        if (err)
                goto err_out;
index 45a3ab5..7622789 100644 (file)
@@ -1340,10 +1340,9 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (sx25) {
                sx25->sx25_family = AF_X25;
                sx25->sx25_addr   = x25->dest_addr;
+               msg->msg_namelen = sizeof(*sx25);
        }
 
-       msg->msg_namelen = sizeof(struct sockaddr_x25);
-
        x25_check_rbuf(sk);
        rc = copied;
 out_free_dgram:
index cfe40ad..2fca916 100644 (file)
@@ -64,7 +64,7 @@ static int __init testfunc(void)
 
        /* put values into the fifo */
        for (i = 0; i != 10; i++)
-               kfifo_put(&test, &i);
+               kfifo_put(&test, i);
 
        /* show the number of used elements */
        printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test));
@@ -85,7 +85,7 @@ static int __init testfunc(void)
        kfifo_skip(&test);
 
        /* put values into the fifo until is full */
-       for (i = 20; kfifo_put(&test, &i); i++)
+       for (i = 20; kfifo_put(&test, i); i++)
                ;
 
        printk(KERN_INFO "queue len: %u\n", kfifo_len(&test));
index 0647379..aa243db 100644 (file)
@@ -39,7 +39,7 @@ static int __init example_init(void)
        kfifo_in(&fifo, "test", 4);
 
        for (i = 0; i != 9; i++)
-               kfifo_put(&fifo, &i);
+               kfifo_put(&fifo, i);
 
        /* kick away first byte */
        kfifo_skip(&fifo);
index 6f8e79e..8dc3c2e 100644 (file)
@@ -61,7 +61,7 @@ static int __init testfunc(void)
 
        /* put values into the fifo */
        for (i = 0; i != 10; i++)
-               kfifo_put(&test, &i);
+               kfifo_put(&test, i);
 
        /* show the number of used elements */
        printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test));
@@ -78,7 +78,7 @@ static int __init testfunc(void)
        kfifo_skip(&test);
 
        /* put values into the fifo until is full */
-       for (i = 20; kfifo_put(&test, &i); i++)
+       for (i = 20; kfifo_put(&test, i); i++)
                ;
 
        printk(KERN_INFO "queue len: %u\n", kfifo_len(&test));
index 8dcdca2..69f0a14 100644 (file)
@@ -79,9 +79,11 @@ modpost = scripts/mod/modpost                    \
  $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S)      \
  $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w)
 
+MODPOST_OPT=$(subst -i,-n,$(filter -i,$(MAKEFLAGS)))
+
 # We can go over command line length here, so be careful.
 quiet_cmd_modpost = MODPOST $(words $(filter-out vmlinux FORCE, $^)) modules
-      cmd_modpost = $(MODLISTCMD) | sed 's/\.ko$$/.o/' | $(modpost) -s -T -
+      cmd_modpost = $(MODLISTCMD) | sed 's/\.ko$$/.o/' | $(modpost) $(MODPOST_OPT) -s -T -
 
 PHONY += __modpost
 __modpost: $(modules:.ko=.o) FORCE
index db0e5cd..91c4117 100644 (file)
@@ -1353,6 +1353,8 @@ static void render_out_of_line_list(FILE *out)
                        render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
                        render_opcode(out, "_jump_target(%u),\n", entry);
                        break;
+               default:
+                       break;
                }
                if (e->action)
                        render_opcode(out, "_action(ACT_%s),\n",
index 6129020..549d0ab 100755 (executable)
@@ -19,9 +19,10 @@ def getsizes(file):
         size, type, name = l[:-1].split()
         if type in "tTdDbBrR":
             # strip generated symbols
-            if name[:6] == "__mod_": continue
-            # function names begin with '.' on 64-bit powerpc
-            if "." in name[1:]: name = "static." + name.split(".")[0]
+            if name.startswith("__mod_"): continue
+            if name == "linux_banner": continue
+            # statics and some other optimizations adds random .NUMBER
+            name = re.sub(r'\.[0-9]+', '', name)
             sym[name] = sym.get(name, 0) + int(size, 16)
     return sym
 
index 61090e0..9c98100 100755 (executable)
@@ -3289,6 +3289,7 @@ sub process {
                        }
                }
                if (!defined $suppress_whiletrailers{$linenr} &&
+                   defined($stat) && defined($cond) &&
                    $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) {
                        my ($s, $c) = ($stat, $cond);
 
diff --git a/scripts/coccinelle/api/devm_request_and_ioremap.cocci b/scripts/coccinelle/api/devm_request_and_ioremap.cocci
deleted file mode 100644 (file)
index 562ec88..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/// Reimplement a call to devm_request_mem_region followed by a call to ioremap
-/// or ioremap_nocache by a call to devm_request_and_ioremap.
-/// Devm_request_and_ioremap was introduced in
-/// 72f8c0bfa0de64c68ee59f40eb9b2683bffffbb0.  It makes the code much more
-/// concise.
-///
-///
-// Confidence: High
-// Copyright: (C) 2011 Julia Lawall, INRIA/LIP6.  GPLv2.
-// Copyright: (C) 2011 Gilles Muller, INRIA/LiP6.  GPLv2.
-// URL: http://coccinelle.lip6.fr/
-// Comments:
-// Options: --no-includes --include-headers
-
-virtual patch
-virtual org
-virtual report
-virtual context
-
-@nm@
-expression myname;
-identifier i;
-@@
-
-struct platform_driver i = { .driver = { .name = myname } };
-
-@depends on patch@
-expression dev,res,size;
-@@
-
--if (!devm_request_mem_region(dev, res->start, size,
--                              \(res->name\|dev_name(dev)\))) {
--   ...
--   return ...;
--}
-... when != res->start
-(
--devm_ioremap(dev,res->start,size)
-+devm_request_and_ioremap(dev,res)
-|
--devm_ioremap_nocache(dev,res->start,size)
-+devm_request_and_ioremap(dev,res)
-)
-... when any
-    when != res->start
-
-// this rule is separate from the previous one, because a single file can
-// have multiple values of myname
-@depends on patch@
-expression dev,res,size;
-expression nm.myname;
-@@
-
--if (!devm_request_mem_region(dev, res->start, size,myname)) {
--   ...
--   return ...;
--}
-... when != res->start
-(
--devm_ioremap(dev,res->start,size)
-+devm_request_and_ioremap(dev,res)
-|
--devm_ioremap_nocache(dev,res->start,size)
-+devm_request_and_ioremap(dev,res)
-)
-... when any
-    when != res->start
-
-
-@pb depends on org || report || context@
-expression dev,res,size;
-expression nm.myname;
-position p1,p2;
-@@
-
-*if
-  (!devm_request_mem_region@p1(dev, res->start, size,
-                              \(res->name\|dev_name(dev)\|myname\))) {
-   ...
-   return ...;
-}
-... when != res->start
-(
-*devm_ioremap@p2(dev,res->start,size)
-|
-*devm_ioremap_nocache@p2(dev,res->start,size)
-)
-... when any
-    when != res->start
-
-@script:python depends on org@
-p1 << pb.p1;
-p2 << pb.p2;
-@@
-
-cocci.print_main("INFO: replace by devm_request_and_ioremap",p1)
-cocci.print_secs("",p2)
-
-@script:python depends on report@
-p1 << pb.p1;
-p2 << pb.p2;
-@@
-
-msg = "INFO: devm_request_mem_region followed by ioremap on line %s can be replaced by devm_request_and_ioremap" % (p2[0].line)
-coccilib.report.print_report(p1[0],msg)
index 9a11f9f..10085de 100644 (file)
@@ -115,6 +115,12 @@ static int read_symbol(FILE *in, struct sym_entry *s)
                        fprintf(stderr, "Read error or end of file.\n");
                return -1;
        }
+       if (strlen(str) > KSYM_NAME_LEN) {
+               fprintf(stderr, "Symbol %s too long for kallsyms (%zu vs %d).\n"
+                                "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
+                       str, strlen(str), KSYM_NAME_LEN);
+               return -1;
+       }
 
        sym = str;
        /* skip prefix char */
index df198a5..ba663e1 100644 (file)
@@ -93,7 +93,7 @@ struct symbol {
 #define SYMBOL_CHOICEVAL  0x0020  /* used as a value in a choice block */
 #define SYMBOL_VALID      0x0080  /* set when symbol.curr is calculated */
 #define SYMBOL_OPTIONAL   0x0100  /* choice is optional - values can be 'n' */
-#define SYMBOL_WRITE      0x0200  /* ? */
+#define SYMBOL_WRITE      0x0200  /* write symbol to file (KCONFIG_CONFIG) */
 #define SYMBOL_CHANGED    0x0400  /* ? */
 #define SYMBOL_AUTO       0x1000  /* value from environment variable */
 #define SYMBOL_CHECKED    0x2000  /* used during dependency checking */
index 2c39631..59184bb 100644 (file)
@@ -25,7 +25,7 @@
 static const char mconf_readme[] = N_(
 "Overview\n"
 "--------\n"
-"This interface let you select features and parameters for the build.\n"
+"This interface lets you select features and parameters for the build.\n"
 "Features can either be built-in, modularized, or ignored. Parameters\n"
 "must be entered in as decimal or hexadecimal numbers or text.\n"
 "\n"
@@ -39,15 +39,15 @@ static const char mconf_readme[] = N_(
 "\n"
 "To change any of these features, highlight it with the cursor\n"
 "keys and press <Y> to build it in, <M> to make it a module or\n"
-"<N> to removed it.  You may also press the <Space Bar> to cycle\n"
-"through the available options (ie. Y->N->M->Y).\n"
+"<N> to remove it.  You may also press the <Space Bar> to cycle\n"
+"through the available options (i.e. Y->N->M->Y).\n"
 "\n"
 "Some additional keyboard hints:\n"
 "\n"
 "Menus\n"
 "----------\n"
-"o  Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
-"   you wish to change or submenu wish to select and press <Enter>.\n"
+"o  Use the Up/Down arrow keys (cursor keys) to highlight the item you\n"
+"   wish to change or the submenu you wish to select and press <Enter>.\n"
 "   Submenus are designated by \"--->\", empty ones by \"----\".\n"
 "\n"
 "   Shortcut: Press the option's highlighted letter (hotkey).\n"
@@ -65,7 +65,7 @@ static const char mconf_readme[] = N_(
 "             there is a delayed response which you may find annoying.\n"
 "\n"
 "   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
-"   <Exit> and <Help>.\n"
+"   <Exit>, <Help>, <Save>, and <Load>.\n"
 "\n"
 "o  To get help with an item, use the cursor keys to highlight <Help>\n"
 "   and press <ENTER>.\n"
@@ -105,7 +105,7 @@ 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 <u>, <d>, <SPACE BAR> and <B> for \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>, <q>, <Enter> or <Esc><Esc> to exit.\n"
@@ -117,23 +117,21 @@ static const char mconf_readme[] = N_(
 "those who, for various reasons, find it necessary to switch\n"
 "between different configurations.\n"
 "\n"
-"At the end of the main menu you will find two options.  One is\n"
-"for saving the current configuration to a file of your choosing.\n"
-"The other option is for loading a previously saved alternate\n"
-"configuration.\n"
+"The <Save> button will let you save the current configuration to\n"
+"a file of your choosing.  Use the <Load> button to load a previously\n"
+"saved alternate configuration.\n"
 "\n"
-"Even if you don't use alternate configuration files, but you\n"
-"find during a Menuconfig session that you have completely messed\n"
-"up your settings, you may use the \"Load Alternate...\" option to\n"
-"restore your previously saved settings from \".config\" without\n"
-"restarting Menuconfig.\n"
+"Even if you don't use alternate configuration files, but you find\n"
+"during a Menuconfig session that you have completely messed up your\n"
+"settings, you may use the <Load> button to restore your previously\n"
+"saved settings from \".config\" without restarting Menuconfig.\n"
 "\n"
 "Other information\n"
 "-----------------\n"
-"If you use Menuconfig in an XTERM window make sure you have your\n"
-"$TERM variable set to point to a xterm definition which supports color.\n"
-"Otherwise, Menuconfig will look rather bad.  Menuconfig will not\n"
-"display correctly in a RXVT window because rxvt displays only one\n"
+"If you use Menuconfig in an XTERM window, make sure you have your\n"
+"$TERM variable set to point to an xterm definition which supports\n"
+"color.  Otherwise, Menuconfig will look rather bad.  Menuconfig will\n"
+"not display correctly in an RXVT window because rxvt displays only one\n"
 "intensity of color, bright.\n"
 "\n"
 "Menuconfig will display larger menus on screens or xterms which are\n"
@@ -148,8 +146,8 @@ static const char mconf_readme[] = N_(
 "\n"
 "Optional personality available\n"
 "------------------------------\n"
-"If you prefer to have all of the options listed in a single menu, rather\n"
-"than the default multimenu hierarchy, run the menuconfig with\n"
+"If you prefer to have all of the options listed in a single menu,\n"
+"rather than the default multimenu hierarchy, run the menuconfig with\n"
 "MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
 "\n"
 "make MENUCONFIG_MODE=single_menu menuconfig\n"
@@ -172,7 +170,7 @@ static const char mconf_readme[] = N_(
 " mono       => selects colors suitable for monochrome displays\n"
 " blackbg    => selects a color scheme with black background\n"
 " classic    => theme with blue background. The classic look\n"
-" bluetitle  => a LCD friendly version of classic. (default)\n"
+" bluetitle  => an LCD friendly version of classic. (default)\n"
 "\n"),
 menu_instructions[] = N_(
        "Arrow keys navigate the menu.  "
@@ -238,24 +236,24 @@ search_help[] = N_(
        "Symbol: FOO [=m]\n"
        "Type  : tristate\n"
        "Prompt: Foo bus is used to drive the bar HW\n"
-       "  Defined at drivers/pci/Kconfig:47\n"
-       "  Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
        "  Location:\n"
        "    -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
        "      -> PCI support (PCI [=y])\n"
        "(1)     -> PCI access mode (<choice> [=y])\n"
+       "  Defined at drivers/pci/Kconfig:47\n"
+       "  Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
        "  Selects: LIBCRC32\n"
-       "  Selected by: BAR\n"
+       "  Selected by: BAR [=n]\n"
        "-----------------------------------------------------------------\n"
        "o The line 'Type:' shows the type of the configuration option for\n"
        "  this symbol (boolean, tristate, string, ...)\n"
        "o The line 'Prompt:' shows the text used in the menu structure for\n"
        "  this symbol\n"
-       "o The 'Defined at' line tell at what file / line number the symbol\n"
+       "o The 'Defined at' line tells at what file / line number the symbol\n"
        "  is defined\n"
-       "o The 'Depends on:' line tell what symbols needs to be defined for\n"
+       "o The 'Depends on:' line tells what symbols need to be defined for\n"
        "  this symbol to be visible in the menu (selectable)\n"
-       "o The 'Location:' lines tell where in the menu structure this symbol\n"
+       "o The 'Location:' lines tells where in the menu structure this symbol\n"
        "  is located\n"
        "    A location followed by a [=y] indicates that this is a\n"
        "    selectable menu item - and the current value is displayed inside\n"
@@ -263,9 +261,9 @@ search_help[] = N_(
        "    Press the key in the (#) prefix to jump directly to that\n"
        "    location. You will be returned to the current search results\n"
        "    after exiting this new menu.\n"
-       "o The 'Selects:' line tell what symbol will be automatically\n"
+       "o The 'Selects:' line tells what symbols will be automatically\n"
        "  selected if this symbol is selected (y or m)\n"
-       "o The 'Selected by' line tell what symbol has selected this symbol\n"
+       "o The 'Selected by' line tells what symbol has selected this symbol\n"
        "\n"
        "Only relevant lines are shown.\n"
        "\n\n"
index c1d5320..db1512a 100644 (file)
@@ -119,9 +119,10 @@ void menu_set_type(int type)
                sym->type = type;
                return;
        }
-       menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'",
-           sym->name ? sym->name : "<choice>",
-           sym_type_name(sym->type), sym_type_name(type));
+       menu_warn(current_entry,
+               "ignoring type redefinition of '%s' from '%s' to '%s'",
+               sym->name ? sym->name : "<choice>",
+               sym_type_name(sym->type), sym_type_name(type));
 }
 
 struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
@@ -583,7 +584,7 @@ static void get_prompt_str(struct gstr *r, struct property *prop,
                for (j = 4; --i >= 0; j += 2) {
                        menu = submenu[i];
                        if (head && location && menu == location)
-                               jump->offset = r->len - 1;
+                               jump->offset = strlen(r->s);
                        str_printf(r, "%*c-> %s", j, ' ',
                                   _(menu_get_prompt(menu)));
                        if (menu->sym) {
@@ -597,7 +598,7 @@ static void get_prompt_str(struct gstr *r, struct property *prop,
 }
 
 /*
- * get peoperty of type P_SYMBOL
+ * get property of type P_SYMBOL
  */
 static struct property *get_symbol_prop(struct symbol *sym)
 {
index 1500c38..9d3b04b 100644 (file)
@@ -69,6 +69,11 @@ static inline QString qgettext(const QString& str)
        return QString::fromLocal8Bit(gettext(str.latin1()));
 }
 
+ConfigSettings::ConfigSettings()
+       : QSettings("kernel.org", "qconf")
+{
+}
+
 /**
  * Reads a list of integer values from the application settings.
  */
index 3715b3e..bde0c6b 100644 (file)
@@ -32,6 +32,7 @@ class ConfigMainWindow;
 
 class ConfigSettings : public QSettings {
 public:
+       ConfigSettings();
        Q3ValueList<int> readSizes(const QString& key, bool *ok);
        bool writeSizes(const QString& key, const Q3ValueList<int>& value);
 };
index c9a6775..7caabdb 100644 (file)
@@ -1047,7 +1047,7 @@ sym_re_search_free:
  * When we check for recursive dependencies we use a stack to save
  * current state so we can print out relevant info to user.
  * The entries are located on the call stack so no need to free memory.
- * Note inser() remove() must always match to properly clear the stack.
+ * Note insert() remove() must always match to properly clear the stack.
  */
 static struct dep_stack {
        struct dep_stack *prev, *next;
index 6555a47..1a9f53e 100644 (file)
@@ -68,7 +68,6 @@ static void alloc_string(const char *str, int size)
 }
 %}
 
-ws     [ \n\t]
 n      [A-Za-z0-9_]
 
 %%
index dbd3e1e..da058da 100755 (executable)
@@ -2128,8 +2128,7 @@ sub dump_function($$) {
 
        create_parameterlist($args, ',', $file);
     } else {
-       print STDERR "Error(${file}:$.): cannot understand prototype: '$prototype'\n";
-       ++$errors;
+       print STDERR "Warning(${file}:$.): cannot understand function prototype: '$prototype'\n";
        return;
     }
 
index bfcea5d..1785576 100644 (file)
@@ -17,6 +17,7 @@
 #include <string.h>
 #include <limits.h>
 #include <stdbool.h>
+#include <errno.h>
 #include "modpost.h"
 #include "../../include/generated/autoconf.h"
 #include "../../include/linux/license.h"
@@ -37,6 +38,8 @@ static int warn_unresolved = 0;
 /* How a symbol is exported */
 static int sec_mismatch_count = 0;
 static int sec_mismatch_verbose = 1;
+/* ignore missing files */
+static int ignore_missing_files;
 
 enum export {
        export_plain,      export_unused,     export_gpl,
@@ -161,7 +164,7 @@ struct symbol {
        unsigned int vmlinux:1;    /* 1 if symbol is defined in vmlinux */
        unsigned int kernel:1;     /* 1 if symbol is from kernel
                                    *  (only for external modules) **/
-       unsigned int preloaded:1;  /* 1 if symbol from Module.symvers */
+       unsigned int preloaded:1;  /* 1 if symbol from Module.symvers, or crc */
        enum export  export;       /* Type of export */
        char name[0];
 };
@@ -329,8 +332,11 @@ static void sym_update_crc(const char *name, struct module *mod,
 {
        struct symbol *s = find_symbol(name);
 
-       if (!s)
+       if (!s) {
                s = new_symbol(name, mod, export);
+               /* Don't complain when we find it later. */
+               s->preloaded = 1;
+       }
        s->crc = crc;
        s->crc_valid = 1;
 }
@@ -407,6 +413,11 @@ static int parse_elf(struct elf_info *info, const char *filename)
 
        hdr = grab_file(filename, &info->size);
        if (!hdr) {
+               if (ignore_missing_files) {
+                       fprintf(stderr, "%s: %s (ignored)\n", filename,
+                               strerror(errno));
+                       return 0;
+               }
                perror(filename);
                exit(1);
        }
@@ -1852,7 +1863,7 @@ static void add_header(struct buffer *b, struct module *mod)
        buf_printf(b, "\n");
        buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
        buf_printf(b, "\n");
-       buf_printf(b, "struct module __this_module\n");
+       buf_printf(b, "__visible struct module __this_module\n");
        buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n");
        buf_printf(b, "\t.name = KBUILD_MODNAME,\n");
        if (mod->has_init)
@@ -2118,7 +2129,7 @@ int main(int argc, char **argv)
        struct ext_sym_list *extsym_iter;
        struct ext_sym_list *extsym_start = NULL;
 
-       while ((opt = getopt(argc, argv, "i:I:e:msST:o:awM:K:")) != -1) {
+       while ((opt = getopt(argc, argv, "i:I:e:mnsST:o:awM:K:")) != -1) {
                switch (opt) {
                case 'i':
                        kernel_read = optarg;
@@ -2138,6 +2149,9 @@ int main(int argc, char **argv)
                case 'm':
                        modversions = 1;
                        break;
+               case 'n':
+                       ignore_missing_files = 1;
+                       break;
                case 'o':
                        dump_write = optarg;
                        break;
index 9dfcd6d..deb2994 100644 (file)
@@ -416,7 +416,7 @@ void get_src_version(const char *modname, char sum[], unsigned sumlen)
                basename = strrchr(modname, '/') + 1;
        else
                basename = modname;
-       sprintf(filelist, "%s/%.*s.mod", modverdir,
+       snprintf(filelist, sizeof(filelist), "%s/%.*s.mod", modverdir,
                (int) strlen(basename) - 2, basename);
 
        file = grab_file(filelist, &len);
index a674fd5..d0da663 100755 (executable)
@@ -214,13 +214,13 @@ $local_regex = "^[0-9a-fA-F]+\\s+t\\s+(\\S+)";
 $weak_regex = "^[0-9a-fA-F]+\\s+([wW])\\s+(\\S+)";
 $section_regex = "Disassembly of section\\s+(\\S+):";
 $function_regex = "^([0-9a-fA-F]+)\\s+<(.*?)>:";
-$mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount\$";
+$mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s(mcount|__fentry__)\$";
 $section_type = '@progbits';
 $mcount_adjust = 0;
 $type = ".long";
 
 if ($arch eq "x86_64") {
-    $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount([+-]0x[0-9a-zA-Z]+)?\$";
+    $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s(mcount|__fentry__)([+-]0x[0-9a-zA-Z]+)?\$";
     $type = ".quad";
     $alignment = 8;
     $mcount_adjust = -1;
index 17df305..e25732b 100755 (executable)
@@ -13,7 +13,7 @@ import sys
 import string
 
 def usage():
-       print """usage: show_delta [<options>] <filename>
+       print ("""usage: show_delta [<options>] <filename>
 
 This program parses the output from a set of printk message lines which
 have time data prefixed because the CONFIG_PRINTK_TIME option is set, or
@@ -35,7 +35,7 @@ ex: $ dmesg >timefile
 
 will show times relative to the line in the kernel output
 starting with "NET4".
-"""
+""")
        sys.exit(1)
 
 # returns a tuple containing the seconds and text for each message line
@@ -94,11 +94,11 @@ def main():
        try:
                lines = open(filein,"r").readlines()
        except:
-               print "Problem opening file: %s" % filein
+               print ("Problem opening file: %s" % filein)
                sys.exit(1)
 
        if base_str:
-               print 'base= "%s"' % base_str
+               print ('base= "%s"' % base_str)
                # assume a numeric base.  If that fails, try searching
                # for a matching line.
                try:
@@ -117,13 +117,13 @@ def main():
                                        # stop at first match
                                        break
                        if not found:
-                               print 'Couldn\'t find line matching base pattern "%s"' % base_str
+                               print ('Couldn\'t find line matching base pattern "%s"' % base_str)
                                sys.exit(1)
        else:
                base_time = 0.0
 
        for line in lines:
-               print convert_line(line, base_time),
+               print (convert_line(line, base_time),)
 
 main()
 
index 74f02e4..58c4559 100755 (executable)
@@ -149,15 +149,16 @@ dogtags()
 exuberant()
 {
        all_target_sources | xargs $1 -a                        \
-       -I __initdata,__exitdata,__initconst,__devinitdata      \
-       -I __devinitconst,__cpuinitdata,__initdata_memblock     \
-       -I __refdata,__attribute                                \
+       -I __initdata,__exitdata,__initconst,                   \
+       -I __cpuinitdata,__initdata_memblock                    \
+       -I __refdata,__attribute,__maybe_unused,__always_unused \
        -I __acquires,__releases,__deprecated                   \
        -I __read_mostly,__aligned,____cacheline_aligned        \
        -I ____cacheline_aligned_in_smp                         \
+       -I __cacheline_aligned,__cacheline_aligned_in_smp       \
        -I ____cacheline_internodealigned_in_smp                \
        -I __used,__packed,__packed2__,__must_check,__must_hold \
-       -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL                      \
+       -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL,ACPI_EXPORT_SYMBOL   \
        -I DEFINE_TRACE,EXPORT_TRACEPOINT_SYMBOL,EXPORT_TRACEPOINT_SYMBOL_GPL \
        -I static,const                                         \
        --extra=+f --c-kinds=+px                                \
index c26c81e..a5918e0 100644 (file)
@@ -16,7 +16,6 @@ obj-$(CONFIG_MMU)                     += min_addr.o
 # Object file lists
 obj-$(CONFIG_SECURITY)                 += security.o capability.o
 obj-$(CONFIG_SECURITYFS)               += inode.o
-# Must precede capability.o in order to stack properly.
 obj-$(CONFIG_SECURITY_SELINUX)         += selinux/built-in.o
 obj-$(CONFIG_SECURITY_SMACK)           += smack/built-in.o
 obj-$(CONFIG_AUDIT)                    += lsm_audit.o
index 031d2d9..89c7865 100644 (file)
@@ -111,7 +111,6 @@ static const char *const aa_audit_type[] = {
 static void audit_pre(struct audit_buffer *ab, void *ca)
 {
        struct common_audit_data *sa = ca;
-       struct task_struct *tsk = sa->aad->tsk ? sa->aad->tsk : current;
 
        if (aa_g_audit_header) {
                audit_log_format(ab, "apparmor=");
@@ -132,11 +131,6 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
 
        if (sa->aad->profile) {
                struct aa_profile *profile = sa->aad->profile;
-               pid_t pid;
-               rcu_read_lock();
-               pid = rcu_dereference(tsk->real_parent)->pid;
-               rcu_read_unlock();
-               audit_log_format(ab, " parent=%d", pid);
                if (profile->ns != root_ns) {
                        audit_log_format(ab, " namespace=");
                        audit_log_untrustedstring(ab, profile->ns->base.hname);
@@ -149,12 +143,6 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
                audit_log_format(ab, " name=");
                audit_log_untrustedstring(ab, sa->aad->name);
        }
-
-       if (sa->aad->tsk) {
-               audit_log_format(ab, " pid=%d comm=", tsk->pid);
-               audit_log_untrustedstring(ab, tsk->comm);
-       }
-
 }
 
 /**
@@ -212,7 +200,7 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
 
        if (sa->aad->type == AUDIT_APPARMOR_KILL)
                (void)send_sig_info(SIGKILL, NULL,
-                                   sa->aad->tsk ?  sa->aad->tsk : current);
+                                   sa->u.tsk ?  sa->u.tsk : current);
 
        if (sa->aad->type == AUDIT_APPARMOR_ALLOWED)
                return complain_error(sa->aad->error);
index 84d1f5f..1101c6f 100644 (file)
@@ -53,8 +53,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
 
 /**
  * audit_caps - audit a capability
- * @profile: profile confining task (NOT NULL)
- * @task: task capability test was performed against (NOT NULL)
+ * @profile: profile being tested for confinement (NOT NULL)
  * @cap: capability tested
  * @error: error code returned by test
  *
@@ -63,8 +62,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
  *
  * Returns: 0 or sa->error on success,  error code on failure
  */
-static int audit_caps(struct aa_profile *profile, struct task_struct *task,
-                     int cap, int error)
+static int audit_caps(struct aa_profile *profile, int cap, int error)
 {
        struct audit_cache *ent;
        int type = AUDIT_APPARMOR_AUTO;
@@ -73,7 +71,6 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task,
        sa.type = LSM_AUDIT_DATA_CAP;
        sa.aad = &aad;
        sa.u.cap = cap;
-       sa.aad->tsk = task;
        sa.aad->op = OP_CAPABLE;
        sa.aad->error = error;
 
@@ -124,8 +121,7 @@ static int profile_capable(struct aa_profile *profile, int cap)
 
 /**
  * aa_capable - test permission to use capability
- * @task: task doing capability test against (NOT NULL)
- * @profile: profile confining @task (NOT NULL)
+ * @profile: profile being tested against (NOT NULL)
  * @cap: capability to be tested
  * @audit: whether an audit record should be generated
  *
@@ -133,8 +129,7 @@ static int profile_capable(struct aa_profile *profile, int cap)
  *
  * Returns: 0 on success, or else an error code.
  */
-int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap,
-              int audit)
+int aa_capable(struct aa_profile *profile, int cap, int audit)
 {
        int error = profile_capable(profile, cap);
 
@@ -144,5 +139,5 @@ int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap,
                return error;
        }
 
-       return audit_caps(profile, task, cap, error);
+       return audit_caps(profile, cap, error);
 }
index 26c607c..452567d 100644 (file)
@@ -50,23 +50,21 @@ void aa_free_domain_entries(struct aa_domain *domain)
 
 /**
  * may_change_ptraced_domain - check if can change profile on ptraced task
- * @task: task we want to change profile of   (NOT NULL)
  * @to_profile: profile to change to  (NOT NULL)
  *
- * Check if the task is ptraced and if so if the tracing task is allowed
+ * Check if current is ptraced and if so if the tracing task is allowed
  * to trace the new domain
  *
  * Returns: %0 or error if change not allowed
  */
-static int may_change_ptraced_domain(struct task_struct *task,
-                                    struct aa_profile *to_profile)
+static int may_change_ptraced_domain(struct aa_profile *to_profile)
 {
        struct task_struct *tracer;
        struct aa_profile *tracerp = NULL;
        int error = 0;
 
        rcu_read_lock();
-       tracer = ptrace_parent(task);
+       tracer = ptrace_parent(current);
        if (tracer)
                /* released below */
                tracerp = aa_get_task_profile(tracer);
@@ -75,7 +73,7 @@ static int may_change_ptraced_domain(struct task_struct *task,
        if (!tracer || unconfined(tracerp))
                goto out;
 
-       error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH);
+       error = aa_may_ptrace(tracerp, to_profile, PTRACE_MODE_ATTACH);
 
 out:
        rcu_read_unlock();
@@ -477,7 +475,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
        }
 
        if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
-               error = may_change_ptraced_domain(current, new_profile);
+               error = may_change_ptraced_domain(new_profile);
                if (error) {
                        aa_put_profile(new_profile);
                        goto audit;
@@ -690,7 +688,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
                        }
                }
 
-               error = may_change_ptraced_domain(current, hat);
+               error = may_change_ptraced_domain(hat);
                if (error) {
                        info = "ptraced";
                        error = -EPERM;
@@ -829,7 +827,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
        }
 
        /* check if tracing task is allowed to trace target domain */
-       error = may_change_ptraced_domain(current, target);
+       error = may_change_ptraced_domain(target);
        if (error) {
                info = "ptrace prevents transition";
                goto audit;
index 30e8d76..ba3dfd1 100644 (file)
@@ -109,7 +109,6 @@ struct apparmor_audit_data {
        void *profile;
        const char *name;
        const char *info;
-       struct task_struct *tsk;
        union {
                void *target;
                struct {
index 2e7c9d6..fc3fa38 100644 (file)
@@ -4,7 +4,7 @@
  * This file contains AppArmor capability mediation definitions.
  *
  * Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2010 Canonical Ltd.
+ * Copyright 2009-2013 Canonical Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -38,8 +38,7 @@ struct aa_caps {
 
 extern struct aa_fs_entry aa_fs_entry_caps[];
 
-int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap,
-              int audit);
+int aa_capable(struct aa_profile *profile, int cap, int audit);
 
 static inline void aa_free_cap_rules(struct aa_caps *caps)
 {
index aeda0fb..288ca76 100644 (file)
@@ -19,8 +19,8 @@
 
 struct aa_profile;
 
-int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer,
-                 struct aa_profile *tracee, unsigned int mode);
+int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee,
+                 unsigned int mode);
 
 int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
              unsigned int mode);
index c51d226..777ac1c 100644 (file)
@@ -54,15 +54,14 @@ static int aa_audit_ptrace(struct aa_profile *profile,
 
 /**
  * aa_may_ptrace - test if tracer task can trace the tracee
- * @tracer_task: task who will do the tracing  (NOT NULL)
  * @tracer: profile of the task doing the tracing  (NOT NULL)
  * @tracee: task to be traced
  * @mode: whether PTRACE_MODE_READ || PTRACE_MODE_ATTACH
  *
  * Returns: %0 else error code if permission denied or error
  */
-int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer,
-                 struct aa_profile *tracee, unsigned int mode)
+int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee,
+                 unsigned int mode)
 {
        /* TODO: currently only based on capability, not extended ptrace
         *       rules,
@@ -72,7 +71,7 @@ int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer,
        if (unconfined(tracer) || tracer == tracee)
                return 0;
        /* log this capability request */
-       return aa_capable(tracer_task, tracer, CAP_SYS_PTRACE, 1);
+       return aa_capable(tracer, CAP_SYS_PTRACE, 1);
 }
 
 /**
@@ -101,7 +100,7 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
        if (!unconfined(tracer_p)) {
                struct aa_profile *tracee_p = aa_get_task_profile(tracee);
 
-               error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode);
+               error = aa_may_ptrace(tracer_p, tracee_p, mode);
                error = aa_audit_ptrace(tracer_p, tracee_p, error);
 
                aa_put_profile(tracee_p);
index fb99e18..4257b7e 100644 (file)
@@ -145,7 +145,7 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
        if (!error) {
                profile = aa_cred_profile(cred);
                if (!unconfined(profile))
-                       error = aa_capable(current, profile, cap, audit);
+                       error = aa_capable(profile, cap, audit);
        }
        return error;
 }
index dbeb9bc..8b4f24a 100644 (file)
@@ -777,9 +777,15 @@ static int cap_xfrm_policy_delete_security(struct xfrm_sec_ctx *ctx)
        return 0;
 }
 
-static int cap_xfrm_state_alloc_security(struct xfrm_state *x,
-                                        struct xfrm_user_sec_ctx *sec_ctx,
-                                        u32 secid)
+static int cap_xfrm_state_alloc(struct xfrm_state *x,
+                               struct xfrm_user_sec_ctx *sec_ctx)
+{
+       return 0;
+}
+
+static int cap_xfrm_state_alloc_acquire(struct xfrm_state *x,
+                                       struct xfrm_sec_ctx *polsec,
+                                       u32 secid)
 {
        return 0;
 }
@@ -1101,7 +1107,8 @@ void __init security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, xfrm_policy_clone_security);
        set_to_cap_if_null(ops, xfrm_policy_free_security);
        set_to_cap_if_null(ops, xfrm_policy_delete_security);
-       set_to_cap_if_null(ops, xfrm_state_alloc_security);
+       set_to_cap_if_null(ops, xfrm_state_alloc);
+       set_to_cap_if_null(ops, xfrm_state_alloc_acquire);
        set_to_cap_if_null(ops, xfrm_state_free_security);
        set_to_cap_if_null(ops, xfrm_state_delete_security);
        set_to_cap_if_null(ops, xfrm_policy_lookup);
index 0b759e1..77ca965 100644 (file)
@@ -13,7 +13,9 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/err.h>
+#include <linux/sched.h>
 #include <linux/rbtree.h>
+#include <linux/cred.h>
 #include <linux/key-type.h>
 #include <linux/digsig.h>
 
 
 static struct key *keyring[INTEGRITY_KEYRING_MAX];
 
+#ifdef CONFIG_IMA_TRUSTED_KEYRING
+static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
+       ".evm",
+       ".module",
+       ".ima",
+};
+#else
 static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
        "_evm",
        "_module",
        "_ima",
 };
+#endif
 
 int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
-                                       const char *digest, int digestlen)
+                           const char *digest, int digestlen)
 {
        if (id >= INTEGRITY_KEYRING_MAX)
                return -EINVAL;
 
        if (!keyring[id]) {
                keyring[id] =
-                       request_key(&key_type_keyring, keyring_name[id], NULL);
+                   request_key(&key_type_keyring, keyring_name[id], NULL);
                if (IS_ERR(keyring[id])) {
                        int err = PTR_ERR(keyring[id]);
                        pr_err("no %s keyring: %d\n", keyring_name[id], err);
@@ -44,9 +54,10 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
                }
        }
 
-       switch (sig[0]) {
+       switch (sig[1]) {
        case 1:
-               return digsig_verify(keyring[id], sig, siglen,
+               /* v1 API expect signature without xattr type */
+               return digsig_verify(keyring[id], sig + 1, siglen - 1,
                                     digest, digestlen);
        case 2:
                return asymmetric_verify(keyring[id], sig, siglen,
@@ -55,3 +66,21 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
 
        return -EOPNOTSUPP;
 }
+
+int integrity_init_keyring(const unsigned int id)
+{
+       const struct cred *cred = current_cred();
+       const struct user_struct *user = cred->user;
+
+       keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
+                                   KGIDT_INIT(0), cred,
+                                   ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                                    KEY_USR_VIEW | KEY_USR_READ),
+                                   KEY_ALLOC_NOT_IN_QUOTA, user->uid_keyring);
+       if (!IS_ERR(keyring[id]))
+               set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags);
+       else
+               pr_info("Can't allocate %s keyring (%ld)\n",
+                       keyring_name[id], PTR_ERR(keyring[id]));
+       return 0;
+}
index b475466..9eae480 100644 (file)
 #include "integrity.h"
 
 /*
- * signature format v2 - for using with asymmetric keys
- */
-struct signature_v2_hdr {
-       uint8_t version;        /* signature format version */
-       uint8_t hash_algo;      /* Digest algorithm [enum pkey_hash_algo] */
-       uint32_t keyid;         /* IMA key identifier - not X509/PGP specific*/
-       uint16_t sig_size;      /* signature size */
-       uint8_t sig[0];         /* signature payload */
-} __packed;
-
-/*
  * Request an asymmetric key.
  */
 static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
index af9b685..336b3dd 100644 (file)
@@ -123,7 +123,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
                goto out;
        }
 
-       xattr_len = rc - 1;
+       xattr_len = rc;
 
        /* check value type */
        switch (xattr_data->type) {
@@ -143,7 +143,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
                if (rc)
                        break;
                rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
-                                       xattr_data->digest, xattr_len,
+                                       (const char *)xattr_data, xattr_len,
                                        calc.digest, sizeof(calc.digest));
                if (!rc) {
                        /* we probably want to replace rsa with hmac here */
index b1753e9..46408b9 100644 (file)
@@ -11,8 +11,9 @@
 
 #include <linux/module.h>
 #include <linux/xattr.h>
+#include <linux/evm.h>
 
-int posix_xattr_acl(char *xattr)
+int posix_xattr_acl(const char *xattr)
 {
        int xattr_len = strlen(xattr);
 
index 74522db..c49d3f1 100644 (file)
@@ -70,6 +70,8 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
 
 static void iint_free(struct integrity_iint_cache *iint)
 {
+       kfree(iint->ima_hash);
+       iint->ima_hash = NULL;
        iint->version = 0;
        iint->flags = 0UL;
        iint->ima_file_status = INTEGRITY_UNKNOWN;
index 39196ab..dad8d4c 100644 (file)
@@ -9,6 +9,7 @@ config IMA
        select CRYPTO_HMAC
        select CRYPTO_MD5
        select CRYPTO_SHA1
+       select CRYPTO_HASH_INFO
        select TCG_TPM if HAS_IOMEM && !UML
        select TCG_TIS if TCG_TPM && X86
        select TCG_IBMVTPM if TCG_TPM && PPC64
@@ -45,6 +46,69 @@ config IMA_LSM_RULES
        help
          Disabling this option will disregard LSM based policy rules.
 
+choice
+       prompt "Default template"
+       default IMA_NG_TEMPLATE
+       depends on IMA
+       help
+         Select the default IMA measurement template.
+
+         The original 'ima' measurement list template contains a
+         hash, defined as 20 bytes, and a null terminated pathname,
+         limited to 255 characters.  The 'ima-ng' measurement list
+         template permits both larger hash digests and longer
+         pathnames.
+
+       config IMA_TEMPLATE
+               bool "ima"
+       config IMA_NG_TEMPLATE
+               bool "ima-ng (default)"
+       config IMA_SIG_TEMPLATE
+               bool "ima-sig"
+endchoice
+
+config IMA_DEFAULT_TEMPLATE
+       string
+       depends on IMA
+       default "ima" if IMA_TEMPLATE
+       default "ima-ng" if IMA_NG_TEMPLATE
+       default "ima-sig" if IMA_SIG_TEMPLATE
+
+choice
+       prompt "Default integrity hash algorithm"
+       default IMA_DEFAULT_HASH_SHA1
+       depends on IMA
+       help
+          Select the default hash algorithm used for the measurement
+          list, integrity appraisal and audit log.  The compiled default
+          hash algorithm can be overwritten using the kernel command
+          line 'ima_hash=' option.
+
+       config IMA_DEFAULT_HASH_SHA1
+               bool "SHA1 (default)"
+               depends on CRYPTO_SHA1
+
+       config IMA_DEFAULT_HASH_SHA256
+               bool "SHA256"
+               depends on CRYPTO_SHA256 && !IMA_TEMPLATE
+
+       config IMA_DEFAULT_HASH_SHA512
+               bool "SHA512"
+               depends on CRYPTO_SHA512 && !IMA_TEMPLATE
+
+       config IMA_DEFAULT_HASH_WP512
+               bool "WP512"
+               depends on CRYPTO_WP512 && !IMA_TEMPLATE
+endchoice
+
+config IMA_DEFAULT_HASH
+       string
+       depends on IMA
+       default "sha1" if IMA_DEFAULT_HASH_SHA1
+       default "sha256" if IMA_DEFAULT_HASH_SHA256
+       default "sha512" if IMA_DEFAULT_HASH_SHA512
+       default "wp512" if IMA_DEFAULT_HASH_WP512
+
 config IMA_APPRAISE
        bool "Appraise integrity measurements"
        depends on IMA
@@ -59,3 +123,11 @@ config IMA_APPRAISE
          For more information on integrity appraisal refer to:
          <http://linux-ima.sourceforge.net>
          If unsure, say N.
+
+config IMA_TRUSTED_KEYRING
+       bool "Require all keys on the _ima keyring be signed"
+       depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
+       default y
+       help
+          This option requires that all keys added to the _ima
+          keyring be signed by a key on the system trusted keyring.
index 56dfee7..d79263d 100644 (file)
@@ -6,5 +6,5 @@
 obj-$(CONFIG_IMA) += ima.o
 
 ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
-        ima_policy.o
+        ima_policy.o ima_template.o ima_template_lib.o
 ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
index b3dd616..bf03c6a 100644 (file)
@@ -36,23 +36,48 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
 #define IMA_HASH_BITS 9
 #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
 
+#define IMA_TEMPLATE_FIELD_ID_MAX_LEN  16
+#define IMA_TEMPLATE_NUM_FIELDS_MAX    15
+
+#define IMA_TEMPLATE_IMA_NAME "ima"
+#define IMA_TEMPLATE_IMA_FMT "d|n"
+
 /* set during initialization */
 extern int ima_initialized;
 extern int ima_used_chip;
-extern char *ima_hash;
+extern int ima_hash_algo;
 extern int ima_appraise;
 
-/* IMA inode template definition */
-struct ima_template_data {
-       u8 digest[IMA_DIGEST_SIZE];     /* sha1/md5 measurement hash */
-       char file_name[IMA_EVENT_NAME_LEN_MAX + 1];     /* name + \0 */
+/* IMA template field data definition */
+struct ima_field_data {
+       u8 *data;
+       u32 len;
+};
+
+/* IMA template field definition */
+struct ima_template_field {
+       const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN];
+       int (*field_init) (struct integrity_iint_cache *iint, struct file *file,
+                          const unsigned char *filename,
+                          struct evm_ima_xattr_data *xattr_value,
+                          int xattr_len, struct ima_field_data *field_data);
+       void (*field_show) (struct seq_file *m, enum ima_show_type show,
+                           struct ima_field_data *field_data);
+};
+
+/* IMA template descriptor definition */
+struct ima_template_desc {
+       char *name;
+       char *fmt;
+       int num_fields;
+       struct ima_template_field **fields;
 };
 
 struct ima_template_entry {
-       u8 digest[IMA_DIGEST_SIZE];     /* sha1 or md5 measurement hash */
-       const char *template_name;
-       int template_len;
-       struct ima_template_data template;
+       u8 digest[TPM_DIGEST_SIZE];     /* sha1 or md5 measurement hash */
+       struct ima_template_desc *template_desc; /* template descriptor */
+       u32 template_data_len;
+       struct ima_field_data template_data[0]; /* template related data */
 };
 
 struct ima_queue_entry {
@@ -69,13 +94,21 @@ int ima_fs_init(void);
 void ima_fs_cleanup(void);
 int ima_inode_alloc(struct inode *inode);
 int ima_add_template_entry(struct ima_template_entry *entry, int violation,
-                          const char *op, struct inode *inode);
-int ima_calc_file_hash(struct file *file, char *digest);
-int ima_calc_buffer_hash(const void *data, int len, char *digest);
-int ima_calc_boot_aggregate(char *digest);
-void ima_add_violation(struct inode *inode, const unsigned char *filename,
+                          const char *op, struct inode *inode,
+                          const unsigned char *filename);
+int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash);
+int ima_calc_field_array_hash(struct ima_field_data *field_data, int num_fields,
+                             struct ima_digest_data *hash);
+int __init ima_calc_boot_aggregate(struct ima_digest_data *hash);
+void ima_add_violation(struct file *file, const unsigned char *filename,
                       const char *op, const char *cause);
 int ima_init_crypto(void);
+void ima_putc(struct seq_file *m, void *data, int datalen);
+void ima_print_digest(struct seq_file *m, u8 *digest, int size);
+struct ima_template_desc *ima_template_desc_current(void);
+int ima_init_template(void);
+
+int ima_init_template(void);
 
 /*
  * used to protect h_table and sha_table
@@ -98,14 +131,21 @@ static inline unsigned long ima_hash_key(u8 *digest)
 int ima_get_action(struct inode *inode, int mask, int function);
 int ima_must_measure(struct inode *inode, int mask, int function);
 int ima_collect_measurement(struct integrity_iint_cache *iint,
-                           struct file *file);
+                           struct file *file,
+                           struct evm_ima_xattr_data **xattr_value,
+                           int *xattr_len);
 void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
-                          const unsigned char *filename);
+                          const unsigned char *filename,
+                          struct evm_ima_xattr_data *xattr_value,
+                          int xattr_len);
 void ima_audit_measurement(struct integrity_iint_cache *iint,
                           const unsigned char *filename);
+int ima_alloc_init_template(struct integrity_iint_cache *iint,
+                           struct file *file, const unsigned char *filename,
+                           struct evm_ima_xattr_data *xattr_value,
+                           int xattr_len, struct ima_template_entry **entry);
 int ima_store_template(struct ima_template_entry *entry, int violation,
-                      struct inode *inode);
-void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
+                      struct inode *inode, const unsigned char *filename);
 const char *ima_d_path(struct path *path, char **pathbuf);
 
 /* rbtree tree calls to lookup, insert, delete
@@ -131,17 +171,25 @@ void ima_delete_rules(void);
 
 #ifdef CONFIG_IMA_APPRAISE
 int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
-                            struct file *file, const unsigned char *filename);
+                            struct file *file, const unsigned char *filename,
+                            struct evm_ima_xattr_data *xattr_value,
+                            int xattr_len);
 int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
 void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
 enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
                                           int func);
+void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len,
+                      struct ima_digest_data *hash);
+int ima_read_xattr(struct dentry *dentry,
+                  struct evm_ima_xattr_data **xattr_value);
 
 #else
 static inline int ima_appraise_measurement(int func,
                                           struct integrity_iint_cache *iint,
                                           struct file *file,
-                                          const unsigned char *filename)
+                                          const unsigned char *filename,
+                                          struct evm_ima_xattr_data *xattr_value,
+                                          int xattr_len)
 {
        return INTEGRITY_UNKNOWN;
 }
@@ -162,6 +210,19 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c
 {
        return INTEGRITY_UNKNOWN;
 }
+
+static inline void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
+                                    int xattr_len,
+                                    struct ima_digest_data *hash)
+{
+}
+
+static inline int ima_read_xattr(struct dentry *dentry,
+                                struct evm_ima_xattr_data **xattr_value)
+{
+       return 0;
+}
+
 #endif
 
 /* LSM based policy rules require audit */
index 1c03e8f..0e75408 100644 (file)
 #include <linux/fs.h>
 #include <linux/xattr.h>
 #include <linux/evm.h>
+#include <crypto/hash_info.h>
 #include "ima.h"
 
-static const char *IMA_TEMPLATE_NAME = "ima";
+/*
+ * ima_alloc_init_template - create and initialize a new template entry
+ */
+int ima_alloc_init_template(struct integrity_iint_cache *iint,
+                           struct file *file, const unsigned char *filename,
+                           struct evm_ima_xattr_data *xattr_value,
+                           int xattr_len, struct ima_template_entry **entry)
+{
+       struct ima_template_desc *template_desc = ima_template_desc_current();
+       int i, result = 0;
+
+       *entry = kzalloc(sizeof(**entry) + template_desc->num_fields *
+                        sizeof(struct ima_field_data), GFP_NOFS);
+       if (!*entry)
+               return -ENOMEM;
+
+       for (i = 0; i < template_desc->num_fields; i++) {
+               struct ima_template_field *field = template_desc->fields[i];
+               u32 len;
+
+               result = field->field_init(iint, file, filename,
+                                          xattr_value, xattr_len,
+                                          &((*entry)->template_data[i]));
+               if (result != 0)
+                       goto out;
+
+               len = (*entry)->template_data[i].len;
+               (*entry)->template_data_len += sizeof(len);
+               (*entry)->template_data_len += len;
+       }
+       (*entry)->template_desc = template_desc;
+       return 0;
+out:
+       kfree(*entry);
+       *entry = NULL;
+       return result;
+}
 
 /*
  * ima_store_template - store ima template measurements
@@ -39,28 +76,34 @@ static const char *IMA_TEMPLATE_NAME = "ima";
  * Returns 0 on success, error code otherwise
  */
 int ima_store_template(struct ima_template_entry *entry,
-                      int violation, struct inode *inode)
+                      int violation, struct inode *inode,
+                      const unsigned char *filename)
 {
        const char *op = "add_template_measure";
        const char *audit_cause = "hashing_error";
+       char *template_name = entry->template_desc->name;
        int result;
-
-       memset(entry->digest, 0, sizeof(entry->digest));
-       entry->template_name = IMA_TEMPLATE_NAME;
-       entry->template_len = sizeof(entry->template);
+       struct {
+               struct ima_digest_data hdr;
+               char digest[TPM_DIGEST_SIZE];
+       } hash;
 
        if (!violation) {
-               result = ima_calc_buffer_hash(&entry->template,
-                                               entry->template_len,
-                                               entry->digest);
+               int num_fields = entry->template_desc->num_fields;
+
+               /* this function uses default algo */
+               hash.hdr.algo = HASH_ALGO_SHA1;
+               result = ima_calc_field_array_hash(&entry->template_data[0],
+                                                  num_fields, &hash.hdr);
                if (result < 0) {
                        integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
-                                           entry->template_name, op,
+                                           template_name, op,
                                            audit_cause, result, 0);
                        return result;
                }
+               memcpy(entry->digest, hash.hdr.digest, hash.hdr.length);
        }
-       result = ima_add_template_entry(entry, violation, op, inode);
+       result = ima_add_template_entry(entry, violation, op, inode, filename);
        return result;
 }
 
@@ -71,24 +114,24 @@ int ima_store_template(struct ima_template_entry *entry,
  * By extending the PCR with 0xFF's instead of with zeroes, the PCR
  * value is invalidated.
  */
-void ima_add_violation(struct inode *inode, const unsigned char *filename,
+void ima_add_violation(struct file *file, const unsigned char *filename,
                       const char *op, const char *cause)
 {
        struct ima_template_entry *entry;
+       struct inode *inode = file->f_dentry->d_inode;
        int violation = 1;
        int result;
 
        /* can overflow, only indicator */
        atomic_long_inc(&ima_htable.violations);
 
-       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
-       if (!entry) {
+       result = ima_alloc_init_template(NULL, file, filename,
+                                        NULL, 0, &entry);
+       if (result < 0) {
                result = -ENOMEM;
                goto err_out;
        }
-       memset(&entry->template, 0, sizeof(entry->template));
-       strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
-       result = ima_store_template(entry, violation, inode);
+       result = ima_store_template(entry, violation, inode, filename);
        if (result < 0)
                kfree(entry);
 err_out:
@@ -138,20 +181,42 @@ int ima_must_measure(struct inode *inode, int mask, int function)
  * Return 0 on success, error code otherwise
  */
 int ima_collect_measurement(struct integrity_iint_cache *iint,
-                           struct file *file)
+                           struct file *file,
+                           struct evm_ima_xattr_data **xattr_value,
+                           int *xattr_len)
 {
        struct inode *inode = file_inode(file);
        const char *filename = file->f_dentry->d_name.name;
        int result = 0;
+       struct {
+               struct ima_digest_data hdr;
+               char digest[IMA_MAX_DIGEST_SIZE];
+       } hash;
+
+       if (xattr_value)
+               *xattr_len = ima_read_xattr(file->f_dentry, xattr_value);
 
        if (!(iint->flags & IMA_COLLECTED)) {
                u64 i_version = file_inode(file)->i_version;
 
-               iint->ima_xattr.type = IMA_XATTR_DIGEST;
-               result = ima_calc_file_hash(file, iint->ima_xattr.digest);
+               /* use default hash algorithm */
+               hash.hdr.algo = ima_hash_algo;
+
+               if (xattr_value)
+                       ima_get_hash_algo(*xattr_value, *xattr_len, &hash.hdr);
+
+               result = ima_calc_file_hash(file, &hash.hdr);
                if (!result) {
-                       iint->version = i_version;
-                       iint->flags |= IMA_COLLECTED;
+                       int length = sizeof(hash.hdr) + hash.hdr.length;
+                       void *tmpbuf = krealloc(iint->ima_hash, length,
+                                               GFP_NOFS);
+                       if (tmpbuf) {
+                               iint->ima_hash = tmpbuf;
+                               memcpy(iint->ima_hash, &hash, length);
+                               iint->version = i_version;
+                               iint->flags |= IMA_COLLECTED;
+                       } else
+                               result = -ENOMEM;
                }
        }
        if (result)
@@ -177,7 +242,9 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
  * Must be called with iint->mutex held.
  */
 void ima_store_measurement(struct integrity_iint_cache *iint,
-                          struct file *file, const unsigned char *filename)
+                          struct file *file, const unsigned char *filename,
+                          struct evm_ima_xattr_data *xattr_value,
+                          int xattr_len)
 {
        const char *op = "add_template_measure";
        const char *audit_cause = "ENOMEM";
@@ -189,19 +256,15 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
        if (iint->flags & IMA_MEASURED)
                return;
 
-       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
-       if (!entry) {
+       result = ima_alloc_init_template(iint, file, filename,
+                                        xattr_value, xattr_len, &entry);
+       if (result < 0) {
                integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
                                    op, audit_cause, result, 0);
                return;
        }
-       memset(&entry->template, 0, sizeof(entry->template));
-       memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE);
-       strcpy(entry->template.file_name,
-              (strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ?
-              file->f_dentry->d_name.name : filename);
 
-       result = ima_store_template(entry, violation, inode);
+       result = ima_store_template(entry, violation, inode, filename);
        if (!result || result == -EEXIST)
                iint->flags |= IMA_MEASURED;
        if (result < 0)
@@ -212,14 +275,16 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
                           const unsigned char *filename)
 {
        struct audit_buffer *ab;
-       char hash[(IMA_DIGEST_SIZE * 2) + 1];
+       char hash[(iint->ima_hash->length * 2) + 1];
+       const char *algo_name = hash_algo_name[iint->ima_hash->algo];
+       char algo_hash[sizeof(hash) + strlen(algo_name) + 2];
        int i;
 
        if (iint->flags & IMA_AUDITED)
                return;
 
-       for (i = 0; i < IMA_DIGEST_SIZE; i++)
-               hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]);
+       for (i = 0; i < iint->ima_hash->length; i++)
+               hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]);
        hash[i * 2] = '\0';
 
        ab = audit_log_start(current->audit_context, GFP_KERNEL,
@@ -230,7 +295,8 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
        audit_log_format(ab, "file=");
        audit_log_untrustedstring(ab, filename);
        audit_log_format(ab, " hash=");
-       audit_log_untrustedstring(ab, hash);
+       snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash);
+       audit_log_untrustedstring(ab, algo_hash);
 
        audit_log_task_info(ab, current);
        audit_log_end(ab);
index 2d4beca..46353ee 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/magic.h>
 #include <linux/ima.h>
 #include <linux/evm.h>
+#include <crypto/hash_info.h>
 
 #include "ima.h"
 
@@ -43,19 +44,31 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
 }
 
 static int ima_fix_xattr(struct dentry *dentry,
-                         struct integrity_iint_cache *iint)
+                        struct integrity_iint_cache *iint)
 {
-       iint->ima_xattr.type = IMA_XATTR_DIGEST;
-       return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
-                                    (u8 *)&iint->ima_xattr,
-                                     sizeof(iint->ima_xattr), 0);
+       int rc, offset;
+       u8 algo = iint->ima_hash->algo;
+
+       if (algo <= HASH_ALGO_SHA1) {
+               offset = 1;
+               iint->ima_hash->xattr.sha1.type = IMA_XATTR_DIGEST;
+       } else {
+               offset = 0;
+               iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG;
+               iint->ima_hash->xattr.ng.algo = algo;
+       }
+       rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
+                                  &iint->ima_hash->xattr.data[offset],
+                                  (sizeof(iint->ima_hash->xattr) - offset) +
+                                  iint->ima_hash->length, 0);
+       return rc;
 }
 
 /* Return specific func appraised cached result */
 enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
                                           int func)
 {
-       switch(func) {
+       switch (func) {
        case MMAP_CHECK:
                return iint->ima_mmap_status;
        case BPRM_CHECK:
@@ -71,7 +84,7 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
 static void ima_set_cache_status(struct integrity_iint_cache *iint,
                                 int func, enum integrity_status status)
 {
-       switch(func) {
+       switch (func) {
        case MMAP_CHECK:
                iint->ima_mmap_status = status;
                break;
@@ -90,7 +103,7 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
 
 static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
 {
-       switch(func) {
+       switch (func) {
        case MMAP_CHECK:
                iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);
                break;
@@ -107,6 +120,50 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
        }
 }
 
+void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len,
+                      struct ima_digest_data *hash)
+{
+       struct signature_v2_hdr *sig;
+
+       if (!xattr_value || xattr_len < 2)
+               return;
+
+       switch (xattr_value->type) {
+       case EVM_IMA_XATTR_DIGSIG:
+               sig = (typeof(sig))xattr_value;
+               if (sig->version != 2 || xattr_len <= sizeof(*sig))
+                       return;
+               hash->algo = sig->hash_algo;
+               break;
+       case IMA_XATTR_DIGEST_NG:
+               hash->algo = xattr_value->digest[0];
+               break;
+       case IMA_XATTR_DIGEST:
+               /* this is for backward compatibility */
+               if (xattr_len == 21) {
+                       unsigned int zero = 0;
+                       if (!memcmp(&xattr_value->digest[16], &zero, 4))
+                               hash->algo = HASH_ALGO_MD5;
+                       else
+                               hash->algo = HASH_ALGO_SHA1;
+               } else if (xattr_len == 17)
+                       hash->algo = HASH_ALGO_MD5;
+               break;
+       }
+}
+
+int ima_read_xattr(struct dentry *dentry,
+                  struct evm_ima_xattr_data **xattr_value)
+{
+       struct inode *inode = dentry->d_inode;
+
+       if (!inode->i_op->getxattr)
+               return 0;
+
+       return vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value,
+                                 0, GFP_NOFS);
+}
+
 /*
  * ima_appraise_measurement - appraise file measurement
  *
@@ -116,23 +173,22 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
  * Return 0 on success, error code otherwise
  */
 int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
-                            struct file *file, const unsigned char *filename)
+                            struct file *file, const unsigned char *filename,
+                            struct evm_ima_xattr_data *xattr_value,
+                            int xattr_len)
 {
        struct dentry *dentry = file->f_dentry;
        struct inode *inode = dentry->d_inode;
-       struct evm_ima_xattr_data *xattr_value = NULL;
        enum integrity_status status = INTEGRITY_UNKNOWN;
        const char *op = "appraise_data";
        char *cause = "unknown";
-       int rc;
+       int rc = xattr_len, hash_start = 0;
 
        if (!ima_appraise)
                return 0;
        if (!inode->i_op->getxattr)
                return INTEGRITY_UNKNOWN;
 
-       rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
-                               0, GFP_NOFS);
        if (rc <= 0) {
                if (rc && rc != -ENODATA)
                        goto out;
@@ -153,14 +209,25 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
                goto out;
        }
        switch (xattr_value->type) {
+       case IMA_XATTR_DIGEST_NG:
+               /* first byte contains algorithm id */
+               hash_start = 1;
        case IMA_XATTR_DIGEST:
                if (iint->flags & IMA_DIGSIG_REQUIRED) {
                        cause = "IMA signature required";
                        status = INTEGRITY_FAIL;
                        break;
                }
-               rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
-                           IMA_DIGEST_SIZE);
+               if (xattr_len - sizeof(xattr_value->type) - hash_start >=
+                               iint->ima_hash->length)
+                       /* xattr length may be longer. md5 hash in previous
+                          version occupied 20 bytes in xattr, instead of 16
+                        */
+                       rc = memcmp(&xattr_value->digest[hash_start],
+                                   iint->ima_hash->digest,
+                                   iint->ima_hash->length);
+               else
+                       rc = -EINVAL;
                if (rc) {
                        cause = "invalid-hash";
                        status = INTEGRITY_FAIL;
@@ -171,9 +238,9 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
        case EVM_IMA_XATTR_DIGSIG:
                iint->flags |= IMA_DIGSIG;
                rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
-                                            xattr_value->digest, rc - 1,
-                                            iint->ima_xattr.digest,
-                                            IMA_DIGEST_SIZE);
+                                            (const char *)xattr_value, rc,
+                                            iint->ima_hash->digest,
+                                            iint->ima_hash->length);
                if (rc == -EOPNOTSUPP) {
                        status = INTEGRITY_UNKNOWN;
                } else if (rc) {
@@ -203,7 +270,6 @@ out:
                ima_cache_flags(iint, func);
        }
        ima_set_cache_status(iint, func, status);
-       kfree(xattr_value);
        return status;
 }
 
@@ -219,7 +285,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
        if (iint->flags & IMA_DIGSIG)
                return;
 
-       rc = ima_collect_measurement(iint, file);
+       rc = ima_collect_measurement(iint, file, NULL, NULL);
        if (rc < 0)
                return;
 
@@ -315,3 +381,14 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
        }
        return result;
 }
+
+#ifdef CONFIG_IMA_TRUSTED_KEYRING
+static int __init init_ima_keyring(void)
+{
+       int ret;
+
+       ret = integrity_init_keyring(INTEGRITY_KEYRING_IMA);
+       return 0;
+}
+late_initcall(init_ima_keyring);
+#endif
index a02e079..676e029 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <crypto/hash.h>
+#include <crypto/hash_info.h>
 #include "ima.h"
 
 static struct crypto_shash *ima_shash_tfm;
@@ -28,31 +29,58 @@ int ima_init_crypto(void)
 {
        long rc;
 
-       ima_shash_tfm = crypto_alloc_shash(ima_hash, 0, 0);
+       ima_shash_tfm = crypto_alloc_shash(hash_algo_name[ima_hash_algo], 0, 0);
        if (IS_ERR(ima_shash_tfm)) {
                rc = PTR_ERR(ima_shash_tfm);
-               pr_err("Can not allocate %s (reason: %ld)\n", ima_hash, rc);
+               pr_err("Can not allocate %s (reason: %ld)\n",
+                      hash_algo_name[ima_hash_algo], rc);
                return rc;
        }
        return 0;
 }
 
+static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo)
+{
+       struct crypto_shash *tfm = ima_shash_tfm;
+       int rc;
+
+       if (algo != ima_hash_algo && algo < HASH_ALGO__LAST) {
+               tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
+               if (IS_ERR(tfm)) {
+                       rc = PTR_ERR(tfm);
+                       pr_err("Can not allocate %s (reason: %d)\n",
+                              hash_algo_name[algo], rc);
+               }
+       }
+       return tfm;
+}
+
+static void ima_free_tfm(struct crypto_shash *tfm)
+{
+       if (tfm != ima_shash_tfm)
+               crypto_free_shash(tfm);
+}
+
 /*
  * Calculate the MD5/SHA1 file digest
  */
-int ima_calc_file_hash(struct file *file, char *digest)
+static int ima_calc_file_hash_tfm(struct file *file,
+                                 struct ima_digest_data *hash,
+                                 struct crypto_shash *tfm)
 {
        loff_t i_size, offset = 0;
        char *rbuf;
        int rc, read = 0;
        struct {
                struct shash_desc shash;
-               char ctx[crypto_shash_descsize(ima_shash_tfm)];
+               char ctx[crypto_shash_descsize(tfm)];
        } desc;
 
-       desc.shash.tfm = ima_shash_tfm;
+       desc.shash.tfm = tfm;
        desc.shash.flags = 0;
 
+       hash->length = crypto_shash_digestsize(tfm);
+
        rc = crypto_shash_init(&desc.shash);
        if (rc != 0)
                return rc;
@@ -85,27 +113,83 @@ int ima_calc_file_hash(struct file *file, char *digest)
        }
        kfree(rbuf);
        if (!rc)
-               rc = crypto_shash_final(&desc.shash, digest);
+               rc = crypto_shash_final(&desc.shash, hash->digest);
        if (read)
                file->f_mode &= ~FMODE_READ;
 out:
        return rc;
 }
 
+int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
+{
+       struct crypto_shash *tfm;
+       int rc;
+
+       tfm = ima_alloc_tfm(hash->algo);
+       if (IS_ERR(tfm))
+               return PTR_ERR(tfm);
+
+       rc = ima_calc_file_hash_tfm(file, hash, tfm);
+
+       ima_free_tfm(tfm);
+
+       return rc;
+}
+
 /*
- * Calculate the hash of a given buffer
+ * Calculate the hash of template data
  */
-int ima_calc_buffer_hash(const void *data, int len, char *digest)
+static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data,
+                                        int num_fields,
+                                        struct ima_digest_data *hash,
+                                        struct crypto_shash *tfm)
 {
        struct {
                struct shash_desc shash;
-               char ctx[crypto_shash_descsize(ima_shash_tfm)];
+               char ctx[crypto_shash_descsize(tfm)];
        } desc;
+       int rc, i;
 
-       desc.shash.tfm = ima_shash_tfm;
+       desc.shash.tfm = tfm;
        desc.shash.flags = 0;
 
-       return crypto_shash_digest(&desc.shash, data, len, digest);
+       hash->length = crypto_shash_digestsize(tfm);
+
+       rc = crypto_shash_init(&desc.shash);
+       if (rc != 0)
+               return rc;
+
+       for (i = 0; i < num_fields; i++) {
+               rc = crypto_shash_update(&desc.shash,
+                                        (const u8 *) &field_data[i].len,
+                                        sizeof(field_data[i].len));
+               rc = crypto_shash_update(&desc.shash, field_data[i].data,
+                                        field_data[i].len);
+               if (rc)
+                       break;
+       }
+
+       if (!rc)
+               rc = crypto_shash_final(&desc.shash, hash->digest);
+
+       return rc;
+}
+
+int ima_calc_field_array_hash(struct ima_field_data *field_data, int num_fields,
+                             struct ima_digest_data *hash)
+{
+       struct crypto_shash *tfm;
+       int rc;
+
+       tfm = ima_alloc_tfm(hash->algo);
+       if (IS_ERR(tfm))
+               return PTR_ERR(tfm);
+
+       rc = ima_calc_field_array_hash_tfm(field_data, num_fields, hash, tfm);
+
+       ima_free_tfm(tfm);
+
+       return rc;
 }
 
 static void __init ima_pcrread(int idx, u8 *pcr)
@@ -120,16 +204,17 @@ static void __init ima_pcrread(int idx, u8 *pcr)
 /*
  * Calculate the boot aggregate hash
  */
-int __init ima_calc_boot_aggregate(char *digest)
+static int __init ima_calc_boot_aggregate_tfm(char *digest,
+                                             struct crypto_shash *tfm)
 {
-       u8 pcr_i[IMA_DIGEST_SIZE];
+       u8 pcr_i[TPM_DIGEST_SIZE];
        int rc, i;
        struct {
                struct shash_desc shash;
-               char ctx[crypto_shash_descsize(ima_shash_tfm)];
+               char ctx[crypto_shash_descsize(tfm)];
        } desc;
 
-       desc.shash.tfm = ima_shash_tfm;
+       desc.shash.tfm = tfm;
        desc.shash.flags = 0;
 
        rc = crypto_shash_init(&desc.shash);
@@ -140,9 +225,26 @@ int __init ima_calc_boot_aggregate(char *digest)
        for (i = TPM_PCR0; i < TPM_PCR8; i++) {
                ima_pcrread(i, pcr_i);
                /* now accumulate with current aggregate */
-               rc = crypto_shash_update(&desc.shash, pcr_i, IMA_DIGEST_SIZE);
+               rc = crypto_shash_update(&desc.shash, pcr_i, TPM_DIGEST_SIZE);
        }
        if (!rc)
                crypto_shash_final(&desc.shash, digest);
        return rc;
 }
+
+int __init ima_calc_boot_aggregate(struct ima_digest_data *hash)
+{
+       struct crypto_shash *tfm;
+       int rc;
+
+       tfm = ima_alloc_tfm(hash->algo);
+       if (IS_ERR(tfm))
+               return PTR_ERR(tfm);
+
+       hash->length = crypto_shash_digestsize(tfm);
+       rc = ima_calc_boot_aggregate_tfm(hash->digest, tfm);
+
+       ima_free_tfm(tfm);
+
+       return rc;
+}
index 38477c9..d47a7c8 100644 (file)
@@ -88,8 +88,7 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
         * against concurrent list-extension
         */
        rcu_read_lock();
-       qe = list_entry_rcu(qe->later.next,
-                           struct ima_queue_entry, later);
+       qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, later);
        rcu_read_unlock();
        (*pos)++;
 
@@ -100,7 +99,7 @@ static void ima_measurements_stop(struct seq_file *m, void *v)
 {
 }
 
-static void ima_putc(struct seq_file *m, void *data, int datalen)
+void ima_putc(struct seq_file *m, void *data, int datalen)
 {
        while (datalen--)
                seq_putc(m, *(char *)data++);
@@ -111,6 +110,7 @@ static void ima_putc(struct seq_file *m, void *data, int datalen)
  *       char[20]=template digest
  *       32bit-le=template name size
  *       char[n]=template name
+ *       [eventdata length]
  *       eventdata[n]=template specific data
  */
 static int ima_measurements_show(struct seq_file *m, void *v)
@@ -120,6 +120,7 @@ static int ima_measurements_show(struct seq_file *m, void *v)
        struct ima_template_entry *e;
        int namelen;
        u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+       int i;
 
        /* get entry */
        e = qe->entry;
@@ -134,18 +135,25 @@ static int ima_measurements_show(struct seq_file *m, void *v)
        ima_putc(m, &pcr, sizeof pcr);
 
        /* 2nd: template digest */
-       ima_putc(m, e->digest, IMA_DIGEST_SIZE);
+       ima_putc(m, e->digest, TPM_DIGEST_SIZE);
 
        /* 3rd: template name size */
-       namelen = strlen(e->template_name);
+       namelen = strlen(e->template_desc->name);
        ima_putc(m, &namelen, sizeof namelen);
 
        /* 4th:  template name */
-       ima_putc(m, (void *)e->template_name, namelen);
+       ima_putc(m, e->template_desc->name, namelen);
+
+       /* 5th:  template length (except for 'ima' template) */
+       if (strcmp(e->template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0)
+               ima_putc(m, &e->template_data_len,
+                        sizeof(e->template_data_len));
 
-       /* 5th:  template specific data */
-       ima_template_show(m, (struct ima_template_data *)&e->template,
-                         IMA_SHOW_BINARY);
+       /* 6th:  template specific data */
+       for (i = 0; i < e->template_desc->num_fields; i++) {
+               e->template_desc->fields[i]->field_show(m, IMA_SHOW_BINARY,
+                                                       &e->template_data[i]);
+       }
        return 0;
 }
 
@@ -168,41 +176,21 @@ static const struct file_operations ima_measurements_ops = {
        .release = seq_release,
 };
 
-static void ima_print_digest(struct seq_file *m, u8 *digest)
+void ima_print_digest(struct seq_file *m, u8 *digest, int size)
 {
        int i;
 
-       for (i = 0; i < IMA_DIGEST_SIZE; i++)
+       for (i = 0; i < size; i++)
                seq_printf(m, "%02x", *(digest + i));
 }
 
-void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show)
-{
-       struct ima_template_data *entry = e;
-       int namelen;
-
-       switch (show) {
-       case IMA_SHOW_ASCII:
-               ima_print_digest(m, entry->digest);
-               seq_printf(m, " %s\n", entry->file_name);
-               break;
-       case IMA_SHOW_BINARY:
-               ima_putc(m, entry->digest, IMA_DIGEST_SIZE);
-
-               namelen = strlen(entry->file_name);
-               ima_putc(m, &namelen, sizeof namelen);
-               ima_putc(m, entry->file_name, namelen);
-       default:
-               break;
-       }
-}
-
 /* print in ascii */
 static int ima_ascii_measurements_show(struct seq_file *m, void *v)
 {
        /* the list never shrinks, so we don't need a lock here */
        struct ima_queue_entry *qe = v;
        struct ima_template_entry *e;
+       int i;
 
        /* get entry */
        e = qe->entry;
@@ -213,14 +201,21 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
        seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
 
        /* 2nd: SHA1 template hash */
-       ima_print_digest(m, e->digest);
+       ima_print_digest(m, e->digest, TPM_DIGEST_SIZE);
 
        /* 3th:  template name */
-       seq_printf(m, " %s ", e->template_name);
+       seq_printf(m, " %s", e->template_desc->name);
 
        /* 4th:  template specific data */
-       ima_template_show(m, (struct ima_template_data *)&e->template,
-                         IMA_SHOW_ASCII);
+       for (i = 0; i < e->template_desc->num_fields; i++) {
+               seq_puts(m, " ");
+               if (e->template_data[i].len == 0)
+                       continue;
+
+               e->template_desc->fields[i]->field_show(m, IMA_SHOW_ASCII,
+                                                       &e->template_data[i]);
+       }
+       seq_puts(m, "\n");
        return 0;
 }
 
index 162ea72..15f34bd 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
 #include <linux/err.h>
+#include <crypto/hash_info.h>
 #include "ima.h"
 
 /* name for boot aggregate entry */
@@ -42,28 +43,38 @@ int ima_used_chip;
 static void __init ima_add_boot_aggregate(void)
 {
        struct ima_template_entry *entry;
+       struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
        const char *op = "add_boot_aggregate";
        const char *audit_cause = "ENOMEM";
        int result = -ENOMEM;
-       int violation = 1;
+       int violation = 0;
+       struct {
+               struct ima_digest_data hdr;
+               char digest[TPM_DIGEST_SIZE];
+       } hash;
 
-       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
-       if (!entry)
-               goto err_out;
+       memset(iint, 0, sizeof(*iint));
+       memset(&hash, 0, sizeof(hash));
+       iint->ima_hash = &hash.hdr;
+       iint->ima_hash->algo = HASH_ALGO_SHA1;
+       iint->ima_hash->length = SHA1_DIGEST_SIZE;
 
-       memset(&entry->template, 0, sizeof(entry->template));
-       strncpy(entry->template.file_name, boot_aggregate_name,
-               IMA_EVENT_NAME_LEN_MAX);
        if (ima_used_chip) {
-               violation = 0;
-               result = ima_calc_boot_aggregate(entry->template.digest);
+               result = ima_calc_boot_aggregate(&hash.hdr);
                if (result < 0) {
                        audit_cause = "hashing_error";
                        kfree(entry);
                        goto err_out;
                }
        }
-       result = ima_store_template(entry, violation, NULL);
+
+       result = ima_alloc_init_template(iint, NULL, boot_aggregate_name,
+                                        NULL, 0, &entry);
+       if (result < 0)
+               return;
+
+       result = ima_store_template(entry, violation, NULL,
+                                   boot_aggregate_name);
        if (result < 0)
                kfree(entry);
        return;
@@ -74,7 +85,7 @@ err_out:
 
 int __init ima_init(void)
 {
-       u8 pcr_i[IMA_DIGEST_SIZE];
+       u8 pcr_i[TPM_DIGEST_SIZE];
        int rc;
 
        ima_used_chip = 0;
@@ -88,6 +99,10 @@ int __init ima_init(void)
        rc = ima_init_crypto();
        if (rc)
                return rc;
+       rc = ima_init_template();
+       if (rc != 0)
+               return rc;
+
        ima_add_boot_aggregate();       /* boot aggregate must be first entry */
        ima_init_policy();
 
index e9508d5..149ee11 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/xattr.h>
 #include <linux/ima.h>
+#include <crypto/hash_info.h>
 
 #include "ima.h"
 
@@ -35,11 +36,33 @@ int ima_appraise = IMA_APPRAISE_ENFORCE;
 int ima_appraise;
 #endif
 
-char *ima_hash = "sha1";
+int ima_hash_algo = HASH_ALGO_SHA1;
+static int hash_setup_done;
+
 static int __init hash_setup(char *str)
 {
-       if (strncmp(str, "md5", 3) == 0)
-               ima_hash = "md5";
+       struct ima_template_desc *template_desc = ima_template_desc_current();
+       int i;
+
+       if (hash_setup_done)
+               return 1;
+
+       if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) {
+               if (strncmp(str, "sha1", 4) == 0)
+                       ima_hash_algo = HASH_ALGO_SHA1;
+               else if (strncmp(str, "md5", 3) == 0)
+                       ima_hash_algo = HASH_ALGO_MD5;
+               goto out;
+       }
+
+       for (i = 0; i < HASH_ALGO__LAST; i++) {
+               if (strcmp(str, hash_algo_name[i]) == 0) {
+                       ima_hash_algo = i;
+                       break;
+               }
+       }
+out:
+       hash_setup_done = 1;
        return 1;
 }
 __setup("ima_hash=", hash_setup);
@@ -92,10 +115,9 @@ out:
                pathname = dentry->d_name.name;
 
        if (send_tomtou)
-               ima_add_violation(inode, pathname,
-                                 "invalid_pcr", "ToMToU");
+               ima_add_violation(file, pathname, "invalid_pcr", "ToMToU");
        if (send_writers)
-               ima_add_violation(inode, pathname,
+               ima_add_violation(file, pathname,
                                  "invalid_pcr", "open_writers");
        kfree(pathbuf);
 }
@@ -144,9 +166,12 @@ static int process_measurement(struct file *file, const char *filename,
 {
        struct inode *inode = file_inode(file);
        struct integrity_iint_cache *iint;
+       struct ima_template_desc *template_desc = ima_template_desc_current();
        char *pathbuf = NULL;
        const char *pathname = NULL;
        int rc = -ENOMEM, action, must_appraise, _func;
+       struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL;
+       int xattr_len = 0;
 
        if (!ima_initialized || !S_ISREG(inode->i_mode))
                return 0;
@@ -185,7 +210,13 @@ static int process_measurement(struct file *file, const char *filename,
                goto out_digsig;
        }
 
-       rc = ima_collect_measurement(iint, file);
+       if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) {
+               if (action & IMA_APPRAISE_SUBMASK)
+                       xattr_ptr = &xattr_value;
+       } else
+               xattr_ptr = &xattr_value;
+
+       rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len);
        if (rc != 0)
                goto out_digsig;
 
@@ -194,9 +225,11 @@ static int process_measurement(struct file *file, const char *filename,
                pathname = (const char *)file->f_dentry->d_name.name;
 
        if (action & IMA_MEASURE)
-               ima_store_measurement(iint, file, pathname);
+               ima_store_measurement(iint, file, pathname,
+                                     xattr_value, xattr_len);
        if (action & IMA_APPRAISE_SUBMASK)
-               rc = ima_appraise_measurement(_func, iint, file, pathname);
+               rc = ima_appraise_measurement(_func, iint, file, pathname,
+                                             xattr_value, xattr_len);
        if (action & IMA_AUDIT)
                ima_audit_measurement(iint, pathname);
        kfree(pathbuf);
@@ -205,6 +238,7 @@ out_digsig:
                rc = -EACCES;
 out:
        mutex_unlock(&inode->i_mutex);
+       kfree(xattr_value);
        if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
                return -EACCES;
        return 0;
@@ -244,9 +278,9 @@ int ima_file_mmap(struct file *file, unsigned long prot)
 int ima_bprm_check(struct linux_binprm *bprm)
 {
        return process_measurement(bprm->file,
-                                (strcmp(bprm->filename, bprm->interp) == 0) ?
-                                bprm->filename : bprm->interp,
-                                MAY_EXEC, BPRM_CHECK);
+                                  (strcmp(bprm->filename, bprm->interp) == 0) ?
+                                  bprm->filename : bprm->interp,
+                                  MAY_EXEC, BPRM_CHECK);
 }
 
 /**
@@ -263,8 +297,8 @@ int ima_file_check(struct file *file, int mask)
 {
        ima_rdwr_violation_check(file);
        return process_measurement(file, NULL,
-                                mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
-                                FILE_CHECK);
+                                  mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
+                                  FILE_CHECK);
 }
 EXPORT_SYMBOL_GPL(ima_file_check);
 
@@ -294,6 +328,7 @@ static int __init init_ima(void)
 {
        int error;
 
+       hash_setup(CONFIG_IMA_DEFAULT_HASH);
        error = ima_init();
        if (!error)
                ima_initialized = 1;
index 399433a..a9c3d3c 100644 (file)
@@ -73,7 +73,6 @@ static struct ima_rule_entry default_rules[] = {
        {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
-       {.action = DONT_MEASURE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
index ff63fe0..d85e997 100644 (file)
@@ -50,7 +50,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
        key = ima_hash_key(digest_value);
        rcu_read_lock();
        hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) {
-               rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE);
+               rc = memcmp(qe->entry->digest, digest_value, TPM_DIGEST_SIZE);
                if (rc == 0) {
                        ret = qe;
                        break;
@@ -104,9 +104,10 @@ static int ima_pcr_extend(const u8 *hash)
  * and extend the pcr.
  */
 int ima_add_template_entry(struct ima_template_entry *entry, int violation,
-                          const char *op, struct inode *inode)
+                          const char *op, struct inode *inode,
+                          const unsigned char *filename)
 {
-       u8 digest[IMA_DIGEST_SIZE];
+       u8 digest[TPM_DIGEST_SIZE];
        const char *audit_cause = "hash_added";
        char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX];
        int audit_info = 1;
@@ -141,8 +142,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
        }
 out:
        mutex_unlock(&ima_extend_list_mutex);
-       integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
-                           entry->template.file_name,
+       integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
                            op, audit_cause, result, audit_info);
        return result;
 }
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
new file mode 100644 (file)
index 0000000..4e5da99
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2013 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Author: Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_template.c
+ *      Helpers to manage template descriptors.
+ */
+#include <crypto/hash_info.h>
+
+#include "ima.h"
+#include "ima_template_lib.h"
+
+static struct ima_template_desc defined_templates[] = {
+       {.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
+       {.name = "ima-ng",.fmt = "d-ng|n-ng"},
+       {.name = "ima-sig",.fmt = "d-ng|n-ng|sig"},
+};
+
+static struct ima_template_field supported_fields[] = {
+       {.field_id = "d",.field_init = ima_eventdigest_init,
+        .field_show = ima_show_template_digest},
+       {.field_id = "n",.field_init = ima_eventname_init,
+        .field_show = ima_show_template_string},
+       {.field_id = "d-ng",.field_init = ima_eventdigest_ng_init,
+        .field_show = ima_show_template_digest_ng},
+       {.field_id = "n-ng",.field_init = ima_eventname_ng_init,
+        .field_show = ima_show_template_string},
+       {.field_id = "sig",.field_init = ima_eventsig_init,
+        .field_show = ima_show_template_sig},
+};
+
+static struct ima_template_desc *ima_template;
+static struct ima_template_desc *lookup_template_desc(const char *name);
+
+static int __init ima_template_setup(char *str)
+{
+       struct ima_template_desc *template_desc;
+       int template_len = strlen(str);
+
+       /*
+        * Verify that a template with the supplied name exists.
+        * If not, use CONFIG_IMA_DEFAULT_TEMPLATE.
+        */
+       template_desc = lookup_template_desc(str);
+       if (!template_desc)
+               return 1;
+
+       /*
+        * Verify whether the current hash algorithm is supported
+        * by the 'ima' template.
+        */
+       if (template_len == 3 && strcmp(str, IMA_TEMPLATE_IMA_NAME) == 0 &&
+           ima_hash_algo != HASH_ALGO_SHA1 && ima_hash_algo != HASH_ALGO_MD5) {
+               pr_err("IMA: template does not support hash alg\n");
+               return 1;
+       }
+
+       ima_template = template_desc;
+       return 1;
+}
+__setup("ima_template=", ima_template_setup);
+
+static struct ima_template_desc *lookup_template_desc(const char *name)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(defined_templates); i++) {
+               if (strcmp(defined_templates[i].name, name) == 0)
+                       return defined_templates + i;
+       }
+
+       return NULL;
+}
+
+static struct ima_template_field *lookup_template_field(const char *field_id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(supported_fields); i++)
+               if (strncmp(supported_fields[i].field_id, field_id,
+                           IMA_TEMPLATE_FIELD_ID_MAX_LEN) == 0)
+                       return &supported_fields[i];
+       return NULL;
+}
+
+static int template_fmt_size(char *template_fmt)
+{
+       char c;
+       int template_fmt_len = strlen(template_fmt);
+       int i = 0, j = 0;
+
+       while (i < template_fmt_len) {
+               c = template_fmt[i];
+               if (c == '|')
+                       j++;
+               i++;
+       }
+
+       return j + 1;
+}
+
+static int template_desc_init_fields(char *template_fmt,
+                                    struct ima_template_field ***fields,
+                                    int *num_fields)
+{
+       char *c, *template_fmt_ptr = template_fmt;
+       int template_num_fields = template_fmt_size(template_fmt);
+       int i, result = 0;
+
+       if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX)
+               return -EINVAL;
+
+       *fields = kzalloc(template_num_fields * sizeof(*fields), GFP_KERNEL);
+       if (*fields == NULL) {
+               result = -ENOMEM;
+               goto out;
+       }
+       for (i = 0; (c = strsep(&template_fmt_ptr, "|")) != NULL &&
+            i < template_num_fields; i++) {
+               struct ima_template_field *f = lookup_template_field(c);
+
+               if (!f) {
+                       result = -ENOENT;
+                       goto out;
+               }
+               (*fields)[i] = f;
+       }
+       *num_fields = i;
+       return 0;
+out:
+       kfree(*fields);
+       *fields = NULL;
+       return result;
+}
+
+static int init_defined_templates(void)
+{
+       int i = 0;
+       int result = 0;
+
+       /* Init defined templates. */
+       for (i = 0; i < ARRAY_SIZE(defined_templates); i++) {
+               struct ima_template_desc *template = &defined_templates[i];
+
+               result = template_desc_init_fields(template->fmt,
+                                                  &(template->fields),
+                                                  &(template->num_fields));
+               if (result < 0)
+                       return result;
+       }
+       return result;
+}
+
+struct ima_template_desc *ima_template_desc_current(void)
+{
+       if (!ima_template)
+               ima_template =
+                   lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE);
+       return ima_template;
+}
+
+int ima_init_template(void)
+{
+       int result;
+
+       result = init_defined_templates();
+       if (result < 0)
+               return result;
+
+       return 0;
+}
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
new file mode 100644 (file)
index 0000000..6d66ad6
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2013 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Author: Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_template_lib.c
+ *      Library of supported template fields.
+ */
+#include <crypto/hash_info.h>
+
+#include "ima_template_lib.h"
+
+static bool ima_template_hash_algo_allowed(u8 algo)
+{
+       if (algo == HASH_ALGO_SHA1 || algo == HASH_ALGO_MD5)
+               return true;
+
+       return false;
+}
+
+enum data_formats {
+       DATA_FMT_DIGEST = 0,
+       DATA_FMT_DIGEST_WITH_ALGO,
+       DATA_FMT_EVENT_NAME,
+       DATA_FMT_STRING,
+       DATA_FMT_HEX
+};
+
+static int ima_write_template_field_data(const void *data, const u32 datalen,
+                                        enum data_formats datafmt,
+                                        struct ima_field_data *field_data)
+{
+       u8 *buf, *buf_ptr;
+       u32 buflen;
+
+       switch (datafmt) {
+       case DATA_FMT_EVENT_NAME:
+               buflen = IMA_EVENT_NAME_LEN_MAX + 1;
+               break;
+       case DATA_FMT_STRING:
+               buflen = datalen + 1;
+               break;
+       default:
+               buflen = datalen;
+       }
+
+       buf = kzalloc(buflen, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       memcpy(buf, data, datalen);
+
+       /*
+        * Replace all space characters with underscore for event names and
+        * strings. This avoid that, during the parsing of a measurements list,
+        * filenames with spaces or that end with the suffix ' (deleted)' are
+        * split into multiple template fields (the space is the delimitator
+        * character for measurements lists in ASCII format).
+        */
+       if (datafmt == DATA_FMT_EVENT_NAME || datafmt == DATA_FMT_STRING) {
+               for (buf_ptr = buf; buf_ptr - buf < datalen; buf_ptr++)
+                       if (*buf_ptr == ' ')
+                               *buf_ptr = '_';
+       }
+
+       field_data->data = buf;
+       field_data->len = buflen;
+       return 0;
+}
+
+static void ima_show_template_data_ascii(struct seq_file *m,
+                                        enum ima_show_type show,
+                                        enum data_formats datafmt,
+                                        struct ima_field_data *field_data)
+{
+       u8 *buf_ptr = field_data->data, buflen = field_data->len;
+
+       switch (datafmt) {
+       case DATA_FMT_DIGEST_WITH_ALGO:
+               buf_ptr = strnchr(field_data->data, buflen, ':');
+               if (buf_ptr != field_data->data)
+                       seq_printf(m, "%s", field_data->data);
+
+               /* skip ':' and '\0' */
+               buf_ptr += 2;
+               buflen -= buf_ptr - field_data->data;
+       case DATA_FMT_DIGEST:
+       case DATA_FMT_HEX:
+               if (!buflen)
+                       break;
+               ima_print_digest(m, buf_ptr, buflen);
+               break;
+       case DATA_FMT_STRING:
+               seq_printf(m, "%s", buf_ptr);
+               break;
+       default:
+               break;
+       }
+}
+
+static void ima_show_template_data_binary(struct seq_file *m,
+                                         enum ima_show_type show,
+                                         enum data_formats datafmt,
+                                         struct ima_field_data *field_data)
+{
+       ima_putc(m, &field_data->len, sizeof(u32));
+       if (!field_data->len)
+               return;
+       ima_putc(m, field_data->data, field_data->len);
+}
+
+static void ima_show_template_field_data(struct seq_file *m,
+                                        enum ima_show_type show,
+                                        enum data_formats datafmt,
+                                        struct ima_field_data *field_data)
+{
+       switch (show) {
+       case IMA_SHOW_ASCII:
+               ima_show_template_data_ascii(m, show, datafmt, field_data);
+               break;
+       case IMA_SHOW_BINARY:
+               ima_show_template_data_binary(m, show, datafmt, field_data);
+               break;
+       default:
+               break;
+       }
+}
+
+void ima_show_template_digest(struct seq_file *m, enum ima_show_type show,
+                             struct ima_field_data *field_data)
+{
+       ima_show_template_field_data(m, show, DATA_FMT_DIGEST, field_data);
+}
+
+void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show,
+                                struct ima_field_data *field_data)
+{
+       ima_show_template_field_data(m, show, DATA_FMT_DIGEST_WITH_ALGO,
+                                    field_data);
+}
+
+void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
+                             struct ima_field_data *field_data)
+{
+       ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data);
+}
+
+void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
+                          struct ima_field_data *field_data)
+{
+       ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
+}
+
+static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo,
+                                      struct ima_field_data *field_data,
+                                      bool size_limit)
+{
+       /*
+        * digest formats:
+        *  - DATA_FMT_DIGEST: digest
+        *  - DATA_FMT_DIGEST_WITH_ALGO: [<hash algo>] + ':' + '\0' + digest,
+        *    where <hash algo> is provided if the hash algoritm is not
+        *    SHA1 or MD5
+        */
+       u8 buffer[CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE] = { 0 };
+       enum data_formats fmt = DATA_FMT_DIGEST;
+       u32 offset = 0;
+
+       if (!size_limit) {
+               fmt = DATA_FMT_DIGEST_WITH_ALGO;
+               if (hash_algo < HASH_ALGO__LAST)
+                       offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1,
+                                          "%s", hash_algo_name[hash_algo]);
+               buffer[offset] = ':';
+               offset += 2;
+       }
+
+       if (digest)
+               memcpy(buffer + offset, digest, digestsize);
+       else
+               /*
+                * If digest is NULL, the event being recorded is a violation.
+                * Make room for the digest by increasing the offset of
+                * IMA_DIGEST_SIZE.
+                */
+               offset += IMA_DIGEST_SIZE;
+
+       return ima_write_template_field_data(buffer, offset + digestsize,
+                                            fmt, field_data);
+}
+
+/*
+ * This function writes the digest of an event (with size limit).
+ */
+int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file,
+                        const unsigned char *filename,
+                        struct evm_ima_xattr_data *xattr_value, int xattr_len,
+                        struct ima_field_data *field_data)
+{
+       struct {
+               struct ima_digest_data hdr;
+               char digest[IMA_MAX_DIGEST_SIZE];
+       } hash;
+       u8 *cur_digest = NULL;
+       u32 cur_digestsize = 0;
+       struct inode *inode;
+       int result;
+
+       memset(&hash, 0, sizeof(hash));
+
+       if (!iint)              /* recording a violation. */
+               goto out;
+
+       if (ima_template_hash_algo_allowed(iint->ima_hash->algo)) {
+               cur_digest = iint->ima_hash->digest;
+               cur_digestsize = iint->ima_hash->length;
+               goto out;
+       }
+
+       if (!file)              /* missing info to re-calculate the digest */
+               return -EINVAL;
+
+       inode = file_inode(file);
+       hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ?
+           ima_hash_algo : HASH_ALGO_SHA1;
+       result = ima_calc_file_hash(file, &hash.hdr);
+       if (result) {
+               integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
+                                   filename, "collect_data",
+                                   "failed", result, 0);
+               return result;
+       }
+       cur_digest = hash.hdr.digest;
+       cur_digestsize = hash.hdr.length;
+out:
+       return ima_eventdigest_init_common(cur_digest, cur_digestsize, -1,
+                                          field_data, true);
+}
+
+/*
+ * This function writes the digest of an event (without size limit).
+ */
+int ima_eventdigest_ng_init(struct integrity_iint_cache *iint,
+                           struct file *file, const unsigned char *filename,
+                           struct evm_ima_xattr_data *xattr_value,
+                           int xattr_len, struct ima_field_data *field_data)
+{
+       u8 *cur_digest = NULL, hash_algo = HASH_ALGO__LAST;
+       u32 cur_digestsize = 0;
+
+       /* If iint is NULL, we are recording a violation. */
+       if (!iint)
+               goto out;
+
+       cur_digest = iint->ima_hash->digest;
+       cur_digestsize = iint->ima_hash->length;
+
+       hash_algo = iint->ima_hash->algo;
+out:
+       return ima_eventdigest_init_common(cur_digest, cur_digestsize,
+                                          hash_algo, field_data, false);
+}
+
+static int ima_eventname_init_common(struct integrity_iint_cache *iint,
+                                    struct file *file,
+                                    const unsigned char *filename,
+                                    struct ima_field_data *field_data,
+                                    bool size_limit)
+{
+       const char *cur_filename = NULL;
+       u32 cur_filename_len = 0;
+       enum data_formats fmt = size_limit ?
+           DATA_FMT_EVENT_NAME : DATA_FMT_STRING;
+
+       BUG_ON(filename == NULL && file == NULL);
+
+       if (filename) {
+               cur_filename = filename;
+               cur_filename_len = strlen(filename);
+
+               if (!size_limit || cur_filename_len <= IMA_EVENT_NAME_LEN_MAX)
+                       goto out;
+       }
+
+       if (file) {
+               cur_filename = file->f_dentry->d_name.name;
+               cur_filename_len = strlen(cur_filename);
+       } else
+               /*
+                * Truncate filename if the latter is too long and
+                * the file descriptor is not available.
+                */
+               cur_filename_len = IMA_EVENT_NAME_LEN_MAX;
+out:
+       return ima_write_template_field_data(cur_filename, cur_filename_len,
+                                            fmt, field_data);
+}
+
+/*
+ * This function writes the name of an event (with size limit).
+ */
+int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file,
+                      const unsigned char *filename,
+                      struct evm_ima_xattr_data *xattr_value, int xattr_len,
+                      struct ima_field_data *field_data)
+{
+       return ima_eventname_init_common(iint, file, filename,
+                                        field_data, true);
+}
+
+/*
+ * This function writes the name of an event (without size limit).
+ */
+int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file,
+                         const unsigned char *filename,
+                         struct evm_ima_xattr_data *xattr_value, int xattr_len,
+                         struct ima_field_data *field_data)
+{
+       return ima_eventname_init_common(iint, file, filename,
+                                        field_data, false);
+}
+
+/*
+ *  ima_eventsig_init - include the file signature as part of the template data
+ */
+int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file,
+                     const unsigned char *filename,
+                     struct evm_ima_xattr_data *xattr_value, int xattr_len,
+                     struct ima_field_data *field_data)
+{
+       enum data_formats fmt = DATA_FMT_HEX;
+       int rc = 0;
+
+       if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
+               goto out;
+
+       rc = ima_write_template_field_data(xattr_value, xattr_len, fmt,
+                                          field_data);
+out:
+       return rc;
+}
diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h
new file mode 100644 (file)
index 0000000..63f6b52
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Author: Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_template_lib.h
+ *      Header for the library of supported template fields.
+ */
+#ifndef __LINUX_IMA_TEMPLATE_LIB_H
+#define __LINUX_IMA_TEMPLATE_LIB_H
+
+#include <linux/seq_file.h>
+#include "ima.h"
+
+void ima_show_template_digest(struct seq_file *m, enum ima_show_type show,
+                             struct ima_field_data *field_data);
+void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show,
+                                struct ima_field_data *field_data);
+void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
+                             struct ima_field_data *field_data);
+void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
+                          struct ima_field_data *field_data);
+int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file,
+                        const unsigned char *filename,
+                        struct evm_ima_xattr_data *xattr_value, int xattr_len,
+                        struct ima_field_data *field_data);
+int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file,
+                      const unsigned char *filename,
+                      struct evm_ima_xattr_data *xattr_value, int xattr_len,
+                      struct ima_field_data *field_data);
+int ima_eventdigest_ng_init(struct integrity_iint_cache *iint,
+                           struct file *file, const unsigned char *filename,
+                           struct evm_ima_xattr_data *xattr_value,
+                           int xattr_len, struct ima_field_data *field_data);
+int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file,
+                         const unsigned char *filename,
+                         struct evm_ima_xattr_data *xattr_value, int xattr_len,
+                         struct ima_field_data *field_data);
+int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file,
+                     const unsigned char *filename,
+                     struct evm_ima_xattr_data *xattr_value, int xattr_len,
+                     struct ima_field_data *field_data);
+#endif /* __LINUX_IMA_TEMPLATE_LIB_H */
index c42fb7a..b9e7c13 100644 (file)
@@ -54,25 +54,57 @@ enum evm_ima_xattr_type {
        IMA_XATTR_DIGEST = 0x01,
        EVM_XATTR_HMAC,
        EVM_IMA_XATTR_DIGSIG,
+       IMA_XATTR_DIGEST_NG,
 };
 
 struct evm_ima_xattr_data {
        u8 type;
        u8 digest[SHA1_DIGEST_SIZE];
-}  __attribute__((packed));
+} __packed;
+
+#define IMA_MAX_DIGEST_SIZE    64
+
+struct ima_digest_data {
+       u8 algo;
+       u8 length;
+       union {
+               struct {
+                       u8 unused;
+                       u8 type;
+               } sha1;
+               struct {
+                       u8 type;
+                       u8 algo;
+               } ng;
+               u8 data[2];
+       } xattr;
+       u8 digest[0];
+} __packed;
+
+/*
+ * signature format v2 - for using with asymmetric keys
+ */
+struct signature_v2_hdr {
+       uint8_t type;           /* xattr type */
+       uint8_t version;        /* signature format version */
+       uint8_t hash_algo;      /* Digest algorithm [enum pkey_hash_algo] */
+       uint32_t keyid;         /* IMA key identifier - not X509/PGP specific */
+       uint16_t sig_size;      /* signature size */
+       uint8_t sig[0];         /* signature payload */
+} __packed;
 
 /* integrity data associated with an inode */
 struct integrity_iint_cache {
-       struct rb_node rb_node; /* rooted in integrity_iint_tree */
+       struct rb_node rb_node; /* rooted in integrity_iint_tree */
        struct inode *inode;    /* back pointer to inode in question */
        u64 version;            /* track inode changes */
        unsigned long flags;
-       struct evm_ima_xattr_data ima_xattr;
        enum integrity_status ima_file_status:4;
        enum integrity_status ima_mmap_status:4;
        enum integrity_status ima_bprm_status:4;
        enum integrity_status ima_module_status:4;
        enum integrity_status evm_status:4;
+       struct ima_digest_data *ima_hash;
 };
 
 /* rbtree tree calls to lookup, insert, delete
@@ -89,7 +121,7 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
 #ifdef CONFIG_INTEGRITY_SIGNATURE
 
 int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
-                                       const char *digest, int digestlen);
+                           const char *digest, int digestlen);
 
 #else
 
@@ -105,12 +137,19 @@ static inline int integrity_digsig_verify(const unsigned int id,
 #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
 int asymmetric_verify(struct key *keyring, const char *sig,
                      int siglen, const char *data, int datalen);
+
+int integrity_init_keyring(const unsigned int id);
 #else
 static inline int asymmetric_verify(struct key *keyring, const char *sig,
                                    int siglen, const char *data, int datalen)
 {
        return -EOPNOTSUPP;
 }
+
+static int integrity_init_keyring(const unsigned int id)
+{
+       return 0;
+}
 #endif
 
 #ifdef CONFIG_INTEGRITY_AUDIT
index a90d6d3..a4f3f8c 100644 (file)
@@ -4,6 +4,7 @@
 
 config KEYS
        bool "Enable access key retention support"
+       select ASSOCIATIVE_ARRAY
        help
          This option provides support for retaining authentication tokens and
          access keys in the kernel.
@@ -19,6 +20,34 @@ config KEYS
 
          If you are unsure as to whether this is required, answer N.
 
+config PERSISTENT_KEYRINGS
+       bool "Enable register of persistent per-UID keyrings"
+       depends on KEYS
+       help
+         This option provides a register of persistent per-UID keyrings,
+         primarily aimed at Kerberos key storage.  The keyrings are persistent
+         in the sense that they stay around after all processes of that UID
+         have exited, not that they survive the machine being rebooted.
+
+         A particular keyring may be accessed by either the user whose keyring
+         it is or by a process with administrative privileges.  The active
+         LSMs gets to rule on which admin-level processes get to access the
+         cache.
+
+         Keyrings are created and added into the register upon demand and get
+         removed if they expire (a default timeout is set upon creation).
+
+config BIG_KEYS
+       bool "Large payload keys"
+       depends on KEYS
+       depends on TMPFS
+       help
+         This option provides support for holding large keys within the kernel
+         (for example Kerberos ticket caches).  The data may be stored out to
+         swapspace by tmpfs.
+
+         If you are unsure as to whether this is required, answer N.
+
 config TRUSTED_KEYS
        tristate "TRUSTED KEYS"
        depends on KEYS && TCG_TPM
index 504aaa0..dfb3a7b 100644 (file)
@@ -18,9 +18,11 @@ obj-y := \
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
+obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
 
 #
 # Key types
 #
+obj-$(CONFIG_BIG_KEYS) += big_key.o
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
new file mode 100644 (file)
index 0000000..7f44c32
--- /dev/null
@@ -0,0 +1,207 @@
+/* Large capacity key type
+ *
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <linux/file.h>
+#include <linux/shmem_fs.h>
+#include <linux/err.h>
+#include <keys/user-type.h>
+#include <keys/big_key-type.h>
+
+MODULE_LICENSE("GPL");
+
+/*
+ * If the data is under this limit, there's no point creating a shm file to
+ * hold it as the permanently resident metadata for the shmem fs will be at
+ * least as large as the data.
+ */
+#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
+
+/*
+ * big_key defined keys take an arbitrary string as the description and an
+ * arbitrary blob of data as the payload
+ */
+struct key_type key_type_big_key = {
+       .name                   = "big_key",
+       .def_lookup_type        = KEYRING_SEARCH_LOOKUP_DIRECT,
+       .instantiate            = big_key_instantiate,
+       .match                  = user_match,
+       .revoke                 = big_key_revoke,
+       .destroy                = big_key_destroy,
+       .describe               = big_key_describe,
+       .read                   = big_key_read,
+};
+
+/*
+ * Instantiate a big key
+ */
+int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
+{
+       struct path *path = (struct path *)&key->payload.data2;
+       struct file *file;
+       ssize_t written;
+       size_t datalen = prep->datalen;
+       int ret;
+
+       ret = -EINVAL;
+       if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
+               goto error;
+
+       /* Set an arbitrary quota */
+       ret = key_payload_reserve(key, 16);
+       if (ret < 0)
+               goto error;
+
+       key->type_data.x[1] = datalen;
+
+       if (datalen > BIG_KEY_FILE_THRESHOLD) {
+               /* Create a shmem file to store the data in.  This will permit the data
+                * to be swapped out if needed.
+                *
+                * TODO: Encrypt the stored data with a temporary key.
+                */
+               file = shmem_file_setup("", datalen, 0);
+               if (IS_ERR(file)) {
+                       ret = PTR_ERR(file);
+                       goto err_quota;
+               }
+
+               written = kernel_write(file, prep->data, prep->datalen, 0);
+               if (written != datalen) {
+                       ret = written;
+                       if (written >= 0)
+                               ret = -ENOMEM;
+                       goto err_fput;
+               }
+
+               /* Pin the mount and dentry to the key so that we can open it again
+                * later
+                */
+               *path = file->f_path;
+               path_get(path);
+               fput(file);
+       } else {
+               /* Just store the data in a buffer */
+               void *data = kmalloc(datalen, GFP_KERNEL);
+               if (!data) {
+                       ret = -ENOMEM;
+                       goto err_quota;
+               }
+
+               key->payload.data = memcpy(data, prep->data, prep->datalen);
+       }
+       return 0;
+
+err_fput:
+       fput(file);
+err_quota:
+       key_payload_reserve(key, 0);
+error:
+       return ret;
+}
+
+/*
+ * dispose of the links from a revoked keyring
+ * - called with the key sem write-locked
+ */
+void big_key_revoke(struct key *key)
+{
+       struct path *path = (struct path *)&key->payload.data2;
+
+       /* clear the quota */
+       key_payload_reserve(key, 0);
+       if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD)
+               vfs_truncate(path, 0);
+}
+
+/*
+ * dispose of the data dangling from the corpse of a big_key key
+ */
+void big_key_destroy(struct key *key)
+{
+       if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) {
+               struct path *path = (struct path *)&key->payload.data2;
+               path_put(path);
+               path->mnt = NULL;
+               path->dentry = NULL;
+       } else {
+               kfree(key->payload.data);
+               key->payload.data = NULL;
+       }
+}
+
+/*
+ * describe the big_key key
+ */
+void big_key_describe(const struct key *key, struct seq_file *m)
+{
+       unsigned long datalen = key->type_data.x[1];
+
+       seq_puts(m, key->description);
+
+       if (key_is_instantiated(key))
+               seq_printf(m, ": %lu [%s]",
+                          datalen,
+                          datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
+}
+
+/*
+ * read the key data
+ * - the key's semaphore is read-locked
+ */
+long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
+{
+       unsigned long datalen = key->type_data.x[1];
+       long ret;
+
+       if (!buffer || buflen < datalen)
+               return datalen;
+
+       if (datalen > BIG_KEY_FILE_THRESHOLD) {
+               struct path *path = (struct path *)&key->payload.data2;
+               struct file *file;
+               loff_t pos;
+
+               file = dentry_open(path, O_RDONLY, current_cred());
+               if (IS_ERR(file))
+                       return PTR_ERR(file);
+
+               pos = 0;
+               ret = vfs_read(file, buffer, datalen, &pos);
+               fput(file);
+               if (ret >= 0 && ret != datalen)
+                       ret = -EIO;
+       } else {
+               ret = datalen;
+               if (copy_to_user(buffer, key->payload.data, datalen) != 0)
+                       ret = -EFAULT;
+       }
+
+       return ret;
+}
+
+/*
+ * Module stuff
+ */
+static int __init big_key_init(void)
+{
+       return register_key_type(&key_type_big_key);
+}
+
+static void __exit big_key_cleanup(void)
+{
+       unregister_key_type(&key_type_big_key);
+}
+
+module_init(big_key_init);
+module_exit(big_key_cleanup);
index d65fa7f..bbd32c7 100644 (file)
@@ -138,6 +138,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
        case KEYCTL_INVALIDATE:
                return keyctl_invalidate_key(arg2);
 
+       case KEYCTL_GET_PERSISTENT:
+               return keyctl_get_persistent(arg2, arg3);
+
        default:
                return -EOPNOTSUPP;
        }
index d67c97b..d3222b6 100644 (file)
@@ -131,50 +131,6 @@ void key_gc_keytype(struct key_type *ktype)
 }
 
 /*
- * Garbage collect pointers from a keyring.
- *
- * Not called with any locks held.  The keyring's key struct will not be
- * deallocated under us as only our caller may deallocate it.
- */
-static void key_gc_keyring(struct key *keyring, time_t limit)
-{
-       struct keyring_list *klist;
-       int loop;
-
-       kenter("%x", key_serial(keyring));
-
-       if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
-                             (1 << KEY_FLAG_REVOKED)))
-               goto dont_gc;
-
-       /* scan the keyring looking for dead keys */
-       rcu_read_lock();
-       klist = rcu_dereference(keyring->payload.subscriptions);
-       if (!klist)
-               goto unlock_dont_gc;
-
-       loop = klist->nkeys;
-       smp_rmb();
-       for (loop--; loop >= 0; loop--) {
-               struct key *key = rcu_dereference(klist->keys[loop]);
-               if (key_is_dead(key, limit))
-                       goto do_gc;
-       }
-
-unlock_dont_gc:
-       rcu_read_unlock();
-dont_gc:
-       kleave(" [no gc]");
-       return;
-
-do_gc:
-       rcu_read_unlock();
-
-       keyring_gc(keyring, limit);
-       kleave(" [gc]");
-}
-
-/*
  * Garbage collect a list of unreferenced, detached keys
  */
 static noinline void key_gc_unused_keys(struct list_head *keys)
@@ -392,8 +348,7 @@ found_unreferenced_key:
         */
 found_keyring:
        spin_unlock(&key_serial_lock);
-       kdebug("scan keyring %d", key->serial);
-       key_gc_keyring(key, limit);
+       keyring_gc(key, limit);
        goto maybe_resched;
 
        /* We found a dead key that is still referenced.  Reset its type and
index d4f1468..80b2aac 100644 (file)
@@ -89,42 +89,53 @@ extern struct key_type *key_type_lookup(const char *type);
 extern void key_type_put(struct key_type *ktype);
 
 extern int __key_link_begin(struct key *keyring,
-                           const struct key_type *type,
-                           const char *description,
-                           unsigned long *_prealloc);
+                           const struct keyring_index_key *index_key,
+                           struct assoc_array_edit **_edit);
 extern int __key_link_check_live_key(struct key *keyring, struct key *key);
-extern void __key_link(struct key *keyring, struct key *key,
-                      unsigned long *_prealloc);
+extern void __key_link(struct key *key, struct assoc_array_edit **_edit);
 extern void __key_link_end(struct key *keyring,
-                          struct key_type *type,
-                          unsigned long prealloc);
+                          const struct keyring_index_key *index_key,
+                          struct assoc_array_edit *edit);
 
-extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
-                                     const struct key_type *type,
-                                     const char *description,
-                                     key_perm_t perm);
+extern key_ref_t find_key_to_update(key_ref_t keyring_ref,
+                                   const struct keyring_index_key *index_key);
 
 extern struct key *keyring_search_instkey(struct key *keyring,
                                          key_serial_t target_id);
 
+extern int iterate_over_keyring(const struct key *keyring,
+                               int (*func)(const struct key *key, void *data),
+                               void *data);
+
 typedef int (*key_match_func_t)(const struct key *, const void *);
 
+struct keyring_search_context {
+       struct keyring_index_key index_key;
+       const struct cred       *cred;
+       key_match_func_t        match;
+       const void              *match_data;
+       unsigned                flags;
+#define KEYRING_SEARCH_LOOKUP_TYPE     0x0001  /* [as type->def_lookup_type] */
+#define KEYRING_SEARCH_NO_STATE_CHECK  0x0002  /* Skip state checks */
+#define KEYRING_SEARCH_DO_STATE_CHECK  0x0004  /* Override NO_STATE_CHECK */
+#define KEYRING_SEARCH_NO_UPDATE_TIME  0x0008  /* Don't update times */
+#define KEYRING_SEARCH_NO_CHECK_PERM   0x0010  /* Don't check permissions */
+#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020  /* Give an error on excessive depth */
+
+       int (*iterator)(const void *object, void *iterator_data);
+
+       /* Internal stuff */
+       int                     skipped_ret;
+       bool                    possessed;
+       key_ref_t               result;
+       struct timespec         now;
+};
+
 extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
-                                   const struct cred *cred,
-                                   struct key_type *type,
-                                   const void *description,
-                                   key_match_func_t match,
-                                   bool no_state_check);
-
-extern key_ref_t search_my_process_keyrings(struct key_type *type,
-                                           const void *description,
-                                           key_match_func_t match,
-                                           bool no_state_check,
-                                           const struct cred *cred);
-extern key_ref_t search_process_keyrings(struct key_type *type,
-                                        const void *description,
-                                        key_match_func_t match,
-                                        const struct cred *cred);
+                                   struct keyring_search_context *ctx);
+
+extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
+extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
 
 extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
 
@@ -202,7 +213,7 @@ extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
 /*
  * Determine whether a key is dead.
  */
-static inline bool key_is_dead(struct key *key, time_t limit)
+static inline bool key_is_dead(const struct key *key, time_t limit)
 {
        return
                key->flags & ((1 << KEY_FLAG_DEAD) |
@@ -244,6 +255,15 @@ extern long keyctl_invalidate_key(key_serial_t);
 extern long keyctl_instantiate_key_common(key_serial_t,
                                          const struct iovec *,
                                          unsigned, size_t, key_serial_t);
+#ifdef CONFIG_PERSISTENT_KEYRINGS
+extern long keyctl_get_persistent(uid_t, key_serial_t);
+extern unsigned persistent_keyring_expiry;
+#else
+static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
+{
+       return -EOPNOTSUPP;
+}
+#endif
 
 /*
  * Debugging key validation
index 8fb7c7b..55d110f 100644 (file)
@@ -242,8 +242,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
                }
        }
 
-       desclen = strlen(desc) + 1;
-       quotalen = desclen + type->def_datalen;
+       desclen = strlen(desc);
+       quotalen = desclen + 1 + type->def_datalen;
 
        /* get hold of the key tracking for this user */
        user = key_user_lookup(uid);
@@ -277,7 +277,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
                goto no_memory_2;
 
        if (desc) {
-               key->description = kmemdup(desc, desclen, GFP_KERNEL);
+               key->index_key.desc_len = desclen;
+               key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
                if (!key->description)
                        goto no_memory_3;
        }
@@ -285,7 +286,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
        atomic_set(&key->usage, 1);
        init_rwsem(&key->sem);
        lockdep_set_class(&key->sem, &type->lock_class);
-       key->type = type;
+       key->index_key.type = type;
        key->user = user;
        key->quotalen = quotalen;
        key->datalen = type->def_datalen;
@@ -299,6 +300,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
 
        if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
                key->flags |= 1 << KEY_FLAG_IN_QUOTA;
+       if (flags & KEY_ALLOC_TRUSTED)
+               key->flags |= 1 << KEY_FLAG_TRUSTED;
 
        memset(&key->type_data, 0, sizeof(key->type_data));
 
@@ -408,7 +411,7 @@ static int __key_instantiate_and_link(struct key *key,
                                      struct key_preparsed_payload *prep,
                                      struct key *keyring,
                                      struct key *authkey,
-                                     unsigned long *_prealloc)
+                                     struct assoc_array_edit **_edit)
 {
        int ret, awaken;
 
@@ -435,7 +438,7 @@ static int __key_instantiate_and_link(struct key *key,
 
                        /* and link it into the destination keyring */
                        if (keyring)
-                               __key_link(keyring, key, _prealloc);
+                               __key_link(key, _edit);
 
                        /* disable the authorisation key */
                        if (authkey)
@@ -475,7 +478,7 @@ int key_instantiate_and_link(struct key *key,
                             struct key *authkey)
 {
        struct key_preparsed_payload prep;
-       unsigned long prealloc;
+       struct assoc_array_edit *edit;
        int ret;
 
        memset(&prep, 0, sizeof(prep));
@@ -489,17 +492,15 @@ int key_instantiate_and_link(struct key *key,
        }
 
        if (keyring) {
-               ret = __key_link_begin(keyring, key->type, key->description,
-                                      &prealloc);
+               ret = __key_link_begin(keyring, &key->index_key, &edit);
                if (ret < 0)
                        goto error_free_preparse;
        }
 
-       ret = __key_instantiate_and_link(key, &prep, keyring, authkey,
-                                        &prealloc);
+       ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
 
        if (keyring)
-               __key_link_end(keyring, key->type, prealloc);
+               __key_link_end(keyring, &key->index_key, edit);
 
 error_free_preparse:
        if (key->type->preparse)
@@ -537,7 +538,7 @@ int key_reject_and_link(struct key *key,
                        struct key *keyring,
                        struct key *authkey)
 {
-       unsigned long prealloc;
+       struct assoc_array_edit *edit;
        struct timespec now;
        int ret, awaken, link_ret = 0;
 
@@ -548,8 +549,7 @@ int key_reject_and_link(struct key *key,
        ret = -EBUSY;
 
        if (keyring)
-               link_ret = __key_link_begin(keyring, key->type,
-                                           key->description, &prealloc);
+               link_ret = __key_link_begin(keyring, &key->index_key, &edit);
 
        mutex_lock(&key_construction_mutex);
 
@@ -557,9 +557,10 @@ int key_reject_and_link(struct key *key,
        if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
                /* mark the key as being negatively instantiated */
                atomic_inc(&key->user->nikeys);
+               key->type_data.reject_error = -error;
+               smp_wmb();
                set_bit(KEY_FLAG_NEGATIVE, &key->flags);
                set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
-               key->type_data.reject_error = -error;
                now = current_kernel_time();
                key->expiry = now.tv_sec + timeout;
                key_schedule_gc(key->expiry + key_gc_delay);
@@ -571,7 +572,7 @@ int key_reject_and_link(struct key *key,
 
                /* and link it into the destination keyring */
                if (keyring && link_ret == 0)
-                       __key_link(keyring, key, &prealloc);
+                       __key_link(key, &edit);
 
                /* disable the authorisation key */
                if (authkey)
@@ -581,7 +582,7 @@ int key_reject_and_link(struct key *key,
        mutex_unlock(&key_construction_mutex);
 
        if (keyring)
-               __key_link_end(keyring, key->type, prealloc);
+               __key_link_end(keyring, &key->index_key, edit);
 
        /* wake up anyone waiting for a key to be constructed */
        if (awaken)
@@ -645,7 +646,7 @@ found:
        /* this races with key_put(), but that doesn't matter since key_put()
         * doesn't actually change the key
         */
-       atomic_inc(&key->usage);
+       __key_get(key);
 
 error:
        spin_unlock(&key_serial_lock);
@@ -780,25 +781,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
                               key_perm_t perm,
                               unsigned long flags)
 {
-       unsigned long prealloc;
+       struct keyring_index_key index_key = {
+               .description    = description,
+       };
        struct key_preparsed_payload prep;
+       struct assoc_array_edit *edit;
        const struct cred *cred = current_cred();
-       struct key_type *ktype;
        struct key *keyring, *key = NULL;
        key_ref_t key_ref;
        int ret;
 
        /* look up the key type to see if it's one of the registered kernel
         * types */
-       ktype = key_type_lookup(type);
-       if (IS_ERR(ktype)) {
+       index_key.type = key_type_lookup(type);
+       if (IS_ERR(index_key.type)) {
                key_ref = ERR_PTR(-ENODEV);
                goto error;
        }
 
        key_ref = ERR_PTR(-EINVAL);
-       if (!ktype->match || !ktype->instantiate ||
-           (!description && !ktype->preparse))
+       if (!index_key.type->match || !index_key.type->instantiate ||
+           (!index_key.description && !index_key.type->preparse))
                goto error_put_type;
 
        keyring = key_ref_to_ptr(keyring_ref);
@@ -812,21 +815,28 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
        memset(&prep, 0, sizeof(prep));
        prep.data = payload;
        prep.datalen = plen;
-       prep.quotalen = ktype->def_datalen;
-       if (ktype->preparse) {
-               ret = ktype->preparse(&prep);
+       prep.quotalen = index_key.type->def_datalen;
+       prep.trusted = flags & KEY_ALLOC_TRUSTED;
+       if (index_key.type->preparse) {
+               ret = index_key.type->preparse(&prep);
                if (ret < 0) {
                        key_ref = ERR_PTR(ret);
                        goto error_put_type;
                }
-               if (!description)
-                       description = prep.description;
+               if (!index_key.description)
+                       index_key.description = prep.description;
                key_ref = ERR_PTR(-EINVAL);
-               if (!description)
+               if (!index_key.description)
                        goto error_free_prep;
        }
+       index_key.desc_len = strlen(index_key.description);
+
+       key_ref = ERR_PTR(-EPERM);
+       if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags))
+               goto error_free_prep;
+       flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0;
 
-       ret = __key_link_begin(keyring, ktype, description, &prealloc);
+       ret = __key_link_begin(keyring, &index_key, &edit);
        if (ret < 0) {
                key_ref = ERR_PTR(ret);
                goto error_free_prep;
@@ -844,10 +854,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
         * key of the same type and description in the destination keyring and
         * update that instead if possible
         */
-       if (ktype->update) {
-               key_ref = __keyring_search_one(keyring_ref, ktype, description,
-                                              0);
-               if (!IS_ERR(key_ref))
+       if (index_key.type->update) {
+               key_ref = find_key_to_update(keyring_ref, &index_key);
+               if (key_ref)
                        goto found_matching_key;
        }
 
@@ -856,23 +865,24 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
                perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
                perm |= KEY_USR_VIEW;
 
-               if (ktype->read)
+               if (index_key.type->read)
                        perm |= KEY_POS_READ;
 
-               if (ktype == &key_type_keyring || ktype->update)
+               if (index_key.type == &key_type_keyring ||
+                   index_key.type->update)
                        perm |= KEY_POS_WRITE;
        }
 
        /* allocate a new key */
-       key = key_alloc(ktype, description, cred->fsuid, cred->fsgid, cred,
-                       perm, flags);
+       key = key_alloc(index_key.type, index_key.description,
+                       cred->fsuid, cred->fsgid, cred, perm, flags);
        if (IS_ERR(key)) {
                key_ref = ERR_CAST(key);
                goto error_link_end;
        }
 
        /* instantiate it and link it into the target keyring */
-       ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc);
+       ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &edit);
        if (ret < 0) {
                key_put(key);
                key_ref = ERR_PTR(ret);
@@ -882,12 +892,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
        key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
 
 error_link_end:
-       __key_link_end(keyring, ktype, prealloc);
+       __key_link_end(keyring, &index_key, edit);
 error_free_prep:
-       if (ktype->preparse)
-               ktype->free_preparse(&prep);
+       if (index_key.type->preparse)
+               index_key.type->free_preparse(&prep);
 error_put_type:
-       key_type_put(ktype);
+       key_type_put(index_key.type);
 error:
        return key_ref;
 
@@ -895,7 +905,7 @@ error:
        /* we found a matching key, so we're going to try to update it
         * - we can drop the locks first as we have the key pinned
         */
-       __key_link_end(keyring, ktype, prealloc);
+       __key_link_end(keyring, &index_key, edit);
 
        key_ref = __key_update(key_ref, &prep);
        goto error_free_prep;
index 33cfd27..cee72ce 100644 (file)
@@ -1667,6 +1667,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
        case KEYCTL_INVALIDATE:
                return keyctl_invalidate_key((key_serial_t) arg2);
 
+       case KEYCTL_GET_PERSISTENT:
+               return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
+
        default:
                return -EOPNOTSUPP;
        }
index 6ece7f2..69f0cb7 100644 (file)
@@ -1,6 +1,6 @@
 /* Keyring handling
  *
- * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004-2005, 2008, 2013 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
 #include <linux/seq_file.h>
 #include <linux/err.h>
 #include <keys/keyring-type.h>
+#include <keys/user-type.h>
+#include <linux/assoc_array_priv.h>
 #include <linux/uaccess.h>
 #include "internal.h"
 
-#define rcu_dereference_locked_keyring(keyring)                                \
-       (rcu_dereference_protected(                                     \
-               (keyring)->payload.subscriptions,                       \
-               rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
-
-#define rcu_deref_link_locked(klist, index, keyring)                   \
-       (rcu_dereference_protected(                                     \
-               (klist)->keys[index],                                   \
-               rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
-
-#define MAX_KEYRING_LINKS                                              \
-       min_t(size_t, USHRT_MAX - 1,                                    \
-             ((PAGE_SIZE - sizeof(struct keyring_list)) / sizeof(struct key *)))
-
-#define KEY_LINK_FIXQUOTA 1UL
-
 /*
  * When plumbing the depths of the key tree, this sets a hard limit
  * set on how deep we're willing to go.
  */
 #define KEYRING_NAME_HASH_SIZE (1 << 5)
 
+/*
+ * We mark pointers we pass to the associative array with bit 1 set if
+ * they're keyrings and clear otherwise.
+ */
+#define KEYRING_PTR_SUBTYPE    0x2UL
+
+static inline bool keyring_ptr_is_keyring(const struct assoc_array_ptr *x)
+{
+       return (unsigned long)x & KEYRING_PTR_SUBTYPE;
+}
+static inline struct key *keyring_ptr_to_key(const struct assoc_array_ptr *x)
+{
+       void *object = assoc_array_ptr_to_leaf(x);
+       return (struct key *)((unsigned long)object & ~KEYRING_PTR_SUBTYPE);
+}
+static inline void *keyring_key_to_ptr(struct key *key)
+{
+       if (key->type == &key_type_keyring)
+               return (void *)((unsigned long)key | KEYRING_PTR_SUBTYPE);
+       return key;
+}
+
 static struct list_head        keyring_name_hash[KEYRING_NAME_HASH_SIZE];
 static DEFINE_RWLOCK(keyring_name_lock);
 
@@ -67,7 +75,6 @@ static inline unsigned keyring_hash(const char *desc)
  */
 static int keyring_instantiate(struct key *keyring,
                               struct key_preparsed_payload *prep);
-static int keyring_match(const struct key *keyring, const void *criterion);
 static void keyring_revoke(struct key *keyring);
 static void keyring_destroy(struct key *keyring);
 static void keyring_describe(const struct key *keyring, struct seq_file *m);
@@ -76,9 +83,9 @@ static long keyring_read(const struct key *keyring,
 
 struct key_type key_type_keyring = {
        .name           = "keyring",
-       .def_datalen    = sizeof(struct keyring_list),
+       .def_datalen    = 0,
        .instantiate    = keyring_instantiate,
-       .match          = keyring_match,
+       .match          = user_match,
        .revoke         = keyring_revoke,
        .destroy        = keyring_destroy,
        .describe       = keyring_describe,
@@ -127,6 +134,7 @@ static int keyring_instantiate(struct key *keyring,
 
        ret = -EINVAL;
        if (prep->datalen == 0) {
+               assoc_array_init(&keyring->keys);
                /* make the keyring available by name if it has one */
                keyring_publish_name(keyring);
                ret = 0;
@@ -136,15 +144,226 @@ static int keyring_instantiate(struct key *keyring,
 }
 
 /*
- * Match keyrings on their name
+ * Multiply 64-bits by 32-bits to 96-bits and fold back to 64-bit.  Ideally we'd
+ * fold the carry back too, but that requires inline asm.
+ */
+static u64 mult_64x32_and_fold(u64 x, u32 y)
+{
+       u64 hi = (u64)(u32)(x >> 32) * y;
+       u64 lo = (u64)(u32)(x) * y;
+       return lo + ((u64)(u32)hi << 32) + (u32)(hi >> 32);
+}
+
+/*
+ * Hash a key type and description.
+ */
+static unsigned long hash_key_type_and_desc(const struct keyring_index_key *index_key)
+{
+       const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP;
+       const unsigned long level_mask = ASSOC_ARRAY_LEVEL_STEP_MASK;
+       const char *description = index_key->description;
+       unsigned long hash, type;
+       u32 piece;
+       u64 acc;
+       int n, desc_len = index_key->desc_len;
+
+       type = (unsigned long)index_key->type;
+
+       acc = mult_64x32_and_fold(type, desc_len + 13);
+       acc = mult_64x32_and_fold(acc, 9207);
+       for (;;) {
+               n = desc_len;
+               if (n <= 0)
+                       break;
+               if (n > 4)
+                       n = 4;
+               piece = 0;
+               memcpy(&piece, description, n);
+               description += n;
+               desc_len -= n;
+               acc = mult_64x32_and_fold(acc, piece);
+               acc = mult_64x32_and_fold(acc, 9207);
+       }
+
+       /* Fold the hash down to 32 bits if need be. */
+       hash = acc;
+       if (ASSOC_ARRAY_KEY_CHUNK_SIZE == 32)
+               hash ^= acc >> 32;
+
+       /* Squidge all the keyrings into a separate part of the tree to
+        * ordinary keys by making sure the lowest level segment in the hash is
+        * zero for keyrings and non-zero otherwise.
+        */
+       if (index_key->type != &key_type_keyring && (hash & level_mask) == 0)
+               return hash | (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1;
+       if (index_key->type == &key_type_keyring && (hash & level_mask) != 0)
+               return (hash + (hash << level_shift)) & ~level_mask;
+       return hash;
+}
+
+/*
+ * Build the next index key chunk.
+ *
+ * On 32-bit systems the index key is laid out as:
+ *
+ *     0       4       5       9...
+ *     hash    desclen typeptr desc[]
+ *
+ * On 64-bit systems:
+ *
+ *     0       8       9       17...
+ *     hash    desclen typeptr desc[]
+ *
+ * We return it one word-sized chunk at a time.
  */
-static int keyring_match(const struct key *keyring, const void *description)
+static unsigned long keyring_get_key_chunk(const void *data, int level)
+{
+       const struct keyring_index_key *index_key = data;
+       unsigned long chunk = 0;
+       long offset = 0;
+       int desc_len = index_key->desc_len, n = sizeof(chunk);
+
+       level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
+       switch (level) {
+       case 0:
+               return hash_key_type_and_desc(index_key);
+       case 1:
+               return ((unsigned long)index_key->type << 8) | desc_len;
+       case 2:
+               if (desc_len == 0)
+                       return (u8)((unsigned long)index_key->type >>
+                                   (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
+               n--;
+               offset = 1;
+       default:
+               offset += sizeof(chunk) - 1;
+               offset += (level - 3) * sizeof(chunk);
+               if (offset >= desc_len)
+                       return 0;
+               desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
+               offset += desc_len;
+               do {
+                       chunk <<= 8;
+                       chunk |= ((u8*)index_key->description)[--offset];
+               } while (--desc_len > 0);
+
+               if (level == 2) {
+                       chunk <<= 8;
+                       chunk |= (u8)((unsigned long)index_key->type >>
+                                     (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
+               }
+               return chunk;
+       }
+}
+
+static unsigned long keyring_get_object_key_chunk(const void *object, int level)
+{
+       const struct key *key = keyring_ptr_to_key(object);
+       return keyring_get_key_chunk(&key->index_key, level);
+}
+
+static bool keyring_compare_object(const void *object, const void *data)
 {
-       return keyring->description &&
-               strcmp(keyring->description, description) == 0;
+       const struct keyring_index_key *index_key = data;
+       const struct key *key = keyring_ptr_to_key(object);
+
+       return key->index_key.type == index_key->type &&
+               key->index_key.desc_len == index_key->desc_len &&
+               memcmp(key->index_key.description, index_key->description,
+                      index_key->desc_len) == 0;
 }
 
 /*
+ * Compare the index keys of a pair of objects and determine the bit position
+ * at which they differ - if they differ.
+ */
+static int keyring_diff_objects(const void *_a, const void *_b)
+{
+       const struct key *key_a = keyring_ptr_to_key(_a);
+       const struct key *key_b = keyring_ptr_to_key(_b);
+       const struct keyring_index_key *a = &key_a->index_key;
+       const struct keyring_index_key *b = &key_b->index_key;
+       unsigned long seg_a, seg_b;
+       int level, i;
+
+       level = 0;
+       seg_a = hash_key_type_and_desc(a);
+       seg_b = hash_key_type_and_desc(b);
+       if ((seg_a ^ seg_b) != 0)
+               goto differ;
+
+       /* The number of bits contributed by the hash is controlled by a
+        * constant in the assoc_array headers.  Everything else thereafter we
+        * can deal with as being machine word-size dependent.
+        */
+       level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8;
+       seg_a = a->desc_len;
+       seg_b = b->desc_len;
+       if ((seg_a ^ seg_b) != 0)
+               goto differ;
+
+       /* The next bit may not work on big endian */
+       level++;
+       seg_a = (unsigned long)a->type;
+       seg_b = (unsigned long)b->type;
+       if ((seg_a ^ seg_b) != 0)
+               goto differ;
+
+       level += sizeof(unsigned long);
+       if (a->desc_len == 0)
+               goto same;
+
+       i = 0;
+       if (((unsigned long)a->description | (unsigned long)b->description) &
+           (sizeof(unsigned long) - 1)) {
+               do {
+                       seg_a = *(unsigned long *)(a->description + i);
+                       seg_b = *(unsigned long *)(b->description + i);
+                       if ((seg_a ^ seg_b) != 0)
+                               goto differ_plus_i;
+                       i += sizeof(unsigned long);
+               } while (i < (a->desc_len & (sizeof(unsigned long) - 1)));
+       }
+
+       for (; i < a->desc_len; i++) {
+               seg_a = *(unsigned char *)(a->description + i);
+               seg_b = *(unsigned char *)(b->description + i);
+               if ((seg_a ^ seg_b) != 0)
+                       goto differ_plus_i;
+       }
+
+same:
+       return -1;
+
+differ_plus_i:
+       level += i;
+differ:
+       i = level * 8 + __ffs(seg_a ^ seg_b);
+       return i;
+}
+
+/*
+ * Free an object after stripping the keyring flag off of the pointer.
+ */
+static void keyring_free_object(void *object)
+{
+       key_put(keyring_ptr_to_key(object));
+}
+
+/*
+ * Operations for keyring management by the index-tree routines.
+ */
+static const struct assoc_array_ops keyring_assoc_array_ops = {
+       .get_key_chunk          = keyring_get_key_chunk,
+       .get_object_key_chunk   = keyring_get_object_key_chunk,
+       .compare_object         = keyring_compare_object,
+       .diff_objects           = keyring_diff_objects,
+       .free_object            = keyring_free_object,
+};
+
+/*
  * Clean up a keyring when it is destroyed.  Unpublish its name if it had one
  * and dispose of its data.
  *
@@ -155,9 +374,6 @@ static int keyring_match(const struct key *keyring, const void *description)
  */
 static void keyring_destroy(struct key *keyring)
 {
-       struct keyring_list *klist;
-       int loop;
-
        if (keyring->description) {
                write_lock(&keyring_name_lock);
 
@@ -168,12 +384,7 @@ static void keyring_destroy(struct key *keyring)
                write_unlock(&keyring_name_lock);
        }
 
-       klist = rcu_access_pointer(keyring->payload.subscriptions);
-       if (klist) {
-               for (loop = klist->nkeys - 1; loop >= 0; loop--)
-                       key_put(rcu_access_pointer(klist->keys[loop]));
-               kfree(klist);
-       }
+       assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops);
 }
 
 /*
@@ -181,76 +392,88 @@ static void keyring_destroy(struct key *keyring)
  */
 static void keyring_describe(const struct key *keyring, struct seq_file *m)
 {
-       struct keyring_list *klist;
-
        if (keyring->description)
                seq_puts(m, keyring->description);
        else
                seq_puts(m, "[anon]");
 
        if (key_is_instantiated(keyring)) {
-               rcu_read_lock();
-               klist = rcu_dereference(keyring->payload.subscriptions);
-               if (klist)
-                       seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
+               if (keyring->keys.nr_leaves_on_tree != 0)
+                       seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree);
                else
                        seq_puts(m, ": empty");
-               rcu_read_unlock();
        }
 }
 
+struct keyring_read_iterator_context {
+       size_t                  qty;
+       size_t                  count;
+       key_serial_t __user     *buffer;
+};
+
+static int keyring_read_iterator(const void *object, void *data)
+{
+       struct keyring_read_iterator_context *ctx = data;
+       const struct key *key = keyring_ptr_to_key(object);
+       int ret;
+
+       kenter("{%s,%d},,{%zu/%zu}",
+              key->type->name, key->serial, ctx->count, ctx->qty);
+
+       if (ctx->count >= ctx->qty)
+               return 1;
+
+       ret = put_user(key->serial, ctx->buffer);
+       if (ret < 0)
+               return ret;
+       ctx->buffer++;
+       ctx->count += sizeof(key->serial);
+       return 0;
+}
+
 /*
  * Read a list of key IDs from the keyring's contents in binary form
  *
- * The keyring's semaphore is read-locked by the caller.
+ * The keyring's semaphore is read-locked by the caller.  This prevents someone
+ * from modifying it under us - which could cause us to read key IDs multiple
+ * times.
  */
 static long keyring_read(const struct key *keyring,
                         char __user *buffer, size_t buflen)
 {
-       struct keyring_list *klist;
-       struct key *key;
-       size_t qty, tmp;
-       int loop, ret;
+       struct keyring_read_iterator_context ctx;
+       unsigned long nr_keys;
+       int ret;
 
-       ret = 0;
-       klist = rcu_dereference_locked_keyring(keyring);
-       if (klist) {
-               /* calculate how much data we could return */
-               qty = klist->nkeys * sizeof(key_serial_t);
-
-               if (buffer && buflen > 0) {
-                       if (buflen > qty)
-                               buflen = qty;
-
-                       /* copy the IDs of the subscribed keys into the
-                        * buffer */
-                       ret = -EFAULT;
-
-                       for (loop = 0; loop < klist->nkeys; loop++) {
-                               key = rcu_deref_link_locked(klist, loop,
-                                                           keyring);
-
-                               tmp = sizeof(key_serial_t);
-                               if (tmp > buflen)
-                                       tmp = buflen;
-
-                               if (copy_to_user(buffer,
-                                                &key->serial,
-                                                tmp) != 0)
-                                       goto error;
-
-                               buflen -= tmp;
-                               if (buflen == 0)
-                                       break;
-                               buffer += tmp;
-                       }
-               }
+       kenter("{%d},,%zu", key_serial(keyring), buflen);
+
+       if (buflen & (sizeof(key_serial_t) - 1))
+               return -EINVAL;
+
+       nr_keys = keyring->keys.nr_leaves_on_tree;
+       if (nr_keys == 0)
+               return 0;
 
-               ret = qty;
+       /* Calculate how much data we could return */
+       ctx.qty = nr_keys * sizeof(key_serial_t);
+
+       if (!buffer || !buflen)
+               return ctx.qty;
+
+       if (buflen > ctx.qty)
+               ctx.qty = buflen;
+
+       /* Copy the IDs of the subscribed keys into the buffer */
+       ctx.buffer = (key_serial_t __user *)buffer;
+       ctx.count = 0;
+       ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx);
+       if (ret < 0) {
+               kleave(" = %d [iterate]", ret);
+               return ret;
        }
 
-error:
-       return ret;
+       kleave(" = %zu [ok]", ctx.count);
+       return ctx.count;
 }
 
 /*
@@ -277,227 +500,361 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
 }
 EXPORT_SYMBOL(keyring_alloc);
 
-/**
- * keyring_search_aux - Search a keyring tree for a key matching some criteria
- * @keyring_ref: A pointer to the keyring with possession indicator.
- * @cred: The credentials to use for permissions checks.
- * @type: The type of key to search for.
- * @description: Parameter for @match.
- * @match: Function to rule on whether or not a key is the one required.
- * @no_state_check: Don't check if a matching key is bad
- *
- * Search the supplied keyring tree for a key that matches the criteria given.
- * The root keyring and any linked keyrings must grant Search permission to the
- * caller to be searchable and keys can only be found if they too grant Search
- * to the caller. The possession flag on the root keyring pointer controls use
- * of the possessor bits in permissions checking of the entire tree.  In
- * addition, the LSM gets to forbid keyring searches and key matches.
- *
- * The search is performed as a breadth-then-depth search up to the prescribed
- * limit (KEYRING_SEARCH_MAX_DEPTH).
- *
- * Keys are matched to the type provided and are then filtered by the match
- * function, which is given the description to use in any way it sees fit.  The
- * match function may use any attributes of a key that it wishes to to
- * determine the match.  Normally the match function from the key type would be
- * used.
- *
- * RCU is used to prevent the keyring key lists from disappearing without the
- * need to take lots of locks.
- *
- * Returns a pointer to the found key and increments the key usage count if
- * successful; -EAGAIN if no matching keys were found, or if expired or revoked
- * keys were found; -ENOKEY if only negative keys were found; -ENOTDIR if the
- * specified keyring wasn't a keyring.
- *
- * In the case of a successful return, the possession attribute from
- * @keyring_ref is propagated to the returned key reference.
+/*
+ * Iteration function to consider each key found.
  */
-key_ref_t keyring_search_aux(key_ref_t keyring_ref,
-                            const struct cred *cred,
-                            struct key_type *type,
-                            const void *description,
-                            key_match_func_t match,
-                            bool no_state_check)
+static int keyring_search_iterator(const void *object, void *iterator_data)
 {
-       struct {
-               /* Need a separate keylist pointer for RCU purposes */
-               struct key *keyring;
-               struct keyring_list *keylist;
-               int kix;
-       } stack[KEYRING_SEARCH_MAX_DEPTH];
-
-       struct keyring_list *keylist;
-       struct timespec now;
-       unsigned long possessed, kflags;
-       struct key *keyring, *key;
-       key_ref_t key_ref;
-       long err;
-       int sp, nkeys, kix;
+       struct keyring_search_context *ctx = iterator_data;
+       const struct key *key = keyring_ptr_to_key(object);
+       unsigned long kflags = key->flags;
 
-       keyring = key_ref_to_ptr(keyring_ref);
-       possessed = is_key_possessed(keyring_ref);
-       key_check(keyring);
+       kenter("{%d}", key->serial);
 
-       /* top keyring must have search permission to begin the search */
-       err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
-       if (err < 0) {
-               key_ref = ERR_PTR(err);
-               goto error;
+       /* ignore keys not of this type */
+       if (key->type != ctx->index_key.type) {
+               kleave(" = 0 [!type]");
+               return 0;
        }
 
-       key_ref = ERR_PTR(-ENOTDIR);
-       if (keyring->type != &key_type_keyring)
-               goto error;
+       /* skip invalidated, revoked and expired keys */
+       if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
+               if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
+                             (1 << KEY_FLAG_REVOKED))) {
+                       ctx->result = ERR_PTR(-EKEYREVOKED);
+                       kleave(" = %d [invrev]", ctx->skipped_ret);
+                       goto skipped;
+               }
 
-       rcu_read_lock();
+               if (key->expiry && ctx->now.tv_sec >= key->expiry) {
+                       ctx->result = ERR_PTR(-EKEYEXPIRED);
+                       kleave(" = %d [expire]", ctx->skipped_ret);
+                       goto skipped;
+               }
+       }
 
-       now = current_kernel_time();
-       err = -EAGAIN;
-       sp = 0;
-
-       /* firstly we should check to see if this top-level keyring is what we
-        * are looking for */
-       key_ref = ERR_PTR(-EAGAIN);
-       kflags = keyring->flags;
-       if (keyring->type == type && match(keyring, description)) {
-               key = keyring;
-               if (no_state_check)
-                       goto found;
+       /* keys that don't match */
+       if (!ctx->match(key, ctx->match_data)) {
+               kleave(" = 0 [!match]");
+               return 0;
+       }
 
-               /* check it isn't negative and hasn't expired or been
-                * revoked */
-               if (kflags & (1 << KEY_FLAG_REVOKED))
-                       goto error_2;
-               if (key->expiry && now.tv_sec >= key->expiry)
-                       goto error_2;
-               key_ref = ERR_PTR(key->type_data.reject_error);
-               if (kflags & (1 << KEY_FLAG_NEGATIVE))
-                       goto error_2;
-               goto found;
+       /* key must have search permissions */
+       if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) &&
+           key_task_permission(make_key_ref(key, ctx->possessed),
+                               ctx->cred, KEY_SEARCH) < 0) {
+               ctx->result = ERR_PTR(-EACCES);
+               kleave(" = %d [!perm]", ctx->skipped_ret);
+               goto skipped;
        }
 
-       /* otherwise, the top keyring must not be revoked, expired, or
-        * negatively instantiated if we are to search it */
-       key_ref = ERR_PTR(-EAGAIN);
-       if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
-                     (1 << KEY_FLAG_REVOKED) |
-                     (1 << KEY_FLAG_NEGATIVE)) ||
-           (keyring->expiry && now.tv_sec >= keyring->expiry))
-               goto error_2;
-
-       /* start processing a new keyring */
-descend:
-       kflags = keyring->flags;
-       if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
-                     (1 << KEY_FLAG_REVOKED)))
-               goto not_this_keyring;
+       if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
+               /* we set a different error code if we pass a negative key */
+               if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
+                       smp_rmb();
+                       ctx->result = ERR_PTR(key->type_data.reject_error);
+                       kleave(" = %d [neg]", ctx->skipped_ret);
+                       goto skipped;
+               }
+       }
 
-       keylist = rcu_dereference(keyring->payload.subscriptions);
-       if (!keylist)
-               goto not_this_keyring;
+       /* Found */
+       ctx->result = make_key_ref(key, ctx->possessed);
+       kleave(" = 1 [found]");
+       return 1;
 
-       /* iterate through the keys in this keyring first */
-       nkeys = keylist->nkeys;
-       smp_rmb();
-       for (kix = 0; kix < nkeys; kix++) {
-               key = rcu_dereference(keylist->keys[kix]);
-               kflags = key->flags;
+skipped:
+       return ctx->skipped_ret;
+}
 
-               /* ignore keys not of this type */
-               if (key->type != type)
-                       continue;
+/*
+ * Search inside a keyring for a key.  We can search by walking to it
+ * directly based on its index-key or we can iterate over the entire
+ * tree looking for it, based on the match function.
+ */
+static int search_keyring(struct key *keyring, struct keyring_search_context *ctx)
+{
+       if ((ctx->flags & KEYRING_SEARCH_LOOKUP_TYPE) ==
+           KEYRING_SEARCH_LOOKUP_DIRECT) {
+               const void *object;
+
+               object = assoc_array_find(&keyring->keys,
+                                         &keyring_assoc_array_ops,
+                                         &ctx->index_key);
+               return object ? ctx->iterator(object, ctx) : 0;
+       }
+       return assoc_array_iterate(&keyring->keys, ctx->iterator, ctx);
+}
 
-               /* skip invalidated, revoked and expired keys */
-               if (!no_state_check) {
-                       if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
-                                     (1 << KEY_FLAG_REVOKED)))
-                               continue;
+/*
+ * Search a tree of keyrings that point to other keyrings up to the maximum
+ * depth.
+ */
+static bool search_nested_keyrings(struct key *keyring,
+                                  struct keyring_search_context *ctx)
+{
+       struct {
+               struct key *keyring;
+               struct assoc_array_node *node;
+               int slot;
+       } stack[KEYRING_SEARCH_MAX_DEPTH];
 
-                       if (key->expiry && now.tv_sec >= key->expiry)
-                               continue;
-               }
+       struct assoc_array_shortcut *shortcut;
+       struct assoc_array_node *node;
+       struct assoc_array_ptr *ptr;
+       struct key *key;
+       int sp = 0, slot;
 
-               /* keys that don't match */
-               if (!match(key, description))
-                       continue;
+       kenter("{%d},{%s,%s}",
+              keyring->serial,
+              ctx->index_key.type->name,
+              ctx->index_key.description);
 
-               /* key must have search permissions */
-               if (key_task_permission(make_key_ref(key, possessed),
-                                       cred, KEY_SEARCH) < 0)
-                       continue;
+       if (ctx->index_key.description)
+               ctx->index_key.desc_len = strlen(ctx->index_key.description);
 
-               if (no_state_check)
+       /* Check to see if this top-level keyring is what we are looking for
+        * and whether it is valid or not.
+        */
+       if (ctx->flags & KEYRING_SEARCH_LOOKUP_ITERATE ||
+           keyring_compare_object(keyring, &ctx->index_key)) {
+               ctx->skipped_ret = 2;
+               ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK;
+               switch (ctx->iterator(keyring_key_to_ptr(keyring), ctx)) {
+               case 1:
                        goto found;
-
-               /* we set a different error code if we pass a negative key */
-               if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
-                       err = key->type_data.reject_error;
-                       continue;
+               case 2:
+                       return false;
+               default:
+                       break;
                }
+       }
+
+       ctx->skipped_ret = 0;
+       if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK)
+               ctx->flags &= ~KEYRING_SEARCH_DO_STATE_CHECK;
 
+       /* Start processing a new keyring */
+descend_to_keyring:
+       kdebug("descend to %d", keyring->serial);
+       if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
+                             (1 << KEY_FLAG_REVOKED)))
+               goto not_this_keyring;
+
+       /* Search through the keys in this keyring before its searching its
+        * subtrees.
+        */
+       if (search_keyring(keyring, ctx))
                goto found;
-       }
 
-       /* search through the keyrings nested in this one */
-       kix = 0;
-ascend:
-       nkeys = keylist->nkeys;
-       smp_rmb();
-       for (; kix < nkeys; kix++) {
-               key = rcu_dereference(keylist->keys[kix]);
-               if (key->type != &key_type_keyring)
-                       continue;
+       /* Then manually iterate through the keyrings nested in this one.
+        *
+        * Start from the root node of the index tree.  Because of the way the
+        * hash function has been set up, keyrings cluster on the leftmost
+        * branch of the root node (root slot 0) or in the root node itself.
+        * Non-keyrings avoid the leftmost branch of the root entirely (root
+        * slots 1-15).
+        */
+       ptr = ACCESS_ONCE(keyring->keys.root);
+       if (!ptr)
+               goto not_this_keyring;
 
-               /* recursively search nested keyrings
-                * - only search keyrings for which we have search permission
+       if (assoc_array_ptr_is_shortcut(ptr)) {
+               /* If the root is a shortcut, either the keyring only contains
+                * keyring pointers (everything clusters behind root slot 0) or
+                * doesn't contain any keyring pointers.
                 */
-               if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+               shortcut = assoc_array_ptr_to_shortcut(ptr);
+               smp_read_barrier_depends();
+               if ((shortcut->index_key[0] & ASSOC_ARRAY_FAN_MASK) != 0)
+                       goto not_this_keyring;
+
+               ptr = ACCESS_ONCE(shortcut->next_node);
+               node = assoc_array_ptr_to_node(ptr);
+               goto begin_node;
+       }
+
+       node = assoc_array_ptr_to_node(ptr);
+       smp_read_barrier_depends();
+
+       ptr = node->slots[0];
+       if (!assoc_array_ptr_is_meta(ptr))
+               goto begin_node;
+
+descend_to_node:
+       /* Descend to a more distal node in this keyring's content tree and go
+        * through that.
+        */
+       kdebug("descend");
+       if (assoc_array_ptr_is_shortcut(ptr)) {
+               shortcut = assoc_array_ptr_to_shortcut(ptr);
+               smp_read_barrier_depends();
+               ptr = ACCESS_ONCE(shortcut->next_node);
+               BUG_ON(!assoc_array_ptr_is_node(ptr));
+               node = assoc_array_ptr_to_node(ptr);
+       }
+
+begin_node:
+       kdebug("begin_node");
+       smp_read_barrier_depends();
+       slot = 0;
+ascend_to_node:
+       /* Go through the slots in a node */
+       for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
+               ptr = ACCESS_ONCE(node->slots[slot]);
+
+               if (assoc_array_ptr_is_meta(ptr) && node->back_pointer)
+                       goto descend_to_node;
+
+               if (!keyring_ptr_is_keyring(ptr))
                        continue;
 
-               if (key_task_permission(make_key_ref(key, possessed),
-                                       cred, KEY_SEARCH) < 0)
+               key = keyring_ptr_to_key(ptr);
+
+               if (sp >= KEYRING_SEARCH_MAX_DEPTH) {
+                       if (ctx->flags & KEYRING_SEARCH_DETECT_TOO_DEEP) {
+                               ctx->result = ERR_PTR(-ELOOP);
+                               return false;
+                       }
+                       goto not_this_keyring;
+               }
+
+               /* Search a nested keyring */
+               if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) &&
+                   key_task_permission(make_key_ref(key, ctx->possessed),
+                                       ctx->cred, KEY_SEARCH) < 0)
                        continue;
 
                /* stack the current position */
                stack[sp].keyring = keyring;
-               stack[sp].keylist = keylist;
-               stack[sp].kix = kix;
+               stack[sp].node = node;
+               stack[sp].slot = slot;
                sp++;
 
                /* begin again with the new keyring */
                keyring = key;
-               goto descend;
+               goto descend_to_keyring;
        }
 
-       /* the keyring we're looking at was disqualified or didn't contain a
-        * matching key */
+       /* We've dealt with all the slots in the current node, so now we need
+        * to ascend to the parent and continue processing there.
+        */
+       ptr = ACCESS_ONCE(node->back_pointer);
+       slot = node->parent_slot;
+
+       if (ptr && assoc_array_ptr_is_shortcut(ptr)) {
+               shortcut = assoc_array_ptr_to_shortcut(ptr);
+               smp_read_barrier_depends();
+               ptr = ACCESS_ONCE(shortcut->back_pointer);
+               slot = shortcut->parent_slot;
+       }
+       if (!ptr)
+               goto not_this_keyring;
+       node = assoc_array_ptr_to_node(ptr);
+       smp_read_barrier_depends();
+       slot++;
+
+       /* If we've ascended to the root (zero backpointer), we must have just
+        * finished processing the leftmost branch rather than the root slots -
+        * so there can't be any more keyrings for us to find.
+        */
+       if (node->back_pointer) {
+               kdebug("ascend %d", slot);
+               goto ascend_to_node;
+       }
+
+       /* The keyring we're looking at was disqualified or didn't contain a
+        * matching key.
+        */
 not_this_keyring:
-       if (sp > 0) {
-               /* resume the processing of a keyring higher up in the tree */
-               sp--;
-               keyring = stack[sp].keyring;
-               keylist = stack[sp].keylist;
-               kix = stack[sp].kix + 1;
-               goto ascend;
+       kdebug("not_this_keyring %d", sp);
+       if (sp <= 0) {
+               kleave(" = false");
+               return false;
        }
 
-       key_ref = ERR_PTR(err);
-       goto error_2;
+       /* Resume the processing of a keyring higher up in the tree */
+       sp--;
+       keyring = stack[sp].keyring;
+       node = stack[sp].node;
+       slot = stack[sp].slot + 1;
+       kdebug("ascend to %d [%d]", keyring->serial, slot);
+       goto ascend_to_node;
 
-       /* we found a viable match */
+       /* We found a viable match */
 found:
-       atomic_inc(&key->usage);
-       key->last_used_at = now.tv_sec;
-       keyring->last_used_at = now.tv_sec;
-       while (sp > 0)
-               stack[--sp].keyring->last_used_at = now.tv_sec;
+       key = key_ref_to_ptr(ctx->result);
        key_check(key);
-       key_ref = make_key_ref(key, possessed);
-error_2:
+       if (!(ctx->flags & KEYRING_SEARCH_NO_UPDATE_TIME)) {
+               key->last_used_at = ctx->now.tv_sec;
+               keyring->last_used_at = ctx->now.tv_sec;
+               while (sp > 0)
+                       stack[--sp].keyring->last_used_at = ctx->now.tv_sec;
+       }
+       kleave(" = true");
+       return true;
+}
+
+/**
+ * keyring_search_aux - Search a keyring tree for a key matching some criteria
+ * @keyring_ref: A pointer to the keyring with possession indicator.
+ * @ctx: The keyring search context.
+ *
+ * Search the supplied keyring tree for a key that matches the criteria given.
+ * The root keyring and any linked keyrings must grant Search permission to the
+ * caller to be searchable and keys can only be found if they too grant Search
+ * to the caller. The possession flag on the root keyring pointer controls use
+ * of the possessor bits in permissions checking of the entire tree.  In
+ * addition, the LSM gets to forbid keyring searches and key matches.
+ *
+ * The search is performed as a breadth-then-depth search up to the prescribed
+ * limit (KEYRING_SEARCH_MAX_DEPTH).
+ *
+ * Keys are matched to the type provided and are then filtered by the match
+ * function, which is given the description to use in any way it sees fit.  The
+ * match function may use any attributes of a key that it wishes to to
+ * determine the match.  Normally the match function from the key type would be
+ * used.
+ *
+ * RCU can be used to prevent the keyring key lists from disappearing without
+ * the need to take lots of locks.
+ *
+ * Returns a pointer to the found key and increments the key usage count if
+ * successful; -EAGAIN if no matching keys were found, or if expired or revoked
+ * keys were found; -ENOKEY if only negative keys were found; -ENOTDIR if the
+ * specified keyring wasn't a keyring.
+ *
+ * In the case of a successful return, the possession attribute from
+ * @keyring_ref is propagated to the returned key reference.
+ */
+key_ref_t keyring_search_aux(key_ref_t keyring_ref,
+                            struct keyring_search_context *ctx)
+{
+       struct key *keyring;
+       long err;
+
+       ctx->iterator = keyring_search_iterator;
+       ctx->possessed = is_key_possessed(keyring_ref);
+       ctx->result = ERR_PTR(-EAGAIN);
+
+       keyring = key_ref_to_ptr(keyring_ref);
+       key_check(keyring);
+
+       if (keyring->type != &key_type_keyring)
+               return ERR_PTR(-ENOTDIR);
+
+       if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM)) {
+               err = key_task_permission(keyring_ref, ctx->cred, KEY_SEARCH);
+               if (err < 0)
+                       return ERR_PTR(err);
+       }
+
+       rcu_read_lock();
+       ctx->now = current_kernel_time();
+       if (search_nested_keyrings(keyring, ctx))
+               __key_get(key_ref_to_ptr(ctx->result));
        rcu_read_unlock();
-error:
-       return key_ref;
+       return ctx->result;
 }
 
 /**
@@ -507,77 +864,73 @@ error:
  * @description: The name of the keyring we want to find.
  *
  * As keyring_search_aux() above, but using the current task's credentials and
- * type's default matching function.
+ * type's default matching function and preferred search method.
  */
 key_ref_t keyring_search(key_ref_t keyring,
                         struct key_type *type,
                         const char *description)
 {
-       if (!type->match)
+       struct keyring_search_context ctx = {
+               .index_key.type         = type,
+               .index_key.description  = description,
+               .cred                   = current_cred(),
+               .match                  = type->match,
+               .match_data             = description,
+               .flags                  = (type->def_lookup_type |
+                                          KEYRING_SEARCH_DO_STATE_CHECK),
+       };
+
+       if (!ctx.match)
                return ERR_PTR(-ENOKEY);
 
-       return keyring_search_aux(keyring, current->cred,
-                                 type, description, type->match, false);
+       return keyring_search_aux(keyring, &ctx);
 }
 EXPORT_SYMBOL(keyring_search);
 
 /*
- * Search the given keyring only (no recursion).
+ * Search the given keyring for a key that might be updated.
  *
  * The caller must guarantee that the keyring is a keyring and that the
- * permission is granted to search the keyring as no check is made here.
- *
- * RCU is used to make it unnecessary to lock the keyring key list here.
+ * permission is granted to modify the keyring as no check is made here.  The
+ * caller must also hold a lock on the keyring semaphore.
  *
  * Returns a pointer to the found key with usage count incremented if
- * successful and returns -ENOKEY if not found.  Revoked keys and keys not
- * providing the requested permission are skipped over.
+ * successful and returns NULL if not found.  Revoked and invalidated keys are
+ * skipped over.
  *
  * If successful, the possession indicator is propagated from the keyring ref
  * to the returned key reference.
  */
-key_ref_t __keyring_search_one(key_ref_t keyring_ref,
-                              const struct key_type *ktype,
-                              const char *description,
-                              key_perm_t perm)
+key_ref_t find_key_to_update(key_ref_t keyring_ref,
+                            const struct keyring_index_key *index_key)
 {
-       struct keyring_list *klist;
-       unsigned long possessed;
        struct key *keyring, *key;
-       int nkeys, loop;
+       const void *object;
 
        keyring = key_ref_to_ptr(keyring_ref);
-       possessed = is_key_possessed(keyring_ref);
 
-       rcu_read_lock();
+       kenter("{%d},{%s,%s}",
+              keyring->serial, index_key->type->name, index_key->description);
 
-       klist = rcu_dereference(keyring->payload.subscriptions);
-       if (klist) {
-               nkeys = klist->nkeys;
-               smp_rmb();
-               for (loop = 0; loop < nkeys ; loop++) {
-                       key = rcu_dereference(klist->keys[loop]);
-                       if (key->type == ktype &&
-                           (!key->type->match ||
-                            key->type->match(key, description)) &&
-                           key_permission(make_key_ref(key, possessed),
-                                          perm) == 0 &&
-                           !(key->flags & ((1 << KEY_FLAG_INVALIDATED) |
-                                           (1 << KEY_FLAG_REVOKED)))
-                           )
-                               goto found;
-               }
-       }
+       object = assoc_array_find(&keyring->keys, &keyring_assoc_array_ops,
+                                 index_key);
 
-       rcu_read_unlock();
-       return ERR_PTR(-ENOKEY);
+       if (object)
+               goto found;
+
+       kleave(" = NULL");
+       return NULL;
 
 found:
-       atomic_inc(&key->usage);
-       keyring->last_used_at = key->last_used_at =
-               current_kernel_time().tv_sec;
-       rcu_read_unlock();
-       return make_key_ref(key, possessed);
+       key = keyring_ptr_to_key(object);
+       if (key->flags & ((1 << KEY_FLAG_INVALIDATED) |
+                         (1 << KEY_FLAG_REVOKED))) {
+               kleave(" = NULL [x]");
+               return NULL;
+       }
+       __key_get(key);
+       kleave(" = {%d}", key->serial);
+       return make_key_ref(key, is_key_possessed(keyring_ref));
 }
 
 /*
@@ -640,6 +993,19 @@ out:
        return keyring;
 }
 
+static int keyring_detect_cycle_iterator(const void *object,
+                                        void *iterator_data)
+{
+       struct keyring_search_context *ctx = iterator_data;
+       const struct key *key = keyring_ptr_to_key(object);
+
+       kenter("{%d}", key->serial);
+
+       BUG_ON(key != ctx->match_data);
+       ctx->result = ERR_PTR(-EDEADLK);
+       return 1;
+}
+
 /*
  * See if a cycle will will be created by inserting acyclic tree B in acyclic
  * tree A at the topmost level (ie: as a direct child of A).
@@ -649,116 +1015,39 @@ out:
  */
 static int keyring_detect_cycle(struct key *A, struct key *B)
 {
-       struct {
-               struct keyring_list *keylist;
-               int kix;
-       } stack[KEYRING_SEARCH_MAX_DEPTH];
-
-       struct keyring_list *keylist;
-       struct key *subtree, *key;
-       int sp, nkeys, kix, ret;
+       struct keyring_search_context ctx = {
+               .index_key      = A->index_key,
+               .match_data     = A,
+               .iterator       = keyring_detect_cycle_iterator,
+               .flags          = (KEYRING_SEARCH_LOOKUP_DIRECT |
+                                  KEYRING_SEARCH_NO_STATE_CHECK |
+                                  KEYRING_SEARCH_NO_UPDATE_TIME |
+                                  KEYRING_SEARCH_NO_CHECK_PERM |
+                                  KEYRING_SEARCH_DETECT_TOO_DEEP),
+       };
 
        rcu_read_lock();
-
-       ret = -EDEADLK;
-       if (A == B)
-               goto cycle_detected;
-
-       subtree = B;
-       sp = 0;
-
-       /* start processing a new keyring */
-descend:
-       if (test_bit(KEY_FLAG_REVOKED, &subtree->flags))
-               goto not_this_keyring;
-
-       keylist = rcu_dereference(subtree->payload.subscriptions);
-       if (!keylist)
-               goto not_this_keyring;
-       kix = 0;
-
-ascend:
-       /* iterate through the remaining keys in this keyring */
-       nkeys = keylist->nkeys;
-       smp_rmb();
-       for (; kix < nkeys; kix++) {
-               key = rcu_dereference(keylist->keys[kix]);
-
-               if (key == A)
-                       goto cycle_detected;
-
-               /* recursively check nested keyrings */
-               if (key->type == &key_type_keyring) {
-                       if (sp >= KEYRING_SEARCH_MAX_DEPTH)
-                               goto too_deep;
-
-                       /* stack the current position */
-                       stack[sp].keylist = keylist;
-                       stack[sp].kix = kix;
-                       sp++;
-
-                       /* begin again with the new keyring */
-                       subtree = key;
-                       goto descend;
-               }
-       }
-
-       /* the keyring we're looking at was disqualified or didn't contain a
-        * matching key */
-not_this_keyring:
-       if (sp > 0) {
-               /* resume the checking of a keyring higher up in the tree */
-               sp--;
-               keylist = stack[sp].keylist;
-               kix = stack[sp].kix + 1;
-               goto ascend;
-       }
-
-       ret = 0; /* no cycles detected */
-
-error:
+       search_nested_keyrings(B, &ctx);
        rcu_read_unlock();
-       return ret;
-
-too_deep:
-       ret = -ELOOP;
-       goto error;
-
-cycle_detected:
-       ret = -EDEADLK;
-       goto error;
-}
-
-/*
- * Dispose of a keyring list after the RCU grace period, freeing the unlinked
- * key
- */
-static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
-{
-       struct keyring_list *klist =
-               container_of(rcu, struct keyring_list, rcu);
-
-       if (klist->delkey != USHRT_MAX)
-               key_put(rcu_access_pointer(klist->keys[klist->delkey]));
-       kfree(klist);
+       return PTR_ERR(ctx.result) == -EAGAIN ? 0 : PTR_ERR(ctx.result);
 }
 
 /*
  * Preallocate memory so that a key can be linked into to a keyring.
  */
-int __key_link_begin(struct key *keyring, const struct key_type *type,
-                    const char *description, unsigned long *_prealloc)
+int __key_link_begin(struct key *keyring,
+                    const struct keyring_index_key *index_key,
+                    struct assoc_array_edit **_edit)
        __acquires(&keyring->sem)
        __acquires(&keyring_serialise_link_sem)
 {
-       struct keyring_list *klist, *nklist;
-       unsigned long prealloc;
-       unsigned max;
-       time_t lowest_lru;
-       size_t size;
-       int loop, lru, ret;
+       struct assoc_array_edit *edit;
+       int ret;
+
+       kenter("%d,%s,%s,",
+              keyring->serial, index_key->type->name, index_key->description);
 
-       kenter("%d,%s,%s,", key_serial(keyring), type->name, description);
+       BUG_ON(index_key->desc_len == 0);
 
        if (keyring->type != &key_type_keyring)
                return -ENOTDIR;
@@ -771,100 +1060,39 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
 
        /* serialise link/link calls to prevent parallel calls causing a cycle
         * when linking two keyring in opposite orders */
-       if (type == &key_type_keyring)
+       if (index_key->type == &key_type_keyring)
                down_write(&keyring_serialise_link_sem);
 
-       klist = rcu_dereference_locked_keyring(keyring);
-
-       /* see if there's a matching key we can displace */
-       lru = -1;
-       if (klist && klist->nkeys > 0) {
-               lowest_lru = TIME_T_MAX;
-               for (loop = klist->nkeys - 1; loop >= 0; loop--) {
-                       struct key *key = rcu_deref_link_locked(klist, loop,
-                                                               keyring);
-                       if (key->type == type &&
-                           strcmp(key->description, description) == 0) {
-                               /* Found a match - we'll replace the link with
-                                * one to the new key.  We record the slot
-                                * position.
-                                */
-                               klist->delkey = loop;
-                               prealloc = 0;
-                               goto done;
-                       }
-                       if (key->last_used_at < lowest_lru) {
-                               lowest_lru = key->last_used_at;
-                               lru = loop;
-                       }
-               }
-       }
-
-       /* If the keyring is full then do an LRU discard */
-       if (klist &&
-           klist->nkeys == klist->maxkeys &&
-           klist->maxkeys >= MAX_KEYRING_LINKS) {
-               kdebug("LRU discard %d\n", lru);
-               klist->delkey = lru;
-               prealloc = 0;
-               goto done;
-       }
-
-       /* check that we aren't going to overrun the user's quota */
-       ret = key_payload_reserve(keyring,
-                                 keyring->datalen + KEYQUOTA_LINK_BYTES);
-       if (ret < 0)
+       /* Create an edit script that will insert/replace the key in the
+        * keyring tree.
+        */
+       edit = assoc_array_insert(&keyring->keys,
+                                 &keyring_assoc_array_ops,
+                                 index_key,
+                                 NULL);
+       if (IS_ERR(edit)) {
+               ret = PTR_ERR(edit);
                goto error_sem;
+       }
 
-       if (klist && klist->nkeys < klist->maxkeys) {
-               /* there's sufficient slack space to append directly */
-               klist->delkey = klist->nkeys;
-               prealloc = KEY_LINK_FIXQUOTA;
-       } else {
-               /* grow the key list */
-               max = 4;
-               if (klist) {
-                       max += klist->maxkeys;
-                       if (max > MAX_KEYRING_LINKS)
-                               max = MAX_KEYRING_LINKS;
-                       BUG_ON(max <= klist->maxkeys);
-               }
-
-               size = sizeof(*klist) + sizeof(struct key *) * max;
-
-               ret = -ENOMEM;
-               nklist = kmalloc(size, GFP_KERNEL);
-               if (!nklist)
-                       goto error_quota;
-
-               nklist->maxkeys = max;
-               if (klist) {
-                       memcpy(nklist->keys, klist->keys,
-                              sizeof(struct key *) * klist->nkeys);
-                       nklist->delkey = klist->nkeys;
-                       nklist->nkeys = klist->nkeys + 1;
-                       klist->delkey = USHRT_MAX;
-               } else {
-                       nklist->nkeys = 1;
-                       nklist->delkey = 0;
-               }
-
-               /* add the key into the new space */
-               RCU_INIT_POINTER(nklist->keys[nklist->delkey], NULL);
-               prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA;
+       /* If we're not replacing a link in-place then we're going to need some
+        * extra quota.
+        */
+       if (!edit->dead_leaf) {
+               ret = key_payload_reserve(keyring,
+                                         keyring->datalen + KEYQUOTA_LINK_BYTES);
+               if (ret < 0)
+                       goto error_cancel;
        }
 
-done:
-       *_prealloc = prealloc;
+       *_edit = edit;
        kleave(" = 0");
        return 0;
 
-error_quota:
-       /* undo the quota changes */
-       key_payload_reserve(keyring,
-                           keyring->datalen - KEYQUOTA_LINK_BYTES);
+error_cancel:
+       assoc_array_cancel_edit(edit);
 error_sem:
-       if (type == &key_type_keyring)
+       if (index_key->type == &key_type_keyring)
                up_write(&keyring_serialise_link_sem);
 error_krsem:
        up_write(&keyring->sem);
@@ -895,60 +1123,12 @@ int __key_link_check_live_key(struct key *keyring, struct key *key)
  * holds at most one link to any given key of a particular type+description
  * combination.
  */
-void __key_link(struct key *keyring, struct key *key,
-               unsigned long *_prealloc)
+void __key_link(struct key *key, struct assoc_array_edit **_edit)
 {
-       struct keyring_list *klist, *nklist;
-       struct key *discard;
-
-       nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA);
-       *_prealloc = 0;
-
-       kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
-
-       klist = rcu_dereference_locked_keyring(keyring);
-
-       atomic_inc(&key->usage);
-       keyring->last_used_at = key->last_used_at =
-               current_kernel_time().tv_sec;
-
-       /* there's a matching key we can displace or an empty slot in a newly
-        * allocated list we can fill */
-       if (nklist) {
-               kdebug("reissue %hu/%hu/%hu",
-                      nklist->delkey, nklist->nkeys, nklist->maxkeys);
-
-               RCU_INIT_POINTER(nklist->keys[nklist->delkey], key);
-
-               rcu_assign_pointer(keyring->payload.subscriptions, nklist);
-
-               /* dispose of the old keyring list and, if there was one, the
-                * displaced key */
-               if (klist) {
-                       kdebug("dispose %hu/%hu/%hu",
-                              klist->delkey, klist->nkeys, klist->maxkeys);
-                       call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
-               }
-       } else if (klist->delkey < klist->nkeys) {
-               kdebug("replace %hu/%hu/%hu",
-                      klist->delkey, klist->nkeys, klist->maxkeys);
-
-               discard = rcu_dereference_protected(
-                       klist->keys[klist->delkey],
-                       rwsem_is_locked(&keyring->sem));
-               rcu_assign_pointer(klist->keys[klist->delkey], key);
-               /* The garbage collector will take care of RCU
-                * synchronisation */
-               key_put(discard);
-       } else {
-               /* there's sufficient slack space to append directly */
-               kdebug("append %hu/%hu/%hu",
-                      klist->delkey, klist->nkeys, klist->maxkeys);
-
-               RCU_INIT_POINTER(klist->keys[klist->delkey], key);
-               smp_wmb();
-               klist->nkeys++;
-       }
+       __key_get(key);
+       assoc_array_insert_set_object(*_edit, keyring_key_to_ptr(key));
+       assoc_array_apply_edit(*_edit);
+       *_edit = NULL;
 }
 
 /*
@@ -956,24 +1136,22 @@ void __key_link(struct key *keyring, struct key *key,
  *
  * Must be called with __key_link_begin() having being called.
  */
-void __key_link_end(struct key *keyring, struct key_type *type,
-                   unsigned long prealloc)
+void __key_link_end(struct key *keyring,
+                   const struct keyring_index_key *index_key,
+                   struct assoc_array_edit *edit)
        __releases(&keyring->sem)
        __releases(&keyring_serialise_link_sem)
 {
-       BUG_ON(type == NULL);
-       BUG_ON(type->name == NULL);
-       kenter("%d,%s,%lx", keyring->serial, type->name, prealloc);
+       BUG_ON(index_key->type == NULL);
+       kenter("%d,%s,", keyring->serial, index_key->type->name);
 
-       if (type == &key_type_keyring)
+       if (index_key->type == &key_type_keyring)
                up_write(&keyring_serialise_link_sem);
 
-       if (prealloc) {
-               if (prealloc & KEY_LINK_FIXQUOTA)
-                       key_payload_reserve(keyring,
-                                           keyring->datalen -
-                                           KEYQUOTA_LINK_BYTES);
-               kfree((struct keyring_list *)(prealloc & ~KEY_LINK_FIXQUOTA));
+       if (edit && !edit->dead_leaf) {
+               key_payload_reserve(keyring,
+                                   keyring->datalen - KEYQUOTA_LINK_BYTES);
+               assoc_array_cancel_edit(edit);
        }
        up_write(&keyring->sem);
 }
@@ -1000,20 +1178,28 @@ void __key_link_end(struct key *keyring, struct key_type *type,
  */
 int key_link(struct key *keyring, struct key *key)
 {
-       unsigned long prealloc;
+       struct assoc_array_edit *edit;
        int ret;
 
+       kenter("{%d,%d}", keyring->serial, atomic_read(&keyring->usage));
+
        key_check(keyring);
        key_check(key);
 
-       ret = __key_link_begin(keyring, key->type, key->description, &prealloc);
+       if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) &&
+           !test_bit(KEY_FLAG_TRUSTED, &key->flags))
+               return -EPERM;
+
+       ret = __key_link_begin(keyring, &key->index_key, &edit);
        if (ret == 0) {
+               kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
                ret = __key_link_check_live_key(keyring, key);
                if (ret == 0)
-                       __key_link(keyring, key, &prealloc);
-               __key_link_end(keyring, key->type, prealloc);
+                       __key_link(key, &edit);
+               __key_link_end(keyring, &key->index_key, edit);
        }
 
+       kleave(" = %d {%d,%d}", ret, keyring->serial, atomic_read(&keyring->usage));
        return ret;
 }
 EXPORT_SYMBOL(key_link);
@@ -1037,90 +1223,37 @@ EXPORT_SYMBOL(key_link);
  */
 int key_unlink(struct key *keyring, struct key *key)
 {
-       struct keyring_list *klist, *nklist;
-       int loop, ret;
+       struct assoc_array_edit *edit;
+       int ret;
 
        key_check(keyring);
        key_check(key);
 
-       ret = -ENOTDIR;
        if (keyring->type != &key_type_keyring)
-               goto error;
+               return -ENOTDIR;
 
        down_write(&keyring->sem);
 
-       klist = rcu_dereference_locked_keyring(keyring);
-       if (klist) {
-               /* search the keyring for the key */
-               for (loop = 0; loop < klist->nkeys; loop++)
-                       if (rcu_access_pointer(klist->keys[loop]) == key)
-                               goto key_is_present;
+       edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops,
+                                 &key->index_key);
+       if (IS_ERR(edit)) {
+               ret = PTR_ERR(edit);
+               goto error;
        }
-
-       up_write(&keyring->sem);
        ret = -ENOENT;
-       goto error;
-
-key_is_present:
-       /* we need to copy the key list for RCU purposes */
-       nklist = kmalloc(sizeof(*klist) +
-                        sizeof(struct key *) * klist->maxkeys,
-                        GFP_KERNEL);
-       if (!nklist)
-               goto nomem;
-       nklist->maxkeys = klist->maxkeys;
-       nklist->nkeys = klist->nkeys - 1;
-
-       if (loop > 0)
-               memcpy(&nklist->keys[0],
-                      &klist->keys[0],
-                      loop * sizeof(struct key *));
-
-       if (loop < nklist->nkeys)
-               memcpy(&nklist->keys[loop],
-                      &klist->keys[loop + 1],
-                      (nklist->nkeys - loop) * sizeof(struct key *));
-
-       /* adjust the user's quota */
-       key_payload_reserve(keyring,
-                           keyring->datalen - KEYQUOTA_LINK_BYTES);
-
-       rcu_assign_pointer(keyring->payload.subscriptions, nklist);
-
-       up_write(&keyring->sem);
-
-       /* schedule for later cleanup */
-       klist->delkey = loop;
-       call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
+       if (edit == NULL)
+               goto error;
 
+       assoc_array_apply_edit(edit);
+       key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES);
        ret = 0;
 
 error:
-       return ret;
-nomem:
-       ret = -ENOMEM;
        up_write(&keyring->sem);
-       goto error;
+       return ret;
 }
 EXPORT_SYMBOL(key_unlink);
 
-/*
- * Dispose of a keyring list after the RCU grace period, releasing the keys it
- * links to.
- */
-static void keyring_clear_rcu_disposal(struct rcu_head *rcu)
-{
-       struct keyring_list *klist;
-       int loop;
-
-       klist = container_of(rcu, struct keyring_list, rcu);
-
-       for (loop = klist->nkeys - 1; loop >= 0; loop--)
-               key_put(rcu_access_pointer(klist->keys[loop]));
-
-       kfree(klist);
-}
-
 /**
  * keyring_clear - Clear a keyring
  * @keyring: The keyring to clear.
@@ -1131,33 +1264,25 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu)
  */
 int keyring_clear(struct key *keyring)
 {
-       struct keyring_list *klist;
+       struct assoc_array_edit *edit;
        int ret;
 
-       ret = -ENOTDIR;
-       if (keyring->type == &key_type_keyring) {
-               /* detach the pointer block with the locks held */
-               down_write(&keyring->sem);
-
-               klist = rcu_dereference_locked_keyring(keyring);
-               if (klist) {
-                       /* adjust the quota */
-                       key_payload_reserve(keyring,
-                                           sizeof(struct keyring_list));
-
-                       rcu_assign_pointer(keyring->payload.subscriptions,
-                                          NULL);
-               }
-
-               up_write(&keyring->sem);
+       if (keyring->type != &key_type_keyring)
+               return -ENOTDIR;
 
-               /* free the keys after the locks have been dropped */
-               if (klist)
-                       call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
+       down_write(&keyring->sem);
 
+       edit = assoc_array_clear(&keyring->keys, &keyring_assoc_array_ops);
+       if (IS_ERR(edit)) {
+               ret = PTR_ERR(edit);
+       } else {
+               if (edit)
+                       assoc_array_apply_edit(edit);
+               key_payload_reserve(keyring, 0);
                ret = 0;
        }
 
+       up_write(&keyring->sem);
        return ret;
 }
 EXPORT_SYMBOL(keyring_clear);
@@ -1169,111 +1294,68 @@ EXPORT_SYMBOL(keyring_clear);
  */
 static void keyring_revoke(struct key *keyring)
 {
-       struct keyring_list *klist;
+       struct assoc_array_edit *edit;
+
+       edit = assoc_array_clear(&keyring->keys, &keyring_assoc_array_ops);
+       if (!IS_ERR(edit)) {
+               if (edit)
+                       assoc_array_apply_edit(edit);
+               key_payload_reserve(keyring, 0);
+       }
+}
+
+static bool keyring_gc_select_iterator(void *object, void *iterator_data)
+{
+       struct key *key = keyring_ptr_to_key(object);
+       time_t *limit = iterator_data;
 
-       klist = rcu_dereference_locked_keyring(keyring);
+       if (key_is_dead(key, *limit))
+               return false;
+       key_get(key);
+       return true;
+}
 
-       /* adjust the quota */
-       key_payload_reserve(keyring, 0);
+static int keyring_gc_check_iterator(const void *object, void *iterator_data)
+{
+       const struct key *key = keyring_ptr_to_key(object);
+       time_t *limit = iterator_data;
 
-       if (klist) {
-               rcu_assign_pointer(keyring->payload.subscriptions, NULL);
-               call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
-       }
+       key_check(key);
+       return key_is_dead(key, *limit);
 }
 
 /*
- * Collect garbage from the contents of a keyring, replacing the old list with
- * a new one with the pointers all shuffled down.
+ * Garbage collect pointers from a keyring.
  *
- * Dead keys are classed as oned that are flagged as being dead or are revoked,
- * expired or negative keys that were revoked or expired before the specified
- * limit.
+ * Not called with any locks held.  The keyring's key struct will not be
+ * deallocated under us as only our caller may deallocate it.
  */
 void keyring_gc(struct key *keyring, time_t limit)
 {
-       struct keyring_list *klist, *new;
-       struct key *key;
-       int loop, keep, max;
-
-       kenter("{%x,%s}", key_serial(keyring), keyring->description);
-
-       down_write(&keyring->sem);
-
-       klist = rcu_dereference_locked_keyring(keyring);
-       if (!klist)
-               goto no_klist;
-
-       /* work out how many subscriptions we're keeping */
-       keep = 0;
-       for (loop = klist->nkeys - 1; loop >= 0; loop--)
-               if (!key_is_dead(rcu_deref_link_locked(klist, loop, keyring),
-                                limit))
-                       keep++;
-
-       if (keep == klist->nkeys)
-               goto just_return;
-
-       /* allocate a new keyring payload */
-       max = roundup(keep, 4);
-       new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *),
-                     GFP_KERNEL);
-       if (!new)
-               goto nomem;
-       new->maxkeys = max;
-       new->nkeys = 0;
-       new->delkey = 0;
-
-       /* install the live keys
-        * - must take care as expired keys may be updated back to life
-        */
-       keep = 0;
-       for (loop = klist->nkeys - 1; loop >= 0; loop--) {
-               key = rcu_deref_link_locked(klist, loop, keyring);
-               if (!key_is_dead(key, limit)) {
-                       if (keep >= max)
-                               goto discard_new;
-                       RCU_INIT_POINTER(new->keys[keep++], key_get(key));
-               }
-       }
-       new->nkeys = keep;
-
-       /* adjust the quota */
-       key_payload_reserve(keyring,
-                           sizeof(struct keyring_list) +
-                           KEYQUOTA_LINK_BYTES * keep);
+       int result;
 
-       if (keep == 0) {
-               rcu_assign_pointer(keyring->payload.subscriptions, NULL);
-               kfree(new);
-       } else {
-               rcu_assign_pointer(keyring->payload.subscriptions, new);
-       }
+       kenter("%x{%s}", keyring->serial, keyring->description ?: "");
 
-       up_write(&keyring->sem);
+       if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
+                             (1 << KEY_FLAG_REVOKED)))
+               goto dont_gc;
 
-       call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
-       kleave(" [yes]");
-       return;
-
-discard_new:
-       new->nkeys = keep;
-       keyring_clear_rcu_disposal(&new->rcu);
-       up_write(&keyring->sem);
-       kleave(" [discard]");
-       return;
-
-just_return:
-       up_write(&keyring->sem);
-       kleave(" [no dead]");
-       return;
+       /* scan the keyring looking for dead keys */
+       rcu_read_lock();
+       result = assoc_array_iterate(&keyring->keys,
+                                    keyring_gc_check_iterator, &limit);
+       rcu_read_unlock();
+       if (result == true)
+               goto do_gc;
 
-no_klist:
-       up_write(&keyring->sem);
-       kleave(" [no_klist]");
+dont_gc:
+       kleave(" [no gc]");
        return;
 
-nomem:
+do_gc:
+       down_write(&keyring->sem);
+       assoc_array_gc(&keyring->keys, &keyring_assoc_array_ops,
+                      keyring_gc_select_iterator, &limit);
        up_write(&keyring->sem);
-       kleave(" [oom]");
+       kleave(" [gc]");
 }
diff --git a/security/keys/persistent.c b/security/keys/persistent.c
new file mode 100644 (file)
index 0000000..0ad3ee2
--- /dev/null
@@ -0,0 +1,167 @@
+/* General persistent per-UID keyrings register
+ *
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/user_namespace.h>
+#include "internal.h"
+
+unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */
+
+/*
+ * Create the persistent keyring register for the current user namespace.
+ *
+ * Called with the namespace's sem locked for writing.
+ */
+static int key_create_persistent_register(struct user_namespace *ns)
+{
+       struct key *reg = keyring_alloc(".persistent_register",
+                                       KUIDT_INIT(0), KGIDT_INIT(0),
+                                       current_cred(),
+                                       ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                                        KEY_USR_VIEW | KEY_USR_READ),
+                                       KEY_ALLOC_NOT_IN_QUOTA, NULL);
+       if (IS_ERR(reg))
+               return PTR_ERR(reg);
+
+       ns->persistent_keyring_register = reg;
+       return 0;
+}
+
+/*
+ * Create the persistent keyring for the specified user.
+ *
+ * Called with the namespace's sem locked for writing.
+ */
+static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
+                                      struct keyring_index_key *index_key)
+{
+       struct key *persistent;
+       key_ref_t reg_ref, persistent_ref;
+
+       if (!ns->persistent_keyring_register) {
+               long err = key_create_persistent_register(ns);
+               if (err < 0)
+                       return ERR_PTR(err);
+       } else {
+               reg_ref = make_key_ref(ns->persistent_keyring_register, true);
+               persistent_ref = find_key_to_update(reg_ref, index_key);
+               if (persistent_ref)
+                       return persistent_ref;
+       }
+
+       persistent = keyring_alloc(index_key->description,
+                                  uid, INVALID_GID, current_cred(),
+                                  ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                                   KEY_USR_VIEW | KEY_USR_READ),
+                                  KEY_ALLOC_NOT_IN_QUOTA,
+                                  ns->persistent_keyring_register);
+       if (IS_ERR(persistent))
+               return ERR_CAST(persistent);
+
+       return make_key_ref(persistent, true);
+}
+
+/*
+ * Get the persistent keyring for a specific UID and link it to the nominated
+ * keyring.
+ */
+static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
+                              key_ref_t dest_ref)
+{
+       struct keyring_index_key index_key;
+       struct key *persistent;
+       key_ref_t reg_ref, persistent_ref;
+       char buf[32];
+       long ret;
+
+       /* Look in the register if it exists */
+       index_key.type = &key_type_keyring;
+       index_key.description = buf;
+       index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
+
+       if (ns->persistent_keyring_register) {
+               reg_ref = make_key_ref(ns->persistent_keyring_register, true);
+               down_read(&ns->persistent_keyring_register_sem);
+               persistent_ref = find_key_to_update(reg_ref, &index_key);
+               up_read(&ns->persistent_keyring_register_sem);
+
+               if (persistent_ref)
+                       goto found;
+       }
+
+       /* It wasn't in the register, so we'll need to create it.  We might
+        * also need to create the register.
+        */
+       down_write(&ns->persistent_keyring_register_sem);
+       persistent_ref = key_create_persistent(ns, uid, &index_key);
+       up_write(&ns->persistent_keyring_register_sem);
+       if (!IS_ERR(persistent_ref))
+               goto found;
+
+       return PTR_ERR(persistent_ref);
+
+found:
+       ret = key_task_permission(persistent_ref, current_cred(), KEY_LINK);
+       if (ret == 0) {
+               persistent = key_ref_to_ptr(persistent_ref);
+               ret = key_link(key_ref_to_ptr(dest_ref), persistent);
+               if (ret == 0) {
+                       key_set_timeout(persistent, persistent_keyring_expiry);
+                       ret = persistent->serial;               
+               }
+       }
+
+       key_ref_put(persistent_ref);
+       return ret;
+}
+
+/*
+ * Get the persistent keyring for a specific UID and link it to the nominated
+ * keyring.
+ */
+long keyctl_get_persistent(uid_t _uid, key_serial_t destid)
+{
+       struct user_namespace *ns = current_user_ns();
+       key_ref_t dest_ref;
+       kuid_t uid;
+       long ret;
+
+       /* -1 indicates the current user */
+       if (_uid == (uid_t)-1) {
+               uid = current_uid();
+       } else {
+               uid = make_kuid(ns, _uid);
+               if (!uid_valid(uid))
+                       return -EINVAL;
+
+               /* You can only see your own persistent cache if you're not
+                * sufficiently privileged.
+                */
+               if (!uid_eq(uid, current_uid()) &&
+                   !uid_eq(uid, current_euid()) &&
+                   !ns_capable(ns, CAP_SETUID))
+                       return -EPERM;
+       }
+
+       /* There must be a destination keyring */
+       dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_WRITE);
+       if (IS_ERR(dest_ref))
+               return PTR_ERR(dest_ref);
+       if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) {
+               ret = -ENOTDIR;
+               goto out_put_dest;
+       }
+
+       ret = key_get_persistent(ns, uid, dest_ref);
+
+out_put_dest:
+       key_ref_put(dest_ref);
+       return ret;
+}
index 217b685..88e9a46 100644 (file)
@@ -182,7 +182,6 @@ static void proc_keys_stop(struct seq_file *p, void *v)
 
 static int proc_keys_show(struct seq_file *m, void *v)
 {
-       const struct cred *cred = current_cred();
        struct rb_node *_p = v;
        struct key *key = rb_entry(_p, struct key, serial_node);
        struct timespec now;
@@ -191,15 +190,23 @@ static int proc_keys_show(struct seq_file *m, void *v)
        char xbuf[12];
        int rc;
 
+       struct keyring_search_context ctx = {
+               .index_key.type         = key->type,
+               .index_key.description  = key->description,
+               .cred                   = current_cred(),
+               .match                  = lookup_user_key_possessed,
+               .match_data             = key,
+               .flags                  = (KEYRING_SEARCH_NO_STATE_CHECK |
+                                          KEYRING_SEARCH_LOOKUP_DIRECT),
+       };
+
        key_ref = make_key_ref(key, 0);
 
        /* determine if the key is possessed by this process (a test we can
         * skip if the key does not indicate the possessor can view it
         */
        if (key->perm & KEY_POS_VIEW) {
-               skey_ref = search_my_process_keyrings(key->type, key,
-                                                     lookup_user_key_possessed,
-                                                     true, cred);
+               skey_ref = search_my_process_keyrings(&ctx);
                if (!IS_ERR(skey_ref)) {
                        key_ref_put(skey_ref);
                        key_ref = make_key_ref(key, 1);
@@ -211,7 +218,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
         * - the caller holds a spinlock, and thus the RCU read lock, making our
         *   access to __current_cred() safe
         */
-       rc = key_task_permission(key_ref, cred, KEY_VIEW);
+       rc = key_task_permission(key_ref, ctx.cred, KEY_VIEW);
        if (rc < 0)
                return 0;
 
index 42defae..0cf8a13 100644 (file)
@@ -235,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
                if (IS_ERR(keyring))
                        return PTR_ERR(keyring);
        } else {
-               atomic_inc(&keyring->usage);
+               __key_get(keyring);
        }
 
        /* install the keyring */
@@ -319,11 +319,7 @@ void key_fsgid_changed(struct task_struct *tsk)
  * In the case of a successful return, the possession attribute is set on the
  * returned key reference.
  */
-key_ref_t search_my_process_keyrings(struct key_type *type,
-                                    const void *description,
-                                    key_match_func_t match,
-                                    bool no_state_check,
-                                    const struct cred *cred)
+key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
 {
        key_ref_t key_ref, ret, err;
 
@@ -339,10 +335,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
        err = ERR_PTR(-EAGAIN);
 
        /* search the thread keyring first */
-       if (cred->thread_keyring) {
+       if (ctx->cred->thread_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(cred->thread_keyring, 1),
-                       cred, type, description, match, no_state_check);
+                       make_key_ref(ctx->cred->thread_keyring, 1), ctx);
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -358,10 +353,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
        }
 
        /* search the process keyring second */
-       if (cred->process_keyring) {
+       if (ctx->cred->process_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(cred->process_keyring, 1),
-                       cred, type, description, match, no_state_check);
+                       make_key_ref(ctx->cred->process_keyring, 1), ctx);
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -379,11 +373,11 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
        }
 
        /* search the session keyring */
-       if (cred->session_keyring) {
+       if (ctx->cred->session_keyring) {
                rcu_read_lock();
                key_ref = keyring_search_aux(
-                       make_key_ref(rcu_dereference(cred->session_keyring), 1),
-                       cred, type, description, match, no_state_check);
+                       make_key_ref(rcu_dereference(ctx->cred->session_keyring), 1),
+                       ctx);
                rcu_read_unlock();
 
                if (!IS_ERR(key_ref))
@@ -402,10 +396,10 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
                }
        }
        /* or search the user-session keyring */
-       else if (cred->user->session_keyring) {
+       else if (ctx->cred->user->session_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(cred->user->session_keyring, 1),
-                       cred, type, description, match, no_state_check);
+                       make_key_ref(ctx->cred->user->session_keyring, 1),
+                       ctx);
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -437,18 +431,14 @@ found:
  *
  * Return same as search_my_process_keyrings().
  */
-key_ref_t search_process_keyrings(struct key_type *type,
-                                 const void *description,
-                                 key_match_func_t match,
-                                 const struct cred *cred)
+key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
 {
        struct request_key_auth *rka;
        key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;
 
        might_sleep();
 
-       key_ref = search_my_process_keyrings(type, description, match,
-                                            false, cred);
+       key_ref = search_my_process_keyrings(ctx);
        if (!IS_ERR(key_ref))
                goto found;
        err = key_ref;
@@ -457,18 +447,21 @@ key_ref_t search_process_keyrings(struct key_type *type,
         * search the keyrings of the process mentioned there
         * - we don't permit access to request_key auth keys via this method
         */
-       if (cred->request_key_auth &&
-           cred == current_cred() &&
-           type != &key_type_request_key_auth
+       if (ctx->cred->request_key_auth &&
+           ctx->cred == current_cred() &&
+           ctx->index_key.type != &key_type_request_key_auth
            ) {
+               const struct cred *cred = ctx->cred;
+
                /* defend against the auth key being revoked */
                down_read(&cred->request_key_auth->sem);
 
-               if (key_validate(cred->request_key_auth) == 0) {
-                       rka = cred->request_key_auth->payload.data;
+               if (key_validate(ctx->cred->request_key_auth) == 0) {
+                       rka = ctx->cred->request_key_auth->payload.data;
 
-                       key_ref = search_process_keyrings(type, description,
-                                                         match, rka->cred);
+                       ctx->cred = rka->cred;
+                       key_ref = search_process_keyrings(ctx);
+                       ctx->cred = cred;
 
                        up_read(&cred->request_key_auth->sem);
 
@@ -522,19 +515,23 @@ int lookup_user_key_possessed(const struct key *key, const void *target)
 key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
                          key_perm_t perm)
 {
+       struct keyring_search_context ctx = {
+               .match  = lookup_user_key_possessed,
+               .flags  = (KEYRING_SEARCH_NO_STATE_CHECK |
+                          KEYRING_SEARCH_LOOKUP_DIRECT),
+       };
        struct request_key_auth *rka;
-       const struct cred *cred;
        struct key *key;
        key_ref_t key_ref, skey_ref;
        int ret;
 
 try_again:
-       cred = get_current_cred();
+       ctx.cred = get_current_cred();
        key_ref = ERR_PTR(-ENOKEY);
 
        switch (id) {
        case KEY_SPEC_THREAD_KEYRING:
-               if (!cred->thread_keyring) {
+               if (!ctx.cred->thread_keyring) {
                        if (!(lflags & KEY_LOOKUP_CREATE))
                                goto error;
 
@@ -546,13 +543,13 @@ try_again:
                        goto reget_creds;
                }
 
-               key = cred->thread_keyring;
-               atomic_inc(&key->usage);
+               key = ctx.cred->thread_keyring;
+               __key_get(key);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_PROCESS_KEYRING:
-               if (!cred->process_keyring) {
+               if (!ctx.cred->process_keyring) {
                        if (!(lflags & KEY_LOOKUP_CREATE))
                                goto error;
 
@@ -564,13 +561,13 @@ try_again:
                        goto reget_creds;
                }
 
-               key = cred->process_keyring;
-               atomic_inc(&key->usage);
+               key = ctx.cred->process_keyring;
+               __key_get(key);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_SESSION_KEYRING:
-               if (!cred->session_keyring) {
+               if (!ctx.cred->session_keyring) {
                        /* always install a session keyring upon access if one
                         * doesn't exist yet */
                        ret = install_user_keyrings();
@@ -580,13 +577,13 @@ try_again:
                                ret = join_session_keyring(NULL);
                        else
                                ret = install_session_keyring(
-                                       cred->user->session_keyring);
+                                       ctx.cred->user->session_keyring);
 
                        if (ret < 0)
                                goto error;
                        goto reget_creds;
-               } else if (cred->session_keyring ==
-                          cred->user->session_keyring &&
+               } else if (ctx.cred->session_keyring ==
+                          ctx.cred->user->session_keyring &&
                           lflags & KEY_LOOKUP_CREATE) {
                        ret = join_session_keyring(NULL);
                        if (ret < 0)
@@ -595,33 +592,33 @@ try_again:
                }
 
                rcu_read_lock();
-               key = rcu_dereference(cred->session_keyring);
-               atomic_inc(&key->usage);
+               key = rcu_dereference(ctx.cred->session_keyring);
+               __key_get(key);
                rcu_read_unlock();
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_USER_KEYRING:
-               if (!cred->user->uid_keyring) {
+               if (!ctx.cred->user->uid_keyring) {
                        ret = install_user_keyrings();
                        if (ret < 0)
                                goto error;
                }
 
-               key = cred->user->uid_keyring;
-               atomic_inc(&key->usage);
+               key = ctx.cred->user->uid_keyring;
+               __key_get(key);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_USER_SESSION_KEYRING:
-               if (!cred->user->session_keyring) {
+               if (!ctx.cred->user->session_keyring) {
                        ret = install_user_keyrings();
                        if (ret < 0)
                                goto error;
                }
 
-               key = cred->user->session_keyring;
-               atomic_inc(&key->usage);
+               key = ctx.cred->user->session_keyring;
+               __key_get(key);
                key_ref = make_key_ref(key, 1);
                break;
 
@@ -631,29 +628,29 @@ try_again:
                goto error;
 
        case KEY_SPEC_REQKEY_AUTH_KEY:
-               key = cred->request_key_auth;
+               key = ctx.cred->request_key_auth;
                if (!key)
                        goto error;
 
-               atomic_inc(&key->usage);
+               __key_get(key);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_REQUESTOR_KEYRING:
-               if (!cred->request_key_auth)
+               if (!ctx.cred->request_key_auth)
                        goto error;
 
-               down_read(&cred->request_key_auth->sem);
+               down_read(&ctx.cred->request_key_auth->sem);
                if (test_bit(KEY_FLAG_REVOKED,
-                            &cred->request_key_auth->flags)) {
+                            &ctx.cred->request_key_auth->flags)) {
                        key_ref = ERR_PTR(-EKEYREVOKED);
                        key = NULL;
                } else {
-                       rka = cred->request_key_auth->payload.data;
+                       rka = ctx.cred->request_key_auth->payload.data;
                        key = rka->dest_keyring;
-                       atomic_inc(&key->usage);
+                       __key_get(key);
                }
-               up_read(&cred->request_key_auth->sem);
+               up_read(&ctx.cred->request_key_auth->sem);
                if (!key)
                        goto error;
                key_ref = make_key_ref(key, 1);
@@ -673,9 +670,13 @@ try_again:
                key_ref = make_key_ref(key, 0);
 
                /* check to see if we possess the key */
-               skey_ref = search_process_keyrings(key->type, key,
-                                                  lookup_user_key_possessed,
-                                                  cred);
+               ctx.index_key.type              = key->type;
+               ctx.index_key.description       = key->description;
+               ctx.index_key.desc_len          = strlen(key->description);
+               ctx.match_data                  = key;
+               kdebug("check possessed");
+               skey_ref = search_process_keyrings(&ctx);
+               kdebug("possessed=%p", skey_ref);
 
                if (!IS_ERR(skey_ref)) {
                        key_put(key);
@@ -715,14 +716,14 @@ try_again:
                goto invalid_key;
 
        /* check the permissions */
-       ret = key_task_permission(key_ref, cred, perm);
+       ret = key_task_permission(key_ref, ctx.cred, perm);
        if (ret < 0)
                goto invalid_key;
 
        key->last_used_at = current_kernel_time().tv_sec;
 
 error:
-       put_cred(cred);
+       put_cred(ctx.cred);
        return key_ref;
 
 invalid_key:
@@ -733,7 +734,7 @@ invalid_key:
        /* if we attempted to install a keyring, then it may have caused new
         * creds to be installed */
 reget_creds:
-       put_cred(cred);
+       put_cred(ctx.cred);
        goto try_again;
 }
 
@@ -856,3 +857,13 @@ void key_change_session_keyring(struct callback_head *twork)
 
        commit_creds(new);
 }
+
+/*
+ * Make sure that root's user and user-session keyrings exist.
+ */
+static int __init init_root_keyring(void)
+{
+       return install_user_keyrings();
+}
+
+late_initcall(init_root_keyring);
index c411f9b..3814119 100644 (file)
@@ -345,33 +345,34 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)
  * May return a key that's already under construction instead if there was a
  * race between two thread calling request_key().
  */
-static int construct_alloc_key(struct key_type *type,
-                              const char *description,
+static int construct_alloc_key(struct keyring_search_context *ctx,
                               struct key *dest_keyring,
                               unsigned long flags,
                               struct key_user *user,
                               struct key **_key)
 {
-       const struct cred *cred = current_cred();
-       unsigned long prealloc;
+       struct assoc_array_edit *edit;
        struct key *key;
        key_perm_t perm;
        key_ref_t key_ref;
        int ret;
 
-       kenter("%s,%s,,,", type->name, description);
+       kenter("%s,%s,,,",
+              ctx->index_key.type->name, ctx->index_key.description);
 
        *_key = NULL;
        mutex_lock(&user->cons_lock);
 
        perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
        perm |= KEY_USR_VIEW;
-       if (type->read)
+       if (ctx->index_key.type->read)
                perm |= KEY_POS_READ;
-       if (type == &key_type_keyring || type->update)
+       if (ctx->index_key.type == &key_type_keyring ||
+           ctx->index_key.type->update)
                perm |= KEY_POS_WRITE;
 
-       key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
+       key = key_alloc(ctx->index_key.type, ctx->index_key.description,
+                       ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,
                        perm, flags);
        if (IS_ERR(key))
                goto alloc_failed;
@@ -379,8 +380,7 @@ static int construct_alloc_key(struct key_type *type,
        set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
 
        if (dest_keyring) {
-               ret = __key_link_begin(dest_keyring, type, description,
-                                      &prealloc);
+               ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit);
                if (ret < 0)
                        goto link_prealloc_failed;
        }
@@ -390,16 +390,16 @@ static int construct_alloc_key(struct key_type *type,
         * waited for locks */
        mutex_lock(&key_construction_mutex);
 
-       key_ref = search_process_keyrings(type, description, type->match, cred);
+       key_ref = search_process_keyrings(ctx);
        if (!IS_ERR(key_ref))
                goto key_already_present;
 
        if (dest_keyring)
-               __key_link(dest_keyring, key, &prealloc);
+               __key_link(key, &edit);
 
        mutex_unlock(&key_construction_mutex);
        if (dest_keyring)
-               __key_link_end(dest_keyring, type, prealloc);
+               __key_link_end(dest_keyring, &ctx->index_key, edit);
        mutex_unlock(&user->cons_lock);
        *_key = key;
        kleave(" = 0 [%d]", key_serial(key));
@@ -414,8 +414,8 @@ key_already_present:
        if (dest_keyring) {
                ret = __key_link_check_live_key(dest_keyring, key);
                if (ret == 0)
-                       __key_link(dest_keyring, key, &prealloc);
-               __key_link_end(dest_keyring, type, prealloc);
+                       __key_link(key, &edit);
+               __key_link_end(dest_keyring, &ctx->index_key, edit);
                if (ret < 0)
                        goto link_check_failed;
        }
@@ -444,8 +444,7 @@ alloc_failed:
 /*
  * Commence key construction.
  */
-static struct key *construct_key_and_link(struct key_type *type,
-                                         const char *description,
+static struct key *construct_key_and_link(struct keyring_search_context *ctx,
                                          const char *callout_info,
                                          size_t callout_len,
                                          void *aux,
@@ -464,8 +463,7 @@ static struct key *construct_key_and_link(struct key_type *type,
 
        construct_get_dest_keyring(&dest_keyring);
 
-       ret = construct_alloc_key(type, description, dest_keyring, flags, user,
-                                 &key);
+       ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key);
        key_user_put(user);
 
        if (ret == 0) {
@@ -529,17 +527,24 @@ struct key *request_key_and_link(struct key_type *type,
                                 struct key *dest_keyring,
                                 unsigned long flags)
 {
-       const struct cred *cred = current_cred();
+       struct keyring_search_context ctx = {
+               .index_key.type         = type,
+               .index_key.description  = description,
+               .cred                   = current_cred(),
+               .match                  = type->match,
+               .match_data             = description,
+               .flags                  = KEYRING_SEARCH_LOOKUP_DIRECT,
+       };
        struct key *key;
        key_ref_t key_ref;
        int ret;
 
        kenter("%s,%s,%p,%zu,%p,%p,%lx",
-              type->name, description, callout_info, callout_len, aux,
-              dest_keyring, flags);
+              ctx.index_key.type->name, ctx.index_key.description,
+              callout_info, callout_len, aux, dest_keyring, flags);
 
        /* search all the process keyrings for a key */
-       key_ref = search_process_keyrings(type, description, type->match, cred);
+       key_ref = search_process_keyrings(&ctx);
 
        if (!IS_ERR(key_ref)) {
                key = key_ref_to_ptr(key_ref);
@@ -562,9 +567,8 @@ struct key *request_key_and_link(struct key_type *type,
                if (!callout_info)
                        goto error;
 
-               key = construct_key_and_link(type, description, callout_info,
-                                            callout_len, aux, dest_keyring,
-                                            flags);
+               key = construct_key_and_link(&ctx, callout_info, callout_len,
+                                            aux, dest_keyring, flags);
        }
 
 error:
@@ -592,8 +596,10 @@ int wait_for_key_construction(struct key *key, bool intr)
                          intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
        if (ret < 0)
                return ret;
-       if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+       if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
+               smp_rmb();
                return key->type_data.reject_error;
+       }
        return key_validate(key);
 }
 EXPORT_SYMBOL(wait_for_key_construction);
index 85730d5..7495a93 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <asm/uaccess.h>
 #include "internal.h"
+#include <keys/user-type.h>
 
 static int request_key_auth_instantiate(struct key *,
                                        struct key_preparsed_payload *);
@@ -222,32 +223,26 @@ error_alloc:
 }
 
 /*
- * See if an authorisation key is associated with a particular key.
- */
-static int key_get_instantiation_authkey_match(const struct key *key,
-                                              const void *_id)
-{
-       struct request_key_auth *rka = key->payload.data;
-       key_serial_t id = (key_serial_t)(unsigned long) _id;
-
-       return rka->target_key->serial == id;
-}
-
-/*
  * Search the current process's keyrings for the authorisation key for
  * instantiation of a key.
  */
 struct key *key_get_instantiation_authkey(key_serial_t target_id)
 {
-       const struct cred *cred = current_cred();
+       char description[16];
+       struct keyring_search_context ctx = {
+               .index_key.type         = &key_type_request_key_auth,
+               .index_key.description  = description,
+               .cred                   = current_cred(),
+               .match                  = user_match,
+               .match_data             = description,
+               .flags                  = KEYRING_SEARCH_LOOKUP_DIRECT,
+       };
        struct key *authkey;
        key_ref_t authkey_ref;
 
-       authkey_ref = search_process_keyrings(
-               &key_type_request_key_auth,
-               (void *) (unsigned long) target_id,
-               key_get_instantiation_authkey_match,
-               cred);
+       sprintf(description, "%x", target_id);
+
+       authkey_ref = search_process_keyrings(&ctx);
 
        if (IS_ERR(authkey_ref)) {
                authkey = ERR_CAST(authkey_ref);
index ee32d18..8c0af08 100644 (file)
@@ -61,5 +61,16 @@ ctl_table key_sysctls[] = {
                .extra1 = (void *) &zero,
                .extra2 = (void *) &max,
        },
+#ifdef CONFIG_PERSISTENT_KEYRINGS
+       {
+               .procname = "persistent_keyring_expiry",
+               .data = &persistent_keyring_expiry,
+               .maxlen = sizeof(unsigned),
+               .mode = 0644,
+               .proc_handler = proc_dointvec_minmax,
+               .extra1 = (void *) &zero,
+               .extra2 = (void *) &max,
+       },
+#endif
        { }
 };
index 55dc889..faa2cae 100644 (file)
@@ -25,14 +25,15 @@ static int logon_vet_description(const char *desc);
  * arbitrary blob of data as the payload
  */
 struct key_type key_type_user = {
-       .name           = "user",
-       .instantiate    = user_instantiate,
-       .update         = user_update,
-       .match          = user_match,
-       .revoke         = user_revoke,
-       .destroy        = user_destroy,
-       .describe       = user_describe,
-       .read           = user_read,
+       .name                   = "user",
+       .def_lookup_type        = KEYRING_SEARCH_LOOKUP_DIRECT,
+       .instantiate            = user_instantiate,
+       .update                 = user_update,
+       .match                  = user_match,
+       .revoke                 = user_revoke,
+       .destroy                = user_destroy,
+       .describe               = user_describe,
+       .read                   = user_read,
 };
 
 EXPORT_SYMBOL_GPL(key_type_user);
@@ -45,6 +46,7 @@ EXPORT_SYMBOL_GPL(key_type_user);
  */
 struct key_type key_type_logon = {
        .name                   = "logon",
+       .def_lookup_type        = KEYRING_SEARCH_LOOKUP_DIRECT,
        .instantiate            = user_instantiate,
        .update                 = user_update,
        .match                  = user_match,
index 234bc2a..9a62045 100644 (file)
@@ -397,7 +397,8 @@ void common_lsm_audit(struct common_audit_data *a,
        if (a == NULL)
                return;
        /* we use GFP_ATOMIC so we won't sleep */
-       ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC);
+       ab = audit_log_start(current->audit_context, GFP_ATOMIC | __GFP_NOWARN,
+                            AUDIT_AVC);
 
        if (ab == NULL)
                return;
index 4dc31f4..15b6928 100644 (file)
@@ -1340,22 +1340,17 @@ int security_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
        return security_ops->xfrm_policy_delete_security(ctx);
 }
 
-int security_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
+int security_xfrm_state_alloc(struct xfrm_state *x,
+                             struct xfrm_user_sec_ctx *sec_ctx)
 {
-       return security_ops->xfrm_state_alloc_security(x, sec_ctx, 0);
+       return security_ops->xfrm_state_alloc(x, sec_ctx);
 }
 EXPORT_SYMBOL(security_xfrm_state_alloc);
 
 int security_xfrm_state_alloc_acquire(struct xfrm_state *x,
                                      struct xfrm_sec_ctx *polsec, u32 secid)
 {
-       if (!polsec)
-               return 0;
-       /*
-        * We want the context to be taken from secid which is usually
-        * from the sock.
-        */
-       return security_ops->xfrm_state_alloc_security(x, NULL, secid);
+       return security_ops->xfrm_state_alloc_acquire(x, polsec, secid);
 }
 
 int security_xfrm_state_delete(struct xfrm_state *x)
index c540795..794c3ca 100644 (file)
@@ -95,7 +95,9 @@
 #include "audit.h"
 #include "avc_ss.h"
 
-#define NUM_SEL_MNT_OPTS 5
+#define SB_TYPE_FMT "%s%s%s"
+#define SB_SUBTYPE(sb) (sb->s_subtype && sb->s_subtype[0])
+#define SB_TYPE_ARGS(sb) sb->s_type->name, SB_SUBTYPE(sb) ? "." : "", SB_SUBTYPE(sb) ? sb->s_subtype : ""
 
 extern struct security_operations *security_ops;
 
@@ -139,12 +141,28 @@ static struct kmem_cache *sel_inode_cache;
  * This function checks the SECMARK reference counter to see if any SECMARK
  * targets are currently configured, if the reference counter is greater than
  * zero SECMARK is considered to be enabled.  Returns true (1) if SECMARK is
- * enabled, false (0) if SECMARK is disabled.
+ * enabled, false (0) if SECMARK is disabled.  If the always_check_network
+ * policy capability is enabled, SECMARK is always considered enabled.
  *
  */
 static int selinux_secmark_enabled(void)
 {
-       return (atomic_read(&selinux_secmark_refcount) > 0);
+       return (selinux_policycap_alwaysnetwork || atomic_read(&selinux_secmark_refcount));
+}
+
+/**
+ * selinux_peerlbl_enabled - Check to see if peer labeling is currently enabled
+ *
+ * Description:
+ * This function checks if NetLabel or labeled IPSEC is enabled.  Returns true
+ * (1) if any are enabled or false (0) if neither are enabled.  If the
+ * always_check_network policy capability is enabled, peer labeling
+ * is always considered enabled.
+ *
+ */
+static int selinux_peerlbl_enabled(void)
+{
+       return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled());
 }
 
 /*
@@ -309,8 +327,11 @@ enum {
        Opt_defcontext = 3,
        Opt_rootcontext = 4,
        Opt_labelsupport = 5,
+       Opt_nextmntopt = 6,
 };
 
+#define NUM_SEL_MNT_OPTS       (Opt_nextmntopt - 1)
+
 static const match_table_t tokens = {
        {Opt_context, CONTEXT_STR "%s"},
        {Opt_fscontext, FSCONTEXT_STR "%s"},
@@ -355,6 +376,29 @@ static int may_context_mount_inode_relabel(u32 sid,
        return rc;
 }
 
+static int selinux_is_sblabel_mnt(struct super_block *sb)
+{
+       struct superblock_security_struct *sbsec = sb->s_security;
+
+       if (sbsec->behavior == SECURITY_FS_USE_XATTR ||
+           sbsec->behavior == SECURITY_FS_USE_TRANS ||
+           sbsec->behavior == SECURITY_FS_USE_TASK)
+               return 1;
+
+       /* Special handling for sysfs. Is genfs but also has setxattr handler*/
+       if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0)
+               return 1;
+
+       /*
+        * Special handling for rootfs. Is genfs but supports
+        * setting SELinux context on in-core inodes.
+        */
+       if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0)
+               return 1;
+
+       return 0;
+}
+
 static int sb_finish_set_opts(struct super_block *sb)
 {
        struct superblock_security_struct *sbsec = sb->s_security;
@@ -369,8 +413,8 @@ static int sb_finish_set_opts(struct super_block *sb)
                   the first boot of the SELinux kernel before we have
                   assigned xattr values to the filesystem. */
                if (!root_inode->i_op->getxattr) {
-                       printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
-                              "xattr support\n", sb->s_id, sb->s_type->name);
+                       printk(KERN_WARNING "SELinux: (dev %s, type "SB_TYPE_FMT") has no "
+                              "xattr support\n", sb->s_id, SB_TYPE_ARGS(sb));
                        rc = -EOPNOTSUPP;
                        goto out;
                }
@@ -378,35 +422,27 @@ static int sb_finish_set_opts(struct super_block *sb)
                if (rc < 0 && rc != -ENODATA) {
                        if (rc == -EOPNOTSUPP)
                                printk(KERN_WARNING "SELinux: (dev %s, type "
-                                      "%s) has no security xattr handler\n",
-                                      sb->s_id, sb->s_type->name);
+                                      SB_TYPE_FMT") has no security xattr handler\n",
+                                      sb->s_id, SB_TYPE_ARGS(sb));
                        else
                                printk(KERN_WARNING "SELinux: (dev %s, type "
-                                      "%s) getxattr errno %d\n", sb->s_id,
-                                      sb->s_type->name, -rc);
+                                      SB_TYPE_FMT") getxattr errno %d\n", sb->s_id,
+                                      SB_TYPE_ARGS(sb), -rc);
                        goto out;
                }
        }
 
-       sbsec->flags |= (SE_SBINITIALIZED | SE_SBLABELSUPP);
-
        if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
-               printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
-                      sb->s_id, sb->s_type->name);
+               printk(KERN_ERR "SELinux: initialized (dev %s, type "SB_TYPE_FMT"), unknown behavior\n",
+                      sb->s_id, SB_TYPE_ARGS(sb));
        else
-               printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
-                      sb->s_id, sb->s_type->name,
+               printk(KERN_DEBUG "SELinux: initialized (dev %s, type "SB_TYPE_FMT"), %s\n",
+                      sb->s_id, SB_TYPE_ARGS(sb),
                       labeling_behaviors[sbsec->behavior-1]);
 
-       if (sbsec->behavior == SECURITY_FS_USE_GENFS ||
-           sbsec->behavior == SECURITY_FS_USE_MNTPOINT ||
-           sbsec->behavior == SECURITY_FS_USE_NONE ||
-           sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
-               sbsec->flags &= ~SE_SBLABELSUPP;
-
-       /* Special handling for sysfs. Is genfs but also has setxattr handler*/
-       if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0)
-               sbsec->flags |= SE_SBLABELSUPP;
+       sbsec->flags |= SE_SBINITIALIZED;
+       if (selinux_is_sblabel_mnt(sb))
+               sbsec->flags |= SBLABEL_MNT;
 
        /* Initialize the root inode. */
        rc = inode_doinit_with_dentry(root_inode, root);
@@ -460,15 +496,18 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
        if (!ss_initialized)
                return -EINVAL;
 
+       /* make sure we always check enough bits to cover the mask */
+       BUILD_BUG_ON(SE_MNTMASK >= (1 << NUM_SEL_MNT_OPTS));
+
        tmp = sbsec->flags & SE_MNTMASK;
        /* count the number of mount options for this sb */
-       for (i = 0; i < 8; i++) {
+       for (i = 0; i < NUM_SEL_MNT_OPTS; i++) {
                if (tmp & 0x01)
                        opts->num_mnt_opts++;
                tmp >>= 1;
        }
        /* Check if the Label support flag is set */
-       if (sbsec->flags & SE_SBLABELSUPP)
+       if (sbsec->flags & SBLABEL_MNT)
                opts->num_mnt_opts++;
 
        opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
@@ -515,9 +554,9 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
                opts->mnt_opts[i] = context;
                opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
        }
-       if (sbsec->flags & SE_SBLABELSUPP) {
+       if (sbsec->flags & SBLABEL_MNT) {
                opts->mnt_opts[i] = NULL;
-               opts->mnt_opts_flags[i++] = SE_SBLABELSUPP;
+               opts->mnt_opts_flags[i++] = SBLABEL_MNT;
        }
 
        BUG_ON(i != opts->num_mnt_opts);
@@ -561,7 +600,6 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        const struct cred *cred = current_cred();
        int rc = 0, i;
        struct superblock_security_struct *sbsec = sb->s_security;
-       const char *name = sb->s_type->name;
        struct inode *inode = sbsec->sb->s_root->d_inode;
        struct inode_security_struct *root_isec = inode->i_security;
        u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
@@ -614,14 +652,14 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        for (i = 0; i < num_opts; i++) {
                u32 sid;
 
-               if (flags[i] == SE_SBLABELSUPP)
+               if (flags[i] == SBLABEL_MNT)
                        continue;
                rc = security_context_to_sid(mount_options[i],
                                             strlen(mount_options[i]), &sid);
                if (rc) {
                        printk(KERN_WARNING "SELinux: security_context_to_sid"
-                              "(%s) failed for (dev %s, type %s) errno=%d\n",
-                              mount_options[i], sb->s_id, name, rc);
+                              "(%s) failed for (dev %s, type "SB_TYPE_FMT") errno=%d\n",
+                              mount_options[i], sb->s_id, SB_TYPE_ARGS(sb), rc);
                        goto out;
                }
                switch (flags[i]) {
@@ -685,9 +723,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
                 * Determine the labeling behavior to use for this
                 * filesystem type.
                 */
-               rc = security_fs_use((sbsec->flags & SE_SBPROC) ?
-                                       "proc" : sb->s_type->name,
-                                       &sbsec->behavior, &sbsec->sid);
+               rc = security_fs_use(sb);
                if (rc) {
                        printk(KERN_WARNING
                                "%s: security_fs_use(%s) returned %d\n",
@@ -770,7 +806,8 @@ out:
 out_double_mount:
        rc = -EINVAL;
        printk(KERN_WARNING "SELinux: mount invalid.  Same superblock, different "
-              "security settings for (dev %s, type %s)\n", sb->s_id, name);
+              "security settings for (dev %s, type "SB_TYPE_FMT")\n", sb->s_id,
+              SB_TYPE_ARGS(sb));
        goto out;
 }
 
@@ -1037,7 +1074,7 @@ static void selinux_write_opts(struct seq_file *m,
                case DEFCONTEXT_MNT:
                        prefix = DEFCONTEXT_STR;
                        break;
-               case SE_SBLABELSUPP:
+               case SBLABEL_MNT:
                        seq_putc(m, ',');
                        seq_puts(m, LABELSUPP_STR);
                        continue;
@@ -1649,7 +1686,7 @@ static int may_create(struct inode *dir,
        if (rc)
                return rc;
 
-       if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
+       if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {
                rc = security_transition_sid(sid, dsec->sid, tclass,
                                             &dentry->d_name, &newsid);
                if (rc)
@@ -2437,14 +2474,14 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
                u32 sid;
                size_t len;
 
-               if (flags[i] == SE_SBLABELSUPP)
+               if (flags[i] == SBLABEL_MNT)
                        continue;
                len = strlen(mount_options[i]);
                rc = security_context_to_sid(mount_options[i], len, &sid);
                if (rc) {
                        printk(KERN_WARNING "SELinux: security_context_to_sid"
-                              "(%s) failed for (dev %s, type %s) errno=%d\n",
-                              mount_options[i], sb->s_id, sb->s_type->name, rc);
+                              "(%s) failed for (dev %s, type "SB_TYPE_FMT") errno=%d\n",
+                              mount_options[i], sb->s_id, SB_TYPE_ARGS(sb), rc);
                        goto out_free_opts;
                }
                rc = -EINVAL;
@@ -2482,8 +2519,8 @@ out_free_secdata:
        return rc;
 out_bad_option:
        printk(KERN_WARNING "SELinux: unable to change security options "
-              "during remount (dev %s, type=%s)\n", sb->s_id,
-              sb->s_type->name);
+              "during remount (dev %s, type "SB_TYPE_FMT")\n", sb->s_id,
+              SB_TYPE_ARGS(sb));
        goto out_free_opts;
 }
 
@@ -2606,7 +2643,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
        if ((sbsec->flags & SE_SBINITIALIZED) &&
            (sbsec->behavior == SECURITY_FS_USE_MNTPOINT))
                newsid = sbsec->mntpoint_sid;
-       else if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
+       else if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {
                rc = security_transition_sid(sid, dsec->sid,
                                             inode_mode_to_security_class(inode->i_mode),
                                             qstr, &newsid);
@@ -2628,7 +2665,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                isec->initialized = 1;
        }
 
-       if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP))
+       if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT))
                return -EOPNOTSUPP;
 
        if (name)
@@ -2830,7 +2867,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
                return selinux_inode_setotherxattr(dentry, name);
 
        sbsec = inode->i_sb->s_security;
-       if (!(sbsec->flags & SE_SBLABELSUPP))
+       if (!(sbsec->flags & SBLABEL_MNT))
                return -EOPNOTSUPP;
 
        if (!inode_owner_or_capable(inode))
@@ -3791,8 +3828,12 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
        u32 nlbl_sid;
        u32 nlbl_type;
 
-       selinux_skb_xfrm_sid(skb, &xfrm_sid);
-       selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
+       err = selinux_skb_xfrm_sid(skb, &xfrm_sid);
+       if (unlikely(err))
+               return -EACCES;
+       err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
+       if (unlikely(err))
+               return -EACCES;
 
        err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid);
        if (unlikely(err)) {
@@ -4246,7 +4287,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                return selinux_sock_rcv_skb_compat(sk, skb, family);
 
        secmark_active = selinux_secmark_enabled();
-       peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+       peerlbl_active = selinux_peerlbl_enabled();
        if (!secmark_active && !peerlbl_active)
                return 0;
 
@@ -4628,7 +4669,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
 
        secmark_active = selinux_secmark_enabled();
        netlbl_active = netlbl_enabled();
-       peerlbl_active = netlbl_active || selinux_xfrm_enabled();
+       peerlbl_active = selinux_peerlbl_enabled();
        if (!secmark_active && !peerlbl_active)
                return NF_ACCEPT;
 
@@ -4780,7 +4821,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
                return NF_ACCEPT;
 #endif
        secmark_active = selinux_secmark_enabled();
-       peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+       peerlbl_active = selinux_peerlbl_enabled();
        if (!secmark_active && !peerlbl_active)
                return NF_ACCEPT;
 
@@ -5784,7 +5825,8 @@ static struct security_operations selinux_ops = {
        .xfrm_policy_clone_security =   selinux_xfrm_policy_clone,
        .xfrm_policy_free_security =    selinux_xfrm_policy_free,
        .xfrm_policy_delete_security =  selinux_xfrm_policy_delete,
-       .xfrm_state_alloc_security =    selinux_xfrm_state_alloc,
+       .xfrm_state_alloc =             selinux_xfrm_state_alloc,
+       .xfrm_state_alloc_acquire =     selinux_xfrm_state_alloc_acquire,
        .xfrm_state_free_security =     selinux_xfrm_state_free,
        .xfrm_state_delete_security =   selinux_xfrm_state_delete,
        .xfrm_policy_lookup =           selinux_xfrm_policy_lookup,
index aa47bca..b1dfe10 100644 (file)
@@ -58,8 +58,8 @@ struct superblock_security_struct {
        u32 sid;                        /* SID of file system superblock */
        u32 def_sid;                    /* default SID for labeling */
        u32 mntpoint_sid;               /* SECURITY_FS_USE_MNTPOINT context for files */
-       unsigned int behavior;          /* labeling behavior */
-       unsigned char flags;            /* which mount options were specified */
+       unsigned short behavior;        /* labeling behavior */
+       unsigned short flags;           /* which mount options were specified */
        struct mutex lock;
        struct list_head isec_head;
        spinlock_t isec_lock;
index 8fd8e18..fe341ae 100644 (file)
 /* Mask for just the mount related flags */
 #define SE_MNTMASK     0x0f
 /* Super block security struct flags for mount options */
+/* BE CAREFUL, these need to be the low order bits for selinux_get_mnt_opts */
 #define CONTEXT_MNT    0x01
 #define FSCONTEXT_MNT  0x02
 #define ROOTCONTEXT_MNT        0x04
 #define DEFCONTEXT_MNT 0x08
+#define SBLABEL_MNT    0x10
 /* Non-mount related flags */
-#define SE_SBINITIALIZED       0x10
-#define SE_SBPROC              0x20
-#define SE_SBLABELSUPP 0x40
+#define SE_SBINITIALIZED       0x0100
+#define SE_SBPROC              0x0200
 
 #define CONTEXT_STR    "context="
 #define FSCONTEXT_STR  "fscontext="
@@ -68,12 +69,15 @@ extern int selinux_enabled;
 enum {
        POLICYDB_CAPABILITY_NETPEER,
        POLICYDB_CAPABILITY_OPENPERM,
+       POLICYDB_CAPABILITY_REDHAT1,
+       POLICYDB_CAPABILITY_ALWAYSNETWORK,
        __POLICYDB_CAPABILITY_MAX
 };
 #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
 
 extern int selinux_policycap_netpeer;
 extern int selinux_policycap_openperm;
+extern int selinux_policycap_alwaysnetwork;
 
 /*
  * type_datum properties
@@ -172,8 +176,7 @@ int security_get_allow_unknown(void);
 #define SECURITY_FS_USE_NATIVE         7 /* use native label support */
 #define SECURITY_FS_USE_MAX            7 /* Highest SECURITY_FS_USE_XXX */
 
-int security_fs_use(const char *fstype, unsigned int *behavior,
-       u32 *sid);
+int security_fs_use(struct super_block *sb);
 
 int security_genfs_sid(const char *fstype, char *name, u16 sclass,
        u32 *sid);
index 6713f04..0dec76c 100644 (file)
 #include <net/flow.h>
 
 int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
-                             struct xfrm_user_sec_ctx *sec_ctx);
+                             struct xfrm_user_sec_ctx *uctx);
 int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
                              struct xfrm_sec_ctx **new_ctxp);
 void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx);
 int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx);
 int selinux_xfrm_state_alloc(struct xfrm_state *x,
-       struct xfrm_user_sec_ctx *sec_ctx, u32 secid);
+                            struct xfrm_user_sec_ctx *uctx);
+int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x,
+                                    struct xfrm_sec_ctx *polsec, u32 secid);
 void selinux_xfrm_state_free(struct xfrm_state *x);
 int selinux_xfrm_state_delete(struct xfrm_state *x);
 int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir);
 int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
-                       struct xfrm_policy *xp, const struct flowi *fl);
-
-/*
- * Extract the security blob from the sock (it's actually on the socket)
- */
-static inline struct inode_security_struct *get_sock_isec(struct sock *sk)
-{
-       if (!sk->sk_socket)
-               return NULL;
-
-       return SOCK_INODE(sk->sk_socket)->i_security;
-}
+                                     struct xfrm_policy *xp,
+                                     const struct flowi *fl);
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 extern atomic_t selinux_xfrm_refcount;
@@ -42,10 +34,10 @@ static inline int selinux_xfrm_enabled(void)
        return (atomic_read(&selinux_xfrm_refcount) > 0);
 }
 
-int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
-                       struct common_audit_data *ad);
-int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
-                       struct common_audit_data *ad, u8 proto);
+int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
+                             struct common_audit_data *ad);
+int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
+                               struct common_audit_data *ad, u8 proto);
 int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
 
 static inline void selinux_xfrm_notify_policyload(void)
@@ -64,19 +56,21 @@ static inline int selinux_xfrm_enabled(void)
        return 0;
 }
 
-static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
-                       struct common_audit_data *ad)
+static inline int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
+                                           struct common_audit_data *ad)
 {
        return 0;
 }
 
-static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
-                       struct common_audit_data *ad, u8 proto)
+static inline int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
+                                             struct common_audit_data *ad,
+                                             u8 proto)
 {
        return 0;
 }
 
-static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
+static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid,
+                                             int ckall)
 {
        *sid = SECSID_NULL;
        return 0;
@@ -87,10 +81,9 @@ static inline void selinux_xfrm_notify_policyload(void)
 }
 #endif
 
-static inline void selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
+static inline int selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
 {
-       int err = selinux_xfrm_decode_session(skb, sid, 0);
-       BUG_ON(err);
+       return selinux_xfrm_decode_session(skb, sid, 0);
 }
 
 #endif /* _SELINUX_XFRM_H_ */
index da4b8b2..6235d05 100644 (file)
@@ -442,8 +442,7 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
            sksec->nlbl_state != NLBL_CONNLABELED)
                return 0;
 
-       local_bh_disable();
-       bh_lock_sock_nested(sk);
+       lock_sock(sk);
 
        /* connected sockets are allowed to disconnect when the address family
         * is set to AF_UNSPEC, if that is what is happening we want to reset
@@ -464,7 +463,6 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
                sksec->nlbl_state = NLBL_CONNLABELED;
 
 socket_connect_return:
-       bh_unlock_sock(sk);
-       local_bh_enable();
+       release_sock(sk);
        return rc;
 }
index c5454c0..03a72c3 100644 (file)
@@ -166,6 +166,7 @@ static void sel_netnode_insert(struct sel_netnode *node)
                break;
        default:
                BUG();
+               return;
        }
 
        /* we need to impose a limit on the growth of the hash table so check
@@ -225,6 +226,7 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
                break;
        default:
                BUG();
+               ret = -EINVAL;
        }
        if (ret != 0)
                goto out;
index 855e464..332ac8a 100644 (file)
@@ -116,6 +116,8 @@ static struct nlmsg_perm nlmsg_audit_perms[] =
        { AUDIT_MAKE_EQUIV,     NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },
        { AUDIT_TTY_GET,        NETLINK_AUDIT_SOCKET__NLMSG_READ     },
        { AUDIT_TTY_SET,        NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT   },
+       { AUDIT_GET_FEATURE,    NETLINK_AUDIT_SOCKET__NLMSG_READ     },
+       { AUDIT_SET_FEATURE,    NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },
 };
 
 
index ff42773..5122aff 100644 (file)
@@ -44,7 +44,9 @@
 /* Policy capability filenames */
 static char *policycap_names[] = {
        "network_peer_controls",
-       "open_perms"
+       "open_perms",
+       "redhat1",
+       "always_check_network"
 };
 
 unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
index 30f119b..820313a 100644 (file)
@@ -213,7 +213,12 @@ netlbl_import_failure:
 }
 #endif /* CONFIG_NETLABEL */
 
-int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
+/*
+ * Check to see if all the bits set in e2 are also set in e1. Optionally,
+ * if last_e2bit is non-zero, the highest set bit in e2 cannot exceed
+ * last_e2bit.
+ */
+int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit)
 {
        struct ebitmap_node *n1, *n2;
        int i;
@@ -223,14 +228,25 @@ int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
 
        n1 = e1->node;
        n2 = e2->node;
+
        while (n1 && n2 && (n1->startbit <= n2->startbit)) {
                if (n1->startbit < n2->startbit) {
                        n1 = n1->next;
                        continue;
                }
-               for (i = 0; i < EBITMAP_UNIT_NUMS; i++) {
+               for (i = EBITMAP_UNIT_NUMS - 1; (i >= 0) && !n2->maps[i]; )
+                       i--;    /* Skip trailing NULL map entries */
+               if (last_e2bit && (i >= 0)) {
+                       u32 lastsetbit = n2->startbit + i * EBITMAP_UNIT_SIZE +
+                                        __fls(n2->maps[i]);
+                       if (lastsetbit > last_e2bit)
+                               return 0;
+               }
+
+               while (i >= 0) {
                        if ((n1->maps[i] & n2->maps[i]) != n2->maps[i])
                                return 0;
+                       i--;
                }
 
                n1 = n1->next;
index 922f8af..712c8a7 100644 (file)
 
 #include <net/netlabel.h>
 
-#define EBITMAP_UNIT_NUMS      ((32 - sizeof(void *) - sizeof(u32))    \
+#ifdef CONFIG_64BIT
+#define        EBITMAP_NODE_SIZE       64
+#else
+#define        EBITMAP_NODE_SIZE       32
+#endif
+
+#define EBITMAP_UNIT_NUMS      ((EBITMAP_NODE_SIZE-sizeof(void *)-sizeof(u32))\
                                        / sizeof(unsigned long))
 #define EBITMAP_UNIT_SIZE      BITS_PER_LONG
 #define EBITMAP_SIZE           (EBITMAP_UNIT_NUMS * EBITMAP_UNIT_SIZE)
@@ -117,7 +123,7 @@ static inline void ebitmap_node_clr_bit(struct ebitmap_node *n,
 
 int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);
 int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src);
-int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2);
+int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit);
 int ebitmap_get_bit(struct ebitmap *e, unsigned long bit);
 int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);
 void ebitmap_destroy(struct ebitmap *e);
index 40de8d3..c85bc1e 100644 (file)
@@ -160,8 +160,6 @@ void mls_sid_to_context(struct context *context,
 int mls_level_isvalid(struct policydb *p, struct mls_level *l)
 {
        struct level_datum *levdatum;
-       struct ebitmap_node *node;
-       int i;
 
        if (!l->sens || l->sens > p->p_levels.nprim)
                return 0;
@@ -170,19 +168,13 @@ int mls_level_isvalid(struct policydb *p, struct mls_level *l)
        if (!levdatum)
                return 0;
 
-       ebitmap_for_each_positive_bit(&l->cat, node, i) {
-               if (i > p->p_cats.nprim)
-                       return 0;
-               if (!ebitmap_get_bit(&levdatum->level->cat, i)) {
-                       /*
-                        * Category may not be associated with
-                        * sensitivity.
-                        */
-                       return 0;
-               }
-       }
-
-       return 1;
+       /*
+        * Return 1 iff all the bits set in l->cat are also be set in
+        * levdatum->level->cat and no bit in l->cat is larger than
+        * p->p_cats.nprim.
+        */
+       return ebitmap_contains(&levdatum->level->cat, &l->cat,
+                               p->p_cats.nprim);
 }
 
 int mls_range_isvalid(struct policydb *p, struct mls_range *r)
index 03bed52..e936487 100644 (file)
@@ -35,7 +35,7 @@ static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2)
 static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2)
 {
        return ((l1->sens >= l2->sens) &&
-               ebitmap_contains(&l1->cat, &l2->cat));
+               ebitmap_contains(&l1->cat, &l2->cat, 0));
 }
 
 #define mls_level_incomp(l1, l2) \
index c8adde3..f6195eb 100644 (file)
@@ -3203,9 +3203,8 @@ static int range_write_helper(void *key, void *data, void *ptr)
 
 static int range_write(struct policydb *p, void *fp)
 {
-       size_t nel;
        __le32 buf[1];
-       int rc;
+       int rc, nel;
        struct policy_data pd;
 
        pd.p = p;
index b4feecc..ee470a0 100644 (file)
@@ -72,6 +72,7 @@
 
 int selinux_policycap_netpeer;
 int selinux_policycap_openperm;
+int selinux_policycap_alwaysnetwork;
 
 static DEFINE_RWLOCK(policy_rwlock);
 
@@ -1812,6 +1813,8 @@ static void security_load_policycaps(void)
                                                  POLICYDB_CAPABILITY_NETPEER);
        selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,
                                                  POLICYDB_CAPABILITY_OPENPERM);
+       selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps,
+                                                 POLICYDB_CAPABILITY_ALWAYSNETWORK);
 }
 
 static int security_preserve_bools(struct policydb *p);
@@ -2323,43 +2326,74 @@ out:
 
 /**
  * security_fs_use - Determine how to handle labeling for a filesystem.
- * @fstype: filesystem type
- * @behavior: labeling behavior
- * @sid: SID for filesystem (superblock)
+ * @sb: superblock in question
  */
-int security_fs_use(
-       const char *fstype,
-       unsigned int *behavior,
-       u32 *sid)
+int security_fs_use(struct super_block *sb)
 {
        int rc = 0;
        struct ocontext *c;
+       struct superblock_security_struct *sbsec = sb->s_security;
+       const char *fstype = sb->s_type->name;
+       const char *subtype = (sb->s_subtype && sb->s_subtype[0]) ? sb->s_subtype : NULL;
+       struct ocontext *base = NULL;
 
        read_lock(&policy_rwlock);
 
-       c = policydb.ocontexts[OCON_FSUSE];
-       while (c) {
-               if (strcmp(fstype, c->u.name) == 0)
+       for (c = policydb.ocontexts[OCON_FSUSE]; c; c = c->next) {
+               char *sub;
+               int baselen;
+
+               baselen = strlen(fstype);
+
+               /* if base does not match, this is not the one */
+               if (strncmp(fstype, c->u.name, baselen))
+                       continue;
+
+               /* if there is no subtype, this is the one! */
+               if (!subtype)
+                       break;
+
+               /* skip past the base in this entry */
+               sub = c->u.name + baselen;
+
+               /* entry is only a base. save it. keep looking for subtype */
+               if (sub[0] == '\0') {
+                       base = c;
+                       continue;
+               }
+
+               /* entry is not followed by a subtype, so it is not a match */
+               if (sub[0] != '.')
+                       continue;
+
+               /* whew, we found a subtype of this fstype */
+               sub++; /* move past '.' */
+
+               /* exact match of fstype AND subtype */
+               if (!strcmp(subtype, sub))
                        break;
-               c = c->next;
        }
 
+       /* in case we had found an fstype match but no subtype match */
+       if (!c)
+               c = base;
+
        if (c) {
-               *behavior = c->v.behavior;
+               sbsec->behavior = c->v.behavior;
                if (!c->sid[0]) {
                        rc = sidtab_context_to_sid(&sidtab, &c->context[0],
                                                   &c->sid[0]);
                        if (rc)
                                goto out;
                }
-               *sid = c->sid[0];
+               sbsec->sid = c->sid[0];
        } else {
-               rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid);
+               rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, &sbsec->sid);
                if (rc) {
-                       *behavior = SECURITY_FS_USE_NONE;
+                       sbsec->behavior = SECURITY_FS_USE_NONE;
                        rc = 0;
                } else {
-                       *behavior = SECURITY_FS_USE_GENFS;
+                       sbsec->behavior = SECURITY_FS_USE_GENFS;
                }
        }
 
index d030818..a91d205 100644 (file)
@@ -56,7 +56,7 @@
 atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0);
 
 /*
- * Returns true if an LSM/SELinux context
+ * Returns true if the context is an LSM/SELinux context.
  */
 static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)
 {
@@ -66,7 +66,7 @@ static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)
 }
 
 /*
- * Returns true if the xfrm contains a security blob for SELinux
+ * Returns true if the xfrm contains a security blob for SELinux.
  */
 static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
 {
@@ -74,48 +74,111 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
 }
 
 /*
- * LSM hook implementation that authorizes that a flow can use
- * a xfrm policy rule.
+ * Allocates a xfrm_sec_state and populates it using the supplied security
+ * xfrm_user_sec_ctx context.
  */
-int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
+static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
+                                  struct xfrm_user_sec_ctx *uctx)
 {
        int rc;
-       u32 sel_sid;
+       const struct task_security_struct *tsec = current_security();
+       struct xfrm_sec_ctx *ctx = NULL;
+       u32 str_len;
 
-       /* Context sid is either set to label or ANY_ASSOC */
-       if (ctx) {
-               if (!selinux_authorizable_ctx(ctx))
-                       return -EINVAL;
-
-               sel_sid = ctx->ctx_sid;
-       } else
-               /*
-                * All flows should be treated as polmatch'ing an
-                * otherwise applicable "non-labeled" policy. This
-                * would prevent inadvertent "leaks".
-                */
-               return 0;
+       if (ctxp == NULL || uctx == NULL ||
+           uctx->ctx_doi != XFRM_SC_DOI_LSM ||
+           uctx->ctx_alg != XFRM_SC_ALG_SELINUX)
+               return -EINVAL;
 
-       rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION,
-                         ASSOCIATION__POLMATCH,
-                         NULL);
+       str_len = uctx->ctx_len;
+       if (str_len >= PAGE_SIZE)
+               return -ENOMEM;
 
-       if (rc == -EACCES)
-               return -ESRCH;
+       ctx = kmalloc(sizeof(*ctx) + str_len + 1, GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
 
+       ctx->ctx_doi = XFRM_SC_DOI_LSM;
+       ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
+       ctx->ctx_len = str_len;
+       memcpy(ctx->ctx_str, &uctx[1], str_len);
+       ctx->ctx_str[str_len] = '\0';
+       rc = security_context_to_sid(ctx->ctx_str, str_len, &ctx->ctx_sid);
+       if (rc)
+               goto err;
+
+       rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+                         SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL);
+       if (rc)
+               goto err;
+
+       *ctxp = ctx;
+       atomic_inc(&selinux_xfrm_refcount);
+       return 0;
+
+err:
+       kfree(ctx);
        return rc;
 }
 
 /*
+ * Free the xfrm_sec_ctx structure.
+ */
+static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx)
+{
+       if (!ctx)
+               return;
+
+       atomic_dec(&selinux_xfrm_refcount);
+       kfree(ctx);
+}
+
+/*
+ * Authorize the deletion of a labeled SA or policy rule.
+ */
+static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx)
+{
+       const struct task_security_struct *tsec = current_security();
+
+       if (!ctx)
+               return 0;
+
+       return avc_has_perm(tsec->sid, ctx->ctx_sid,
+                           SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
+                           NULL);
+}
+
+/*
+ * LSM hook implementation that authorizes that a flow can use a xfrm policy
+ * rule.
+ */
+int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
+{
+       int rc;
+
+       /* All flows should be treated as polmatch'ing an otherwise applicable
+        * "non-labeled" policy. This would prevent inadvertent "leaks". */
+       if (!ctx)
+               return 0;
+
+       /* Context sid is either set to label or ANY_ASSOC */
+       if (!selinux_authorizable_ctx(ctx))
+               return -EINVAL;
+
+       rc = avc_has_perm(fl_secid, ctx->ctx_sid,
+                         SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL);
+       return (rc == -EACCES ? -ESRCH : rc);
+}
+
+/*
  * LSM hook implementation that authorizes that a state matches
  * the given policy, flow combo.
  */
-
-int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp,
-                       const struct flowi *fl)
+int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
+                                     struct xfrm_policy *xp,
+                                     const struct flowi *fl)
 {
        u32 state_sid;
-       int rc;
 
        if (!xp->security)
                if (x->security)
@@ -138,187 +201,80 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *
        if (fl->flowi_secid != state_sid)
                return 0;
 
-       rc = avc_has_perm(fl->flowi_secid, state_sid, SECCLASS_ASSOCIATION,
-                         ASSOCIATION__SENDTO,
-                         NULL)? 0:1;
-
-       /*
-        * We don't need a separate SA Vs. policy polmatch check
-        * since the SA is now of the same label as the flow and
-        * a flow Vs. policy polmatch check had already happened
-        * in selinux_xfrm_policy_lookup() above.
-        */
-
-       return rc;
+       /* We don't need a separate SA Vs. policy polmatch check since the SA
+        * is now of the same label as the flow and a flow Vs. policy polmatch
+        * check had already happened in selinux_xfrm_policy_lookup() above. */
+       return (avc_has_perm(fl->flowi_secid, state_sid,
+                           SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO,
+                           NULL) ? 0 : 1);
 }
 
 /*
  * LSM hook implementation that checks and/or returns the xfrm sid for the
  * incoming packet.
  */
-
 int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
 {
+       u32 sid_session = SECSID_NULL;
        struct sec_path *sp;
 
-       *sid = SECSID_NULL;
-
        if (skb == NULL)
-               return 0;
+               goto out;
 
        sp = skb->sp;
        if (sp) {
-               int i, sid_set = 0;
+               int i;
 
-               for (i = sp->len-1; i >= 0; i--) {
+               for (i = sp->len - 1; i >= 0; i--) {
                        struct xfrm_state *x = sp->xvec[i];
                        if (selinux_authorizable_xfrm(x)) {
                                struct xfrm_sec_ctx *ctx = x->security;
 
-                               if (!sid_set) {
-                                       *sid = ctx->ctx_sid;
-                                       sid_set = 1;
-
+                               if (sid_session == SECSID_NULL) {
+                                       sid_session = ctx->ctx_sid;
                                        if (!ckall)
-                                               break;
-                               } else if (*sid != ctx->ctx_sid)
+                                               goto out;
+                               } else if (sid_session != ctx->ctx_sid) {
+                                       *sid = SECSID_NULL;
                                        return -EINVAL;
+                               }
                        }
                }
        }
 
-       return 0;
-}
-
-/*
- * Security blob allocation for xfrm_policy and xfrm_state
- * CTX does not have a meaningful value on input
- */
-static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
-       struct xfrm_user_sec_ctx *uctx, u32 sid)
-{
-       int rc = 0;
-       const struct task_security_struct *tsec = current_security();
-       struct xfrm_sec_ctx *ctx = NULL;
-       char *ctx_str = NULL;
-       u32 str_len;
-
-       BUG_ON(uctx && sid);
-
-       if (!uctx)
-               goto not_from_user;
-
-       if (uctx->ctx_alg != XFRM_SC_ALG_SELINUX)
-               return -EINVAL;
-
-       str_len = uctx->ctx_len;
-       if (str_len >= PAGE_SIZE)
-               return -ENOMEM;
-
-       *ctxp = ctx = kmalloc(sizeof(*ctx) +
-                             str_len + 1,
-                             GFP_KERNEL);
-
-       if (!ctx)
-               return -ENOMEM;
-
-       ctx->ctx_doi = uctx->ctx_doi;
-       ctx->ctx_len = str_len;
-       ctx->ctx_alg = uctx->ctx_alg;
-
-       memcpy(ctx->ctx_str,
-              uctx+1,
-              str_len);
-       ctx->ctx_str[str_len] = 0;
-       rc = security_context_to_sid(ctx->ctx_str,
-                                    str_len,
-                                    &ctx->ctx_sid);
-
-       if (rc)
-               goto out;
-
-       /*
-        * Does the subject have permission to set security context?
-        */
-       rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
-                         SECCLASS_ASSOCIATION,
-                         ASSOCIATION__SETCONTEXT, NULL);
-       if (rc)
-               goto out;
-
-       return rc;
-
-not_from_user:
-       rc = security_sid_to_context(sid, &ctx_str, &str_len);
-       if (rc)
-               goto out;
-
-       *ctxp = ctx = kmalloc(sizeof(*ctx) +
-                             str_len,
-                             GFP_ATOMIC);
-
-       if (!ctx) {
-               rc = -ENOMEM;
-               goto out;
-       }
-
-       ctx->ctx_doi = XFRM_SC_DOI_LSM;
-       ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
-       ctx->ctx_sid = sid;
-       ctx->ctx_len = str_len;
-       memcpy(ctx->ctx_str,
-              ctx_str,
-              str_len);
-
-       goto out2;
-
 out:
-       *ctxp = NULL;
-       kfree(ctx);
-out2:
-       kfree(ctx_str);
-       return rc;
+       *sid = sid_session;
+       return 0;
 }
 
 /*
- * LSM hook implementation that allocs and transfers uctx spec to
- * xfrm_policy.
+ * LSM hook implementation that allocs and transfers uctx spec to xfrm_policy.
  */
 int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
                              struct xfrm_user_sec_ctx *uctx)
 {
-       int err;
-
-       BUG_ON(!uctx);
-
-       err = selinux_xfrm_sec_ctx_alloc(ctxp, uctx, 0);
-       if (err == 0)
-               atomic_inc(&selinux_xfrm_refcount);
-
-       return err;
+       return selinux_xfrm_alloc_user(ctxp, uctx);
 }
 
-
 /*
- * LSM hook implementation that copies security data structure from old to
- * new for policy cloning.
+ * LSM hook implementation that copies security data structure from old to new
+ * for policy cloning.
  */
 int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
                              struct xfrm_sec_ctx **new_ctxp)
 {
        struct xfrm_sec_ctx *new_ctx;
 
-       if (old_ctx) {
-               new_ctx = kmalloc(sizeof(*old_ctx) + old_ctx->ctx_len,
-                                 GFP_ATOMIC);
-               if (!new_ctx)
-                       return -ENOMEM;
+       if (!old_ctx)
+               return 0;
+
+       new_ctx = kmemdup(old_ctx, sizeof(*old_ctx) + old_ctx->ctx_len,
+                         GFP_ATOMIC);
+       if (!new_ctx)
+               return -ENOMEM;
+       atomic_inc(&selinux_xfrm_refcount);
+       *new_ctxp = new_ctx;
 
-               memcpy(new_ctx, old_ctx, sizeof(*new_ctx));
-               memcpy(new_ctx->ctx_str, old_ctx->ctx_str, new_ctx->ctx_len);
-               atomic_inc(&selinux_xfrm_refcount);
-               *new_ctxp = new_ctx;
-       }
        return 0;
 }
 
@@ -327,8 +283,7 @@ int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
  */
 void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
 {
-       atomic_dec(&selinux_xfrm_refcount);
-       kfree(ctx);
+       selinux_xfrm_free(ctx);
 }
 
 /*
@@ -336,31 +291,55 @@ void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
  */
 int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
 {
-       const struct task_security_struct *tsec = current_security();
-
-       if (!ctx)
-               return 0;
+       return selinux_xfrm_delete(ctx);
+}
 
-       return avc_has_perm(tsec->sid, ctx->ctx_sid,
-                           SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
-                           NULL);
+/*
+ * LSM hook implementation that allocates a xfrm_sec_state, populates it using
+ * the supplied security context, and assigns it to the xfrm_state.
+ */
+int selinux_xfrm_state_alloc(struct xfrm_state *x,
+                            struct xfrm_user_sec_ctx *uctx)
+{
+       return selinux_xfrm_alloc_user(&x->security, uctx);
 }
 
 /*
- * LSM hook implementation that allocs and transfers sec_ctx spec to
- * xfrm_state.
+ * LSM hook implementation that allocates a xfrm_sec_state and populates based
+ * on a secid.
  */
-int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx,
-               u32 secid)
+int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x,
+                                    struct xfrm_sec_ctx *polsec, u32 secid)
 {
-       int err;
+       int rc;
+       struct xfrm_sec_ctx *ctx;
+       char *ctx_str = NULL;
+       int str_len;
+
+       if (!polsec)
+               return 0;
 
-       BUG_ON(!x);
+       if (secid == 0)
+               return -EINVAL;
 
-       err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid);
-       if (err == 0)
-               atomic_inc(&selinux_xfrm_refcount);
-       return err;
+       rc = security_sid_to_context(secid, &ctx_str, &str_len);
+       if (rc)
+               return rc;
+
+       ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->ctx_doi = XFRM_SC_DOI_LSM;
+       ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
+       ctx->ctx_sid = secid;
+       ctx->ctx_len = str_len;
+       memcpy(ctx->ctx_str, ctx_str, str_len);
+       kfree(ctx_str);
+
+       x->security = ctx;
+       atomic_inc(&selinux_xfrm_refcount);
+       return 0;
 }
 
 /*
@@ -368,24 +347,15 @@ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uct
  */
 void selinux_xfrm_state_free(struct xfrm_state *x)
 {
-       atomic_dec(&selinux_xfrm_refcount);
-       kfree(x->security);
+       selinux_xfrm_free(x->security);
 }
 
- /*
 * LSM hook implementation that authorizes deletion of labeled SAs.
 */
+/*
+ * LSM hook implementation that authorizes deletion of labeled SAs.
+ */
 int selinux_xfrm_state_delete(struct xfrm_state *x)
 {
-       const struct task_security_struct *tsec = current_security();
-       struct xfrm_sec_ctx *ctx = x->security;
-
-       if (!ctx)
-               return 0;
-
-       return avc_has_perm(tsec->sid, ctx->ctx_sid,
-                           SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
-                           NULL);
+       return selinux_xfrm_delete(x->security);
 }
 
 /*
@@ -395,14 +365,12 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
  * we need to check for unlabelled access since this may not have
  * gone thru the IPSec process.
  */
-int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
-                               struct common_audit_data *ad)
+int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
+                             struct common_audit_data *ad)
 {
-       int i, rc = 0;
-       struct sec_path *sp;
-       u32 sel_sid = SECINITSID_UNLABELED;
-
-       sp = skb->sp;
+       int i;
+       struct sec_path *sp = skb->sp;
+       u32 peer_sid = SECINITSID_UNLABELED;
 
        if (sp) {
                for (i = 0; i < sp->len; i++) {
@@ -410,23 +378,17 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
 
                        if (x && selinux_authorizable_xfrm(x)) {
                                struct xfrm_sec_ctx *ctx = x->security;
-                               sel_sid = ctx->ctx_sid;
+                               peer_sid = ctx->ctx_sid;
                                break;
                        }
                }
        }
 
-       /*
-        * This check even when there's no association involved is
-        * intended, according to Trent Jaeger, to make sure a
-        * process can't engage in non-ipsec communication unless
-        * explicitly allowed by policy.
-        */
-
-       rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION,
-                         ASSOCIATION__RECVFROM, ad);
-
-       return rc;
+       /* This check even when there's no association involved is intended,
+        * according to Trent Jaeger, to make sure a process can't engage in
+        * non-IPsec communication unless explicitly allowed by policy. */
+       return avc_has_perm(sk_sid, peer_sid,
+                           SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad);
 }
 
 /*
@@ -436,49 +398,38 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
  * If we do have a authorizable security association, then it has already been
  * checked in the selinux_xfrm_state_pol_flow_match hook above.
  */
-int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
-                                       struct common_audit_data *ad, u8 proto)
+int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
+                               struct common_audit_data *ad, u8 proto)
 {
        struct dst_entry *dst;
-       int rc = 0;
-
-       dst = skb_dst(skb);
-
-       if (dst) {
-               struct dst_entry *dst_test;
-
-               for (dst_test = dst; dst_test != NULL;
-                    dst_test = dst_test->child) {
-                       struct xfrm_state *x = dst_test->xfrm;
-
-                       if (x && selinux_authorizable_xfrm(x))
-                               goto out;
-               }
-       }
 
        switch (proto) {
        case IPPROTO_AH:
        case IPPROTO_ESP:
        case IPPROTO_COMP:
-               /*
-                * We should have already seen this packet once before
-                * it underwent xfrm(s). No need to subject it to the
-                * unlabeled check.
-                */
-               goto out;
+               /* We should have already seen this packet once before it
+                * underwent xfrm(s). No need to subject it to the unlabeled
+                * check. */
+               return 0;
        default:
                break;
        }
 
-       /*
-        * This check even when there's no association involved is
-        * intended, according to Trent Jaeger, to make sure a
-        * process can't engage in non-ipsec communication unless
-        * explicitly allowed by policy.
-        */
+       dst = skb_dst(skb);
+       if (dst) {
+               struct dst_entry *iter;
 
-       rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
-                         ASSOCIATION__SENDTO, ad);
-out:
-       return rc;
+               for (iter = dst; iter != NULL; iter = iter->child) {
+                       struct xfrm_state *x = iter->xfrm;
+
+                       if (x && selinux_authorizable_xfrm(x))
+                               return 0;
+               }
+       }
+
+       /* This check even when there's no association involved is intended,
+        * according to Trent Jaeger, to make sure a process can't engage in
+        * non-IPsec communication unless explicitly allowed by policy. */
+       return avc_has_perm(sk_sid, SECINITSID_UNLABELED,
+                           SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad);
 }
index 076b8e8..364cc64 100644 (file)
@@ -177,9 +177,13 @@ struct smk_port_label {
 #define SMACK_CIPSO_MAXCATNUM           184     /* 23 * 8 */
 
 /*
- * Flag for transmute access
+ * Flags for untraditional access modes.
+ * It shouldn't be necessary to avoid conflicts with definitions
+ * in fs.h, but do so anyway.
  */
-#define MAY_TRANSMUTE  64
+#define MAY_TRANSMUTE  0x00001000      /* Controls directory labeling */
+#define MAY_LOCK       0x00002000      /* Locks should be writes, but ... */
+
 /*
  * Just to make the common cases easier to deal with
  */
@@ -188,9 +192,9 @@ struct smk_port_label {
 #define MAY_NOT                0
 
 /*
- * Number of access types used by Smack (rwxat)
+ * Number of access types used by Smack (rwxatl)
  */
-#define SMK_NUM_ACCESS_TYPE 5
+#define SMK_NUM_ACCESS_TYPE 6
 
 /* SMACK data */
 struct smack_audit_data {
index b3b59b1..14293cd 100644 (file)
@@ -84,6 +84,8 @@ int log_policy = SMACK_AUDIT_DENIED;
  *
  * Do the object check first because that is more
  * likely to differ.
+ *
+ * Allowing write access implies allowing locking.
  */
 int smk_access_entry(char *subject_label, char *object_label,
                        struct list_head *rule_list)
@@ -99,6 +101,11 @@ int smk_access_entry(char *subject_label, char *object_label,
                }
        }
 
+       /*
+        * MAY_WRITE implies MAY_LOCK.
+        */
+       if ((may & MAY_WRITE) == MAY_WRITE)
+               may |= MAY_LOCK;
        return may;
 }
 
@@ -245,6 +252,7 @@ out_audit:
 static inline void smack_str_from_perm(char *string, int access)
 {
        int i = 0;
+
        if (access & MAY_READ)
                string[i++] = 'r';
        if (access & MAY_WRITE)
@@ -255,6 +263,8 @@ static inline void smack_str_from_perm(char *string, int access)
                string[i++] = 'a';
        if (access & MAY_TRANSMUTE)
                string[i++] = 't';
+       if (access & MAY_LOCK)
+               string[i++] = 'l';
        string[i] = '\0';
 }
 /**
index 8825375..b0be893 100644 (file)
@@ -185,7 +185,7 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
        smk_ad_setfield_u_tsk(&ad, ctp);
 
-       rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad);
+       rc = smk_curacc(skp->smk_known, mode, &ad);
        return rc;
 }
 
@@ -1146,7 +1146,7 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd,
  * @file: the object
  * @cmd: unused
  *
- * Returns 0 if current has write access, error code otherwise
+ * Returns 0 if current has lock access, error code otherwise
  */
 static int smack_file_lock(struct file *file, unsigned int cmd)
 {
@@ -1154,7 +1154,7 @@ static int smack_file_lock(struct file *file, unsigned int cmd)
 
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
        smk_ad_setfield_u_fs_path(&ad, file->f_path);
-       return smk_curacc(file->f_security, MAY_WRITE, &ad);
+       return smk_curacc(file->f_security, MAY_LOCK, &ad);
 }
 
 /**
@@ -1178,8 +1178,13 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
 
        switch (cmd) {
        case F_GETLK:
+               break;
        case F_SETLK:
        case F_SETLKW:
+               smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
+               smk_ad_setfield_u_fs_path(&ad, file->f_path);
+               rc = smk_curacc(file->f_security, MAY_LOCK, &ad);
+               break;
        case F_SETOWN:
        case F_SETSIG:
                smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
index 80f4b4a..160aa08 100644 (file)
@@ -139,7 +139,7 @@ const char *smack_cipso_option = SMACK_CIPSO_OPTION;
  * SMK_LOADLEN: Smack rule length
  */
 #define SMK_OACCESS    "rwxa"
-#define SMK_ACCESS     "rwxat"
+#define SMK_ACCESS     "rwxatl"
 #define SMK_OACCESSLEN (sizeof(SMK_OACCESS) - 1)
 #define SMK_ACCESSLEN  (sizeof(SMK_ACCESS) - 1)
 #define SMK_OLOADLEN   (SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN)
@@ -282,6 +282,10 @@ static int smk_perm_from_str(const char *string)
                case 'T':
                        perm |= MAY_TRANSMUTE;
                        break;
+               case 'l':
+               case 'L':
+                       perm |= MAY_LOCK;
+                       break;
                default:
                        return perm;
                }
@@ -452,7 +456,7 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
                /*
                 * Minor hack for backward compatibility
                 */
-               if (count != SMK_OLOADLEN && count != SMK_LOADLEN)
+               if (count < SMK_OLOADLEN || count > SMK_LOADLEN)
                        return -EINVAL;
        } else {
                if (count >= PAGE_SIZE) {
@@ -592,6 +596,8 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
                seq_putc(s, 'a');
        if (srp->smk_access & MAY_TRANSMUTE)
                seq_putc(s, 't');
+       if (srp->smk_access & MAY_LOCK)
+               seq_putc(s, 'l');
 
        seq_putc(s, '\n');
 }
index 61ab640..9dc5806 100644 (file)
@@ -644,7 +644,7 @@ static int n##_control_put(struct snd_kcontrol *kcontrol,           \
                           struct snd_ctl_elem_value *ucontrol)         \
 {                                                                      \
        struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol);        \
-       if (gpio->methods && gpio->methods->get_##n)                    \
+       if (gpio->methods && gpio->methods->set_##n)                    \
                gpio->methods->set_##n(gpio,                            \
                        !!ucontrol->value.integer.value[0]);            \
        return 1;                                                       \
@@ -1135,7 +1135,7 @@ static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
 {
        struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
 
-       if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
+       if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore)
                ldev->gpio.methods->all_amps_restore(&ldev->gpio);
 
        return 0;
index d9af638..9d518ac 100644 (file)
@@ -384,8 +384,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
                return -EFAULT;
 
        mutex_lock(&stream->device->lock);
-       if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED ||
-                       stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+       if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
                retval = -EBADFD;
                goto out;
        }
index b35fe73..8658578 100644 (file)
@@ -34,12 +34,12 @@ static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
        SW_LINEIN_INSERT,
 };
 
-static int snd_jack_dev_free(struct snd_device *device)
+static int snd_jack_dev_disconnect(struct snd_device *device)
 {
        struct snd_jack *jack = device->device_data;
 
-       if (jack->private_free)
-               jack->private_free(jack);
+       if (!jack->input_dev)
+               return 0;
 
        /* If the input device is registered with the input subsystem
         * then we need to use a different deallocator. */
@@ -47,6 +47,18 @@ static int snd_jack_dev_free(struct snd_device *device)
                input_unregister_device(jack->input_dev);
        else
                input_free_device(jack->input_dev);
+       jack->input_dev = NULL;
+       return 0;
+}
+
+static int snd_jack_dev_free(struct snd_device *device)
+{
+       struct snd_jack *jack = device->device_data;
+
+       if (jack->private_free)
+               jack->private_free(jack);
+
+       snd_jack_dev_disconnect(device);
 
        kfree(jack->id);
        kfree(jack);
@@ -110,6 +122,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
        static struct snd_device_ops ops = {
                .dev_free = snd_jack_dev_free,
                .dev_register = snd_jack_dev_register,
+               .dev_disconnect = snd_jack_dev_disconnect,
        };
 
        jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL);
index 9d93f02..5e1c7bc 100644 (file)
@@ -184,11 +184,7 @@ static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size)
        /* Assign the pool into private_data field */
        dmab->private_data = pool;
 
-       dmab->area = (void *)gen_pool_alloc(pool, size);
-       if (!dmab->area)
-               return;
-
-       dmab->addr = gen_pool_virt_to_phys(pool, (unsigned long)dmab->area);
+       dmab->area = gen_pool_dma_alloc(pool, size, &dmab->addr);
 }
 
 /**
index f664bae..328bd29 100644 (file)
@@ -188,8 +188,8 @@ static int pcsp_probe(struct platform_device *dev)
 static int pcsp_remove(struct platform_device *dev)
 {
        struct snd_pcsp *chip = platform_get_drvdata(dev);
-       alsa_card_pcsp_exit(chip);
        pcspkr_input_remove(chip->input_dev);
+       alsa_card_pcsp_exit(chip);
        return 0;
 }
 
index 6feee66..57bcd31 100644 (file)
@@ -543,7 +543,7 @@ static int dice_change_rate(struct dice *dice, unsigned int clock_rate)
        __be32 value;
        int err;
 
-       INIT_COMPLETION(dice->clock_accepted);
+       reinit_completion(&dice->clock_accepted);
 
        value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1);
        err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
index 81aeb93..0a90bd6 100644 (file)
 #ifdef MSND_CLASSIC
 #  include "msnd_classic.h"
 #  define LOGNAME                      "msnd_classic"
+#  define DEV_NAME                     "msnd-classic"
 #else
 #  include "msnd_pinnacle.h"
 #  define LOGNAME                      "snd_msnd_pinnacle"
+#  define DEV_NAME                     "msnd-pinnacle"
 #endif
 
 static void set_default_audio_parameters(struct snd_msnd *chip)
@@ -1067,8 +1069,6 @@ static int snd_msnd_isa_remove(struct device *pdev, unsigned int dev)
        return 0;
 }
 
-#define DEV_NAME "msnd-pinnacle"
-
 static struct isa_driver snd_msnd_driver = {
        .match          = snd_msnd_isa_match,
        .probe          = snd_msnd_isa_probe,
index a2f87f9..e5db001 100644 (file)
@@ -1196,7 +1196,7 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
        int num_samples;
        unsigned char *msample_hdr;
 
-       msample_hdr = kmalloc(sizeof(WF_MSAMPLE_BYTES), GFP_KERNEL);
+       msample_hdr = kmalloc(WF_MSAMPLE_BYTES, GFP_KERNEL);
        if (! msample_hdr)
                return -ENOMEM;
 
index dd5403d..afb90f4 100644 (file)
@@ -2579,9 +2579,6 @@ int snd_hda_codec_reset(struct hda_codec *codec)
        cancel_delayed_work_sync(&codec->jackpoll_work);
 #ifdef CONFIG_PM
        cancel_delayed_work_sync(&codec->power_work);
-       codec->power_on = 0;
-       codec->power_transition = 0;
-       codec->power_jiffies = jiffies;
        flush_workqueue(bus->workq);
 #endif
        snd_hda_ctls_clear(codec);
index 32d3e38..79ca80f 100644 (file)
@@ -680,7 +680,7 @@ int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
 
        spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0);
 
-       if (!spkalloc) {
+       if (spkalloc <= 0) {
                snd_printd(KERN_INFO "HDMI ATI/AMD: no speaker allocation for ELD\n");
                return -EINVAL;
        }
@@ -742,6 +742,9 @@ int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
                snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3);
                ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0);
 
+               if (ati_sad <= 0)
+                       continue;
+
                if (ati_sad & ATI_AUDIODESC_RATES) {
                        /* format is supported, copy SAD as-is */
                        buf[pos++] = (ati_sad & 0x0000ff) >> 0;
@@ -765,21 +768,39 @@ int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
                return -EINVAL;
        }
 
+       /*
+        * HDMI VSDB latency format:
+        * separately for both audio and video:
+        *  0          field not valid or unknown latency
+        *  [1..251]   msecs = (x-1)*2  (max 500ms with x = 251 = 0xfb)
+        *  255        audio/video not supported
+        *
+        * HDA latency format:
+        * single value indicating video latency relative to audio:
+        *  0          unknown or 0ms
+        *  [1..250]   msecs = x*2  (max 500ms with x = 250 = 0xfa)
+        *  [251..255] reserved
+        */
        aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0);
        if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) {
-               int video_latency = (aud_synch & ATI_DELAY_VIDEO_LATENCY) - 1;
-               int audio_latency = ((aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8) - 1;
+               int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY);
+               int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8;
 
-               if (video_latency > audio_latency)
-                       buf[6] = min(video_latency - audio_latency, 0xfa);
+               if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb &&
+                   video_latency_hdmi > audio_latency_hdmi)
+                       buf[6] = video_latency_hdmi - audio_latency_hdmi;
+               /* else unknown/invalid or 0ms or video ahead of audio, so use zero */
        }
 
-       /* Baseline length */
-       buf[2] = pos - 4;
-
        /* SAD count */
        buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4;
 
+       /* Baseline ELD block length is 4-byte aligned */
+       pos = round_up(pos, 4);
+
+       /* Baseline ELD length (4-byte header is not counted in) */
+       buf[2] = (pos - 4) / 4;
+
        *eld_size = pos;
 
        return 0;
index 276f6e7..3067ed4 100644 (file)
@@ -796,10 +796,10 @@ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
        if (spec->own_eapd_ctl ||
            !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
                return;
-       if (codec->inv_eapd)
-               enable = !enable;
        if (spec->keep_eapd_on && !enable)
                return;
+       if (codec->inv_eapd)
+               enable = !enable;
        snd_hda_codec_update_cache(codec, pin, 0,
                                   AC_VERB_SET_EAPD_BTLENABLE,
                                   enable ? 0x02 : 0x00);
index 87d2e03..1a83559 100644 (file)
@@ -139,6 +139,18 @@ static int ad198x_suspend(struct hda_codec *codec)
 }
 #endif
 
+/* follow EAPD via vmaster hook */
+static void ad_vmaster_eapd_hook(void *private_data, int enabled)
+{
+       struct hda_codec *codec = private_data;
+       struct ad198x_spec *spec = codec->spec;
+
+       if (!spec->eapd_nid)
+               return;
+       snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
+                                  AC_VERB_SET_EAPD_BTLENABLE,
+                                  enabled ? 0x02 : 0x00);
+}
 
 /*
  * Automatic parse of I/O pins from the BIOS configuration
@@ -219,8 +231,14 @@ static int alloc_ad_spec(struct hda_codec *codec)
 static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
                                     const struct hda_fixup *fix, int action)
 {
-       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+       struct ad198x_spec *spec = codec->spec;
+
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
                codec->inv_jack_detect = 1;
+               spec->gen.keep_eapd_on = 1;
+               spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+               spec->eapd_nid = 0x1b;
+       }
 }
 
 enum {
@@ -465,19 +483,6 @@ static int patch_ad1983(struct hda_codec *codec)
  * AD1981 HD specific
  */
 
-/* follow EAPD via vmaster hook */
-static void ad_vmaster_eapd_hook(void *private_data, int enabled)
-{
-       struct hda_codec *codec = private_data;
-       struct ad198x_spec *spec = codec->spec;
-
-       if (!spec->eapd_nid)
-               return;
-       snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
-                                  AC_VERB_SET_EAPD_BTLENABLE,
-                                  enabled ? 0x02 : 0x00);
-}
-
 static void ad1981_fixup_hp_eapd(struct hda_codec *codec,
                                 const struct hda_fixup *fix, int action)
 {
index 072755c..fc492ac 100644 (file)
@@ -47,6 +47,10 @@ struct cs_spec {
        unsigned int spdif_present:1;
        unsigned int sense_b:1;
        hda_nid_t vendor_nid;
+
+       /* for MBP SPDIF control */
+       int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol);
 };
 
 /* available models with CS420x */
@@ -331,10 +335,21 @@ static int cs_init(struct hda_codec *codec)
        return 0;
 }
 
+static int cs_build_controls(struct hda_codec *codec)
+{
+       int err;
+
+       err = snd_hda_gen_build_controls(codec);
+       if (err < 0)
+               return err;
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
+       return 0;
+}
+
 #define cs_free                snd_hda_gen_free
 
 static const struct hda_codec_ops cs_patch_ops = {
-       .build_controls = snd_hda_gen_build_controls,
+       .build_controls = cs_build_controls,
        .build_pcms = snd_hda_gen_build_pcms,
        .init = cs_init,
        .free = cs_free,
@@ -599,12 +614,14 @@ static int patch_cs420x(struct hda_codec *codec)
 enum {
        CS4208_MAC_AUTO,
        CS4208_MBA6,
+       CS4208_MBP11,
        CS4208_GPIO0,
 };
 
 static const struct hda_model_fixup cs4208_models[] = {
        { .id = CS4208_GPIO0, .name = "gpio0" },
        { .id = CS4208_MBA6, .name = "mba6" },
+       { .id = CS4208_MBP11, .name = "mbp11" },
        {}
 };
 
@@ -615,6 +632,7 @@ static const struct snd_pci_quirk cs4208_fixup_tbl[] = {
 
 /* codec SSID matching */
 static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11),
        SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
        SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
        {} /* terminator */
@@ -646,6 +664,36 @@ static void cs4208_fixup_mac(struct hda_codec *codec,
        snd_hda_apply_fixup(codec, action);
 }
 
+static int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct cs_spec *spec = codec->spec;
+       hda_nid_t pin = spec->gen.autocfg.dig_out_pins[0];
+       int pinctl = ucontrol->value.integer.value[0] ? PIN_OUT : 0;
+
+       snd_hda_set_pin_ctl_cache(codec, pin, pinctl);
+       return spec->spdif_sw_put(kcontrol, ucontrol);
+}
+
+/* hook the SPDIF switch */
+static void cs4208_fixup_spdif_switch(struct hda_codec *codec,
+                                     const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_BUILD) {
+               struct cs_spec *spec = codec->spec;
+               struct snd_kcontrol *kctl;
+
+               if (!spec->gen.autocfg.dig_out_pins[0])
+                       return;
+               kctl = snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch");
+               if (!kctl)
+                       return;
+               spec->spdif_sw_put = kctl->put;
+               kctl->put = cs4208_spdif_sw_put;
+       }
+}
+
 static const struct hda_fixup cs4208_fixups[] = {
        [CS4208_MBA6] = {
                .type = HDA_FIXUP_PINS,
@@ -653,6 +701,12 @@ static const struct hda_fixup cs4208_fixups[] = {
                .chained = true,
                .chain_id = CS4208_GPIO0,
        },
+       [CS4208_MBP11] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cs4208_fixup_spdif_switch,
+               .chained = true,
+               .chain_id = CS4208_GPIO0,
+       },
        [CS4208_GPIO0] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = cs4208_fixup_gpio0,
index e687923..08407be 100644 (file)
@@ -763,12 +763,12 @@ static struct channel_map_table map_tables[] = {
        { SNDRV_CHMAP_RC,       RC },
        { SNDRV_CHMAP_FLC,      FLC },
        { SNDRV_CHMAP_FRC,      FRC },
-       { SNDRV_CHMAP_FLH,      FLH },
-       { SNDRV_CHMAP_FRH,      FRH },
+       { SNDRV_CHMAP_TFL,      FLH },
+       { SNDRV_CHMAP_TFR,      FRH },
        { SNDRV_CHMAP_FLW,      FLW },
        { SNDRV_CHMAP_FRW,      FRW },
        { SNDRV_CHMAP_TC,       TC },
-       { SNDRV_CHMAP_FCH,      FCH },
+       { SNDRV_CHMAP_TFC,      FCH },
        {} /* terminator */
 };
 
@@ -1247,6 +1247,9 @@ static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
                pinctl = snd_hda_codec_read(codec, pin_nid, 0,
                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 
+               if (pinctl < 0)
+                       return hbr ? -EINVAL : 0;
+
                new_pinctl = pinctl & ~AC_PINCTL_EPT;
                if (hbr)
                        new_pinctl |= AC_PINCTL_EPT_HBR;
@@ -3091,7 +3094,7 @@ static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
        int hbr_ctl, hbr_ctl_new;
 
        hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0);
-       if (hbr_ctl & ATI_HBR_CAPABLE) {
+       if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) {
                if (hbr)
                        hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE;
                else
index 24d924d..04d1e6b 100644 (file)
@@ -3344,8 +3344,10 @@ static void alc_update_headset_mode(struct hda_codec *codec)
        else
                new_headset_mode = ALC_HEADSET_MODE_HEADPHONE;
 
-       if (new_headset_mode == spec->current_headset_mode)
+       if (new_headset_mode == spec->current_headset_mode) {
+               snd_hda_gen_update_outputs(codec);
                return;
+       }
 
        switch (new_headset_mode) {
        case ALC_HEADSET_MODE_UNPLUGGED:
index 69a549a..d2cc004 100644 (file)
@@ -100,6 +100,7 @@ enum {
        STAC_92HD83XXX_HEADSET_JACK,
        STAC_92HD83XXX_HP,
        STAC_HP_ENVY_BASS,
+       STAC_HP_BNB13_EQ,
        STAC_92HD83XXX_MODELS
 };
 
@@ -2106,6 +2107,434 @@ static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec,
                spec->headset_jack = 1;
 }
 
+static const struct hda_verb hp_bnb13_eq_verbs[] = {
+       /* 44.1KHz base */
+       { 0x22, 0x7A6, 0x3E },
+       { 0x22, 0x7A7, 0x68 },
+       { 0x22, 0x7A8, 0x17 },
+       { 0x22, 0x7A9, 0x3E },
+       { 0x22, 0x7AA, 0x68 },
+       { 0x22, 0x7AB, 0x17 },
+       { 0x22, 0x7AC, 0x00 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x83 },
+       { 0x22, 0x7A7, 0x2F },
+       { 0x22, 0x7A8, 0xD1 },
+       { 0x22, 0x7A9, 0x83 },
+       { 0x22, 0x7AA, 0x2F },
+       { 0x22, 0x7AB, 0xD1 },
+       { 0x22, 0x7AC, 0x01 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x3E },
+       { 0x22, 0x7A7, 0x68 },
+       { 0x22, 0x7A8, 0x17 },
+       { 0x22, 0x7A9, 0x3E },
+       { 0x22, 0x7AA, 0x68 },
+       { 0x22, 0x7AB, 0x17 },
+       { 0x22, 0x7AC, 0x02 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x7C },
+       { 0x22, 0x7A7, 0xC6 },
+       { 0x22, 0x7A8, 0x0C },
+       { 0x22, 0x7A9, 0x7C },
+       { 0x22, 0x7AA, 0xC6 },
+       { 0x22, 0x7AB, 0x0C },
+       { 0x22, 0x7AC, 0x03 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0xC3 },
+       { 0x22, 0x7A7, 0x25 },
+       { 0x22, 0x7A8, 0xAF },
+       { 0x22, 0x7A9, 0xC3 },
+       { 0x22, 0x7AA, 0x25 },
+       { 0x22, 0x7AB, 0xAF },
+       { 0x22, 0x7AC, 0x04 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x3E },
+       { 0x22, 0x7A7, 0x85 },
+       { 0x22, 0x7A8, 0x73 },
+       { 0x22, 0x7A9, 0x3E },
+       { 0x22, 0x7AA, 0x85 },
+       { 0x22, 0x7AB, 0x73 },
+       { 0x22, 0x7AC, 0x05 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x85 },
+       { 0x22, 0x7A7, 0x39 },
+       { 0x22, 0x7A8, 0xC7 },
+       { 0x22, 0x7A9, 0x85 },
+       { 0x22, 0x7AA, 0x39 },
+       { 0x22, 0x7AB, 0xC7 },
+       { 0x22, 0x7AC, 0x06 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x3C },
+       { 0x22, 0x7A7, 0x90 },
+       { 0x22, 0x7A8, 0xB0 },
+       { 0x22, 0x7A9, 0x3C },
+       { 0x22, 0x7AA, 0x90 },
+       { 0x22, 0x7AB, 0xB0 },
+       { 0x22, 0x7AC, 0x07 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x7A },
+       { 0x22, 0x7A7, 0xC6 },
+       { 0x22, 0x7A8, 0x39 },
+       { 0x22, 0x7A9, 0x7A },
+       { 0x22, 0x7AA, 0xC6 },
+       { 0x22, 0x7AB, 0x39 },
+       { 0x22, 0x7AC, 0x08 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0xC4 },
+       { 0x22, 0x7A7, 0xE9 },
+       { 0x22, 0x7A8, 0xDC },
+       { 0x22, 0x7A9, 0xC4 },
+       { 0x22, 0x7AA, 0xE9 },
+       { 0x22, 0x7AB, 0xDC },
+       { 0x22, 0x7AC, 0x09 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x3D },
+       { 0x22, 0x7A7, 0xE1 },
+       { 0x22, 0x7A8, 0x0D },
+       { 0x22, 0x7A9, 0x3D },
+       { 0x22, 0x7AA, 0xE1 },
+       { 0x22, 0x7AB, 0x0D },
+       { 0x22, 0x7AC, 0x0A },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x89 },
+       { 0x22, 0x7A7, 0xB6 },
+       { 0x22, 0x7A8, 0xEB },
+       { 0x22, 0x7A9, 0x89 },
+       { 0x22, 0x7AA, 0xB6 },
+       { 0x22, 0x7AB, 0xEB },
+       { 0x22, 0x7AC, 0x0B },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x39 },
+       { 0x22, 0x7A7, 0x9D },
+       { 0x22, 0x7A8, 0xFE },
+       { 0x22, 0x7A9, 0x39 },
+       { 0x22, 0x7AA, 0x9D },
+       { 0x22, 0x7AB, 0xFE },
+       { 0x22, 0x7AC, 0x0C },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x76 },
+       { 0x22, 0x7A7, 0x49 },
+       { 0x22, 0x7A8, 0x15 },
+       { 0x22, 0x7A9, 0x76 },
+       { 0x22, 0x7AA, 0x49 },
+       { 0x22, 0x7AB, 0x15 },
+       { 0x22, 0x7AC, 0x0D },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0xC8 },
+       { 0x22, 0x7A7, 0x80 },
+       { 0x22, 0x7A8, 0xF5 },
+       { 0x22, 0x7A9, 0xC8 },
+       { 0x22, 0x7AA, 0x80 },
+       { 0x22, 0x7AB, 0xF5 },
+       { 0x22, 0x7AC, 0x0E },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x40 },
+       { 0x22, 0x7A7, 0x00 },
+       { 0x22, 0x7A8, 0x00 },
+       { 0x22, 0x7A9, 0x40 },
+       { 0x22, 0x7AA, 0x00 },
+       { 0x22, 0x7AB, 0x00 },
+       { 0x22, 0x7AC, 0x0F },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x90 },
+       { 0x22, 0x7A7, 0x68 },
+       { 0x22, 0x7A8, 0xF1 },
+       { 0x22, 0x7A9, 0x90 },
+       { 0x22, 0x7AA, 0x68 },
+       { 0x22, 0x7AB, 0xF1 },
+       { 0x22, 0x7AC, 0x10 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x34 },
+       { 0x22, 0x7A7, 0x47 },
+       { 0x22, 0x7A8, 0x6C },
+       { 0x22, 0x7A9, 0x34 },
+       { 0x22, 0x7AA, 0x47 },
+       { 0x22, 0x7AB, 0x6C },
+       { 0x22, 0x7AC, 0x11 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x6F },
+       { 0x22, 0x7A7, 0x97 },
+       { 0x22, 0x7A8, 0x0F },
+       { 0x22, 0x7A9, 0x6F },
+       { 0x22, 0x7AA, 0x97 },
+       { 0x22, 0x7AB, 0x0F },
+       { 0x22, 0x7AC, 0x12 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0xCB },
+       { 0x22, 0x7A7, 0xB8 },
+       { 0x22, 0x7A8, 0x94 },
+       { 0x22, 0x7A9, 0xCB },
+       { 0x22, 0x7AA, 0xB8 },
+       { 0x22, 0x7AB, 0x94 },
+       { 0x22, 0x7AC, 0x13 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x40 },
+       { 0x22, 0x7A7, 0x00 },
+       { 0x22, 0x7A8, 0x00 },
+       { 0x22, 0x7A9, 0x40 },
+       { 0x22, 0x7AA, 0x00 },
+       { 0x22, 0x7AB, 0x00 },
+       { 0x22, 0x7AC, 0x14 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x95 },
+       { 0x22, 0x7A7, 0x76 },
+       { 0x22, 0x7A8, 0x5B },
+       { 0x22, 0x7A9, 0x95 },
+       { 0x22, 0x7AA, 0x76 },
+       { 0x22, 0x7AB, 0x5B },
+       { 0x22, 0x7AC, 0x15 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x31 },
+       { 0x22, 0x7A7, 0xAC },
+       { 0x22, 0x7A8, 0x31 },
+       { 0x22, 0x7A9, 0x31 },
+       { 0x22, 0x7AA, 0xAC },
+       { 0x22, 0x7AB, 0x31 },
+       { 0x22, 0x7AC, 0x16 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x6A },
+       { 0x22, 0x7A7, 0x89 },
+       { 0x22, 0x7A8, 0xA5 },
+       { 0x22, 0x7A9, 0x6A },
+       { 0x22, 0x7AA, 0x89 },
+       { 0x22, 0x7AB, 0xA5 },
+       { 0x22, 0x7AC, 0x17 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0xCE },
+       { 0x22, 0x7A7, 0x53 },
+       { 0x22, 0x7A8, 0xCF },
+       { 0x22, 0x7A9, 0xCE },
+       { 0x22, 0x7AA, 0x53 },
+       { 0x22, 0x7AB, 0xCF },
+       { 0x22, 0x7AC, 0x18 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x40 },
+       { 0x22, 0x7A7, 0x00 },
+       { 0x22, 0x7A8, 0x00 },
+       { 0x22, 0x7A9, 0x40 },
+       { 0x22, 0x7AA, 0x00 },
+       { 0x22, 0x7AB, 0x00 },
+       { 0x22, 0x7AC, 0x19 },
+       { 0x22, 0x7AD, 0x80 },
+       /* 48KHz base */
+       { 0x22, 0x7A6, 0x3E },
+       { 0x22, 0x7A7, 0x88 },
+       { 0x22, 0x7A8, 0xDC },
+       { 0x22, 0x7A9, 0x3E },
+       { 0x22, 0x7AA, 0x88 },
+       { 0x22, 0x7AB, 0xDC },
+       { 0x22, 0x7AC, 0x1A },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x82 },
+       { 0x22, 0x7A7, 0xEE },
+       { 0x22, 0x7A8, 0x46 },
+       { 0x22, 0x7A9, 0x82 },
+       { 0x22, 0x7AA, 0xEE },
+       { 0x22, 0x7AB, 0x46 },
+       { 0x22, 0x7AC, 0x1B },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x3E },
+       { 0x22, 0x7A7, 0x88 },
+       { 0x22, 0x7A8, 0xDC },
+       { 0x22, 0x7A9, 0x3E },
+       { 0x22, 0x7AA, 0x88 },
+       { 0x22, 0x7AB, 0xDC },
+       { 0x22, 0x7AC, 0x1C },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x7D },
+       { 0x22, 0x7A7, 0x09 },
+       { 0x22, 0x7A8, 0x28 },
+       { 0x22, 0x7A9, 0x7D },
+       { 0x22, 0x7AA, 0x09 },
+       { 0x22, 0x7AB, 0x28 },
+       { 0x22, 0x7AC, 0x1D },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0xC2 },
+       { 0x22, 0x7A7, 0xE5 },
+       { 0x22, 0x7A8, 0xB4 },
+       { 0x22, 0x7A9, 0xC2 },
+       { 0x22, 0x7AA, 0xE5 },
+       { 0x22, 0x7AB, 0xB4 },
+       { 0x22, 0x7AC, 0x1E },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x3E },
+       { 0x22, 0x7A7, 0xA3 },
+       { 0x22, 0x7A8, 0x1F },
+       { 0x22, 0x7A9, 0x3E },
+       { 0x22, 0x7AA, 0xA3 },
+       { 0x22, 0x7AB, 0x1F },
+       { 0x22, 0x7AC, 0x1F },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x84 },
+       { 0x22, 0x7A7, 0xCA },
+       { 0x22, 0x7A8, 0xF1 },
+       { 0x22, 0x7A9, 0x84 },
+       { 0x22, 0x7AA, 0xCA },
+       { 0x22, 0x7AB, 0xF1 },
+       { 0x22, 0x7AC, 0x20 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x3C },
+       { 0x22, 0x7A7, 0xD5 },
+       { 0x22, 0x7A8, 0x9C },
+       { 0x22, 0x7A9, 0x3C },
+       { 0x22, 0x7AA, 0xD5 },
+       { 0x22, 0x7AB, 0x9C },
+       { 0x22, 0x7AC, 0x21 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x7B },
+       { 0x22, 0x7A7, 0x35 },
+       { 0x22, 0x7A8, 0x0F },
+       { 0x22, 0x7A9, 0x7B },
+       { 0x22, 0x7AA, 0x35 },
+       { 0x22, 0x7AB, 0x0F },
+       { 0x22, 0x7AC, 0x22 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0xC4 },
+       { 0x22, 0x7A7, 0x87 },
+       { 0x22, 0x7A8, 0x45 },
+       { 0x22, 0x7A9, 0xC4 },
+       { 0x22, 0x7AA, 0x87 },
+       { 0x22, 0x7AB, 0x45 },
+       { 0x22, 0x7AC, 0x23 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x3E },
+       { 0x22, 0x7A7, 0x0A },
+       { 0x22, 0x7A8, 0x78 },
+       { 0x22, 0x7A9, 0x3E },
+       { 0x22, 0x7AA, 0x0A },
+       { 0x22, 0x7AB, 0x78 },
+       { 0x22, 0x7AC, 0x24 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x88 },
+       { 0x22, 0x7A7, 0xE2 },
+       { 0x22, 0x7A8, 0x05 },
+       { 0x22, 0x7A9, 0x88 },
+       { 0x22, 0x7AA, 0xE2 },
+       { 0x22, 0x7AB, 0x05 },
+       { 0x22, 0x7AC, 0x25 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x3A },
+       { 0x22, 0x7A7, 0x1A },
+       { 0x22, 0x7A8, 0xA3 },
+       { 0x22, 0x7A9, 0x3A },
+       { 0x22, 0x7AA, 0x1A },
+       { 0x22, 0x7AB, 0xA3 },
+       { 0x22, 0x7AC, 0x26 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x77 },
+       { 0x22, 0x7A7, 0x1D },
+       { 0x22, 0x7A8, 0xFB },
+       { 0x22, 0x7A9, 0x77 },
+       { 0x22, 0x7AA, 0x1D },
+       { 0x22, 0x7AB, 0xFB },
+       { 0x22, 0x7AC, 0x27 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0xC7 },
+       { 0x22, 0x7A7, 0xDA },
+       { 0x22, 0x7A8, 0xE5 },
+       { 0x22, 0x7A9, 0xC7 },
+       { 0x22, 0x7AA, 0xDA },
+       { 0x22, 0x7AB, 0xE5 },
+       { 0x22, 0x7AC, 0x28 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x40 },
+       { 0x22, 0x7A7, 0x00 },
+       { 0x22, 0x7A8, 0x00 },
+       { 0x22, 0x7A9, 0x40 },
+       { 0x22, 0x7AA, 0x00 },
+       { 0x22, 0x7AB, 0x00 },
+       { 0x22, 0x7AC, 0x29 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x8E },
+       { 0x22, 0x7A7, 0xD7 },
+       { 0x22, 0x7A8, 0x22 },
+       { 0x22, 0x7A9, 0x8E },
+       { 0x22, 0x7AA, 0xD7 },
+       { 0x22, 0x7AB, 0x22 },
+       { 0x22, 0x7AC, 0x2A },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x35 },
+       { 0x22, 0x7A7, 0x26 },
+       { 0x22, 0x7A8, 0xC6 },
+       { 0x22, 0x7A9, 0x35 },
+       { 0x22, 0x7AA, 0x26 },
+       { 0x22, 0x7AB, 0xC6 },
+       { 0x22, 0x7AC, 0x2B },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x71 },
+       { 0x22, 0x7A7, 0x28 },
+       { 0x22, 0x7A8, 0xDE },
+       { 0x22, 0x7A9, 0x71 },
+       { 0x22, 0x7AA, 0x28 },
+       { 0x22, 0x7AB, 0xDE },
+       { 0x22, 0x7AC, 0x2C },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0xCA },
+       { 0x22, 0x7A7, 0xD9 },
+       { 0x22, 0x7A8, 0x3A },
+       { 0x22, 0x7A9, 0xCA },
+       { 0x22, 0x7AA, 0xD9 },
+       { 0x22, 0x7AB, 0x3A },
+       { 0x22, 0x7AC, 0x2D },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x40 },
+       { 0x22, 0x7A7, 0x00 },
+       { 0x22, 0x7A8, 0x00 },
+       { 0x22, 0x7A9, 0x40 },
+       { 0x22, 0x7AA, 0x00 },
+       { 0x22, 0x7AB, 0x00 },
+       { 0x22, 0x7AC, 0x2E },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x93 },
+       { 0x22, 0x7A7, 0x5E },
+       { 0x22, 0x7A8, 0xD8 },
+       { 0x22, 0x7A9, 0x93 },
+       { 0x22, 0x7AA, 0x5E },
+       { 0x22, 0x7AB, 0xD8 },
+       { 0x22, 0x7AC, 0x2F },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x32 },
+       { 0x22, 0x7A7, 0xB7 },
+       { 0x22, 0x7A8, 0xB1 },
+       { 0x22, 0x7A9, 0x32 },
+       { 0x22, 0x7AA, 0xB7 },
+       { 0x22, 0x7AB, 0xB1 },
+       { 0x22, 0x7AC, 0x30 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x6C },
+       { 0x22, 0x7A7, 0xA1 },
+       { 0x22, 0x7A8, 0x28 },
+       { 0x22, 0x7A9, 0x6C },
+       { 0x22, 0x7AA, 0xA1 },
+       { 0x22, 0x7AB, 0x28 },
+       { 0x22, 0x7AC, 0x31 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0xCD },
+       { 0x22, 0x7A7, 0x48 },
+       { 0x22, 0x7A8, 0x4F },
+       { 0x22, 0x7A9, 0xCD },
+       { 0x22, 0x7AA, 0x48 },
+       { 0x22, 0x7AB, 0x4F },
+       { 0x22, 0x7AC, 0x32 },
+       { 0x22, 0x7AD, 0x80 },
+       { 0x22, 0x7A6, 0x40 },
+       { 0x22, 0x7A7, 0x00 },
+       { 0x22, 0x7A8, 0x00 },
+       { 0x22, 0x7A9, 0x40 },
+       { 0x22, 0x7AA, 0x00 },
+       { 0x22, 0x7AB, 0x00 },
+       { 0x22, 0x7AC, 0x33 },
+       { 0x22, 0x7AD, 0x80 },
+       /* common */
+       { 0x22, 0x782, 0xC1 },
+       { 0x22, 0x771, 0x2C },
+       { 0x22, 0x772, 0x2C },
+       { 0x22, 0x788, 0x04 },
+       { 0x01, 0x7B0, 0x08 },
+       {}
+};
+
 static const struct hda_fixup stac92hd83xxx_fixups[] = {
        [STAC_92HD83XXX_REF] = {
                .type = HDA_FIXUP_PINS,
@@ -2174,6 +2603,12 @@ static const struct hda_fixup stac92hd83xxx_fixups[] = {
                        {}
                },
        },
+       [STAC_HP_BNB13_EQ] = {
+               .type = HDA_FIXUP_VERBS,
+               .v.verbs = hp_bnb13_eq_verbs,
+               .chained = true,
+               .chain_id = STAC_92HD83XXX_HP_MIC_LED,
+       },
 };
 
 static const struct hda_model_fixup stac92hd83xxx_models[] = {
@@ -2189,6 +2624,7 @@ static const struct hda_model_fixup stac92hd83xxx_models[] = {
        { .id = STAC_92HD83XXX_HP_MIC_LED, .name = "hp-mic-led" },
        { .id = STAC_92HD83XXX_HEADSET_JACK, .name = "headset-jack" },
        { .id = STAC_HP_ENVY_BASS, .name = "hp-envy-bass" },
+       { .id = STAC_HP_BNB13_EQ, .name = "hp-bnb13-eq" },
        {}
 };
 
@@ -2235,7 +2671,101 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1888,
                          "HP Envy Spectre", STAC_HP_ENVY_BASS),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df,
-                         "HP Folio", STAC_92HD83XXX_HP_MIC_LED),
+                         "HP Folio", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18F8,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1909,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x190A,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1940,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1941,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1942,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1943,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1944,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1945,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1946,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1948,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1949,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194A,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194B,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194C,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194E,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194F,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1950,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1951,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195A,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195B,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195C,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1991,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2103,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2104,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2105,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2106,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2107,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2108,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2109,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x210A,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x210B,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211C,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211D,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211E,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211F,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2120,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2121,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2122,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2123,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x213E,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x213F,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2140,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B2,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B3,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B5,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B6,
+                         "HP bNB13", STAC_HP_BNB13_EQ),
        SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x1900,
                          "HP", STAC_92HD83XXX_HP_MIC_LED),
        SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2000,
index 01aecc2..0d1c27e 100644 (file)
@@ -65,7 +65,7 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
         * already bound. If not it means binding failed, and then there
         * is no point in keeping the device instantiated.
         */
-       if (!keywest_ctx->client->driver) {
+       if (!keywest_ctx->client->dev.driver) {
                i2c_unregister_device(keywest_ctx->client);
                keywest_ctx->client = NULL;
                return -ENODEV;
@@ -76,7 +76,7 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
         * This is safe because i2c-core holds the core_lock mutex for us.
         */
        list_add_tail(&keywest_ctx->client->detected,
-                     &keywest_ctx->client->driver->clients);
+                     &to_i2c_driver(keywest_ctx->client->dev.driver)->clients);
        return 0;
 }
 
index 9a174fc..39d7748 100644 (file)
@@ -121,6 +121,7 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
                bf5xx_i2s->tcr2 |= 7;
                bf5xx_i2s->rcr2 |= 7;
                sport_handle->wdsize = 1;
+               break;
        case SNDRV_PCM_FORMAT_S16_LE:
                bf5xx_i2s->tcr2 |= 15;
                bf5xx_i2s->rcr2 |= 15;
index f2e62e4..19e9f22 100644 (file)
@@ -614,7 +614,7 @@ struct _pll_div {
 };
 
 /* Note : pll code from original alc5632 driver. Not sure of how good it is */
-/* usefull only for master mode */
+/* useful only for master mode */
 static const struct _pll_div codec_master_pll_div[] = {
 
        {  2048000,  8192000,   0x0ea0},
index 1a9412d..6fb8f00 100644 (file)
 #define CS42L52_MICB_CTL                       0x11
 #define        CS42L52_MIC_CTL_MIC_SEL_MASK            0xBF
 #define        CS42L52_MIC_CTL_MIC_SEL_SHIFT           6
-#define CS42L52_MIC_CTL_TYPE_MASK              0xDF
+#define CS42L52_MIC_CTL_TYPE_MASK              0x20
 #define CS42L52_MIC_CTL_TYPE_SHIFT             5
 
 
index 8bbddc1..a08e8bf 100644 (file)
@@ -685,13 +685,13 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
 
-SND_SOC_BYTES_MASK("EQ1 Coefficeints", ARIZONA_EQ1_1, 21,
+SND_SOC_BYTES_MASK("EQ1 Coefficients", ARIZONA_EQ1_1, 21,
                   ARIZONA_EQ1_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ2 Coefficeints", ARIZONA_EQ2_1, 21,
+SND_SOC_BYTES_MASK("EQ2 Coefficients", ARIZONA_EQ2_1, 21,
                   ARIZONA_EQ2_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ3 Coefficeints", ARIZONA_EQ3_1, 21,
+SND_SOC_BYTES_MASK("EQ3 Coefficients", ARIZONA_EQ3_1, 21,
                   ARIZONA_EQ3_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ4 Coefficeints", ARIZONA_EQ4_1, 21,
+SND_SOC_BYTES_MASK("EQ4 Coefficients", ARIZONA_EQ4_1, 21,
                   ARIZONA_EQ4_ENA_MASK),
 
 SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
index 8c91be5..f2d1094 100644 (file)
@@ -101,13 +101,13 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
 
-SND_SOC_BYTES_MASK("EQ1 Coefficeints", ARIZONA_EQ1_1, 21,
+SND_SOC_BYTES_MASK("EQ1 Coefficients", ARIZONA_EQ1_1, 21,
                   ARIZONA_EQ1_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ2 Coefficeints", ARIZONA_EQ2_1, 21,
+SND_SOC_BYTES_MASK("EQ2 Coefficients", ARIZONA_EQ2_1, 21,
                   ARIZONA_EQ2_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ3 Coefficeints", ARIZONA_EQ3_1, 21,
+SND_SOC_BYTES_MASK("EQ3 Coefficients", ARIZONA_EQ3_1, 21,
                   ARIZONA_EQ3_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ4 Coefficeints", ARIZONA_EQ4_1, 21,
+SND_SOC_BYTES_MASK("EQ4 Coefficients", ARIZONA_EQ4_1, 21,
                   ARIZONA_EQ4_ENA_MASK),
 
 SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
index ac1ff99..543c5c2 100644 (file)
@@ -3728,6 +3728,8 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
        if (ret < 0)
                goto err_enable;
 
+       regcache_cache_only(wm8962->regmap, true);
+
        /* The drivers should power up as needed */
        regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies);
 
index 6ec3de3..1392bb3 100644 (file)
@@ -170,13 +170,13 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
 
-SND_SOC_BYTES_MASK("EQ1 Coefficeints", ARIZONA_EQ1_1, 21,
+SND_SOC_BYTES_MASK("EQ1 Coefficients", ARIZONA_EQ1_1, 21,
                   ARIZONA_EQ1_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ2 Coefficeints", ARIZONA_EQ2_1, 21,
+SND_SOC_BYTES_MASK("EQ2 Coefficients", ARIZONA_EQ2_1, 21,
                   ARIZONA_EQ2_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ3 Coefficeints", ARIZONA_EQ3_1, 21,
+SND_SOC_BYTES_MASK("EQ3 Coefficients", ARIZONA_EQ3_1, 21,
                   ARIZONA_EQ3_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ4 Coefficeints", ARIZONA_EQ4_1, 21,
+SND_SOC_BYTES_MASK("EQ4 Coefficients", ARIZONA_EQ4_1, 21,
                   ARIZONA_EQ4_ENA_MASK),
 
 SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
@@ -887,7 +887,7 @@ static const struct snd_soc_dapm_route wm8997_dapm_routes[] = {
        ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
 
        ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
-       ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC2INT2"),
+       ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
 
        ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
        ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
index fa64cd8..fb5d107 100644 (file)
@@ -238,7 +238,7 @@ static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
        print_buf_info(prtd->ram_channel, "i ram_channel");
        pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status);
 
-       if (unlikely(ch_status != DMA_COMPLETE))
+       if (unlikely(ch_status != EDMA_DMA_COMPLETE))
                return;
 
        if (snd_pcm_running(substream)) {
index 41740e4..c75d43b 100644 (file)
@@ -42,7 +42,8 @@ struct imx_pcm_runtime_data {
        struct hrtimer hrt;
        int poll_time_ns;
        struct snd_pcm_substream *substream;
-       atomic_t running;
+       atomic_t playing;
+       atomic_t capturing;
 };
 
 static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
@@ -52,7 +53,7 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
        struct snd_pcm_substream *substream = iprtd->substream;
        struct pt_regs regs;
 
-       if (!atomic_read(&iprtd->running))
+       if (!atomic_read(&iprtd->playing) && !atomic_read(&iprtd->capturing))
                return HRTIMER_NORESTART;
 
        get_fiq_regs(&regs);
@@ -106,7 +107,6 @@ static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int fiq_enable;
 static int imx_pcm_fiq;
 
 static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -118,23 +118,27 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               atomic_set(&iprtd->running, 1);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       atomic_set(&iprtd->playing, 1);
+               else
+                       atomic_set(&iprtd->capturing, 1);
                hrtimer_start(&iprtd->hrt, ns_to_ktime(iprtd->poll_time_ns),
                      HRTIMER_MODE_REL);
-               if (++fiq_enable == 1)
-                       enable_fiq(imx_pcm_fiq);
-
+               enable_fiq(imx_pcm_fiq);
                break;
 
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               atomic_set(&iprtd->running, 0);
-
-               if (--fiq_enable == 0)
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       atomic_set(&iprtd->playing, 0);
+               else
+                       atomic_set(&iprtd->capturing, 0);
+               if (!atomic_read(&iprtd->playing) &&
+                               !atomic_read(&iprtd->capturing))
                        disable_fiq(imx_pcm_fiq);
-
                break;
+
        default:
                return -EINVAL;
        }
@@ -182,7 +186,8 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
 
        iprtd->substream = substream;
 
-       atomic_set(&iprtd->running, 0);
+       atomic_set(&iprtd->playing, 0);
+       atomic_set(&iprtd->capturing, 0);
        hrtimer_init(&iprtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        iprtd->hrt.function = snd_hrtimer_callback;
 
index 361f94f..61e4885 100644 (file)
@@ -215,7 +215,7 @@ static int imx_wm8962_probe(struct platform_device *pdev)
                goto fail;
        }
        codec_dev = of_find_i2c_device_by_node(codec_np);
-       if (!codec_dev || !codec_dev->driver) {
+       if (!codec_dev || !codec_dev->dev.driver) {
                dev_err(&pdev->dev, "failed to find codec platform device\n");
                ret = -EINVAL;
                goto fail;
index 2acf987..350ba23 100644 (file)
@@ -74,7 +74,7 @@ static void s3c_ac97_activate(struct snd_ac97 *ac97)
        if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE)
                return; /* Return if already active */
 
-       INIT_COMPLETION(s3c_ac97.done);
+       reinit_completion(&s3c_ac97.done);
 
        ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
        ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
@@ -103,7 +103,7 @@ static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
 
        s3c_ac97_activate(ac97);
 
-       INIT_COMPLETION(s3c_ac97.done);
+       reinit_completion(&s3c_ac97.done);
 
        ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
        ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
@@ -140,7 +140,7 @@ static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
 
        s3c_ac97_activate(ac97);
 
-       INIT_COMPLETION(s3c_ac97.done);
+       reinit_completion(&s3c_ac97.done);
 
        ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
        ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
index b47f6fe..dbb1b62 100644 (file)
@@ -907,19 +907,24 @@ static int snd_cs4231_playback_prepare(struct snd_pcm_substream *substream)
        struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
        unsigned long flags;
+       int ret = 0;
 
        spin_lock_irqsave(&chip->lock, flags);
 
        chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
                                            CS4231_PLAYBACK_PIO);
 
-       if (WARN_ON(runtime->period_size > 0xffff + 1))
-               return -EINVAL;
+       if (WARN_ON(runtime->period_size > 0xffff + 1)) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        chip->p_periods_sent = 0;
+
+out:
        spin_unlock_irqrestore(&chip->lock, flags);
 
-       return 0;
+       return ret;
 }
 
 static int snd_cs4231_capture_hw_params(struct snd_pcm_substream *substream,
index d42a584..3454262 100644 (file)
@@ -433,6 +433,89 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
        }
 }
 
+/* EMU0204 */
+static int snd_emu0204_ch_switch_info(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_info *uinfo)
+{
+       static const char *texts[2] = {"1/2",
+                                      "3/4"
+       };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item > 1)
+               uinfo->value.enumerated.item = 1;
+       strcpy(uinfo->value.enumerated.name,
+               texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = kcontrol->private_value;
+       return 0;
+}
+
+static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       unsigned int value = ucontrol->value.enumerated.item[0];
+       int err, changed;
+       unsigned char buf[2];
+
+       if (value > 1)
+               return -EINVAL;
+
+       buf[0] = 0x01;
+       buf[1] = value ? 0x02 : 0x01;
+
+       changed = value != kcontrol->private_value;
+       down_read(&mixer->chip->shutdown_rwsem);
+       if (mixer->chip->shutdown) {
+               err = -ENODEV;
+               goto out;
+       }
+       err = snd_usb_ctl_msg(mixer->chip->dev,
+                     usb_sndctrlpipe(mixer->chip->dev, 0), UAC_SET_CUR,
+                     USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+                     0x0400, 0x0e00, buf, 2);
+ out:
+       up_read(&mixer->chip->shutdown_rwsem);
+       if (err < 0)
+               return err;
+       kcontrol->private_value = value;
+       return changed;
+}
+
+
+static struct snd_kcontrol_new snd_emu0204_controls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Front Jack Channels",
+               .info = snd_emu0204_ch_switch_info,
+               .get = snd_emu0204_ch_switch_get,
+               .put = snd_emu0204_ch_switch_put,
+               .private_value = 0,
+       },
+};
+
+static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer)
+{
+       int i, err;
+
+       for (i = 0; i < ARRAY_SIZE(snd_emu0204_controls); ++i) {
+               err = snd_ctl_add(mixer->chip->card,
+                       snd_ctl_new1(&snd_emu0204_controls[i], mixer));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
 /* ASUS Xonar U1 / U3 controls */
 
 static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
@@ -1545,6 +1628,13 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
                                              snd_audigy2nx_proc_read);
                break;
 
+       /* EMU0204 */
+       case USB_ID(0x041e, 0x3f19):
+               err = snd_emu0204_controls_create(mixer);
+               if (err < 0)
+                       break;
+               break;
+
        case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
        case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */
                err = snd_c400_create_mixer(mixer);
index d737d0e..2fb71be 100644 (file)
@@ -273,8 +273,8 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
                SNDRV_CHMAP_TSL,        /* top side left */
                SNDRV_CHMAP_TSR,        /* top side right */
                SNDRV_CHMAP_BC,         /* bottom center */
-               SNDRV_CHMAP_BLC,        /* bottom left center */
-               SNDRV_CHMAP_BRC,        /* bottom right center */
+               SNDRV_CHMAP_RLC,        /* back left of center */
+               SNDRV_CHMAP_RRC,        /* back right of center */
                0 /* terminator */
        };
        struct snd_pcm_chmap_elem *chmap;
index 052f7c4..43b42c4 100644 (file)
@@ -201,6 +201,12 @@ abort events and some memory events in precise mode on modern Intel CPUs.
 --transaction::
 Record transaction flags for transaction related events.
 
+--force-per-cpu::
+Force the use of per-cpu mmaps.  By default, when tasks are specified (i.e. -p,
+-t or -u options) per-thread mmaps are created.  This option overrides that and
+forces per-cpu mmaps.  A side-effect of that is that inheritance is
+automatically enabled.  Add the -i option also to disable inheritance.
+
 SEE ALSO
 --------
 linkperf:perf-stat[1], linkperf:perf-list[1]
index 89acc17..6ea9e85 100644 (file)
@@ -325,6 +325,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
                     opt_set_filter),
        OPT_CALLBACK('x', "exec", NULL, "executable|path",
                        "target executable name or path", opt_set_target),
+       OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
+                   "Disable symbol demangling"),
        OPT_END()
        };
        int ret;
index 4d644fe..7c8020a 100644 (file)
@@ -888,6 +888,8 @@ const struct option record_options[] = {
                    "sample by weight (on special events only)"),
        OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction,
                    "sample transaction flags (special events only)"),
+       OPT_BOOLEAN(0, "force-per-cpu", &record.opts.target.force_per_cpu,
+                   "force the use of per-cpu mmaps"),
        OPT_END()
 };
 
index b8f8e29..71e6402 100644 (file)
@@ -1172,7 +1172,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
        status = target__validate(target);
        if (status) {
                target__strerror(target, status, errbuf, BUFSIZ);
-               ui__warning("%s", errbuf);
+               ui__warning("%s\n", errbuf);
        }
 
        status = target__parse_uid(target);
@@ -1180,7 +1180,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
                int saved_errno = errno;
 
                target__strerror(target, status, errbuf, BUFSIZ);
-               ui__error("%s", errbuf);
+               ui__error("%s\n", errbuf);
 
                status = -saved_errno;
                goto out_delete_evlist;
index 6b230af..8be17fc 100644 (file)
@@ -2112,9 +2112,9 @@ static size_t thread__dump_stats(struct thread_trace *ttrace,
 
        printed += fprintf(fp, "\n");
 
-       printed += fprintf(fp, "                                                    msec/call\n");
-       printed += fprintf(fp, "   syscall            calls      min      avg      max stddev\n");
-       printed += fprintf(fp, "   --------------- -------- -------- -------- -------- ------\n");
+       printed += fprintf(fp, "   syscall            calls      min       avg       max      stddev\n");
+       printed += fprintf(fp, "                               (msec)    (msec)    (msec)        (%%)\n");
+       printed += fprintf(fp, "   --------------- -------- --------- --------- ---------     ------\n");
 
        /* each int_node is a syscall */
        while (inode) {
@@ -2131,9 +2131,9 @@ static size_t thread__dump_stats(struct thread_trace *ttrace,
 
                        sc = &trace->syscalls.table[inode->i];
                        printed += fprintf(fp, "   %-15s", sc->name);
-                       printed += fprintf(fp, " %8" PRIu64 " %8.3f %8.3f",
+                       printed += fprintf(fp, " %8" PRIu64 " %9.3f %9.3f",
                                           n, min, avg);
-                       printed += fprintf(fp, " %8.3f %6.2f\n", max, pct);
+                       printed += fprintf(fp, " %9.3f %9.2f%%\n", max, pct);
                }
 
                inode = intlist__next(inode);
index f5905f2..f7d11a8 100644 (file)
@@ -142,7 +142,8 @@ CORE_FEATURE_TESTS =                        \
        libunwind                       \
        on-exit                         \
        stackprotector                  \
-       stackprotector-all
+       stackprotector-all              \
+       timerfd
 
 #
 # So here we detect whether test-all was rebuilt, to be able
@@ -328,8 +329,14 @@ ifndef NO_LIBUNWIND
     msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 1.1);
     NO_LIBUNWIND := 1
   else
-    ifneq ($(feature-libunwind-debug-frame), 1)
-      msg := $(warning No debug_frame support found in libunwind);
+    ifeq ($(ARCH),arm)
+      $(call feature_check,libunwind-debug-frame)
+      ifneq ($(feature-libunwind-debug-frame), 1)
+        msg := $(warning No debug_frame support found in libunwind);
+        CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
+      endif
+    else
+      # non-ARM has no dwarf_find_debug_frame() function:
       CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
     endif
   endif
@@ -405,7 +412,6 @@ else
   endif
 endif
 
-$(call feature_check,timerfd)
 ifeq ($(feature-timerfd), 1)
   CFLAGS += -DHAVE_TIMERFD_SUPPORT
 else
index e8e195f..87e7900 100644 (file)
@@ -76,6 +76,9 @@ test-libnuma:
 test-libunwind:
        $(BUILD) $(LIBUNWIND_LIBS) -lelf
 
+test-libunwind-debug-frame:
+       $(BUILD) $(LIBUNWIND_LIBS) -lelf
+
 test-libaudit:
        $(BUILD) -laudit
 
index 799865b..59e7a70 100644 (file)
 # include "test-libunwind.c"
 #undef main
 
-#define main main_test_libunwind_debug_frame
-# include "test-libunwind-debug-frame.c"
-#undef main
-
 #define main main_test_libaudit
 # include "test-libaudit.c"
 #undef main
index ef671cd..3cbd104 100644 (file)
@@ -441,9 +441,8 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist)
 
 static int test__checkevent_pmu_events(struct perf_evlist *evlist)
 {
-       struct perf_evsel *evsel;
+       struct perf_evsel *evsel = perf_evlist__first(evlist);
 
-       evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
        TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
        TEST_ASSERT_VAL("wrong exclude_user",
index bbc782e..cbaa7af 100644 (file)
@@ -569,7 +569,7 @@ void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
                browser->top = browser->top + browser->top_idx + offset;
                break;
        case SEEK_END:
-               browser->top = browser->top + browser->nr_entries + offset;
+               browser->top = browser->top + browser->nr_entries - 1 + offset;
                break;
        default:
                return;
@@ -680,7 +680,7 @@ static void __ui_browser__line_arrow_down(struct ui_browser *browser,
        if (end >= browser->top_idx + browser->height)
                end_row = browser->height - 1;
        else
-               end_row = end - browser->top_idx;;
+               end_row = end - browser->top_idx;
 
        ui_browser__gotorc(browser, row, column);
        SLsmg_draw_vline(end_row - row + 1);
index 16848bb..a440e03 100644 (file)
@@ -1847,15 +1847,15 @@ browse_hists:
                        switch (key) {
                        case K_TAB:
                                if (pos->node.next == &evlist->entries)
-                                       pos = list_entry(evlist->entries.next, struct perf_evsel, node);
+                                       pos = perf_evlist__first(evlist);
                                else
-                                       pos = list_entry(pos->node.next, struct perf_evsel, node);
+                                       pos = perf_evsel__next(pos);
                                goto browse_hists;
                        case K_UNTAB:
                                if (pos->node.prev == &evlist->entries)
-                                       pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
+                                       pos = perf_evlist__last(evlist);
                                else
-                                       pos = list_entry(pos->node.prev, struct perf_evsel, node);
+                                       pos = perf_evsel__prev(pos);
                                goto browse_hists;
                        case K_ESC:
                                if (!ui_browser__dialog_yesno(&menu->b,
@@ -1943,8 +1943,7 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
 
 single_entry:
        if (nr_entries == 1) {
-               struct perf_evsel *first = list_entry(evlist->entries.next,
-                                                     struct perf_evsel, node);
+               struct perf_evsel *first = perf_evlist__first(evlist);
                const char *ev_name = perf_evsel__name(first);
 
                return perf_evsel__hists_browse(first, nr_entries, help,
index 6e3a846..bb788c1 100644 (file)
@@ -209,8 +209,10 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                       &event->mmap.start, &event->mmap.len, prot,
                       &event->mmap.pgoff,
                       execname);
-
-               if (n != 5)
+               /*
+                * Anon maps don't have the execname.
+                */
+               if (n < 4)
                        continue;
                /*
                 * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c
index dc6fa3f..bbc746a 100644 (file)
@@ -819,7 +819,9 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
        if (evlist->threads == NULL)
                return -1;
 
-       if (target__has_task(target))
+       if (target->force_per_cpu)
+               evlist->cpus = cpu_map__new(target->cpu_list);
+       else if (target__has_task(target))
                evlist->cpus = cpu_map__dummy_new();
        else if (!target__has_cpu(target) && !target->uses_mmap)
                evlist->cpus = cpu_map__dummy_new();
@@ -1148,7 +1150,7 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)
                                   perf_evsel__name(evsel));
        }
 
-       return printed + fprintf(fp, "\n");;
+       return printed + fprintf(fp, "\n");
 }
 
 int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused,
index 18f7c18..46dd4c2 100644 (file)
@@ -645,7 +645,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
                }
        }
 
-       if (target__has_cpu(&opts->target))
+       if (target__has_cpu(&opts->target) || opts->target.force_per_cpu)
                perf_evsel__set_sample_bit(evsel, CPU);
 
        if (opts->period)
@@ -653,7 +653,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
 
        if (!perf_missing_features.sample_id_all &&
            (opts->sample_time || !opts->no_inherit ||
-            target__has_cpu(&opts->target)))
+            target__has_cpu(&opts->target) || opts->target.force_per_cpu))
                perf_evsel__set_sample_bit(evsel, TIME);
 
        if (opts->raw_samples) {
index f502965..1ea7c92 100644 (file)
@@ -279,6 +279,11 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel)
        return list_entry(evsel->node.next, struct perf_evsel, node);
 }
 
+static inline struct perf_evsel *perf_evsel__prev(struct perf_evsel *evsel)
+{
+       return list_entry(evsel->node.prev, struct perf_evsel, node);
+}
+
 /**
  * perf_evsel__is_group_leader - Return whether given evsel is a leader event
  *
index 0393912..84cdb07 100644 (file)
@@ -1368,7 +1368,7 @@ int machine__resolve_callchain(struct machine *machine,
 
        return unwind__get_entries(unwind_entry, &callchain_cursor, machine,
                                   thread, evsel->attr.sample_regs_user,
-                                  sample);
+                                  sample, max_stack);
 
 }
 
index 89bab71..2d0c506 100644 (file)
@@ -12,6 +12,7 @@ struct target {
        uid_t        uid;
        bool         system_wide;
        bool         uses_mmap;
+       bool         force_per_cpu;
 };
 
 enum target_errno {
index 5390d0b..0efd539 100644 (file)
@@ -559,7 +559,7 @@ static unw_accessors_t accessors = {
 };
 
 static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
-                      void *arg)
+                      void *arg, int max_stack)
 {
        unw_addr_space_t addr_space;
        unw_cursor_t c;
@@ -575,7 +575,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
        if (ret)
                display_error(ret);
 
-       while (!ret && (unw_step(&c) > 0)) {
+       while (!ret && (unw_step(&c) > 0) && max_stack--) {
                unw_word_t ip;
 
                unw_get_reg(&c, UNW_REG_IP, &ip);
@@ -588,7 +588,8 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
 
 int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
                        struct machine *machine, struct thread *thread,
-                       u64 sample_uregs, struct perf_sample *data)
+                       u64 sample_uregs, struct perf_sample *data,
+                       int max_stack)
 {
        unw_word_t ip;
        struct unwind_info ui = {
@@ -610,5 +611,5 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
        if (ret)
                return -ENOMEM;
 
-       return get_entries(&ui, cb, arg);
+       return get_entries(&ui, cb, arg, max_stack);
 }
index ec0c71a..d5966f4 100644 (file)
@@ -18,7 +18,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
                        struct machine *machine,
                        struct thread *thread,
                        u64 sample_uregs,
-                       struct perf_sample *data);
+                       struct perf_sample *data, int max_stack);
 int unwind__arch_reg_id(int regnum);
 #else
 static inline int
@@ -27,7 +27,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
                    struct machine *machine __maybe_unused,
                    struct thread *thread __maybe_unused,
                    u64 sample_uregs __maybe_unused,
-                   struct perf_sample *data __maybe_unused)
+                   struct perf_sample *data __maybe_unused,
+                   int max_stack __maybe_unused)
 {
        return 0;
 }
index fe70207..9d77f13 100644 (file)
@@ -2,7 +2,7 @@
  * turbostat -- show CPU frequency and C-state residency
  * on modern Intel turbo-capable processors.
  *
- * Copyright (c) 2012 Intel Corporation.
+ * Copyright (c) 2013 Intel Corporation.
  * Len Brown <len.brown@intel.com>
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -47,6 +47,8 @@ unsigned int skip_c1;
 unsigned int do_nhm_cstates;
 unsigned int do_snb_cstates;
 unsigned int do_c8_c9_c10;
+unsigned int do_slm_cstates;
+unsigned int use_c1_residency_msr;
 unsigned int has_aperf;
 unsigned int has_epb;
 unsigned int units = 1000000000;       /* Ghz etc */
@@ -81,6 +83,8 @@ double rapl_joule_counter_range;
 #define RAPL_DRAM      (1 << 3)
 #define RAPL_PKG_PERF_STATUS   (1 << 4)
 #define RAPL_DRAM_PERF_STATUS  (1 << 5)
+#define RAPL_PKG_POWER_INFO    (1 << 6)
+#define RAPL_CORE_POLICY       (1 << 7)
 #define        TJMAX_DEFAULT   100
 
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
@@ -96,7 +100,7 @@ struct thread_data {
        unsigned long long tsc;
        unsigned long long aperf;
        unsigned long long mperf;
-       unsigned long long c1;  /* derived */
+       unsigned long long c1;
        unsigned long long extra_msr64;
        unsigned long long extra_delta64;
        unsigned long long extra_msr32;
@@ -266,7 +270,7 @@ void print_header(void)
                outp += sprintf(outp, "           MSR 0x%03X", extra_msr_offset64);
        if (do_nhm_cstates)
                outp += sprintf(outp, "    %%c1");
-       if (do_nhm_cstates)
+       if (do_nhm_cstates && !do_slm_cstates)
                outp += sprintf(outp, "    %%c3");
        if (do_nhm_cstates)
                outp += sprintf(outp, "    %%c6");
@@ -280,9 +284,9 @@ void print_header(void)
 
        if (do_snb_cstates)
                outp += sprintf(outp, "   %%pc2");
-       if (do_nhm_cstates)
+       if (do_nhm_cstates && !do_slm_cstates)
                outp += sprintf(outp, "   %%pc3");
-       if (do_nhm_cstates)
+       if (do_nhm_cstates && !do_slm_cstates)
                outp += sprintf(outp, "   %%pc6");
        if (do_snb_cstates)
                outp += sprintf(outp, "   %%pc7");
@@ -480,7 +484,7 @@ int format_counters(struct thread_data *t, struct core_data *c,
        if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE))
                goto done;
 
-       if (do_nhm_cstates)
+       if (do_nhm_cstates && !do_slm_cstates)
                outp += sprintf(outp, " %6.2f", 100.0 * c->c3/t->tsc);
        if (do_nhm_cstates)
                outp += sprintf(outp, " %6.2f", 100.0 * c->c6/t->tsc);
@@ -499,9 +503,9 @@ int format_counters(struct thread_data *t, struct core_data *c,
 
        if (do_snb_cstates)
                outp += sprintf(outp, " %6.2f", 100.0 * p->pc2/t->tsc);
-       if (do_nhm_cstates)
+       if (do_nhm_cstates && !do_slm_cstates)
                outp += sprintf(outp, " %6.2f", 100.0 * p->pc3/t->tsc);
-       if (do_nhm_cstates)
+       if (do_nhm_cstates && !do_slm_cstates)
                outp += sprintf(outp, " %6.2f", 100.0 * p->pc6/t->tsc);
        if (do_snb_cstates)
                outp += sprintf(outp, " %6.2f", 100.0 * p->pc7/t->tsc);
@@ -648,17 +652,24 @@ delta_thread(struct thread_data *new, struct thread_data *old,
        }
 
 
-       /*
-        * As counter collection is not atomic,
-        * it is possible for mperf's non-halted cycles + idle states
-        * to exceed TSC's all cycles: show c1 = 0% in that case.
-        */
-       if ((old->mperf + core_delta->c3 + core_delta->c6 + core_delta->c7) > old->tsc)
-               old->c1 = 0;
-       else {
-               /* normal case, derive c1 */
-               old->c1 = old->tsc - old->mperf - core_delta->c3
+       if (use_c1_residency_msr) {
+               /*
+                * Some models have a dedicated C1 residency MSR,
+                * which should be more accurate than the derivation below.
+                */
+       } else {
+               /*
+                * As counter collection is not atomic,
+                * it is possible for mperf's non-halted cycles + idle states
+                * to exceed TSC's all cycles: show c1 = 0% in that case.
+                */
+               if ((old->mperf + core_delta->c3 + core_delta->c6 + core_delta->c7) > old->tsc)
+                       old->c1 = 0;
+               else {
+                       /* normal case, derive c1 */
+                       old->c1 = old->tsc - old->mperf - core_delta->c3
                                - core_delta->c6 - core_delta->c7;
+               }
        }
 
        if (old->mperf == 0) {
@@ -872,13 +883,21 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
                if (get_msr(cpu, extra_msr_offset64, &t->extra_msr64))
                        return -5;
 
+       if (use_c1_residency_msr) {
+               if (get_msr(cpu, MSR_CORE_C1_RES, &t->c1))
+                       return -6;
+       }
+
        /* collect core counters only for 1st thread in core */
        if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE))
                return 0;
 
-       if (do_nhm_cstates) {
+       if (do_nhm_cstates && !do_slm_cstates) {
                if (get_msr(cpu, MSR_CORE_C3_RESIDENCY, &c->c3))
                        return -6;
+       }
+
+       if (do_nhm_cstates) {
                if (get_msr(cpu, MSR_CORE_C6_RESIDENCY, &c->c6))
                        return -7;
        }
@@ -898,7 +917,7 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
        if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE))
                return 0;
 
-       if (do_nhm_cstates) {
+       if (do_nhm_cstates && !do_slm_cstates) {
                if (get_msr(cpu, MSR_PKG_C3_RESIDENCY, &p->pc3))
                        return -9;
                if (get_msr(cpu, MSR_PKG_C6_RESIDENCY, &p->pc6))
@@ -977,7 +996,7 @@ void print_verbose_header(void)
                ratio, bclk, ratio * bclk);
 
        get_msr(0, MSR_IA32_POWER_CTL, &msr);
-       fprintf(stderr, "cpu0: MSR_IA32_POWER_CTL: 0x%08llx (C1E: %sabled)\n",
+       fprintf(stderr, "cpu0: MSR_IA32_POWER_CTL: 0x%08llx (C1E auto-promotion: %sabled)\n",
                msr, msr & 0x2 ? "EN" : "DIS");
 
        if (!do_ivt_turbo_ratio_limit)
@@ -1046,25 +1065,28 @@ print_nhm_turbo_ratio_limits:
 
        switch(msr & 0x7) {
        case 0:
-               fprintf(stderr, "pc0");
+               fprintf(stderr, do_slm_cstates ? "no pkg states" : "pc0");
                break;
        case 1:
-               fprintf(stderr, do_snb_cstates ? "pc2" : "pc0");
+               fprintf(stderr, do_slm_cstates ? "no pkg states" : do_snb_cstates ? "pc2" : "pc0");
                break;
        case 2:
-               fprintf(stderr, do_snb_cstates ? "pc6-noret" : "pc3");
+               fprintf(stderr, do_slm_cstates ? "invalid" : do_snb_cstates ? "pc6-noret" : "pc3");
                break;
        case 3:
-               fprintf(stderr, "pc6");
+               fprintf(stderr, do_slm_cstates ? "invalid" : "pc6");
                break;
        case 4:
-               fprintf(stderr, "pc7");
+               fprintf(stderr, do_slm_cstates ? "pc4" : "pc7");
                break;
        case 5:
-               fprintf(stderr, do_snb_cstates ? "pc7s" : "invalid");
+               fprintf(stderr, do_slm_cstates ? "invalid" : do_snb_cstates ? "pc7s" : "invalid");
+               break;
+       case 6:
+               fprintf(stderr, do_slm_cstates ? "pc6" : "invalid");
                break;
        case 7:
-               fprintf(stderr, "unlimited");
+               fprintf(stderr, do_slm_cstates ? "pc7" : "unlimited");
                break;
        default:
                fprintf(stderr, "invalid");
@@ -1460,6 +1482,8 @@ int has_nehalem_turbo_ratio_limit(unsigned int family, unsigned int model)
        case 0x3F:      /* HSW */
        case 0x45:      /* HSW */
        case 0x46:      /* HSW */
+       case 0x37:      /* BYT */
+       case 0x4D:      /* AVN */
                return 1;
        case 0x2E:      /* Nehalem-EX Xeon - Beckton */
        case 0x2F:      /* Westmere-EX Xeon - Eagleton */
@@ -1532,14 +1556,33 @@ int print_epb(struct thread_data *t, struct core_data *c, struct pkg_data *p)
 #define        RAPL_POWER_GRANULARITY  0x7FFF  /* 15 bit power granularity */
 #define        RAPL_TIME_GRANULARITY   0x3F /* 6 bit time granularity */
 
+double get_tdp(model)
+{
+       unsigned long long msr;
+
+       if (do_rapl & RAPL_PKG_POWER_INFO)
+               if (!get_msr(0, MSR_PKG_POWER_INFO, &msr))
+                       return ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units;
+
+       switch (model) {
+       case 0x37:
+       case 0x4D:
+               return 30.0;
+       default:
+               return 135.0;
+       }
+}
+
+
 /*
  * rapl_probe()
  *
- * sets do_rapl
+ * sets do_rapl, rapl_power_units, rapl_energy_units, rapl_time_units
  */
 void rapl_probe(unsigned int family, unsigned int model)
 {
        unsigned long long msr;
+       unsigned int time_unit;
        double tdp;
 
        if (!genuine_intel)
@@ -1555,11 +1598,15 @@ void rapl_probe(unsigned int family, unsigned int model)
        case 0x3F:      /* HSW */
        case 0x45:      /* HSW */
        case 0x46:      /* HSW */
-               do_rapl = RAPL_PKG | RAPL_CORES | RAPL_GFX;
+               do_rapl = RAPL_PKG | RAPL_CORES | RAPL_CORE_POLICY | RAPL_GFX | RAPL_PKG_POWER_INFO;
                break;
        case 0x2D:
        case 0x3E:
-               do_rapl = RAPL_PKG | RAPL_CORES | RAPL_DRAM | RAPL_PKG_PERF_STATUS | RAPL_DRAM_PERF_STATUS;
+               do_rapl = RAPL_PKG | RAPL_CORES | RAPL_CORE_POLICY | RAPL_DRAM | RAPL_PKG_PERF_STATUS | RAPL_DRAM_PERF_STATUS | RAPL_PKG_POWER_INFO;
+               break;
+       case 0x37:      /* BYT */
+       case 0x4D:      /* AVN */
+               do_rapl = RAPL_PKG | RAPL_CORES ;
                break;
        default:
                return;
@@ -1570,19 +1617,22 @@ void rapl_probe(unsigned int family, unsigned int model)
                return;
 
        rapl_power_units = 1.0 / (1 << (msr & 0xF));
-       rapl_energy_units = 1.0 / (1 << (msr >> 8 & 0x1F));
-       rapl_time_units = 1.0 / (1 << (msr >> 16 & 0xF));
+       if (model == 0x37)
+               rapl_energy_units = 1.0 * (1 << (msr >> 8 & 0x1F)) / 1000000;
+       else
+               rapl_energy_units = 1.0 / (1 << (msr >> 8 & 0x1F));
 
-       /* get TDP to determine energy counter range */
-       if (get_msr(0, MSR_PKG_POWER_INFO, &msr))
-               return;
+       time_unit = msr >> 16 & 0xF;
+       if (time_unit == 0)
+               time_unit = 0xA;
 
-       tdp = ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units;
+       rapl_time_units = 1.0 / (1 << (time_unit));
 
-       rapl_joule_counter_range = 0xFFFFFFFF * rapl_energy_units / tdp;
+       tdp = get_tdp(model);
 
+       rapl_joule_counter_range = 0xFFFFFFFF * rapl_energy_units / tdp;
        if (verbose)
-               fprintf(stderr, "RAPL: %.0f sec. Joule Counter Range\n", rapl_joule_counter_range);
+               fprintf(stderr, "RAPL: %.0f sec. Joule Counter Range, at %.0f Watts\n", rapl_joule_counter_range, tdp);
 
        return;
 }
@@ -1668,7 +1718,6 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p)
 {
        unsigned long long msr;
        int cpu;
-       double local_rapl_power_units, local_rapl_energy_units, local_rapl_time_units;
 
        if (!do_rapl)
                return 0;
@@ -1686,23 +1735,13 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p)
        if (get_msr(cpu, MSR_RAPL_POWER_UNIT, &msr))
                return -1;
 
-       local_rapl_power_units = 1.0 / (1 << (msr & 0xF));
-       local_rapl_energy_units = 1.0 / (1 << (msr >> 8 & 0x1F));
-       local_rapl_time_units = 1.0 / (1 << (msr >> 16 & 0xF));
-
-       if (local_rapl_power_units != rapl_power_units)
-               fprintf(stderr, "cpu%d, ERROR: Power units mis-match\n", cpu);
-       if (local_rapl_energy_units != rapl_energy_units)
-               fprintf(stderr, "cpu%d, ERROR: Energy units mis-match\n", cpu);
-       if (local_rapl_time_units != rapl_time_units)
-               fprintf(stderr, "cpu%d, ERROR: Time units mis-match\n", cpu);
-
        if (verbose) {
                fprintf(stderr, "cpu%d: MSR_RAPL_POWER_UNIT: 0x%08llx "
                        "(%f Watts, %f Joules, %f sec.)\n", cpu, msr,
-                       local_rapl_power_units, local_rapl_energy_units, local_rapl_time_units);
+                       rapl_power_units, rapl_energy_units, rapl_time_units);
        }
-       if (do_rapl & RAPL_PKG) {
+       if (do_rapl & RAPL_PKG_POWER_INFO) {
+
                if (get_msr(cpu, MSR_PKG_POWER_INFO, &msr))
                        return -5;
 
@@ -1714,6 +1753,9 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p)
                        ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units,
                        ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units);
 
+       }
+       if (do_rapl & RAPL_PKG) {
+
                if (get_msr(cpu, MSR_PKG_POWER_LIMIT, &msr))
                        return -9;
 
@@ -1749,12 +1791,16 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p)
 
                print_power_limit_msr(cpu, msr, "DRAM Limit");
        }
-       if (do_rapl & RAPL_CORES) {
+       if (do_rapl & RAPL_CORE_POLICY) {
                if (verbose) {
                        if (get_msr(cpu, MSR_PP0_POLICY, &msr))
                                return -7;
 
                        fprintf(stderr, "cpu%d: MSR_PP0_POLICY: %lld\n", cpu, msr & 0xF);
+               }
+       }
+       if (do_rapl & RAPL_CORES) {
+               if (verbose) {
 
                        if (get_msr(cpu, MSR_PP0_POWER_LIMIT, &msr))
                                return -9;
@@ -1813,10 +1859,48 @@ int has_c8_c9_c10(unsigned int family, unsigned int model)
 }
 
 
+int is_slm(unsigned int family, unsigned int model)
+{
+       if (!genuine_intel)
+               return 0;
+       switch (model) {
+       case 0x37:      /* BYT */
+       case 0x4D:      /* AVN */
+               return 1;
+       }
+       return 0;
+}
+
+#define SLM_BCLK_FREQS 5
+double slm_freq_table[SLM_BCLK_FREQS] = { 83.3, 100.0, 133.3, 116.7, 80.0};
+
+double slm_bclk(void)
+{
+       unsigned long long msr = 3;
+       unsigned int i;
+       double freq;
+
+       if (get_msr(0, MSR_FSB_FREQ, &msr))
+               fprintf(stderr, "SLM BCLK: unknown\n");
+
+       i = msr & 0xf;
+       if (i >= SLM_BCLK_FREQS) {
+               fprintf(stderr, "SLM BCLK[%d] invalid\n", i);
+               msr = 3;
+       }
+       freq = slm_freq_table[i];
+
+       fprintf(stderr, "SLM BCLK: %.1f Mhz\n", freq);
+
+       return freq;
+}
+
 double discover_bclk(unsigned int family, unsigned int model)
 {
        if (is_snb(family, model))
                return 100.00;
+       else if (is_slm(family, model))
+               return slm_bclk();
        else
                return 133.33;
 }
@@ -1873,7 +1957,7 @@ int set_temperature_target(struct thread_data *t, struct core_data *c, struct pk
                fprintf(stderr, "cpu%d: MSR_IA32_TEMPERATURE_TARGET: 0x%08llx (%d C)\n",
                        cpu, msr, target_c_local);
 
-       if (target_c_local < 85 || target_c_local > 120)
+       if (target_c_local < 85 || target_c_local > 127)
                goto guess;
 
        tcc_activation_temp = target_c_local;
@@ -1970,6 +2054,7 @@ void check_cpuid()
        do_smi = do_nhm_cstates;
        do_snb_cstates = is_snb(family, model);
        do_c8_c9_c10 = has_c8_c9_c10(family, model);
+       do_slm_cstates = is_slm(family, model);
        bclk = discover_bclk(family, model);
 
        do_nehalem_turbo_ratio_limit = has_nehalem_turbo_ratio_limit(family, model);
@@ -2331,7 +2416,7 @@ int main(int argc, char **argv)
        cmdline(argc, argv);
 
        if (verbose)
-               fprintf(stderr, "turbostat v3.4 April 17, 2013"
+               fprintf(stderr, "turbostat v3.5 April 26, 2013"
                        " - Len Brown <lenb@kernel.org>\n");
 
        turbostat_init();
index da7a195..bdb71a2 100644 (file)
@@ -41,13 +41,14 @@ struct vdev_info {
        struct vhost_memory *mem;
 };
 
-void vq_notify(struct virtqueue *vq)
+bool vq_notify(struct virtqueue *vq)
 {
        struct vq_info *info = vq->priv;
        unsigned long long v = 1;
        int r;
        r = write(info->kick, &v, sizeof v);
        assert(r == sizeof v);
+       return true;
 }
 
 void vq_callback(struct virtqueue *vq)
@@ -171,7 +172,8 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq,
                                                         GFP_ATOMIC);
                                if (likely(r == 0)) {
                                        ++started;
-                                       virtqueue_kick(vq->vq);
+                                       if (unlikely(!virtqueue_kick(vq->vq))
+                                               r = -1;
                                }
                        } else
                                r = -1;
index d053ea4..14a4f4c 100644 (file)
@@ -22,7 +22,7 @@ static u64 user_addr_offset;
 #define RINGSIZE 256
 #define ALIGN 4096
 
-static void never_notify_host(struct virtqueue *vq)
+static bool never_notify_host(struct virtqueue *vq)
 {
        abort();
 }
@@ -65,17 +65,22 @@ struct guest_virtio_device {
        unsigned long notifies;
 };
 
-static void parallel_notify_host(struct virtqueue *vq)
+static bool parallel_notify_host(struct virtqueue *vq)
 {
+       int rc;
        struct guest_virtio_device *gvdev;
 
        gvdev = container_of(vq->vdev, struct guest_virtio_device, vdev);
-       write(gvdev->to_host_fd, "", 1);
+       rc = write(gvdev->to_host_fd, "", 1);
+       if (rc < 0)
+               return false;
        gvdev->notifies++;
+       return true;
 }
 
-static void no_notify_host(struct virtqueue *vq)
+static bool no_notify_host(struct virtqueue *vq)
 {
+       return true;
 }
 
 #define NUM_XFERS (10000000)
index 779262f..fbe1a48 100644 (file)
@@ -27,3 +27,6 @@ config HAVE_KVM_MSI
 
 config HAVE_KVM_CPU_RELAX_INTERCEPT
        bool
+
+config KVM_VFIO
+       bool
index 8a39dda..8631d9c 100644 (file)
@@ -56,7 +56,6 @@ void kvm_async_pf_vcpu_init(struct kvm_vcpu *vcpu)
 
 static void async_pf_execute(struct work_struct *work)
 {
-       struct page *page = NULL;
        struct kvm_async_pf *apf =
                container_of(work, struct kvm_async_pf, work);
        struct mm_struct *mm = apf->mm;
@@ -68,14 +67,12 @@ static void async_pf_execute(struct work_struct *work)
 
        use_mm(mm);
        down_read(&mm->mmap_sem);
-       get_user_pages(current, mm, addr, 1, 1, 0, &page, NULL);
+       get_user_pages(current, mm, addr, 1, 1, 0, NULL, NULL);
        up_read(&mm->mmap_sem);
        unuse_mm(mm);
 
        spin_lock(&vcpu->async_pf.lock);
        list_add_tail(&apf->link, &vcpu->async_pf.done);
-       apf->page = page;
-       apf->done = true;
        spin_unlock(&vcpu->async_pf.lock);
 
        /*
@@ -83,7 +80,7 @@ static void async_pf_execute(struct work_struct *work)
         * this point
         */
 
-       trace_kvm_async_pf_completed(addr, page, gva);
+       trace_kvm_async_pf_completed(addr, gva);
 
        if (waitqueue_active(&vcpu->wq))
                wake_up_interruptible(&vcpu->wq);
@@ -99,9 +96,8 @@ void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu)
                struct kvm_async_pf *work =
                        list_entry(vcpu->async_pf.queue.next,
                                   typeof(*work), queue);
-               cancel_work_sync(&work->work);
                list_del(&work->queue);
-               if (!work->done) { /* work was canceled */
+               if (cancel_work_sync(&work->work)) {
                        mmdrop(work->mm);
                        kvm_put_kvm(vcpu->kvm); /* == work->vcpu->kvm */
                        kmem_cache_free(async_pf_cache, work);
@@ -114,8 +110,6 @@ void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu)
                        list_entry(vcpu->async_pf.done.next,
                                   typeof(*work), link);
                list_del(&work->link);
-               if (!is_error_page(work->page))
-                       kvm_release_page_clean(work->page);
                kmem_cache_free(async_pf_cache, work);
        }
        spin_unlock(&vcpu->async_pf.lock);
@@ -135,14 +129,11 @@ void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu)
                list_del(&work->link);
                spin_unlock(&vcpu->async_pf.lock);
 
-               if (work->page)
-                       kvm_arch_async_page_ready(vcpu, work);
+               kvm_arch_async_page_ready(vcpu, work);
                kvm_arch_async_page_present(vcpu, work);
 
                list_del(&work->queue);
                vcpu->async_pf.queued--;
-               if (!is_error_page(work->page))
-                       kvm_release_page_clean(work->page);
                kmem_cache_free(async_pf_cache, work);
        }
 }
@@ -165,8 +156,7 @@ int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn,
        if (!work)
                return 0;
 
-       work->page = NULL;
-       work->done = false;
+       work->wakeup_all = false;
        work->vcpu = vcpu;
        work->gva = gva;
        work->addr = gfn_to_hva(vcpu->kvm, gfn);
@@ -206,7 +196,7 @@ int kvm_async_pf_wakeup_all(struct kvm_vcpu *vcpu)
        if (!work)
                return -ENOMEM;
 
-       work->page = KVM_ERR_PTR_BAD_PAGE;
+       work->wakeup_all = true;
        INIT_LIST_HEAD(&work->queue); /* for list_del to work */
 
        spin_lock(&vcpu->async_pf.lock);
index 72a130b..0df7d4b 100644 (file)
@@ -79,7 +79,7 @@ int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot)
        flags = IOMMU_READ;
        if (!(slot->flags & KVM_MEM_READONLY))
                flags |= IOMMU_WRITE;
-       if (kvm->arch.iommu_flags & KVM_IOMMU_CACHE_COHERENCY)
+       if (!kvm->arch.iommu_noncoherent)
                flags |= IOMMU_CACHE;
 
 
@@ -103,6 +103,10 @@ int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot)
                while ((gfn << PAGE_SHIFT) & (page_size - 1))
                        page_size >>= 1;
 
+               /* Make sure hva is aligned to the page size we want to map */
+               while (__gfn_to_hva_memslot(slot, gfn) & (page_size - 1))
+                       page_size >>= 1;
+
                /*
                 * Pin all pages we are about to map in memory. This is
                 * important because we unmap and unpin in 4kb steps later.
@@ -140,6 +144,9 @@ static int kvm_iommu_map_memslots(struct kvm *kvm)
        struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
 
+       if (kvm->arch.iommu_noncoherent)
+               kvm_arch_register_noncoherent_dma(kvm);
+
        idx = srcu_read_lock(&kvm->srcu);
        slots = kvm_memslots(kvm);
 
@@ -158,7 +165,8 @@ int kvm_assign_device(struct kvm *kvm,
 {
        struct pci_dev *pdev = NULL;
        struct iommu_domain *domain = kvm->arch.iommu_domain;
-       int r, last_flags;
+       int r;
+       bool noncoherent;
 
        /* check if iommu exists and in use */
        if (!domain)
@@ -174,15 +182,13 @@ int kvm_assign_device(struct kvm *kvm,
                return r;
        }
 
-       last_flags = kvm->arch.iommu_flags;
-       if (iommu_domain_has_cap(kvm->arch.iommu_domain,
-                                IOMMU_CAP_CACHE_COHERENCY))
-               kvm->arch.iommu_flags |= KVM_IOMMU_CACHE_COHERENCY;
+       noncoherent = !iommu_domain_has_cap(kvm->arch.iommu_domain,
+                                           IOMMU_CAP_CACHE_COHERENCY);
 
        /* Check if need to update IOMMU page table for guest memory */
-       if ((last_flags ^ kvm->arch.iommu_flags) ==
-                       KVM_IOMMU_CACHE_COHERENCY) {
+       if (noncoherent != kvm->arch.iommu_noncoherent) {
                kvm_iommu_unmap_memslots(kvm);
+               kvm->arch.iommu_noncoherent = noncoherent;
                r = kvm_iommu_map_memslots(kvm);
                if (r)
                        goto out_unmap;
@@ -190,11 +196,7 @@ int kvm_assign_device(struct kvm *kvm,
 
        pdev->dev_flags |= PCI_DEV_FLAGS_ASSIGNED;
 
-       printk(KERN_DEBUG "assign device %x:%x:%x.%x\n",
-               assigned_dev->host_segnr,
-               assigned_dev->host_busnr,
-               PCI_SLOT(assigned_dev->host_devfn),
-               PCI_FUNC(assigned_dev->host_devfn));
+       dev_info(&pdev->dev, "kvm assign device\n");
 
        return 0;
 out_unmap:
@@ -220,11 +222,7 @@ int kvm_deassign_device(struct kvm *kvm,
 
        pdev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED;
 
-       printk(KERN_DEBUG "deassign device %x:%x:%x.%x\n",
-               assigned_dev->host_segnr,
-               assigned_dev->host_busnr,
-               PCI_SLOT(assigned_dev->host_devfn),
-               PCI_FUNC(assigned_dev->host_devfn));
+       dev_info(&pdev->dev, "kvm deassign device\n");
 
        return 0;
 }
@@ -336,6 +334,9 @@ static int kvm_iommu_unmap_memslots(struct kvm *kvm)
 
        srcu_read_unlock(&kvm->srcu, idx);
 
+       if (kvm->arch.iommu_noncoherent)
+               kvm_arch_unregister_noncoherent_dma(kvm);
+
        return 0;
 }
 
@@ -350,6 +351,7 @@ int kvm_iommu_unmap_guest(struct kvm *kvm)
        mutex_lock(&kvm->slots_lock);
        kvm_iommu_unmap_memslots(kvm);
        kvm->arch.iommu_domain = NULL;
+       kvm->arch.iommu_noncoherent = false;
        mutex_unlock(&kvm->slots_lock);
 
        iommu_domain_free(domain);
index 1cf9ccb..a0aa84b 100644 (file)
@@ -70,7 +70,8 @@ MODULE_LICENSE("GPL");
  *             kvm->lock --> kvm->slots_lock --> kvm->irq_lock
  */
 
-DEFINE_RAW_SPINLOCK(kvm_lock);
+DEFINE_SPINLOCK(kvm_lock);
+static DEFINE_RAW_SPINLOCK(kvm_count_lock);
 LIST_HEAD(vm_list);
 
 static cpumask_var_t cpus_hardware_enabled;
@@ -186,6 +187,7 @@ void kvm_flush_remote_tlbs(struct kvm *kvm)
                ++kvm->stat.remote_tlb_flush;
        cmpxchg(&kvm->tlbs_dirty, dirty_count, 0);
 }
+EXPORT_SYMBOL_GPL(kvm_flush_remote_tlbs);
 
 void kvm_reload_remote_mmus(struct kvm *kvm)
 {
@@ -490,9 +492,9 @@ static struct kvm *kvm_create_vm(unsigned long type)
        if (r)
                goto out_err;
 
-       raw_spin_lock(&kvm_lock);
+       spin_lock(&kvm_lock);
        list_add(&kvm->vm_list, &vm_list);
-       raw_spin_unlock(&kvm_lock);
+       spin_unlock(&kvm_lock);
 
        return kvm;
 
@@ -540,13 +542,13 @@ static void kvm_destroy_dirty_bitmap(struct kvm_memory_slot *memslot)
 /*
  * Free any memory in @free but not in @dont.
  */
-static void kvm_free_physmem_slot(struct kvm_memory_slot *free,
+static void kvm_free_physmem_slot(struct kvm *kvm, struct kvm_memory_slot *free,
                                  struct kvm_memory_slot *dont)
 {
        if (!dont || free->dirty_bitmap != dont->dirty_bitmap)
                kvm_destroy_dirty_bitmap(free);
 
-       kvm_arch_free_memslot(free, dont);
+       kvm_arch_free_memslot(kvm, free, dont);
 
        free->npages = 0;
 }
@@ -557,7 +559,7 @@ void kvm_free_physmem(struct kvm *kvm)
        struct kvm_memory_slot *memslot;
 
        kvm_for_each_memslot(memslot, slots)
-               kvm_free_physmem_slot(memslot, NULL);
+               kvm_free_physmem_slot(kvm, memslot, NULL);
 
        kfree(kvm->memslots);
 }
@@ -581,9 +583,9 @@ static void kvm_destroy_vm(struct kvm *kvm)
        struct mm_struct *mm = kvm->mm;
 
        kvm_arch_sync_events(kvm);
-       raw_spin_lock(&kvm_lock);
+       spin_lock(&kvm_lock);
        list_del(&kvm->vm_list);
-       raw_spin_unlock(&kvm_lock);
+       spin_unlock(&kvm_lock);
        kvm_free_irq_routing(kvm);
        for (i = 0; i < KVM_NR_BUSES; i++)
                kvm_io_bus_destroy(kvm->buses[i]);
@@ -821,7 +823,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
        if (change == KVM_MR_CREATE) {
                new.userspace_addr = mem->userspace_addr;
 
-               if (kvm_arch_create_memslot(&new, npages))
+               if (kvm_arch_create_memslot(kvm, &new, npages))
                        goto out_free;
        }
 
@@ -872,6 +874,19 @@ int __kvm_set_memory_region(struct kvm *kvm,
                        goto out_free;
        }
 
+       /* actual memory is freed via old in kvm_free_physmem_slot below */
+       if (change == KVM_MR_DELETE) {
+               new.dirty_bitmap = NULL;
+               memset(&new.arch, 0, sizeof(new.arch));
+       }
+
+       old_memslots = install_new_memslots(kvm, slots, &new);
+
+       kvm_arch_commit_memory_region(kvm, mem, &old, change);
+
+       kvm_free_physmem_slot(kvm, &old, &new);
+       kfree(old_memslots);
+
        /*
         * IOMMU mapping:  New slots need to be mapped.  Old slots need to be
         * un-mapped and re-mapped if their base changes.  Since base change
@@ -883,29 +898,15 @@ int __kvm_set_memory_region(struct kvm *kvm,
         */
        if ((change == KVM_MR_CREATE) || (change == KVM_MR_MOVE)) {
                r = kvm_iommu_map_pages(kvm, &new);
-               if (r)
-                       goto out_slots;
-       }
-
-       /* actual memory is freed via old in kvm_free_physmem_slot below */
-       if (change == KVM_MR_DELETE) {
-               new.dirty_bitmap = NULL;
-               memset(&new.arch, 0, sizeof(new.arch));
+               return r;
        }
 
-       old_memslots = install_new_memslots(kvm, slots, &new);
-
-       kvm_arch_commit_memory_region(kvm, mem, &old, change);
-
-       kvm_free_physmem_slot(&old, &new);
-       kfree(old_memslots);
-
        return 0;
 
 out_slots:
        kfree(slots);
 out_free:
-       kvm_free_physmem_slot(&new, &old);
+       kvm_free_physmem_slot(kvm, &new, &old);
 out:
        return r;
 }
@@ -964,6 +965,7 @@ int kvm_get_dirty_log(struct kvm *kvm,
 out:
        return r;
 }
+EXPORT_SYMBOL_GPL(kvm_get_dirty_log);
 
 bool kvm_largepages_enabled(void)
 {
@@ -1613,8 +1615,9 @@ EXPORT_SYMBOL_GPL(kvm_read_guest_cached);
 
 int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len)
 {
-       return kvm_write_guest_page(kvm, gfn, (const void *) empty_zero_page,
-                                   offset, len);
+       const void *zero_page = (const void *) __va(page_to_phys(ZERO_PAGE(0)));
+
+       return kvm_write_guest_page(kvm, gfn, zero_page, offset, len);
 }
 EXPORT_SYMBOL_GPL(kvm_clear_guest_page);
 
@@ -1654,6 +1657,7 @@ void mark_page_dirty(struct kvm *kvm, gfn_t gfn)
        memslot = gfn_to_memslot(kvm, gfn);
        mark_page_dirty_in_slot(kvm, memslot, gfn);
 }
+EXPORT_SYMBOL_GPL(mark_page_dirty);
 
 /*
  * The vCPU has executed a HLT instruction with in-kernel mode enabled.
@@ -1679,6 +1683,7 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu)
 
        finish_wait(&vcpu->wq, &wait);
 }
+EXPORT_SYMBOL_GPL(kvm_vcpu_block);
 
 #ifndef CONFIG_S390
 /*
@@ -2271,6 +2276,11 @@ static int kvm_ioctl_create_device(struct kvm *kvm,
                ops = &kvm_xics_ops;
                break;
 #endif
+#ifdef CONFIG_KVM_VFIO
+       case KVM_DEV_TYPE_VFIO:
+               ops = &kvm_vfio_ops;
+               break;
+#endif
        default:
                return -ENODEV;
        }
@@ -2519,44 +2529,12 @@ out:
 }
 #endif
 
-static int kvm_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
-{
-       struct page *page[1];
-       unsigned long addr;
-       int npages;
-       gfn_t gfn = vmf->pgoff;
-       struct kvm *kvm = vma->vm_file->private_data;
-
-       addr = gfn_to_hva(kvm, gfn);
-       if (kvm_is_error_hva(addr))
-               return VM_FAULT_SIGBUS;
-
-       npages = get_user_pages(current, current->mm, addr, 1, 1, 0, page,
-                               NULL);
-       if (unlikely(npages != 1))
-               return VM_FAULT_SIGBUS;
-
-       vmf->page = page[0];
-       return 0;
-}
-
-static const struct vm_operations_struct kvm_vm_vm_ops = {
-       .fault = kvm_vm_fault,
-};
-
-static int kvm_vm_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       vma->vm_ops = &kvm_vm_vm_ops;
-       return 0;
-}
-
 static struct file_operations kvm_vm_fops = {
        .release        = kvm_vm_release,
        .unlocked_ioctl = kvm_vm_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = kvm_vm_compat_ioctl,
 #endif
-       .mmap           = kvm_vm_mmap,
        .llseek         = noop_llseek,
 };
 
@@ -2683,11 +2661,12 @@ static void hardware_enable_nolock(void *junk)
        }
 }
 
-static void hardware_enable(void *junk)
+static void hardware_enable(void)
 {
-       raw_spin_lock(&kvm_lock);
-       hardware_enable_nolock(junk);
-       raw_spin_unlock(&kvm_lock);
+       raw_spin_lock(&kvm_count_lock);
+       if (kvm_usage_count)
+               hardware_enable_nolock(NULL);
+       raw_spin_unlock(&kvm_count_lock);
 }
 
 static void hardware_disable_nolock(void *junk)
@@ -2700,11 +2679,12 @@ static void hardware_disable_nolock(void *junk)
        kvm_arch_hardware_disable(NULL);
 }
 
-static void hardware_disable(void *junk)
+static void hardware_disable(void)
 {
-       raw_spin_lock(&kvm_lock);
-       hardware_disable_nolock(junk);
-       raw_spin_unlock(&kvm_lock);
+       raw_spin_lock(&kvm_count_lock);
+       if (kvm_usage_count)
+               hardware_disable_nolock(NULL);
+       raw_spin_unlock(&kvm_count_lock);
 }
 
 static void hardware_disable_all_nolock(void)
@@ -2718,16 +2698,16 @@ static void hardware_disable_all_nolock(void)
 
 static void hardware_disable_all(void)
 {
-       raw_spin_lock(&kvm_lock);
+       raw_spin_lock(&kvm_count_lock);
        hardware_disable_all_nolock();
-       raw_spin_unlock(&kvm_lock);
+       raw_spin_unlock(&kvm_count_lock);
 }
 
 static int hardware_enable_all(void)
 {
        int r = 0;
 
-       raw_spin_lock(&kvm_lock);
+       raw_spin_lock(&kvm_count_lock);
 
        kvm_usage_count++;
        if (kvm_usage_count == 1) {
@@ -2740,7 +2720,7 @@ static int hardware_enable_all(void)
                }
        }
 
-       raw_spin_unlock(&kvm_lock);
+       raw_spin_unlock(&kvm_count_lock);
 
        return r;
 }
@@ -2750,20 +2730,17 @@ static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val,
 {
        int cpu = (long)v;
 
-       if (!kvm_usage_count)
-               return NOTIFY_OK;
-
        val &= ~CPU_TASKS_FROZEN;
        switch (val) {
        case CPU_DYING:
                printk(KERN_INFO "kvm: disabling virtualization on CPU%d\n",
                       cpu);
-               hardware_disable(NULL);
+               hardware_disable();
                break;
        case CPU_STARTING:
                printk(KERN_INFO "kvm: enabling virtualization on CPU%d\n",
                       cpu);
-               hardware_enable(NULL);
+               hardware_enable();
                break;
        }
        return NOTIFY_OK;
@@ -3056,10 +3033,10 @@ static int vm_stat_get(void *_offset, u64 *val)
        struct kvm *kvm;
 
        *val = 0;
-       raw_spin_lock(&kvm_lock);
+       spin_lock(&kvm_lock);
        list_for_each_entry(kvm, &vm_list, vm_list)
                *val += *(u32 *)((void *)kvm + offset);
-       raw_spin_unlock(&kvm_lock);
+       spin_unlock(&kvm_lock);
        return 0;
 }
 
@@ -3073,12 +3050,12 @@ static int vcpu_stat_get(void *_offset, u64 *val)
        int i;
 
        *val = 0;
-       raw_spin_lock(&kvm_lock);
+       spin_lock(&kvm_lock);
        list_for_each_entry(kvm, &vm_list, vm_list)
                kvm_for_each_vcpu(i, vcpu, kvm)
                        *val += *(u32 *)((void *)vcpu + offset);
 
-       raw_spin_unlock(&kvm_lock);
+       spin_unlock(&kvm_lock);
        return 0;
 }
 
@@ -3133,7 +3110,7 @@ static int kvm_suspend(void)
 static void kvm_resume(void)
 {
        if (kvm_usage_count) {
-               WARN_ON(raw_spin_is_locked(&kvm_lock));
+               WARN_ON(raw_spin_is_locked(&kvm_count_lock));
                hardware_enable_nolock(NULL);
        }
 }
diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c
new file mode 100644 (file)
index 0000000..ca4260e
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * VFIO-KVM bridge pseudo device
+ *
+ * Copyright (C) 2013 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/kvm_host.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+
+struct kvm_vfio_group {
+       struct list_head node;
+       struct vfio_group *vfio_group;
+};
+
+struct kvm_vfio {
+       struct list_head group_list;
+       struct mutex lock;
+       bool noncoherent;
+};
+
+static struct vfio_group *kvm_vfio_group_get_external_user(struct file *filep)
+{
+       struct vfio_group *vfio_group;
+       struct vfio_group *(*fn)(struct file *);
+
+       fn = symbol_get(vfio_group_get_external_user);
+       if (!fn)
+               return ERR_PTR(-EINVAL);
+
+       vfio_group = fn(filep);
+
+       symbol_put(vfio_group_get_external_user);
+
+       return vfio_group;
+}
+
+static void kvm_vfio_group_put_external_user(struct vfio_group *vfio_group)
+{
+       void (*fn)(struct vfio_group *);
+
+       fn = symbol_get(vfio_group_put_external_user);
+       if (!fn)
+               return;
+
+       fn(vfio_group);
+
+       symbol_put(vfio_group_put_external_user);
+}
+
+/*
+ * Groups can use the same or different IOMMU domains.  If the same then
+ * adding a new group may change the coherency of groups we've previously
+ * been told about.  We don't want to care about any of that so we retest
+ * each group and bail as soon as we find one that's noncoherent.  This
+ * means we only ever [un]register_noncoherent_dma once for the whole device.
+ */
+static void kvm_vfio_update_coherency(struct kvm_device *dev)
+{
+       struct kvm_vfio *kv = dev->private;
+       bool noncoherent = false;
+       struct kvm_vfio_group *kvg;
+
+       mutex_lock(&kv->lock);
+
+       list_for_each_entry(kvg, &kv->group_list, node) {
+               /*
+                * TODO: We need an interface to check the coherency of
+                * the IOMMU domain this group is using.  For now, assume
+                * it's always noncoherent.
+                */
+               noncoherent = true;
+               break;
+       }
+
+       if (noncoherent != kv->noncoherent) {
+               kv->noncoherent = noncoherent;
+
+               if (kv->noncoherent)
+                       kvm_arch_register_noncoherent_dma(dev->kvm);
+               else
+                       kvm_arch_unregister_noncoherent_dma(dev->kvm);
+       }
+
+       mutex_unlock(&kv->lock);
+}
+
+static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
+{
+       struct kvm_vfio *kv = dev->private;
+       struct vfio_group *vfio_group;
+       struct kvm_vfio_group *kvg;
+       void __user *argp = (void __user *)arg;
+       struct fd f;
+       int32_t fd;
+       int ret;
+
+       switch (attr) {
+       case KVM_DEV_VFIO_GROUP_ADD:
+               if (get_user(fd, (int32_t __user *)argp))
+                       return -EFAULT;
+
+               f = fdget(fd);
+               if (!f.file)
+                       return -EBADF;
+
+               vfio_group = kvm_vfio_group_get_external_user(f.file);
+               fdput(f);
+
+               if (IS_ERR(vfio_group))
+                       return PTR_ERR(vfio_group);
+
+               mutex_lock(&kv->lock);
+
+               list_for_each_entry(kvg, &kv->group_list, node) {
+                       if (kvg->vfio_group == vfio_group) {
+                               mutex_unlock(&kv->lock);
+                               kvm_vfio_group_put_external_user(vfio_group);
+                               return -EEXIST;
+                       }
+               }
+
+               kvg = kzalloc(sizeof(*kvg), GFP_KERNEL);
+               if (!kvg) {
+                       mutex_unlock(&kv->lock);
+                       kvm_vfio_group_put_external_user(vfio_group);
+                       return -ENOMEM;
+               }
+
+               list_add_tail(&kvg->node, &kv->group_list);
+               kvg->vfio_group = vfio_group;
+
+               mutex_unlock(&kv->lock);
+
+               kvm_vfio_update_coherency(dev);
+
+               return 0;
+
+       case KVM_DEV_VFIO_GROUP_DEL:
+               if (get_user(fd, (int32_t __user *)argp))
+                       return -EFAULT;
+
+               f = fdget(fd);
+               if (!f.file)
+                       return -EBADF;
+
+               vfio_group = kvm_vfio_group_get_external_user(f.file);
+               fdput(f);
+
+               if (IS_ERR(vfio_group))
+                       return PTR_ERR(vfio_group);
+
+               ret = -ENOENT;
+
+               mutex_lock(&kv->lock);
+
+               list_for_each_entry(kvg, &kv->group_list, node) {
+                       if (kvg->vfio_group != vfio_group)
+                               continue;
+
+                       list_del(&kvg->node);
+                       kvm_vfio_group_put_external_user(kvg->vfio_group);
+                       kfree(kvg);
+                       ret = 0;
+                       break;
+               }
+
+               mutex_unlock(&kv->lock);
+
+               kvm_vfio_group_put_external_user(vfio_group);
+
+               kvm_vfio_update_coherency(dev);
+
+               return ret;
+       }
+
+       return -ENXIO;
+}
+
+static int kvm_vfio_set_attr(struct kvm_device *dev,
+                            struct kvm_device_attr *attr)
+{
+       switch (attr->group) {
+       case KVM_DEV_VFIO_GROUP:
+               return kvm_vfio_set_group(dev, attr->attr, attr->addr);
+       }
+
+       return -ENXIO;
+}
+
+static int kvm_vfio_has_attr(struct kvm_device *dev,
+                            struct kvm_device_attr *attr)
+{
+       switch (attr->group) {
+       case KVM_DEV_VFIO_GROUP:
+               switch (attr->attr) {
+               case KVM_DEV_VFIO_GROUP_ADD:
+               case KVM_DEV_VFIO_GROUP_DEL:
+                       return 0;
+               }
+
+               break;
+       }
+
+       return -ENXIO;
+}
+
+static void kvm_vfio_destroy(struct kvm_device *dev)
+{
+       struct kvm_vfio *kv = dev->private;
+       struct kvm_vfio_group *kvg, *tmp;
+
+       list_for_each_entry_safe(kvg, tmp, &kv->group_list, node) {
+               kvm_vfio_group_put_external_user(kvg->vfio_group);
+               list_del(&kvg->node);
+               kfree(kvg);
+       }
+
+       kvm_vfio_update_coherency(dev);
+
+       kfree(kv);
+       kfree(dev); /* alloc by kvm_ioctl_create_device, free by .destroy */
+}
+
+static int kvm_vfio_create(struct kvm_device *dev, u32 type)
+{
+       struct kvm_device *tmp;
+       struct kvm_vfio *kv;
+
+       /* Only one VFIO "device" per VM */
+       list_for_each_entry(tmp, &dev->kvm->devices, vm_node)
+               if (tmp->ops == &kvm_vfio_ops)
+                       return -EBUSY;
+
+       kv = kzalloc(sizeof(*kv), GFP_KERNEL);
+       if (!kv)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&kv->group_list);
+       mutex_init(&kv->lock);
+
+       dev->private = kv;
+
+       return 0;
+}
+
+struct kvm_device_ops kvm_vfio_ops = {
+       .name = "kvm-vfio",
+       .create = kvm_vfio_create,
+       .destroy = kvm_vfio_destroy,
+       .set_attr = kvm_vfio_set_attr,
+       .has_attr = kvm_vfio_has_attr,
+};